1use crate::model::cursor::Cursors;
27use crate::model::event::{BufferId, SplitDirection, SplitId};
28use crate::view::ui::view_pipeline::Layout;
29use crate::view::viewport::Viewport;
30use crate::{services::plugins::api::ViewTransformPayload, state::ViewMode};
31use ratatui::layout::Rect;
32use serde::{Deserialize, Serialize};
33
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36pub enum SplitNode {
37 Leaf {
39 buffer_id: BufferId,
41 split_id: SplitId,
43 },
44 Split {
46 direction: SplitDirection,
48 first: Box<Self>,
50 second: Box<Self>,
52 ratio: f32,
55 split_id: SplitId,
57 },
58}
59
60#[derive(Debug, Clone)]
70pub struct SplitViewState {
71 pub cursors: Cursors,
73
74 pub viewport: Viewport,
76
77 pub open_buffers: Vec<BufferId>,
80
81 pub tab_scroll_offset: usize,
83
84 pub view_mode: ViewMode,
86
87 pub compose_width: Option<u16>,
89
90 pub compose_column_guides: Option<Vec<u16>>,
92
93 pub compose_prev_line_numbers: Option<bool>,
95
96 pub view_transform: Option<ViewTransformPayload>,
98
99 pub layout: Option<Layout>,
102
103 pub layout_dirty: bool,
105
106 pub focus_history: Vec<BufferId>,
109
110 pub sync_group: Option<u32>,
113
114 pub composite_view: Option<BufferId>,
119}
120
121impl SplitViewState {
122 pub fn new(width: u16, height: u16) -> Self {
124 Self {
125 cursors: Cursors::new(),
126 viewport: Viewport::new(width, height),
127 open_buffers: Vec::new(),
128 tab_scroll_offset: 0,
129 view_mode: ViewMode::Source,
130 compose_width: None,
131 compose_column_guides: None,
132 compose_prev_line_numbers: None,
133 view_transform: None,
134 layout: None,
135 layout_dirty: true, focus_history: Vec::new(),
137 sync_group: None,
138 composite_view: None,
139 }
140 }
141
142 pub fn with_buffer(width: u16, height: u16, buffer_id: BufferId) -> Self {
144 Self {
145 cursors: Cursors::new(),
146 viewport: Viewport::new(width, height),
147 open_buffers: vec![buffer_id],
148 tab_scroll_offset: 0,
149 view_mode: ViewMode::Source,
150 compose_width: None,
151 compose_column_guides: None,
152 compose_prev_line_numbers: None,
153 view_transform: None,
154 layout: None,
155 layout_dirty: true, focus_history: Vec::new(),
157 sync_group: None,
158 composite_view: None,
159 }
160 }
161
162 pub fn invalidate_layout(&mut self) {
164 self.layout_dirty = true;
165 }
166
167 pub fn ensure_layout(
175 &mut self,
176 tokens: &[fresh_core::api::ViewTokenWire],
177 source_range: std::ops::Range<usize>,
178 tab_size: usize,
179 ) -> &Layout {
180 if self.layout.is_none() || self.layout_dirty {
181 self.layout = Some(Layout::from_tokens(tokens, source_range, tab_size));
182 self.layout_dirty = false;
183 }
184 self.layout.as_ref().unwrap()
185 }
186
187 pub fn get_layout(&self) -> Option<&Layout> {
189 if self.layout_dirty {
190 None
191 } else {
192 self.layout.as_ref()
193 }
194 }
195
196 pub fn add_buffer(&mut self, buffer_id: BufferId) {
198 if !self.open_buffers.contains(&buffer_id) {
199 self.open_buffers.push(buffer_id);
200 }
201 }
202
203 pub fn remove_buffer(&mut self, buffer_id: BufferId) {
205 self.open_buffers.retain(|&id| id != buffer_id);
206 }
207
208 pub fn has_buffer(&self, buffer_id: BufferId) -> bool {
210 self.open_buffers.contains(&buffer_id)
211 }
212
213 pub fn push_focus(&mut self, buffer_id: BufferId) {
216 self.focus_history.retain(|&id| id != buffer_id);
218 self.focus_history.push(buffer_id);
219 if self.focus_history.len() > 50 {
221 self.focus_history.remove(0);
222 }
223 }
224
225 pub fn previous_buffer(&self) -> Option<BufferId> {
227 self.focus_history.last().copied()
228 }
229
230 pub fn pop_focus(&mut self) -> Option<BufferId> {
232 self.focus_history.pop()
233 }
234
235 pub fn remove_from_history(&mut self, buffer_id: BufferId) {
237 self.focus_history.retain(|&id| id != buffer_id);
238 }
239}
240
241impl SplitNode {
242 pub fn leaf(buffer_id: BufferId, split_id: SplitId) -> Self {
244 Self::Leaf {
245 buffer_id,
246 split_id,
247 }
248 }
249
250 pub fn split(
252 direction: SplitDirection,
253 first: SplitNode,
254 second: SplitNode,
255 ratio: f32,
256 split_id: SplitId,
257 ) -> Self {
258 SplitNode::Split {
259 direction,
260 first: Box::new(first),
261 second: Box::new(second),
262 ratio: ratio.clamp(0.1, 0.9), split_id,
264 }
265 }
266
267 pub fn id(&self) -> SplitId {
269 match self {
270 Self::Leaf { split_id, .. } | Self::Split { split_id, .. } => *split_id,
271 }
272 }
273
274 pub fn buffer_id(&self) -> Option<BufferId> {
276 match self {
277 Self::Leaf { buffer_id, .. } => Some(*buffer_id),
278 Self::Split { .. } => None,
279 }
280 }
281
282 pub fn find_mut(&mut self, target_id: SplitId) -> Option<&mut Self> {
284 if self.id() == target_id {
285 return Some(self);
286 }
287
288 match self {
289 Self::Leaf { .. } => None,
290 Self::Split { first, second, .. } => first
291 .find_mut(target_id)
292 .or_else(|| second.find_mut(target_id)),
293 }
294 }
295
296 pub fn find(&self, target_id: SplitId) -> Option<&Self> {
298 if self.id() == target_id {
299 return Some(self);
300 }
301
302 match self {
303 Self::Leaf { .. } => None,
304 Self::Split { first, second, .. } => {
305 first.find(target_id).or_else(|| second.find(target_id))
306 }
307 }
308 }
309
310 pub fn get_leaves_with_rects(&self, rect: Rect) -> Vec<(SplitId, BufferId, Rect)> {
312 match self {
313 Self::Leaf {
314 buffer_id,
315 split_id,
316 } => {
317 vec![(*split_id, *buffer_id, rect)]
318 }
319 Self::Split {
320 direction,
321 first,
322 second,
323 ratio,
324 ..
325 } => {
326 let (first_rect, second_rect) = split_rect(rect, *direction, *ratio);
327 let mut leaves = first.get_leaves_with_rects(first_rect);
328 leaves.extend(second.get_leaves_with_rects(second_rect));
329 leaves
330 }
331 }
332 }
333
334 pub fn get_separators(&self, rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
337 self.get_separators_with_ids(rect)
338 .into_iter()
339 .map(|(_, dir, x, y, len)| (dir, x, y, len))
340 .collect()
341 }
342
343 pub fn get_separators_with_ids(
346 &self,
347 rect: Rect,
348 ) -> Vec<(SplitId, SplitDirection, u16, u16, u16)> {
349 match self {
350 Self::Leaf { .. } => vec![],
351 Self::Split {
352 direction,
353 first,
354 second,
355 ratio,
356 split_id,
357 } => {
358 let (first_rect, second_rect) = split_rect(rect, *direction, *ratio);
359 let mut separators = Vec::new();
360
361 match direction {
363 SplitDirection::Horizontal => {
364 separators.push((
367 *split_id,
368 SplitDirection::Horizontal,
369 rect.x,
370 first_rect.y + first_rect.height,
371 rect.width,
372 ));
373 }
374 SplitDirection::Vertical => {
375 separators.push((
378 *split_id,
379 SplitDirection::Vertical,
380 first_rect.x + first_rect.width,
381 rect.y,
382 rect.height,
383 ));
384 }
385 }
386
387 separators.extend(first.get_separators_with_ids(first_rect));
389 separators.extend(second.get_separators_with_ids(second_rect));
390 separators
391 }
392 }
393 }
394
395 pub fn all_split_ids(&self) -> Vec<SplitId> {
397 let mut ids = vec![self.id()];
398 match self {
399 Self::Leaf { .. } => ids,
400 Self::Split { first, second, .. } => {
401 ids.extend(first.all_split_ids());
402 ids.extend(second.all_split_ids());
403 ids
404 }
405 }
406 }
407
408 pub fn leaf_split_ids(&self) -> Vec<SplitId> {
410 match self {
411 Self::Leaf { split_id, .. } => vec![*split_id],
412 Self::Split { first, second, .. } => {
413 let mut ids = first.leaf_split_ids();
414 ids.extend(second.leaf_split_ids());
415 ids
416 }
417 }
418 }
419
420 pub fn count_leaves(&self) -> usize {
422 match self {
423 Self::Leaf { .. } => 1,
424 Self::Split { first, second, .. } => first.count_leaves() + second.count_leaves(),
425 }
426 }
427}
428
429fn split_rect(rect: Rect, direction: SplitDirection, ratio: f32) -> (Rect, Rect) {
432 match direction {
433 SplitDirection::Horizontal => {
434 let total_height = rect.height.saturating_sub(1); let first_height = (total_height as f32 * ratio).round() as u16;
437 let second_height = total_height.saturating_sub(first_height);
438
439 let first = Rect {
440 x: rect.x,
441 y: rect.y,
442 width: rect.width,
443 height: first_height,
444 };
445
446 let second = Rect {
447 x: rect.x,
448 y: rect.y + first_height + 1, width: rect.width,
450 height: second_height,
451 };
452
453 (first, second)
454 }
455 SplitDirection::Vertical => {
456 let total_width = rect.width.saturating_sub(1); let first_width = (total_width as f32 * ratio).round() as u16;
459 let second_width = total_width.saturating_sub(first_width);
460
461 let first = Rect {
462 x: rect.x,
463 y: rect.y,
464 width: first_width,
465 height: rect.height,
466 };
467
468 let second = Rect {
469 x: rect.x + first_width + 1, y: rect.y,
471 width: second_width,
472 height: rect.height,
473 };
474
475 (first, second)
476 }
477 }
478}
479
480#[derive(Debug)]
482pub struct SplitManager {
483 root: SplitNode,
485
486 active_split: SplitId,
488
489 next_split_id: usize,
491
492 maximized_split: Option<SplitId>,
494}
495
496impl SplitManager {
497 pub fn new(buffer_id: BufferId) -> Self {
499 let split_id = SplitId(0);
500 Self {
501 root: SplitNode::leaf(buffer_id, split_id),
502 active_split: split_id,
503 next_split_id: 1,
504 maximized_split: None,
505 }
506 }
507
508 pub fn root(&self) -> &SplitNode {
510 &self.root
511 }
512
513 pub fn active_split(&self) -> SplitId {
515 self.active_split
516 }
517
518 pub fn set_active_split(&mut self, split_id: SplitId) -> bool {
520 if self.root.find(split_id).is_some() {
522 self.active_split = split_id;
523 true
524 } else {
525 false
526 }
527 }
528
529 pub fn active_buffer_id(&self) -> Option<BufferId> {
531 self.root
532 .find(self.active_split)
533 .and_then(|node| node.buffer_id())
534 }
535
536 pub fn get_buffer_id(&self, split_id: SplitId) -> Option<BufferId> {
538 self.root.find(split_id).and_then(|node| node.buffer_id())
539 }
540
541 pub fn set_active_buffer_id(&mut self, new_buffer_id: BufferId) -> bool {
544 if let Some(SplitNode::Leaf { buffer_id, .. }) = self.root.find_mut(self.active_split) {
545 *buffer_id = new_buffer_id;
546 return true;
547 }
548 false
549 }
550
551 pub fn set_split_buffer(
554 &mut self,
555 split_id: SplitId,
556 new_buffer_id: BufferId,
557 ) -> Result<(), String> {
558 if let Some(node) = self.root.find_mut(split_id) {
559 if let SplitNode::Leaf { buffer_id, .. } = node {
560 *buffer_id = new_buffer_id;
561 return Ok(());
562 }
563 return Err(format!("Split {:?} is not a leaf", split_id));
564 }
565 Err(format!("Split {:?} not found", split_id))
566 }
567
568 fn allocate_split_id(&mut self) -> SplitId {
570 let id = SplitId(self.next_split_id);
571 self.next_split_id += 1;
572 id
573 }
574
575 pub fn split_active(
577 &mut self,
578 direction: SplitDirection,
579 new_buffer_id: BufferId,
580 ratio: f32,
581 ) -> Result<SplitId, String> {
582 let active_id = self.active_split;
583
584 let result = self.replace_split_with_split(active_id, direction, new_buffer_id, ratio);
586
587 if let Ok(new_split_id) = result {
588 self.active_split = new_split_id;
590 Ok(new_split_id)
591 } else {
592 result
593 }
594 }
595
596 fn replace_split_with_split(
598 &mut self,
599 target_id: SplitId,
600 direction: SplitDirection,
601 new_buffer_id: BufferId,
602 ratio: f32,
603 ) -> Result<SplitId, String> {
604 let temp_id = self.allocate_split_id();
606 let new_split_id = self.allocate_split_id();
607 let new_leaf_id = self.allocate_split_id();
608
609 if self.root.id() == target_id {
611 let old_root =
612 std::mem::replace(&mut self.root, SplitNode::leaf(new_buffer_id, temp_id));
613
614 self.root = SplitNode::split(
615 direction,
616 old_root,
617 SplitNode::leaf(new_buffer_id, new_leaf_id),
618 ratio,
619 new_split_id,
620 );
621
622 return Ok(new_leaf_id);
623 }
624
625 if let Some(node) = self.root.find_mut(target_id) {
627 let old_node = std::mem::replace(node, SplitNode::leaf(new_buffer_id, temp_id));
628
629 *node = SplitNode::split(
630 direction,
631 old_node,
632 SplitNode::leaf(new_buffer_id, new_leaf_id),
633 ratio,
634 new_split_id,
635 );
636
637 Ok(new_leaf_id)
638 } else {
639 Err(format!("Split {:?} not found", target_id))
640 }
641 }
642
643 pub fn close_split(&mut self, split_id: SplitId) -> Result<(), String> {
645 if self.root.count_leaves() <= 1 {
647 return Err("Cannot close the last split".to_string());
648 }
649
650 if self.root.id() == split_id && self.root.buffer_id().is_some() {
652 return Err("Cannot close the only split".to_string());
653 }
654
655 if self.maximized_split == Some(split_id) {
657 self.maximized_split = None;
658 }
659
660 let result = self.remove_split_node(split_id);
663
664 if result.is_ok() && self.active_split == split_id {
666 let leaf_ids = self.root.leaf_split_ids();
667 if let Some(&first_leaf) = leaf_ids.first() {
668 self.active_split = first_leaf;
669 }
670 }
671
672 result
673 }
674
675 fn remove_split_node(&mut self, target_id: SplitId) -> Result<(), String> {
677 if self.root.id() == target_id {
679 if let SplitNode::Split { first, .. } = &self.root {
680 self.root = (**first).clone();
683 return Ok(());
684 }
685 }
686
687 Self::remove_child_static(&mut self.root, target_id)
689 }
690
691 fn remove_child_static(node: &mut SplitNode, target_id: SplitId) -> Result<(), String> {
693 match node {
694 SplitNode::Leaf { .. } => Err("Target not found".to_string()),
695 SplitNode::Split { first, second, .. } => {
696 if first.id() == target_id {
698 *node = (**second).clone();
700 Ok(())
701 } else if second.id() == target_id {
702 *node = (**first).clone();
704 Ok(())
705 } else {
706 Self::remove_child_static(first, target_id)
708 .or_else(|_| Self::remove_child_static(second, target_id))
709 }
710 }
711 }
712 }
713
714 pub fn adjust_ratio(&mut self, split_id: SplitId, delta: f32) -> Result<(), String> {
716 if let Some(node) = self.root.find_mut(split_id) {
717 if let SplitNode::Split { ratio, .. } = node {
718 *ratio = (*ratio + delta).clamp(0.1, 0.9);
719 Ok(())
720 } else {
721 Err("Target is not a split container".to_string())
722 }
723 } else {
724 Err("Split not found".to_string())
725 }
726 }
727
728 pub fn get_visible_buffers(&self, viewport_rect: Rect) -> Vec<(SplitId, BufferId, Rect)> {
730 if let Some(maximized_id) = self.maximized_split {
732 if let Some(node) = self.root.find(maximized_id) {
733 if let Some(buffer_id) = node.buffer_id() {
734 return vec![(maximized_id, buffer_id, viewport_rect)];
735 }
736 }
737 }
739 self.root.get_leaves_with_rects(viewport_rect)
740 }
741
742 pub fn get_separators(&self, viewport_rect: Rect) -> Vec<(SplitDirection, u16, u16, u16)> {
745 if self.maximized_split.is_some() {
747 return vec![];
748 }
749 self.root.get_separators(viewport_rect)
750 }
751
752 pub fn get_separators_with_ids(
755 &self,
756 viewport_rect: Rect,
757 ) -> Vec<(SplitId, SplitDirection, u16, u16, u16)> {
758 if self.maximized_split.is_some() {
760 return vec![];
761 }
762 self.root.get_separators_with_ids(viewport_rect)
763 }
764
765 pub fn get_ratio(&self, split_id: SplitId) -> Option<f32> {
767 if let Some(SplitNode::Split { ratio, .. }) = self.root.find(split_id) {
768 Some(*ratio)
769 } else {
770 None
771 }
772 }
773
774 pub fn set_ratio(&mut self, split_id: SplitId, new_ratio: f32) -> Result<(), String> {
776 if let Some(node) = self.root.find_mut(split_id) {
777 if let SplitNode::Split { ratio, .. } = node {
778 *ratio = new_ratio.clamp(0.1, 0.9);
779 Ok(())
780 } else {
781 Err("Target is not a split container".to_string())
782 }
783 } else {
784 Err("Split not found".to_string())
785 }
786 }
787
788 pub fn distribute_splits_evenly(&mut self) {
791 Self::distribute_node_evenly(&mut self.root);
792 }
793
794 fn distribute_node_evenly(node: &mut SplitNode) -> usize {
797 match node {
798 SplitNode::Leaf { .. } => 1,
799 SplitNode::Split {
800 first,
801 second,
802 ratio,
803 ..
804 } => {
805 let first_leaves = Self::distribute_node_evenly(first);
806 let second_leaves = Self::distribute_node_evenly(second);
807 let total_leaves = first_leaves + second_leaves;
808
809 *ratio = (first_leaves as f32 / total_leaves as f32).clamp(0.1, 0.9);
812
813 total_leaves
814 }
815 }
816 }
817
818 pub fn next_split(&mut self) {
820 let leaf_ids = self.root.leaf_split_ids();
821 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
822 let next_pos = (pos + 1) % leaf_ids.len();
823 self.active_split = leaf_ids[next_pos];
824 }
825 }
826
827 pub fn prev_split(&mut self) {
829 let leaf_ids = self.root.leaf_split_ids();
830 if let Some(pos) = leaf_ids.iter().position(|id| *id == self.active_split) {
831 let prev_pos = if pos == 0 { leaf_ids.len() } else { pos } - 1;
832 self.active_split = leaf_ids[prev_pos];
833 }
834 }
835
836 pub fn splits_for_buffer(&self, target_buffer_id: BufferId) -> Vec<SplitId> {
838 self.root
839 .get_leaves_with_rects(Rect {
840 x: 0,
841 y: 0,
842 width: 1,
843 height: 1,
844 })
845 .into_iter()
846 .filter(|(_, buffer_id, _)| *buffer_id == target_buffer_id)
847 .map(|(split_id, _, _)| split_id)
848 .collect()
849 }
850
851 pub fn buffer_for_split(&self, target_split_id: SplitId) -> Option<BufferId> {
853 self.root
854 .get_leaves_with_rects(Rect {
855 x: 0,
856 y: 0,
857 width: 1,
858 height: 1,
859 })
860 .into_iter()
861 .find(|(split_id, _, _)| *split_id == target_split_id)
862 .map(|(_, buffer_id, _)| buffer_id)
863 }
864
865 pub fn maximize_split(&mut self) -> Result<(), String> {
868 if self.root.count_leaves() <= 1 {
870 return Err("Cannot maximize: only one split exists".to_string());
871 }
872
873 if self.maximized_split.is_some() {
875 return Err("A split is already maximized".to_string());
876 }
877
878 self.maximized_split = Some(self.active_split);
880 Ok(())
881 }
882
883 pub fn unmaximize_split(&mut self) -> Result<(), String> {
886 if self.maximized_split.is_none() {
887 return Err("No split is maximized".to_string());
888 }
889
890 self.maximized_split = None;
891 Ok(())
892 }
893
894 pub fn is_maximized(&self) -> bool {
896 self.maximized_split.is_some()
897 }
898
899 pub fn maximized_split(&self) -> Option<SplitId> {
901 self.maximized_split
902 }
903
904 pub fn toggle_maximize(&mut self) -> Result<bool, String> {
908 if self.is_maximized() {
909 self.unmaximize_split()?;
910 Ok(false)
911 } else {
912 self.maximize_split()?;
913 Ok(true)
914 }
915 }
916
917 pub fn get_splits_in_group(
919 &self,
920 group_id: u32,
921 view_states: &std::collections::HashMap<SplitId, SplitViewState>,
922 ) -> Vec<SplitId> {
923 self.root
924 .leaf_split_ids()
925 .into_iter()
926 .filter(|id| {
927 view_states
928 .get(id)
929 .and_then(|vs| vs.sync_group)
930 .is_some_and(|g| g == group_id)
931 })
932 .collect()
933 }
934}
935
936#[cfg(test)]
937mod tests {
938 use super::*;
939
940 #[test]
941 fn test_create_split_manager() {
942 let buffer_id = BufferId(0);
943 let manager = SplitManager::new(buffer_id);
944
945 assert_eq!(manager.active_buffer_id(), Some(buffer_id));
946 assert_eq!(manager.root().count_leaves(), 1);
947 }
948
949 #[test]
950 fn test_horizontal_split() {
951 let buffer_a = BufferId(0);
952 let buffer_b = BufferId(1);
953
954 let mut manager = SplitManager::new(buffer_a);
955 let result = manager.split_active(SplitDirection::Horizontal, buffer_b, 0.5);
956
957 assert!(result.is_ok());
958 assert_eq!(manager.root().count_leaves(), 2);
959 }
960
961 #[test]
962 fn test_vertical_split() {
963 let buffer_a = BufferId(0);
964 let buffer_b = BufferId(1);
965
966 let mut manager = SplitManager::new(buffer_a);
967 let result = manager.split_active(SplitDirection::Vertical, buffer_b, 0.5);
968
969 assert!(result.is_ok());
970 assert_eq!(manager.root().count_leaves(), 2);
971 }
972
973 #[test]
974 fn test_nested_splits() {
975 let buffer_a = BufferId(0);
976 let buffer_b = BufferId(1);
977 let buffer_c = BufferId(2);
978
979 let mut manager = SplitManager::new(buffer_a);
980
981 manager
983 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
984 .unwrap();
985
986 manager
988 .split_active(SplitDirection::Vertical, buffer_c, 0.5)
989 .unwrap();
990
991 assert_eq!(manager.root().count_leaves(), 3);
992 }
993
994 #[test]
995 fn test_close_split() {
996 let buffer_a = BufferId(0);
997 let buffer_b = BufferId(1);
998
999 let mut manager = SplitManager::new(buffer_a);
1000 let new_split = manager
1001 .split_active(SplitDirection::Horizontal, buffer_b, 0.5)
1002 .unwrap();
1003
1004 assert_eq!(manager.root().count_leaves(), 2);
1005
1006 let result = manager.close_split(new_split);
1008 assert!(result.is_ok());
1009 assert_eq!(manager.root().count_leaves(), 1);
1010 }
1011
1012 #[test]
1013 fn test_cannot_close_last_split() {
1014 let buffer_a = BufferId(0);
1015 let mut manager = SplitManager::new(buffer_a);
1016
1017 let result = manager.close_split(manager.active_split());
1018 assert!(result.is_err());
1019 }
1020
1021 #[test]
1022 fn test_split_rect_horizontal() {
1023 let rect = Rect {
1024 x: 0,
1025 y: 0,
1026 width: 100,
1027 height: 100,
1028 };
1029
1030 let (first, second) = split_rect(rect, SplitDirection::Horizontal, 0.5);
1031
1032 assert_eq!(first.height, 50);
1034 assert_eq!(second.height, 49);
1035 assert_eq!(first.width, 100);
1036 assert_eq!(second.width, 100);
1037 assert_eq!(first.y, 0);
1038 assert_eq!(second.y, 51); }
1040
1041 #[test]
1042 fn test_split_rect_vertical() {
1043 let rect = Rect {
1044 x: 0,
1045 y: 0,
1046 width: 100,
1047 height: 100,
1048 };
1049
1050 let (first, second) = split_rect(rect, SplitDirection::Vertical, 0.5);
1051
1052 assert_eq!(first.width, 50);
1054 assert_eq!(second.width, 49);
1055 assert_eq!(first.height, 100);
1056 assert_eq!(second.height, 100);
1057 assert_eq!(first.x, 0);
1058 assert_eq!(second.x, 51); }
1060}