utils.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. use crate::{node::NodeType, real_dom::RealDom, state::State, tree::TreeView, NodeId, RealNodeId};
  2. use dioxus_core::{Mutation, Mutations};
  3. use std::fmt::Debug;
  4. #[derive(Debug)]
  5. pub enum ElementProduced {
  6. /// The iterator produced an element by progressing to the next node in a depth first order.
  7. Progressed(RealNodeId),
  8. /// The iterator reached the end of the tree and looped back to the root
  9. Looped(RealNodeId),
  10. }
  11. impl ElementProduced {
  12. pub fn id(&self) -> RealNodeId {
  13. match self {
  14. ElementProduced::Progressed(id) => *id,
  15. ElementProduced::Looped(id) => *id,
  16. }
  17. }
  18. }
  19. #[derive(Debug)]
  20. enum NodePosition {
  21. AtNode,
  22. InChild(usize),
  23. }
  24. impl NodePosition {
  25. fn map(&self, mut f: impl FnMut(usize) -> usize) -> Self {
  26. match self {
  27. Self::AtNode => Self::AtNode,
  28. Self::InChild(i) => Self::InChild(f(*i)),
  29. }
  30. }
  31. fn get_or_insert(&mut self, child_idx: usize) -> usize {
  32. match self {
  33. Self::AtNode => {
  34. *self = Self::InChild(child_idx);
  35. child_idx
  36. }
  37. Self::InChild(i) => *i,
  38. }
  39. }
  40. }
  41. /// Focus systems need a iterator that can persist through changes in the [dioxus_core::VirtualDom].
  42. /// This iterator traverses the tree depth first.
  43. /// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]).
  44. /// The iterator loops around when it reaches the end or the beginning.
  45. pub struct PersistantElementIter {
  46. // stack of elements and fragments
  47. stack: smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>,
  48. }
  49. impl Default for PersistantElementIter {
  50. fn default() -> Self {
  51. PersistantElementIter {
  52. stack: smallvec::smallvec![(NodeId(0), NodePosition::AtNode)],
  53. }
  54. }
  55. }
  56. impl PersistantElementIter {
  57. pub fn new() -> Self {
  58. Self::default()
  59. }
  60. /// remove stale element refreneces
  61. /// returns true if the focused element is removed
  62. pub fn prune<S: State>(&mut self, mutations: &Mutations, rdom: &RealDom<S>) -> bool {
  63. let mut changed = false;
  64. let ids_removed: Vec<_> = mutations
  65. .edits
  66. .iter()
  67. .filter_map(|m| {
  68. // nodes within templates will never be removedns
  69. if let Mutation::Remove { id } = m {
  70. Some(rdom.element_to_node_id(*id))
  71. } else {
  72. None
  73. }
  74. })
  75. .collect();
  76. // if any element is removed in the chain, remove it and its children from the stack
  77. if let Some(r) = self
  78. .stack
  79. .iter()
  80. .position(|(el_id, _)| ids_removed.iter().any(|id| el_id == id))
  81. {
  82. self.stack.truncate(r);
  83. changed = true;
  84. }
  85. // if a child is removed or inserted before or at the current element, update the child index
  86. for (el_id, child_idx) in self.stack.iter_mut() {
  87. if let NodePosition::InChild(child_idx) = child_idx {
  88. if let Some(children) = &rdom.tree.children_ids(*el_id) {
  89. for m in &mutations.edits {
  90. match m {
  91. Mutation::Remove { id } => {
  92. let id = rdom.element_to_node_id(*id);
  93. if children.iter().take(*child_idx + 1).any(|c| *c == id) {
  94. *child_idx -= 1;
  95. }
  96. }
  97. Mutation::InsertBefore { id, m } => {
  98. let id = rdom.element_to_node_id(*id);
  99. if children.iter().take(*child_idx + 1).any(|c| *c == id) {
  100. *child_idx += *m;
  101. }
  102. }
  103. Mutation::InsertAfter { id, m } => {
  104. let id = rdom.element_to_node_id(*id);
  105. if children.iter().take(*child_idx).any(|c| *c == id) {
  106. *child_idx += *m;
  107. }
  108. }
  109. _ => (),
  110. }
  111. }
  112. }
  113. }
  114. }
  115. changed
  116. }
  117. /// get the next element
  118. pub fn next<S: State>(&mut self, rdom: &RealDom<S>) -> ElementProduced {
  119. if self.stack.is_empty() {
  120. let id = NodeId(0);
  121. let new = (id, NodePosition::AtNode);
  122. self.stack.push(new);
  123. ElementProduced::Looped(id)
  124. } else {
  125. let (last, old_child_idx) = self.stack.last_mut().unwrap();
  126. let node = &rdom[*last];
  127. match &node.node_data.node_type {
  128. NodeType::Element { .. } => {
  129. let children = rdom.tree.children_ids(*last).unwrap();
  130. *old_child_idx = old_child_idx.map(|i| i + 1);
  131. // if we have children, go to the next child
  132. let child_idx = old_child_idx.get_or_insert(0);
  133. if child_idx >= children.len() {
  134. self.pop();
  135. self.next(rdom)
  136. } else {
  137. let id = children[child_idx];
  138. if let NodeType::Element { .. } = &rdom[id].node_data.node_type {
  139. self.stack.push((id, NodePosition::AtNode));
  140. }
  141. ElementProduced::Progressed(id)
  142. }
  143. }
  144. NodeType::Text { .. } | NodeType::Placeholder { .. } => {
  145. // we are at a leaf, so we are done
  146. ElementProduced::Progressed(self.pop())
  147. }
  148. }
  149. }
  150. }
  151. /// get the previous element
  152. pub fn prev<S: State>(&mut self, rdom: &RealDom<S>) -> ElementProduced {
  153. // recursively add the last child element to the stack
  154. fn push_back<S: State>(
  155. stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>,
  156. new_node: RealNodeId,
  157. rdom: &RealDom<S>,
  158. ) -> RealNodeId {
  159. match &rdom[new_node].node_data.node_type {
  160. NodeType::Element { .. } => {
  161. let children = rdom.tree.children_ids(new_node).unwrap();
  162. if children.is_empty() {
  163. new_node
  164. } else {
  165. stack.push((new_node, NodePosition::InChild(children.len() - 1)));
  166. push_back(stack, *children.last().unwrap(), rdom)
  167. }
  168. }
  169. _ => new_node,
  170. }
  171. }
  172. if self.stack.is_empty() {
  173. let new_node = NodeId(0);
  174. ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom))
  175. } else {
  176. let (last, old_child_idx) = self.stack.last_mut().unwrap();
  177. let node = &rdom[*last];
  178. match &node.node_data.node_type {
  179. NodeType::Element { .. } => {
  180. let children = rdom.tree.children_ids(*last).unwrap();
  181. // if we have children, go to the next child
  182. if let NodePosition::InChild(0) = old_child_idx {
  183. ElementProduced::Progressed(self.pop())
  184. } else {
  185. *old_child_idx = old_child_idx.map(|i| i - 1);
  186. if let NodePosition::InChild(child_idx) = old_child_idx {
  187. if *child_idx >= children.len() || children.is_empty() {
  188. self.pop();
  189. self.prev(rdom)
  190. } else {
  191. let new_node = children[*child_idx];
  192. ElementProduced::Progressed(push_back(
  193. &mut self.stack,
  194. new_node,
  195. rdom,
  196. ))
  197. }
  198. } else {
  199. self.pop();
  200. self.prev(rdom)
  201. }
  202. }
  203. }
  204. NodeType::Text { .. } | NodeType::Placeholder { .. } => {
  205. // we are at a leaf, so we are done
  206. ElementProduced::Progressed(self.pop())
  207. }
  208. }
  209. }
  210. }
  211. fn pop(&mut self) -> RealNodeId {
  212. self.stack.pop().unwrap().0
  213. }
  214. }
  215. #[derive(Default, Clone, Debug)]
  216. struct Empty {}
  217. impl State for Empty {
  218. const PASSES: &'static [crate::AnyPass<crate::node::Node<Self>>] = &[];
  219. const MASKS: &'static [crate::NodeMask] = &[];
  220. }
  221. #[test]
  222. #[allow(unused_variables)]
  223. fn traverse() {
  224. use dioxus::prelude::*;
  225. #[allow(non_snake_case)]
  226. fn Base(cx: Scope) -> Element {
  227. render!(
  228. div{
  229. div{
  230. "hello"
  231. p{
  232. "world"
  233. }
  234. "hello world"
  235. }
  236. }
  237. )
  238. }
  239. let mut vdom = VirtualDom::new(Base);
  240. let mutations = vdom.rebuild();
  241. let mut rdom: RealDom<Empty> = RealDom::new();
  242. let _to_update = rdom.apply_mutations(mutations);
  243. let mut iter = PersistantElementIter::new();
  244. let div_tag = "div".to_string();
  245. assert!(matches!(
  246. &rdom[iter.next(&rdom).id()].node_data.node_type,
  247. NodeType::Element { tag: div_tag, .. }
  248. ));
  249. assert!(matches!(
  250. &rdom[iter.next(&rdom).id()].node_data.node_type,
  251. NodeType::Element { tag: div_tag, .. }
  252. ));
  253. let text1 = "hello".to_string();
  254. assert!(matches!(
  255. &rdom[iter.next(&rdom).id()].node_data.node_type,
  256. NodeType::Text { text: text1, .. }
  257. ));
  258. let p_tag = "p".to_string();
  259. assert!(matches!(
  260. &rdom[iter.next(&rdom).id()].node_data.node_type,
  261. NodeType::Element { tag: p_tag, .. }
  262. ));
  263. let text2 = "world".to_string();
  264. assert!(matches!(
  265. &rdom[iter.next(&rdom).id()].node_data.node_type,
  266. NodeType::Text { text: text2, .. }
  267. ));
  268. let text3 = "hello world".to_string();
  269. assert!(matches!(
  270. &rdom[iter.next(&rdom).id()].node_data.node_type,
  271. NodeType::Text { text: text3, .. }
  272. ));
  273. assert!(matches!(
  274. &rdom[iter.next(&rdom).id()].node_data.node_type,
  275. NodeType::Element { tag: div_tag, .. }
  276. ));
  277. assert!(matches!(
  278. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  279. NodeType::Text { text: text3, .. }
  280. ));
  281. assert!(matches!(
  282. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  283. NodeType::Text { text: text2, .. }
  284. ));
  285. assert!(matches!(
  286. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  287. NodeType::Element { tag: p_tag, .. }
  288. ));
  289. assert!(matches!(
  290. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  291. NodeType::Text { text: text1, .. }
  292. ));
  293. assert!(matches!(
  294. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  295. NodeType::Element { tag: div_tag, .. }
  296. ));
  297. assert!(matches!(
  298. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  299. NodeType::Element { tag: div_tag, .. }
  300. ));
  301. assert!(matches!(
  302. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  303. NodeType::Element { tag: div_tag, .. }
  304. ));
  305. assert!(matches!(
  306. &rdom[iter.prev(&rdom).id()].node_data.node_type,
  307. NodeType::Text { text: text3, .. }
  308. ));
  309. }
  310. #[test]
  311. #[allow(unused_variables)]
  312. fn persist_removes() {
  313. use dioxus::prelude::*;
  314. #[allow(non_snake_case)]
  315. fn Base(cx: Scope) -> Element {
  316. let children = match cx.generation() % 2 {
  317. 0 => 3,
  318. 1 => 2,
  319. _ => unreachable!(),
  320. };
  321. render!(
  322. div{
  323. (0..children).map(|i|{
  324. rsx!{
  325. p{
  326. key: "{i}",
  327. "{i}"
  328. }
  329. }
  330. })
  331. }
  332. )
  333. }
  334. let mut vdom = VirtualDom::new(Base);
  335. let mut rdom: RealDom<Empty> = RealDom::new();
  336. let build = vdom.rebuild();
  337. let _to_update = rdom.apply_mutations(build);
  338. // this will end on the node that is removed
  339. let mut iter1 = PersistantElementIter::new();
  340. // this will end on the after node that is removed
  341. let mut iter2 = PersistantElementIter::new();
  342. // div
  343. iter1.next(&rdom).id();
  344. iter2.next(&rdom).id();
  345. // p
  346. iter1.next(&rdom).id();
  347. iter2.next(&rdom).id();
  348. // "1"
  349. iter1.next(&rdom).id();
  350. iter2.next(&rdom).id();
  351. // p
  352. iter1.next(&rdom).id();
  353. iter2.next(&rdom).id();
  354. // "2"
  355. iter1.next(&rdom).id();
  356. iter2.next(&rdom).id();
  357. // p
  358. iter2.next(&rdom).id();
  359. // "3"
  360. iter2.next(&rdom).id();
  361. vdom.mark_dirty(ScopeId(0));
  362. let update = vdom.render_immediate();
  363. iter1.prune(&update, &rdom);
  364. iter2.prune(&update, &rdom);
  365. let _to_update = rdom.apply_mutations(update);
  366. let p_tag = "1".to_string();
  367. let idx = iter1.next(&rdom).id();
  368. assert!(matches!(
  369. &rdom[idx].node_data.node_type,
  370. NodeType::Element { tag: p_tag, .. }
  371. ));
  372. let text = "2".to_string();
  373. let idx = iter1.next(&rdom).id();
  374. assert!(matches!(
  375. &rdom[idx].node_data.node_type,
  376. NodeType::Text { text, .. }
  377. ));
  378. let div_tag = "div".to_string();
  379. let idx = iter2.next(&rdom).id();
  380. assert!(matches!(
  381. &rdom[idx].node_data.node_type,
  382. NodeType::Element { tag: div_tag, .. }
  383. ));
  384. }
  385. #[test]
  386. #[allow(unused_variables)]
  387. fn persist_instertions_before() {
  388. use dioxus::prelude::*;
  389. #[allow(non_snake_case)]
  390. fn Base(cx: Scope) -> Element {
  391. let children = match cx.generation() % 2 {
  392. 0 => 3,
  393. 1 => 2,
  394. _ => unreachable!(),
  395. };
  396. render!(
  397. div{
  398. (0..children).map(|i|{
  399. rsx!{
  400. p{
  401. key: "{i}",
  402. "{i}"
  403. }
  404. }
  405. })
  406. }
  407. )
  408. }
  409. let mut vdom = VirtualDom::new(Base);
  410. let mut rdom: RealDom<Empty> = RealDom::new();
  411. let build = vdom.rebuild();
  412. let _to_update = rdom.apply_mutations(build);
  413. let mut iter = PersistantElementIter::new();
  414. // div
  415. iter.next(&rdom).id();
  416. // p
  417. iter.next(&rdom).id();
  418. // "1"
  419. iter.next(&rdom).id();
  420. // p
  421. iter.next(&rdom).id();
  422. // "2"
  423. iter.next(&rdom).id();
  424. vdom.mark_dirty(ScopeId(0));
  425. let update = vdom.render_immediate();
  426. iter.prune(&update, &rdom);
  427. let _to_update = rdom.apply_mutations(update);
  428. let p_tag = "div".to_string();
  429. let idx = iter.next(&rdom).id();
  430. assert!(matches!(
  431. &rdom[idx].node_data.node_type,
  432. NodeType::Element { tag: p_tag, .. }
  433. ));
  434. }
  435. #[test]
  436. #[allow(unused_variables)]
  437. fn persist_instertions_after() {
  438. use dioxus::prelude::*;
  439. #[allow(non_snake_case)]
  440. fn Base(cx: Scope) -> Element {
  441. let children = match cx.generation() % 2 {
  442. 0 => 3,
  443. 1 => 2,
  444. _ => unreachable!(),
  445. };
  446. render!(
  447. div{
  448. (0..children).map(|i|{
  449. rsx!{
  450. p{
  451. key: "{i}",
  452. "{i}"
  453. }
  454. }
  455. })
  456. }
  457. )
  458. }
  459. let mut vdom = VirtualDom::new(Base);
  460. let mut rdom: RealDom<Empty> = RealDom::new();
  461. let build = vdom.rebuild();
  462. let _to_update = rdom.apply_mutations(build);
  463. let mut iter = PersistantElementIter::new();
  464. // div
  465. iter.next(&rdom).id();
  466. // p
  467. iter.next(&rdom).id();
  468. // "hello"
  469. iter.next(&rdom).id();
  470. // p
  471. iter.next(&rdom).id();
  472. // "world"
  473. iter.next(&rdom).id();
  474. let update = vdom.rebuild();
  475. iter.prune(&update, &rdom);
  476. let _to_update = rdom.apply_mutations(update);
  477. let p_tag = "p".to_string();
  478. let idx = iter.next(&rdom).id();
  479. assert!(matches!(
  480. &rdom[idx].node_data.node_type,
  481. NodeType::Element { tag: p_tag, .. }
  482. ));
  483. let text = "hello world".to_string();
  484. let idx = iter.next(&rdom).id();
  485. assert!(matches!(
  486. &rdom[idx].node_data.node_type,
  487. NodeType::Text { text, .. }
  488. ));
  489. }