1use crate::decorations::frame_state::{WindowFrameState, WindowFrameStyle};
5use rat_focus::HasFocus;
6use rat_widget::util::revert_style;
7use ratatui::buffer::Buffer;
8use ratatui::layout::Rect;
9use ratatui::prelude::BlockExt;
10use ratatui::style::{Style, Stylize};
11use ratatui::text::Span;
12use ratatui::widgets::{Block, StatefulWidget, Widget};
13
14#[derive(Debug, Default)]
26pub struct MacFrame<'a> {
27 no_fill: bool,
28 block: Option<Block<'a>>,
29
30 style: Style,
31 top_style: Option<Style>,
32 focus_style: Option<Style>,
33 hover_style: Style,
34 drag_style: Style,
35 close_style: Option<Style>,
36 min_style: Option<Style>,
37 max_style: Option<Style>,
38
39 limit: Option<Rect>,
40
41 can_move: Option<bool>,
42 can_resize: Option<bool>,
43 can_close: Option<bool>,
44 can_min: Option<bool>,
45 can_max: Option<bool>,
46}
47
48impl<'a> MacFrame<'a> {
49 pub fn new() -> Self {
50 Self {
51 no_fill: Default::default(),
52 block: Default::default(),
53 style: Default::default(),
54 top_style: Default::default(),
55 focus_style: Default::default(),
56 hover_style: Default::default(),
57 drag_style: Default::default(),
58 close_style: Default::default(),
59 min_style: Default::default(),
60 max_style: Default::default(),
61 limit: Default::default(),
62 can_move: Default::default(),
63 can_resize: Default::default(),
64 can_close: Default::default(),
65 can_min: Default::default(),
66 can_max: Default::default(),
67 }
68 }
69
70 pub fn no_fill(mut self) -> Self {
72 self.no_fill = true;
73 self
74 }
75
76 pub fn limit(mut self, area: Rect) -> Self {
80 self.limit = Some(area);
81 self
82 }
83
84 pub fn can_move(mut self, v: bool) -> Self {
86 self.can_move = Some(v);
87 self
88 }
89
90 pub fn can_resize(mut self, v: bool) -> Self {
92 self.can_resize = Some(v);
93 self
94 }
95
96 pub fn can_close(mut self, v: bool) -> Self {
98 self.can_close = Some(v);
99 self
100 }
101
102 pub fn can_min(mut self, v: bool) -> Self {
104 self.can_min = Some(v);
105 self
106 }
107
108 pub fn can_max(mut self, v: bool) -> Self {
110 self.can_max = Some(v);
111 self
112 }
113
114 pub fn block(mut self, block: Block<'a>) -> Self {
116 self.block = Some(block.style(self.style));
117 self
118 }
119
120 pub fn styles(mut self, styles: WindowFrameStyle) -> Self {
121 self.style = styles.style;
122 self.block = styles.block;
123 if styles.top.is_some() {
124 self.top_style = styles.top;
125 }
126 if styles.focus.is_some() {
127 self.focus_style = styles.focus;
128 }
129 if let Some(hover) = styles.hover {
130 self.hover_style = hover;
131 }
132 if let Some(drag) = styles.drag {
133 self.drag_style = drag;
134 }
135 if let Some(drag) = styles.drag {
136 self.drag_style = drag;
137 }
138 if let Some(close) = styles.close {
139 self.close_style = Some(close);
140 }
141 if let Some(min) = styles.min {
142 self.min_style = Some(min);
143 }
144 if let Some(max) = styles.max {
145 self.max_style = Some(max);
146 }
147 if let Some(can_move) = styles.can_move {
148 self.can_move = Some(can_move);
149 }
150 if let Some(can_resize) = styles.can_resize {
151 self.can_resize = Some(can_resize);
152 }
153 if let Some(can_close) = styles.can_close {
154 self.can_move = Some(can_close);
155 }
156 if let Some(can_min) = styles.can_min {
157 self.can_min = Some(can_min);
158 }
159 if let Some(can_max) = styles.can_max {
160 self.can_max = Some(can_max);
161 }
162 self
163 }
164
165 pub fn style(mut self, style: Style) -> Self {
167 self.style = style;
168 self.block = self.block.map(|v| v.style(style));
169 self
170 }
171
172 pub fn title_style(mut self, style: Style) -> Self {
174 self.top_style = Some(style);
175 self
176 }
177
178 pub fn focus_style(mut self, style: Style) -> Self {
180 self.top_style = Some(style);
181 self
182 }
183
184 pub fn hover_style(mut self, hover: Style) -> Self {
186 self.hover_style = hover;
187 self
188 }
189
190 pub fn drag_style(mut self, drag: Style) -> Self {
192 self.drag_style = drag;
193 self
194 }
195}
196
197impl<'a> StatefulWidget for MacFrame<'a> {
198 type State = WindowFrameState;
199
200 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
201 if let Some(limit) = self.limit {
202 state.limit = limit;
203 } else {
204 state.limit = area;
205 }
206 state.area = state.area.intersection(state.limit);
207 state.widget_area = self.block.inner_if_some(state.area);
208
209 if let Some(v) = self.can_move {
210 state.can_move = v;
211 }
212 if let Some(v) = self.can_resize {
213 state.can_resize = v;
214 }
215 if let Some(v) = self.can_close {
216 state.can_close = v;
217 }
218
219 if state.can_resize {
220 state.resize_area = Rect::new(
221 state.area.right().saturating_sub(2),
222 state.area.bottom().saturating_sub(1),
223 2,
224 1,
225 );
226 } else {
227 state.resize_area = Default::default();
228 }
229 if state.can_close && !state.area.is_empty() {
230 state.close_area = Rect::new(state.area.x + 2, state.area.y, 3, 1);
231 } else {
232 state.close_area = Default::default();
233 }
234 if state.can_min && !state.area.is_empty() {
235 state.min_area = Rect::new(state.area.x + 5, state.area.y, 3, 1);
236 } else {
237 state.min_area = Default::default();
238 }
239 if state.can_max && !state.area.is_empty() {
240 state.max_area = Rect::new(state.area.x + 8, state.area.y, 3, 1);
241 } else {
242 state.max_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 + 11, state.area.y,
250 state.area.width.saturating_sub(13),
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_close && !state.area.is_empty() {
301 Span::from(" ⬤ ")
302 .style(self.close_style.unwrap_or(self.style.red()))
303 .render(state.close_area, buf);
304 }
305 if state.can_min && !state.area.is_empty() {
306 Span::from(" ⬤ ")
307 .style(self.min_style.unwrap_or(self.style.yellow()))
308 .render(state.min_area, buf);
309 }
310 if state.can_max && !state.area.is_empty() {
311 Span::from(" ⬤ ")
312 .style(self.max_style.unwrap_or(self.style.green()))
313 .render(state.max_area, buf);
314 }
315
316 if state.mouse_close.hover.get() {
317 buf.set_style(
318 state.close_area,
319 revert_style(self.close_style.unwrap_or(self.style.red())),
320 );
321 }
322 if state.mouse_min.hover.get() {
323 buf.set_style(
324 state.min_area,
325 revert_style(self.min_style.unwrap_or(self.style.yellow())),
326 );
327 }
328 if state.mouse_max.hover.get() {
329 buf.set_style(
330 state.max_area,
331 revert_style(self.max_style.unwrap_or(self.style.green())),
332 );
333 }
334
335 if state.mouse_move.drag.get() {
336 buf.set_style(state.move_area, self.drag_style);
337 } else if state.mouse_move.hover.get() {
338 buf.set_style(state.move_area, self.hover_style);
339 }
340
341 if state.mouse_resize.drag.get() {
342 buf.set_style(state.resize_area, self.drag_style);
343 } else if state.mouse_resize.hover.get() {
344 buf.set_style(state.resize_area, self.hover_style);
345 }
346 }
347}