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