Group timeline rows by object and edit values in the row (#408)
The rows of the animation timeline stood in the order they were keyed. With several objects and properties the list was hard to read, and a value could only be changed by picking a key and using the inspector. The Play button also kept saying Pause after scrubbing had paused. - Each object gets a header row with its name, indented by depth. The groups follow the scene hierarchy, the root first. Rows whose object is missing go to the end. - Under an object the rows follow the order in which it lists its properties: X, Y, Z, then the order of the inspector. Only the view is sorted, the animation itself is unchanged and there is no undo step. After a move in the hierarchy the order is right by itself. - The row head shows the same field as the inspector with the value at the playhead. Without keys it shows the value of the object, for a relative row the offset. - A change in that field sets or changes the key at the playhead, selects it and is one undo step. A running playback pauses first. - The Play/Pause button follows every change of the playing state, so scrubbing that pauses shows Play again. Tests: - Unit animation + timeline-view: 100 passed, 3 new. - e2e 25 on melektaus-atx: ALL PASSED, 34 checks, 3 new (groups, order and values at frame 4, a typed 55 becomes a selected key on frame 4, Play then a click on the ruler shows Play and pauses). - Gate on melektaus-atx: exit 0, 297 files, 4561 tests. - tsc, eslint, Prettier clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dd60a795e1
commit
8679dfbe65
9 files changed
+449
-69
No files matched your search
@@ -14,6 +14,7 @@ import {
|
||||
ROW_HEIGHT,
|
||||
rowAt,
|
||||
rowCentreY,
|
||||
rowTop,
|
||||
RULER_HEIGHT,
|
||||
rulerStep,
|
||||
type TimelineView,
|
||||
@@ -125,6 +126,18 @@ describe('picking keys', () => {
|
||||
expect(keysWithin(view, TRACKS, a, b).map((k) => k.trackId)).toEqual(['a', 'a']);
|
||||
});
|
||||
|
||||
it("skips an object's head between the rows (#408)", () => {
|
||||
const lanes = [null, TRACKS[0]!, null, TRACKS[1]!];
|
||||
expect(keyAt(view, lanes, { x: frameCentreX(view, 4), y: rowCentreY(0) })).toBeNull();
|
||||
expect(keyAt(view, lanes, { x: frameCentreX(view, 4), y: rowCentreY(1) })).toEqual({
|
||||
trackId: 'a',
|
||||
frame: 4,
|
||||
});
|
||||
expect(keyAt(view, lanes, { x: frameCentreX(view, 2), y: rowCentreY(3) })?.trackId).toBe('b');
|
||||
const all = keysWithin(view, lanes, { x: 0, y: RULER_HEIGHT }, { x: frameToX(view, 10), y: rowTop(4) });
|
||||
expect(all.map((k) => k.trackId)).toEqual(['a', 'a', 'b']);
|
||||
});
|
||||
|
||||
it('a move never takes a key before frame 0', () => {
|
||||
const keys = [
|
||||
{ trackId: 'a', frame: 2 },
|
||||
|
||||
Reference in new issue
Block a user