tsu_editor/
window_manager.rs1#![allow(clippy::cast_precision_loss)]
2#![allow(clippy::cast_sign_loss)]
3#![allow(clippy::cast_possible_truncation)]
4
5use crate::{editor::Point, window::Window};
6
7#[derive(Debug, Clone)]
8pub enum Split {
9 Window(Window),
10 Horizontal {
11 top: Box<Split>,
12 bottom: Box<Split>,
13 ratio: f32,
14 },
15 Vertical {
16 left: Box<Split>,
17 right: Box<Split>,
18 ratio: f32,
19 },
20}
21
22impl Split {
23 #[must_use]
24 pub fn new_window(buffer_index: usize, position: Point, size: (usize, usize)) -> Self {
25 Split::Window(Window::new(buffer_index, position, size))
26 }
27
28 #[must_use]
29 pub fn windows(&self) -> Vec<&Window> {
30 match self {
31 Split::Window(w) => vec![w],
32 Split::Horizontal { top, bottom, .. } => {
33 let mut windows = top.windows();
34 windows.extend(bottom.windows());
35 windows
36 }
37 Split::Vertical { left, right, .. } => {
38 let mut windows = left.windows();
39 windows.extend(right.windows());
40 windows
41 }
42 }
43 }
44
45 pub fn windows_mut(&mut self) -> Vec<&mut Window> {
46 match self {
47 Split::Window(w) => vec![w],
48 Split::Horizontal { top, bottom, .. } => {
49 let mut windows = top.windows_mut();
50 windows.extend(bottom.windows_mut());
51 windows
52 }
53 Split::Vertical { left, right, .. } => {
54 let mut windows = left.windows_mut();
55 windows.extend(right.windows_mut());
56 windows
57 }
58 }
59 }
60
61 pub fn layout(&mut self, position: Point, size: (usize, usize)) {
62 match self {
63 Split::Window(w) => {
64 w.position = position;
65 w.size = size;
66 }
67 Split::Horizontal { top, bottom, ratio } => {
68 let available_height = size.1.saturating_sub(1);
69 let split_y = (available_height as f32 * *ratio) as usize;
70
71 top.layout(position, (size.0, split_y));
72 bottom.layout(
73 Point::new(position.x, position.y + split_y + 1),
74 (size.0, available_height - split_y),
75 );
76 }
77 Split::Vertical { left, right, ratio } => {
78 let available_width = size.0.saturating_sub(1);
79 let split_x = (available_width as f32 * *ratio) as usize;
80
81 left.layout(position, (split_x, size.1));
82 right.layout(
83 Point::new(position.x + split_x + 1, position.y),
84 (available_width - split_x, size.1),
85 );
86 }
87 }
88 }
89}
90
91pub struct WindowManager {
92 root: Split,
93 active_window_id: usize,
94}
95
96impl WindowManager {
97 #[must_use]
98 pub fn new(buffer_index: usize, terminal_size: (usize, usize)) -> Self {
99 let mut root = Split::new_window(
100 buffer_index,
101 Point::new(0, 0),
102 (terminal_size.0, terminal_size.1.saturating_sub(2)),
103 );
104
105 if let Split::Window(w) = &mut root {
106 w.active = true;
107 }
108
109 Self {
110 root,
111 active_window_id: 0,
112 }
113 }
114
115 #[must_use]
116 pub fn active_window(&self) -> Option<&Window> {
117 self.root.windows().get(self.active_window_id).copied()
118 }
119
120 pub fn active_window_mut(&mut self) -> Option<&mut Window> {
121 let mut current_id = 0;
122 Self::get_window_mut_recursive(&mut self.root, &mut current_id, self.active_window_id)
123 }
124
125 fn get_window_mut_recursive<'a>(
126 node: &'a mut Split,
127 current_id: &mut usize,
128 target_id: usize,
129 ) -> Option<&'a mut Window> {
130 match node {
131 Split::Window(window) => {
132 if *current_id == target_id {
133 Some(window)
134 } else {
135 *current_id += 1;
136 None
137 }
138 }
139 Split::Horizontal { top, bottom, .. } => {
140 if let Some(window) = Self::get_window_mut_recursive(top, current_id, target_id) {
141 return Some(window);
142 }
143 Self::get_window_mut_recursive(bottom, current_id, target_id)
144 }
145 Split::Vertical { left, right, .. } => {
146 if let Some(window) = Self::get_window_mut_recursive(left, current_id, target_id) {
147 return Some(window);
148 }
149 Self::get_window_mut_recursive(right, current_id, target_id)
150 }
151 }
152 }
153
154 #[must_use]
155 pub fn windows(&self) -> Vec<&Window> {
156 self.root.windows()
157 }
158
159 pub fn windows_mut(&mut self) -> Vec<&mut Window> {
160 self.root.windows_mut()
161 }
162
163 pub fn resize(&mut self, terminal_size: (usize, usize)) {
164 self.root.layout(
165 Point::new(0, 0),
166 (terminal_size.0, terminal_size.1.saturating_sub(2)),
167 );
168 }
169
170 #[must_use]
171 pub fn active_window_id(&self) -> usize {
172 self.active_window_id
173 }
174
175 pub fn set_active(&mut self, window_id: usize) {
176 for window in self.root.windows_mut() {
177 window.active = false;
178 }
179
180 if let Some(window) = self.root.windows_mut().get_mut(window_id) {
181 window.active = true;
182 self.active_window_id = window_id;
183 }
184 }
185}