End the hover of a widget that gets covered
Breaking out of the layer loop left every sensor below the consuming layer untouched, so one that was hovered stayed hovered: moving onto a widget in a layer above never ended the hover of what it covered, and nothing ever would. Consumption now carries into the hit test rather than stopping the walk. A covered widget is simply not in shape, so its hover ends and its `HoverEnd` runs; `should_run` already refuses non-position senses once the hover is not on, so nothing else reaches it. It is applied after a layer rather than during one, so senses on the same layer still do not block each other.
This commit is contained in:
1 parent
8cac927438
commit
5494642dec
2 files changed
+40
-6
No files matched your search
+10
-6
@@ -183,12 +183,16 @@ impl SensorUi for UiRenderState {
|
|||||||
// update it there?
|
// update it there?
|
||||||
let mut active = std::mem::take(&mut rsc.events_mut().get_type::<CursorSense>().active);
|
let mut active = std::mem::take(&mut rsc.events_mut().get_type::<CursorSense>().active);
|
||||||
let position_only = cursor.position_only();
|
let position_only = cursor.position_only();
|
||||||
|
let mut consumed = false;
|
||||||
for layer in self.layers.indices().rev() {
|
for layer in self.layers.indices().rev() {
|
||||||
let mut consumed = false;
|
let mut consumed_here = false;
|
||||||
for (id, sensor) in active.get_mut(&layer).into_flat_iter() {
|
for (id, sensor) in active.get_mut(&layer).into_flat_iter() {
|
||||||
let shape = self.active.get(id).unwrap().region;
|
let shape = self.active.get(id).unwrap().region;
|
||||||
let region = shape.to_px(window_size);
|
let region = shape.to_px(window_size);
|
||||||
let in_shape = cursor.exists && region.contains(cursor.pos);
|
// Once a layer above has taken the input, everything under it
|
||||||
|
// is covered rather than skipped: it is not hovered, so a
|
||||||
|
// widget that was gets to end its hover.
|
||||||
|
let in_shape = !consumed && cursor.exists && region.contains(cursor.pos);
|
||||||
sensor.hover.update(in_shape);
|
sensor.hover.update(in_shape);
|
||||||
if sensor.hover == ActivationState::Off {
|
if sensor.hover == ActivationState::Off {
|
||||||
continue;
|
continue;
|
||||||
@@ -212,11 +216,11 @@ impl SensorUi for UiRenderState {
|
|||||||
// hovering does not reach through one -- but not at a widget
|
// hovering does not reach through one -- but not at a widget
|
||||||
// it has just left, which is here only to end its hover.
|
// it has just left, which is here only to end its hover.
|
||||||
let answered = rsc.run_event::<CursorSense>(*id, data, state);
|
let answered = rsc.run_event::<CursorSense>(*id, data, state);
|
||||||
consumed |= answered || (position_only && in_shape);
|
consumed_here |= answered || (position_only && in_shape);
|
||||||
}
|
|
||||||
if consumed {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// Applied after the layer, never during it: senses on one layer
|
||||||
|
// do not block each other.
|
||||||
|
consumed |= consumed_here;
|
||||||
}
|
}
|
||||||
rsc.events_mut().get_type::<CursorSense>().active = active;
|
rsc.events_mut().get_type::<CursorSense>().active = active;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,3 +288,33 @@ fn hovering_a_button_above_does_not_stop_a_later_scroll() {
|
|||||||
);
|
);
|
||||||
assert_eq!(clicked.take(), []);
|
assert_eq!(clicked.take(), []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn covering_a_widget_ends_its_hover() {
|
||||||
|
let ui = &mut Ui::new();
|
||||||
|
let below = full(ui);
|
||||||
|
// Only the left half of the layer above is a widget, so the cursor can
|
||||||
|
// start beside it and then move onto it.
|
||||||
|
let (above, gap) = (full(ui), full(ui));
|
||||||
|
let below_hover = ui.listen(&below, CursorSense::HoverStart | CursorSense::HoverEnd);
|
||||||
|
let above_hover = ui.listen(&above, CursorSense::HoverStart);
|
||||||
|
let row = ui.rsc.ui.widgets.add_strong(Span {
|
||||||
|
children: vec![above.any(), gap.any()],
|
||||||
|
dir: Dir::RIGHT,
|
||||||
|
gap: 0.0,
|
||||||
|
});
|
||||||
|
ui.stack(vec![below.any(), row.any()]);
|
||||||
|
|
||||||
|
let beside_it = ui.cursor((80.0, 50.0));
|
||||||
|
ui.run(beside_it);
|
||||||
|
assert_eq!(below_hover.take(), [CursorSense::HoverStart]);
|
||||||
|
|
||||||
|
let onto_above = ui.cursor((20.0, 50.0));
|
||||||
|
ui.run(onto_above);
|
||||||
|
assert_eq!(above_hover.take(), [CursorSense::HoverStart]);
|
||||||
|
assert_eq!(
|
||||||
|
below_hover.take(),
|
||||||
|
[CursorSense::HoverEnd],
|
||||||
|
"a widget covered by one that took the input is no longer hovered"
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user