1#![cfg_attr(target_family = "wasm", no_main)]
2
3use gpui::{
4 App, Context, Corner, Div, Hsla, Stateful, Window, WindowOptions, anchored, deferred, div,
5 prelude::*, px,
6};
7use gpui_platform::application;
8
9/// An example show use deferred to create a floating layers.
10struct HelloWorld {
11 open: bool,
12 secondary_open: bool,
13}
14
15fn button(id: &'static str) -> Stateful<Div> {
16 div()
17 .id(id)
18 .bg(gpui::black())
19 .text_color(gpui::white())
20 .px_3()
21 .py_1()
22}
23
24fn popover() -> Div {
25 div()
26 .flex()
27 .flex_col()
28 .items_center()
29 .justify_center()
30 .shadow_lg()
31 .p_3()
32 .rounded_md()
33 .bg(gpui::white())
34 .text_color(gpui::black())
35 .border_1()
36 .text_sm()
37 .border_color(gpui::black().opacity(0.1))
38}
39
40fn line(color: Hsla) -> Div {
41 div().w(px(480.)).h_2().bg(color.opacity(0.25))
42}
43
44impl HelloWorld {
45 fn render_secondary_popover(
46 &mut self,
47 _window: &mut Window,
48 cx: &mut Context<Self>,
49 ) -> impl IntoElement {
50 button("secondary-btn")
51 .mt_2()
52 .child("Child Popover")
53 .on_click(cx.listener(|this, _, _, cx| {
54 this.secondary_open = true;
55 cx.notify();
56 }))
57 .when(self.secondary_open, |this| {
58 this.child(
59 // GPUI can't support deferred here yet,
60 // it was inside another deferred element.
61 anchored()
62 .anchor(Corner::TopLeft)
63 .snap_to_window_with_margin(px(8.))
64 .child(
65 popover()
66 .child("This is second level Popover")
67 .bg(gpui::white())
68 .border_color(gpui::blue())
69 .on_mouse_down_out(cx.listener(|this, _, _, cx| {
70 this.secondary_open = false;
71 cx.notify();
72 })),
73 ),
74 )
75 })
76 }
77}
78
79impl Render for HelloWorld {
80 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
81 div()
82 .flex()
83 .flex_col()
84 .gap_3()
85 .size_full()
86 .bg(gpui::white())
87 .text_color(gpui::black())
88 .justify_center()
89 .items_center()
90 .child(
91 div()
92 .flex()
93 .flex_row()
94 .gap_4()
95 .child(
96 button("popover0").child("Opened Popover").child(
97 deferred(
98 anchored()
99 .anchor(Corner::TopLeft)
100 .snap_to_window_with_margin(px(8.))
101 .child(popover().w_96().gap_3().child(
102 "This is a default opened Popover, \
103 we can use deferred to render it \
104 in a floating layer.",
105 )),
106 )
107 .priority(0),
108 ),
109 )
110 .child(
111 button("popover1")
112 .child("Open Popover")
113 .on_click(cx.listener(|this, _, _, cx| {
114 this.open = true;
115 cx.notify();
116 }))
117 .when(self.open, |this| {
118 this.child(
119 deferred(
120 anchored()
121 .anchor(Corner::TopLeft)
122 .snap_to_window_with_margin(px(8.))
123 .child(
124 popover()
125 .w_96()
126 .gap_3()
127 .child(
128 "This is first level Popover, \
129 we can use deferred to render it \
130 in a floating layer.\n\
131 Click outside to close.",
132 )
133 .when(!self.secondary_open, |this| {
134 this.on_mouse_down_out(cx.listener(
135 |this, _, _, cx| {
136 this.open = false;
137 cx.notify();
138 },
139 ))
140 })
141 // Here we need render popover after the content
142 // to ensure it will be on top layer.
143 .child(
144 self.render_secondary_popover(window, cx),
145 ),
146 ),
147 )
148 .priority(1),
149 )
150 }),
151 ),
152 )
153 .child(
154 "Here is an example text rendered, \
155 to ensure the Popover will float above this contents.",
156 )
157 .children([
158 line(gpui::red()),
159 line(gpui::yellow()),
160 line(gpui::blue()),
161 line(gpui::green()),
162 ])
163 }
164}
165
166fn run_example() {
167 application().run(|cx: &mut App| {
168 cx.open_window(WindowOptions::default(), |_, cx| {
169 cx.new(|_| HelloWorld {
170 open: false,
171 secondary_open: false,
172 })
173 })
174 .unwrap();
175 cx.activate(true);
176 });
177}
178
179#[cfg(not(target_family = "wasm"))]
180fn main() {
181 run_example();
182}
183
184#[cfg(target_family = "wasm")]
185#[wasm_bindgen::prelude::wasm_bindgen(start)]
186pub fn start() {
187 gpui_platform::web_init();
188 run_example();
189}