popover.rs

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