makepad_widgets/
toggle_panel.rs

1use crate::{button::*, makepad_derive_widget::*, makepad_draw::*, view::*, widget::*};
2
3live_design!{
4    link widgets;
5    use link::theme::*;
6    use makepad_draw::shader::std::*;
7    
8    use crate::view_ui::CachedView;
9    use crate::view_ui::View;
10    use crate::button::Button; 
11    
12    pub TogglePanelBase = {{TogglePanel}} {}
13    pub TOGGLE_PANEL_CLOSE_ICON = dep("crate://self/resources/icons/close_left_panel.svg")
14    pub TOGGLE_PANEL_OPEN_ICON = dep("crate://self/resources/icons/open_left_panel.svg")
15    pub TogglePanel = <TogglePanelBase> {
16        flow: Overlay,
17        width: 300,
18        height: Fill,
19        
20        open_content = <CachedView> {
21            width: Fill
22            height: Fill
23            
24            draw_bg: {
25                instance opacity: 1.0
26                    
27                fn pixel(self) -> vec4 {
28                    return #f00;
29                    let color = sample2d(self.image, self.pos * self.scale + self.shift) + vec4(self.marked, 0.0, 0.0, 0.0);
30                    return Pal::premul(vec4(color.xyz, color.w * self.opacity))
31                }
32            }
33        }
34        
35        persistent_content = <View> {
36            height: Fit
37            width: Fill
38            default = <View> {
39                height: Fit,
40                width: Fill,
41                padding: {top: 58, left: 15, right: 15}
42                spacing: 10,
43                
44                before = <View> {
45                    height: Fit,
46                    width: Fit,
47                    spacing: 10,
48                }
49                
50                close = <Button> {
51                    draw_icon: {
52                        svg_file: (TOGGLE_PANEL_CLOSE_ICON),
53                    }
54                }
55                
56                open = <Button> {
57                    visible: false,
58                    draw_icon: {
59                        svg_file: (TOGGLE_PANEL_OPEN_ICON),
60                    }
61                }
62                
63                after = <View> {
64                    height: Fit,
65                    width: Fit,
66                    spacing: 10,
67                }
68            }
69        }
70        
71        animator: {
72            panel = {
73                default: open,
74                open = {
75                    redraw: true,
76                    from: {all: Forward {duration: 0.3}}
77                    ease: ExpDecay {d1: 0.80, d2: 0.97}
78                    apply: {animator_panel_progress: 1.0, open_content = { draw_bg: {opacity: 1.0} }}
79                }
80                close = {
81                    redraw: true,
82                    from: {all: Forward {duration: 0.3}}
83                    ease: ExpDecay {d1: 0.80, d2: 0.97}
84                    apply: {animator_panel_progress: 0.0, open_content = { draw_bg: {opacity: 0.0} }}
85                }
86            }
87        }
88    }
89}
90
91/// A toggable side panel that can be expanded and collapsed to a maximum and minimum size.
92#[derive(Live, Widget)]
93pub struct TogglePanel {
94    #[deref]
95    view: View,
96
97    /// Internal use only. Used by the animator to track the progress of the panel
98    /// animation to overcome some limitations (for ex: `apply_over` doesn't work well
99    /// over the animator).
100    #[live]
101    animator_panel_progress: f32,
102
103    /// The size of the panel when it is fully open.
104    #[live(300.0)]
105    open_size: f32,
106
107    /// The size of the panel when it is fully closed.
108    #[live(110.0)]
109    close_size: f32,
110
111    #[animator]
112    animator: Animator,
113}
114
115impl LiveHook for TogglePanel {
116    fn after_new_from_doc(&mut self, cx: &mut Cx) {
117        if self.is_open(cx) {
118            self.animator_panel_progress = 1.0;
119        } else {
120            self.animator_panel_progress = 0.0;
121        };
122    }
123}
124
125impl Widget for TogglePanel {
126    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
127        if self.animator_handle_event(cx, event).must_redraw() {
128            self.redraw(cx)
129        };
130
131        if let Event::Actions(actions) = event {
132            let open = self.button(id!(open));
133            let close = self.button(id!(close));
134
135            if open.clicked(actions) {
136                open.set_visible(cx, false);
137                close.set_visible(cx, true);
138                self.set_open(cx, true);
139            }
140
141            if close.clicked(actions) {
142                close.set_visible(cx, false);
143                open.set_visible(cx, true);
144                self.set_open(cx, false);
145            }
146        }
147
148        self.view.handle_event(cx, event, scope);
149    }
150
151    fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
152        let mut walk = walk;
153
154        let size_range = self.open_size - self.close_size;
155        let size = self.close_size + size_range * self.animator_panel_progress;
156        walk.width = Size::Fixed(size.into());
157
158        self.view.draw_walk(cx, scope, walk)
159    }
160}
161
162impl TogglePanel {
163    /// Returns whether the panel is currently open.
164    pub fn is_open(&self, cx: &Cx) -> bool {
165        self.animator_in_state(cx, id!(panel.open))
166    }
167
168    /// Sets whether the panel is open. Causes the panel to animate to the new state.
169    pub fn set_open(&mut self, cx: &mut Cx, open: bool) {
170        if open {
171            self.animator_play(cx, id!(panel.open));
172        } else {
173            self.animator_play(cx, id!(panel.close));
174        }
175    }
176}
177
178impl TogglePanelRef {
179    /// Calls `is_open` on it's inner.
180    pub fn is_open(&self, cx: &Cx) -> bool {
181        if let Some(inner) = self.borrow() {
182            inner.is_open(cx)
183        } else {
184            false
185        }
186    }
187
188    /// Calls `set_open` on it's inner.
189    pub fn set_open(&self, cx: &mut Cx, open: bool) {
190        if let Some(mut inner) = self.borrow_mut() {
191            inner.set_open(cx, open);
192        }
193    }
194}