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                    // 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}