diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 587379a85..680cbff22 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -246,6 +246,18 @@ fn text_shape_mask_matte_parent() { assert_eq!(info["layers"].as_array().unwrap().len(), 2); } +/// A shape layer the size of the comp and placed at its default position covers the frame: the +/// Shape Tool's double-click (#1284) makes one this way, for backgrounds, mattes and overlays. +#[test] +fn a_comp_sized_shape_layer_sits_on_the_comp_centre() { + let mut s = Session::default(); + s.execute("comp.new", json!({"width": 400, "height": 300, "duration": 2})).unwrap(); + let sh = s.execute("layer.newShape", json!({"kind": "rect", "size": [400, 300], "fill": "#ff0000"})).unwrap()["layer"].as_u64().unwrap(); + let l = s.active_comp().unwrap().layer(effectcraft_project::LayerId(sh)).unwrap().clone(); + let pos = l.props.prop("transform/position").unwrap().value.clone(); + assert!(matches!(pos, effectcraft_keyframe::Value::Vec3(p) if (p[0] - 200.0).abs() < 1e-9 && (p[1] - 150.0).abs() < 1e-9), "{pos:?}"); +} + /// The toolbar's Fill / Stroke change a selected shape layer's paint (#205): a shape drawn /// without a stroke gets one, later edits change it, and the Contents "Add:" items (#206) include /// an empty Path and a Gradient Stroke that render. diff --git a/crates/ui-egui/src/header.rs b/crates/ui-egui/src/header.rs index 1260c9c9e..0c6a9d169 100644 --- a/crates/ui-egui/src/header.rs +++ b/crates/ui-egui/src/header.rs @@ -82,6 +82,15 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { { app.ui.status = e; } + // Double-click a shape tool: a shape layer filling the composition, centred, for + // backgrounds, mattes and overlays (as After Effects does). + if cur.is_shape() + && resp.double_clicked() + && let Some((w, h)) = app.session.active_comp().map(|c| (c.width, c.height)) + && let Err(e) = crate::menus::invoke(app, ui.ctx(), "layer.newShape", json!({"kind": crate::panels::viewer::shape_kind(cur), "size": [w, h]})) + { + app.ui.status = e; + } if slot.len() > 1 { let held_for = resp.is_pointer_button_down_on().then(|| ui.input(|i| i.pointer.press_start_time().map(|s| i.time - s))).flatten(); if let Some(seconds) = held_for diff --git a/crates/ui-egui/src/panels/viewer.rs b/crates/ui-egui/src/panels/viewer.rs index 5fe7697c9..8410bb78c 100644 --- a/crates/ui-egui/src/panels/viewer.rs +++ b/crates/ui-egui/src/panels/viewer.rs @@ -2138,7 +2138,7 @@ struct ShapeDrag { } /// The kind `tool` draws, as `shape.newShape` / `layer.addMask` name it. -fn shape_kind(tool: Tool) -> &'static str { +pub(crate) fn shape_kind(tool: Tool) -> &'static str { match tool { Tool::Rectangle => "rect", Tool::RoundedRect => "rounded",