gpui_kit_material/components/
adaptive.rs1use gpui::{prelude::*, *};
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub enum WindowWidthClass {
15 Compact,
17 Medium,
19 Expanded,
21 Large,
23 ExtraLarge,
25}
26
27impl WindowWidthClass {
28 pub const fn min_width(self) -> f32 {
30 match self {
31 Self::Compact => 0.,
32 Self::Medium => 600.,
33 Self::Expanded => 840.,
34 Self::Large => 1200.,
35 Self::ExtraLarge => 1600.,
36 }
37 }
38
39 pub fn of(width: f32) -> Self {
41 if width >= 1600. {
42 Self::ExtraLarge
43 } else if width >= 1200. {
44 Self::Large
45 } else if width >= 840. {
46 Self::Expanded
47 } else if width >= 600. {
48 Self::Medium
49 } else {
50 Self::Compact
51 }
52 }
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub enum WindowHeightClass {
58 Compact,
60 Medium,
62 Expanded,
64}
65
66impl WindowHeightClass {
67 pub fn of(height: f32) -> Self {
69 if height >= 900. {
70 Self::Expanded
71 } else if height >= 480. {
72 Self::Medium
73 } else {
74 Self::Compact
75 }
76 }
77}
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
81pub struct WindowSizeClass {
82 pub width: WindowWidthClass,
84 pub height: WindowHeightClass,
86}
87
88impl WindowSizeClass {
89 pub fn of(width: f32, height: f32) -> Self {
91 Self {
92 width: WindowWidthClass::of(width),
93 height: WindowHeightClass::of(height),
94 }
95 }
96
97 pub fn of_size(size: Size<Pixels>) -> Self {
99 Self::of(f32::from(size.width), f32::from(size.height))
100 }
101
102 pub fn of_window(window: &Window) -> Self {
104 Self::of_size(window.viewport_size())
105 }
106}
107
108type Builder = Box<dyn FnOnce(Bounds<Pixels>, &mut Window, &mut App) -> AnyElement>;
109
110pub struct SizedContent {
118 id: ElementId,
119 builder: Option<Builder>,
120}
121
122pub fn sized_content(
124 id: impl Into<ElementId>,
125 builder: impl FnOnce(Bounds<Pixels>, &mut Window, &mut App) -> AnyElement + 'static,
126) -> SizedContent {
127 SizedContent {
128 id: id.into(),
129 builder: Some(Box::new(builder)),
130 }
131}
132
133impl IntoElement for SizedContent {
134 type Element = Self;
135 fn into_element(self) -> Self {
136 self
137 }
138}
139
140impl Element for SizedContent {
141 type RequestLayoutState = ();
142 type PrepaintState = Option<AnyElement>;
143
144 fn id(&self) -> Option<ElementId> {
145 Some(self.id.clone())
146 }
147
148 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
149 None
150 }
151
152 fn request_layout(
153 &mut self,
154 _: Option<&GlobalElementId>,
155 _: Option<&InspectorElementId>,
156 window: &mut Window,
157 cx: &mut App,
158 ) -> (LayoutId, ()) {
159 let mut style = Style::default();
160 style.size.width = relative(1.).into();
161 style.size.height = relative(1.).into();
162 (window.request_layout(style, None, cx), ())
163 }
164
165 fn prepaint(
166 &mut self,
167 _: Option<&GlobalElementId>,
168 _: Option<&InspectorElementId>,
169 bounds: Bounds<Pixels>,
170 _: &mut (),
171 window: &mut Window,
172 cx: &mut App,
173 ) -> Option<AnyElement> {
174 let builder = self.builder.take()?;
175 let mut child = builder(bounds, window, cx);
176 child.prepaint_as_root(bounds.origin, bounds.size.into(), window, cx);
177 Some(child)
178 }
179
180 fn paint(
181 &mut self,
182 _: Option<&GlobalElementId>,
183 _: Option<&InspectorElementId>,
184 _: Bounds<Pixels>,
185 _: &mut (),
186 child: &mut Option<AnyElement>,
187 window: &mut Window,
188 cx: &mut App,
189 ) {
190 if let Some(child) = child {
191 child.paint(window, cx);
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use core::prelude::v1::test;
200
201 #[test]
202 fn the_breakpoints_are_the_references_lower_bounds() {
203 use WindowWidthClass::*;
204 for (width, class) in [
205 (0., Compact),
206 (599.9, Compact),
207 (600., Medium),
208 (839.9, Medium),
209 (840., Expanded),
210 (1199.9, Expanded),
211 (1200., Large),
212 (1599.9, Large),
213 (1600., ExtraLarge),
214 ] {
215 assert_eq!(WindowWidthClass::of(width), class, "{width}");
216 assert!(class.min_width() <= width);
217 }
218 assert_eq!(WindowWidthClass::of(f32::NAN), Compact);
219 assert_eq!(WindowHeightClass::of(479.), WindowHeightClass::Compact);
220 assert_eq!(WindowHeightClass::of(480.), WindowHeightClass::Medium);
221 assert_eq!(WindowHeightClass::of(900.), WindowHeightClass::Expanded);
222 }
223
224 struct Harness;
225 impl Render for Harness {
226 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
227 div()
228 .size(px(500.))
229 .child(sized_content("sized", |bounds, _, _| {
230 div()
231 .debug_selector(|| "built".into())
232 .w(bounds.size.width / 2.)
233 .h(bounds.size.height)
234 .into_any_element()
235 }))
236 }
237 }
238
239 #[gpui::test]
240 fn content_is_built_from_the_given_size_in_the_first_frame(cx: &mut gpui::TestAppContext) {
241 let (_, cx) = cx.add_window_view(|_, _| Harness);
242 cx.update(|window, cx| {
243 window.refresh();
244 window.draw(cx).clear(cx);
245 });
246 let built = cx.debug_bounds("built").expect("built in the first frame");
247 assert_eq!(built.size, size(px(250.), px(500.)));
248 }
249}