events.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. use bumpalo::boxed::Box as BumpBox;
  2. use std::{
  3. any::Any,
  4. cell::{Cell, RefCell},
  5. fmt::Debug,
  6. rc::Rc,
  7. };
  8. pub struct UiEvent<T: 'static + ?Sized> {
  9. pub(crate) bubbles: Rc<Cell<bool>>,
  10. pub(crate) data: Rc<T>,
  11. }
  12. impl UiEvent<dyn Any> {
  13. pub fn downcast<T: 'static + Sized>(self) -> Option<UiEvent<T>> {
  14. Some(UiEvent {
  15. bubbles: self.bubbles,
  16. data: self.data.downcast().ok()?,
  17. })
  18. }
  19. }
  20. impl<T: ?Sized> Clone for UiEvent<T> {
  21. fn clone(&self) -> Self {
  22. Self {
  23. bubbles: self.bubbles.clone(),
  24. data: self.data.clone(),
  25. }
  26. }
  27. }
  28. impl<T> UiEvent<T> {
  29. pub fn cancel_bubble(&self) {
  30. self.bubbles.set(false);
  31. }
  32. }
  33. impl<T> std::ops::Deref for UiEvent<T> {
  34. type Target = Rc<T>;
  35. fn deref(&self) -> &Self::Target {
  36. &self.data
  37. }
  38. }
  39. impl<T: Debug> std::fmt::Debug for UiEvent<T> {
  40. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  41. f.debug_struct("UiEvent")
  42. .field("bubble_state", &self.bubbles)
  43. .field("data", &self.data)
  44. .finish()
  45. }
  46. }
  47. /// Priority of Event Triggers.
  48. ///
  49. /// Internally, Dioxus will abort work that's taking too long if new, more important work arrives. Unlike React, Dioxus
  50. /// won't be afraid to pause work or flush changes to the Real Dom. This is called "cooperative scheduling". Some Renderers
  51. /// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
  52. ///
  53. /// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes.
  54. ///
  55. /// React has a 5-tier priority system. However, they break things into "Continuous" and "Discrete" priority. For now,
  56. /// we keep it simple, and just use a 3-tier priority system.
  57. ///
  58. /// - `NoPriority` = 0
  59. /// - `LowPriority` = 1
  60. /// - `NormalPriority` = 2
  61. /// - `UserBlocking` = 3
  62. /// - `HighPriority` = 4
  63. /// - `ImmediatePriority` = 5
  64. ///
  65. /// We still have a concept of discrete vs continuous though - discrete events won't be batched, but continuous events will.
  66. /// This means that multiple "scroll" events will be processed in a single frame, but multiple "click" events will be
  67. /// flushed before proceeding. Multiple discrete events is highly unlikely, though.
  68. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
  69. pub enum EventPriority {
  70. /// Work that must be completed during the EventHandler phase.
  71. ///
  72. /// Currently this is reserved for controlled inputs.
  73. Immediate = 3,
  74. /// "High Priority" work will not interrupt other high priority work, but will interrupt medium and low priority work.
  75. ///
  76. /// This is typically reserved for things like user interaction.
  77. ///
  78. /// React calls these "discrete" events, but with an extra category of "user-blocking" (Immediate).
  79. High = 2,
  80. /// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
  81. /// than "High Priority" events and will take precedence over low priority events.
  82. ///
  83. /// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
  84. ///
  85. /// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc).
  86. Medium = 1,
  87. /// "Low Priority" work will always be preempted unless the work is significantly delayed, in which case it will be
  88. /// advanced to the front of the work queue until completed.
  89. ///
  90. /// The primary user of Low Priority work is the asynchronous work system (Suspense).
  91. ///
  92. /// This is considered "idle" work or "background" work.
  93. Low = 0,
  94. }
  95. type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
  96. /// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
  97. ///
  98. /// This makes it possible to pass `move |evt| {}` style closures into components as property fields.
  99. ///
  100. ///
  101. /// # Example
  102. ///
  103. /// ```rust, ignore
  104. ///
  105. /// rsx!{
  106. /// MyComponent { onclick: move |evt| log::info!("clicked"), }
  107. /// }
  108. ///
  109. /// #[derive(Props)]
  110. /// struct MyProps<'a> {
  111. /// onclick: EventHandler<'a, MouseEvent>,
  112. /// }
  113. ///
  114. /// fn MyComponent(cx: Scope<'a, MyProps<'a>>) -> Element {
  115. /// cx.render(rsx!{
  116. /// button {
  117. /// onclick: move |evt| cx.props.onclick.call(evt),
  118. /// }
  119. /// })
  120. /// }
  121. ///
  122. /// ```
  123. pub struct EventHandler<'bump, T = ()> {
  124. /// The (optional) callback that the user specified
  125. /// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
  126. pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
  127. }
  128. impl<'a, T> Default for EventHandler<'a, T> {
  129. fn default() -> Self {
  130. Self {
  131. callback: RefCell::new(None),
  132. }
  133. }
  134. }
  135. impl<T> EventHandler<'_, T> {
  136. /// Call this event handler with the appropriate event type
  137. pub fn call(&self, event: T) {
  138. if let Some(callback) = self.callback.borrow_mut().as_mut() {
  139. callback(event);
  140. }
  141. }
  142. /// Forcibly drop the internal handler callback, releasing memory
  143. pub fn release(&self) {
  144. self.callback.replace(None);
  145. }
  146. }
  147. #[test]
  148. fn matches_slice() {
  149. let left = &[1, 2, 3];
  150. let right = &[1, 2, 3, 4, 5];
  151. assert!(is_path_ascendant(left, right));
  152. assert!(!is_path_ascendant(right, left));
  153. assert!(!is_path_ascendant(left, left));
  154. assert!(is_path_ascendant(&[1, 2], &[1, 2, 3, 4, 5]));
  155. }