diff --git a/crates/engine/src/cmd/gradient.rs b/crates/engine/src/cmd/gradient.rs index c73a23d6c..8a3485c49 100644 --- a/crates/engine/src/cmd/gradient.rs +++ b/crates/engine/src/cmd/gradient.rs @@ -80,19 +80,28 @@ fn select_stop(s: &mut Session, p: &Value) -> Result { } /// Whose gradient a selected stop belongs to: the active document, its first selected object -/// (None: the default paint), the proxy in front and the Appearance panel's active item. -/// Selecting other art, toggling the proxy or picking another item leaves no stop selected. +/// (None: the default paint), whether a part of a symbol instance's art is picked, the proxy in +/// front and the Appearance panel's active item. Selecting other art, picking a part of the +/// instance, toggling the proxy or picking another item leaves no stop selected. #[derive(Clone, Copy, Debug, PartialEq)] pub(crate) struct StopOwner { doc: Option, object: Option, + part: bool, fill: bool, item: Option, } impl StopOwner { pub(crate) fn of(s: &Session) -> Self { - Self { doc: s.active, object: s.active().and_then(|d| d.selection.objects.first().copied()), fill: s.fill_active, item: s.appearance_item() } + let st = s.active(); + Self { + doc: s.active, + object: st.and_then(|d| d.selection.objects.first().copied()), + part: st.is_some_and(|d| d.selection.symbol_part.is_some()), + fill: s.fill_active, + item: s.appearance_item(), + } } } diff --git a/crates/engine/src/tests/dynamic_symbols.rs b/crates/engine/src/tests/dynamic_symbols.rs index eab2f3067..f4ae97bb1 100644 --- a/crates/engine/src/tests/dynamic_symbols.rs +++ b/crates/engine/src/tests/dynamic_symbols.rs @@ -903,6 +903,29 @@ fn gradients_and_patterns_on_a_picked_object_are_taken() { assert_eq!((undo_steps(&s), &s.paint.fill), (undo, &fill), "nothing changes"); } +/// A stop selected on the whole instance's gradient is not selected once a part of its art is +/// picked, so the Gradient tool does not take Delete for it, and is selected again when the whole +/// instance is. +#[test] +fn picking_a_part_leaves_no_gradient_stop_selected() { + let mut s = session(); + let inst = pair(&mut s, "dynamic"); + let g = json!({"kind": "linear", "stops": [{"offset": 0, "color": "#ff0000"}, {"offset": 1, "color": "#0000ff"}]}); + s.execute("select.set", &json!({"ids": [inst.0]})).unwrap(); + s.execute("paint.setFill", &json!({"gradient": g})).unwrap(); + s.fill_active = true; + let view = ViewInfo::default(); + s.select_tool("gradient", view).unwrap(); + s.execute("gradient.selectStop", &json!({"index": 1})).unwrap(); + assert_eq!(s.selected_stop(), Some(1)); + assert!(s.tool_claims_key(vectorcraft_tools::ToolKey::Delete, view)); + s.execute("symbol.instance.selectPart", &json!({"id": inst.0, "path": [1]})).unwrap(); + assert_eq!(s.selected_stop(), None); + assert!(!s.tool_claims_key(vectorcraft_tools::ToolKey::Delete, view)); + s.execute("select.set", &json!({"ids": [inst.0]})).unwrap(); + assert_eq!(s.selected_stop(), Some(1)); +} + #[test] fn opacity_and_blend_on_a_picked_object_go_through_transparency_set() { let mut s = session();