mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-26 19:18:14 -06:00
#637: double-clicking a layer's bar acts like its name; text layers edit their text
Double-clicking a text layer in the Timeline (its name or its bar) edits its text in the Composition viewer with the whole text selected, as in After Effects; it used to open the rename field (Enter still renames). The layer bar had no double-click at all: it now opens a precomp, opens footage and solids in the Layer panel and edits text, like the name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
67434e9038
commit
e108f460c4
@@ -563,6 +563,18 @@ pub fn begin_rename(app: &mut EffectcraftApp, ctx: &egui::Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/// What double-clicking a layer's name or bar does, as in After Effects: a precomp opens, footage
|
||||
/// and solids open in the Layer panel (paint happens there), text is edited in the viewer with the
|
||||
/// whole text selected. `None`: the name renames, the bar does nothing.
|
||||
fn double_click_action(layer: &Layer) -> Option<(String, serde_json::Value)> {
|
||||
match &layer.source {
|
||||
LayerSource::Comp { item } => Some(("comp.open".into(), json!({"comp": item.0}))),
|
||||
LayerSource::Footage { .. } | LayerSource::Solid { .. } => Some(("layer.openLayer".into(), json!({"layer": layer.id.0}))),
|
||||
LayerSource::Text => Some(("text.edit".into(), json!({"layer": layer.id.0}))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the rename field on layer `id`; it takes the keyboard focus on its first frame only.
|
||||
fn start_rename(ctx: &egui::Context, id: u64, name: &str) {
|
||||
ctx.data_mut(|d| {
|
||||
@@ -1870,13 +1882,9 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
}
|
||||
}
|
||||
if row_resp.double_clicked() {
|
||||
match &layer.source {
|
||||
LayerSource::Comp { item } => actions.push(("comp.open".into(), json!({"comp": item.0}))),
|
||||
// Footage and solids open in the Layer panel (paint happens there).
|
||||
LayerSource::Footage { .. } | LayerSource::Solid { .. } => {
|
||||
actions.push(("layer.openLayer".into(), json!({"layer": layer.id.0})));
|
||||
}
|
||||
_ => start_rename(&ctx, layer.id.0, &layer.name),
|
||||
match double_click_action(layer) {
|
||||
Some(a) => actions.push(a),
|
||||
None => start_rename(&ctx, layer.id.0, &layer.name),
|
||||
}
|
||||
}
|
||||
layer_context_menu(&row_resp, layer, &mut actions);
|
||||
@@ -2084,6 +2092,11 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
if lin.hovered() || lout.hovered() || lin.dragged() || lout.dragged() {
|
||||
ctx.set_cursor_icon(egui::CursorIcon::ResizeHorizontal);
|
||||
}
|
||||
if body.double_clicked()
|
||||
&& let Some(a) = double_click_action(layer)
|
||||
{
|
||||
actions.push(a);
|
||||
}
|
||||
let shift = ui.input(|i| i.modifiers.shift);
|
||||
if let Some(layers) = (body.clicked() && shift).then(|| shift_range(&ctx, cid, &comp, &rows, &selected, layer.id)).flatten() {
|
||||
// Shift-click: the layers from the last one clicked to this one, as in
|
||||
|
||||
@@ -167,6 +167,18 @@ fn nested_comp_markers_show_on_the_precomp_bar() {
|
||||
assert!((h.state().session.time().seconds() - 1.0).abs() < 0.034, "{}", h.state().session.time().seconds());
|
||||
}
|
||||
|
||||
/// #637: double-clicking a precomp layer's bar opens its comp, as double-clicking its name does.
|
||||
#[test]
|
||||
fn double_clicking_a_precomp_bar_opens_its_comp() {
|
||||
let (mut h, pre, main) = harness();
|
||||
h.state_mut().session.execute("comp.open", json!({"comp": main})).unwrap();
|
||||
h.run_steps(4);
|
||||
let l = h.state().session.project.comp(ItemId(main)).unwrap().layers[0].id.0;
|
||||
let bar = rect(&h, &format!("timeline.layer.{l}.bar"));
|
||||
click_n(&mut h, bar.center(), 2);
|
||||
assert_eq!(h.state().session.active_comp_id(), Some(ItemId(pre)));
|
||||
}
|
||||
|
||||
/// Issue #46: a panel dragged by its panel-menu (hamburger) button onto the left edge of another
|
||||
/// group docks there as its own group; dragging the gap between panels resizes them.
|
||||
#[test]
|
||||
|
||||
@@ -277,3 +277,29 @@ fn type_tool_drag_makes_paragraph_text_with_box_handles() {
|
||||
assert!(doc2.box_size.unwrap()[0] > bs[0] + 20.0, "{:?}", doc2.box_size);
|
||||
assert!(h.state().session.state.text_edit.is_some(), "still editing after resizing");
|
||||
}
|
||||
|
||||
/// Double-clicking a text layer in the Timeline, on its name or on its bar, edits its text in the
|
||||
/// Composition viewer with the whole text selected, as in After Effects (Enter renames).
|
||||
#[test]
|
||||
fn timeline_double_click_on_a_text_layer_edits_its_text() {
|
||||
for target in ["row", "bar"] {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"name": "Type", "width": 640, "height": 360, "duration": 2})).unwrap();
|
||||
let lid = s.execute("layer.newText", json!({"text": "alpha beta"})).unwrap()["layer"].as_u64().unwrap();
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).with_step_dt(1.0 / 60.0).build_eframe(move |_| EffectcraftApp::new(s));
|
||||
h.state_mut().show_panel(PanelKind::Composition);
|
||||
h.run_steps(3);
|
||||
let p = rect(&h, &format!("timeline.layer.{lid}.{target}")).center();
|
||||
click(&mut h, p, Modifiers::NONE);
|
||||
click(&mut h, p, Modifiers::NONE);
|
||||
let (layer, text, anchor, caret) = edited(&h);
|
||||
assert_eq!(layer, lid, "{target}");
|
||||
assert_eq!((anchor.min(caret), anchor.max(caret)), (0, 10), "everything selected ({target})");
|
||||
assert!(h.state().auto.find(&format!("timeline.layer.{lid}.nameEdit")).is_none(), "no rename field ({target})");
|
||||
assert_eq!(text, "alpha beta");
|
||||
type_text(&mut h, "gamma");
|
||||
key(&mut h, Key::Escape, Modifiers::NONE);
|
||||
assert!(h.state().session.state.text_edit.is_none(), "Escape commits ({target})");
|
||||
assert_eq!(layer_doc(&h.state().session, LayerId(lid)).unwrap().text, "gamma", "typing replaced the text ({target})");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user