Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
983e15cd9a | ||
|
|
338f10f070 | ||
|
|
234abb8052 | ||
|
|
80b28fff1a | ||
|
|
2026e5d088 | ||
|
|
15a156595b | ||
|
|
dc008441e4 | ||
|
|
5fe7c3b6df | ||
|
|
71e46c125a | ||
|
|
2d15195a58 | ||
|
|
f8b7912181 |
No files matched your search
@@ -135,15 +135,6 @@ impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The event lists this widget was registered with, without running
|
|
||||||
/// anything. Asking what a widget would match is a separate question
|
|
||||||
/// from dispatching to it: input routing needs the first to decide
|
|
||||||
/// whether a widget consumes an event, and `run_fn` can only answer
|
|
||||||
/// the second.
|
|
||||||
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,
|
||||||
|
|||||||
+1
-40
@@ -52,18 +52,6 @@ impl CursorSense {
|
|||||||
pub fn is_dragging(&self) -> bool {
|
pub fn is_dragging(&self) -> bool {
|
||||||
matches!(self, CursorSense::Pressing(CursorButton::Left))
|
matches!(self, CursorSense::Pressing(CursorButton::Left))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True for a sense that names something happening this frame (a
|
|
||||||
/// button transitioning, a scroll), as opposed to the ambient
|
|
||||||
/// `Hover*` family that is on for as long as the cursor rests there.
|
|
||||||
/// Only a momentary sense can consume an input -- see
|
|
||||||
/// `SensorUi::run_sensors`.
|
|
||||||
pub fn is_momentary(&self) -> bool {
|
|
||||||
!matches!(
|
|
||||||
self,
|
|
||||||
CursorSense::HoverStart | CursorSense::Hovering | CursorSense::HoverEnd
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@@ -175,11 +163,6 @@ impl SensorUi for UiRenderState {
|
|||||||
// this would probably be done through a generic parameter that adds yet another rsc /
|
// this would probably be done through a generic parameter that adds yet another rsc /
|
||||||
// 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?
|
||||||
// Whether anything momentary is happening this frame at all; see
|
|
||||||
// `consumed` below, which is only judged per kind when it is.
|
|
||||||
let momentary_active =
|
|
||||||
cursor.scroll_delta != Vec2::ZERO || cursor.buttons.iter().any(|(_, a)| !a.is_off());
|
|
||||||
|
|
||||||
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);
|
||||||
for layer in self.layers.indices().rev() {
|
for layer in self.layers.indices().rev() {
|
||||||
let mut sensed = false;
|
let mut sensed = false;
|
||||||
@@ -191,29 +174,7 @@ impl SensorUi for UiRenderState {
|
|||||||
if sensor.hover == ActivationState::Off {
|
if sensor.hover == ActivationState::Off {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
sensed = true;
|
||||||
// A widget in shape always still runs: a hover-only
|
|
||||||
// highlight must fire on the topmost thing under the
|
|
||||||
// cursor even while a scroll passes through it. What is
|
|
||||||
// judged per input kind is whether it *consumes* that
|
|
||||||
// input, stopping a lower layer from seeing it. With
|
|
||||||
// nothing momentary happening, being in shape is
|
|
||||||
// consumption and the topmost widget wins the hover; with
|
|
||||||
// a scroll or a press happening, only a widget that
|
|
||||||
// registered a matching momentary sense consumes it, so a
|
|
||||||
// button registered for `click()` alone cannot block a
|
|
||||||
// scroll meant for the list behind it.
|
|
||||||
let consumed = if momentary_active {
|
|
||||||
rsc.events_mut()
|
|
||||||
.get_type::<CursorSense>()
|
|
||||||
.registered(*id)
|
|
||||||
.any(|senses| {
|
|
||||||
matches!(should_run(senses, &cursor, sensor.hover), Some(s) if s.is_momentary())
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
sensed |= consumed;
|
|
||||||
|
|
||||||
let cursor = cursor.clone();
|
let cursor = cursor.clone();
|
||||||
|
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
//! A widget that registered only `click()` used to block a `Scroll` meant
|
|
||||||
//! for whatever is behind it: `run_sensors` decided "consumed, stop looking
|
|
||||||
//! at lower layers" from mere hover rather than from anything actually
|
|
||||||
//! matching the input. These drive `run_sensors` directly, which needs no
|
|
||||||
//! GPU and no window.
|
|
||||||
|
|
||||||
use iris::prelude::*;
|
|
||||||
use std::{cell::Cell, rc::Rc};
|
|
||||||
|
|
||||||
struct SenseRsc {
|
|
||||||
ui: UiData,
|
|
||||||
events: EventManager<SenseRsc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiRsc for SenseRsc {
|
|
||||||
fn ui(&self) -> &UiData {
|
|
||||||
&self.ui
|
|
||||||
}
|
|
||||||
fn ui_mut(&mut self) -> &mut UiData {
|
|
||||||
&mut self.ui
|
|
||||||
}
|
|
||||||
fn on_draw(&mut self, active: &ActiveData) {
|
|
||||||
self.events.draw(active);
|
|
||||||
}
|
|
||||||
fn on_undraw(&mut self, active: &ActiveData) {
|
|
||||||
self.events.undraw(active);
|
|
||||||
}
|
|
||||||
fn on_remove(&mut self, id: WidgetId) {
|
|
||||||
self.events.remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasState for SenseRsc {
|
|
||||||
type State = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasEvents for SenseRsc {
|
|
||||||
fn events(&self) -> &EventManager<Self> {
|
|
||||||
&self.events
|
|
||||||
}
|
|
||||||
fn events_mut(&mut self) -> &mut EventManager<Self> {
|
|
||||||
&mut self.events
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_at(pos: Vec2) -> CursorState {
|
|
||||||
CursorState {
|
|
||||||
pos,
|
|
||||||
exists: true,
|
|
||||||
buttons: Default::default(),
|
|
||||||
scroll_delta: Vec2::ZERO,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_button_over_a_list_scrolls_the_list_and_still_clicks() {
|
|
||||||
let mut rsc = SenseRsc {
|
|
||||||
ui: UiData::default(),
|
|
||||||
events: EventManager::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Both cover the whole window: the button "sitting over" the list.
|
|
||||||
let list = rsc.ui.widgets.add_strong(Rect::new(UiColor::WHITE));
|
|
||||||
let list_weak = list.weak();
|
|
||||||
let button = rsc.ui.widgets.add_strong(Rect::new(UiColor::RED));
|
|
||||||
let button_weak = button.weak();
|
|
||||||
|
|
||||||
let scrolled = Rc::new(Cell::new(false));
|
|
||||||
let clicked = Rc::new(Cell::new(false));
|
|
||||||
{
|
|
||||||
let scrolled = scrolled.clone();
|
|
||||||
rsc.register_event(list_weak, CursorSense::Scroll, move |_ctx, _rsc| {
|
|
||||||
scrolled.set(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let clicked = clicked.clone();
|
|
||||||
rsc.register_event(button_weak, CursorSense::click(), move |_ctx, _rsc| {
|
|
||||||
clicked.set(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Stack draws its children on separate layers in order, which is
|
|
||||||
// exactly the "one thing drawn over another" shape `run_sensors`
|
|
||||||
// walks top layer first.
|
|
||||||
let root = rsc
|
|
||||||
.ui
|
|
||||||
.widgets
|
|
||||||
.add_strong(Stack {
|
|
||||||
children: vec![list.any(), button.any()],
|
|
||||||
size: StackSize::default(),
|
|
||||||
})
|
|
||||||
.any();
|
|
||||||
|
|
||||||
let mut render = UiRenderState::new();
|
|
||||||
render.resize((100.0, 100.0));
|
|
||||||
render.update(&root, &mut rsc);
|
|
||||||
|
|
||||||
let mut state = ();
|
|
||||||
let mut scroll_cursor = cursor_at((50.0, 50.0).into());
|
|
||||||
scroll_cursor.scroll_delta = (0.0, 10.0).into();
|
|
||||||
render.run_sensors(&mut rsc, &mut state, scroll_cursor, (100.0, 100.0).into());
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
scrolled.get(),
|
|
||||||
"a scroll over the button must still reach the list underneath it"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
!clicked.get(),
|
|
||||||
"a scroll is not a click; the button must not have fired"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut click_cursor = cursor_at((50.0, 50.0).into());
|
|
||||||
click_cursor.buttons.left = ActivationState::Start;
|
|
||||||
render.run_sensors(&mut rsc, &mut state, click_cursor, (100.0, 100.0).into());
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
clicked.get(),
|
|
||||||
"the button on top must still receive an actual click"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user