events.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. //! Internal and external event system
  2. //!
  3. //!
  4. //! This is all kinda WIP, but the bones are there.
  5. use crate::{ElementId, ScopeId};
  6. use std::{any::Any, cell::Cell, fmt::Debug, rc::Rc, sync::Arc};
  7. pub(crate) struct BubbleState {
  8. pub canceled: Cell<bool>,
  9. }
  10. impl BubbleState {
  11. pub fn new() -> Self {
  12. Self {
  13. canceled: Cell::new(false),
  14. }
  15. }
  16. }
  17. /// User Events are events that are shuttled from the renderer into the [`VirtualDom`] through the scheduler channel.
  18. ///
  19. /// These events will be passed to the appropriate Element given by `mounted_dom_id` and then bubbled up through the tree
  20. /// where each listener is checked and fired if the event name matches.
  21. ///
  22. /// It is the expectation that the event name matches the corresponding event listener, otherwise Dioxus will panic in
  23. /// attempting to downcast the event data.
  24. ///
  25. /// Because Event Data is sent across threads, it must be `Send + Sync`. We are hoping to lift the `Sync` restriction but
  26. /// `Send` will not be lifted. The entire `UserEvent` must also be `Send + Sync` due to its use in the scheduler channel.
  27. ///
  28. /// # Example
  29. /// ```rust, ignore
  30. /// fn App(cx: Scope) -> Element {
  31. /// render!(div {
  32. /// onclick: move |_| println!("Clicked!")
  33. /// })
  34. /// }
  35. ///
  36. /// let mut dom = VirtualDom::new(App);
  37. /// let mut scheduler = dom.get_scheduler_channel();
  38. /// scheduler.unbounded_send(SchedulerMsg::UiEvent(
  39. /// UserEvent {
  40. /// scope_id: None,
  41. /// priority: EventPriority::Medium,
  42. /// name: "click",
  43. /// element: Some(ElementId(0)),
  44. /// data: Arc::new(ClickEvent { .. })
  45. /// }
  46. /// )).unwrap();
  47. /// ```
  48. #[derive(Debug, Clone)]
  49. pub struct UserEvent {
  50. /// The originator of the event trigger if available
  51. pub scope_id: Option<ScopeId>,
  52. /// The priority of the event to be scheduled around ongoing work
  53. pub priority: EventPriority,
  54. /// The optional real node associated with the trigger
  55. pub element: Option<ElementId>,
  56. /// The event type IE "onclick" or "onmouseover"
  57. pub name: &'static str,
  58. /// If the event is bubbles up through the vdom
  59. pub bubbles: bool,
  60. /// The event data to be passed onto the event handler
  61. pub data: Arc<dyn Any + Send + Sync>,
  62. }
  63. /// Priority of Event Triggers.
  64. ///
  65. /// Internally, Dioxus will abort work that's taking too long if new, more important work arrives. Unlike React, Dioxus
  66. /// won't be afraid to pause work or flush changes to the Real Dom. This is called "cooperative scheduling". Some Renderers
  67. /// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
  68. ///
  69. /// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes.
  70. ///
  71. /// React has a 5-tier priority system. However, they break things into "Continuous" and "Discrete" priority. For now,
  72. /// we keep it simple, and just use a 3-tier priority system.
  73. ///
  74. /// - `NoPriority` = 0
  75. /// - `LowPriority` = 1
  76. /// - `NormalPriority` = 2
  77. /// - `UserBlocking` = 3
  78. /// - `HighPriority` = 4
  79. /// - `ImmediatePriority` = 5
  80. ///
  81. /// We still have a concept of discrete vs continuous though - discrete events won't be batched, but continuous events will.
  82. /// This means that multiple "scroll" events will be processed in a single frame, but multiple "click" events will be
  83. /// flushed before proceeding. Multiple discrete events is highly unlikely, though.
  84. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
  85. pub enum EventPriority {
  86. /// Work that must be completed during the EventHandler phase.
  87. ///
  88. /// Currently this is reserved for controlled inputs.
  89. Immediate = 3,
  90. /// "High Priority" work will not interrupt other high priority work, but will interrupt medium and low priority work.
  91. ///
  92. /// This is typically reserved for things like user interaction.
  93. ///
  94. /// React calls these "discrete" events, but with an extra category of "user-blocking" (Immediate).
  95. High = 2,
  96. /// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
  97. /// than "High Priority" events and will take precedence over low priority events.
  98. ///
  99. /// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
  100. ///
  101. /// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc).
  102. Medium = 1,
  103. /// "Low Priority" work will always be preempted unless the work is significantly delayed, in which case it will be
  104. /// advanced to the front of the work queue until completed.
  105. ///
  106. /// The primary user of Low Priority work is the asynchronous work system (Suspense).
  107. ///
  108. /// This is considered "idle" work or "background" work.
  109. Low = 0,
  110. }
  111. /// The internal Dioxus type that carries any event data to the relevant handler.
  112. pub struct AnyEvent {
  113. pub(crate) bubble_state: Rc<BubbleState>,
  114. pub(crate) data: Arc<dyn Any + Send + Sync>,
  115. }
  116. impl AnyEvent {
  117. /// Convert this [`AnyEvent`] into a specific [`UiEvent`] with [`EventData`].
  118. ///
  119. /// ```rust, ignore
  120. /// let evt: FormEvent = evvt.downcast().unwrap();
  121. /// ```
  122. #[must_use]
  123. pub fn downcast<T: Send + Sync + 'static>(self) -> Option<UiEvent<T>> {
  124. let AnyEvent { data, bubble_state } = self;
  125. data.downcast::<T>()
  126. .ok()
  127. .map(|data| UiEvent { data, bubble_state })
  128. }
  129. }
  130. /// A [`UiEvent`] is a type that wraps various [`EventData`].
  131. ///
  132. /// You should prefer to use the name of the event directly, rather than
  133. /// the [`UiEvent`]<T> generic type.
  134. ///
  135. /// For the HTML crate, this would include `MouseEvent`, `FormEvent` etc.
  136. pub struct UiEvent<T> {
  137. /// The internal data of the event
  138. /// This is wrapped in an Arc so that it can be sent across threads
  139. pub data: Arc<T>,
  140. #[allow(unused)]
  141. bubble_state: Rc<BubbleState>,
  142. }
  143. impl<T: Debug> std::fmt::Debug for UiEvent<T> {
  144. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  145. f.debug_struct("UiEvent").field("data", &self.data).finish()
  146. }
  147. }
  148. impl<T> std::ops::Deref for UiEvent<T> {
  149. type Target = T;
  150. fn deref(&self) -> &Self::Target {
  151. self.data.as_ref()
  152. }
  153. }
  154. impl<T> UiEvent<T> {
  155. /// Prevent this event from bubbling up the tree.
  156. pub fn cancel_bubble(&self) {
  157. self.bubble_state.canceled.set(true);
  158. }
  159. }