1use gpui::{
2 canvas, div, point, prelude::*, px, App, Bounds, Context, CursorStyle, Decorations, Entity,
3 HitboxBehavior, IntoElement, MouseButton, Pixels, Point, Render, ResizeEdge, Size, Window,
4};
5
6const RESIZE_INSET: Pixels = px(4.0);
9
10pub struct WindowChrome<V: Render + 'static> {
20 content: Entity<V>,
21}
22
23impl<V: Render + 'static> WindowChrome<V> {
24 pub fn new(content: Entity<V>) -> Self {
25 Self { content }
26 }
27}
28
29impl<V: Render + 'static> Render for WindowChrome<V> {
30 fn render(&mut self, window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
31 let decorations = window.window_decorations();
32 let content = self.content.clone();
33
34 div().size_full().child(content).map(|d| match decorations {
35 Decorations::Server => d,
36 Decorations::Client { .. } => d
37 .child(
40 canvas(
41 |_bounds, window, _cx| {
42 window.insert_hitbox(
43 Bounds::new(
44 point(px(0.0), px(0.0)),
45 window.window_bounds().get_bounds().size,
46 ),
47 HitboxBehavior::Normal,
48 )
49 },
50 move |_bounds, hitbox, window, _cx| {
51 let mouse = window.mouse_position();
52 let size = window.window_bounds().get_bounds().size;
53 let Some(edge) = resize_edge_at(mouse, RESIZE_INSET, size) else {
54 return;
55 };
56 window.set_cursor_style(cursor_for_edge(edge), &hitbox);
57 },
58 )
59 .size_full()
60 .absolute(),
61 )
62 .on_mouse_move(|_, window, _| window.refresh())
63 .on_mouse_down(MouseButton::Left, move |e, window, _| {
64 let size = window.window_bounds().get_bounds().size;
65 if let Some(edge) = resize_edge_at(e.position, RESIZE_INSET, size) {
66 window.start_window_resize(edge);
67 }
68 }),
69 })
70 }
71}
72
73pub fn wrap_with_chrome<V: Render + 'static>(
75 content: Entity<V>,
76 cx: &mut App,
77) -> Entity<WindowChrome<V>> {
78 cx.new(|_| WindowChrome::new(content))
79}
80
81fn resize_edge_at(pos: Point<Pixels>, inset: Pixels, size: Size<Pixels>) -> Option<ResizeEdge> {
82 let top = pos.y < inset;
83 let bottom = pos.y > size.height - inset;
84 let left = pos.x < inset;
85 let right = pos.x > size.width - inset;
86 Some(match (top, bottom, left, right) {
87 (true, _, true, _) => ResizeEdge::TopLeft,
88 (true, _, _, true) => ResizeEdge::TopRight,
89 (_, true, true, _) => ResizeEdge::BottomLeft,
90 (_, true, _, true) => ResizeEdge::BottomRight,
91 (true, _, _, _) => ResizeEdge::Top,
92 (_, true, _, _) => ResizeEdge::Bottom,
93 (_, _, true, _) => ResizeEdge::Left,
94 (_, _, _, true) => ResizeEdge::Right,
95 _ => return None,
96 })
97}
98
99fn cursor_for_edge(edge: ResizeEdge) -> CursorStyle {
100 match edge {
101 ResizeEdge::Top | ResizeEdge::Bottom => CursorStyle::ResizeUpDown,
102 ResizeEdge::Left | ResizeEdge::Right => CursorStyle::ResizeLeftRight,
103 ResizeEdge::TopLeft | ResizeEdge::BottomRight => CursorStyle::ResizeUpLeftDownRight,
104 ResizeEdge::TopRight | ResizeEdge::BottomLeft => CursorStyle::ResizeUpRightDownLeft,
105 }
106}