Report consumption from run_event
`Event::consumes` says whether having run uses up what triggered it, defaulting to no. `run_fn` already calls `should_run` per registration, so it ors that across everything that ran and hands it back through `run_event`. `CursorSenses` answers it with the sense it matched: a press or a scroll is used up, hovering is not. That drops `TypeEventManager::registered` and the second pass over a widget's senses -- the match that decides consumption is now the same one that decides whether the handler runs. A cursor that is only resting still stops at the layer it is over, which `run_event` cannot report because nothing need answer for it to be true. It must not stop at a widget it has merely left, though, or ending a hover above blocks the hover below: `leaving_a_widget_does_not_block_the_layer_below` is that case, and it fails on `main` too.
This commit is contained in:
1 parent
e53ce585e6
commit
827d317f41
5 files changed
+57
-26
No files matched your search
@@ -135,19 +135,16 @@ impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What this widget registered, without running any of it.
|
|
||||||
pub fn registered(&self, id: WidgetId) -> impl Iterator<Item = &E> {
|
|
||||||
self.map.get(&id).into_iter().flatten().map(|(e, _)| e)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_fn<'a>(
|
pub fn run_fn<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: impl IdLike,
|
id: impl IdLike,
|
||||||
) -> impl for<'b> FnOnce(EventCtx<'_, Rsc, E::Data<'b>>, &mut Rsc) + 'a {
|
) -> impl for<'b> FnOnce(EventCtx<'_, Rsc, E::Data<'b>>, &mut Rsc) -> bool + 'a {
|
||||||
let fs = self.map.get(&id.id()).cloned().unwrap_or_default();
|
let fs = self.map.get(&id.id()).cloned().unwrap_or_default();
|
||||||
move |ctx, rsc| {
|
move |ctx, rsc| {
|
||||||
|
let mut consumed = false;
|
||||||
for (e, f) in fs {
|
for (e, f) in fs {
|
||||||
if let Some(data) = e.should_run(&ctx.data) {
|
if let Some(data) = e.should_run(&ctx.data) {
|
||||||
|
consumed |= e.consumes(&data);
|
||||||
f(
|
f(
|
||||||
EventCtx {
|
EventCtx {
|
||||||
state: ctx.state,
|
state: ctx.state,
|
||||||
@@ -157,6 +154,7 @@ impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
consumed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,14 @@ pub trait Event: Sized + 'static + Clone {
|
|||||||
fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option<Self::Data<'a>> {
|
fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option<Self::Data<'a>> {
|
||||||
Some(data.clone())
|
Some(data.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether having run on this data uses up whatever triggered it, so
|
||||||
|
/// nothing further should see it. `run_event` reports back the `or` of
|
||||||
|
/// this across everything that ran.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn consumes(&self, data: &Self::Data<'_>) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EventLike {
|
pub trait EventLike {
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ pub trait HasEvents: Sized + UiRsc + HasState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait RunEvents: HasEvents {
|
pub trait RunEvents: HasEvents {
|
||||||
|
/// Whether anything that ran consumed the input; see `Event::consumes`.
|
||||||
fn run_event<E: EventLike>(
|
fn run_event<E: EventLike>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: impl IdLike,
|
id: impl IdLike,
|
||||||
data: <E::Event as Event>::Data<'_>,
|
data: <E::Event as Event>::Data<'_>,
|
||||||
state: &mut Self::State,
|
state: &mut Self::State,
|
||||||
) {
|
) -> bool {
|
||||||
let f = self.events_mut().get_type::<E>().run_fn(id);
|
let f = self.events_mut().get_type::<E>().run_fn(id);
|
||||||
f(EventCtx { state, data }, self)
|
f(EventCtx { state, data }, self)
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-19
@@ -37,19 +37,11 @@ impl Event for CursorSenses {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CursorSenses {
|
/// A press or a scroll is used up by whatever answered it, so it stops
|
||||||
/// Whether a widget with these senses stops the input reaching the layer
|
/// there. Hovering is not: a cursor resting somewhere goes on resting.
|
||||||
/// below it. A cursor that is only somewhere stops at the first layer it
|
fn consumes(&self, data: &Self::Data<'_>) -> bool {
|
||||||
/// is over. A cursor doing something -- a press, a scroll -- stops only
|
!data.sense.position_only()
|
||||||
/// where something answers to it, so a button drawn over a list does not
|
|
||||||
/// swallow the list's scrolling.
|
|
||||||
fn consumes(&self, cursor: &CursorState, hover: ActivationState) -> bool {
|
|
||||||
if cursor.position_only() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
should_run(self, cursor, hover).is_some_and(|sense| !sense.position_only())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +182,7 @@ impl SensorUi for UiRenderState {
|
|||||||
// state like thing, but local to render state, and is passed to UiRsc events so you can
|
// state like thing, but local to render state, and is passed to UiRsc events so you can
|
||||||
// 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 resting = cursor.position_only();
|
||||||
for layer in self.layers.indices().rev() {
|
for layer in self.layers.indices().rev() {
|
||||||
let mut consumed = false;
|
let mut consumed = false;
|
||||||
for (id, sensor) in active.get_mut(&layer).into_flat_iter() {
|
for (id, sensor) in active.get_mut(&layer).into_flat_iter() {
|
||||||
@@ -200,12 +193,6 @@ impl SensorUi for UiRenderState {
|
|||||||
if sensor.hover == ActivationState::Off {
|
if sensor.hover == ActivationState::Off {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
consumed |= rsc
|
|
||||||
.events_mut()
|
|
||||||
.get_type::<CursorSense>()
|
|
||||||
.registered(*id)
|
|
||||||
.any(|senses| senses.consumes(&cursor, sensor.hover));
|
|
||||||
|
|
||||||
let cursor = cursor.clone();
|
let cursor = cursor.clone();
|
||||||
|
|
||||||
let data = CursorData {
|
let data = CursorData {
|
||||||
@@ -219,7 +206,13 @@ impl SensorUi for UiRenderState {
|
|||||||
sense: CursorSense::Hovering,
|
sense: CursorSense::Hovering,
|
||||||
render: self,
|
render: self,
|
||||||
};
|
};
|
||||||
rsc.run_event::<CursorSense>(*id, data, state);
|
// A cursor that is only resting stops at the layer it is
|
||||||
|
// over, answered or not -- but not at a widget it has just
|
||||||
|
// left, which is here only to end its hover. An action stops
|
||||||
|
// where something answered it, so a button over a list does
|
||||||
|
// not swallow the list's scrolling.
|
||||||
|
let answered = rsc.run_event::<CursorSense>(*id, data, state);
|
||||||
|
consumed |= answered || (resting && in_shape);
|
||||||
}
|
}
|
||||||
if consumed {
|
if consumed {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -232,3 +232,34 @@ fn leaving_a_widget_still_ends_its_hover() {
|
|||||||
ui.run(gone);
|
ui.run(gone);
|
||||||
assert_eq!(hover.take(), [CursorSense::HoverEnd]);
|
assert_eq!(hover.take(), [CursorSense::HoverEnd]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn leaving_a_widget_does_not_block_the_layer_below() {
|
||||||
|
let ui = &mut Ui::new();
|
||||||
|
let below = full(ui);
|
||||||
|
// Only the left half of the layer above is a widget, so the cursor can
|
||||||
|
// leave it without leaving the one underneath.
|
||||||
|
let (above, gap) = (full(ui), full(ui));
|
||||||
|
let below_hover = ui.listen(&below, CursorSense::HoverStart);
|
||||||
|
let above_hover = ui.listen(&above, CursorSense::HoverStart | CursorSense::HoverEnd);
|
||||||
|
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 on_above = ui.cursor((20.0, 50.0));
|
||||||
|
ui.run(on_above);
|
||||||
|
assert_eq!(above_hover.take(), [CursorSense::HoverStart]);
|
||||||
|
assert_eq!(below_hover.take(), [], "the layer above is over it");
|
||||||
|
|
||||||
|
let beside_it = ui.cursor((80.0, 50.0));
|
||||||
|
ui.run(beside_it);
|
||||||
|
assert_eq!(above_hover.take(), [CursorSense::HoverEnd]);
|
||||||
|
assert_eq!(
|
||||||
|
below_hover.take(),
|
||||||
|
[CursorSense::HoverStart],
|
||||||
|
"ending a hover above must not stop the hover below"
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user