events.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. /// rsx!(cx, 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)]
  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. /// The event data to be passed onto the event handler
  59. pub data: Arc<dyn Any + Send + Sync>,
  60. }
  61. /// Priority of Event Triggers.
  62. ///
  63. /// Internally, Dioxus will abort work that's taking too long if new, more important work arrives. Unlike React, Dioxus
  64. /// won't be afraid to pause work or flush changes to the RealDOM. This is called "cooperative scheduling". Some Renderers
  65. /// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
  66. ///
  67. /// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes.
  68. ///
  69. /// React has a 5-tier priority system. However, they break things into "Continuous" and "Discrete" priority. For now,
  70. /// we keep it simple, and just use a 3-tier priority system.
  71. ///
  72. /// - NoPriority = 0
  73. /// - LowPriority = 1
  74. /// - NormalPriority = 2
  75. /// - UserBlocking = 3
  76. /// - HighPriority = 4
  77. /// - ImmediatePriority = 5
  78. ///
  79. /// We still have a concept of discrete vs continuous though - discrete events won't be batched, but continuous events will.
  80. /// This means that multiple "scroll" events will be processed in a single frame, but multiple "click" events will be
  81. /// flushed before proceeding. Multiple discrete events is highly unlikely, though.
  82. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
  83. pub enum EventPriority {
  84. /// Work that must be completed during the EventHandler phase.
  85. ///
  86. /// Currently this is reserved for controlled inputs.
  87. Immediate = 3,
  88. /// "High Priority" work will not interrupt other high priority work, but will interrupt medium and low priority work.
  89. ///
  90. /// This is typically reserved for things like user interaction.
  91. ///
  92. /// React calls these "discrete" events, but with an extra category of "user-blocking" (Immediate).
  93. High = 2,
  94. /// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
  95. /// than "High Priority" events and will take precedence over low priority events.
  96. ///
  97. /// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
  98. ///
  99. /// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc).
  100. Medium = 1,
  101. /// "Low Priority" work will always be preempted unless the work is significantly delayed, in which case it will be
  102. /// advanced to the front of the work queue until completed.
  103. ///
  104. /// The primary user of Low Priority work is the asynchronous work system (Suspense).
  105. ///
  106. /// This is considered "idle" work or "background" work.
  107. Low = 0,
  108. }
  109. pub struct AnyEvent {
  110. pub(crate) bubble_state: Rc<BubbleState>,
  111. pub(crate) data: Arc<dyn Any + Send + Sync>,
  112. }
  113. impl AnyEvent {
  114. pub fn downcast<T: Send + Sync + 'static>(self) -> Option<UiEvent<T>> {
  115. let AnyEvent { data, bubble_state } = self;
  116. if let Ok(data) = data.downcast::<T>() {
  117. Some(UiEvent { bubble_state, data })
  118. } else {
  119. None
  120. }
  121. }
  122. }
  123. pub struct UiEvent<T> {
  124. pub data: Arc<T>,
  125. #[allow(unused)]
  126. bubble_state: Rc<BubbleState>,
  127. }
  128. impl<T: Debug> std::fmt::Debug for UiEvent<T> {
  129. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  130. f.debug_struct("UiEvent").field("data", &self.data).finish()
  131. }
  132. }
  133. impl<T> std::ops::Deref for UiEvent<T> {
  134. type Target = T;
  135. fn deref(&self) -> &Self::Target {
  136. self.data.as_ref()
  137. }
  138. }
  139. impl<T> UiEvent<T> {
  140. /// Prevent this event from bubbling up the tree.
  141. pub fn cancel_bubble(&self) {
  142. self.bubble_state.canceled.set(true);
  143. }
  144. }