1use gpui::{
2 App, Application, Bounds, Context, CursorStyle, Decorations, HitboxBehavior, Hsla, MouseButton,
3 Pixels, Point, ResizeEdge, Size, Window, WindowBackgroundAppearance, WindowBounds,
4 WindowDecorations, WindowOptions, black, canvas, div, green, point, prelude::*, px, rgb, size,
5 transparent_black, white,
6};
7
8struct WindowShadow {}
9
10// Things to do:
11// 1. We need a way of calculating which edge or corner the mouse is on,
12// and then dispatch on that
13// 2. We need to improve the shadow rendering significantly
14// 3. We need to implement the techniques in here in Zed
15
16impl Render for WindowShadow {
17 fn render(&mut self, window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
18 let decorations = window.window_decorations();
19 let rounding = px(10.0);
20 let shadow_size = px(10.0);
21 let border_size = px(1.0);
22 let grey = rgb(0x808080);
23 window.set_client_inset(shadow_size);
24
25 div()
26 .id("window-backdrop")
27 .bg(transparent_black())
28 .map(|div| match decorations {
29 Decorations::Server => div,
30 Decorations::Client { tiling, .. } => div
31 .bg(gpui::transparent_black())
32 .child(
33 canvas(
34 |_bounds, window, _cx| {
35 window.insert_hitbox(
36 Bounds::new(
37 point(px(0.0), px(0.0)),
38 window.window_bounds().get_bounds().size,
39 ),
40 HitboxBehavior::Normal,
41 )
42 },
43 move |_bounds, hitbox, window, _cx| {
44 let mouse = window.mouse_position();
45 let size = window.window_bounds().get_bounds().size;
46 let Some(edge) = resize_edge(mouse, shadow_size, size) else {
47 return;
48 };
49 window.set_cursor_style(
50 match edge {
51 ResizeEdge::Top | ResizeEdge::Bottom => {
52 CursorStyle::ResizeUpDown
53 }
54 ResizeEdge::Left | ResizeEdge::Right => {
55 CursorStyle::ResizeLeftRight
56 }
57 ResizeEdge::TopLeft | ResizeEdge::BottomRight => {
58 CursorStyle::ResizeUpLeftDownRight
59 }
60 ResizeEdge::TopRight | ResizeEdge::BottomLeft => {
61 CursorStyle::ResizeUpRightDownLeft
62 }
63 },
64 &hitbox,
65 );
66 },
67 )
68 .size_full()
69 .absolute(),
70 )
71 .when(!(tiling.top || tiling.right), |div| {
72 div.rounded_tr(rounding)
73 })
74 .when(!(tiling.top || tiling.left), |div| div.rounded_tl(rounding))
75 .when(!tiling.top, |div| div.pt(shadow_size))
76 .when(!tiling.bottom, |div| div.pb(shadow_size))
77 .when(!tiling.left, |div| div.pl(shadow_size))
78 .when(!tiling.right, |div| div.pr(shadow_size))
79 .on_mouse_move(|_e, window, _cx| window.refresh())
80 .on_mouse_down(MouseButton::Left, move |e, window, _cx| {
81 let size = window.window_bounds().get_bounds().size;
82 let pos = e.position;
83
84 match resize_edge(pos, shadow_size, size) {
85 Some(edge) => window.start_window_resize(edge),
86 None => window.start_window_move(),
87 };
88 }),
89 })
90 .size_full()
91 .child(
92 div()
93 .cursor(CursorStyle::Arrow)
94 .map(|div| match decorations {
95 Decorations::Server => div,
96 Decorations::Client { tiling } => div
97 .border_color(grey)
98 .when(!(tiling.top || tiling.right), |div| {
99 div.rounded_tr(rounding)
100 })
101 .when(!(tiling.top || tiling.left), |div| div.rounded_tl(rounding))
102 .when(!tiling.top, |div| div.border_t(border_size))
103 .when(!tiling.bottom, |div| div.border_b(border_size))
104 .when(!tiling.left, |div| div.border_l(border_size))
105 .when(!tiling.right, |div| div.border_r(border_size))
106 .when(!tiling.is_tiled(), |div| {
107 div.shadow(vec![gpui::BoxShadow {
108 color: Hsla {
109 h: 0.,
110 s: 0.,
111 l: 0.,
112 a: 0.4,
113 },
114 blur_radius: shadow_size / 2.,
115 spread_radius: px(0.),
116 offset: point(px(0.0), px(0.0)),
117 }])
118 }),
119 })
120 .on_mouse_move(|_e, _, cx| {
121 cx.stop_propagation();
122 })
123 .bg(gpui::rgb(0xCCCCFF))
124 .size_full()
125 .flex()
126 .flex_col()
127 .justify_around()
128 .child(
129 div().w_full().flex().flex_row().justify_around().child(
130 div()
131 .flex()
132 .bg(white())
133 .size(px(300.0))
134 .justify_center()
135 .items_center()
136 .shadow_lg()
137 .border_1()
138 .border_color(rgb(0x0000ff))
139 .text_xl()
140 .text_color(rgb(0xffffff))
141 .child(
142 div()
143 .id("hello")
144 .w(px(200.0))
145 .h(px(100.0))
146 .bg(green())
147 .shadow(vec![gpui::BoxShadow {
148 color: Hsla {
149 h: 0.,
150 s: 0.,
151 l: 0.,
152 a: 1.0,
153 },
154 blur_radius: px(20.0),
155 spread_radius: px(0.0),
156 offset: point(px(0.0), px(0.0)),
157 }])
158 .map(|div| match decorations {
159 Decorations::Server => div,
160 Decorations::Client { .. } => div
161 .on_mouse_down(
162 MouseButton::Left,
163 |_e, window, _| {
164 window.start_window_move();
165 },
166 )
167 .on_click(|e, window, _| {
168 if e.is_right_click() {
169 window.show_window_menu(e.position());
170 }
171 })
172 .text_color(black())
173 .child("this is the custom titlebar"),
174 }),
175 ),
176 ),
177 ),
178 )
179 }
180}
181
182fn resize_edge(pos: Point<Pixels>, shadow_size: Pixels, size: Size<Pixels>) -> Option<ResizeEdge> {
183 let edge = if pos.y < shadow_size && pos.x < shadow_size {
184 ResizeEdge::TopLeft
185 } else if pos.y < shadow_size && pos.x > size.width - shadow_size {
186 ResizeEdge::TopRight
187 } else if pos.y < shadow_size {
188 ResizeEdge::Top
189 } else if pos.y > size.height - shadow_size && pos.x < shadow_size {
190 ResizeEdge::BottomLeft
191 } else if pos.y > size.height - shadow_size && pos.x > size.width - shadow_size {
192 ResizeEdge::BottomRight
193 } else if pos.y > size.height - shadow_size {
194 ResizeEdge::Bottom
195 } else if pos.x < shadow_size {
196 ResizeEdge::Left
197 } else if pos.x > size.width - shadow_size {
198 ResizeEdge::Right
199 } else {
200 return None;
201 };
202 Some(edge)
203}
204
205fn main() {
206 Application::new().run(|cx: &mut App| {
207 let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
208 cx.open_window(
209 WindowOptions {
210 window_bounds: Some(WindowBounds::Windowed(bounds)),
211 window_background: WindowBackgroundAppearance::Opaque,
212 window_decorations: Some(WindowDecorations::Client),
213 ..Default::default()
214 },
215 |window, cx| {
216 cx.new(|cx| {
217 cx.observe_window_appearance(window, |_, window, _| {
218 window.refresh();
219 })
220 .detach();
221 WindowShadow {}
222 })
223 },
224 )
225 .unwrap();
226 });
227}