anathema_widgets/layout/
mod.rs

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