anathema_widgets/layout/
mod.rs

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