embedded_ui/kit/
linear.rs

1use core::marker::PhantomData;
2
3use alloc::vec::Vec;
4
5use crate::{
6    align::{Alignment, Axis},
7    el::{El, ElId},
8    event::{Capture, CommonEvent, Event, EventResponse, Propagate},
9    layout::{Layout, Viewport},
10    padding::Padding,
11    render::Renderer,
12    size::{Length, Size},
13    state::StateNode,
14    ui::UiCtx,
15    widget::Widget,
16};
17
18pub trait LinearDirection {
19    const AXIS: Axis;
20}
21
22pub struct DirectionColumn;
23impl LinearDirection for DirectionColumn {
24    const AXIS: Axis = Axis::Y;
25}
26
27pub struct DirectionRow;
28impl LinearDirection for DirectionRow {
29    const AXIS: Axis = Axis::X;
30}
31
32pub type Column<'a, Message, R, E, S> = Linear<'a, Message, R, E, S, DirectionColumn>;
33pub type Row<'a, Message, R, E, S> = Linear<'a, Message, R, E, S, DirectionRow>;
34
35pub struct Linear<'a, Message, R: Renderer, E: Event, S, D: LinearDirection> {
36    spacing: u32,
37    size: Size<Length>,
38    padding: Padding,
39    gap: u32,
40    align: Alignment,
41    children: Vec<El<'a, Message, R, E, S>>,
42
43    dir: PhantomData<D>,
44}
45
46impl<'a, Message, R: Renderer, E: Event, S, D: LinearDirection> Linear<'a, Message, R, E, S, D> {
47    pub fn new(children: impl IntoIterator<Item = El<'a, Message, R, E, S>>) -> Self {
48        Self {
49            spacing: 0,
50            size: Size::fill(),
51            padding: Padding::default(),
52            gap: 0,
53            align: Alignment::Start,
54            children: children.into_iter().collect(),
55            dir: PhantomData,
56        }
57    }
58
59    pub fn spacing(mut self, spacing: u32) -> Self {
60        self.spacing = spacing;
61        self
62    }
63
64    pub fn width(mut self, width: impl Into<Length>) -> Self {
65        self.size.width = width.into();
66        self
67    }
68
69    pub fn height(mut self, height: impl Into<Length>) -> Self {
70        self.size.height = height.into();
71        self
72    }
73
74    pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
75        self.padding = padding.into();
76        self
77    }
78
79    pub fn gap(mut self, gap: u32) -> Self {
80        self.gap = gap;
81        self
82    }
83
84    pub fn align(mut self, align: Alignment) -> Self {
85        self.align = align;
86        self
87    }
88
89    pub fn add(mut self, child: impl Into<El<'a, Message, R, E, S>>) -> Self {
90        self.children.push(child.into());
91        self
92    }
93
94    // fn focus_child(&self, child_index: usize, focus_offset: i32) -> FocusResult {
95    //     let new_focus_index = child_index as i32 + focus_offset;
96
97    //     if new_focus_index < 0 {
98    //         return FocusResult::Outside(new_focus_index);
99    //     }
100
101    //     let new_focused_child =
102    //         self.children.iter().filter_map(|child| child.id()).nth(new_focus_index as usize);
103
104    //     if let Some(new_focused_child) = new_focused_child {
105    //         FocusResult::Child(new_focused_child)
106    //     } else {
107    //         FocusResult::Outside(new_focus_index)
108    //     }
109    // }
110}
111
112impl<'a, Message, R: Renderer, E: Event, S, D: LinearDirection> Widget<Message, R, E, S>
113    for Linear<'a, Message, R, E, S, D>
114{
115    fn id(&self) -> Option<crate::el::ElId> {
116        None
117    }
118
119    fn tree_ids(&self) -> Vec<ElId> {
120        self.children.iter().map(|child| child.tree_ids()).flatten().collect()
121    }
122
123    fn size(&self) -> crate::size::Size<Length> {
124        self.size
125    }
126
127    fn state_children(&self) -> Vec<StateNode> {
128        self.children.iter().map(|child| StateNode::new(child)).collect()
129    }
130
131    fn on_event(
132        &mut self,
133        ctx: &mut UiCtx<Message>,
134        event: E,
135        state: &mut crate::state::StateNode,
136    ) -> EventResponse<E> {
137        for (child, child_state) in self.children.iter_mut().zip(state.children.iter_mut()) {
138            match child.on_event(ctx, event.clone(), child_state)? {
139                Propagate::Ignored => {},
140                bubbled @ Propagate::BubbleUp(_, _) => return bubbled.into(),
141            }
142        }
143
144        Propagate::Ignored.into()
145    }
146
147    fn layout(
148        &self,
149        ctx: &mut UiCtx<Message>,
150        state: &mut StateNode,
151        styler: &S,
152        limits: &crate::layout::Limits,
153        viewport: &Viewport,
154    ) -> crate::layout::LayoutNode {
155        Layout::flex(
156            ctx,
157            state,
158            styler,
159            D::AXIS,
160            limits,
161            self.size,
162            crate::layout::Position::Relative,
163            viewport,
164            self.padding,
165            self.gap,
166            self.align,
167            &self.children,
168        )
169    }
170
171    fn draw(
172        &self,
173        ctx: &mut UiCtx<Message>,
174        state: &mut StateNode,
175        renderer: &mut R,
176        styler: &S,
177        layout: crate::layout::Layout,
178    ) {
179        // TODO: Draw only children inside viewport?
180        for ((child, child_state), child_layout) in
181            self.children.iter().zip(state.children.iter_mut()).zip(layout.children())
182        {
183            child.draw(ctx, child_state, renderer, styler, child_layout);
184        }
185    }
186}
187
188impl<'a, Message, R, E, S, D> From<Linear<'a, Message, R, E, S, D>> for El<'a, Message, R, E, S>
189where
190    Message: 'a,
191    R: Renderer + 'a,
192    E: Event + 'a,
193    S: 'a,
194    D: LinearDirection + 'a,
195{
196    fn from(value: Linear<'a, Message, R, E, S, D>) -> Self {
197        Self::new(value)
198    }
199}