mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
#1220: Hide layer bounds during path editing
This commit is contained in:
@@ -1028,6 +1028,14 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Path editing keeps vertices and tangents visible without the layer's transform
|
||||
// box competing with them (#1220). The path's own free-transform box is separate.
|
||||
let editing_path = paths.iter().any(|p| {
|
||||
p.layer == l.id && (app.ui.tool.is_pen() || (app.ui.tool == Tool::Selection && sel_vertices.iter().any(|v| v.layer == l.id && v.mask == p.uid)))
|
||||
});
|
||||
if editing_path {
|
||||
continue;
|
||||
}
|
||||
let Some((m, q, _)) = layer_quad(&ectx, l) else {
|
||||
// A 3D layer reaching past the camera: the part of its box in front of it.
|
||||
let outline = layer_outline(&ectx, l);
|
||||
|
||||
@@ -677,6 +677,103 @@ fn fill_options_change_the_selected_shape_layer() {
|
||||
}
|
||||
}
|
||||
|
||||
/// #1220: path tools hide the layer transform box, while path vertices remain editable.
|
||||
#[test]
|
||||
fn path_editing_hides_layer_bounds_and_keeps_corner_vertices_editable() {
|
||||
for mask in [false, true] {
|
||||
let mut h = harness();
|
||||
let vertices = [[200.0, 100.0], [400.0, 100.0], [400.0, 260.0], [200.0, 260.0]];
|
||||
let (layer, uid, kind) = if mask {
|
||||
let layer = layer_id(&h, "Plate");
|
||||
h.state_mut().session.execute("layer.select", json!({"layers": [layer]})).unwrap();
|
||||
let r = h
|
||||
.state_mut()
|
||||
.session
|
||||
.execute("mask.new", json!({"layer": layer, "vertices": vertices, "outTangents": [[40, 0], [0, 0], [0, 0], [0, 0]], "closed": true}))
|
||||
.unwrap();
|
||||
(layer, r["mask"].as_u64().unwrap(), "mask")
|
||||
} else {
|
||||
let r = h.state_mut().session.execute("shape.newPath", json!({"vertices": vertices, "outTangents": [[40, 0], [0, 0], [0, 0], [0, 0]], "closed": true, "fill": false, "stroke": "#ffffff", "strokeWidth": 4})).unwrap();
|
||||
(r["layer"].as_u64().unwrap(), r["path"].as_u64().unwrap(), "shapePath")
|
||||
};
|
||||
h.state_mut().session.state.selected_vertices.clear();
|
||||
h.run_steps(2);
|
||||
let handle = format!("viewer.handle.{layer}.0");
|
||||
assert!(h.state().auto.find(&handle).is_some(), "ordinary layer selection has transform handles (mask={mask})");
|
||||
for tool in [Tool::Pen, Tool::PenAdd, Tool::PenDelete, Tool::PenConvert, Tool::MaskFeather] {
|
||||
h.state_mut().ui.tool = tool;
|
||||
h.run_steps(2);
|
||||
assert!(h.state().auto.find(&handle).is_none(), "{tool:?}: hide the layer box");
|
||||
assert!(h.state().auto.find(&format!("viewer.{kind}.{uid}.vertex.0")).is_some(), "{tool:?}: keep the path point");
|
||||
}
|
||||
h.state_mut().ui.tool = Tool::Selection;
|
||||
h.run_steps(2);
|
||||
let vertex = format!("viewer.{kind}.{uid}.vertex.0");
|
||||
let from = rect(&h, &vertex).center();
|
||||
click(&mut h, from);
|
||||
h.run_steps(2); // The automation registry also keeps the preceding frame's elements.
|
||||
assert!(h.state().auto.find(&handle).is_none(), "selecting a vertex starts path editing (mask={mask})");
|
||||
let position = h.state().session.active_comp().unwrap().layer(LayerId(layer)).unwrap().props.prop("transform/position").unwrap().value.clone();
|
||||
let path = |h: &Harness<'_, EffectcraftApp>| {
|
||||
h.state()
|
||||
.session
|
||||
.active_comp()
|
||||
.unwrap()
|
||||
.layer(LayerId(layer))
|
||||
.unwrap()
|
||||
.props
|
||||
.find_group(uid)
|
||||
.unwrap()
|
||||
.get("path")
|
||||
.unwrap()
|
||||
.value
|
||||
.as_path()
|
||||
.unwrap()
|
||||
.clone()
|
||||
};
|
||||
let before = path(&h);
|
||||
drag(&mut h, from, from + vec2(15.0, 12.0));
|
||||
assert_ne!(path(&h).vertices[0], before.vertices[0], "the corner vertex can still be moved");
|
||||
assert_eq!(path(&h).vertices[1..], before.vertices[1..]);
|
||||
assert_eq!(h.state().session.active_comp().unwrap().layer(LayerId(layer)).unwrap().props.prop("transform/position").unwrap().value, position);
|
||||
h.state_mut().session.execute("edit.undo", json!({})).unwrap();
|
||||
h.run_steps(2);
|
||||
assert_eq!(path(&h), before, "moving the vertex undoes in one step");
|
||||
let z = rect(&h, "viewer.comp").width() / 640.0;
|
||||
let tangent = rect(&h, &vertex).center() + vec2(40.0 * z, 0.0);
|
||||
drag(&mut h, tangent, tangent + vec2(0.0, 20.0));
|
||||
assert_ne!(path(&h).out_tangents[0], before.out_tangents[0], "the Bézier handle is still editable");
|
||||
assert_eq!(path(&h).vertices, before.vertices, "a tangent drag does not move the path points");
|
||||
h.state_mut().session.execute("edit.undo", json!({})).unwrap();
|
||||
h.run_steps(2);
|
||||
assert_eq!(path(&h), before, "the tangent drag undoes in one step");
|
||||
h.state_mut().ui.tool = Tool::Rotate;
|
||||
h.run_steps(2);
|
||||
assert!(h.state().auto.find(&handle).is_some(), "a layer transform tool restores the box");
|
||||
h.state_mut().ui.tool = Tool::Selection;
|
||||
h.state_mut().session.state.selected_vertices.clear();
|
||||
h.run_steps(2);
|
||||
assert!(h.state().auto.find(&handle).is_some(), "ending vertex selection restores the box");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_tools_keep_bounds_on_layers_without_visible_paths() {
|
||||
let mut h = harness();
|
||||
let layer = layer_id(&h, "Box");
|
||||
h.state_mut().session.execute("layer.select", json!({"layers": [layer]})).unwrap();
|
||||
h.state_mut().ui.tool = Tool::Pen;
|
||||
h.run_steps(2);
|
||||
let handle = format!("viewer.handle.{layer}.0");
|
||||
assert!(h.state().auto.find(&handle).is_some(), "a layer without a path keeps its box");
|
||||
h.state_mut().session.execute("mask.new", json!({"layer": layer, "vertices": [[0, 0], [80, 0], [80, 80]], "closed": true})).unwrap();
|
||||
h.run_steps(2);
|
||||
assert!(h.state().auto.find(&handle).is_none());
|
||||
h.state_mut().ui.viewer.show_masks = false;
|
||||
h.run_steps(2);
|
||||
assert!(h.state().auto.find(&handle).is_some(), "hidden paths do not suppress the layer controls");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shape_pen_draws_a_closed_shape_layer() {
|
||||
let mut h = harness();
|
||||
|
||||
+5
-1
@@ -1,6 +1,6 @@
|
||||
# Where EffectCraft falls short
|
||||
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (issue, PR and catalog references gap 21: percentage guides and effect colour labels in; earlier change: re-audited against `upstream/main` 470ea7a3; ranked gap list from the 10 October re-measure otherwise unchanged) · **Target:** Adobe After Effects 2026 (26.5.0.89)
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-13 · **Change:** minor (path-editing layer bounds, #1220; issue, PR and catalog references gap 21: percentage guides and effect colour labels in; earlier change: re-audited against `upstream/main` 470ea7a3; ranked gap list from the 10 October re-measure otherwise unchanged) · **Target:** Adobe After Effects 2026 (26.5.0.89)
|
||||
|
||||
An honest assessment of how far EffectCraft is from being a real replacement for After Effects,
|
||||
and the work that closes the gap. This document is meant for contributors and agents choosing
|
||||
@@ -114,6 +114,9 @@ The most important missing piece: it turns every other estimate here into a meas
|
||||
- Run the headless snapshot and control-channel checks on Linux (X11 and Wayland) and Windows, not
|
||||
only macOS. Interactions that only fail with real input (docking drags, viewer pan, drag-and-drop
|
||||
import, inline rename) need scripted input tests.
|
||||
- Path editing (#1220): selected shape and mask paths suppress the layer transform box while
|
||||
their points are edited. `ui_viewer` regressions cover Pen-family tools, Selection vertex
|
||||
dragging and undo, restoration of layer controls, and layers without visible paths.
|
||||
- Regression evidence for [#66](https://github.com/storytold/effectcraft/issues/66): scripted
|
||||
pointer tests in `ui_viewer` and `ui_text_edit`, checked headlessly on Linux, cover viewer clicks,
|
||||
marquee selection and text picking when layers are hidden or excluded by solo. They also check
|
||||
@@ -322,6 +325,7 @@ Issues #41–#47 (all from the Linux AppImage 0.1.1, 5 October): panning the vie
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-13 | patch | G2: layer bounds no longer compete with editable shape/mask path points (#1220) |
|
||||
| 2026-10-12 | minor | Audited against `upstream/main` 470ea7a3: open-issue count and crash list, closed issues removed from rows 1, 3, 5, 6, 8–12, 15–17, 22–24, G8 and G9 (#759 `#include`), localization catalogs recounted (≈ 12% to ≈ 18%); rows 7, 14 and 21 left to open PRs #1102, #792 and #1098 |
|
||||
| 2026-10-10 | minor | Alpha gate checked: no alpha blockers |
|
||||
| 2026-10-12 | minor | Gap 21: percentage guides and effect colour labels added (#1095) |
|
||||
|
||||
+8
-1
@@ -1,6 +1,6 @@
|
||||
# UI parity
|
||||
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (closed issue references re-audited against `upstream/main` 470ea7a3) · **Target:** Adobe After Effects 2026
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-13 · **Change:** minor (path-editing layer bounds, #1220; closed issue references re-audited against `upstream/main` 470ea7a3) · **Target:** Adobe After Effects 2026
|
||||
|
||||
How closely EffectCraft's interaction matches After Effects: tools, handles, snapping, nudging,
|
||||
modifier keys, shortcuts, drag behaviour, numeric entry, panels, context menus and feel. Evidence
|
||||
@@ -33,6 +33,12 @@ feel and platform differences, and the 26.x interface changes are not in yet.
|
||||
| Accessibility | AccessKit enabled; focus labels tested; Project panel keyboard navigation and accessible names (#776) | 20% | No screen-reader pass or keyboard-navigation audit of the other panels; no contrast check |
|
||||
| UI scale | Settings ▸ Appearance ▸ UI Scale 75–200% | beyond | After Effects follows the system's scaling (#284) |
|
||||
|
||||
Path editing (#1220): the Composition viewer suppresses a selected layer’s transform box while
|
||||
using a Pen-family tool on a visible path, or while selecting its vertices with the Selection
|
||||
tool. Path points, Bézier handles and the path’s free-transform controls remain available.
|
||||
Layer transform tools and ordinary layer selection restore the box. Headless `ui_viewer` tests
|
||||
cover shape paths and masks, corner-vertex dragging and undo, and layers without visible paths.
|
||||
|
||||
## Estimate
|
||||
|
||||
**≈ 60–120 h**: the open interaction issues above (25–45 h, about 0.2–0.5 h each with a scripted
|
||||
@@ -44,6 +50,7 @@ Effects running; most of the rest parallelises.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-13 | patch | Layer bounds are suppressed during path-point editing (#1220); shape/mask interaction regressions |
|
||||
| 2026-10-12 | minor | Closed issues (#302, #320, #491, #511, #553 and others) marked as closed instead of open; panels in their own windows (#486) |
|
||||
| 2026-10-12 | minor | Percentage guides and effect colour labels (26.5) added; rows updated |
|
||||
| 2026-10-10 | major | First UI parity checklist |
|
||||
|
||||
Reference in New Issue
Block a user