1use super::*;
2
3impl DockGraph {
4 pub(crate) fn simplify_window_forest(&mut self, window: AppWindowId) {
15 if let Some(root) = self.window_root(window) {
16 match self.simplify_subtree(root) {
17 Some(next_root) => self.set_window_root(window, next_root),
18 None => {
19 let _ = self.remove_window_root(window);
20 }
21 }
22 }
23
24 let Some(mut floatings) = self.window_floatings.remove(&window) else {
25 return;
26 };
27
28 floatings.retain_mut(|w| match self.simplify_subtree(w.floating) {
29 Some(next_root) => {
30 w.floating = next_root;
31 true
32 }
33 None => false,
34 });
35
36 if !floatings.is_empty() {
37 self.window_floatings.insert(window, floatings);
38 }
39 }
40
41 fn simplify_subtree(&mut self, node: DockNodeId) -> Option<DockNodeId> {
42 let n = self.nodes.get(node)?.clone();
43 match n {
44 DockNode::Tabs { tabs, mut active } => {
45 if tabs.is_empty() {
46 return None;
47 }
48 if active >= tabs.len() {
49 active = tabs.len().saturating_sub(1);
50 }
51 if let Some(DockNode::Tabs {
52 tabs: list,
53 active: cur,
54 }) = self.nodes.get_mut(node)
55 {
56 *list = tabs;
57 *cur = active;
58 }
59 Some(node)
60 }
61 DockNode::Floating { child } => {
62 let child = self.simplify_subtree(child)?;
63 if let Some(DockNode::Floating { child: cur }) = self.nodes.get_mut(node) {
64 *cur = child;
65 }
66 Some(node)
67 }
68 DockNode::Split {
69 axis,
70 children,
71 fractions,
72 } => {
73 let mut next_children: Vec<DockNodeId> = Vec::new();
74 let mut next_fractions: Vec<f32> = Vec::new();
75
76 for (i, child) in children.into_iter().enumerate() {
78 let Some(child) = self.simplify_subtree(child) else {
79 continue;
80 };
81 let f = fractions.get(i).copied().unwrap_or(1.0);
82 next_children.push(child);
83 next_fractions.push(f);
84 }
85
86 if next_children.is_empty() {
87 return None;
88 }
89 if next_children.len() == 1 {
90 return Some(next_children[0]);
91 }
92
93 self.flatten_same_axis_splits(axis, &mut next_children, &mut next_fractions);
94
95 if next_children.is_empty() {
96 return None;
97 }
98 if next_children.len() == 1 {
99 return Some(next_children[0]);
100 }
101
102 normalize_shares(&mut next_fractions);
103 debug_assert_eq!(next_children.len(), next_fractions.len());
104
105 if let Some(DockNode::Split {
106 children: cur_children,
107 fractions: cur_fractions,
108 ..
109 }) = self.nodes.get_mut(node)
110 {
111 *cur_children = next_children;
112 *cur_fractions = next_fractions;
113 }
114
115 Some(node)
116 }
117 }
118 }
119
120 fn flatten_same_axis_splits(
121 &mut self,
122 axis: Axis,
123 children: &mut Vec<DockNodeId>,
124 fractions: &mut Vec<f32>,
125 ) {
126 let mut changed = true;
127 while changed {
128 changed = false;
129
130 let mut out_children: Vec<DockNodeId> = Vec::with_capacity(children.len());
131 let mut out_fractions: Vec<f32> = Vec::with_capacity(fractions.len());
132
133 for (child, parent_share) in children.iter().copied().zip(fractions.iter().copied()) {
134 let Some(DockNode::Split {
135 axis: child_axis,
136 children: grand_children,
137 fractions: grand_fractions,
138 }) = self.nodes.get(child)
139 else {
140 out_children.push(child);
141 out_fractions.push(parent_share);
142 continue;
143 };
144
145 if *child_axis != axis {
146 out_children.push(child);
147 out_fractions.push(parent_share);
148 continue;
149 }
150
151 changed = true;
153
154 let mut grand_shares = grand_fractions.clone();
155 normalize_shares(&mut grand_shares);
156 debug_assert_eq!(grand_children.len(), grand_shares.len());
157
158 for (&gc, &gs) in grand_children.iter().zip(grand_shares.iter()) {
159 out_children.push(gc);
160 out_fractions.push(parent_share * gs);
161 }
162 }
163
164 *children = out_children;
165 *fractions = out_fractions;
166 }
167 }
168
169 pub fn move_panel(
170 &mut self,
171 window: AppWindowId,
172 panel: PanelKey,
173 target_tabs: DockNodeId,
174 zone: DropZone,
175 ) -> bool {
176 self.move_panel_with_insert_index(window, panel, target_tabs, zone, None)
177 }
178
179 pub fn move_panel_with_insert_index(
180 &mut self,
181 window: AppWindowId,
182 panel: PanelKey,
183 target_tabs: DockNodeId,
184 zone: DropZone,
185 insert_index: Option<usize>,
186 ) -> bool {
187 self.move_panel_between_windows(window, panel, window, target_tabs, zone, insert_index)
188 }
189
190 pub fn move_panel_between_windows(
191 &mut self,
192 source_window: AppWindowId,
193 panel: PanelKey,
194 target_window: AppWindowId,
195 target_tabs: DockNodeId,
196 zone: DropZone,
197 insert_index: Option<usize>,
198 ) -> bool {
199 let Some((source_tabs, source_index)) = self.find_panel_in_window(source_window, &panel)
200 else {
201 return false;
202 };
203
204 if zone == DropZone::Center
205 && source_window == target_window
206 && source_tabs == target_tabs
207 && insert_index.is_none()
208 {
209 return true;
210 }
211
212 if !self.remove_panel_from_tabs(source_tabs, source_index) {
213 return false;
214 }
215
216 if zone == DropZone::Center {
217 let mut index = insert_index;
218 if source_window == target_window
219 && source_tabs == target_tabs
220 && let Some(i) = index.as_mut()
221 && *i > source_index
222 {
223 *i = i.saturating_sub(1);
224 }
225
226 let ok = self.insert_panel_into_tabs_at(target_tabs, panel, index);
227 self.simplify_window_forest(source_window);
228 if target_window != source_window {
229 self.simplify_window_forest(target_window);
230 }
231 return ok;
232 }
233
234 let axis = match zone {
235 DropZone::Left | DropZone::Right => Axis::Horizontal,
236 DropZone::Top | DropZone::Bottom => Axis::Vertical,
237 DropZone::Center => unreachable!(),
238 };
239
240 let new_tabs = self.insert_node(DockNode::Tabs {
241 tabs: vec![panel],
242 active: 0,
243 });
244
245 if self.insert_edge_child_prefer_same_axis_split(
246 target_window,
247 target_tabs,
248 axis,
249 zone,
250 new_tabs,
251 ) {
252 self.simplify_window_forest(source_window);
253 if target_window != source_window {
254 self.simplify_window_forest(target_window);
255 }
256 return true;
257 }
258
259 let (first, second) = match zone {
260 DropZone::Left | DropZone::Top => (new_tabs, target_tabs),
261 DropZone::Right | DropZone::Bottom => (target_tabs, new_tabs),
262 DropZone::Center => unreachable!(),
263 };
264
265 let split = self.insert_node(DockNode::Split {
266 axis,
267 children: vec![first, second],
268 fractions: vec![0.5, 0.5],
269 });
270
271 self.replace_node_in_window_tree(target_window, target_tabs, split);
272 self.simplify_window_forest(source_window);
273 if target_window != source_window {
274 self.simplify_window_forest(target_window);
275 }
276 true
277 }
278
279 pub fn move_tabs_between_windows(
280 &mut self,
281 source_window: AppWindowId,
282 source_tabs: DockNodeId,
283 target_window: AppWindowId,
284 target_tabs: DockNodeId,
285 zone: DropZone,
286 insert_index: Option<usize>,
287 ) -> bool {
288 if zone == DropZone::Center && source_window == target_window && source_tabs == target_tabs
289 {
290 return true;
291 }
292
293 if self
294 .root_for_node_in_window_forest(source_window, source_tabs)
295 .is_none()
296 {
297 return false;
298 }
299 if self
300 .root_for_node_in_window_forest(target_window, target_tabs)
301 .is_none()
302 {
303 return false;
304 }
305
306 let (panels, active) = match self.nodes.get(source_tabs) {
307 Some(DockNode::Tabs { tabs, active }) if !tabs.is_empty() => (tabs.clone(), *active),
308 _ => return false,
309 };
310 let active = active.min(panels.len().saturating_sub(1));
311
312 if zone == DropZone::Center
313 && !matches!(self.nodes.get(target_tabs), Some(DockNode::Tabs { .. }))
314 {
315 return false;
316 }
317
318 if let Some(DockNode::Tabs { tabs, active }) = self.nodes.get_mut(source_tabs) {
319 tabs.clear();
320 *active = 0;
321 }
322 if self.window_root(source_window) == Some(source_tabs) {
323 let _ = self.remove_window_root(source_window);
324 }
325 self.collapse_empty_tabs_upwards(source_window, source_tabs);
326 self.remove_empty_floating_windows(source_window);
327
328 if zone == DropZone::Center {
329 let ok = self.insert_panels_into_tabs_at(target_tabs, &panels, insert_index, active);
330 self.simplify_window_forest(target_window);
331 return ok;
332 }
333
334 let axis = match zone {
335 DropZone::Left | DropZone::Right => Axis::Horizontal,
336 DropZone::Top | DropZone::Bottom => Axis::Vertical,
337 DropZone::Center => unreachable!(),
338 };
339
340 let new_tabs = self.insert_node(DockNode::Tabs {
341 tabs: panels,
342 active,
343 });
344
345 if self.insert_edge_child_prefer_same_axis_split(
346 target_window,
347 target_tabs,
348 axis,
349 zone,
350 new_tabs,
351 ) {
352 self.simplify_window_forest(target_window);
353 return true;
354 }
355
356 let (first, second) = match zone {
357 DropZone::Left | DropZone::Top => (new_tabs, target_tabs),
358 DropZone::Right | DropZone::Bottom => (target_tabs, new_tabs),
359 DropZone::Center => unreachable!(),
360 };
361
362 let split = self.insert_node(DockNode::Split {
363 axis,
364 children: vec![first, second],
365 fractions: vec![0.5, 0.5],
366 });
367
368 self.replace_node_in_window_tree(target_window, target_tabs, split);
369 self.simplify_window_forest(target_window);
370 true
371 }
372
373 pub fn close_panel(&mut self, window: AppWindowId, panel: PanelKey) -> bool {
374 let Some((tabs, index)) = self.find_panel_in_window(window, &panel) else {
375 return false;
376 };
377 if !self.remove_panel_from_tabs(tabs, index) {
378 return false;
379 }
380 self.simplify_window_forest(window);
381 true
382 }
383
384 pub fn float_panel_to_window(
385 &mut self,
386 source_window: AppWindowId,
387 panel: PanelKey,
388 new_window: AppWindowId,
389 ) -> bool {
390 let Some((source_tabs, source_index)) = self.find_panel_in_window(source_window, &panel)
391 else {
392 return false;
393 };
394 if !self.remove_panel_from_tabs(source_tabs, source_index) {
395 return false;
396 }
397
398 let tabs = self.insert_node(DockNode::Tabs {
399 tabs: vec![panel],
400 active: 0,
401 });
402 self.set_window_root(new_window, tabs);
403 self.simplify_window_forest(source_window);
404 self.simplify_window_forest(new_window);
405 true
406 }
407
408 pub fn float_tabs_to_window(
409 &mut self,
410 source_window: AppWindowId,
411 source_tabs: DockNodeId,
412 new_window: AppWindowId,
413 ) -> bool {
414 if self
415 .root_for_node_in_window_forest(source_window, source_tabs)
416 .is_none()
417 {
418 return false;
419 }
420
421 let (panels, active) = match self.nodes.get(source_tabs) {
422 Some(DockNode::Tabs { tabs, active }) if !tabs.is_empty() => (tabs.clone(), *active),
423 _ => return false,
424 };
425 let active = active.min(panels.len().saturating_sub(1));
426
427 if let Some(DockNode::Tabs { tabs, active }) = self.nodes.get_mut(source_tabs) {
428 tabs.clear();
429 *active = 0;
430 }
431 if self.window_root(source_window) == Some(source_tabs) {
432 let _ = self.remove_window_root(source_window);
433 }
434
435 let tabs = self.insert_node(DockNode::Tabs {
436 tabs: panels,
437 active,
438 });
439 self.set_window_root(new_window, tabs);
440
441 self.simplify_window_forest(source_window);
442 self.simplify_window_forest(new_window);
443 true
444 }
445
446 pub fn float_panel_in_window(
447 &mut self,
448 source_window: AppWindowId,
449 panel: PanelKey,
450 target_window: AppWindowId,
451 rect: Rect,
452 ) -> bool {
453 let Some((source_tabs, source_index)) = self.find_panel_in_window(source_window, &panel)
454 else {
455 return false;
456 };
457 if !self.remove_panel_from_tabs(source_tabs, source_index) {
458 return false;
459 }
460
461 let tabs = self.insert_node(DockNode::Tabs {
462 tabs: vec![panel],
463 active: 0,
464 });
465 let floating = self.insert_node(DockNode::Floating { child: tabs });
466 self.floating_windows_mut(target_window)
467 .push(DockFloatingWindow { floating, rect });
468
469 self.simplify_window_forest(source_window);
470 self.simplify_window_forest(target_window);
471 true
472 }
473
474 pub fn float_tabs_in_window(
475 &mut self,
476 source_window: AppWindowId,
477 source_tabs: DockNodeId,
478 target_window: AppWindowId,
479 rect: Rect,
480 ) -> bool {
481 if self
482 .root_for_node_in_window_forest(source_window, source_tabs)
483 .is_none()
484 {
485 return false;
486 }
487
488 let (panels, active) = match self.nodes.get(source_tabs) {
489 Some(DockNode::Tabs { tabs, active }) if !tabs.is_empty() => (tabs.clone(), *active),
490 _ => return false,
491 };
492 let active = active.min(panels.len().saturating_sub(1));
493
494 if let Some(DockNode::Tabs { tabs, active }) = self.nodes.get_mut(source_tabs) {
495 tabs.clear();
496 *active = 0;
497 }
498 if self.window_root(source_window) == Some(source_tabs) {
499 let _ = self.remove_window_root(source_window);
500 }
501 self.simplify_window_forest(source_window);
502
503 let tabs = self.insert_node(DockNode::Tabs {
504 tabs: panels,
505 active,
506 });
507 let floating = self.insert_node(DockNode::Floating { child: tabs });
508 self.floating_windows_mut(target_window)
509 .push(DockFloatingWindow { floating, rect });
510 self.simplify_window_forest(target_window);
511 true
512 }
513
514 pub fn set_floating_rect(
515 &mut self,
516 window: AppWindowId,
517 floating: DockNodeId,
518 rect: Rect,
519 ) -> bool {
520 let Some(list) = self.window_floatings.get_mut(&window) else {
521 return false;
522 };
523 let Some(entry) = list.iter_mut().find(|w| w.floating == floating) else {
524 return false;
525 };
526 entry.rect = rect;
527 true
528 }
529
530 pub fn raise_floating(&mut self, window: AppWindowId, floating: DockNodeId) -> bool {
531 let Some(list) = self.window_floatings.get_mut(&window) else {
532 return false;
533 };
534 let Some(index) = list.iter().position(|w| w.floating == floating) else {
535 return false;
536 };
537 if index + 1 == list.len() {
538 return true;
539 }
540 let entry = list.remove(index);
541 list.push(entry);
542 true
543 }
544
545 pub fn merge_floating_into(
546 &mut self,
547 window: AppWindowId,
548 floating: DockNodeId,
549 target_tabs: DockNodeId,
550 ) -> bool {
551 let Some(list) = self.window_floatings.get(&window) else {
552 return false;
553 };
554 if !list.iter().any(|w| w.floating == floating) {
555 return false;
556 }
557
558 let Some(DockNode::Tabs { .. }) = self.nodes.get(target_tabs) else {
559 return false;
560 };
561 let Some(target_root) = self.root_for_node_in_window_forest(window, target_tabs) else {
562 return false;
563 };
564 if target_root == floating {
567 return false;
568 }
569
570 let panels = self.collect_panels_in_subtree(floating);
571 for panel in panels {
572 let _ = self.move_panel_between_windows(
573 window,
574 panel,
575 window,
576 target_tabs,
577 DropZone::Center,
578 None,
579 );
580 }
581
582 if let Some(list) = self.window_floatings.get_mut(&window)
583 && let Some(index) = list.iter().position(|w| w.floating == floating)
584 {
585 list.remove(index);
586 }
587 self.simplify_window_forest(window);
588 true
589 }
590
591 pub fn set_active_tab(&mut self, tabs: DockNodeId, active: usize) -> bool {
592 let Some(DockNode::Tabs {
593 tabs: list,
594 active: cur,
595 }) = self.nodes.get_mut(tabs)
596 else {
597 return false;
598 };
599 if list.is_empty() {
600 *cur = 0;
601 return true;
602 }
603 *cur = active.min(list.len() - 1);
604 true
605 }
606
607 pub fn update_split_two(&mut self, split: DockNodeId, first_fraction: f32) -> bool {
608 let Some(DockNode::Split {
609 children,
610 fractions,
611 ..
612 }) = self.nodes.get_mut(split)
613 else {
614 return false;
615 };
616 if children.len() != 2 || fractions.len() != 2 {
617 return false;
618 }
619 let f0 = first_fraction.clamp(0.0, 1.0);
620 fractions[0] = f0;
621 fractions[1] = 1.0 - f0;
622 true
623 }
624
625 pub fn update_split_fractions(&mut self, split: DockNodeId, mut next: Vec<f32>) -> bool {
626 let Some(DockNode::Split {
627 children,
628 fractions,
629 ..
630 }) = self.nodes.get_mut(split)
631 else {
632 return false;
633 };
634 if children.len() < 2 || next.len() != children.len() {
635 return false;
636 }
637
638 for f in &mut next {
639 if !f.is_finite() {
640 *f = 0.0;
641 }
642 *f = (*f).max(0.0);
643 }
644 let sum: f32 = next.iter().sum();
645 if !sum.is_finite() || sum <= f32::EPSILON {
646 next = vec![1.0 / next.len() as f32; next.len()];
647 } else {
648 for f in &mut next {
649 *f /= sum;
650 }
651 let len = next.len();
652 if len >= 1 {
653 let rest: f32 = next.iter().take(len.saturating_sub(1)).sum();
654 next[len - 1] = (1.0 - rest).clamp(0.0, 1.0);
655 }
656 }
657
658 *fractions = next;
659 true
660 }
661
662 fn insert_panel_into_tabs_at(
663 &mut self,
664 tabs: DockNodeId,
665 panel: PanelKey,
666 index: Option<usize>,
667 ) -> bool {
668 let Some(DockNode::Tabs { tabs: list, active }) = self.nodes.get_mut(tabs) else {
669 return false;
670 };
671 if list.contains(&panel) {
672 return true;
673 }
674
675 match index {
676 Some(i) => {
677 let i = i.min(list.len());
678 list.insert(i, panel);
679 *active = i;
680 }
681 None => {
682 list.push(panel);
683 *active = list.len().saturating_sub(1);
684 }
685 }
686 true
687 }
688
689 fn insert_panels_into_tabs_at(
690 &mut self,
691 tabs: DockNodeId,
692 panels: &[PanelKey],
693 index: Option<usize>,
694 active_in_group: usize,
695 ) -> bool {
696 let Some(DockNode::Tabs { tabs: list, active }) = self.nodes.get_mut(tabs) else {
697 return false;
698 };
699 if panels.is_empty() {
700 return true;
701 }
702
703 let mut insert_at = index.unwrap_or(list.len()).min(list.len());
704 for panel in panels {
705 if list.contains(panel) {
706 continue;
707 }
708 list.insert(insert_at, panel.clone());
709 insert_at = insert_at.saturating_add(1);
710 }
711
712 if let Some(active_panel) = panels.get(active_in_group)
713 && let Some(ix) = list.iter().position(|p| p == active_panel)
714 {
715 *active = ix;
716 }
717 if list.is_empty() {
718 *active = 0;
719 } else if *active >= list.len() {
720 *active = list.len().saturating_sub(1);
721 }
722
723 true
724 }
725
726 fn remove_panel_from_tabs(&mut self, tabs: DockNodeId, index: usize) -> bool {
727 let Some(DockNode::Tabs { tabs: list, active }) = self.nodes.get_mut(tabs) else {
728 return false;
729 };
730 if index >= list.len() {
731 return false;
732 }
733
734 list.remove(index);
735 if list.is_empty() {
736 *active = 0;
737 } else if *active >= list.len() {
738 *active = list.len().saturating_sub(1);
739 } else if index < *active {
740 *active = active.saturating_sub(1);
741 }
742 true
743 }
744
745 fn insert_edge_child_prefer_same_axis_split(
746 &mut self,
747 window: AppWindowId,
748 target: DockNodeId,
749 axis: Axis,
750 zone: DropZone,
751 new_child: DockNodeId,
752 ) -> bool {
753 let Some(decision) = self.edge_dock_decision(window, target, zone) else {
756 return false;
757 };
758 let EdgeDockDecision::InsertIntoSplit {
759 split,
760 anchor_index,
761 insert_index,
762 } = decision
763 else {
764 return false;
765 };
766
767 let Some(DockNode::Split {
768 axis: split_axis,
769 children,
770 fractions,
771 }) = self.nodes.get_mut(split)
772 else {
773 return false;
774 };
775 if *split_axis != axis || children.len() != fractions.len() || children.is_empty() {
776 return false;
777 }
778
779 split_share_and_insert(children, fractions, anchor_index, insert_index, new_child);
780 true
781 }
782
783 fn replace_node_in_window_tree(
784 &mut self,
785 window: AppWindowId,
786 old: DockNodeId,
787 new: DockNodeId,
788 ) {
789 if self.window_root(window) == Some(old) {
790 self.set_window_root(window, new);
791 return;
792 }
793 if let Some(list) = self.window_floatings.get_mut(&window) {
794 for w in list {
795 if w.floating == old {
796 w.floating = new;
797 return;
798 }
799 }
800 }
801
802 if let Some(root) = self.window_root(window)
803 && let Some(parent) = self.find_parent_in_subtree(root, old)
804 {
805 self.replace_child_in_node(parent, old, new);
806 return;
807 }
808
809 let floating_roots: Vec<DockNodeId> = self
812 .window_floatings
813 .get(&window)
814 .map(|list| list.iter().map(|w| w.floating).collect())
815 .unwrap_or_default();
816 for floating in floating_roots {
817 if let Some(parent) = self.find_parent_in_subtree(floating, old) {
818 self.replace_child_in_node(parent, old, new);
819 return;
820 }
821 }
822 }
823
824 fn replace_child_in_node(
825 &mut self,
826 node: DockNodeId,
827 old: DockNodeId,
828 new: DockNodeId,
829 ) -> bool {
830 let Some(n) = self.nodes.get_mut(node) else {
831 return false;
832 };
833 match n {
834 DockNode::Split { children, .. } => {
835 let Some(index) = children.iter().position(|c| *c == old) else {
836 return false;
837 };
838 children[index] = new;
839 true
840 }
841 DockNode::Floating { child } => {
842 if *child != old {
843 return false;
844 }
845 *child = new;
846 true
847 }
848 DockNode::Tabs { .. } => false,
849 }
850 }
851
852 fn find_parent_in_subtree(&self, node: DockNodeId, target: DockNodeId) -> Option<DockNodeId> {
853 let n = self.nodes.get(node)?;
854 match n {
855 DockNode::Tabs { .. } => None,
856 DockNode::Split { children, .. } => {
857 if children.contains(&target) {
858 return Some(node);
859 }
860 children
861 .iter()
862 .copied()
863 .find_map(|child| self.find_parent_in_subtree(child, target))
864 }
865 DockNode::Floating { child } => {
866 if *child == target {
867 return Some(node);
868 }
869 self.find_parent_in_subtree(*child, target)
870 }
871 }
872 }
873
874 fn collapse_empty_tabs_upwards(&mut self, window: AppWindowId, start_tabs: DockNodeId) {
875 let Some(_root) = self.root_for_node_in_window_forest(window, start_tabs) else {
876 return;
877 };
878
879 let _ = start_tabs;
882 self.simplify_window_forest(window);
883 }
884
885 pub(super) fn root_for_node_in_window_forest(
886 &self,
887 window: AppWindowId,
888 target: DockNodeId,
889 ) -> Option<DockNodeId> {
890 fn contains(graph: &DockGraph, root: DockNodeId, target: DockNodeId) -> bool {
891 if root == target {
892 return true;
893 }
894 let Some(n) = graph.nodes.get(root) else {
895 return false;
896 };
897 match n {
898 DockNode::Tabs { .. } => false,
899 DockNode::Split { children, .. } => {
900 children.iter().copied().any(|c| contains(graph, c, target))
901 }
902 DockNode::Floating { child } => contains(graph, *child, target),
903 }
904 }
905
906 if let Some(root) = self.window_root(window)
907 && contains(self, root, target)
908 {
909 return Some(root);
910 }
911 if let Some(list) = self.window_floatings.get(&window) {
912 for w in list {
913 if contains(self, w.floating, target) {
914 return Some(w.floating);
915 }
916 }
917 }
918 None
919 }
920
921 fn remove_empty_floating_windows(&mut self, window: AppWindowId) {
922 self.simplify_window_forest(window);
925 }
926}
927
928fn normalize_shares(shares: &mut Vec<f32>) {
929 for f in shares.iter_mut() {
930 if !f.is_finite() {
931 *f = 0.0;
932 }
933 if *f < 0.0 {
934 *f = 0.0;
935 }
936 }
937
938 let sum: f32 = shares.iter().sum();
939 if !sum.is_finite() || sum <= f32::EPSILON {
940 let n = shares.len().max(1);
941 *shares = vec![1.0 / n as f32; n];
942 return;
943 }
944
945 for f in shares.iter_mut() {
946 *f /= sum;
947 }
948
949 let len = shares.len();
951 if len >= 1 {
952 let rest: f32 = shares.iter().take(len.saturating_sub(1)).sum();
953 shares[len - 1] = (1.0 - rest).clamp(0.0, 1.0);
954 }
955}
956
957fn split_share_and_insert(
958 children: &mut Vec<DockNodeId>,
959 fractions: &mut Vec<f32>,
960 anchor_index: usize,
961 insert_index: usize,
962 new_child: DockNodeId,
963) {
964 debug_assert!(!children.is_empty());
965 debug_assert_eq!(children.len(), fractions.len());
966 debug_assert!(anchor_index < fractions.len());
967 debug_assert!(insert_index <= fractions.len());
968
969 let k = 0.5_f32;
971
972 let old = fractions[anchor_index];
973 let keep = old * (1.0 - k);
974 let take = old * k;
975
976 fractions[anchor_index] = keep;
977 children.insert(insert_index, new_child);
978 fractions.insert(insert_index, take);
979}