1use gpui::{
2 App, Bounds, Context, KeyBinding, PromptButton, PromptLevel, Window, WindowBounds, WindowKind,
3 WindowOptions, actions, div, prelude::*, px, rgb, size,
4};
5use gpui_platform::application;
6
7struct SubWindow {
8 custom_titlebar: bool,
9 is_dialog: bool,
10}
11
12fn button(text: &str, on_click: impl Fn(&mut Window, &mut App) + 'static) -> impl IntoElement {
13 div()
14 .id(text.to_string())
15 .flex_none()
16 .px_2()
17 .bg(rgb(0xf7f7f7))
18 .active(|this| this.opacity(0.85))
19 .border_1()
20 .border_color(rgb(0xe0e0e0))
21 .rounded_sm()
22 .cursor_pointer()
23 .child(text.to_string())
24 .on_click(move |_, window, cx| on_click(window, cx))
25}
26
27impl Render for SubWindow {
28 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29 let window_bounds =
30 WindowBounds::Windowed(Bounds::centered(None, size(px(250.0), px(200.0)), cx));
31
32 div()
33 .flex()
34 .flex_col()
35 .bg(rgb(0xffffff))
36 .size_full()
37 .gap_2()
38 .when(self.custom_titlebar, |cx| {
39 cx.child(
40 div()
41 .flex()
42 .h(px(32.))
43 .px_4()
44 .bg(gpui::blue())
45 .text_color(gpui::white())
46 .w_full()
47 .child(
48 div()
49 .flex()
50 .items_center()
51 .justify_center()
52 .size_full()
53 .child("Custom Titlebar"),
54 ),
55 )
56 })
57 .child(
58 div()
59 .p_8()
60 .flex()
61 .flex_col()
62 .gap_2()
63 .child("SubWindow")
64 .when(self.is_dialog, |div| {
65 div.child(button("Open Nested Dialog", move |_, cx| {
66 cx.open_window(
67 WindowOptions {
68 window_bounds: Some(window_bounds),
69 kind: WindowKind::Dialog,
70 ..Default::default()
71 },
72 |_, cx| {
73 cx.new(|_| SubWindow {
74 custom_titlebar: false,
75 is_dialog: true,
76 })
77 },
78 )
79 .unwrap();
80 }))
81 })
82 .child(button("Close", |window, _| {
83 window.remove_window();
84 })),
85 )
86 }
87}
88
89struct WindowDemo {}
90
91impl Render for WindowDemo {
92 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
93 let window_bounds =
94 WindowBounds::Windowed(Bounds::centered(None, size(px(300.0), px(300.0)), cx));
95
96 div()
97 .p_4()
98 .flex()
99 .flex_wrap()
100 .bg(rgb(0xffffff))
101 .size_full()
102 .justify_center()
103 .content_center()
104 .gap_2()
105 .child(button("Normal", move |_, cx| {
106 cx.open_window(
107 WindowOptions {
108 window_bounds: Some(window_bounds),
109 ..Default::default()
110 },
111 |_, cx| {
112 cx.new(|_| SubWindow {
113 custom_titlebar: false,
114 is_dialog: false,
115 })
116 },
117 )
118 .unwrap();
119 }))
120 .child(button("Popup", move |_, cx| {
121 cx.open_window(
122 WindowOptions {
123 window_bounds: Some(window_bounds),
124 kind: WindowKind::PopUp,
125 ..Default::default()
126 },
127 |_, cx| {
128 cx.new(|_| SubWindow {
129 custom_titlebar: false,
130 is_dialog: false,
131 })
132 },
133 )
134 .unwrap();
135 }))
136 .child(button("Floating", move |_, cx| {
137 cx.open_window(
138 WindowOptions {
139 window_bounds: Some(window_bounds),
140 kind: WindowKind::Floating,
141 ..Default::default()
142 },
143 |_, cx| {
144 cx.new(|_| SubWindow {
145 custom_titlebar: false,
146 is_dialog: false,
147 })
148 },
149 )
150 .unwrap();
151 }))
152 .child(button("Dialog", move |_, cx| {
153 cx.open_window(
154 WindowOptions {
155 window_bounds: Some(window_bounds),
156 kind: WindowKind::Dialog,
157 ..Default::default()
158 },
159 |_, cx| {
160 cx.new(|_| SubWindow {
161 custom_titlebar: false,
162 is_dialog: true,
163 })
164 },
165 )
166 .unwrap();
167 }))
168 .child(button("Custom Titlebar", move |_, cx| {
169 cx.open_window(
170 WindowOptions {
171 titlebar: None,
172 window_bounds: Some(window_bounds),
173 ..Default::default()
174 },
175 |_, cx| {
176 cx.new(|_| SubWindow {
177 custom_titlebar: true,
178 is_dialog: false,
179 })
180 },
181 )
182 .unwrap();
183 }))
184 .child(button("Invisible", move |_, cx| {
185 cx.open_window(
186 WindowOptions {
187 show: false,
188 window_bounds: Some(window_bounds),
189 ..Default::default()
190 },
191 |_, cx| {
192 cx.new(|_| SubWindow {
193 custom_titlebar: false,
194 is_dialog: false,
195 })
196 },
197 )
198 .unwrap();
199 }))
200 .child(button("Unmovable", move |_, cx| {
201 cx.open_window(
202 WindowOptions {
203 is_movable: false,
204 titlebar: None,
205 window_bounds: Some(window_bounds),
206 ..Default::default()
207 },
208 |_, cx| {
209 cx.new(|_| SubWindow {
210 custom_titlebar: false,
211 is_dialog: false,
212 })
213 },
214 )
215 .unwrap();
216 }))
217 .child(button("Unresizable", move |_, cx| {
218 cx.open_window(
219 WindowOptions {
220 is_resizable: false,
221 window_bounds: Some(window_bounds),
222 ..Default::default()
223 },
224 |_, cx| {
225 cx.new(|_| SubWindow {
226 custom_titlebar: false,
227 is_dialog: false,
228 })
229 },
230 )
231 .unwrap();
232 }))
233 .child(button("Unminimizable", move |_, cx| {
234 cx.open_window(
235 WindowOptions {
236 is_minimizable: false,
237 window_bounds: Some(window_bounds),
238 ..Default::default()
239 },
240 |_, cx| {
241 cx.new(|_| SubWindow {
242 custom_titlebar: false,
243 is_dialog: false,
244 })
245 },
246 )
247 .unwrap();
248 }))
249 .child(button("Hide Application", |window, cx| {
250 cx.hide();
251
252 // Restore the application after 3 seconds
253 window
254 .spawn(cx, async move |cx| {
255 cx.background_executor()
256 .timer(std::time::Duration::from_secs(3))
257 .await;
258 cx.update(|_, cx| {
259 cx.activate(false);
260 })
261 })
262 .detach();
263 }))
264 .child(button("Resize", |window, _| {
265 let content_size = window.bounds().size;
266 window.resize(size(content_size.height, content_size.width));
267 }))
268 .child(button("Prompt", |window, cx| {
269 let answer = window.prompt(
270 PromptLevel::Info,
271 "Are you sure?",
272 None,
273 &["Ok", "Cancel"],
274 cx,
275 );
276
277 cx.spawn(async move |_| {
278 if answer.await.unwrap() == 0 {
279 println!("You have clicked Ok");
280 } else {
281 println!("You have clicked Cancel");
282 }
283 })
284 .detach();
285 }))
286 .child(button("Prompt (non-English)", |window, cx| {
287 let answer = window.prompt(
288 PromptLevel::Info,
289 "Are you sure?",
290 None,
291 &[PromptButton::ok("确定"), PromptButton::cancel("取消")],
292 cx,
293 );
294
295 cx.spawn(async move |_| {
296 if answer.await.unwrap() == 0 {
297 println!("You have clicked Ok");
298 } else {
299 println!("You have clicked Cancel");
300 }
301 })
302 .detach();
303 }))
304 }
305}
306
307actions!(window, [Quit]);
308
309fn main() {
310 application().run(|cx: &mut App| {
311 let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx);
312
313 cx.open_window(
314 WindowOptions {
315 window_bounds: Some(WindowBounds::Windowed(bounds)),
316 ..Default::default()
317 },
318 |window, cx| {
319 cx.new(|cx| {
320 cx.observe_window_bounds(window, move |_, window, _| {
321 println!("Window bounds changed: {:?}", window.bounds());
322 })
323 .detach();
324
325 WindowDemo {}
326 })
327 },
328 )
329 .unwrap();
330
331 cx.activate(true);
332 cx.on_action(|_: &Quit, cx| cx.quit());
333 cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
334 });
335}