From 626ef7dd0eabd6355fa0807a2de1f186a3153a81 Mon Sep 17 00:00:00 2001 From: FarmerGeorg Date: Sun, 25 Oct 2026 18:24:33 +0100 Subject: [PATCH] Appearance panel: a drop on Delete keeps the active effect on its row When an effect that isn't highlighted is dropped on Delete above the active one, the active effect, the highlight and its anchor now move up with their rows, and removed rows drop out of the highlight. Before, they kept their old row numbers and pointed at the wrong effects. --- crates/ui-egui/src/panels/appearance.rs | 19 ++++++++++++++++ crates/ui-egui/src/panels/tests_appearance.rs | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/crates/ui-egui/src/panels/appearance.rs b/crates/ui-egui/src/panels/appearance.rs index 346449c9d..42137927d 100644 --- a/crates/ui-egui/src/panels/appearance.rs +++ b/crates/ui-egui/src/panels/appearance.rs @@ -1456,10 +1456,29 @@ fn drop_on_buttons(app: &mut VectorcraftApp, ui: &Ui, node: Option<&Node>, sel: && let Dragged::Effect(item, _) = what { select_row(app, ctx, item.map_or(Sel::None, Sel::Item)); + } else if removed && let Dragged::Effect(item, _) = what { + let gone: Vec = gone.iter().map(|k| *k as usize).collect(); + highlight_after_removal(app, ctx, n, sel, Rows::Effects(item), &gone); } } } +/// After the rows `gone` of kind `rows` were removed while the active row survived, the active row, +/// the highlight and its anchor move up past the removed rows below them; removed rows drop out of +/// the highlight. `n` is the object as it was before the removal. +fn highlight_after_removal(app: &mut VectorcraftApp, ctx: &egui::Context, n: &Node, sel: Sel, rows: Rows, gone: &[usize]) { + let h = highlight(ctx, n, sel); + if h.rows != rows { + return; + } + let shift = |k: usize| (!gone.contains(&k)).then(|| k - gone.iter().filter(|g| **g < k).count()); + let order = h.order.iter().filter_map(|k| shift(*k)).collect(); + let anchor = h.anchor.and_then(shift); + let active = sel.rows().filter(|(r, _)| *r == rows).and_then(|(_, a)| shift(a)); + set_active(app, ctx, active.map_or(Sel::None, |a| rows.sel(a))); + set_pstate(ctx, "ap-multi", Highlight { owner: Some(n.id), rows, order, anchor }); +} + /// The command a stack row `what` dropped on Duplicate Selected Item (`copy`) or Delete Selected /// Item runs: a fill/stroke row acts on the highlighted rows `items` when it is one of them, an /// effect row on the highlighted `effects` (list, positions) when it is one of them, else either on diff --git a/crates/ui-egui/src/panels/tests_appearance.rs b/crates/ui-egui/src/panels/tests_appearance.rs index a9415a591..4abb4be5d 100644 --- a/crates/ui-egui/src/panels/tests_appearance.rs +++ b/crates/ui-egui/src/panels/tests_appearance.rs @@ -1406,6 +1406,28 @@ fn a_drop_on_delete_that_removes_the_active_effect_leaves_no_effect_active() { assert_eq!(object_effects(&app), ["distort.twist"], "Twist was never highlighted"); } +/// Dragging an effect that isn't highlighted onto Delete removes only it: the highlighted effects +/// below it move up with their rows, so they stay highlighted and active, and Delete Selected Item +/// then removes them. +#[test] +fn a_drop_on_delete_above_the_active_effect_keeps_the_highlight_on_its_rows() { + let mut app = rect_with_three_effects(); + let ctx = drop_ctx(); + let delete = panel_button(&ctx, &mut app, "Delete Selected Item"); + let (glow, shadow, twist) = + (row_point(&ctx, &mut app, "Outer Glow", 0), row_point(&ctx, &mut app, "Drop Shadow", 0), row_point(&ctx, &mut app, "Twist", 0)); + // Drop Shadow, then Twist with Ctrl: both highlighted, Twist active. + click_mod(&ctx, &mut app, shadow, egui::Modifiers::NONE); + click_mod(&ctx, &mut app, twist, egui::Modifiers::COMMAND); + drag_in_panel(&ctx, &mut app, glow, delete); + assert_eq!(object_effects(&app), ["stylize.dropShadow", "distort.twist"]); + let lit = lit_texts(&ctx, &mut app); + assert!(["Drop Shadow", "Twist"].iter().all(|t| lit.iter().any(|l| l.contains(t))), "still highlighted: {lit:?}"); + let delete = panel_button(&ctx, &mut app, "Delete Selected Item"); + click(&ctx, &mut app, delete, appearance::show); + assert!(object_effects(&app).is_empty(), "both highlighted effects removed: {:?}", object_effects(&app)); +} + /// Whether Duplicate Selected Item and Delete Selected Item are enabled in a frame of the panel /// with input `events`. fn drop_buttons_on(ctx: &egui::Context, app: &mut VectorcraftApp, events: Vec) -> [bool; 2] {