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 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)]
96pub 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(®ion)
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}