1use crate::decorations::frame_state::{WindowFrameState, WindowFrameStyle};
5use rat_focus::HasFocus;
6use ratatui::buffer::Buffer;
7use ratatui::layout::Rect;
8use ratatui::prelude::BlockExt;
9use ratatui::style::Style;
10use ratatui::text::Span;
11use ratatui::widgets::{Block, StatefulWidget, Widget};
12
13#[derive(Debug, Default)]
25pub struct WindowFrame<'a> {
26 no_fill: bool,
27 block: Option<Block<'a>>,
28
29 style: Style,
30 top_style: Option<Style>,
31 focus_style: Option<Style>,
32 hover_style: Style,
33 drag_style: Style,
34 close_style: Option<Style>,
35 min_style: Option<Style>,
36 max_style: Option<Style>,
37
38 limit: Option<Rect>,
39
40 can_move: Option<bool>,
41 can_resize: Option<bool>,
42 can_close: Option<bool>,
43 can_min: Option<bool>,
44 can_max: Option<bool>,
45}
46
47impl<'a> WindowFrame<'a> {
48 pub fn new() -> Self {
49 Self {
50 no_fill: Default::default(),
51 block: Default::default(),
52 style: Default::default(),
53 top_style: Default::default(),
54 focus_style: Default::default(),
55 hover_style: Default::default(),
56 drag_style: Default::default(),
57 close_style: Default::default(),
58 min_style: Default::default(),
59 max_style: Default::default(),
60 limit: Default::default(),
61 can_move: Default::default(),
62 can_resize: Default::default(),
63 can_close: Default::default(),
64 can_min: Default::default(),
65 can_max: Default::default(),
66 }
67 }
68
69 pub fn no_fill(mut self) -> Self {
71 self.no_fill = true;
72 self
73 }
74
75 pub fn limit(mut self, area: Rect) -> Self {
79 self.limit = Some(area);
80 self
81 }
82
83 pub fn can_move(mut self, v: bool) -> Self {
85 self.can_move = Some(v);
86 self
87 }
88
89 pub fn can_resize(mut self, v: bool) -> Self {
91 self.can_resize = Some(v);
92 self
93 }
94
95 pub fn can_close(mut self, v: bool) -> Self {
97 self.can_close = Some(v);
98 self
99 }
100
101 pub fn can_min(mut self, v: bool) -> Self {
103 self.can_min = Some(v);
104 self
105 }
106
107 pub fn can_max(mut self, v: bool) -> Self {
109 self.can_max = Some(v);
110 self
111 }
112
113 pub fn block(mut self, block: Block<'a>) -> Self {
115 self.block = Some(block.style(self.style));
116 self
117 }
118
119 pub fn styles(mut self, styles: WindowFrameStyle) -> Self {
120 self.style = styles.style;
121 self.block = styles.block;
122 if styles.top.is_some() {
123 self.top_style = styles.top;
124 }
125 if styles.focus.is_some() {
126 self.focus_style = styles.focus;
127 }
128 if let Some(hover) = styles.hover {
129 self.hover_style = hover;
130 }
131 if let Some(drag) = styles.drag {
132 self.drag_style = drag;
133 }
134 if let Some(drag) = styles.drag {
135 self.drag_style = drag;
136 }
137 if let Some(close) = styles.close {
138 self.close_style = Some(close);
139 }
140 if let Some(min) = styles.min {
141 self.min_style = Some(min);
142 }
143 if let Some(max) = styles.max {
144 self.max_style = Some(max);
145 }
146 if let Some(can_move) = styles.can_move {
147 self.can_move = Some(can_move);
148 }
149 if let Some(can_resize) = styles.can_resize {
150 self.can_resize = Some(can_resize);
151 }
152 if let Some(can_close) = styles.can_close {
153 self.can_move = Some(can_close);
154 }
155 if let Some(can_min) = styles.can_min {
156 self.can_min = Some(can_min);
157 }
158 if let Some(can_max) = styles.can_max {
159 self.can_max = Some(can_max);
160 }
161 self
162 }
163
164 pub fn style(mut self, style: Style) -> Self {
166 self.style = style;
167 self.block = self.block.map(|v| v.style(style));
168 self
169 }
170
171 pub fn title_style(mut self, style: Style) -> Self {
173 self.top_style = Some(style);
174 self
175 }
176
177 pub fn focus_style(mut self, style: Style) -> Self {
179 self.top_style = Some(style);
180 self
181 }
182
183 pub fn hover_style(mut self, hover: Style) -> Self {
185 self.hover_style = hover;
186 self
187 }
188
189 pub fn drag_style(mut self, drag: Style) -> Self {
191 self.drag_style = drag;
192 self
193 }
194}
195
196impl<'a> StatefulWidget for WindowFrame<'a> {
197 type State = WindowFrameState;
198
199 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
200 if let Some(limit) = self.limit {
201 state.limit = limit;
202 } else {
203 state.limit = area;
204 }
205 state.area = state.area.intersection(state.limit);
206 state.widget_area = self.block.inner_if_some(state.area);
207
208 if let Some(v) = self.can_move {
209 state.can_move = v;
210 }
211 if let Some(v) = self.can_resize {
212 state.can_resize = v;
213 }
214 if let Some(v) = self.can_close {
215 state.can_close = v;
216 }
217
218 if state.can_resize {
219 state.resize_area = Rect::new(
220 state.area.right().saturating_sub(2),
221 state.area.bottom().saturating_sub(1),
222 2,
223 1,
224 );
225 } else {
226 state.resize_area = Default::default();
227 }
228 if state.can_min && !state.area.is_empty() {
229 state.min_area = Rect::new(state.area.right().saturating_sub(10), state.area.y, 3, 1);
230 } else {
231 state.min_area = Default::default();
232 }
233 if state.can_max && !state.area.is_empty() {
234 state.max_area = Rect::new(state.area.right().saturating_sub(7), state.area.y, 3, 1);
235 } else {
236 state.max_area = Default::default();
237 }
238 if state.can_close && !state.area.is_empty() {
239 state.close_area =
240 Rect::new(state.area.right().saturating_sub(4), state.area.top(), 3, 1);
241 } else {
242 state.close_area = Default::default();
243 }
244
245 if state.can_move {
246 if state.can_close || state.can_min || state.can_max {
247 state.move_area = Rect::new(
248 state.area.x + 1,
249 state.area.y,
250 state.area.width.saturating_sub(11),
251 1,
252 );
253 } else {
254 state.move_area = Rect::new(
255 state.area.x + 1,
256 state.area.y,
257 state.area.width.saturating_sub(2),
258 1,
259 );
260 }
261 } else {
262 state.move_area = Default::default();
263 }
264
265 if !self.no_fill {
266 for y in state.area.top()..state.area.bottom() {
267 for x in state.area.left()..state.area.right() {
268 if let Some(cell) = buf.cell_mut((x, y)) {
269 cell.reset();
270 }
271 }
272 }
273 }
274
275 let block = if state.top {
276 if state.is_focused() {
277 if let Some(top_style) = self.focus_style.or(self.top_style) {
278 self.block.map(|v| v.title_style(top_style))
279 } else {
280 self.block
281 }
282 } else {
283 if let Some(top_style) = self.top_style {
284 self.block.map(|v| v.title_style(top_style))
285 } else {
286 self.block
287 }
288 }
289 } else {
290 self.block
291 };
292 let block = if self.no_fill {
293 block.map(|v| v.style(Style::new()))
294 } else {
295 block
296 };
297
298 block.render(state.area, buf);
299
300 if state.can_min && !state.area.is_empty() {
301 Span::from("[↓]")
302 .style(self.style)
303 .render(state.min_area, buf);
304 }
305 if state.can_max && !state.area.is_empty() {
306 if state.area == state.limit {
307 Span::from("[⇵]")
308 .style(self.style)
309 .render(state.max_area, buf);
310 } else {
311 Span::from("[↑]")
312 .style(self.style)
313 .render(state.max_area, buf);
314 }
315 }
316 if state.can_close && !state.area.is_empty() {
317 Span::from("[x]")
318 .style(self.style)
319 .render(state.close_area, buf);
320 }
321
322 if state.mouse_min.hover.get() {
323 buf.set_style(state.min_area, self.hover_style);
324 }
325 if state.mouse_max.hover.get() {
326 buf.set_style(state.max_area, self.hover_style);
327 }
328 if state.mouse_close.hover.get() {
329 buf.set_style(state.close_area, self.hover_style);
330 }
331
332 if state.mouse_move.drag.get() {
333 buf.set_style(state.move_area, self.drag_style);
334 } else if state.mouse_move.hover.get() {
335 buf.set_style(state.move_area, self.hover_style);
336 }
337
338 if state.mouse_resize.drag.get() {
339 buf.set_style(state.resize_area, self.drag_style);
340 } else if state.mouse_resize.hover.get() {
341 buf.set_style(state.resize_area, self.hover_style);
342 }
343 }
344}