Batch 18 (CodeRabbit): Layers empty-list menu under a filter; the pen's eraser end ends Add New Artboard

- #11500: a filter that matches no layer draws no row, so the empty-area
  right-click was never created; the whole empty list now takes it.
- #11532: flipping the pen to its eraser end is a tool switch, so Add New
  Artboard's placing mode ends as with any other switch.

Tests for both (the Layers one fails without the fix).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David MannandClaude Opus 5.5 committed 2026-11-06 03:13:29 +01:00
1 parent ea15b297f5
commit 55a2dffcaa
4 files changed
+42

No files matched your search

+2
View File
@@ -336,6 +336,8 @@ impl Stylus {
Some(true) if app.ui.tool != Tool::Eraser => {
app.stylus.tool_before_eraser = Some(app.ui.tool);
app.ui.tool = Tool::Eraser;
// A tool switch like any other: Add New Artboard's placing mode ends (#11386).
crate::tool_memory::ended_by_tool(app);
true
}
Some(false) => match app.stylus.tool_before_eraser.take() {
+19
View File
@@ -350,3 +350,22 @@ fn eraser_end_switches_tools_once_and_never_mid_drag() {
assert!(!Stylus::sync_eraser_tool(&mut app));
assert_eq!(app.ui.tool, Tool::Eraser);
}
/// The pen's eraser end is a tool switch like any other: Add New Artboard's placing mode ends
/// (#11386), and coming back to the Artboard tool on the tip leaves it off.
#[test]
fn the_eraser_end_ends_the_artboard_placing_mode() {
use crate::state::Tool;
let mut app = crate::PhotocraftApp::new(photocraft_engine::Session::new(), Default::default());
app.session.execute("file.new", serde_json::json!({"width": 60, "height": 60})).unwrap();
app.ui.tool = Tool::Artboard;
app.ui.artboard_tool.adding = true;
let pen = |eraser| Some(PenSample { pressure: 0.5, eraser, ..Default::default() });
app.stylus.feed.set(pen(true));
assert!(Stylus::sync_eraser_tool(&mut app));
assert!(!app.ui.artboard_tool.adding, "the eraser end ends the mode");
app.stylus.feed.set(pen(false));
assert!(Stylus::sync_eraser_tool(&mut app));
assert_eq!(app.ui.tool, Tool::Artboard);
assert!(!app.ui.artboard_tool.adding);
}
@@ -169,3 +169,16 @@ fn a_right_click_on_a_layer_row_still_opens_the_layer_menu() {
assert!(h.query_by_label("Duplicate Layer…").is_some() || h.query_by_label("Duplicate Layer").is_some(), "the layer menu opened");
assert!(h.query_by_label("No Thumbnails").is_none(), "not the empty-area menu");
}
/// A filter that matches no layer draws no row: the whole list is empty space, and its
/// right-click still opens the menu.
#[test]
fn with_no_row_shown_by_the_filter_the_right_click_still_opens_the_menu() {
let mut h = panel(true);
h.state_mut().ui.layer_filter_by = crate::layer_filter_ui::FilterBy::Name;
h.state_mut().ui.layer_filter_name = "no layer has this name".into();
h.run_steps(4);
assert!(h.query_by_label("Layer 1").is_none(), "the filter hides every row");
open_menu(&mut h);
assert!(menu_open(&h), "the empty list's right-click opens the menu");
}
+8
View File
@@ -322,6 +322,14 @@ pub(super) fn layers(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
// A right-click there opens the thumbnail size and clipping choices (#11404).
response.context_menu(|ui| super::layers_empty_menu::show(app, ui));
}
} else {
// No row drawn (a filter that matches no layer): the whole list is empty space,
// and its right-click opens the same menu.
let empty = Rect::from_min_max(pos2(ui.max_rect().left(), rows_bottom), pos2(ui.max_rect().right(), ui.clip_rect().bottom()));
if empty.height() > 0.0 && empty.width() > 0.0 {
let response = ui.interact(empty, ui.scope_id().with("layers-empty-space"), Sense::click());
response.context_menu(|ui| super::layers_empty_menu::show(app, ui));
}
}
// Tab / ⇧Tab in a rename: rename the layer below / above next, as in Photoshop. The
// Background isn't renamed in place (double-clicking it converts it), so it ends there.