Merge PR #8496: fix(ui-egui): expand layer visibility eye hitbox and render sunken well (#8467)

This commit is contained in:
David Mann committed 2026-10-22 23:40:16 +02:00
commit 79ab013961
2 files changed
+68 -5

No files matched your search

+64
View File
@@ -658,3 +658,67 @@ fn clicking_the_lock_icon_on_a_row_unlocks_the_layer() {
assert!(doc.background().is_none(), "the Background became a normal layer");
assert!(!doc.layer(bg).unwrap().locks.any());
}
#[test]
fn eye_column_edges_toggle_visibility_without_selecting_layer() {
// #8467: clicks at the far left or right edge of the eye column must toggle visibility
// rather than falling through to select the row.
let mut s = photocraft_engine::Session::new();
s.execute("file.new", json!({"width": 64, "height": 48})).unwrap();
let l1 = s.execute("layer.new.layer", json!({"name": "Top"})).unwrap()["layer"].as_u64().unwrap();
let l0 = s.execute("layer.new.layer", json!({"name": "Bottom"})).unwrap()["layer"].as_u64().unwrap();
s.execute("layer.select", json!({"layer": l0})).unwrap();
let mut h = harness(s, 1.0, "promedium", 290.0);
let visible = |h: &Harness<'_, PhotocraftApp>, id: u64| h.state().session.active().unwrap().doc.layer(photocraft_doc::LayerId(id)).unwrap().visible;
let selected = |h: &Harness<'_, PhotocraftApp>| h.state().session.active().unwrap().active_layer.unwrap().0;
let row_for = |h: &Harness<'_, PhotocraftApp>, id: u64| recorded(&h.ctx).into_iter().find(|r| r.layer == id).unwrap().row;
let top_row = row_for(&h, l1);
// Click at the far left edge of the eye column (left + 1.0, top + 1.0)
let left_edge = pos2(top_row.left() + 1.0, top_row.top() + 1.0);
click(&mut h, left_edge);
assert!(!visible(&h, l1), "click on left edge of eye column should hide layer");
assert_eq!(selected(&h), l0, "click on eye column must not select the layer");
// Click at the right edge of the eye column (left + 29.0, bottom - 1.0)
let right_edge = pos2(top_row.left() + 29.0, top_row.bottom() - 1.0);
click(&mut h, right_edge);
assert!(visible(&h, l1), "click on right edge of eye column should show layer again");
assert_eq!(selected(&h), l0, "click on eye column must not select the layer");
}
/// #1662: a hidden layer's eye box stays empty, as in Photoshop (only a visible layer paints its
/// eye), in every theme; the widened hit area (#8467) changes nothing that is drawn.
#[test]
fn a_hidden_layers_eye_box_paints_nothing() {
fn bounds(shape: &egui::Shape, out: &mut Vec<Rect>) {
match shape {
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| bounds(s, out)),
s => out.push(s.visual_bounding_rect()),
}
}
for theme in ["promedium", "pro", "studio", "studiolight", "classic"] {
let mut s = photocraft_engine::Session::new();
s.execute("file.new", json!({"width": 64, "height": 48})).unwrap();
let hidden = s.execute("layer.new.layer", json!({"name": "Hidden"})).unwrap()["layer"].as_u64().unwrap();
s.execute("layer.setProps", json!({"layer": hidden, "visible": false})).unwrap();
let shown = s.execute("layer.new.layer", json!({"name": "Shown"})).unwrap()["layer"].as_u64().unwrap();
let h = harness(s, 1.0, theme, 290.0);
let mut drawn = Vec::new();
for clipped in &h.output().shapes {
bounds(&clipped.shape, &mut drawn);
}
let rows = recorded(&h.ctx);
let in_eye_box = |id: u64| {
let row = rows.iter().find(|r| r.layer == id).unwrap().row;
let cell = Rect::from_min_max(row.left_top(), pos2(row.left() + 30.0, row.bottom())).shrink(1.0);
drawn.iter().filter(|b| b.is_positive() && cell.contains_rect(**b)).count()
};
assert!(in_eye_box(shown) > 0, "{theme}: a visible layer paints its eye");
assert_eq!(in_eye_box(hidden), 0, "{theme}: a hidden layer's eye box is left empty");
}
}
+4 -5
View File
@@ -3143,11 +3143,10 @@ fn layer_row(
painter.line_segment([rect.left_bottom(), rect.right_bottom()], Stroke::new(1.0, t.separator));
}
let mut x = rect.left() + 6.0;
let eye = Rect::from_min_size(pos2(x, rect.center().y - 11.0), vec2(22.0, 22.0));
// The eye takes drags too (so a drag starting on it never reorders the row): dragging down
// the eyes gives every row swept over the visibility the first eye toggled to.
let eye_resp = ui.interact(eye, ui.id().with(("eye", l.id.0)), Sense::click_and_drag());
// A hidden layer's eye box is left empty (still clickable).
// The whole eye column takes drags too (so a drag starting on it never reorders the row): dragging down
// the eyes gives every row swept over the visibility the first eye toggled to (#8467).
let eye_resp = ui.interact(eye_cell, ui.id().with(("eye", l.id.0)), Sense::click_and_drag());
// A hidden layer's eye box is left empty (still clickable; the whole column takes the click, #8467).
if l.visible {
// Centre the drawing in the column while preserving the visibility hit area.
icons::paint(ui, eye_cell, "eye", 15.0, t.layer_label_icon(l.label));