1use crate::{Axis, Direction, Rect, ViewId};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct Pane {
8 pub view: ViewId,
9 pub rect: Rect,
10 pub focused: bool,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14enum Node {
15 Leaf(ViewId),
16 Split {
17 axis: Axis,
18 children: Vec<Node>,
19 weights: Vec<u16>,
23 },
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct Tree {
29 root: Node,
30 focus: Vec<usize>,
32}
33
34const DEFAULT_WEIGHT: u16 = 100;
37
38impl Tree {
39 pub fn new(view: ViewId) -> Self {
40 Self {
41 root: Node::Leaf(view),
42 focus: Vec::new(),
43 }
44 }
45
46 pub fn focused(&self) -> ViewId {
47 match Self::at(&self.root, &self.focus) {
48 Some(Node::Leaf(v)) => *v,
49 _ => self.views().first().copied().unwrap_or(ViewId(0)),
52 }
53 }
54
55 pub fn focus_view(&mut self, view: ViewId) -> bool {
62 let mut path = Vec::new();
63 if Self::path_to(&self.root, view, &mut path) {
64 self.focus = path;
65 return true;
66 }
67 false
68 }
69
70 fn path_to(node: &Node, view: ViewId, path: &mut Vec<usize>) -> bool {
75 match node {
76 Node::Leaf(v) => *v == view,
77 Node::Split { children, .. } => {
78 for (i, child) in children.iter().enumerate() {
79 path.push(i);
80 if Self::path_to(child, view, path) {
81 return true;
82 }
83 path.pop();
84 }
85 false
86 }
87 }
88 }
89
90 pub fn views(&self) -> Vec<ViewId> {
92 let mut out = Vec::new();
93 Self::walk(&self.root, &mut |v| out.push(v));
94 out
95 }
96
97 pub fn len(&self) -> usize {
98 self.views().len()
99 }
100
101 pub fn is_empty(&self) -> bool {
102 false }
104
105 pub fn split(&mut self, axis: Axis, view: ViewId) {
107 let path = self.focus.clone();
108 let Some(node) = Self::at_mut(&mut self.root, &path) else {
109 return;
110 };
111
112 match node {
113 Node::Leaf(existing) => {
115 *node = Node::Split {
116 axis,
117 children: vec![Node::Leaf(*existing), Node::Leaf(view)],
118 weights: vec![DEFAULT_WEIGHT, DEFAULT_WEIGHT],
119 };
120 self.focus.push(1);
121 }
122 Node::Split { .. } => {}
123 }
124
125 self.flatten();
129 }
130
131 pub fn close(&mut self) -> bool {
134 if self.focus.is_empty() {
135 return false;
136 }
137 let (parent_path, index) = {
138 let mut p = self.focus.clone();
139 let i = p.pop().expect("focus is non-empty");
140 (p, i)
141 };
142
143 let Some(Node::Split {
144 children, weights, ..
145 }) = Self::at_mut(&mut self.root, &parent_path)
146 else {
147 return false;
148 };
149 children.remove(index);
150 weights.remove(index);
151
152 if children.len() == 1 {
154 let only = children.remove(0);
155 let Some(parent) = Self::at_mut(&mut self.root, &parent_path) else {
156 return false;
157 };
158 *parent = only;
159 self.focus = parent_path;
160 self.descend_to_leaf();
162 } else {
163 self.focus = parent_path;
165 self.focus.push(index.min(children.len() - 1));
166 self.descend_to_leaf();
167 }
168 self.flatten();
169 true
170 }
171
172 pub fn focus_direction(&mut self, dir: Direction, area: Rect) -> bool {
181 let panes = self.layout_with_paths(area);
182 let Some((_, from)) = panes.iter().find(|(_, p)| p.focused).map(|(a, b)| (a, *b)) else {
183 return false;
184 };
185
186 let axis = dir.axis();
187 let best = panes
188 .iter()
189 .filter(|(_, p)| !p.focused)
190 .filter(|(_, p)| from.rect.overlaps_across(p.rect, axis))
191 .filter(|(_, p)| match dir {
192 Direction::Left => p.rect.right() <= from.rect.x,
193 Direction::Right => p.rect.x >= from.rect.right(),
194 Direction::Up => p.rect.bottom() <= from.rect.y,
195 Direction::Down => p.rect.y >= from.rect.bottom(),
196 })
197 .min_by_key(|(_, p)| {
200 let gap = match dir {
201 Direction::Left => from.rect.x.saturating_sub(p.rect.right()),
202 Direction::Right => p.rect.x.saturating_sub(from.rect.right()),
203 Direction::Up => from.rect.y.saturating_sub(p.rect.bottom()),
204 Direction::Down => p.rect.y.saturating_sub(from.rect.bottom()),
205 };
206 let offset = match axis {
207 Axis::Columns => p.rect.y.abs_diff(from.rect.y),
208 Axis::Rows => p.rect.x.abs_diff(from.rect.x),
209 };
210 (gap, offset)
211 });
212
213 match best {
214 Some((path, _)) => {
215 self.focus = path.clone();
216 true
217 }
218 None => false,
219 }
220 }
221
222 pub fn resize(&mut self, dir: Direction, delta: i32) -> bool {
228 let axis = dir.axis();
229 let mut path = self.focus.clone();
230
231 while !path.is_empty() {
232 let index = *path.last().expect("non-empty");
233 let parent_path = &path[..path.len() - 1];
234 let is_match = matches!(
235 Self::at(&self.root, parent_path),
236 Some(Node::Split { axis: a, .. }) if *a == axis
237 );
238 if is_match {
239 let Some(Node::Split { weights, .. }) = Self::at_mut(&mut self.root, parent_path)
240 else {
241 return false;
242 };
243 if weights.len() < 2 {
244 return false;
245 }
246 let neighbour = if index + 1 < weights.len() {
249 index + 1
250 } else {
251 index - 1
252 };
253 let signed = if dir.is_forward() { delta } else { -delta };
254 let taken =
255 signed.clamp(-(weights[index] as i32 - 1), weights[neighbour] as i32 - 1);
256 if taken == 0 {
257 return false;
258 }
259 weights[index] = (weights[index] as i32 + taken) as u16;
260 weights[neighbour] = (weights[neighbour] as i32 - taken) as u16;
261 return true;
262 }
263 path.pop();
264 }
265 false
266 }
267
268 pub fn equalize(&mut self) {
270 Self::equalize_node(&mut self.root);
271 }
272
273 pub fn layout(&self, area: Rect) -> Vec<Pane> {
275 self.layout_with_paths(area)
276 .into_iter()
277 .map(|(_, pane)| pane)
278 .collect()
279 }
280
281 fn layout_with_paths(&self, area: Rect) -> Vec<(Vec<usize>, Pane)> {
282 let mut out = Vec::new();
283 Self::place(&self.root, area, &mut Vec::new(), &self.focus, &mut out);
284 out
285 }
286
287 fn place(
288 node: &Node,
289 area: Rect,
290 path: &mut Vec<usize>,
291 focus: &[usize],
292 out: &mut Vec<(Vec<usize>, Pane)>,
293 ) {
294 match node {
295 Node::Leaf(view) => out.push((
296 path.clone(),
297 Pane {
298 view: *view,
299 rect: area,
300 focused: path.as_slice() == focus,
301 },
302 )),
303 Node::Split {
304 axis,
305 children,
306 weights,
307 } => {
308 for (i, (child, rect)) in children
309 .iter()
310 .zip(divide(area, *axis, weights))
311 .enumerate()
312 {
313 path.push(i);
314 Self::place(child, rect, path, focus, out);
315 path.pop();
316 }
317 }
318 }
319 }
320
321 fn at<'a>(node: &'a Node, path: &[usize]) -> Option<&'a Node> {
322 match path.split_first() {
323 None => Some(node),
324 Some((i, rest)) => match node {
325 Node::Split { children, .. } => Self::at(children.get(*i)?, rest),
326 Node::Leaf(_) => None,
327 },
328 }
329 }
330
331 fn at_mut<'a>(node: &'a mut Node, path: &[usize]) -> Option<&'a mut Node> {
332 match path.split_first() {
333 None => Some(node),
334 Some((i, rest)) => match node {
335 Node::Split { children, .. } => Self::at_mut(children.get_mut(*i)?, rest),
336 Node::Leaf(_) => None,
337 },
338 }
339 }
340
341 fn walk(node: &Node, f: &mut impl FnMut(ViewId)) {
342 match node {
343 Node::Leaf(v) => f(*v),
344 Node::Split { children, .. } => {
345 for c in children {
346 Self::walk(c, f);
347 }
348 }
349 }
350 }
351
352 fn descend_to_leaf(&mut self) {
354 loop {
355 match Self::at(&self.root, &self.focus) {
356 Some(Node::Split { children, .. }) if !children.is_empty() => self.focus.push(0),
357 _ => return,
358 }
359 }
360 }
361
362 fn flatten(&mut self) {
364 let focused = self.focused();
365 Self::flatten_node(&mut self.root);
366 if let Some(path) = Self::path_of(&self.root, focused, &mut Vec::new()) {
369 self.focus = path;
370 }
371 }
372
373 fn flatten_node(node: &mut Node) {
374 let Node::Split {
375 axis,
376 children,
377 weights,
378 } = node
379 else {
380 return;
381 };
382 for c in children.iter_mut() {
383 Self::flatten_node(c);
384 }
385
386 let mut new_children = Vec::new();
387 let mut new_weights = Vec::new();
388 for (child, weight) in std::mem::take(children)
389 .into_iter()
390 .zip(std::mem::take(weights))
391 {
392 match child {
393 Node::Split {
394 axis: inner_axis,
395 children: inner,
396 weights: inner_weights,
397 } if inner_axis == *axis => {
398 let total: u32 = inner_weights.iter().map(|w| *w as u32).sum::<u32>().max(1);
401 for (c, w) in inner.into_iter().zip(inner_weights) {
402 new_children.push(c);
403 new_weights.push(
404 ((w as u32 * weight as u32) / total)
405 .max(1)
406 .min(u16::MAX as u32) as u16,
407 );
408 }
409 }
410 other => {
411 new_children.push(other);
412 new_weights.push(weight);
413 }
414 }
415 }
416 *children = new_children;
417 *weights = new_weights;
418 }
419
420 fn path_of(node: &Node, view: ViewId, path: &mut Vec<usize>) -> Option<Vec<usize>> {
421 match node {
422 Node::Leaf(v) if *v == view => Some(path.clone()),
423 Node::Leaf(_) => None,
424 Node::Split { children, .. } => {
425 for (i, c) in children.iter().enumerate() {
426 path.push(i);
427 if let Some(found) = Self::path_of(c, view, path) {
428 return Some(found);
429 }
430 path.pop();
431 }
432 None
433 }
434 }
435 }
436
437 fn equalize_node(node: &mut Node) {
438 if let Node::Split {
439 children, weights, ..
440 } = node
441 {
442 for w in weights.iter_mut() {
443 *w = DEFAULT_WEIGHT;
444 }
445 for c in children.iter_mut() {
446 Self::equalize_node(c);
447 }
448 }
449 }
450}
451
452fn divide(area: Rect, axis: Axis, weights: &[u16]) -> Vec<Rect> {
458 let n = weights.len();
459 if n == 0 {
460 return Vec::new();
461 }
462 let total_extent = area.extent(axis);
463 if (total_extent as usize) < n {
466 return (0..n)
467 .map(|i| {
468 let mut r = area;
469 let at = area_start(area, axis) + i as u16;
470 set_span(
471 &mut r,
472 axis,
473 at,
474 if (i as u16) < total_extent { 1 } else { 0 },
475 );
476 r
477 })
478 .collect();
479 }
480
481 let sum: u32 = weights
482 .iter()
483 .map(|w| (*w).max(1) as u32)
484 .sum::<u32>()
485 .max(1);
486 let spare = total_extent as u32 - n as u32; let mut spans: Vec<u16> = weights
488 .iter()
489 .map(|w| 1 + ((*w).max(1) as u32 * spare / sum) as u16)
490 .collect();
491
492 let assigned: u32 = spans.iter().map(|s| *s as u32).sum();
495 let mut leftover = total_extent as u32 - assigned;
496 let mut i = 0;
497 while leftover > 0 {
498 spans[i % n] += 1;
499 leftover -= 1;
500 i += 1;
501 }
502
503 let mut out = Vec::with_capacity(n);
504 let mut at = area_start(area, axis);
505 for span in spans {
506 let mut r = area;
507 set_span(&mut r, axis, at, span);
508 out.push(r);
509 at += span;
510 }
511 out
512}
513
514fn area_start(area: Rect, axis: Axis) -> u16 {
515 match axis {
516 Axis::Columns => area.x,
517 Axis::Rows => area.y,
518 }
519}
520
521fn set_span(r: &mut Rect, axis: Axis, at: u16, span: u16) {
522 match axis {
523 Axis::Columns => {
524 r.x = at;
525 r.width = span;
526 }
527 Axis::Rows => {
528 r.y = at;
529 r.height = span;
530 }
531 }
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537
538 fn v(n: u64) -> ViewId {
539 ViewId(n)
540 }
541
542 const AREA: Rect = Rect {
544 x: 0,
545 y: 0,
546 width: 100,
547 height: 40,
548 };
549
550 fn rects(tree: &Tree) -> Vec<(u64, Rect)> {
551 tree.layout(AREA)
552 .into_iter()
553 .map(|p| (p.view.0, p.rect))
554 .collect()
555 }
556
557 #[test]
558 fn one_window_fills_the_area() {
559 let tree = Tree::new(v(1));
560 assert_eq!(rects(&tree), [(1, AREA)]);
561 assert_eq!(tree.focused(), v(1));
562 assert_eq!(tree.len(), 1);
563 }
564
565 #[test]
566 fn a_column_split_divides_the_width_and_focuses_the_new_window() {
567 let mut tree = Tree::new(v(1));
568 tree.split(Axis::Columns, v(2));
569
570 assert_eq!(tree.focused(), v(2), "vim focuses the window it just made");
571 assert_eq!(
572 rects(&tree),
573 [(1, Rect::new(0, 0, 50, 40)), (2, Rect::new(50, 0, 50, 40))]
574 );
575 }
576
577 #[test]
578 fn a_row_split_divides_the_height() {
579 let mut tree = Tree::new(v(1));
580 tree.split(Axis::Rows, v(2));
581 assert_eq!(
582 rects(&tree),
583 [
584 (1, Rect::new(0, 0, 100, 20)),
585 (2, Rect::new(0, 20, 100, 20))
586 ]
587 );
588 }
589
590 #[test]
591 fn panes_tile_the_area_exactly_with_no_gap_or_overlap() {
592 let mut tree = Tree::new(v(1));
594 tree.split(Axis::Columns, v(2));
595 tree.split(Axis::Columns, v(3));
596
597 let area = Rect::new(0, 0, 100, 40); let mut panes = tree.layout(area);
599 panes.sort_by_key(|p| p.rect.x);
600
601 assert_eq!(panes[0].rect.x, 0);
602 for pair in panes.windows(2) {
603 assert_eq!(
604 pair[0].rect.right(),
605 pair[1].rect.x,
606 "panes must abut exactly"
607 );
608 }
609 assert_eq!(panes.last().unwrap().rect.right(), area.right());
610 }
611
612 #[test]
613 fn splitting_on_the_same_axis_flattens_instead_of_nesting() {
614 let mut tree = Tree::new(v(1));
617 tree.split(Axis::Columns, v(2));
618 tree.split(Axis::Columns, v(3));
619
620 assert_eq!(tree.len(), 3);
621 assert_eq!(tree.views(), [v(1), v(2), v(3)]);
622 assert_eq!(tree.focused(), v(3), "focus survives the flattening");
623
624 let widths: Vec<u16> = tree.layout(AREA).iter().map(|p| p.rect.width).collect();
628 assert_eq!(widths, [50, 25, 25], "flattening must preserve the layout");
629
630 tree.equalize();
632 let widths: Vec<u16> = tree.layout(AREA).iter().map(|p| p.rect.width).collect();
633 assert!(
634 widths.iter().max().unwrap() - widths.iter().min().unwrap() <= 1,
635 "equalize should even them out, got {widths:?}"
636 );
637 }
638
639 #[test]
640 fn splitting_on_the_other_axis_does_nest() {
641 let mut tree = Tree::new(v(1));
642 tree.split(Axis::Columns, v(2));
643 tree.split(Axis::Rows, v(3));
644
645 let layout = rects(&tree);
647 assert_eq!(layout.len(), 3);
648 assert!(layout.contains(&(1, Rect::new(0, 0, 50, 40))));
649 assert!(layout.contains(&(2, Rect::new(50, 0, 50, 20))));
650 assert!(layout.contains(&(3, Rect::new(50, 20, 50, 20))));
651 }
652
653 #[test]
654 fn focus_moves_to_what_looks_that_way() {
655 let mut tree = Tree::new(v(1));
656 tree.split(Axis::Columns, v(2)); assert!(tree.focus_direction(Direction::Left, AREA));
659 assert_eq!(tree.focused(), v(1));
660 assert!(tree.focus_direction(Direction::Right, AREA));
661 assert_eq!(tree.focused(), v(2));
662 }
663
664 #[test]
665 fn focus_does_not_move_off_the_edge() {
666 let mut tree = Tree::new(v(1));
667 tree.split(Axis::Columns, v(2));
668 tree.focus_direction(Direction::Left, AREA); assert!(
671 !tree.focus_direction(Direction::Left, AREA),
672 "nothing there"
673 );
674 assert_eq!(tree.focused(), v(1), "and focus stays put");
675 assert!(!tree.focus_direction(Direction::Up, AREA));
676 }
677
678 #[test]
679 fn focus_skips_windows_that_do_not_share_any_rows() {
680 let mut tree = Tree::new(v(1));
684 tree.split(Axis::Columns, v(2));
685 tree.split(Axis::Rows, v(3));
686 tree.focus_direction(Direction::Left, AREA);
687 assert_eq!(tree.focused(), v(1));
688
689 assert!(tree.focus_direction(Direction::Right, AREA));
690 assert_eq!(tree.focused(), v(2), "the top-right shares row 0 with 1");
691
692 assert!(tree.focus_direction(Direction::Down, AREA));
694 assert_eq!(tree.focused(), v(3));
695 assert!(!tree.focus_direction(Direction::Right, AREA));
696 }
697
698 #[test]
699 fn closing_a_window_gives_its_space_to_the_survivor() {
700 let mut tree = Tree::new(v(1));
701 tree.split(Axis::Columns, v(2));
702
703 assert!(tree.close());
704 assert_eq!(tree.len(), 1);
705 assert_eq!(tree.focused(), v(1));
706 assert_eq!(rects(&tree), [(1, AREA)], "the split collapsed entirely");
707 }
708
709 #[test]
710 fn closing_the_last_window_is_refused() {
711 let mut tree = Tree::new(v(1));
713 assert!(!tree.close());
714 assert_eq!(tree.len(), 1);
715 }
716
717 #[test]
718 fn closing_focuses_a_neighbour_and_never_a_split() {
719 let mut tree = Tree::new(v(1));
720 tree.split(Axis::Columns, v(2));
721 tree.split(Axis::Columns, v(3)); assert!(tree.close());
724 assert_eq!(tree.views(), [v(1), v(2)]);
725 assert_eq!(tree.focused(), v(2), "focus falls back to the neighbour");
726
727 assert!(tree.layout(AREA).iter().any(|p| p.focused));
729 }
730
731 #[test]
732 fn closing_a_nested_window_collapses_its_parent_onto_a_leaf() {
733 let mut tree = Tree::new(v(1));
734 tree.split(Axis::Columns, v(2));
735 tree.split(Axis::Rows, v(3)); assert!(tree.close());
738 assert_eq!(tree.len(), 2);
739 assert_eq!(
740 rects(&tree),
741 [(1, Rect::new(0, 0, 50, 40)), (2, Rect::new(50, 0, 50, 40))],
742 "2 should reclaim the whole right half"
743 );
744 assert_eq!(tree.focused(), v(2));
745 assert_eq!(
746 tree.layout(AREA).iter().filter(|p| p.focused).count(),
747 1,
748 "exactly one window is focused"
749 );
750 }
751
752 #[test]
753 fn resizing_takes_from_the_neighbour_rather_than_conjuring_space() {
754 let mut tree = Tree::new(v(1));
755 tree.split(Axis::Columns, v(2)); let before: u16 = tree.layout(AREA)[0].rect.width;
758 assert!(tree.resize(Direction::Right, 50));
759
760 let after = tree.layout(AREA);
761 assert!(after[1].rect.width > before, "the focused window grew");
762 assert!(after[0].rect.width < before, "its neighbour gave the space");
763 assert_eq!(
764 after[0].rect.width + after[1].rect.width,
765 AREA.width,
766 "the total is unchanged"
767 );
768 }
769
770 #[test]
771 fn resizing_along_an_axis_with_no_split_does_nothing() {
772 let mut tree = Tree::new(v(1));
774 tree.split(Axis::Columns, v(2));
775 assert!(!tree.resize(Direction::Down, 10));
776 assert!(
777 !Tree::new(v(1)).resize(Direction::Right, 10),
778 "one window alone"
779 );
780 }
781
782 #[test]
783 fn a_window_can_never_be_resized_out_of_existence() {
784 let mut tree = Tree::new(v(1));
785 tree.split(Axis::Columns, v(2));
786 for _ in 0..50 {
787 tree.resize(Direction::Right, 1_000);
788 }
789 for pane in tree.layout(AREA) {
790 assert!(pane.rect.width >= 1, "a zero-width window cannot be seen");
791 }
792 }
793
794 #[test]
795 fn equalize_undoes_a_resize() {
796 let mut tree = Tree::new(v(1));
797 tree.split(Axis::Columns, v(2));
798 tree.resize(Direction::Right, 60);
799 tree.equalize();
800
801 let widths: Vec<u16> = tree.layout(AREA).iter().map(|p| p.rect.width).collect();
802 assert!(
803 widths[0].abs_diff(widths[1]) <= 1,
804 "expected even columns, got {widths:?}"
805 );
806 }
807
808 #[test]
809 fn a_layout_keeps_its_proportions_at_a_different_terminal_size() {
810 let mut tree = Tree::new(v(1));
813 tree.split(Axis::Columns, v(2));
814 tree.resize(Direction::Right, 50);
815
816 let wide = tree.layout(Rect::new(0, 0, 200, 40));
817 let narrow = tree.layout(Rect::new(0, 0, 100, 40));
818 let ratio = |p: &[Pane]| p[1].rect.width as f32 / p[0].rect.width as f32;
819 assert!(
820 (ratio(&wide) - ratio(&narrow)).abs() < 0.15,
821 "proportions drifted: {} vs {}",
822 ratio(&wide),
823 ratio(&narrow)
824 );
825 }
826
827 #[test]
828 fn a_tiny_area_does_not_produce_overlapping_windows() {
829 let mut tree = Tree::new(v(1));
832 tree.split(Axis::Columns, v(2));
833 tree.split(Axis::Columns, v(3));
834
835 let panes = tree.layout(Rect::new(0, 0, 2, 1));
836 for pair in panes.windows(2) {
837 assert!(
838 pair[0].rect.right() <= pair[1].rect.x,
839 "windows overlap: {:?}",
840 panes.iter().map(|p| p.rect).collect::<Vec<_>>()
841 );
842 }
843 }
844
845 #[test]
846 fn a_zero_sized_area_does_not_panic() {
847 let mut tree = Tree::new(v(1));
848 tree.split(Axis::Rows, v(2));
849 let panes = tree.layout(Rect::new(0, 0, 0, 0));
850 assert_eq!(panes.len(), 2);
851 assert!(panes.iter().all(|p| p.rect.is_empty()));
852 }
853
854 #[test]
855 fn exactly_one_window_is_focused_however_the_tree_was_built() {
856 let mut tree = Tree::new(v(1));
857 tree.split(Axis::Columns, v(2));
858 tree.split(Axis::Rows, v(3));
859 tree.split(Axis::Columns, v(4));
860 tree.focus_direction(Direction::Left, AREA);
861 tree.close();
862
863 assert_eq!(
864 tree.layout(AREA).iter().filter(|p| p.focused).count(),
865 1,
866 "focus is a single window at all times"
867 );
868 }
869
870 #[test]
871 fn a_window_can_be_focused_by_name_from_anywhere_in_the_tree() {
872 let mut t = Tree::new(ViewId(1));
873 t.split(Axis::Columns, ViewId(2));
874 t.split(Axis::Rows, ViewId(3));
875 assert_eq!(t.focused(), ViewId(3));
876
877 assert!(t.focus_view(ViewId(1)), "view 1 is in this tree");
878 assert_eq!(t.focused(), ViewId(1));
879 assert!(t.focus_view(ViewId(2)));
880 assert_eq!(t.focused(), ViewId(2));
881 }
882
883 #[test]
884 fn focusing_a_window_that_is_not_here_changes_nothing() {
885 let mut t = Tree::new(ViewId(1));
888 t.split(Axis::Columns, ViewId(2));
889 let before = t.focused();
890 assert!(!t.focus_view(ViewId(99)));
891 assert_eq!(t.focused(), before);
892 }
893}