window_shadow.rs

  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}