1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use crate::{api::prelude::*, proc_macros::*};

/// The `PopupState` handles the open and close behavior of the `Popup` widget.
#[derive(Default, AsAny)]
pub struct PopupState {}

impl State for PopupState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        ctx.widget().set("visibility", Visibility::Hidden)
    }

    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        let visibility = ctx.widget().clone::<Visibility>("visibility");
        let open = *ctx.widget().get::<bool>("open");

        if open && visibility != Visibility::Visible {
            ctx.widget().set("visibility", Visibility::Visible);
        } else if !open && visibility == Visibility::Visible {
            // todo (workaround) should be collapsed but is set to hidden to force the layout to calculate.
            // There is an issue with the ScrollIndicator that does now update on open.
            ctx.widget().set("visibility", Visibility::Hidden);
            {
                // ctx.widget().get_mut::<Rectangle>("bounds").set_width(0.0);
                // ctx.widget().get_mut::<Rectangle>("bounds").set_height(0.0);
            }
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context) {
        if *ctx.widget().get::<Visibility>("visibility") != Visibility::Visible {
            return;
        }

        if let Some(target) = ctx.widget().try_clone::<u32>("target") {
            let target_position: Point = ctx.get_widget(target.into()).clone("position");
            let target_bounds: Rectangle = ctx.get_widget(target.into()).clone("bounds");

            ctx.widget()
                .get_mut::<Rectangle>("bounds")
                .set_x(target_position.x() + target_bounds.x());
            ctx.widget()
                .get_mut::<Rectangle>("bounds")
                .set_y(1.0 + target_position.y() + target_bounds.y() + target_bounds.height());
        }
    }
}

widget!(
    /// The `Popup` is used to display content that floats over the main content.
    Popup<PopupState> : MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the target id to place the popup.
        target: u32,

        /// Sets or shares the value if the popup is open and visible.
        open: bool
    }
);

impl Template for Popup {
    fn template(self, _: Entity, _: &mut BuildContext) -> Self {
        self.name("Popup")
            .style("popup")
            .open(false)
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .on_mouse_down(|_, _| true)
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PopupLayout::new())
    }
}