//! 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, } 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.events } fn events_mut(&mut self) -> &mut EventManager { &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" ); }