tuikit/widget/
stack.rs

1use crate::canvas::Canvas;
2use crate::draw::{Draw, DrawResult};
3use crate::event::Event;
4use crate::widget::{Rectangle, Widget};
5
6/// A stack of widgets, will draw the including widgets back to front
7pub struct Stack<'a, Message = ()> {
8    inner: Vec<Box<dyn Widget<Message> + 'a>>,
9}
10
11impl<'a, Message> Stack<'a, Message> {
12    pub fn new() -> Self {
13        Self { inner: vec![] }
14    }
15
16    pub fn top(mut self, widget: impl Widget<Message> + 'a) -> Self {
17        self.inner.push(Box::new(widget));
18        self
19    }
20
21    pub fn bottom(mut self, widget: impl Widget<Message> + 'a) -> Self {
22        self.inner.insert(0, Box::new(widget));
23        self
24    }
25}
26
27impl<'a, Message> Draw for Stack<'a, Message> {
28    fn draw(&self, canvas: &mut dyn Canvas) -> DrawResult<()> {
29        for widget in self.inner.iter() {
30            widget.draw(canvas)?
31        }
32
33        Ok(())
34    }
35    fn draw_mut(&mut self, canvas: &mut dyn Canvas) -> DrawResult<()> {
36        for widget in self.inner.iter_mut() {
37            widget.draw_mut(canvas)?
38        }
39
40        Ok(())
41    }
42}
43
44impl<'a, Message> Widget<Message> for Stack<'a, Message> {
45    fn size_hint(&self) -> (Option<usize>, Option<usize>) {
46        // max of the inner widgets
47        let width = self
48            .inner
49            .iter()
50            .map(|widget| widget.size_hint().0)
51            .max()
52            .unwrap_or(None);
53        let height = self
54            .inner
55            .iter()
56            .map(|widget| widget.size_hint().1)
57            .max()
58            .unwrap_or(None);
59        (width, height)
60    }
61
62    fn on_event(&self, event: Event, rect: Rectangle) -> Vec<Message> {
63        // like javascript's capture, from top to bottom
64        for widget in self.inner.iter().rev() {
65            let message = widget.on_event(event, rect);
66            if !message.is_empty() {
67                return message;
68            }
69        }
70        vec![]
71    }
72
73    fn on_event_mut(&mut self, event: Event, rect: Rectangle) -> Vec<Message> {
74        // like javascript's capture, from top to bottom
75        for widget in self.inner.iter_mut().rev() {
76            let message = widget.on_event_mut(event, rect);
77            if !message.is_empty() {
78                return message;
79            }
80        }
81        vec![]
82    }
83}
84
85#[cfg(test)]
86#[allow(dead_code)]
87mod test {
88    use super::*;
89    use crate::cell::Cell;
90    use std::sync::Mutex;
91
92    struct WinHint {
93        pub width_hint: Option<usize>,
94        pub height_hint: Option<usize>,
95    }
96
97    impl Draw for WinHint {
98        fn draw(&self, _canvas: &mut dyn Canvas) -> DrawResult<()> {
99            unimplemented!()
100        }
101    }
102
103    impl Widget for WinHint {
104        fn size_hint(&self) -> (Option<usize>, Option<usize>) {
105            (self.width_hint, self.height_hint)
106        }
107    }
108
109    #[test]
110    fn size_hint() {
111        let stack = Stack::new().top(WinHint {
112            width_hint: None,
113            height_hint: None,
114        });
115        assert_eq!((None, None), stack.size_hint());
116
117        let stack = Stack::new().top(WinHint {
118            width_hint: Some(1),
119            height_hint: Some(1),
120        });
121        assert_eq!((Some(1), Some(1)), stack.size_hint());
122
123        let stack = Stack::new()
124            .top(WinHint {
125                width_hint: Some(1),
126                height_hint: Some(2),
127            })
128            .top(WinHint {
129                width_hint: Some(2),
130                height_hint: Some(1),
131            });
132        assert_eq!((Some(2), Some(2)), stack.size_hint());
133
134        let stack = Stack::new()
135            .top(WinHint {
136                width_hint: None,
137                height_hint: None,
138            })
139            .top(WinHint {
140                width_hint: Some(2),
141                height_hint: Some(1),
142            });
143        assert_eq!((Some(2), Some(1)), stack.size_hint());
144    }
145
146    #[derive(PartialEq, Debug)]
147    enum Called {
148        No,
149        Mut,
150        Immut,
151    }
152
153    struct Drawn {
154        called: Mutex<Called>,
155    }
156
157    impl Draw for Drawn {
158        fn draw(&self, _canvas: &mut dyn Canvas) -> DrawResult<()> {
159            *self.called.lock().unwrap() = Called::Immut;
160            Ok(())
161        }
162        fn draw_mut(&mut self, _canvas: &mut dyn Canvas) -> DrawResult<()> {
163            *self.called.lock().unwrap() = Called::Mut;
164            Ok(())
165        }
166    }
167
168    impl Widget for Drawn {}
169
170    #[derive(Default)]
171    struct TestCanvas {}
172
173    #[allow(unused_variables)]
174    impl Canvas for TestCanvas {
175        fn size(&self) -> crate::Result<(usize, usize)> {
176            Ok((100, 100))
177        }
178
179        fn clear(&mut self) -> crate::Result<()> {
180            unimplemented!()
181        }
182
183        fn put_cell(&mut self, row: usize, col: usize, cell: Cell) -> crate::Result<usize> {
184            Ok(1)
185        }
186
187        fn set_cursor(&mut self, row: usize, col: usize) -> crate::Result<()> {
188            unimplemented!()
189        }
190
191        fn show_cursor(&mut self, show: bool) -> crate::Result<()> {
192            unimplemented!()
193        }
194    }
195
196    #[test]
197    fn mutable_widget() {
198        let mut canvas = TestCanvas::default();
199
200        let mut mutable = Drawn {
201            called: Mutex::new(Called::No),
202        };
203        {
204            let mut stack = Stack::new().top(&mut mutable);
205            let _ = stack.draw_mut(&mut canvas).unwrap();
206        }
207        assert_eq!(Called::Mut, *mutable.called.lock().unwrap());
208
209        let immutable = Drawn {
210            called: Mutex::new(Called::No),
211        };
212        let stack = Stack::new().top(&immutable);
213        let _ = stack.draw(&mut canvas).unwrap();
214        assert_eq!(Called::Immut, *immutable.called.lock().unwrap());
215    }
216}