1use gpui::prelude::*;
15use gpui::{div, px, App, IntoElement, MouseButton, SharedString, Window, WindowControlArea};
16
17use crate::devtools::Probed;
18use crate::theme::theme;
19
20#[derive(IntoElement)]
22pub struct WindowControls {
23 width: f32,
24 height: f32,
25}
26
27impl Default for WindowControls {
28 fn default() -> Self {
29 WindowControls::new()
30 }
31}
32
33impl WindowControls {
34 pub fn new() -> Self {
35 WindowControls {
36 width: 46.0,
37 height: 28.0,
38 }
39 }
40
41 pub fn needed() -> bool {
47 cfg!(target_os = "linux")
48 }
49
50 pub fn button_width(mut self, width: f32) -> Self {
52 self.width = width;
53 self
54 }
55
56 pub fn height(mut self, height: f32) -> Self {
58 self.height = height;
59 self
60 }
61}
62
63impl RenderOnce for WindowControls {
64 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
65 let t = theme(cx);
66 let fg = t.text().hsla();
67 let dim = fg.opacity(0.6);
68 let hover = fg.opacity(0.12);
69 let (width, height) = (self.width, self.height);
70
71 let button = move |id: &'static str, glyph: &'static str| {
72 div()
73 .id(id)
74 .w(px(width))
75 .h_full()
76 .flex()
77 .items_center()
78 .justify_center()
79 .text_color(dim)
80 .hover(move |st| st.bg(hover).text_color(fg))
81 .child(SharedString::new_static(glyph))
82 };
83
84 div()
85 .flex()
86 .items_center()
87 .flex_none()
88 .h(px(height))
89 .child(
90 button("guise-window-min", "\u{2013}")
91 .window_control_area(WindowControlArea::Min)
92 .on_click(|_, window, _| window.minimize_window()),
93 )
94 .child(
95 button("guise-window-max", "\u{25a1}")
96 .window_control_area(WindowControlArea::Max)
97 .on_click(|_, window, _| window.zoom_window()),
98 )
99 .child(
100 button("guise-window-close", "\u{2715}")
101 .window_control_area(WindowControlArea::Close)
102 .on_click(|_, window, _| window.remove_window()),
103 )
104 .probe("WindowControls")
105 }
106}
107
108#[derive(IntoElement)]
114pub struct ResizeHandles {
115 edge: f32,
116 corner: f32,
117}
118
119impl Default for ResizeHandles {
120 fn default() -> Self {
121 ResizeHandles::new()
122 }
123}
124
125impl ResizeHandles {
126 pub fn new() -> Self {
127 ResizeHandles {
128 edge: 6.0,
129 corner: 12.0,
130 }
131 }
132
133 pub fn needed() -> bool {
135 cfg!(target_os = "linux")
136 }
137
138 pub fn edge(mut self, edge: f32) -> Self {
140 self.edge = edge;
141 self
142 }
143
144 pub fn corner(mut self, corner: f32) -> Self {
147 self.corner = corner;
148 self
149 }
150}
151
152impl RenderOnce for ResizeHandles {
153 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
154 use gpui::ResizeEdge;
155
156 let zone = |id: &'static str, edge: ResizeEdge| {
157 div()
158 .id(id)
159 .absolute()
160 .on_mouse_down(MouseButton::Left, move |_, window, _| {
161 window.start_window_resize(edge);
162 })
163 };
164 let (t, c) = (px(self.edge), px(self.corner));
165
166 div()
167 .absolute()
168 .inset_0()
169 .child(
170 zone("guise-resize-t", ResizeEdge::Top)
171 .top_0()
172 .left_0()
173 .right_0()
174 .h(t),
175 )
176 .child(
177 zone("guise-resize-b", ResizeEdge::Bottom)
178 .bottom_0()
179 .left_0()
180 .right_0()
181 .h(t),
182 )
183 .child(
184 zone("guise-resize-l", ResizeEdge::Left)
185 .top_0()
186 .bottom_0()
187 .left_0()
188 .w(t),
189 )
190 .child(
191 zone("guise-resize-r", ResizeEdge::Right)
192 .top_0()
193 .bottom_0()
194 .right_0()
195 .w(t),
196 )
197 .child(
198 zone("guise-resize-tl", ResizeEdge::TopLeft)
199 .top_0()
200 .left_0()
201 .w(c)
202 .h(c),
203 )
204 .child(
205 zone("guise-resize-tr", ResizeEdge::TopRight)
206 .top_0()
207 .right_0()
208 .w(c)
209 .h(c),
210 )
211 .child(
212 zone("guise-resize-bl", ResizeEdge::BottomLeft)
213 .bottom_0()
214 .left_0()
215 .w(c)
216 .h(c),
217 )
218 .child(
219 zone("guise-resize-br", ResizeEdge::BottomRight)
220 .bottom_0()
221 .right_0()
222 .w(c)
223 .h(c),
224 )
225 .probe("ResizeHandles")
226 }
227}
228
229pub const TRAFFIC_LIGHT_INSET: f32 = if cfg!(target_os = "macos") { 88.0 } else { 0.0 };