123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- use bumpalo::boxed::Box as BumpBox;
- use std::{
- any::Any,
- cell::{Cell, RefCell},
- fmt::Debug,
- rc::Rc,
- };
- pub struct UiEvent<T: 'static + ?Sized> {
- pub(crate) bubbles: Rc<Cell<bool>>,
- pub(crate) data: Rc<T>,
- }
- impl UiEvent<dyn Any> {
- pub fn downcast<T: 'static + Sized>(self) -> Option<UiEvent<T>> {
- Some(UiEvent {
- bubbles: self.bubbles,
- data: self.data.downcast().ok()?,
- })
- }
- }
- impl<T: ?Sized> Clone for UiEvent<T> {
- fn clone(&self) -> Self {
- Self {
- bubbles: self.bubbles.clone(),
- data: self.data.clone(),
- }
- }
- }
- impl<T> UiEvent<T> {
- pub fn cancel_bubble(&self) {
- self.bubbles.set(false);
- }
- }
- impl<T> std::ops::Deref for UiEvent<T> {
- type Target = Rc<T>;
- fn deref(&self) -> &Self::Target {
- &self.data
- }
- }
- impl<T: Debug> std::fmt::Debug for UiEvent<T> {
- 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<Option<ExternalListenerCallback<'bump, T>>>,
- }
- impl<'a, T> Default for EventHandler<'a, T> {
- fn default() -> Self {
- Self {
- callback: RefCell::new(None),
- }
- }
- }
- impl<T> 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]));
- }
|