makepad_widgets/
slides_view.rs

1use crate::{
2    makepad_derive_widget::*,
3    makepad_draw::*,
4    widget::*,
5};
6
7live_design!{
8    link widgets;
9    use link::theme::*;
10    use link::widgets::*;
11    use makepad_draw::shader::std::*;
12    
13    pub SlidesViewBase = {{SlidesView}} {
14    }
15    
16    pub SlidesView = <SlidesViewBase> {
17        anim_speed: 0.9
18    }
19    
20    pub Slide = <RoundedView> {
21        width: Fill, height: Fill,
22        flow: Down, spacing: 10,
23        align: { x: 0.0, y: 0.5 }
24        padding: 50.
25        draw_bg: {
26            color: (THEME_COLOR_D_4),
27            border_radius: (THEME_CONTAINER_CORNER_RADIUS)
28        }
29        title = <H1> {
30            text: "SlideTitle",
31            draw_text: {
32                color: (THEME_COLOR_TEXT)
33            }
34        }
35    }
36    
37    pub SlideChapter = <Slide> {
38        width: Fill, height: Fill,
39        flow: Down,
40        align: {x: 0.0, y: 0.5}
41        spacing: 10,
42        padding: 50,
43        draw_bg: {
44            color: (THEME_COLOR_MAKEPAD),
45            border_radius: (THEME_CONTAINER_CORNER_RADIUS)
46        }
47        title = <H1> {
48            text: "SlideTitle",
49            draw_text: {
50                color: (THEME_COLOR_TEXT)
51            }
52        }
53    }
54    
55    pub SlideBody = <H2> {
56        text: "Body of the slide"
57        draw_text: {
58            color: (THEME_COLOR_TEXT)
59        }
60    }
61}
62
63#[derive(Live, LiveRegisterWidget, WidgetRef, WidgetSet)]
64pub struct SlidesView {
65    #[layout] layout: Layout,
66    #[rust] area: Area,
67    #[walk] walk: Walk,
68    #[rust] children: ComponentMap<LiveId, WidgetRef>,
69    #[rust] draw_order: Vec<LiveId>,
70    #[rust] next_frame: NextFrame,
71    #[rust] current_slide: f64,
72    #[rust] goal_slide: f64,
73    #[live] anim_speed: f64,
74    #[rust] draw_state: DrawStateWrap<DrawState>,
75}
76
77impl WidgetNode for SlidesView{
78    fn walk(&mut self, _cx:&mut Cx) -> Walk{
79        self.walk
80    }
81    
82    fn area(&self)->Area{self.area}
83    
84    fn redraw(&mut self, cx: &mut Cx){
85        self.area.redraw(cx)
86    }
87    
88    fn find_widgets(&self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
89        for child in self.children.values() {
90            child.find_widgets(path, cached, results);
91        }
92    }
93    
94    fn uid_to_widget(&self, uid:WidgetUid)->WidgetRef{
95        for child in self.children.values() {
96            let x = child.uid_to_widget(uid);
97            if !x.is_empty(){return x}
98        }
99        WidgetRef::empty()
100    }
101}   
102
103    
104
105#[derive(Clone)]
106enum DrawState {
107    DrawFirst,
108    DrawSecond,
109}
110
111impl LiveHook for SlidesView {
112    fn before_apply(&mut self, _cx: &mut Cx, apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
113        if let ApplyFrom::UpdateFromDoc {..} = apply.from {
114            //self.children.clear();
115            self.draw_order.clear();
116        }
117    }
118    
119    fn apply_value_instance(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) -> usize {
120        let id = nodes[index].id;
121        match apply.from {
122            ApplyFrom::Animate | ApplyFrom::Over => {
123                if let Some(component) = self.children.get_mut(&nodes[index].id) {
124                    component.apply(cx, apply, index, nodes)
125                }
126                else {
127                    nodes.skip_node(index)
128                }
129            }
130            ApplyFrom::NewFromDoc {..} | ApplyFrom::UpdateFromDoc {..} => {
131                if nodes[index].origin.has_prop_type(LivePropType::Instance) {
132                    self.draw_order.push(id);
133                    return self.children.get_or_insert(cx, id, | cx | {
134                        WidgetRef::new(cx)
135                    }).apply(cx, apply, index, nodes);
136                }
137                else {cx.apply_error_no_matching_field(live_error_origin!(), index, nodes);
138                    nodes.skip_node(index)
139                }
140            }
141            _ => {
142                nodes.skip_node(index)
143            }
144        }
145    }
146    
147    fn after_new_from_doc(&mut self, cx: &mut Cx){
148        self.next_frame(cx);
149    }
150    
151}
152
153#[derive(Clone, Debug, DefaultNone)]
154pub enum SlidesViewAction {
155    Flipped(usize),
156    None,
157}
158
159impl Widget for SlidesView {
160    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
161        // lets handle mousedown, setfocus
162        match event {
163            Event::NextFrame(ne) if ne.set.contains(&self.next_frame) => {
164                self.current_slide = self.current_slide * self.anim_speed + self.goal_slide * (1.0 - self.anim_speed);
165                if (self.current_slide - self.goal_slide).abs()>0.00001 {
166                    self.next_frame(cx);
167                    self.area.redraw(cx);
168                }
169                else {
170                    self.current_slide = self.current_slide.round();
171                }
172                                
173            }
174            _ => ()
175        }
176
177        //let uid = self.widget_uid();
178        // lets grab the two slides we are seeing
179        let current = self.current_slide.floor() as usize;
180        if let Some(current_id) = self.draw_order.get(current) {
181            if let Some(current) = self.children.get(&current_id) {
182                scope.with_id(*current_id, |scope|{
183                    current.handle_event(cx, event, scope);
184                })
185            }
186        }
187        if self.current_slide.fract() >0.0 {
188            let next = current + 1;
189            if let Some(next_id) = self.draw_order.get(next) {
190                if let Some(next) = self.children.get(&next_id) {
191                    scope.with_id(*next_id, |scope|{
192                        next.handle_event(cx, event, scope);
193                    })
194                }
195            }
196        }
197        match event.hits(cx, self.area) {
198            Hit::KeyDown(KeyEvent {key_code: KeyCode::ArrowRight, ..}) => {
199                self.next_slide(cx);
200                let uid = self.widget_uid();
201                cx.widget_action(uid, &scope.path, SlidesViewAction::Flipped(self.goal_slide as usize));
202                
203            }
204            Hit::KeyDown(KeyEvent {key_code: KeyCode::ArrowLeft, ..}) => {
205                self.prev_slide(cx);
206                let uid = self.widget_uid();
207                cx.widget_action(uid, &scope.path, SlidesViewAction::Flipped(self.goal_slide as usize));
208            }
209            Hit::FingerDown(_fe) => {
210                cx.set_key_focus(self.area);
211            },
212            _ => ()
213        }
214                        
215    }
216    
217    fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk: Walk) -> DrawStep {
218        // alright lets draw the child slide
219        // we always maximally show 2 slides
220        if self.draw_state.begin(cx, DrawState::DrawFirst) {
221            cx.begin_turtle(walk, Layout::flow_overlay());
222            let rect = cx.turtle().rect();
223            cx.begin_turtle(Walk {
224                abs_pos: None,
225                margin: Default::default(),
226                width: Size::Fill,
227                height: Size::Fill
228            }, Layout::flow_down().with_scroll(
229                dvec2(rect.size.x * self.current_slide.fract(), 0.0)
230            ));
231            
232        }
233        if let Some(DrawState::DrawFirst) = self.draw_state.get() {
234            let first = self.current_slide.floor() as usize;
235            if let Some(first_id) = self.draw_order.get(first) {
236                if let Some(slide) = self.children.get(&first_id) {
237                    let walk = slide.walk(cx);
238                    scope.with_id(*first_id, |scope|{
239                        slide.draw_walk(cx, scope, walk)
240                    })?;
241                }
242            }
243            cx.end_turtle();
244            let rect = cx.turtle().rect();
245            cx.begin_turtle(Walk {
246                abs_pos: None,
247                margin: Default::default(),
248                width: Size::Fill,
249                height: Size::Fill
250            }, Layout::flow_down().with_scroll(
251                dvec2(-rect.size.x * (1.0-self.current_slide.fract()), 0.0)
252            ));
253            self.draw_state.set(DrawState::DrawSecond);
254        }
255        if let Some(DrawState::DrawSecond) = self.draw_state.get() {
256            if self.current_slide.fract() > 0.0 {
257                let second = self.current_slide.floor() as usize + 1;
258                if let Some(second_id) = self.draw_order.get(second) {
259                    if let Some(slide) = self.children.get(&second_id) {
260                        let walk = slide.walk(cx);
261                        scope.with_id(*second_id, |scope|{
262                            slide.draw_walk(cx, scope, walk) 
263                        })?;
264                    }
265                }
266            }
267        }
268        cx.end_turtle();
269        cx.end_turtle_with_area(&mut self.area);
270        DrawStep::done()
271    }
272}
273
274impl SlidesView {
275    fn next_frame(&mut self, cx: &mut Cx) {
276        self.next_frame = cx.new_next_frame();
277    }
278    
279    pub fn next_slide(&mut self, cx: &mut Cx) {
280        self.goal_slide += 1.0;
281        // lets cap goal pos on the # of slides
282        let max_goal_slide = (self.draw_order.len().max(1) - 1) as f64;
283        if self.goal_slide > max_goal_slide {
284            self.goal_slide = max_goal_slide
285        }
286        self.next_frame(cx);
287    }
288    
289    pub fn prev_slide(&mut self, cx: &mut Cx) {
290        self.goal_slide -= 1.0;
291        if self.goal_slide < 0.0 {
292            self.goal_slide = 0.0;
293        }
294        self.next_frame(cx);
295    }
296    
297    pub fn redraw(&mut self, cx: &mut Cx) {
298        self.area.redraw(cx);
299    }
300}
301
302impl SlidesViewRef {
303    pub fn flipped(&self, actions:&Actions)->Option<usize>{
304        if let SlidesViewAction::Flipped(m) = actions.find_widget_action(self.widget_uid()).cast_ref() {
305            Some(*m)
306        } else{
307            None
308        }
309    }                   
310    
311            
312    pub fn set_current_slide(&self, cx:&mut Cx, slide:usize){
313        if let Some(mut inner) = self.borrow_mut() {
314            inner.goal_slide = slide as f64;
315            inner.current_slide = slide as f64;
316            inner.redraw(cx);
317        }
318    }
319        
320    pub fn set_goal_slide(&self, cx:&mut Cx, slide:usize){
321        if let Some(mut inner) = self.borrow_mut() {
322            inner.goal_slide = slide as f64;
323            inner.next_frame(cx);
324        }
325    }
326    pub fn get_slide(&self)->usize{
327        if let Some(inner) = self.borrow() {
328            return inner.current_slide as usize
329        }
330        0
331    }
332    pub fn next_slide(&self, cx: &mut Cx) {
333        if let Some(mut inner) = self.borrow_mut() {
334            inner.next_slide(cx);
335        }
336    }
337    pub fn prev_slide(&self, cx: &mut Cx) {
338        if let Some(mut inner) = self.borrow_mut() {
339            inner.prev_slide(cx);
340        }
341    }
342}
343
344impl SlidesViewSet {
345    pub fn next_slide(&self, cx: &mut Cx) {
346        for item in self.iter() {
347            item.next_slide(cx);
348        }
349    }
350    pub fn prev_slide(&self, cx: &mut Cx) {
351        for item in self.iter() {
352            item.prev_slide(cx);
353        }
354    }
355}