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
15pub 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 root_state: StateNode,
54 styler: S,
55 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 root_state,
79 styler: Default::default(),
81 ctx,
82 }
83 }
84
85 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 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;
116 },
117 _ => {},
118 }
119 }
120 },
121 Propagate::Ignored => {
122 },
124 }
125 } else {
126 }
128 }
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 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
158impl<'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
165impl<'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}