focus.rs 11 KB

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