Skip to main content

anathema_widgets/layout/
mod.rs

1use anathema_geometry::{Pos, Region, Size};
2use anathema_state::{State, StateId, States};
3use anathema_store::tree::TreeView;
4use anathema_templates::{ComponentBlueprintId, Variables};
5use anathema_value_resolver::{AttributeStorage, Attributes, FunctionTable};
6use display::DISPLAY;
7
8pub use self::constraints::Constraints;
9pub use self::display::Display;
10use crate::components::{AnyComponent, ComponentKind, ComponentRegistry};
11use crate::error::{Error, ErrorKind};
12use crate::nodes::element::Element;
13use crate::tree::{FilterOutput, WidgetPositionFilter};
14use crate::{Components, Factory, FloatingWidgets, GlyphMap, WidgetContainer, WidgetId, WidgetKind};
15
16mod constraints;
17pub mod display;
18pub mod text;
19
20pub struct LayoutCtx<'frame, 'bp> {
21    pub states: &'frame mut States,
22    pub(super) globals: &'bp Variables,
23    factory: &'frame Factory,
24    pub attribute_storage: &'frame mut AttributeStorage<'bp>,
25    pub components: &'frame mut Components,
26    pub glyph_map: &'frame mut GlyphMap,
27    pub viewport: &'frame mut Viewport,
28
29    // Need these for the eval context
30    pub floating_widgets: &'frame mut FloatingWidgets,
31    pub component_registry: &'frame mut ComponentRegistry,
32    pub new_components: Vec<(WidgetId, StateId)>,
33    pub stop_runtime: bool,
34    pub(super) function_table: &'bp FunctionTable,
35}
36
37impl<'frame, 'bp> LayoutCtx<'frame, 'bp> {
38    pub fn new(
39        globals: &'bp Variables,
40        factory: &'frame Factory,
41        states: &'frame mut States,
42        attribute_storage: &'frame mut AttributeStorage<'bp>,
43        components: &'frame mut Components,
44        component_registry: &'frame mut ComponentRegistry,
45        floating_widgets: &'frame mut FloatingWidgets,
46        glyph_map: &'frame mut GlyphMap,
47        viewport: &'frame mut Viewport,
48        function_table: &'bp FunctionTable,
49    ) -> Self {
50        Self {
51            states,
52            attribute_storage,
53            components,
54            component_registry,
55            globals,
56            factory,
57            floating_widgets,
58            glyph_map,
59            viewport,
60            new_components: vec![],
61            stop_runtime: false,
62            function_table,
63        }
64    }
65
66    pub fn attributes(&self, node_id: WidgetId) -> &Attributes<'bp> {
67        self.attribute_storage.get(node_id)
68    }
69
70    pub fn eval_ctx(&mut self, parent_component: Option<WidgetId>) -> EvalCtx<'_, 'bp> {
71        EvalCtx {
72            floating_widgets: self.floating_widgets,
73            attribute_storage: self.attribute_storage,
74            states: self.states,
75            component_registry: self.component_registry,
76            components: self.components,
77            globals: self.globals,
78            factory: self.factory,
79            parent_component,
80            new_components: &mut self.new_components,
81            function_table: self.function_table,
82        }
83    }
84
85    pub(crate) fn truncate_children(&mut self, tree: &mut TreeView<'_, WidgetContainer<'bp>>) {
86        tree.truncate_children(&mut |widget| {
87            self.return_component(widget);
88        });
89    }
90
91    pub(crate) fn relative_remove(&mut self, path: &[u16], mut tree: TreeView<'_, WidgetContainer<'bp>>) {
92        tree.relative_remove(path, &mut |widget| {
93            self.return_component(widget);
94        });
95    }
96
97    fn return_component(&mut self, widget: WidgetContainer<'_>) {
98        let WidgetKind::Component(comp) = widget.kind else { return };
99        self.components.try_remove(comp.widget_id);
100        let id = comp.component_id;
101        let state = self.states.remove(comp.state_id).take();
102        self.component_registry.return_component(id, comp.dyn_component, state);
103    }
104}
105
106pub struct EvalCtx<'frame, 'bp> {
107    pub(super) new_components: &'frame mut Vec<(WidgetId, StateId)>,
108    pub(super) floating_widgets: &'frame mut FloatingWidgets,
109    pub(super) attribute_storage: &'frame mut AttributeStorage<'bp>,
110    pub(super) states: &'frame mut States,
111    component_registry: &'frame mut ComponentRegistry,
112    pub(super) components: &'frame mut Components,
113    pub(super) globals: &'bp Variables,
114    pub(super) factory: &'frame Factory,
115    pub(super) function_table: &'bp FunctionTable,
116    pub(super) parent_component: Option<WidgetId>,
117}
118
119impl<'frame, 'bp> EvalCtx<'frame, 'bp> {
120    pub(super) fn get_component(
121        &mut self,
122        component_id: ComponentBlueprintId,
123    ) -> Option<(ComponentKind, Box<dyn AnyComponent>, Box<dyn State>)> {
124        self.component_registry.get(component_id)
125    }
126
127    pub(crate) fn error(&self, kind: ErrorKind) -> Error {
128        Error { kind, path: None }
129    }
130}
131
132#[derive(Debug, Copy, Clone)]
133/// A viewport represents the available space in the root
134pub struct Viewport {
135    size: Size,
136}
137
138impl Viewport {
139    pub fn new(size: impl Into<Size>) -> Self {
140        let size = size.into();
141        Self { size }
142    }
143
144    pub fn size(&self) -> Size {
145        self.size
146    }
147
148    pub fn constraints(&self) -> Constraints {
149        Constraints::new(self.size.width, self.size.height)
150    }
151
152    pub fn resize(&mut self, size: Size) {
153        self.size = size;
154    }
155}
156
157#[derive(Debug, Copy, Clone)]
158pub struct LayoutFilter;
159
160impl<'bp> crate::widget::Filter<'bp> for LayoutFilter {
161    type Output = WidgetContainer<'bp>;
162
163    fn filter<'a>(
164        &mut self,
165        widget: &'a mut WidgetContainer<'bp>,
166        attribute_storage: &AttributeStorage<'_>,
167    ) -> FilterOutput<&'a mut Self::Output, Self> {
168        match &mut widget.kind {
169            WidgetKind::Element(element) => {
170                let attributes = attribute_storage.get(element.id());
171                match attributes.get_as::<Display>(DISPLAY).unwrap_or_default() {
172                    Display::Show | Display::Hide => FilterOutput::Include(widget, *self),
173                    Display::Exclude => FilterOutput::Exclude,
174                }
175            }
176            _ => FilterOutput::Continue,
177        }
178    }
179}
180
181#[derive(Debug, Copy, Clone)]
182pub struct PositionCtx {
183    pub inner_size: Size,
184    pub pos: Pos,
185    pub viewport: Viewport,
186}
187
188impl PositionCtx {
189    pub fn region(&self) -> Region {
190        Region::from((self.pos, self.inner_size))
191    }
192}
193
194#[derive(Debug, Copy, Clone)]
195pub struct PositionFilter(WidgetPositionFilter);
196
197impl PositionFilter {
198    pub fn fixed() -> Self {
199        Self(WidgetPositionFilter::Fixed)
200    }
201
202    pub fn floating() -> Self {
203        Self(WidgetPositionFilter::Floating)
204    }
205
206    pub fn all() -> Self {
207        Self(WidgetPositionFilter::All)
208    }
209
210    pub fn none() -> Self {
211        Self(WidgetPositionFilter::None)
212    }
213}
214
215impl<'bp> crate::widget::Filter<'bp> for PositionFilter {
216    type Output = Element<'bp>;
217
218    fn filter<'a>(
219        &mut self,
220        widget: &'a mut WidgetContainer<'bp>,
221        attribute_storage: &AttributeStorage<'_>,
222    ) -> FilterOutput<&'a mut Self::Output, Self> {
223        match &mut widget.kind {
224            WidgetKind::Element(element) => {
225                let attributes = attribute_storage.get(element.id());
226                match attributes.get_as::<Display>(DISPLAY).unwrap_or_default() {
227                    Display::Show => match self.0 {
228                        WidgetPositionFilter::Floating => match element.is_floating() {
229                            true => FilterOutput::Include(element, Self::all()),
230                            false => FilterOutput::Continue,
231                        },
232                        WidgetPositionFilter::Fixed => match element.is_floating() {
233                            false => FilterOutput::Include(element, *self),
234                            true => FilterOutput::Include(element, Self::none()),
235                        },
236                        WidgetPositionFilter::All => FilterOutput::Include(element, *self),
237                        WidgetPositionFilter::None => FilterOutput::Exclude,
238                    },
239                    Display::Hide | Display::Exclude => FilterOutput::Exclude,
240                }
241            }
242            _ => FilterOutput::Continue,
243        }
244    }
245}
246
247#[cfg(test)]
248mod test {
249    use std::ops::ControlFlow;
250
251    use super::*;
252    use crate::widget::ForEach;
253
254    #[test]
255    fn filter_floating_positioning() {
256        let tpl = "
257        many
258            many
259                many
260                // --------------------
261                float                //
262                    text             // <- only these
263                        text         // <------------
264                            float    //
265                                text //
266                // --------------------
267                many
268            many
269                many
270                    many
271        ";
272
273        let mut expected = vec!["float", "text", "text", "float", "text"];
274        crate::testing::with_template(tpl, move |tree, attributes| {
275            let filter = PositionFilter::floating();
276            let children = ForEach::new(tree, attributes, filter);
277            recur(children, &mut expected);
278        });
279
280        fn recur(mut f: ForEach<'_, '_, PositionFilter>, expected: &mut Vec<&'static str>) {
281            _ = f.each(|el, nodes| {
282                assert_eq!(expected.remove(0), el.ident);
283                recur(nodes, expected);
284                ControlFlow::Continue(())
285            });
286        }
287    }
288}