Let a press on the photograph reach the tool that was armed for it
The brush did nothing, and neither did three other things nobody had tried lately: clicking a subject on the photograph to select it, placing a repair, and sampling a neutral. All four are TouchAreas over the canvas, and all four sat behind the pan/zoom area, which is full-canvas and enabled for everything but a crop. It took every press in the viewport and they were never offered one. Slint hit-tests siblings front-to-back (`send_mouse_event_to_item` visits children `TraversalOrder::FrontToBack`), a TouchArea answers `GrabMouse` on any press it is enabled for, and the first grab aborts the traversal. Front means *last declared*. Each of the four carried a comment saying it sat "above the pan/zoom area so a click reaches it first" — true of the order they were written in, and backwards. Nothing about the geometry decides this, so nothing about the geometry could have fixed it. The pan area is declared first now, as the backstop it always meant to be, and the rule it leaves behind is that the general case goes above the specific ones. `GradientHandles` is the other end of that rule and is why dragging a handle has worked all along while everything between it and the pan area did not. The order is asserted in a test, because this is a fault that compiles, passes every other test, and silently removes four tools at once.
This commit is contained in:
@@ -36,13 +36,6 @@ pub const GESTURES: &[Gesture] = &[
|
||||
pointer: "The scroll wheel over it",
|
||||
keys: "",
|
||||
},
|
||||
Gesture {
|
||||
title: "Paint a mask by hand",
|
||||
section: "Develop",
|
||||
touch: "Choose Paint or Erase, then drag on the photograph",
|
||||
pointer: "Choose Paint or Erase, then drag",
|
||||
keys: "",
|
||||
},
|
||||
Gesture {
|
||||
title: "Move a magnified photograph about",
|
||||
section: "Develop",
|
||||
@@ -50,6 +43,13 @@ pub const GESTURES: &[Gesture] = &[
|
||||
pointer: "Drag it",
|
||||
keys: "",
|
||||
},
|
||||
Gesture {
|
||||
title: "Paint a mask by hand",
|
||||
section: "Develop",
|
||||
touch: "Choose Paint or Erase, then drag on the photograph",
|
||||
pointer: "Choose Paint or Erase, then drag",
|
||||
keys: "",
|
||||
},
|
||||
Gesture {
|
||||
title: "Take back the last change",
|
||||
section: "Develop",
|
||||
|
||||
@@ -1602,3 +1602,60 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-19b | FR-DEV-19c
|
||||
/// The order the canvas handlers are declared in, which decides which of them
|
||||
/// receives a press.
|
||||
///
|
||||
/// Not a unit test of anything this file does, and here rather than nowhere
|
||||
/// because there is nowhere better: it guards a fault that compiles, passes
|
||||
/// every other test, and takes four separate tools out of the application at
|
||||
/// once.
|
||||
///
|
||||
/// Slint hit-tests siblings front-to-back and a `TouchArea` grabs the first
|
||||
/// press it is offered, so **the last handler declared is the one that wins**.
|
||||
/// The full-canvas pan/zoom area must therefore come *first*, and `app.slint`
|
||||
/// had it last — with each of the four handlers behind it carrying a comment
|
||||
/// claiming it sat "above the pan/zoom area" because it was written earlier in
|
||||
/// the file.
|
||||
#[cfg(test)]
|
||||
mod canvas_order {
|
||||
/// Where each canvas handler is declared, by byte offset.
|
||||
fn at(needle: &str) -> usize {
|
||||
let source = include_str!("../ui/app.slint");
|
||||
source
|
||||
.find(needle)
|
||||
.unwrap_or_else(|| panic!("app.slint no longer contains `{needle}`"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_pan_backstop_is_declared_before_every_tool_it_would_swallow() {
|
||||
let pan = at("--- the pan/zoom backstop ---");
|
||||
for tool in [
|
||||
"pick := TouchArea",
|
||||
"paint := TouchArea",
|
||||
"Develop.repairing && root.total > 0",
|
||||
"root.sampling && root.total > 0",
|
||||
] {
|
||||
assert!(
|
||||
pan < at(tool),
|
||||
"`{tool}` is declared before the pan/zoom area, so the pan area \
|
||||
is in front of it and will take every press it was meant to get"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The other end of the same rule: a handle drawn on the photograph has to
|
||||
/// beat the tool armed over it, or a gradient cannot be moved while one is.
|
||||
#[test]
|
||||
fn the_gradient_handles_are_declared_last() {
|
||||
let handles = at("GradientHandles {");
|
||||
for behind in ["pick := TouchArea", "paint := TouchArea"] {
|
||||
assert!(
|
||||
handles > at(behind),
|
||||
"`{behind}` is declared after GradientHandles and would swallow \
|
||||
a press meant for a handle"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user