focus.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. use crate::prevent_default::PreventDefault;
  2. use dioxus_native_core::{
  3. node_ref::{AttributeMaskBuilder, NodeMaskBuilder},
  4. prelude::*,
  5. real_dom::NodeImmutable,
  6. utils::{IteratorMovement, PersistantElementIter},
  7. };
  8. use dioxus_native_core_macro::partial_derive_state;
  9. use once_cell::sync::Lazy;
  10. use rustc_hash::FxHashSet;
  11. use shipyard::Component;
  12. use shipyard::{Get, ViewMut};
  13. use std::{cmp::Ordering, num::NonZeroU16};
  14. use dioxus_native_core::node_ref::NodeView;
  15. #[derive(Component)]
  16. pub struct Focused(pub bool);
  17. #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
  18. pub(crate) enum FocusLevel {
  19. #[default]
  20. Unfocusable,
  21. Focusable,
  22. Ordered(std::num::NonZeroU16),
  23. }
  24. impl FocusLevel {
  25. pub fn focusable(&self) -> bool {
  26. match self {
  27. FocusLevel::Unfocusable => false,
  28. FocusLevel::Focusable => true,
  29. FocusLevel::Ordered(_) => true,
  30. }
  31. }
  32. }
  33. impl PartialOrd for FocusLevel {
  34. fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
  35. match (self, other) {
  36. (FocusLevel::Unfocusable, FocusLevel::Unfocusable) => Some(std::cmp::Ordering::Equal),
  37. (FocusLevel::Unfocusable, FocusLevel::Focusable) => Some(std::cmp::Ordering::Less),
  38. (FocusLevel::Unfocusable, FocusLevel::Ordered(_)) => Some(std::cmp::Ordering::Less),
  39. (FocusLevel::Focusable, FocusLevel::Unfocusable) => Some(std::cmp::Ordering::Greater),
  40. (FocusLevel::Focusable, FocusLevel::Focusable) => Some(std::cmp::Ordering::Equal),
  41. (FocusLevel::Focusable, FocusLevel::Ordered(_)) => Some(std::cmp::Ordering::Greater),
  42. (FocusLevel::Ordered(_), FocusLevel::Unfocusable) => Some(std::cmp::Ordering::Greater),
  43. (FocusLevel::Ordered(_), FocusLevel::Focusable) => Some(std::cmp::Ordering::Less),
  44. (FocusLevel::Ordered(a), FocusLevel::Ordered(b)) => a.partial_cmp(b),
  45. }
  46. }
  47. }
  48. impl Ord for FocusLevel {
  49. fn cmp(&self, other: &Self) -> std::cmp::Ordering {
  50. self.partial_cmp(other).unwrap()
  51. }
  52. }
  53. #[derive(Clone, PartialEq, Debug, Default, Component)]
  54. pub(crate) struct Focus {
  55. pub level: FocusLevel,
  56. }
  57. #[partial_derive_state]
  58. impl State for Focus {
  59. const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
  60. .with_attrs(AttributeMaskBuilder::Some(FOCUS_ATTRIBUTES))
  61. .with_listeners();
  62. type ParentDependencies = ();
  63. type ChildDependencies = ();
  64. type NodeDependencies = ();
  65. fn update<'a>(
  66. &mut self,
  67. node_view: NodeView,
  68. _: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
  69. _: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
  70. _: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
  71. _: &SendAnyMap,
  72. ) -> bool {
  73. let new = Focus {
  74. level: if let Some(a) = node_view
  75. .attributes()
  76. .and_then(|mut a| a.find(|a| a.attribute.name == "tabindex"))
  77. {
  78. if let Some(index) = a
  79. .value
  80. .as_int()
  81. .or_else(|| a.value.as_text().and_then(|v| v.parse::<i64>().ok()))
  82. {
  83. match index.cmp(&0) {
  84. Ordering::Less => FocusLevel::Unfocusable,
  85. Ordering::Equal => FocusLevel::Focusable,
  86. Ordering::Greater => {
  87. FocusLevel::Ordered(NonZeroU16::new(index as u16).unwrap())
  88. }
  89. }
  90. } else {
  91. FocusLevel::Unfocusable
  92. }
  93. } else if node_view
  94. .listeners()
  95. .and_then(|mut listeners| {
  96. listeners.any(|l| FOCUS_EVENTS.contains(&l)).then_some(())
  97. })
  98. .is_some()
  99. {
  100. FocusLevel::Focusable
  101. } else {
  102. FocusLevel::Unfocusable
  103. },
  104. };
  105. if *self != new {
  106. *self = new;
  107. true
  108. } else {
  109. false
  110. }
  111. }
  112. fn create<'a>(
  113. node_view: NodeView<()>,
  114. node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
  115. parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
  116. children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
  117. context: &SendAnyMap,
  118. ) -> Self {
  119. let mut myself = Self::default();
  120. myself.update(node_view, node, parent, children, context);
  121. myself
  122. }
  123. }
  124. static FOCUS_EVENTS: Lazy<FxHashSet<&str>> =
  125. Lazy::new(|| ["keydown", "keypress", "keyup"].into_iter().collect());
  126. const FOCUS_ATTRIBUTES: &[&str] = &["tabindex"];
  127. pub(crate) struct FocusState {
  128. pub(crate) focus_iter: PersistantElementIter,
  129. pub(crate) last_focused_id: Option<NodeId>,
  130. pub(crate) focus_level: FocusLevel,
  131. pub(crate) dirty: bool,
  132. }
  133. impl FocusState {
  134. pub fn create(rdom: &mut RealDom) -> Self {
  135. let focus_iter = PersistantElementIter::create(rdom);
  136. Self {
  137. focus_iter,
  138. last_focused_id: Default::default(),
  139. focus_level: Default::default(),
  140. dirty: Default::default(),
  141. }
  142. }
  143. /// Returns true if the focus has changed.
  144. pub fn progress(&mut self, rdom: &mut RealDom, forward: bool) -> bool {
  145. if let Some(last) = self.last_focused_id {
  146. if rdom.get(last).unwrap().get::<PreventDefault>().map(|p| *p)
  147. == Some(PreventDefault::KeyDown)
  148. {
  149. return false;
  150. }
  151. }
  152. // the id that started focused to track when a loop has happened
  153. let mut loop_marker_id = self.last_focused_id;
  154. let focus_level = &mut self.focus_level;
  155. let mut next_focus = None;
  156. loop {
  157. let new = if forward {
  158. self.focus_iter.next(rdom)
  159. } else {
  160. self.focus_iter.prev(rdom)
  161. };
  162. let new_id = new.id();
  163. if let IteratorMovement::Looped = new.movement() {
  164. let mut closest_level = None;
  165. if forward {
  166. // find the closest focusable element after the current level
  167. rdom.traverse_depth_first(|n| {
  168. let node_level = n.get::<Focus>().unwrap().level;
  169. if node_level != *focus_level
  170. && node_level.focusable()
  171. && node_level > *focus_level
  172. {
  173. if let Some(level) = &mut closest_level {
  174. if node_level < *level {
  175. *level = node_level;
  176. }
  177. } else {
  178. closest_level = Some(node_level);
  179. }
  180. }
  181. });
  182. } else {
  183. // find the closest focusable element before the current level
  184. rdom.traverse_depth_first(|n| {
  185. let node_level = n.get::<Focus>().unwrap().level;
  186. if node_level != *focus_level
  187. && node_level.focusable()
  188. && node_level < *focus_level
  189. {
  190. if let Some(level) = &mut closest_level {
  191. if node_level > *level {
  192. *level = node_level;
  193. }
  194. } else {
  195. closest_level = Some(node_level);
  196. }
  197. }
  198. });
  199. }
  200. // extend the loop_marker_id to allow for another pass
  201. loop_marker_id = None;
  202. if let Some(level) = closest_level {
  203. *focus_level = level;
  204. } else if forward {
  205. *focus_level = FocusLevel::Unfocusable;
  206. } else {
  207. *focus_level = FocusLevel::Focusable;
  208. }
  209. }
  210. // once we have looked at all the elements exit the loop
  211. if let Some(last) = loop_marker_id {
  212. if new_id == last {
  213. break;
  214. }
  215. } else {
  216. loop_marker_id = Some(new_id);
  217. }
  218. let current_level = rdom.get(new_id).unwrap().get::<Focus>().unwrap().level;
  219. let after_previous_focused = if forward {
  220. current_level >= *focus_level
  221. } else {
  222. current_level <= *focus_level
  223. };
  224. if after_previous_focused && current_level.focusable() && current_level == *focus_level
  225. {
  226. next_focus = Some(new_id);
  227. break;
  228. }
  229. }
  230. if let Some(id) = next_focus {
  231. let mut node = rdom.get_mut(id).unwrap();
  232. if !node.get::<Focus>().unwrap().level.focusable() {
  233. panic!()
  234. }
  235. node.insert(Focused(true));
  236. if let Some(old) = self.last_focused_id.replace(id) {
  237. let mut focused_borrow: ViewMut<Focused> = rdom.raw_world().borrow().unwrap();
  238. let focused = (&mut focused_borrow).get(old).unwrap();
  239. focused.0 = false;
  240. }
  241. // reset the position to the currently focused element
  242. while self.focus_iter.next(rdom).id() != id {}
  243. self.dirty = true;
  244. return true;
  245. }
  246. false
  247. }
  248. pub(crate) fn set_focus(&mut self, rdom: &mut RealDom, id: NodeId) {
  249. if let Some(old) = self.last_focused_id.replace(id) {
  250. let mut node = rdom.get_mut(old).unwrap();
  251. node.insert(Focused(false));
  252. }
  253. let mut node = rdom.get_mut(id).unwrap();
  254. node.insert(Focused(true));
  255. self.focus_level = node.get::<Focus>().unwrap().level;
  256. // reset the position to the currently focused element
  257. while self.focus_iter.next(rdom).id() != id {}
  258. self.dirty = true;
  259. }
  260. pub(crate) fn clean(&mut self) -> bool {
  261. let old = self.dirty;
  262. self.dirty = false;
  263. old
  264. }
  265. }