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