embedded_ui/
ui.rs

1use alloc::{collections::VecDeque, vec::Vec};
2use embedded_graphics::pixelcolor::BinaryColor;
3
4use crate::{
5    el::{El, ElId},
6    event::{Controls, Event, EventStub, NullControls, Propagate},
7    layout::{Layout, LayoutNode, Limits, Viewport},
8    render::Renderer,
9    size::Size,
10    state::StateNode,
11    style::{monochrome::Monochrome, Styler},
12    widget::Widget,
13};
14
15/// Global UI states collection
16pub struct UiCtx<Message> {
17    message_pool: VecDeque<Message>,
18    focused: Option<ElId>,
19}
20
21impl<Message> UiCtx<Message> {
22    pub fn new() -> Self {
23        Self { message_pool: VecDeque::new(), focused: None }
24    }
25
26    pub fn focus(&mut self, id: ElId) {
27        self.focused = Some(id)
28    }
29
30    pub fn is_focused<R: Renderer, E: Event, S>(
31        &self,
32        widget: &impl Widget<Message, R, E, S>,
33    ) -> bool {
34        match (self.focused, widget.id()) {
35            (Some(focus), Some(id)) if focus == id => true,
36            _ => false,
37        }
38    }
39
40    pub fn no_focus(&self) -> bool {
41        self.focused.is_none()
42    }
43
44    pub fn publish(&mut self, message: Message) {
45        self.message_pool.push_back(message)
46    }
47}
48
49pub struct UI<'a, Message, R: Renderer, E: Event, S: Styler<R::Color>> {
50    root: El<'a, Message, R, E, S>,
51    root_node: LayoutNode,
52    // viewport_size: Size,
53    root_state: StateNode,
54    styler: S,
55    // events: Vec<E>,
56    ctx: UiCtx<Message>,
57}
58
59impl<'a, Message, R: Renderer, E: Event, S: Styler<R::Color>> UI<'a, Message, R, E, S> {
60    pub fn new(root: impl Widget<Message, R, E, S> + 'a, viewport_size: Size) -> Self {
61        let mut ctx = UiCtx::new();
62
63        let root = El::new(root);
64        let mut root_state = StateNode::new(&root);
65
66        let root_node = root.layout(
67            &mut ctx,
68            &mut root_state,
69            &Default::default(),
70            &Limits::only_max(viewport_size),
71            &Viewport { size: viewport_size },
72        );
73
74        Self {
75            root,
76            root_node,
77            // viewport_size,
78            root_state,
79            // events: Vec::new(),
80            styler: Default::default(),
81            ctx,
82        }
83    }
84
85    // pub fn feed_events(&mut self, events: impl Iterator<Item = E>) {
86    //     self.events.extend(events)
87    // }
88
89    pub fn deque_message(&mut self) -> Option<Message> {
90        self.ctx.message_pool.pop_back()
91    }
92
93    pub fn tick(&mut self, events: impl Iterator<Item = E>) {
94        for event in events {
95            if let core::ops::ControlFlow::Continue(propagate) =
96                self.root.on_event(&mut self.ctx, event.clone(), &mut self.root_state)
97            {
98                match propagate {
99                    Propagate::BubbleUp(bubble_origin, bubbled) => {
100                        // debug!("Capture Bubble up event {bubbled:?} from {bubble_origin:?}");
101                        if let Some(common) = bubbled.as_common() {
102                            match common {
103                                crate::event::CommonEvent::FocusMove(offset) => {
104                                    let tree = self.root.tree_ids();
105                                    let current = tree
106                                        .iter()
107                                        .position(|&id| id == bubble_origin)
108                                        .unwrap_or(0)
109                                        as i32;
110                                    let next_focus = current
111                                        .saturating_add(offset)
112                                        .clamp(0, tree.len().saturating_sub(1) as i32);
113                                    self.ctx.focus(tree[next_focus as usize]);
114                                    // return Capture::Captured.into();
115                                    return;
116                                },
117                                _ => {},
118                            }
119                        }
120                    },
121                    Propagate::Ignored => {
122                        // debug!("Ignored event {event:?}");
123                    },
124                }
125            } else {
126                // debug!("Some element captured event {event:?}");
127            }
128            // TODO: Debug log "ignored event"
129            // Propagate::Ignored.into()
130        }
131    }
132
133    pub fn auto_focus(&mut self) {
134        if let Some(first_el) = self.root.tree_ids().first().copied() {
135            self.ctx.focus(first_el)
136        }
137    }
138
139    pub fn focus(&mut self, id: ElId) {
140        self.ctx.focus(id)
141    }
142
143    pub fn draw(&mut self, renderer: &mut R) {
144        // FIXME: Performance?
145        // TODO: Maybe should clear only root bounds
146        renderer.clear();
147
148        self.root.draw(
149            &mut self.ctx,
150            &mut self.root_state,
151            renderer,
152            &self.styler,
153            Layout::new(&self.root_node),
154        );
155    }
156}
157
158/// Does not have events
159impl<'a, Message, R: Renderer, S: Styler<R::Color>> UI<'a, Message, R, EventStub, S> {
160    pub fn no_events(self) -> Self {
161        self
162    }
163}
164
165/// Does not allow messages
166impl<'a, R: Renderer, E: Event, S: Styler<R::Color>> UI<'a, (), R, E, S> {
167    pub fn static_ui(self) -> Self {
168        self
169    }
170}
171
172impl<'a, Message, R, E> UI<'a, Message, R, E, Monochrome>
173where
174    R: Renderer<Color = BinaryColor>,
175    E: Event,
176{
177    pub fn monochrome(self) -> Self {
178        self
179    }
180}