Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91b71b97dc | ||
|
|
b9c4856e3f | ||
|
|
79dcc156c9 | ||
|
|
c0fcc0345c | ||
|
|
444a2cd138 | ||
|
|
23fb71ee56 | ||
|
|
01a9b8633d | ||
|
|
29d390da52 | ||
|
|
7b318e3271 | ||
|
|
89491a5949 | ||
|
|
a08f61a80c | ||
|
|
4d9839f380 | ||
|
|
b3d3da5dab | ||
|
|
f5864da3c4 | ||
|
|
0106257be0 | ||
|
|
bafaa1db6d |
No files matched your search
@@ -135,11 +135,6 @@ 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,
|
||||||
|
|||||||
+11
-57
@@ -52,19 +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))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes what this sense answers to out of `cursor`, so a widget below
|
|
||||||
/// does not also get it. Hovering takes nothing: it goes to the topmost
|
|
||||||
/// widget in shape, which is not a question about the input.
|
|
||||||
fn take(&self, cursor: &mut CursorState) {
|
|
||||||
match self {
|
|
||||||
Self::PressStart(button) | Self::Pressing(button) | Self::PressEnd(button) => {
|
|
||||||
*cursor.buttons.select_mut(button) = ActivationState::Off
|
|
||||||
}
|
|
||||||
Self::Scroll => cursor.scroll_delta = Vec2::ZERO,
|
|
||||||
Self::HoverStart | Self::Hovering | Self::HoverEnd => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@@ -91,14 +78,6 @@ impl CursorButtons {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_mut(&mut self, button: &CursorButton) -> &mut ActivationState {
|
|
||||||
match button {
|
|
||||||
CursorButton::Left => &mut self.left,
|
|
||||||
CursorButton::Right => &mut self.right,
|
|
||||||
CursorButton::Middle => &mut self.middle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end_frame(&mut self) {
|
pub fn end_frame(&mut self) {
|
||||||
self.left.end_frame();
|
self.left.end_frame();
|
||||||
self.middle.end_frame();
|
self.middle.end_frame();
|
||||||
@@ -185,33 +164,17 @@ 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);
|
||||||
// Narrowed as it descends: a widget takes what it answers to, and
|
|
||||||
// what is left is what the layers below see.
|
|
||||||
let mut cursor = cursor;
|
|
||||||
let mut hovered = false;
|
|
||||||
for layer in self.layers.indices().rev() {
|
for layer in self.layers.indices().rev() {
|
||||||
let mut below = cursor.clone();
|
let mut sensed = false;
|
||||||
let mut hovered_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 over = cursor.exists && region.contains(cursor.pos);
|
let in_shape = cursor.exists && region.contains(cursor.pos);
|
||||||
// Hover goes to the topmost widget in shape and no further.
|
sensor.hover.update(in_shape);
|
||||||
sensor.hover.update(over && !hovered);
|
if sensor.hover == ActivationState::Off {
|
||||||
// `is_off` would be wrong here: it counts `End`, which is
|
|
||||||
// the one frame a hover-end handler has to run on.
|
|
||||||
if !over && sensor.hover == ActivationState::Off {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
hovered_here |= over;
|
sensed = true;
|
||||||
|
|
||||||
for senses in rsc.events_mut().get_type::<CursorSense>().registered(*id) {
|
|
||||||
for sense in senses.iter() {
|
|
||||||
if matches(sense, &cursor, sensor.hover) {
|
|
||||||
sense.take(&mut below);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let cursor = cursor.clone();
|
let cursor = cursor.clone();
|
||||||
|
|
||||||
@@ -228,9 +191,7 @@ impl SensorUi for UiRenderState {
|
|||||||
};
|
};
|
||||||
rsc.run_event::<CursorSense>(*id, data, state);
|
rsc.run_event::<CursorSense>(*id, data, state);
|
||||||
}
|
}
|
||||||
hovered |= hovered_here;
|
if sensed {
|
||||||
cursor = below;
|
|
||||||
if hovered && !is_momentary(&cursor) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,14 +204,8 @@ pub fn should_run(
|
|||||||
cursor: &CursorState,
|
cursor: &CursorState,
|
||||||
hover: ActivationState,
|
hover: ActivationState,
|
||||||
) -> Option<CursorSense> {
|
) -> Option<CursorSense> {
|
||||||
senses
|
for sense in senses.iter() {
|
||||||
.iter()
|
if match sense {
|
||||||
.find(|sense| matches(sense, cursor, hover))
|
|
||||||
.copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn matches(sense: &CursorSense, cursor: &CursorState, hover: ActivationState) -> bool {
|
|
||||||
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(),
|
||||||
CursorSense::PressEnd(button) => cursor.buttons.select(button).is_end(),
|
CursorSense::PressEnd(button) => cursor.buttons.select(button).is_end(),
|
||||||
@@ -258,12 +213,11 @@ fn matches(sense: &CursorSense, cursor: &CursorState, hover: ActivationState) ->
|
|||||||
CursorSense::Hovering => hover.is_on(),
|
CursorSense::Hovering => hover.is_on(),
|
||||||
CursorSense::HoverEnd => hover.is_end(),
|
CursorSense::HoverEnd => hover.is_end(),
|
||||||
CursorSense::Scroll => cursor.scroll_delta != Vec2::ZERO,
|
CursorSense::Scroll => cursor.scroll_delta != Vec2::ZERO,
|
||||||
|
} {
|
||||||
|
return Some(*sense);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
/// Whether anything is happening to the cursor beyond where it rests.
|
|
||||||
fn is_momentary(cursor: &CursorState) -> bool {
|
|
||||||
cursor.scroll_delta != Vec2::ZERO || cursor.buttons.iter().any(|(_, state)| !state.is_off())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActivationState {
|
impl ActivationState {
|
||||||
|
|||||||
@@ -1,180 +0,0 @@
|
|||||||
//! A widget takes only what it answers to: a button over a list takes the
|
|
||||||
//! click and leaves the scroll. 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A button covering a list, on the layer above it: the list scrolls, the
|
|
||||||
/// button clicks, and the returned flags say which fired.
|
|
||||||
fn button_over_list() -> (UiRenderState, SenseRsc, Rc<Cell<bool>>, Rc<Cell<bool>>) {
|
|
||||||
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 each child on its own layer, in order.
|
|
||||||
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);
|
|
||||||
|
|
||||||
(render, rsc, scrolled, clicked)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_button_over_a_list_scrolls_the_list_and_still_clicks() {
|
|
||||||
let (render, mut rsc, scrolled, clicked) = button_over_list();
|
|
||||||
let mut state = ();
|
|
||||||
|
|
||||||
let mut scroll = cursor_at((50.0, 50.0).into());
|
|
||||||
scroll.scroll_delta = (0.0, 10.0).into();
|
|
||||||
render.run_sensors(&mut rsc, &mut state, scroll, (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_at((50.0, 50.0).into());
|
|
||||||
click.buttons.left = ActivationState::Start;
|
|
||||||
render.run_sensors(&mut rsc, &mut state, click, (100.0, 100.0).into());
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
clicked.get(),
|
|
||||||
"the button on top must still receive an actual click"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_click_and_a_scroll_in_one_frame_go_to_different_widgets() {
|
|
||||||
let (render, mut rsc, scrolled, clicked) = button_over_list();
|
|
||||||
let mut state = ();
|
|
||||||
|
|
||||||
let mut both = cursor_at((50.0, 50.0).into());
|
|
||||||
both.scroll_delta = (0.0, 10.0).into();
|
|
||||||
both.buttons.left = ActivationState::Start;
|
|
||||||
render.run_sensors(&mut rsc, &mut state, both, (100.0, 100.0).into());
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
clicked.get(),
|
|
||||||
"the button takes the click it registered for"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
scrolled.get(),
|
|
||||||
"taking the click must not take the scroll with it"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn leaving_a_widget_still_ends_its_hover() {
|
|
||||||
let mut rsc = SenseRsc {
|
|
||||||
ui: UiData::default(),
|
|
||||||
events: EventManager::default(),
|
|
||||||
};
|
|
||||||
let widget = rsc.ui.widgets.add_strong(Rect::new(UiColor::WHITE));
|
|
||||||
let ended = Rc::new(Cell::new(false));
|
|
||||||
{
|
|
||||||
let ended = ended.clone();
|
|
||||||
rsc.register_event(widget.weak(), CursorSense::HoverEnd, move |_ctx, _rsc| {
|
|
||||||
ended.set(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let root = widget.any();
|
|
||||||
let mut render = UiRenderState::new();
|
|
||||||
render.resize((100.0, 100.0));
|
|
||||||
render.update(&root, &mut rsc);
|
|
||||||
|
|
||||||
let mut state = ();
|
|
||||||
render.run_sensors(
|
|
||||||
&mut rsc,
|
|
||||||
&mut state,
|
|
||||||
cursor_at((50.0, 50.0).into()),
|
|
||||||
(100.0, 100.0).into(),
|
|
||||||
);
|
|
||||||
assert!(!ended.get(), "the cursor is still on it");
|
|
||||||
|
|
||||||
let mut gone = cursor_at((50.0, 50.0).into());
|
|
||||||
gone.exists = false;
|
|
||||||
render.run_sensors(&mut rsc, &mut state, gone, (100.0, 100.0).into());
|
|
||||||
assert!(ended.get(), "leaving a widget ends its hover");
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user