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