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