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