From 7e3594b822e5ba61218c70bc5ae8bf930de0cb4a Mon Sep 17 00:00:00 2001 From: Kushagra Sharma <56391795+drunkeninja42@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:38:02 -0500 Subject: [PATCH] fix(ui-egui): a click on the bottom layer's Smart Filters selects them again (#8764) (#8974) * fix(ui-egui): a click on the bottom layer's Smart Filters selects them again (#8764) The empty space below the last Layers panel row deselects every layer on a click (#8100). It started under the last layer's own row, so it covered that layer's effect and Smart Filter rows and, registered after them, took their clicks: on the bottom smart object (a new document's Layer 0), clicking a filter deselected everything instead of selecting it. The click area now starts under everything drawn for the last layer. The drop zone below it (#1721) is unchanged. * Contributors: credit drunkeninja42 as Kushagra Sharma --- contributors/people.toml | 3 +++ crates/ui-egui/src/panels.rs | 9 ++++++-- crates/ui-egui/src/smart_filter_rows_tests.rs | 22 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/contributors/people.toml b/contributors/people.toml index 30077a230f..4afc831d48 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -251,3 +251,6 @@ sync_github_name = true [people.benandez] real_name = "Martin Ball" + +[people.drunkeninja42] +real_name = "Kushagra Sharma" diff --git a/crates/ui-egui/src/panels.rs b/crates/ui-egui/src/panels.rs index b316669258..4dbec87cdc 100644 --- a/crates/ui-egui/src/panels.rs +++ b/crates/ui-egui/src/panels.rs @@ -2838,6 +2838,9 @@ fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) { crate::smart_ui::filter_rows(app, ui, &doc, l, depth, &mut actions); } } + // The bottom of everything drawn: the last layer's effect and Smart Filter rows sit + // under its own row (#8764). + let rows_bottom = ui.min_rect().bottom(); // #1721: the list takes drops everywhere, not only on a row (Photoshop): the space // between two rows lands above the lower one, the empty space below the bottom-most // row below it. Both copy with ⌥ held, move without. @@ -2848,8 +2851,10 @@ fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) { if let Some(&(last, last_rect)) = drawn.last() { let below = Rect::from_min_max(pos2(last_rect.left(), last_rect.bottom()), pos2(last_rect.right(), ui.clip_rect().bottom())); layer_drop_zone(app, &ctx, ui, below, (last, last_rect), "below", &mut actions); - // A click on that empty space deselects every layer, as in Photoshop (#8100). - if below.height() > 0.0 && ui.interact(below, ui.id().with("layers-empty-space"), Sense::click()).clicked() && !selection.is_empty() { + // A click on that empty space deselects every layer, as in Photoshop (#8100). It + // starts under the last layer's sub-rows, which take their own clicks. + let empty = Rect::from_min_max(pos2(below.left(), rows_bottom), below.max); + if empty.height() > 0.0 && ui.interact(empty, ui.id().with("layers-empty-space"), Sense::click()).clicked() && !selection.is_empty() { actions.push(("select.deselectLayers".into(), json!({}))); } } diff --git a/crates/ui-egui/src/smart_filter_rows_tests.rs b/crates/ui-egui/src/smart_filter_rows_tests.rs index d05e02e014..7457b0c780 100644 --- a/crates/ui-egui/src/smart_filter_rows_tests.rs +++ b/crates/ui-egui/src/smart_filter_rows_tests.rs @@ -317,3 +317,25 @@ fn with_the_layers_panel_closed_delete_leaves_the_filter() { h.run_steps(4); assert!(h.state().session.active().unwrap().doc.layer(LayerId(a)).is_none(), "Delete reached Smart A itself"); } + +/// #8764: the bottom layer's filter rows sit where the empty space below the last row begins, so +/// that space's deselect-all click (#8100) must start under them: a click on a filter of a new +/// document's only layer, converted for Smart Filters, selects the filter. +#[test] +fn a_click_selects_a_filter_of_the_bottom_layer() { + let mut s = photocraft_engine::Session::new(); + s.execute("file.new", json!({"width": 16, "height": 16})).unwrap(); + let id = s.execute("filter.convertForSmartFilters", json!({})).unwrap()["layer"].as_u64().unwrap(); + s.execute("filter.blur.gaussianBlur", json!({"radius": 1})).unwrap(); + let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1200.0, 900.0)).with_max_steps(64).with_step_dt(1.0 / 60.0).build_eframe(|cc| { + PhotocraftApp::setup_context(&cc.egui_ctx, Default::default()); + PhotocraftApp::new(s, crate::Services::default()) + }); + h.run_steps(4); + let blur = at(&h, "Gaussian Blur"); + click_at(&mut h, blur); + let st = h.state().session.active().unwrap(); + assert_eq!(st.active_layer, Some(LayerId(id)), "the smart object stays active"); + assert_eq!(st.selected_layers(), [LayerId(id)], "the click did not deselect the layers"); + assert!(selected(&h, "Gaussian Blur")); +}