popover.rs

  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                    // Now GPUI supports nested deferred!
 60                    deferred(
 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 with nested deferred!")
 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                    .priority(2),
 76                )
 77            })
 78    }
 79}
 80
 81impl Render for HelloWorld {
 82    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 83        div()
 84            .flex()
 85            .flex_col()
 86            .gap_3()
 87            .size_full()
 88            .bg(gpui::white())
 89            .text_color(gpui::black())
 90            .justify_center()
 91            .items_center()
 92            .child(
 93                div()
 94                    .flex()
 95                    .flex_row()
 96                    .gap_4()
 97                    .child(
 98                        button("popover0").child("Opened Popover").child(
 99                            deferred(
100                                anchored()
101                                    .anchor(Corner::TopLeft)
102                                    .snap_to_window_with_margin(px(8.))
103                                    .child(popover().w_96().gap_3().child(
104                                        "This is a default opened Popover, \
105                                        we can use deferred to render it \
106                                        in a floating layer.",
107                                    )),
108                            )
109                            .priority(0),
110                        ),
111                    )
112                    .child(
113                        button("popover1")
114                            .child("Open Popover")
115                            .on_click(cx.listener(|this, _, _, cx| {
116                                this.open = true;
117                                cx.notify();
118                            }))
119                            .when(self.open, |this| {
120                                this.child(
121                                    deferred(
122                                        anchored()
123                                            .anchor(Corner::TopLeft)
124                                            .snap_to_window_with_margin(px(8.))
125                                            .child(
126                                                popover()
127                                                    .w_96()
128                                                    .gap_3()
129                                                    .child(
130                                                        "This is first level Popover, \
131                                                   we can use deferred to render it \
132                                                   in a floating layer.\n\
133                                                   Click outside to close.",
134                                                    )
135                                                    .when(!self.secondary_open, |this| {
136                                                        this.on_mouse_down_out(cx.listener(
137                                                            |this, _, _, cx| {
138                                                                this.open = false;
139                                                                cx.notify();
140                                                            },
141                                                        ))
142                                                    })
143                                                    // Here we need render popover after the content
144                                                    // to ensure it will be on top layer.
145                                                    .child(
146                                                        self.render_secondary_popover(window, cx),
147                                                    ),
148                                            ),
149                                    )
150                                    .priority(1),
151                                )
152                            }),
153                    ),
154            )
155            .child(
156                "Here is an example text rendered, \
157                to ensure the Popover will float above this contents.",
158            )
159            .children([
160                line(gpui::red()),
161                line(gpui::yellow()),
162                line(gpui::blue()),
163                line(gpui::green()),
164            ])
165    }
166}
167
168fn run_example() {
169    application().run(|cx: &mut App| {
170        cx.open_window(WindowOptions::default(), |_, cx| {
171            cx.new(|_| HelloWorld {
172                open: false,
173                secondary_open: false,
174            })
175        })
176        .unwrap();
177        cx.activate(true);
178    });
179}
180
181#[cfg(not(target_family = "wasm"))]
182fn main() {
183    run_example();
184}
185
186#[cfg(target_family = "wasm")]
187#[wasm_bindgen::prelude::wasm_bindgen(start)]
188pub fn start() {
189    gpui_platform::web_init();
190    run_example();
191}