mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 20:58:00 -06:00
Merge pull request #4642 from mockydevs/fix/task-bar-direct-selection-4469
Fix #4469: Direct Selection's task bar offers the path's anchor actions
This commit is contained in:
@@ -93,6 +93,10 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) {
|
||||
// (<https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html>).
|
||||
// These run the same commands as the Control bar's anchor buttons.
|
||||
let anchors = !st.selection.anchors.is_empty();
|
||||
// With the Direct Selection tool, a path selected whole offers its anchor points' actions
|
||||
// too, as the reference app's bar does for any path that tool selects (same page), and Edit
|
||||
// Path, which only switches to that tool, is left out (#4469).
|
||||
let direct = app.session.tool_id() == "directSelection";
|
||||
if anchors {
|
||||
items.push((tl!("Remove Anchor Points"), "pen-tool-delete", "path.removeAnchors", none()));
|
||||
items.push((tl!("Connect"), "dc-join", "path.join", none()));
|
||||
@@ -122,7 +126,9 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) {
|
||||
items.push((tl!("Isolate"), "square-dashed", "object.isolate", none()));
|
||||
}
|
||||
} else if is_compound {
|
||||
items.push(edit_path());
|
||||
if !direct {
|
||||
items.push(edit_path());
|
||||
}
|
||||
items.push((tl!("Release Compound Path"), "ungroup", "object.compoundPath.release", none()));
|
||||
} else if let Some(kind) = text {
|
||||
items.push((tl!("Create Outlines"), "type", "type.createOutlines", none()));
|
||||
@@ -134,6 +140,15 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) {
|
||||
}
|
||||
} else if is_image {
|
||||
// Offset Path and Simplify don't apply to an image.
|
||||
} else if !anchors && direct {
|
||||
// Corner and Smooth convert every anchor of a path selected whole; Remove and Cut
|
||||
// need anchors picked out, so they wait for them.
|
||||
if open_path {
|
||||
items.push((tl!("Connect"), "dc-join", "path.join", none()));
|
||||
}
|
||||
items.push((tl!("Corner"), "dc-anchor", TASK_BAR_TO_CORNER, none()));
|
||||
items.push((tl!("Smooth"), "dc-anchor-smooth", TASK_BAR_TO_SMOOTH, none()));
|
||||
items.push((tl!("Simplify"), "spline", "object.path.simplify", none()));
|
||||
} else if !anchors {
|
||||
if open_path {
|
||||
items.push((tl!("Join"), "dc-join", "path.join", none()));
|
||||
|
||||
@@ -163,3 +163,74 @@ fn type_converts_between_point_and_area_type() {
|
||||
click(&mut app, "Convert To Point Type");
|
||||
assert!(matches!(text_kind(&app), TextKind::Point));
|
||||
}
|
||||
|
||||
/// The path's anchors (its first subpath).
|
||||
fn anchors(app: &VectorcraftApp, id: u64) -> Vec<vectorcraft_geom::Anchor> {
|
||||
match kind(app, id) {
|
||||
NodeKind::Path { path, .. } => path.subpaths[0].anchors.clone(),
|
||||
k => panic!("not a path: {k:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// With the Direct Selection tool, a path selected whole offers its anchor points' actions instead
|
||||
/// of Edit Path and Offset Path (#4469), as after the bar's own Edit Path.
|
||||
#[test]
|
||||
fn direct_selection_of_a_whole_path_offers_its_anchor_actions() {
|
||||
let mut app = app();
|
||||
let line = id(app.run("path.create", json!({"anchors": [{"x": 50, "y": 200}, {"x": 150, "y": 80}, {"x": 250, "y": 200}]})).unwrap());
|
||||
select(&mut app, &[line]);
|
||||
click(&mut app, "Edit Path");
|
||||
assert_eq!(app.session.tool_id(), "directSelection");
|
||||
assert!(app.session.active().unwrap().selection.anchors.is_empty(), "the path is selected whole");
|
||||
let (_, texts) = bar(&mut app);
|
||||
for label in ["Connect", "Corner", "Smooth", "Simplify", "Duplicate"] {
|
||||
assert!(has(&texts, label), "{label}: {texts:?}");
|
||||
}
|
||||
for label in ["Edit Path", "Offset Path", "Join", "Remove Anchor Points", "Cut Path"] {
|
||||
assert!(!has(&texts, label), "{label}: {texts:?}");
|
||||
}
|
||||
// Smooth and Corner convert every anchor of the path, one undo step each.
|
||||
click(&mut app, "Smooth");
|
||||
let a = anchors(&app, line);
|
||||
assert_eq!(a[1].kind, vectorcraft_geom::AnchorKind::Smooth, "{a:?}");
|
||||
assert!(a[1].has_in() && a[1].has_out(), "{a:?}");
|
||||
click(&mut app, "Corner");
|
||||
assert!(anchors(&app, line).iter().all(|a| !a.has_in() && !a.has_out()), "{:?}", anchors(&app, line));
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert!(anchors(&app, line)[1].has_in(), "Corner undoes in one step");
|
||||
// Connect closes the open path; a closed one isn't offered it.
|
||||
click(&mut app, "Connect");
|
||||
assert!(matches!(kind(&app, line), NodeKind::Path { path, .. } if path.is_closed()));
|
||||
let (_, texts) = bar(&mut app);
|
||||
assert!(!has(&texts, "Connect") && has(&texts, "Corner"), "{texts:?}");
|
||||
}
|
||||
|
||||
/// Anchors picked out with the Direct Selection tool keep the full set: Remove and Cut need them.
|
||||
#[test]
|
||||
fn direct_selected_anchors_still_offer_remove_and_cut() {
|
||||
let mut app = app();
|
||||
let line = id(app.run("path.create", json!({"anchors": [{"x": 50, "y": 200}, {"x": 150, "y": 80}, {"x": 250, "y": 200}]})).unwrap());
|
||||
app.run("tool.select", json!({"tool": "directSelection"})).unwrap();
|
||||
app.run("select.anchors", json!({"id": line, "anchors": [[0, 1]]})).unwrap();
|
||||
let (_, texts) = bar(&mut app);
|
||||
for label in ["Remove Anchor Points", "Connect", "Cut Path", "Corner", "Smooth"] {
|
||||
assert!(has(&texts, label), "{label}: {texts:?}");
|
||||
}
|
||||
assert!(!has(&texts, "Edit Path"), "{texts:?}");
|
||||
}
|
||||
|
||||
/// A compound path under the Direct Selection tool isn't offered Edit Path, which would only switch
|
||||
/// to the tool already in use; the Selection tool still offers it.
|
||||
#[test]
|
||||
fn a_compound_path_under_direct_selection_drops_edit_path() {
|
||||
let mut app = app();
|
||||
let a = id(app.run("shape.rectangle", json!({"x": 40, "y": 40, "width": 120, "height": 100})).unwrap());
|
||||
let b = id(app.run("shape.ellipse", json!({"x": 70, "y": 60, "width": 50, "height": 50})).unwrap());
|
||||
select(&mut app, &[a, b]);
|
||||
let c = id(app.run("object.compoundPath.make", json!({})).unwrap());
|
||||
click(&mut app, "Edit Path");
|
||||
assert_eq!(app.session.tool_id(), "directSelection");
|
||||
select(&mut app, &[c]);
|
||||
let (_, texts) = bar(&mut app);
|
||||
assert!(has(&texts, "Release Compound Path") && !has(&texts, "Edit Path"), "{texts:?}");
|
||||
}
|
||||
|
||||
@@ -362,6 +362,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | Character panel menu › Show Snap to Glyph Options shows the Snap to Glyph section (Baseline, x-height, Glyph Bounds, Proximity Guides, Angular Guides, Anchor Point; `view.snapToGlyph.options`) (#4232); scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Contextual Task Bar: with the Direct Selection tool, a path selected whole offers Corner, Smooth (every anchor), Connect (open paths) and Simplify instead of Edit Path and Offset Path, as the public overview lists for that tool (<https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html>); a compound path under that tool no longer offers Edit Path (#4469). Tests in `crates/ui-egui/src/tests/taskbar.rs`; scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Align panel menu › Align to Glyph Bounds › Point Text and Area Text align and distribute type by the ink of its glyphs (#4231); the Character and Properties panels' controls for it are not done; scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Art moved whole with the Direct Selection or Group Selection tool and dragged off the canvas goes to the panels (Asset Export, Graphic Styles, Swatches, Symbols) as with the Selection tool; anchor points and segments dragged off stay a reshape (#4233). Tests in `crates/ui-egui/src/canvas/tests.rs`; scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Blob Brush and Eraser Tool Options: Size follows the pen pressure, Fixed or Pressure with a Variation (#4590, #3961); scores and estimates unchanged |
|
||||
|
||||
Reference in New Issue
Block a user