Say position-only, and stop falsifying the cursor
`is_momentary` becomes `position_only` on both the sense and the cursor, inverted so it reads as what it tests. A widget the cursor has left was being handed a blanked cursor so its press senses would not match. `should_run` now skips non-position senses when the pointer is not inside, which is the same rule without lying about the input: the widget still gets the real cursor with its hover ending. `consumes` loses its `momentary` argument, since the cursor answers that itself.
This commit is contained in:
1 parent
f3fd9417d4
commit
e53ce585e6
2 files changed
+39
-42
No files matched your search
+27
-30
@@ -41,14 +41,15 @@ impl Event for CursorSenses {
|
|||||||
|
|
||||||
impl CursorSenses {
|
impl CursorSenses {
|
||||||
/// Whether a widget with these senses stops the input reaching the layer
|
/// Whether a widget with these senses stops the input reaching the layer
|
||||||
/// below it. Where the cursor rests stops at the top layer that is under
|
/// below it. A cursor that is only somewhere stops at the first layer it
|
||||||
/// it; something happening to the cursor stops only at a widget that
|
/// is over. A cursor doing something -- a press, a scroll -- stops only
|
||||||
/// answers to that, so a click-only child does not swallow a scroll.
|
/// where something answers to it, so a button drawn over a list does not
|
||||||
fn consumes(&self, cursor: &CursorState, hover: ActivationState, momentary: bool) -> bool {
|
/// swallow the list's scrolling.
|
||||||
if !momentary {
|
fn consumes(&self, cursor: &CursorState, hover: ActivationState) -> bool {
|
||||||
|
if cursor.position_only() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
should_run(self, cursor, hover).is_some_and(|sense| sense.is_momentary())
|
should_run(self, cursor, hover).is_some_and(|sense| !sense.position_only())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +67,10 @@ impl CursorSense {
|
|||||||
matches!(self, CursorSense::Pressing(CursorButton::Left))
|
matches!(self, CursorSense::Pressing(CursorButton::Left))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this sense is about something happening to the cursor, rather
|
/// False if the sense is a button or a scroll, true if it is only about
|
||||||
/// than about where it rests.
|
/// where the cursor is.
|
||||||
fn is_momentary(&self) -> bool {
|
fn position_only(&self) -> bool {
|
||||||
!matches!(self, Self::HoverStart | Self::Hovering | Self::HoverEnd)
|
matches!(self, Self::HoverStart | Self::Hovering | Self::HoverEnd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,9 +116,10 @@ impl CursorButtons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CursorState {
|
impl CursorState {
|
||||||
/// Whether anything is happening to the cursor beyond where it rests.
|
/// True if the cursor is only reporting where it is: no button and no
|
||||||
pub fn has_momentary_input(&self) -> bool {
|
/// scroll this frame.
|
||||||
self.scroll_delta != Vec2::ZERO || self.buttons.iter().any(|(_, state)| !state.is_off())
|
pub fn position_only(&self) -> bool {
|
||||||
|
self.scroll_delta == Vec2::ZERO && self.buttons.iter().all(|(_, state)| state.is_off())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_frame(&mut self) {
|
pub fn end_frame(&mut self) {
|
||||||
@@ -188,7 +190,6 @@ 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 momentary = cursor.has_momentary_input();
|
|
||||||
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() {
|
||||||
@@ -199,22 +200,13 @@ impl SensorUi for UiRenderState {
|
|||||||
if sensor.hover == ActivationState::Off {
|
if sensor.hover == ActivationState::Off {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// A widget the cursor has left still hears its hover ending,
|
consumed |= rsc
|
||||||
// but a press or a scroll landing elsewhere is not its input.
|
.events_mut()
|
||||||
let cursor = match in_shape {
|
.get_type::<CursorSense>()
|
||||||
true => cursor.clone(),
|
.registered(*id)
|
||||||
false => CursorState {
|
.any(|senses| senses.consumes(&cursor, sensor.hover));
|
||||||
pos: cursor.pos,
|
|
||||||
exists: cursor.exists,
|
let cursor = cursor.clone();
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
consumed = consumed
|
|
||||||
|| rsc
|
|
||||||
.events_mut()
|
|
||||||
.get_type::<CursorSense>()
|
|
||||||
.registered(*id)
|
|
||||||
.any(|senses| senses.consumes(&cursor, sensor.hover, momentary));
|
|
||||||
|
|
||||||
let data = CursorData {
|
let data = CursorData {
|
||||||
pos: cursor.pos - region.top_left,
|
pos: cursor.pos - region.top_left,
|
||||||
@@ -243,6 +235,11 @@ pub fn should_run(
|
|||||||
hover: ActivationState,
|
hover: ActivationState,
|
||||||
) -> Option<CursorSense> {
|
) -> Option<CursorSense> {
|
||||||
for sense in senses.iter() {
|
for sense in senses.iter() {
|
||||||
|
// A widget the cursor is no longer inside senses only its position:
|
||||||
|
// the press that ended its hover landed on something else.
|
||||||
|
if !hover.is_on() && !sense.position_only() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if match sense {
|
if match sense {
|
||||||
CursorSense::PressStart(button) => cursor.buttons.select(button).is_start(),
|
CursorSense::PressStart(button) => cursor.buttons.select(button).is_start(),
|
||||||
CursorSense::Pressing(button) => cursor.buttons.select(button).is_on(),
|
CursorSense::Pressing(button) => cursor.buttons.select(button).is_on(),
|
||||||
|
|||||||
+12
-12
@@ -126,8 +126,8 @@ fn full(ui: &mut Ui) -> StrongWidget<Rect> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hover_stops_at_the_topmost_widget() {
|
fn hover_stops_at_the_topmost_widget() {
|
||||||
let mut ui = Ui::new();
|
let ui = &mut Ui::new();
|
||||||
let (bottom, middle, top) = (full(&mut ui), full(&mut ui), full(&mut ui));
|
let (bottom, middle, top) = (full(ui), full(ui), full(ui));
|
||||||
let bottom_hover = ui.listen(&bottom, CursorSense::HoverStart);
|
let bottom_hover = ui.listen(&bottom, CursorSense::HoverStart);
|
||||||
let middle_hover = ui.listen(&middle, CursorSense::HoverStart);
|
let middle_hover = ui.listen(&middle, CursorSense::HoverStart);
|
||||||
let top_hover = ui.listen(&top, CursorSense::HoverStart);
|
let top_hover = ui.listen(&top, CursorSense::HoverStart);
|
||||||
@@ -147,8 +147,8 @@ fn hover_stops_at_the_topmost_widget() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_scroll_passes_through_every_widget_that_does_not_want_it() {
|
fn a_scroll_passes_through_every_widget_that_does_not_want_it() {
|
||||||
let mut ui = Ui::new();
|
let ui = &mut Ui::new();
|
||||||
let (list, button, overlay) = (full(&mut ui), full(&mut ui), full(&mut ui));
|
let (list, button, overlay) = (full(ui), full(ui), full(ui));
|
||||||
let scrolled = ui.listen(&list, CursorSense::Scroll);
|
let scrolled = ui.listen(&list, CursorSense::Scroll);
|
||||||
let clicked = ui.listen(&button, CursorSense::click());
|
let clicked = ui.listen(&button, CursorSense::click());
|
||||||
let overlay_clicked = ui.listen(&overlay, CursorSense::click());
|
let overlay_clicked = ui.listen(&overlay, CursorSense::click());
|
||||||
@@ -169,8 +169,8 @@ fn a_scroll_passes_through_every_widget_that_does_not_want_it() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_the_topmost_listener_takes_a_press() {
|
fn only_the_topmost_listener_takes_a_press() {
|
||||||
let mut ui = Ui::new();
|
let ui = &mut Ui::new();
|
||||||
let (below, above) = (full(&mut ui), full(&mut ui));
|
let (below, above) = (full(ui), full(ui));
|
||||||
let below_clicked = ui.listen(&below, CursorSense::click());
|
let below_clicked = ui.listen(&below, CursorSense::click());
|
||||||
let above_clicked = ui.listen(&above, CursorSense::click());
|
let above_clicked = ui.listen(&above, CursorSense::click());
|
||||||
ui.stack(vec![below.any(), above.any()]);
|
ui.stack(vec![below.any(), above.any()]);
|
||||||
@@ -185,11 +185,11 @@ fn only_the_topmost_listener_takes_a_press() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_press_beside_the_button_reaches_the_layer_below() {
|
fn a_press_beside_the_button_reaches_the_layer_below() {
|
||||||
let mut ui = Ui::new();
|
let ui = &mut Ui::new();
|
||||||
let list = full(&mut ui);
|
let list = full(ui);
|
||||||
// The row above the list covers it, but only its left half is the button.
|
// The row above the list covers it, but only its left half is the button.
|
||||||
let button = full(&mut ui);
|
let button = full(ui);
|
||||||
let gap = full(&mut ui);
|
let gap = full(ui);
|
||||||
let list_clicked = ui.listen(&list, CursorSense::click());
|
let list_clicked = ui.listen(&list, CursorSense::click());
|
||||||
let button_clicked = ui.listen(&button, CursorSense::click());
|
let button_clicked = ui.listen(&button, CursorSense::click());
|
||||||
let row = ui.rsc.ui.widgets.add_strong(Span {
|
let row = ui.rsc.ui.widgets.add_strong(Span {
|
||||||
@@ -218,8 +218,8 @@ fn a_press_beside_the_button_reaches_the_layer_below() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn leaving_a_widget_still_ends_its_hover() {
|
fn leaving_a_widget_still_ends_its_hover() {
|
||||||
let mut ui = Ui::new();
|
let ui = &mut Ui::new();
|
||||||
let widget = full(&mut ui);
|
let widget = full(ui);
|
||||||
let hover = ui.listen(&widget, CursorSense::HoverStart | CursorSense::HoverEnd);
|
let hover = ui.listen(&widget, CursorSense::HoverStart | CursorSense::HoverEnd);
|
||||||
ui.stack(vec![widget.any()]);
|
ui.stack(vec![widget.any()]);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user