#1284: double-clicking a shape tool adds a comp-sized shape layer

Double-click a shape tool in the toolbar to add a shape layer the size of the composition, at
its centre, for backgrounds, mattes and overlays, as After Effects does. It uses layer.newShape
with the tool's kind, so the undo label and the command are the same as drawing one.

Covered by tests::a_comp_sized_shape_layer_sits_on_the_comp_centre; the double-click itself is
not covered by an automated test.

Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
This commit is contained in:
Claude committed 2026-10-13 19:28:22 +00:00
1 parent a7cad22c13
commit 84589dc288
3 files changed
+22 -1

No files matched your search

+12
View File
@@ -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.
+9
View File
@@ -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
+1 -1
View File
@@ -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",