1use std::cell::{Cell, RefCell};
60use std::collections::{HashMap, HashSet};
61use std::rc::Rc;
62
63use teksilo_core::signal::Signal;
64
65use crate::dnd_types::{
66 DragEligibility, DragSource, DropCommit, DropPosition, DropQuery, DropResponse, ItemKey,
67};
68use crate::tree_data_source::{FlatEntry, TreeDataSource};
69
70#[derive(Debug, Clone)]
78pub struct TreeRow<K, T> {
79 pub key: K,
82 pub item: T,
84 pub depth: usize,
86 pub has_children: Option<bool>,
108}
109
110impl<K, T> TreeRow<K, T> {
111 pub fn new(key: K, item: T, depth: usize) -> Self {
115 Self {
116 key,
117 item,
118 depth,
119 has_children: None,
120 }
121 }
122
123 pub fn with_children(mut self, has_children: bool) -> Self {
129 self.has_children = Some(has_children);
130 self
131 }
132}
133
134type ReorderFn<K> = Rc<dyn Fn(K, K, DropPosition) -> bool>;
144type DragPolicyFn<K> = Rc<dyn Fn(&K) -> DragEligibility>;
146type DropResolverFn<K, T> = Rc<dyn Fn(&K, &K, &T, DropPosition) -> Option<DropPosition>>;
153type SourceFn<K, T> = Rc<dyn Fn() -> Vec<TreeRow<K, T>>>;
155
156struct Row<K, T> {
158 key: K,
159 item: T,
160 depth: usize,
162 parent: Option<K>,
163 has_children: bool,
164 declared: bool,
167}
168
169struct Built<K, T> {
171 rows: Vec<Row<K, T>>,
172 children: HashMap<K, Vec<usize>>,
173 roots: Vec<usize>,
174 row_pos: HashMap<K, usize>,
175 visible: Vec<usize>,
176 vis_pos: HashMap<K, usize>,
177 expanded: HashSet<K>,
178 seen: HashSet<K>,
179}
180
181struct Inner<K: ItemKey, T> {
182 rows: RefCell<Vec<Row<K, T>>>,
183 children: RefCell<HashMap<K, Vec<usize>>>,
185 roots: RefCell<Vec<usize>>,
187 row_pos: RefCell<HashMap<K, usize>>,
189 visible: RefCell<Vec<usize>>,
191 vis_pos: RefCell<HashMap<K, usize>>,
193 expanded: RefCell<HashSet<K>>,
194 seen: RefCell<HashSet<K>>,
197 expand_new: Cell<bool>,
198 all_expanded: Cell<bool>,
201 version: Signal<u64>,
202 version_counter: Cell<u64>,
203 divergence: Cell<Option<usize>>,
204 source: RefCell<Option<SourceFn<K, T>>>,
205 reorder: RefCell<Option<ReorderFn<K>>>,
206 drag_policy: RefCell<Option<DragPolicyFn<K>>>,
207 drop_resolver: RefCell<Option<DropResolverFn<K, T>>>,
208}
209
210pub struct TreeDataSlice<K: ItemKey, T> {
213 inner: Rc<Inner<K, T>>,
214}
215
216impl<K: ItemKey, T> Clone for TreeDataSlice<K, T> {
217 fn clone(&self) -> Self {
218 Self {
219 inner: self.inner.clone(),
220 }
221 }
222}
223
224impl<K: ItemKey, T> Default for TreeDataSlice<K, T> {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229
230impl<K: ItemKey, T> TreeDataSlice<K, T> {
231 pub fn new() -> Self {
235 Self {
236 inner: Rc::new(Inner {
237 rows: RefCell::new(Vec::new()),
238 children: RefCell::new(HashMap::new()),
239 roots: RefCell::new(Vec::new()),
240 row_pos: RefCell::new(HashMap::new()),
241 visible: RefCell::new(Vec::new()),
242 vis_pos: RefCell::new(HashMap::new()),
243 expanded: RefCell::new(HashSet::new()),
244 seen: RefCell::new(HashSet::new()),
245 expand_new: Cell::new(false),
246 all_expanded: Cell::new(false),
247 version: Signal::new(0),
248 version_counter: Cell::new(0),
249 divergence: Cell::new(None),
250 source: RefCell::new(None),
251 reorder: RefCell::new(None),
252 drag_policy: RefCell::new(None),
253 drop_resolver: RefCell::new(None),
254 }),
255 }
256 }
257
258 pub fn set_source(&self, f: impl Fn() -> Vec<TreeRow<K, T>> + 'static) {
263 *self.inner.source.borrow_mut() = Some(Rc::new(f));
264 }
265
266 pub fn set_reorder(&self, f: impl Fn(K, K, DropPosition) -> bool + 'static) {
269 *self.inner.reorder.borrow_mut() = Some(Rc::new(f));
270 }
271
272 pub fn set_drag_policy(&self, f: impl Fn(&K) -> DragEligibility + 'static) {
274 *self.inner.drag_policy.borrow_mut() = Some(Rc::new(f));
275 }
276
277 pub fn set_drop_resolver(
284 &self,
285 f: impl Fn(&K, &K, &T, DropPosition) -> Option<DropPosition> + 'static,
286 ) {
287 *self.inner.drop_resolver.borrow_mut() = Some(Rc::new(f));
288 }
289
290 pub fn set_expand_new_nodes(&self, expand: bool) {
294 self.inner.expand_new.set(expand);
295 }
296
297 pub fn from_rows(rows: Vec<TreeRow<K, T>>) -> Self {
302 let slice = Self::new();
303 let built = slice.build(rows);
304 slice.commit(built);
305 slice
306 }
307
308 pub fn reload(&self)
311 where
312 T: PartialEq,
313 {
314 let f = {
318 let src = self.inner.source.borrow();
319 match src.as_ref() {
320 Some(f) => f.clone(),
321 None => return,
322 }
323 };
324 self.set_rows(f());
325 }
326
327 pub fn set_rows(&self, rows: Vec<TreeRow<K, T>>)
331 where
332 T: PartialEq,
333 {
334 let built = self.build(rows);
335 let all = self.inner.all_expanded.get();
336 let div = {
337 let old_rows = self.inner.rows.borrow();
338 let old_visible = self.inner.visible.borrow();
339 let old_expanded = self.inner.expanded.borrow();
340 common_prefix(
341 &old_rows,
342 &old_visible,
343 &old_expanded,
344 all,
345 &built.rows,
346 &built.visible,
347 &built.expanded,
348 all,
349 )
350 };
351 self.commit(built);
352 self.inner.divergence.set(Some(div));
353 self.bump();
354 }
355
356 pub fn visible_count(&self) -> usize {
360 self.inner.visible.borrow().len()
361 }
362
363 pub fn with_entry<R>(
365 &self,
366 flat_index: usize,
367 f: impl FnOnce(&T, &FlatEntry<K>) -> R,
368 ) -> Option<R> {
369 let visible = self.inner.visible.borrow();
370 let &row_idx = visible.get(flat_index)?;
371 let rows = self.inner.rows.borrow();
372 let row = rows.get(row_idx)?;
373 let entry = FlatEntry {
374 node_id: row.key.clone(),
375 depth: row.depth,
376 has_children: row.has_children,
377 is_expanded: self.inner.all_expanded.get()
378 || self.inner.expanded.borrow().contains(&row.key),
379 };
380 Some(f(&row.item, &entry))
381 }
382
383 pub fn with_key<R>(&self, key: &K, f: impl FnOnce(&T) -> R) -> Option<R> {
389 let idx = *self.inner.row_pos.borrow().get(key)?;
390 let rows = self.inner.rows.borrow();
391 rows.get(idx).map(|r| f(&r.item))
392 }
393
394 pub fn key_at(&self, flat_index: usize) -> Option<K> {
396 let visible = self.inner.visible.borrow();
397 let &row_idx = visible.get(flat_index)?;
398 self.inner.rows.borrow().get(row_idx).map(|r| r.key.clone())
399 }
400
401 pub fn entry_at(&self, flat_index: usize) -> Option<FlatEntry<K>> {
403 let visible = self.inner.visible.borrow();
404 let &row_idx = visible.get(flat_index)?;
405 let rows = self.inner.rows.borrow();
406 let row = rows.get(row_idx)?;
407 Some(FlatEntry {
408 node_id: row.key.clone(),
409 depth: row.depth,
410 has_children: row.has_children,
411 is_expanded: self.inner.all_expanded.get()
412 || self.inner.expanded.borrow().contains(&row.key),
413 })
414 }
415
416 pub fn depth_at(&self, flat_index: usize) -> usize {
418 let visible = self.inner.visible.borrow();
419 visible
420 .get(flat_index)
421 .and_then(|&i| self.inner.rows.borrow().get(i).map(|r| r.depth))
422 .unwrap_or(0)
423 }
424
425 pub fn flat_index_of(&self, key: &K) -> Option<usize> {
427 self.inner.vis_pos.borrow().get(key).copied()
428 }
429
430 pub fn contains_key(&self, key: &K) -> bool {
433 self.inner.row_pos.borrow().contains_key(key)
434 }
435
436 pub fn parent_of(&self, key: &K) -> Option<K> {
438 let idx = *self.inner.row_pos.borrow().get(key)?;
439 self.inner
440 .rows
441 .borrow()
442 .get(idx)
443 .and_then(|r| r.parent.clone())
444 }
445
446 pub fn child_keys_of(&self, key: &K) -> Vec<K> {
448 let children = self.inner.children.borrow();
449 let rows = self.inner.rows.borrow();
450 children
451 .get(key)
452 .map(|idxs| {
453 idxs.iter()
454 .filter_map(|&i| rows.get(i).map(|r| r.key.clone()))
455 .collect()
456 })
457 .unwrap_or_default()
458 }
459
460 pub fn is_expanded(&self, key: &K) -> bool {
467 self.inner.all_expanded.get() || self.inner.expanded.borrow().contains(key)
468 }
469
470 pub fn expand(&self, key: &K)
472 where
473 T: PartialEq,
474 {
475 self.set_expanded_flag(key, true);
476 }
477
478 pub fn collapse(&self, key: &K)
480 where
481 T: PartialEq,
482 {
483 self.set_expanded_flag(key, false);
484 }
485
486 pub fn toggle(&self, key: &K)
488 where
489 T: PartialEq,
490 {
491 let expanded = self.inner.expanded.borrow().contains(key);
493 self.set_expanded_flag(key, !expanded);
494 }
495
496 pub fn expand_all(&self)
498 where
499 T: PartialEq,
500 {
501 let target: HashSet<K> = {
502 let rows = self.inner.rows.borrow();
503 rows.iter()
504 .filter(|r| r.has_children)
505 .map(|r| r.key.clone())
506 .collect()
507 };
508 self.replace_expanded(target);
509 }
510
511 pub fn collapse_all(&self)
513 where
514 T: PartialEq,
515 {
516 self.replace_expanded(HashSet::new());
517 }
518
519 pub fn expanded_keys(&self) -> Vec<K> {
521 self.inner.expanded.borrow().iter().cloned().collect()
522 }
523
524 pub fn set_expanded_keys(&self, keys: &[K])
527 where
528 T: PartialEq,
529 {
530 self.replace_expanded(keys.iter().cloned().collect());
531 }
532
533 pub fn version_signal(&self) -> Signal<u64> {
538 self.inner.version.clone()
539 }
540
541 pub fn first_changed_index(&self) -> Option<usize> {
547 self.inner.divergence.get()
548 }
549
550 fn set_expanded_flag(&self, key: &K, expanded: bool)
553 where
554 T: PartialEq,
555 {
556 let mut target = self.inner.expanded.borrow().clone();
557 let changed = if expanded {
558 target.insert(key.clone())
559 } else {
560 target.remove(key)
561 };
562 if !changed {
563 return;
564 }
565 self.replace_expanded(target);
566 }
567
568 fn replace_expanded(&self, target: HashSet<K>)
571 where
572 T: PartialEq,
573 {
574 let all = self.inner.all_expanded.get();
575 let (visible, vis_pos) = {
576 let rows = self.inner.rows.borrow();
577 let children = self.inner.children.borrow();
578 let roots = self.inner.roots.borrow();
579 flatten(&rows, &children, &roots, &target, all)
580 };
581 let div = {
582 let rows = self.inner.rows.borrow();
583 let old_visible = self.inner.visible.borrow();
584 let old_expanded = self.inner.expanded.borrow();
585 common_prefix(
586 &rows,
587 &old_visible,
588 &old_expanded,
589 all,
590 &rows,
591 &visible,
592 &target,
593 all,
594 )
595 };
596 *self.inner.visible.borrow_mut() = visible;
597 *self.inner.vis_pos.borrow_mut() = vis_pos;
598 *self.inner.expanded.borrow_mut() = target;
599 self.inner.divergence.set(Some(div));
600 self.bump();
601 }
602
603 pub fn set_all_expanded(&self, on: bool)
610 where
611 T: PartialEq,
612 {
613 if self.inner.all_expanded.get() == on {
614 return;
615 }
616 let expanded = self.inner.expanded.borrow().clone();
617 let (visible, vis_pos) = {
618 let rows = self.inner.rows.borrow();
619 let children = self.inner.children.borrow();
620 let roots = self.inner.roots.borrow();
621 flatten(&rows, &children, &roots, &expanded, on)
622 };
623 let div = {
624 let rows = self.inner.rows.borrow();
625 let old_visible = self.inner.visible.borrow();
626 common_prefix(
627 &rows,
628 &old_visible,
629 &expanded,
630 !on, &rows,
632 &visible,
633 &expanded,
634 on,
635 )
636 };
637 self.inner.all_expanded.set(on);
638 *self.inner.visible.borrow_mut() = visible;
639 *self.inner.vis_pos.borrow_mut() = vis_pos;
640 self.inner.divergence.set(Some(div));
641 self.bump();
642 }
643
644 pub fn all_expanded(&self) -> bool {
646 self.inner.all_expanded.get()
647 }
648
649 fn build(&self, input: Vec<TreeRow<K, T>>) -> Built<K, T> {
653 let mut rows: Vec<Row<K, T>> = Vec::with_capacity(input.len());
655 let mut stack: Vec<(usize, K, usize)> = Vec::new();
657 for tr in input {
658 while let Some((d, _, _)) = stack.last() {
659 if *d >= tr.depth {
660 stack.pop();
661 } else {
662 break;
663 }
664 }
665 let (parent, struct_depth) = match stack.last() {
666 Some((_, k, pidx)) => (Some(k.clone()), rows[*pidx].depth + 1),
667 None => (None, 0),
668 };
669 let idx = rows.len();
670 let key = tr.key.clone();
671 rows.push(Row {
672 key: tr.key,
673 item: tr.item,
674 depth: struct_depth,
675 parent,
676 has_children: tr.has_children.unwrap_or(false),
678 declared: tr.has_children.is_some(),
679 });
680 stack.push((tr.depth, key, idx));
681 }
682
683 let mut children: HashMap<K, Vec<usize>> = HashMap::new();
685 let mut roots: Vec<usize> = Vec::new();
686 let mut row_pos: HashMap<K, usize> = HashMap::with_capacity(rows.len());
687 for (i, r) in rows.iter().enumerate() {
688 row_pos.insert(r.key.clone(), i);
689 match &r.parent {
690 Some(pk) => children.entry(pk.clone()).or_default().push(i),
691 None => roots.push(i),
692 }
693 }
694 for r in rows.iter_mut() {
699 if !r.declared {
700 r.has_children = children.get(&r.key).is_some_and(|v| !v.is_empty());
701 }
702 }
703
704 let expand_new = self.inner.expand_new.get();
706 let (mut expanded, mut seen) = {
707 let old_exp = self.inner.expanded.borrow();
708 let old_seen = self.inner.seen.borrow();
709 let mut e = HashSet::new();
710 let s = old_seen.clone();
711 for r in &rows {
712 if old_seen.contains(&r.key) {
713 if old_exp.contains(&r.key) {
714 e.insert(r.key.clone());
715 }
716 } else if expand_new && r.has_children {
717 e.insert(r.key.clone());
718 }
719 }
720 (e, s)
721 };
722 for r in &rows {
723 seen.insert(r.key.clone());
724 }
725 expanded.retain(|k| row_pos.contains_key(k));
727
728 let (visible, vis_pos) = flatten(
730 &rows,
731 &children,
732 &roots,
733 &expanded,
734 self.inner.all_expanded.get(),
735 );
736
737 Built {
738 rows,
739 children,
740 roots,
741 row_pos,
742 visible,
743 vis_pos,
744 expanded,
745 seen,
746 }
747 }
748
749 fn commit(&self, built: Built<K, T>) {
752 *self.inner.rows.borrow_mut() = built.rows;
753 *self.inner.children.borrow_mut() = built.children;
754 *self.inner.roots.borrow_mut() = built.roots;
755 *self.inner.row_pos.borrow_mut() = built.row_pos;
756 *self.inner.visible.borrow_mut() = built.visible;
757 *self.inner.vis_pos.borrow_mut() = built.vis_pos;
758 *self.inner.expanded.borrow_mut() = built.expanded;
759 *self.inner.seen.borrow_mut() = built.seen;
760 }
761
762 fn bump(&self) {
763 let next = self.inner.version_counter.get() + 1;
764 self.inner.version_counter.set(next);
765 self.inner.version.set(next);
766 }
767
768 fn is_descendant(&self, maybe_descendant: &K, ancestor: &K) -> bool {
770 let rows = self.inner.rows.borrow();
771 let row_pos = self.inner.row_pos.borrow();
772 let mut cur = maybe_descendant.clone();
773 for _ in 0..rows.len() {
774 let Some(&idx) = row_pos.get(&cur) else {
775 return false;
776 };
777 let Some(parent) = rows[idx].parent.clone() else {
778 return false;
779 };
780 if &parent == ancestor {
781 return true;
782 }
783 cur = parent;
784 }
785 false
786 }
787
788 fn resolve(&self, dragged: &K, target: &K, position: DropPosition) -> Option<DropPosition> {
790 if dragged == target || self.is_descendant(target, dragged) {
791 return None;
792 }
793 let resolver = self.inner.drop_resolver.borrow().clone();
797 match resolver {
798 Some(f) => {
799 let row_pos = self.inner.row_pos.borrow();
800 let &idx = row_pos.get(target)?; let rows = self.inner.rows.borrow();
802 f(dragged, target, &rows[idx].item, position)
803 }
804 None => Some(position),
805 }
806 }
807}
808
809impl<K: ItemKey, T> std::fmt::Debug for TreeDataSlice<K, T> {
810 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
811 f.debug_struct("TreeDataSlice")
812 .field("visible_count", &self.visible_count())
813 .field("row_count", &self.inner.rows.borrow().len())
814 .field("expanded_count", &self.inner.expanded.borrow().len())
815 .finish()
816 }
817}
818
819fn flatten<K: ItemKey, T>(
822 rows: &[Row<K, T>],
823 children: &HashMap<K, Vec<usize>>,
824 roots: &[usize],
825 expanded: &HashSet<K>,
826 all_expanded: bool,
827) -> (Vec<usize>, HashMap<K, usize>) {
828 let mut visible = Vec::with_capacity(rows.len());
829 let mut vis_pos = HashMap::with_capacity(rows.len());
830 for &root in roots {
831 flatten_node(
832 root,
833 rows,
834 children,
835 expanded,
836 all_expanded,
837 &mut visible,
838 &mut vis_pos,
839 );
840 }
841 (visible, vis_pos)
842}
843
844fn flatten_node<K: ItemKey, T>(
845 idx: usize,
846 rows: &[Row<K, T>],
847 children: &HashMap<K, Vec<usize>>,
848 expanded: &HashSet<K>,
849 all_expanded: bool,
850 visible: &mut Vec<usize>,
851 vis_pos: &mut HashMap<K, usize>,
852) {
853 let row = &rows[idx];
854 vis_pos.insert(row.key.clone(), visible.len());
855 visible.push(idx);
856 if row.has_children
857 && (all_expanded || expanded.contains(&row.key))
858 && let Some(kids) = children.get(&row.key)
859 {
860 for &child in kids {
861 flatten_node(
862 child,
863 rows,
864 children,
865 expanded,
866 all_expanded,
867 visible,
868 vis_pos,
869 );
870 }
871 }
872}
873
874#[allow(clippy::too_many_arguments)]
879fn common_prefix<K: ItemKey, T: PartialEq>(
880 old_rows: &[Row<K, T>],
881 old_visible: &[usize],
882 old_expanded: &HashSet<K>,
883 old_all: bool,
884 new_rows: &[Row<K, T>],
885 new_visible: &[usize],
886 new_expanded: &HashSet<K>,
887 new_all: bool,
888) -> usize {
889 let n = old_visible.len().min(new_visible.len());
890 for i in 0..n {
891 let o = &old_rows[old_visible[i]];
892 let m = &new_rows[new_visible[i]];
893 let o_exp = old_all || old_expanded.contains(&o.key);
894 let m_exp = new_all || new_expanded.contains(&m.key);
895 if o.key != m.key
896 || o.depth != m.depth
897 || o.has_children != m.has_children
898 || o_exp != m_exp
899 || o.item != m.item
900 {
901 return i;
902 }
903 }
904 n
905}
906
907impl<K: ItemKey, T: PartialEq + 'static> TreeDataSource for TreeDataSlice<K, T> {
913 type Item = T;
914 type Key = K;
915
916 fn visible_count(&self) -> usize {
917 TreeDataSlice::visible_count(self)
918 }
919
920 fn with_entry<R>(
921 &self,
922 flat_index: usize,
923 f: impl FnOnce(&Self::Item, &FlatEntry<Self::Key>) -> R,
924 ) -> Option<R> {
925 TreeDataSlice::with_entry(self, flat_index, f)
926 }
927
928 fn key_at(&self, flat_index: usize) -> Option<K> {
929 TreeDataSlice::key_at(self, flat_index)
930 }
931
932 fn flat_index_of(&self, key: &K) -> Option<usize> {
933 TreeDataSlice::flat_index_of(self, key)
934 }
935
936 fn parent(&self, key: &K) -> Option<K> {
937 TreeDataSlice::parent_of(self, key)
938 }
939
940 fn child_keys(&self, key: &K) -> Vec<K> {
941 TreeDataSlice::child_keys_of(self, key)
942 }
943
944 fn version_signal(&self) -> Signal<u64> {
945 TreeDataSlice::version_signal(self)
946 }
947
948 fn first_changed_index(&self) -> Option<usize> {
949 TreeDataSlice::first_changed_index(self)
950 }
951
952 fn contains_key(&self, key: &K) -> bool {
953 TreeDataSlice::contains_key(self, key)
954 }
955
956 fn is_expanded(&self, key: &K) -> bool {
957 TreeDataSlice::is_expanded(self, key)
958 }
959
960 fn set_expanded(&self, key: &K, expanded: bool) {
961 self.set_expanded_flag(key, expanded);
962 }
963
964 fn drag(&self, key: &K) -> DragEligibility {
965 let policy = self.inner.drag_policy.borrow().clone();
968 match policy {
969 Some(f) => f(key),
970 None => DragEligibility::NoDrag,
971 }
972 }
973
974 fn can_accept(&self, query: &DropQuery<'_, K>) -> DropResponse {
975 let dragged = match &query.source {
976 DragSource::SameView { key } => key,
977 DragSource::Foreign { .. } => return DropResponse::Reject,
978 };
979 match self.resolve(dragged, &query.target, query.position) {
980 Some(p) if p == query.position => DropResponse::Accept,
981 Some(p) => DropResponse::Redirect(p),
982 None => DropResponse::Reject,
983 }
984 }
985
986 fn accept_drop(&self, commit: DropCommit<'_, K>) -> bool {
987 let dragged = match &commit.source {
988 DragSource::SameView { key } => key.clone(),
989 DragSource::Foreign { .. } => return false,
990 };
991 let Some(place) = self.resolve(&dragged, &commit.target, commit.position) else {
992 return false;
993 };
994 let reorder = self.inner.reorder.borrow().clone();
999 let applied = match reorder {
1000 Some(f) => f(dragged, commit.target.clone(), place),
1001 None => return false,
1002 };
1003 if applied {
1004 self.reload();
1005 true
1006 } else {
1007 false
1008 }
1009 }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use super::*;
1015
1016 fn sample() -> Vec<TreeRow<u64, &'static str>> {
1026 vec![
1027 TreeRow::new(1, "M", 0),
1028 TreeRow::new(101, "Book", 1),
1029 TreeRow::new(102, "Opening", 2),
1030 TreeRow::new(103, "Dawn", 2),
1031 TreeRow::new(104, "Ch2", 1),
1032 TreeRow::new(105, "Fight", 2),
1033 TreeRow::new(2, "N", 0),
1034 TreeRow::new(106, "Sketch", 1),
1035 ]
1036 }
1037
1038 fn expanded_slice() -> TreeDataSlice<u64, &'static str> {
1039 let slice = TreeDataSlice::new();
1040 slice.set_expand_new_nodes(true);
1041 slice.set_source(sample);
1042 slice.reload();
1043 slice
1044 }
1045
1046 #[test]
1053 fn a_declared_parent_keeps_its_chevron_with_no_children_in_the_stream() {
1054 let slice = TreeDataSlice::from_rows(vec![
1055 TreeRow::new(1, "Scene one", 0).with_children(true),
1056 TreeRow::new(2, "Scene two", 0).with_children(true),
1057 ]);
1058 assert_eq!(slice.visible_count(), 2);
1059 for i in 0..2 {
1060 slice.with_entry(i, |_, e| {
1061 assert!(e.has_children, "a promise the stream cannot corroborate");
1062 });
1063 }
1064 }
1065
1066 #[test]
1069 fn a_declared_leaf_stays_a_leaf() {
1070 let slice = TreeDataSlice::from_rows(vec![
1071 TreeRow::new(1, "Parent", 0).with_children(false),
1072 TreeRow::new(2, "Child", 1),
1073 ]);
1074 slice.set_expanded_keys(&[1]);
1075 slice.with_entry(0, |_, e| assert!(!e.has_children));
1076 }
1077
1078 #[test]
1081 fn an_undeclared_row_is_still_derived_from_the_stream() {
1082 let slice = TreeDataSlice::from_rows(sample());
1083 slice.with_entry(0, |item, e| {
1084 assert_eq!(*item, "M");
1085 assert!(e.has_children, "M has Book and Ch2 beneath it");
1086 });
1087 slice.with_entry(2, |item, e| {
1088 assert_eq!(*item, "Opening");
1089 assert!(!e.has_children, "Opening has nothing beneath it");
1090 });
1091 }
1092
1093 #[test]
1097 fn a_declared_parent_can_be_expanded_and_then_filled_in() {
1098 let filled = std::rc::Rc::new(std::cell::Cell::new(false));
1099 let slice = TreeDataSlice::new();
1100 slice.set_source({
1101 let filled = filled.clone();
1102 move || {
1103 let mut rows = vec![TreeRow::new(1, "Scene one", 0).with_children(true)];
1104 if filled.get() {
1105 rows.push(TreeRow::new(11, "hit at 42", 1));
1106 rows.push(TreeRow::new(12, "hit at 91", 1));
1107 }
1108 rows
1109 }
1110 });
1111 slice.reload();
1112 slice.with_entry(0, |_, e| assert!(e.has_children));
1113 assert_eq!(slice.visible_count(), 1);
1114
1115 slice.set_expanded_keys(&[1]);
1116 assert!(slice.is_expanded(&1));
1117 assert_eq!(slice.visible_count(), 1);
1119
1120 filled.set(true);
1121 slice.reload();
1122 assert!(slice.is_expanded(&1), "the expand survived the reload");
1123 assert_eq!(
1124 slice.visible_count(),
1125 3,
1126 "and the branch is now really there"
1127 );
1128 slice.with_entry(0, |_, e| assert!(e.has_children));
1129 }
1130
1131 #[test]
1132 fn structure_derivation() {
1133 let slice = TreeDataSlice::from_rows(sample());
1134 assert_eq!(slice.parent_of(&1), None); assert_eq!(slice.parent_of(&101), Some(1)); assert_eq!(slice.parent_of(&102), Some(101)); assert_eq!(slice.parent_of(&104), Some(1)); assert_eq!(slice.parent_of(&105), Some(104)); assert_eq!(slice.parent_of(&106), Some(2)); assert_eq!(slice.child_keys_of(&1), vec![101, 104]);
1143 assert_eq!(slice.child_keys_of(&101), vec![102, 103]);
1144 assert_eq!(slice.child_keys_of(&102), Vec::<u64>::new()); }
1146
1147 #[test]
1148 fn collapsed_by_default_shows_roots() {
1149 let slice = TreeDataSlice::from_rows(sample());
1150 assert_eq!(slice.visible_count(), 2); assert_eq!(slice.key_at(0), Some(1));
1152 assert_eq!(slice.key_at(1), Some(2));
1153 }
1154
1155 #[test]
1156 fn expand_new_shows_all() {
1157 let slice = expanded_slice();
1158 assert_eq!(slice.visible_count(), 8);
1159 assert_eq!(
1160 slice.with_entry(1, |item, e| {
1161 assert_eq!(*item, "Book");
1162 assert_eq!(e.depth, 1);
1163 assert!(e.has_children);
1164 }),
1165 Some(())
1166 );
1167 }
1168
1169 #[test]
1170 fn collapse_hides_subtree() {
1171 let slice = expanded_slice();
1172 assert_eq!(slice.visible_count(), 8);
1173 slice.collapse(&101); assert_eq!(slice.visible_count(), 6);
1175 slice.expand(&101);
1176 assert_eq!(slice.visible_count(), 8);
1177 }
1178
1179 #[test]
1180 fn toggle_and_flat_index() {
1181 let slice = TreeDataSlice::from_rows(sample());
1182 assert_eq!(slice.flat_index_of(&1), Some(0));
1183 assert_eq!(slice.flat_index_of(&101), None); slice.toggle(&1);
1185 assert_eq!(slice.flat_index_of(&101), Some(1));
1186 assert!(slice.is_expanded(&1));
1187 }
1188
1189 #[test]
1190 fn expand_all_collapse_all() {
1191 let slice = TreeDataSlice::from_rows(sample());
1192 slice.expand_all();
1193 assert_eq!(slice.visible_count(), 8);
1194 slice.collapse_all();
1195 assert_eq!(slice.visible_count(), 2);
1196 }
1197
1198 #[test]
1199 fn set_all_expanded_reveals_then_restores() {
1200 let slice = TreeDataSlice::from_rows(sample()); assert_eq!(slice.visible_count(), 2);
1202 assert!(!slice.all_expanded());
1203
1204 slice.set_all_expanded(true);
1205 assert_eq!(slice.visible_count(), 8); assert!(slice.all_expanded());
1207 assert!(slice.is_expanded(&1)); slice.set_all_expanded(false);
1210 assert_eq!(slice.visible_count(), 2); assert!(!slice.all_expanded());
1212 }
1213
1214 #[test]
1215 fn reveal_preserves_raw_expand_set() {
1216 let slice = TreeDataSlice::from_rows(sample());
1217 slice.expand(&1); assert_eq!(slice.visible_count(), 4);
1219
1220 slice.set_all_expanded(true);
1221 assert_eq!(slice.visible_count(), 8);
1222
1223 slice.set_all_expanded(false);
1224 assert_eq!(slice.visible_count(), 4);
1226 assert_eq!(slice.expanded_keys(), vec![1]);
1227 }
1228
1229 #[test]
1230 fn filter_keepancestors_reveal_shows_matches() {
1231 use crate::{TreeFilterMode, TreeRowFilter};
1235 let sieve = TreeRowFilter::new()
1236 .filter_mode(TreeFilterMode::KeepAncestors)
1237 .filter(|t: &&str| *t == "Dawn");
1238 let slice = TreeDataSlice::from_rows(sieve.apply(sample()));
1239 assert_eq!(slice.visible_count(), 1);
1241
1242 slice.set_all_expanded(true);
1243 assert_eq!(slice.visible_count(), 3);
1244 let titles: Vec<&str> = (0..3)
1245 .map(|i| slice.with_entry(i, |it, _| *it).unwrap())
1246 .collect();
1247 assert_eq!(titles, vec!["M", "Book", "Dawn"]);
1248 }
1249
1250 #[test]
1251 fn two_slices_independent_expand() {
1252 let a = TreeDataSlice::from_rows(sample());
1253 let b = TreeDataSlice::from_rows(sample());
1254 a.expand(&1);
1255 assert_eq!(a.visible_count(), 4); assert_eq!(b.visible_count(), 2); }
1258
1259 #[test]
1260 fn clone_shares_state() {
1261 let a = TreeDataSlice::from_rows(sample());
1262 let b = a.clone();
1263 a.expand(&1);
1264 assert_eq!(b.visible_count(), 4); }
1266
1267 #[test]
1270 fn divergence_none_before_change() {
1271 let slice = TreeDataSlice::from_rows(sample());
1272 assert_eq!(slice.first_changed_index(), None);
1273 }
1274
1275 #[test]
1276 fn divergence_on_expand_is_toggled_row() {
1277 let slice = TreeDataSlice::from_rows(sample());
1278 slice.expand(&1);
1280 assert_eq!(slice.first_changed_index(), Some(0));
1281 }
1282
1283 #[test]
1284 fn divergence_on_deep_expand_is_that_row() {
1285 let slice = TreeDataSlice::from_rows(sample());
1286 slice.expand(&1); slice.expand(&101);
1289 assert_eq!(slice.first_changed_index(), Some(1));
1290 }
1291
1292 #[test]
1293 fn divergence_on_rename_is_that_row() {
1294 let slice = expanded_slice();
1295 let renamed: Vec<TreeRow<u64, &'static str>> = sample()
1297 .into_iter()
1298 .map(|mut r| {
1299 if r.key == 105 {
1300 r.item = "Duel";
1301 }
1302 r
1303 })
1304 .collect();
1305 slice.set_rows(renamed);
1306 assert_eq!(slice.first_changed_index(), Some(5));
1308 }
1309
1310 #[test]
1311 fn divergence_on_append_is_old_len() {
1312 let slice = expanded_slice();
1313 let mut rows = sample();
1314 rows.push(TreeRow::new(107, "Idea", 1)); slice.set_rows(rows);
1316 assert_eq!(slice.first_changed_index(), Some(8));
1318 }
1319
1320 #[test]
1321 fn reload_preserves_expand_by_key() {
1322 let counter = Rc::new(Cell::new(0u32));
1323 let c = counter.clone();
1324 let slice: TreeDataSlice<u64, &'static str> = TreeDataSlice::new();
1325 slice.set_source(move || {
1326 c.set(c.get() + 1);
1327 sample()
1328 });
1329 slice.reload();
1330 slice.expand(&1);
1331 assert_eq!(slice.visible_count(), 4);
1332 slice.reload(); assert_eq!(slice.visible_count(), 4); assert!(slice.is_expanded(&1));
1335 }
1336
1337 #[test]
1340 fn drag_policy_gate() {
1341 let slice = TreeDataSlice::from_rows(sample());
1342 slice.set_drag_policy(|k| {
1343 if *k < 100 {
1344 DragEligibility::NoDrag } else {
1346 DragEligibility::CanDrag
1347 }
1348 });
1349 assert_eq!(slice.drag(&1), DragEligibility::NoDrag);
1350 assert_eq!(slice.drag(&102), DragEligibility::CanDrag);
1351 }
1352
1353 #[test]
1354 fn drag_default_is_nodrag() {
1355 let slice = TreeDataSlice::from_rows(sample());
1356 assert_eq!(slice.drag(&102), DragEligibility::NoDrag);
1357 }
1358
1359 #[test]
1360 fn can_accept_rejects_cycle() {
1361 let slice = expanded_slice();
1362 let q = DropQuery {
1364 source: DragSource::SameView { key: 101 },
1365 target: 102,
1366 position: DropPosition::Into,
1367 };
1368 assert_eq!(slice.can_accept(&q), DropResponse::Reject);
1369 }
1370
1371 #[test]
1372 fn can_accept_default_accepts_sibling() {
1373 let slice = expanded_slice();
1374 let q = DropQuery {
1375 source: DragSource::SameView { key: 102 },
1376 target: 106,
1377 position: DropPosition::Before,
1378 };
1379 assert_eq!(slice.can_accept(&q), DropResponse::Accept);
1380 }
1381
1382 #[test]
1383 fn drop_resolver_redirects() {
1384 let slice = expanded_slice();
1385 slice.set_drop_resolver(|_dragged, target, target_item, pos| match pos {
1388 DropPosition::Into if *target == 103 && *target_item == "Dawn" => {
1389 Some(DropPosition::After)
1390 }
1391 p => Some(p),
1392 });
1393 let q = DropQuery {
1394 source: DragSource::SameView { key: 102 },
1395 target: 103,
1396 position: DropPosition::Into,
1397 };
1398 assert_eq!(
1399 slice.can_accept(&q),
1400 DropResponse::Redirect(DropPosition::After)
1401 );
1402 }
1403
1404 #[test]
1405 fn accept_drop_runs_reorder_then_reloads() {
1406 let moved = Rc::new(Cell::new(false));
1407 let m = moved.clone();
1408 let slice = expanded_slice();
1409 slice.set_reorder(move |dragged, target, _pos| {
1410 assert_eq!(dragged, 102);
1411 assert_eq!(target, 106);
1412 m.set(true);
1413 true
1414 });
1415 let ok = slice.accept_drop(DropCommit {
1416 source: DragSource::SameView { key: 102 },
1417 target: 106,
1418 position: DropPosition::Before,
1419 });
1420 assert!(ok);
1421 assert!(moved.get());
1422 }
1423
1424 #[test]
1425 fn reorder_closure_can_call_back_into_the_slice() {
1426 let slice = expanded_slice();
1433 let reentrant_target = slice.clone();
1434 slice.set_reorder(move |_dragged, _target, _pos| {
1435 reentrant_target.set_reorder(|_, _, _| true);
1436 true
1437 });
1438 let ok = slice.accept_drop(DropCommit {
1439 source: DragSource::SameView { key: 102 },
1440 target: 106,
1441 position: DropPosition::Before,
1442 });
1443 assert!(ok);
1444 }
1445
1446 #[test]
1447 fn source_closure_can_call_back_into_the_slice() {
1448 let slice = TreeDataSlice::<u64, &'static str>::new();
1454 let reentrant = slice.clone();
1455 slice.set_source(move || {
1456 reentrant.set_source(Vec::new);
1457 vec![TreeRow::new(1, "root", 0)]
1458 });
1459 slice.reload();
1460 assert_eq!(slice.visible_count(), 1);
1461 }
1462
1463 #[test]
1464 fn accept_drop_without_reorder_is_refused() {
1465 let slice = expanded_slice();
1466 let ok = slice.accept_drop(DropCommit {
1467 source: DragSource::SameView { key: 102 },
1468 target: 106,
1469 position: DropPosition::Before,
1470 });
1471 assert!(!ok);
1472 }
1473
1474 #[test]
1475 fn foreign_drop_rejected() {
1476 let slice = expanded_slice();
1477 slice.set_reorder(|_, _, _| true);
1478 let ok = slice.accept_drop(DropCommit {
1482 source: DragSource::SameView { key: 1 },
1483 target: 1, position: DropPosition::Into,
1485 });
1486 assert!(!ok);
1487 }
1488}