Move "Delete row" from the inspector into the timeline menu
The context menu of the timeline is where the other row and key actions live. A separate button in the inspector was easy to miss and sat apart from them. - The timeline menu gets "Delete row" as a danger entry after a separator. It deletes the row under the pointer, whether the click hits the row header or a cell. Below the last row or on the ruler it is disabled. - A right-click on a row header now also selects that row, like a right-click on a key selects the key. A right-click in an empty cell keeps the selection so chosen keys stay available for Copy. - The "Delete row" button in the "Timeline row" inspector is gone. "Delete keys" for a multi-key selection stays. Tests: - ./check.sh: 172 files, 2498 tests, CHECK EXIT 0 - e2e 25: ALL PASSED, 30 checks. New check in 25g deletes a row via a real right-click and restores it with all three keys via Undo. - No counter-probe. No check covers the right-click into a cell. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0c5ac32b59
commit
89b95aa8ac
4 files changed
+36
-11
No files matched your search
@@ -4,6 +4,7 @@ import {
|
||||
copyAnimationKeys,
|
||||
MoveAnimationKeysCommand,
|
||||
PasteAnimationKeysCommand,
|
||||
RemoveAnimationTracksCommand,
|
||||
SetAnimationKeyCommand,
|
||||
ShiftAnimationFramesCommand,
|
||||
shiftedFrame,
|
||||
@@ -278,13 +279,20 @@ export class TimelineCanvas extends Widget {
|
||||
);
|
||||
}
|
||||
|
||||
/** A right-click on a key picks it first, the way a node's menu does, so Copy speaks about it. */
|
||||
/**
|
||||
* A right-click on a key picks it first, the way a node's menu does, so Copy speaks about it; one
|
||||
* on a row's header picks the row. Delete row always means the row under the pointer.
|
||||
*/
|
||||
private openMenu(asset: AnimationAsset, local: Vec2, global: Vec2): void {
|
||||
const hit =
|
||||
local.x >= HEADER_WIDTH && local.y >= RULER_HEIGHT
|
||||
? keyAt(this.view, asset.tracks, { x: local.x - HEADER_WIDTH, y: local.y })
|
||||
: null;
|
||||
if (hit && !hasKey(this.state.selectedKeys, hit)) this.state.select(trackSelection(asset.id, [hit]));
|
||||
const row = local.y >= RULER_HEIGHT ? rowAt(local.y, asset.tracks.length) : -1;
|
||||
const track = row >= 0 ? asset.tracks[row] : undefined;
|
||||
if (track && local.x < HEADER_WIDTH && this.state.selectedTrackId !== track.id)
|
||||
this.state.select(trackSelection(asset.id, [], track.id));
|
||||
const picked = this.state.selectedKeys.length;
|
||||
const at = this.playheadFrame;
|
||||
new Menu([
|
||||
@@ -301,6 +309,15 @@ export class TimelineCanvas extends Widget {
|
||||
{ separator: true },
|
||||
{ label: `Insert frames at ${at}…`, onSelect: () => this.openFramesDialog('insert') },
|
||||
{ label: `Remove frames from ${at}…`, danger: true, onSelect: () => this.openFramesDialog('remove') },
|
||||
{ separator: true },
|
||||
{
|
||||
label: 'Delete row',
|
||||
danger: true,
|
||||
disabled: !track,
|
||||
onSelect: () => {
|
||||
if (track) this.state.execute(new RemoveAnimationTracksCommand(asset.id, [track.id]));
|
||||
},
|
||||
},
|
||||
]).open(global.x, global.y);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
RemoveAnimationKeysCommand,
|
||||
RemoveAnimationTracksCommand,
|
||||
SetAnimationKeyCommand,
|
||||
SetKeyInterpolationCommand,
|
||||
SetTrackRelativeCommand,
|
||||
@@ -112,14 +111,6 @@ export function buildTrackSection(
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
addGap(host);
|
||||
host.body.add(
|
||||
new Button('Delete row', {
|
||||
variant: 'danger',
|
||||
onClick: () => host.state.execute(new RemoveAnimationTracksCommand(assetId, [trackId])),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/** The value of the one picked key, editable in the widget its kind calls for. */
|
||||
|
||||
Reference in new issue
Block a user