Rename views to keyframe, curve and segment and update defaults

This commit is contained in:
Skyler Lehmkuhl 2025-10-23 05:38:10 -04:00
parent 5e1a30d812
commit 9649fe173b
5 changed files with 112 additions and 26 deletions

View File

@ -60,8 +60,8 @@ class GraphicsObject extends Widget {
// Timeline display settings (Phase 3) // Timeline display settings (Phase 3)
this.showSegment = true // Show segment bar in timeline this.showSegment = true // Show segment bar in timeline
this.curvesMode = 'hidden' // 'hidden' | 'minimized' | 'expanded' this.curvesMode = 'keyframe' // 'segment' | 'keyframe' | 'curve'
this.curvesHeight = 150 // Height in pixels when curves are expanded this.curvesHeight = 150 // Height in pixels when curves are in curve view
this._globalEvents.add("mousedown") this._globalEvents.add("mousedown")
this._globalEvents.add("mousemove") this._globalEvents.add("mousemove")

View File

@ -1047,8 +1047,8 @@ class AudioTrack {
// Timeline display settings (for track hierarchy) // Timeline display settings (for track hierarchy)
this.collapsed = false this.collapsed = false
this.curvesMode = 'hidden' // 'hidden' | 'minimized' | 'expanded' this.curvesMode = 'segment' // 'segment' | 'keyframe' | 'curve'
this.curvesHeight = 150 // Height in pixels when curves are expanded this.curvesHeight = 150 // Height in pixels when curves are in curve view
pointerList[this.idx] = this; pointerList[this.idx] = this;
} }

View File

@ -315,8 +315,8 @@ class Shape extends BaseShape {
// Timeline display settings (Phase 3) // Timeline display settings (Phase 3)
this.showSegment = true // Show segment bar in timeline this.showSegment = true // Show segment bar in timeline
this.curvesMode = 'hidden' // 'hidden' | 'minimized' | 'expanded' this.curvesMode = 'keyframe' // 'segment' | 'keyframe' | 'curve'
this.curvesHeight = 150 // Height in pixels when curves are expanded this.curvesHeight = 150 // Height in pixels when curves are in curve view
} }
static fromJSON(json, parent) { static fromJSON(json, parent) {
let fillImage = undefined; let fillImage = undefined;

View File

@ -537,11 +537,11 @@ class TrackHierarchy {
const obj = track.object const obj = track.object
// Calculate additional height needed for curves // Calculate additional height needed for curves
if (obj.curvesMode === 'minimized') { if (obj.curvesMode === 'keyframe') {
// Phase 6: Minimized mode should be compact - no extra height // Phase 6: Minimized mode should be compact - no extra height
// Keyframes are overlaid on the segment bar // Keyframes are overlaid on the segment bar
return baseHeight return baseHeight
} else if (obj.curvesMode === 'expanded') { } else if (obj.curvesMode === 'curve') {
// Use the object's curvesHeight property // Use the object's curvesHeight property
return baseHeight + (obj.curvesHeight || 150) + 10 // +10 for padding return baseHeight + (obj.curvesHeight || 150) + 10 // +10 for padding
} }

View File

@ -604,6 +604,46 @@ class TimelineWindowV2 extends Widget {
this.drawCurves(ctx) this.drawCurves(ctx)
} }
// Draw curve mode button tooltip if hovering
if (this.hoveredCurveModeButton) {
const text = this.hoveredCurveModeButton.modeName
// Measure text to size the tooltip
ctx.font = '11px sans-serif'
const textMetrics = ctx.measureText(text)
const textWidth = textMetrics.width
const tooltipPadding = 4
const tooltipWidth = textWidth + tooltipPadding * 2
const tooltipHeight = 16
// Position tooltip near mouse
let tooltipX = this.hoveredCurveModeButton.x + 10
let tooltipY = this.hoveredCurveModeButton.y - tooltipHeight - 5
// Clamp to stay within bounds
if (tooltipX + tooltipWidth > this.width) {
tooltipX = this.hoveredCurveModeButton.x - tooltipWidth - 10
}
if (tooltipY < 0) {
tooltipY = this.hoveredCurveModeButton.y + 5
}
// Draw tooltip background
ctx.fillStyle = backgroundColor
ctx.fillRect(tooltipX, tooltipY, tooltipWidth, tooltipHeight)
// Draw tooltip border
ctx.strokeStyle = foregroundColor
ctx.lineWidth = 1
ctx.strokeRect(tooltipX, tooltipY, tooltipWidth, tooltipHeight)
// Draw text
ctx.fillStyle = labelColor
ctx.textAlign = 'left'
ctx.textBaseline = 'middle'
ctx.fillText(text, tooltipX + tooltipPadding, tooltipY + tooltipHeight / 2)
}
ctx.restore() ctx.restore()
} }
@ -764,8 +804,8 @@ class TimelineWindowV2 extends Widget {
ctx.font = '10px sans-serif' ctx.font = '10px sans-serif'
ctx.textAlign = 'center' ctx.textAlign = 'center'
ctx.textBaseline = 'middle' ctx.textBaseline = 'middle'
const curveSymbol = track.object.curvesMode === 'expanded' ? '~' : const curveSymbol = track.object.curvesMode === 'curve' ? '~' :
track.object.curvesMode === 'minimized' ? '≈' : '-' track.object.curvesMode === 'keyframe' ? '≈' : '-'
ctx.fillText(curveSymbol, buttonX + buttonSize / 2, buttonY + buttonSize / 2) ctx.fillText(curveSymbol, buttonX + buttonSize / 2, buttonY + buttonSize / 2)
// Segment visibility button (only for object/shape tracks, not audio) // Segment visibility button (only for object/shape tracks, not audio)
@ -783,7 +823,7 @@ class TimelineWindowV2 extends Widget {
} }
// Draw legend for expanded curves (Phase 6) // Draw legend for expanded curves (Phase 6)
if (track.object.curvesMode === 'expanded') { if (track.object.curvesMode === 'curve') {
// Get curves for this track // Get curves for this track
const curves = [] const curves = []
const obj = track.object const obj = track.object
@ -1311,7 +1351,7 @@ class TimelineWindowV2 extends Widget {
const obj = track.object const obj = track.object
// Skip if curves are hidden // Skip if curves are hidden
if (obj.curvesMode === 'hidden') continue if (obj.curvesMode === 'segment') continue
const y = this.trackHierarchy.getTrackY(i) const y = this.trackHierarchy.getTrackY(i)
@ -1368,9 +1408,9 @@ class TimelineWindowV2 extends Widget {
if (curves.length === 0) continue if (curves.length === 0) continue
// Draw based on curves mode // Draw based on curves mode
if (obj.curvesMode === 'minimized') { if (obj.curvesMode === 'keyframe') {
this.drawMinimizedCurves(ctx, curves, y) this.drawMinimizedCurves(ctx, curves, y)
} else if (obj.curvesMode === 'expanded') { } else if (obj.curvesMode === 'curve') {
this.drawExpandedCurves(ctx, curves, y) this.drawExpandedCurves(ctx, curves, y)
} }
} }
@ -1749,14 +1789,22 @@ class TimelineWindowV2 extends Widget {
const curveButtonX = buttonX - buttonSize const curveButtonX = buttonX - buttonSize
if (x >= curveButtonX && x <= curveButtonX + buttonSize && if (x >= curveButtonX && x <= curveButtonX + buttonSize &&
adjustedY >= buttonY && adjustedY <= buttonY + buttonSize) { adjustedY >= buttonY && adjustedY <= buttonY + buttonSize) {
// Cycle through curves modes: hidden -> minimized -> expanded -> hidden // Cycle through curves modes: segment -> keyframe -> curve -> segment
if (track.object.curvesMode === 'hidden') { if (track.object.curvesMode === 'segment') {
track.object.curvesMode = 'minimized' track.object.curvesMode = 'keyframe'
} else if (track.object.curvesMode === 'minimized') { } else if (track.object.curvesMode === 'keyframe') {
track.object.curvesMode = 'expanded' track.object.curvesMode = 'curve'
} else { } else {
track.object.curvesMode = 'hidden' track.object.curvesMode = 'segment'
} }
// Update hover tooltip with new mode name
if (this.hoveredCurveModeButton) {
const modeName = track.object.curvesMode === 'curve' ? 'Curve View' :
track.object.curvesMode === 'keyframe' ? 'Keyframe View' : 'Segment View'
this.hoveredCurveModeButton.modeName = modeName
}
if (this.requestRedraw) this.requestRedraw() if (this.requestRedraw) this.requestRedraw()
return true return true
} }
@ -1772,7 +1820,7 @@ class TimelineWindowV2 extends Widget {
} }
// Check if clicking on legend items (Phase 6) // Check if clicking on legend items (Phase 6)
if (track.object.curvesMode === 'expanded') { if (track.object.curvesMode === 'curve') {
const trackIndex = this.trackHierarchy.tracks.indexOf(track) const trackIndex = this.trackHierarchy.tracks.indexOf(track)
const trackYPos = this.trackHierarchy.getTrackY(trackIndex) const trackYPos = this.trackHierarchy.getTrackY(trackIndex)
const legendPadding = 3 const legendPadding = 3
@ -1846,7 +1894,7 @@ class TimelineWindowV2 extends Widget {
if (track) { if (track) {
// Phase 6: Check if clicking on tangent handle (highest priority for curves) // Phase 6: Check if clicking on tangent handle (highest priority for curves)
if ((track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'expanded') { if ((track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'curve') {
const tangentInfo = this.getTangentHandleAtPoint(track, adjustedX, adjustedY) const tangentInfo = this.getTangentHandleAtPoint(track, adjustedX, adjustedY)
console.log(`Tangent handle check result:`, tangentInfo) console.log(`Tangent handle check result:`, tangentInfo)
if (tangentInfo) { if (tangentInfo) {
@ -1875,7 +1923,7 @@ class TimelineWindowV2 extends Widget {
} }
// Phase 5: Check if clicking on expanded curves // Phase 5: Check if clicking on expanded curves
if ((track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'expanded') { if ((track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'curve') {
const curveClickResult = this.handleCurveClick(track, adjustedX, adjustedY) const curveClickResult = this.handleCurveClick(track, adjustedX, adjustedY)
if (curveClickResult) { if (curveClickResult) {
return true return true
@ -2537,7 +2585,7 @@ class TimelineWindowV2 extends Widget {
*/ */
getTangentHandleAtPoint(track, x, y) { getTangentHandleAtPoint(track, x, y) {
if (track.type !== 'object' && track.type !== 'shape') return null if (track.type !== 'object' && track.type !== 'shape') return null
if (track.object.curvesMode !== 'expanded') return null if (track.object.curvesMode !== 'curve') return null
const trackIndex = this.trackHierarchy.tracks.indexOf(track) const trackIndex = this.trackHierarchy.tracks.indexOf(track)
const trackY = this.trackHierarchy.getTrackY(trackIndex) const trackY = this.trackHierarchy.getTrackY(trackIndex)
@ -2746,6 +2794,44 @@ class TimelineWindowV2 extends Widget {
} }
mousemove(x, y) { mousemove(x, y) {
// Check for curve mode button hover (in track header area)
const trackY = y - this.ruler.height
if (trackY >= 0 && x < this.trackHeaderWidth) {
const adjustedY = trackY - this.trackScrollOffset
const track = this.trackHierarchy.getTrackAtY(adjustedY)
if (track && (track.type === 'object' || track.type === 'shape' || track.type === 'audio')) {
const trackIndex = this.trackHierarchy.tracks.indexOf(track)
const trackYPos = this.trackHierarchy.getTrackY(trackIndex)
const buttonSize = 16
const buttonY = trackYPos + (this.trackHierarchy.trackHeight - buttonSize) / 2
let buttonX = this.trackHeaderWidth - 10 - buttonSize // Rightmost button
// Check if hovering over curve mode button
if (x >= buttonX && x <= buttonX + buttonSize &&
adjustedY >= buttonY && adjustedY <= buttonY + buttonSize) {
// Get the mode name for tooltip
const modeName = track.object.curvesMode === 'curve' ? 'Curve View' :
track.object.curvesMode === 'keyframe' ? 'Keyframe View' : 'Segment View'
this.hoveredCurveModeButton = {
x: x,
y: y,
modeName: modeName
}
if (this.requestRedraw) this.requestRedraw()
} else if (this.hoveredCurveModeButton) {
this.hoveredCurveModeButton = null
if (this.requestRedraw) this.requestRedraw()
}
} else if (this.hoveredCurveModeButton) {
this.hoveredCurveModeButton = null
if (this.requestRedraw) this.requestRedraw()
}
} else if (this.hoveredCurveModeButton) {
this.hoveredCurveModeButton = null
if (this.requestRedraw) this.requestRedraw()
}
// Update hover state for keyframe tooltips (even when not dragging) // Update hover state for keyframe tooltips (even when not dragging)
// Clear hover if mouse is outside timeline curve areas // Clear hover if mouse is outside timeline curve areas
let foundHover = false let foundHover = false
@ -2757,7 +2843,7 @@ class TimelineWindowV2 extends Widget {
const adjustedX = x - this.trackHeaderWidth const adjustedX = x - this.trackHeaderWidth
const track = this.trackHierarchy.getTrackAtY(adjustedY) const track = this.trackHierarchy.getTrackAtY(adjustedY)
if (track && (track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'expanded') { if (track && (track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'curve') {
const trackIndex = this.trackHierarchy.tracks.indexOf(track) const trackIndex = this.trackHierarchy.tracks.indexOf(track)
const trackYPos = this.trackHierarchy.getTrackY(trackIndex) const trackYPos = this.trackHierarchy.getTrackY(trackIndex)
@ -3442,7 +3528,7 @@ class TimelineWindowV2 extends Widget {
const adjustedX = x - this.trackHeaderWidth const adjustedX = x - this.trackHeaderWidth
const track = this.trackHierarchy.getTrackAtY(adjustedY) const track = this.trackHierarchy.getTrackAtY(adjustedY)
if (track && (track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'expanded') { if (track && (track.type === 'object' || track.type === 'shape') && track.object.curvesMode === 'curve') {
// Use similar logic to handleCurveClick to find if we're clicking on a keyframe // Use similar logic to handleCurveClick to find if we're clicking on a keyframe
const trackIndex = this.trackHierarchy.tracks.indexOf(track) const trackIndex = this.trackHierarchy.tracks.indexOf(track)
const trackYPos = this.trackHierarchy.getTrackY(trackIndex) const trackYPos = this.trackHierarchy.getTrackY(trackIndex)