anathema_widgets/widget/
mod.rs

1use std::any::Any;
2use std::cmp::Ordering;
3use std::fmt::{self, Debug};
4use std::ops::ControlFlow;
5
6use anathema_geometry::{Pos, Region, Size};
7use anathema_state::StateId;
8use anathema_store::slab::SecondaryMap;
9use anathema_store::tree::{Tree, TreeView};
10use anathema_templates::ComponentBlueprintId;
11use anathema_value_resolver::AttributeStorage;
12
13pub use self::factory::Factory;
14pub use self::style::{Attributes, Style};
15use crate::WidgetContainer;
16use crate::error::Result;
17use crate::layout::{Constraints, LayoutCtx, PositionCtx, PositionFilter};
18use crate::paint::{PaintCtx, PaintFilter, SizePos};
19pub use crate::tree::{Filter, ForEach, LayoutForEach};
20
21mod factory;
22mod style;
23
24pub type WidgetTreeView<'a, 'bp> = TreeView<'a, WidgetContainer<'bp>>;
25pub type WidgetTree<'a> = Tree<WidgetContainer<'a>>;
26pub type LayoutChildren<'a, 'bp> = LayoutForEach<'a, 'bp>;
27pub type PositionChildren<'a, 'bp> = ForEach<'a, 'bp, PositionFilter>;
28pub type PaintChildren<'a, 'bp> = ForEach<'a, 'bp, PaintFilter>;
29pub type WidgetId = anathema_store::slab::Key;
30
31#[derive(Debug)]
32pub struct CompEntry {
33    /// The state owned by this component
34    pub state_id: StateId,
35    /// The components id in the widget tree
36    pub widget_id: WidgetId,
37
38    /// Does the component accept tick events
39    pub accept_ticks: bool,
40
41    component_id: ComponentBlueprintId,
42    path: Box<[u16]>,
43}
44
45impl PartialOrd for CompEntry {
46    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47        Some(self.path.cmp(&other.path))
48    }
49}
50
51impl Ord for CompEntry {
52    fn cmp(&self, other: &Self) -> Ordering {
53        self.path.cmp(&other.path)
54    }
55}
56
57impl Eq for CompEntry {}
58
59impl PartialEq for CompEntry {
60    fn eq(&self, other: &Self) -> bool {
61        self.path.eq(&other.path)
62    }
63}
64
65/// Store a list of components currently in the tree
66pub struct Components {
67    inner: Vec<CompEntry>,
68}
69
70impl Components {
71    pub fn new() -> Self {
72        Self { inner: vec![] }
73    }
74
75    pub fn push(
76        &mut self,
77        path: Box<[u16]>,
78        component_id: ComponentBlueprintId,
79        widget_id: WidgetId,
80        state_id: StateId,
81        accept_ticks: bool,
82    ) {
83        let entry = CompEntry {
84            path,
85            component_id,
86            widget_id,
87            state_id,
88            accept_ticks,
89        };
90
91        self.inner.push(entry)
92    }
93
94    pub fn try_remove(&mut self, widget_id: WidgetId) {
95        self.inner.retain(|entry| entry.widget_id != widget_id);
96    }
97
98    /// Get the component by its index
99    pub fn get(&mut self, index: usize) -> Option<(WidgetId, StateId)> {
100        self.inner.get(index).map(|e| (e.widget_id, e.state_id))
101    }
102
103    /// This is used to send messages to components.
104    /// The `ComponentBlueprintId` is only available to components that were added
105    /// as a singular component, not prototypes
106    pub fn get_by_component_id(&mut self, id: ComponentBlueprintId) -> Option<&CompEntry> {
107        self.inner.iter().find(|e| e.component_id == id)
108    }
109
110    /// Get the component by its widget id
111    pub fn get_by_widget_id(&mut self, id: WidgetId) -> Option<(WidgetId, StateId)> {
112        self.inner
113            .iter()
114            .find(|entry| entry.widget_id == id)
115            .map(|e| (e.widget_id, e.state_id))
116    }
117
118    /// Get widget id and state id for a component that accepts tick events
119    pub fn get_ticking(&self, index: usize) -> Option<(WidgetId, StateId)> {
120        self.inner
121            .get(index)
122            .and_then(|e| e.accept_ticks.then_some((e.widget_id, e.state_id)))
123    }
124
125    pub fn iter(&self) -> impl Iterator<Item = &CompEntry> {
126        self.inner.iter()
127    }
128
129    pub fn len(&self) -> usize {
130        self.inner.len()
131    }
132}
133
134#[derive(Debug)]
135pub struct FloatingWidgets(SecondaryMap<WidgetId, WidgetId>);
136
137impl FloatingWidgets {
138    pub fn empty() -> Self {
139        Self(SecondaryMap::empty())
140    }
141
142    pub fn try_remove(&mut self, key: WidgetId) {
143        self.0.try_remove(key);
144    }
145
146    pub(crate) fn insert(&mut self, widget_id: WidgetId) {
147        self.0.insert(widget_id, widget_id);
148    }
149
150    pub fn iter(&self) -> impl Iterator<Item = &WidgetId> {
151        self.0.iter()
152    }
153}
154
155/// Parent in a component relationship
156#[derive(Debug, Copy, Clone)]
157pub struct Parent(pub WidgetId);
158
159impl From<Parent> for WidgetId {
160    fn from(value: Parent) -> Self {
161        value.0
162    }
163}
164
165impl From<WidgetId> for Parent {
166    fn from(value: WidgetId) -> Self {
167        Self(value)
168    }
169}
170
171/// Component relationships, tracking the parent component of each component
172pub struct ComponentParents(SecondaryMap<ComponentBlueprintId, Parent>);
173
174impl ComponentParents {
175    pub fn empty() -> Self {
176        Self(SecondaryMap::empty())
177    }
178
179    pub fn try_remove(&mut self, key: ComponentBlueprintId) {
180        self.0.try_remove(key);
181    }
182
183    pub fn get_parent(&self, child: ComponentBlueprintId) -> Option<Parent> {
184        self.0.get(child).copied()
185    }
186}
187
188/// Any widget should never be implemented directly
189/// as it's implemented for any type that implements `Widget`
190pub trait AnyWidget {
191    fn to_any_ref(&self) -> &dyn Any;
192    fn to_any_mut(&mut self) -> &mut dyn Any;
193
194    fn any_layout<'bp>(
195        &mut self,
196        children: LayoutForEach<'_, 'bp>,
197        constraints: Constraints,
198        id: WidgetId,
199        ctx: &mut LayoutCtx<'_, 'bp>,
200    ) -> Result<Size>;
201
202    fn any_position<'bp>(
203        &mut self,
204        children: ForEach<'_, 'bp, PositionFilter>,
205        id: WidgetId,
206        attribute_storage: &AttributeStorage<'bp>,
207        ctx: PositionCtx,
208    );
209
210    fn any_paint<'bp>(
211        &mut self,
212        children: ForEach<'_, 'bp, PaintFilter>,
213        id: WidgetId,
214        attribute_storage: &AttributeStorage<'bp>,
215        ctx: PaintCtx<'_, SizePos>,
216    );
217
218    fn any_floats(&self) -> bool;
219
220    fn any_inner_bounds(&self, pos: Pos, size: Size) -> Region;
221
222    fn any_needs_reflow(&mut self) -> bool;
223}
224
225impl<T: 'static + Widget> AnyWidget for T {
226    fn to_any_ref(&self) -> &dyn Any {
227        self
228    }
229
230    fn to_any_mut(&mut self) -> &mut dyn Any {
231        self
232    }
233
234    fn any_layout<'bp>(
235        &mut self,
236        children: LayoutForEach<'_, 'bp>,
237        constraints: Constraints,
238        id: WidgetId,
239        ctx: &mut LayoutCtx<'_, 'bp>,
240    ) -> Result<Size> {
241        self.layout(children, constraints, id, ctx)
242    }
243
244    fn any_position<'bp>(
245        &mut self,
246        children: ForEach<'_, 'bp, PositionFilter>,
247        id: WidgetId,
248        attribute_storage: &AttributeStorage<'bp>,
249        ctx: PositionCtx,
250    ) {
251        self.position(children, id, attribute_storage, ctx)
252    }
253
254    fn any_paint<'bp>(
255        &mut self,
256        children: ForEach<'_, 'bp, PaintFilter>,
257        id: WidgetId,
258        attribute_storage: &AttributeStorage<'bp>,
259        ctx: PaintCtx<'_, SizePos>,
260    ) {
261        self.paint(children, id, attribute_storage, ctx)
262    }
263
264    fn any_inner_bounds(&self, pos: Pos, size: Size) -> Region {
265        self.inner_bounds(pos, size)
266    }
267
268    fn any_floats(&self) -> bool {
269        self.floats()
270    }
271
272    fn any_needs_reflow(&mut self) -> bool {
273        self.needs_reflow()
274    }
275}
276
277impl Debug for dyn AnyWidget {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        write!(f, "<dyn AnyWidget>")
280    }
281}
282
283pub trait Widget {
284    fn layout<'bp>(
285        &mut self,
286        children: LayoutForEach<'_, 'bp>,
287        constraints: Constraints,
288        id: WidgetId,
289        ctx: &mut LayoutCtx<'_, 'bp>,
290    ) -> Result<Size>;
291
292    fn paint<'bp>(
293        &mut self,
294        mut children: ForEach<'_, 'bp, PaintFilter>,
295        _id: WidgetId,
296        attribute_storage: &AttributeStorage<'bp>,
297        mut ctx: PaintCtx<'_, SizePos>,
298    ) {
299        _ = children.each(|child, children| {
300            let ctx = ctx.to_unsized();
301            child.paint(children, ctx, attribute_storage);
302            ControlFlow::Continue(())
303        });
304    }
305
306    fn position<'bp>(
307        &mut self,
308        children: ForEach<'_, 'bp, PositionFilter>,
309        id: WidgetId,
310        attribute_storage: &AttributeStorage<'bp>,
311        ctx: PositionCtx,
312    );
313
314    fn floats(&self) -> bool {
315        false
316    }
317
318    fn inner_bounds(&self, pos: Pos, size: Size) -> Region {
319        Region::from((pos, size))
320    }
321
322    fn needs_reflow(&mut self) -> bool {
323        false
324    }
325}
326
327impl Debug for dyn Widget {
328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329        write!(f, "<dyn Widget>")
330    }
331}