titik/
widget.rs

1use crate::Event;
2use crate::{buffer::Buffer, Cmd};
3pub use button::Button;
4pub use checkbox::Checkbox;
5use expanse::result::Layout;
6use expanse::{
7    node::{Node, Stretch},
8    style::Style,
9};
10pub use flex_box::FlexBox;
11pub use group_box::GroupBox;
12pub use image_control::Image;
13pub use link::Link;
14pub use list_box::ListBox;
15pub use radio::Radio;
16pub use slider::Slider;
17use std::{any::Any, fmt};
18pub use tab_box::TabBox;
19pub use text_area::TextArea;
20pub use text_input::TextInput;
21pub use text_label::TextLabel;
22
23mod button;
24mod checkbox;
25mod flex_box;
26mod group_box;
27mod image_control;
28mod link;
29mod list_box;
30mod radio;
31mod slider;
32mod tab_box;
33mod text_area;
34mod text_input;
35mod text_label;
36
37/// All widgets must implement the Widget trait
38pub trait Widget<MSG>
39where
40    Self: fmt::Debug,
41{
42    /// return the style of this widget
43    fn style(&self) -> Style;
44
45    /// return the layout of thiswidget
46    fn layout(&self) -> Option<&Layout>;
47
48    /// return the offset of the parent,
49    /// this before any of it's children to be drawn
50    fn get_offset(&self) -> (f32, f32) {
51        (0.0, 0.0)
52    }
53
54    fn set_layout(&mut self, layout: Layout);
55
56    /// add a child to this widget
57    /// returns true if it can accept a child, false otherwise
58    fn add_child(&mut self, _child: Box<dyn Widget<MSG>>) -> bool {
59        false
60    }
61
62    /// get a referemce tp the children of this widget
63    fn children(&self) -> Option<&[Box<dyn Widget<MSG>>]> {
64        None
65    }
66
67    /// get a mutable reference to the children of this widget
68    fn children_mut(&mut self) -> Option<&mut [Box<dyn Widget<MSG>>]> {
69        None
70    }
71
72    /// return a mutable reference to a child at index location
73    fn child_mut(
74        &mut self,
75        _index: usize,
76    ) -> Option<&mut Box<dyn Widget<MSG>>> {
77        None
78    }
79
80    /// this is called in the render loop in the renderer where the widget
81    /// writes into the buffer. The result will then be written into the
82    /// stdout terminal.
83    fn draw(&self, but: &mut Buffer) -> Vec<Cmd>;
84
85    /// build a node with styles from this widget and its children
86    /// The Layout tree is then calculated see `layout::compute_layout`
87    fn style_node(&self, stretch: &mut Stretch) -> Option<Node> {
88        let children_styles = if let Some(children) = self.children() {
89            children
90                .iter()
91                .filter_map(|c| c.style_node(stretch))
92                .collect()
93        } else {
94            vec![]
95        };
96        stretch.new_node(self.style(), &children_styles).ok()
97    }
98
99    /// set the widget as focused
100    fn set_focused(&mut self, _focused: bool) {}
101
102    /// get an Any reference
103    fn as_any(&self) -> &dyn Any;
104
105    /// get an Any mutable reference for casting purposed
106    fn as_any_mut(&mut self) -> &mut dyn Any;
107
108    /// set the size of the widget
109    fn set_size(&mut self, width: Option<f32>, height: Option<f32>);
110
111    /// get a mutable reference of this widget
112    fn as_mut(&mut self) -> Option<&mut Self>
113    where
114        Self: Sized + 'static,
115    {
116        self.as_any_mut().downcast_mut::<Self>()
117    }
118
119    /// this process the event and all callbacks attached to the widgets will be dispatched.
120    fn process_event(&mut self, _event: Event) -> Vec<MSG> {
121        vec![]
122    }
123
124    ///  take the children at this index location
125    fn take_child(&mut self, _index: usize) -> Option<Box<dyn Widget<MSG>>> {
126        None
127    }
128
129    /// set the id of this widget
130    fn set_id(&mut self, id: &str);
131
132    /// get the id of this widget
133    fn get_id(&self) -> &Option<String>;
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139    use crate::*;
140    use expanse::{geometry::*, number::Number};
141    use std::boxed;
142
143    #[test]
144    fn layout() {
145        let mut control = FlexBox::<()>::new();
146        control.horizontal();
147        let mut btn = Button::<()>::new("Hello");
148        btn.set_size(Some(30.0), Some(34.0));
149
150        control.add_child(boxed::Box::new(btn));
151
152        let mut btn = Button::<()>::new("world");
153        btn.set_size(Some(20.0), Some(10.0));
154        control.add_child(boxed::Box::new(btn));
155
156        crate::layout::compute_node_layout(
157            &mut control,
158            Size {
159                width: Number::Defined(100.0),
160                height: Number::Defined(100.0),
161            },
162        );
163
164        let layout1 = control.children().expect("must have children")[1]
165            .layout()
166            .expect("must have layout");
167        assert_eq!(
168            layout1.size,
169            Size {
170                width: 20.0,
171                height: 10.0
172            }
173        );
174
175        assert_eq!(layout1.location, Point { x: 30.0, y: 0.0 });
176    }
177
178    #[test]
179    fn layout2() {
180        let mut control = FlexBox::<()>::new();
181        control.vertical();
182
183        let mut btn1 = Button::<()>::new("Hello");
184        btn1.set_size(Some(100.0), Some(20.0));
185
186        control.add_child(boxed::Box::new(btn1));
187
188        let mut btn2 = Button::<()>::new("world");
189        btn2.set_size(Some(20.0), Some(10.0));
190
191        let mut btn3 = Button::<()>::new("world");
192        btn3.set_size(Some(20.0), Some(10.0));
193
194        let mut hrow = FlexBox::<()>::new();
195        hrow.horizontal();
196
197        hrow.add_child(boxed::Box::new(btn2));
198        hrow.add_child(boxed::Box::new(btn3));
199
200        control.add_child(boxed::Box::new(hrow));
201
202        crate::layout::compute_node_layout(
203            &mut control,
204            Size {
205                width: Number::Defined(100.0),
206                height: Number::Defined(100.0),
207            },
208        );
209
210        let layout_btn2 = control.children().expect("must have children")[1]
211            .children()
212            .expect("must have grand children")[1]
213            .layout()
214            .expect("must have a layout");
215        assert_eq!(layout_btn2.location, Point { x: 20.0, y: 17.0 });
216    }
217}