Skip to main content

tsu_editor/
window_manager.rs

1#![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}