anathema_widgets/nodes/
element.rs

1use std::ops::ControlFlow;
2
3use anathema_geometry::{Pos, Region, Size};
4use anathema_value_resolver::AttributeStorage;
5
6use crate::container::Container;
7use crate::error::Result;
8use crate::layout::{Constraints, LayoutCtx, PositionFilter, Viewport};
9use crate::paint::{PaintCtx, PaintFilter, Unsized};
10use crate::widget::ForEach;
11use crate::{LayoutForEach, WidgetId};
12
13pub enum Layout {
14    Changed(Size),
15    Unchanged(Size),
16    Floating(Size),
17}
18
19impl From<Layout> for Size {
20    fn from(value: Layout) -> Self {
21        match value {
22            Layout::Changed(size) | Layout::Unchanged(size) | Layout::Floating(size) => size,
23        }
24    }
25}
26
27// TODO
28// Merge the `Element` and the `Container` into one type
29#[derive(Debug)]
30pub struct Element<'bp> {
31    pub ident: &'bp str,
32    pub(crate) container: Container,
33}
34
35impl<'bp> Element<'bp> {
36    pub fn id(&self) -> WidgetId {
37        self.container.id
38    }
39
40    pub(crate) fn new(ident: &'bp str, container: Container) -> Self {
41        Self { ident, container }
42    }
43
44    pub fn layout(
45        &mut self,
46        mut children: LayoutForEach<'_, 'bp>,
47        constraints: Constraints,
48        ctx: &mut LayoutCtx<'_, 'bp>,
49    ) -> Result<Layout> {
50        // 1. Check cache
51        // 2. Check cache of children
52        //
53        // If one of the children returns a `Changed` layout result
54        // the transition the widget into full layout mode
55
56        let count = children.len();
57        let mut rebuild = self.container.cache.count_check(count);
58
59        if let Some(size) = self.cached_size() {
60            _ = children.each(ctx, |ctx, node, children| {
61                // If we are here it's because the current node has a valid cache.
62                // We need to use the constraint for the given node in this case as
63                // the constraint is not managed by the current node.
64                //
65                // Example:
66                // If the current node is a border with a fixed width and height,
67                // it would create a new constraint for the child node that is the
68                // width and height - the border size.
69                //
70                // However the border does not store this constraint, it's stored
71                // on the node itself.
72                // Therefore we pass the nodes its own constraint.
73
74                let constraints = match node.container.cache.constraints() {
75                    None => constraints,
76                    Some(constraints) => constraints,
77                };
78
79                match node.layout(children, constraints, ctx)? {
80                    Layout::Changed(_) => {
81                        rebuild = true;
82                        Ok(ControlFlow::Break(()))
83                    }
84                    Layout::Floating(_) | Layout::Unchanged(_) => Ok(ControlFlow::Continue(())),
85                }
86            })?;
87
88            if !self.container.cache.count_check(count) {
89                rebuild = true;
90            }
91
92            if !rebuild {
93                return Ok(Layout::Unchanged(size));
94            }
95        }
96
97        self.container.layout(children, constraints, ctx)
98    }
99
100    pub fn invalidate_cache(&mut self) {
101        self.container.cache.invalidate();
102    }
103
104    /// Position the element
105    pub fn position(
106        &mut self,
107        children: ForEach<'_, 'bp, PositionFilter>,
108        pos: Pos,
109        attribute_storage: &AttributeStorage<'bp>,
110        viewport: Viewport,
111    ) {
112        self.container.position(children, pos, attribute_storage, viewport)
113    }
114
115    /// Draw an element to the surface
116    pub fn paint(
117        &mut self,
118        children: ForEach<'_, 'bp, PaintFilter>,
119        ctx: PaintCtx<'_, Unsized>,
120        attribute_storage: &AttributeStorage<'bp>,
121    ) {
122        self.container.paint(children, ctx, attribute_storage);
123    }
124
125    /// Return the cached size if the constraints are matching
126    /// the cached constraints.
127    pub fn cached_size(&self) -> Option<Size> {
128        self.container.cache.size()
129    }
130
131    pub fn size(&self) -> Size {
132        self.container.cache.size
133    }
134
135    /// Inner bounds in global space
136    pub fn inner_bounds(&self) -> Region {
137        self.container.inner_bounds
138    }
139
140    /// Bounds in global space
141    pub fn bounds(&self) -> Region {
142        let pos = self.get_pos();
143        let size = self.size();
144        Region::from((pos, size))
145    }
146
147    /// Get a mutable reference to the underlying widget of the given type
148    ///
149    /// # Panics
150    ///
151    /// Panics if the element is of a different type
152    pub fn to<T: 'static>(&mut self) -> &mut T {
153        self.try_to().expect("wrong element type")
154    }
155
156    /// Get a mutable reference to the underlying widget of the given type
157    pub fn try_to<T: 'static>(&mut self) -> Option<&mut T> {
158        self.container.inner.to_any_mut().downcast_mut::<T>()
159    }
160
161    /// Get a reference to the underlying widget of the given type
162    ///
163    /// # Panics
164    ///
165    /// Panics if hte element is of a different type
166    pub fn to_ref<T: 'static>(&self) -> &T {
167        self.try_to_ref().expect("wrong element type")
168    }
169
170    /// Get a reference to the underlying widget of the given type
171    pub fn try_to_ref<T: 'static>(&self) -> Option<&T> {
172        self.container.inner.to_any_ref().downcast_ref::<T>()
173    }
174
175    /// Get the position of the container.
176    pub fn get_pos(&self) -> Pos {
177        self.container.cache.pos.unwrap_or(Pos::ZERO)
178    }
179
180    /// Returns true if the widget is a floating widget
181    pub(crate) fn is_floating(&self) -> bool {
182        self.container.inner.any_floats()
183    }
184}