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