app: implement Link/Unlink for timeline clips
CI / Build & test (Linux) (push) Canceled after 8m56s
CI / Build & test (Windows) (push) Canceled after 7m16s

编辑 > 链接/重新链接 (Cmd+L) was an unhandled stub
(PanelCommandHandler::toggle_links returned false), so linking clips
never created graph links and linked drags never happened.

- graphops::set_clips_linked: one undoable entry toggling every pair of
  the selection; the undo restores the exact prior internal topology.
- RealEngine::toggle_clip_links: fully-linked selections unlink,
  otherwise the selection links together (the C++ crude "any member has
  ANY link" rule would have made split halves — which inherit the
  original clip's A/V links — impossible to link to each other).
- TimelinePanel overrides toggle_links, routing the timeline selection.
- MockEngine stores demo link pairs.
- Test: link_unlink_toggles_the_selection_links — dropped A/V pair
  toggles off/on with undo, and split halves link manually.
This commit is contained in:
2026-08-25 03:40:57 +08:00
parent a1bfe5ee5e
commit 1ad2d71c81
5 changed files with 266 additions and 0 deletions
+8
View File
@@ -683,6 +683,14 @@ impl<E: AppEngine> PanelCommandHandler for TimelinePanel<E> {
.update(cx, |engine, cx| engine.sync_clips_by_waveform(ids, true, cx));
true
}
/// 编辑 → 链接/重新链接: toggles the graph links among the selected
/// clips through the engine (one undoable entry).
fn toggle_links(&mut self, cx: &mut Context<Self>) -> bool {
let ids: Vec<ClipId> = self.timeline.read(cx).selection().iter().copied().collect();
self.engine
.update(cx, |engine, cx| engine.toggle_clip_links(ids, cx));
true
}
// --- view ---
fn zoom_in(&mut self, cx: &mut Context<Self>) -> bool {