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