Skip to main content

ui/
material.rs

1//! [`material`] — the material-glass float: wraps a popover/dialog card so its
2//! ENTIRE subtree paints inside one scene layer (a single draw order).
3//!
4//! The single layer order is the point: with per-primitive bounds-tree
5//! ordering, a hover repaint elsewhere can reassign the card's quads relative
6//! to siblings — inside one layer the card's stacking is structural.
7//!
8//! The blur is painted first, structurally under the content: inside one layer
9//! the order is blur, then shadow, tint, border, rows, text. It needs
10//! `Window::paint_backdrop_blur` from our gpui fork (macOS Metal only);
11//! elsewhere the primitive is ignored and the glass reads as the theme's
12//! translucent tint over the OS window blur.
13
14use gpui::{
15    AnyElement, App, Bounds, Corners, Element, GlobalElementId, InspectorElementId, IntoElement,
16    LayoutId, Pixels, Window, px,
17};
18
19use theme::Theme;
20
21/// Backdrop-blur sigma for floating menu/dialog glass — the reference
22/// `.glass-surface` runs `blur(44px)`, and the [`Theme::glass_overlay`] tint is
23/// thin enough that a 16px blur leaves backdrop detail ghosting through rows.
24pub const MENU_BLUR: f32 = 44.0;
25
26/// Frost `child` (a popover card): backdrop-blurred on glass, pass-through on
27/// opaque platforms. `corner_radius` must match the card's rounding.
28pub fn material(corner_radius: f32, blur_radius: f32, child: impl IntoElement) -> Material {
29    Material {
30        corner_radius,
31        blur_radius,
32        child: child.into_any_element(),
33    }
34}
35
36pub struct Material {
37    corner_radius: f32,
38    blur_radius: f32,
39    child: AnyElement,
40}
41
42impl Element for Material {
43    type RequestLayoutState = ();
44    type PrepaintState = ();
45
46    fn id(&self) -> Option<gpui::ElementId> {
47        None
48    }
49
50    fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
51        None
52    }
53
54    fn request_layout(
55        &mut self,
56        _id: Option<&GlobalElementId>,
57        _inspector_id: Option<&InspectorElementId>,
58        window: &mut Window,
59        cx: &mut App,
60    ) -> (LayoutId, ()) {
61        (self.child.request_layout(window, cx), ())
62    }
63
64    fn prepaint(
65        &mut self,
66        _id: Option<&GlobalElementId>,
67        _inspector_id: Option<&InspectorElementId>,
68        _bounds: Bounds<Pixels>,
69        _request_layout: &mut Self::RequestLayoutState,
70        window: &mut Window,
71        cx: &mut App,
72    ) {
73        self.child.prepaint(window, cx);
74    }
75
76    fn paint(
77        &mut self,
78        _id: Option<&GlobalElementId>,
79        _inspector_id: Option<&InspectorElementId>,
80        bounds: Bounds<Pixels>,
81        _request_layout: &mut Self::RequestLayoutState,
82        _prepaint: &mut Self::PrepaintState,
83        window: &mut Window,
84        cx: &mut App,
85    ) {
86        if Theme::of(cx).is_glass() {
87            window.paint_layer(bounds, |window| {
88                window.paint_backdrop_blur(
89                    bounds,
90                    Corners::all(px(self.corner_radius)),
91                    px(self.blur_radius),
92                );
93                self.child.paint(window, cx);
94            });
95        } else {
96            self.child.paint(window, cx);
97        }
98    }
99}
100
101impl IntoElement for Material {
102    type Element = Self;
103
104    fn into_element(self) -> Self::Element {
105        self
106    }
107}
108
109/// Paint `child` in its own scene layer, giving it a fresh draw order above
110/// everything painted so far in the enclosing layer.
111///
112/// Needed for overlays INSIDE a material card: the card's single layer means
113/// every primitive shares one draw order, and equal orders render grouped by
114/// primitive kind (quads, then icons, then images) — so a close button's
115/// circle painted "after" a thumbnail still shows up UNDER the image. A
116/// nested layer restores the intended stacking.
117pub fn layered(child: impl IntoElement) -> Layered {
118    Layered {
119        child: child.into_any_element(),
120    }
121}
122
123pub struct Layered {
124    child: AnyElement,
125}
126
127impl Element for Layered {
128    type RequestLayoutState = ();
129    type PrepaintState = ();
130
131    fn id(&self) -> Option<gpui::ElementId> {
132        None
133    }
134
135    fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
136        None
137    }
138
139    fn request_layout(
140        &mut self,
141        _id: Option<&GlobalElementId>,
142        _inspector_id: Option<&InspectorElementId>,
143        window: &mut Window,
144        cx: &mut App,
145    ) -> (LayoutId, ()) {
146        (self.child.request_layout(window, cx), ())
147    }
148
149    fn prepaint(
150        &mut self,
151        _id: Option<&GlobalElementId>,
152        _inspector_id: Option<&InspectorElementId>,
153        _bounds: Bounds<Pixels>,
154        _request_layout: &mut Self::RequestLayoutState,
155        window: &mut Window,
156        cx: &mut App,
157    ) {
158        self.child.prepaint(window, cx);
159    }
160
161    fn paint(
162        &mut self,
163        _id: Option<&GlobalElementId>,
164        _inspector_id: Option<&InspectorElementId>,
165        bounds: Bounds<Pixels>,
166        _request_layout: &mut Self::RequestLayoutState,
167        _prepaint: &mut Self::PrepaintState,
168        window: &mut Window,
169        cx: &mut App,
170    ) {
171        window.paint_layer(bounds, |window| self.child.paint(window, cx));
172    }
173}
174
175impl IntoElement for Layered {
176    type Element = Self;
177
178    fn into_element(self) -> Self::Element {
179        self
180    }
181}