use bumpalo::boxed::Box as BumpBox; use std::{ any::Any, cell::{Cell, RefCell}, fmt::Debug, rc::Rc, }; pub struct UiEvent { pub(crate) bubbles: Rc>, pub(crate) data: Rc, } impl UiEvent { pub fn downcast(self) -> Option> { Some(UiEvent { bubbles: self.bubbles, data: self.data.downcast().ok()?, }) } } impl Clone for UiEvent { fn clone(&self) -> Self { Self { bubbles: self.bubbles.clone(), data: self.data.clone(), } } } impl UiEvent { pub fn cancel_bubble(&self) { self.bubbles.set(false); } } impl std::ops::Deref for UiEvent { type Target = Rc; fn deref(&self) -> &Self::Target { &self.data } } impl std::fmt::Debug for UiEvent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UiEvent") .field("bubble_state", &self.bubbles) .field("data", &self.data) .finish() } } /// Priority of Event Triggers. /// /// Internally, Dioxus will abort work that's taking too long if new, more important work arrives. Unlike React, Dioxus /// won't be afraid to pause work or flush changes to the Real Dom. This is called "cooperative scheduling". Some Renderers /// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well. /// /// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes. /// /// React has a 5-tier priority system. However, they break things into "Continuous" and "Discrete" priority. For now, /// we keep it simple, and just use a 3-tier priority system. /// /// - `NoPriority` = 0 /// - `LowPriority` = 1 /// - `NormalPriority` = 2 /// - `UserBlocking` = 3 /// - `HighPriority` = 4 /// - `ImmediatePriority` = 5 /// /// We still have a concept of discrete vs continuous though - discrete events won't be batched, but continuous events will. /// This means that multiple "scroll" events will be processed in a single frame, but multiple "click" events will be /// flushed before proceeding. Multiple discrete events is highly unlikely, though. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] pub enum EventPriority { /// Work that must be completed during the EventHandler phase. /// /// Currently this is reserved for controlled inputs. Immediate = 3, /// "High Priority" work will not interrupt other high priority work, but will interrupt medium and low priority work. /// /// This is typically reserved for things like user interaction. /// /// React calls these "discrete" events, but with an extra category of "user-blocking" (Immediate). High = 2, /// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important /// than "High Priority" events and will take precedence over low priority events. /// /// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input. /// /// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc). Medium = 1, /// "Low Priority" work will always be preempted unless the work is significantly delayed, in which case it will be /// advanced to the front of the work queue until completed. /// /// The primary user of Low Priority work is the asynchronous work system (Suspense). /// /// This is considered "idle" work or "background" work. Low = 0, } type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>; /// The callback type generated by the `rsx!` macro when an `on` field is specified for components. /// /// This makes it possible to pass `move |evt| {}` style closures into components as property fields. /// /// /// # Example /// /// ```rust, ignore /// /// rsx!{ /// MyComponent { onclick: move |evt| log::info!("clicked"), } /// } /// /// #[derive(Props)] /// struct MyProps<'a> { /// onclick: EventHandler<'a, MouseEvent>, /// } /// /// fn MyComponent(cx: Scope<'a, MyProps<'a>>) -> Element { /// cx.render(rsx!{ /// button { /// onclick: move |evt| cx.props.onclick.call(evt), /// } /// }) /// } /// /// ``` pub struct EventHandler<'bump, T = ()> { /// The (optional) callback that the user specified /// Uses a `RefCell` to allow for interior mutability, and FnMut closures. pub callback: RefCell>>, } impl<'a, T> Default for EventHandler<'a, T> { fn default() -> Self { Self { callback: RefCell::new(None), } } } impl EventHandler<'_, T> { /// Call this event handler with the appropriate event type pub fn call(&self, event: T) { if let Some(callback) = self.callback.borrow_mut().as_mut() { callback(event); } } /// Forcibly drop the internal handler callback, releasing memory pub fn release(&self) { self.callback.replace(None); } } #[test] fn matches_slice() { let left = &[1, 2, 3]; let right = &[1, 2, 3, 4, 5]; assert!(is_path_ascendant(left, right)); assert!(!is_path_ascendant(right, left)); assert!(!is_path_ascendant(left, left)); assert!(is_path_ascendant(&[1, 2], &[1, 2, 3, 4, 5])); }