anathema_widgets/query/
elements.rs

1use std::ops::ControlFlow;
2
3use anathema_geometry::{Pos, Region};
4use anathema_value_resolver::{AttributeStorage, Attributes};
5
6use super::{Chain, Filter, Nodes, Query, QueryValue};
7use crate::nodes::element::Element;
8use crate::{WidgetId, WidgetKind};
9
10pub struct Elements<'children, 'tree, 'bp> {
11    pub(super) elements: &'children mut Nodes<'tree, 'bp>,
12}
13
14impl<'children, 'tree, 'bp> Elements<'children, 'tree, 'bp> {
15    pub fn by_tag<'a>(&mut self, tag: &'a str) -> ElementQuery<'_, 'tree, 'bp, Kind<'a>> {
16        self.make_query(Kind::ByTag(tag))
17    }
18
19    pub fn at_position<'a>(&mut self, pos: impl Into<Pos>) -> ElementQuery<'_, 'tree, 'bp, Kind<'a>> {
20        self.make_query(Kind::AtPosition(pos.into()))
21    }
22
23    pub fn first<F, U>(&mut self, f: F) -> Option<U>
24    where
25        F: FnMut(&mut Element<'_>, &mut Attributes<'_>) -> U,
26    {
27        self.make_query(Kind::All).first(f)
28    }
29
30    pub fn each<F>(&mut self, f: F)
31    where
32        F: FnMut(&mut Element<'_>, &mut Attributes<'_>),
33    {
34        self.make_query(Kind::All).each(f)
35    }
36
37    pub fn by_attribute<'a>(
38        &mut self,
39        key: &'a str,
40        value: impl Into<QueryValue<'a>>,
41    ) -> ElementQuery<'_, 'tree, 'bp, Kind<'a>> {
42        self.make_query(Kind::ByAttribute(key, value.into()))
43    }
44
45    pub fn by_id<'a>(&mut self, id: WidgetId) -> ElementQuery<'_, 'tree, 'bp, Kind<'a>> {
46        self.make_query(Kind::ById(id))
47    }
48
49    fn make_query<'a>(&mut self, kind: Kind<'a>) -> ElementQuery<'_, 'tree, 'bp, Kind<'a>> {
50        ElementQuery {
51            query: Query {
52                filter: kind,
53                elements: self.elements,
54            },
55        }
56    }
57}
58
59pub struct ElementQuery<'el, 'tree, 'bp, T>
60where
61    T: Filter<'bp, Kind = Element<'bp>> + Copy,
62{
63    query: Query<'el, 'tree, 'bp, T, Element<'bp>>,
64}
65
66impl<'el, 'tree, 'bp, T> ElementQuery<'el, 'tree, 'bp, T>
67where
68    T: Filter<'bp, Kind = Element<'bp>> + Copy,
69{
70    pub fn by_tag(self, name: &str) -> ElementQuery<'el, 'tree, 'bp, Chain<T, Kind<'_>>> {
71        ElementQuery {
72            query: Query {
73                filter: Chain::new(self.query.filter, Kind::ByTag(name)),
74                elements: self.query.elements,
75            },
76        }
77    }
78
79    pub fn at_position(self, pos: impl Into<Pos>) -> ElementQuery<'el, 'tree, 'bp, Chain<T, Kind<'static>>> {
80        ElementQuery {
81            query: Query {
82                filter: Chain::new(self.query.filter, Kind::AtPosition(pos.into())),
83                elements: self.query.elements,
84            },
85        }
86    }
87
88    pub fn by_attribute<'a>(
89        self,
90        key: &'a str,
91        value: impl Into<QueryValue<'a>>,
92    ) -> ElementQuery<'el, 'tree, 'bp, Chain<T, Kind<'a>>> {
93        ElementQuery {
94            query: Query {
95                filter: Chain::new(self.query.filter, Kind::ByAttribute(key, value.into())),
96                elements: self.query.elements,
97            },
98        }
99    }
100
101    pub fn first<F, U>(self, mut f: F) -> Option<U>
102    where
103        F: FnMut(&mut Element<'_>, &mut Attributes<'_>) -> U,
104    {
105        match self.query(&mut f, false) {
106            ControlFlow::Continue(_) => None,
107            ControlFlow::Break(val) => Some(val),
108        }
109    }
110
111    pub fn each<F>(self, mut f: F)
112    where
113        F: FnMut(&mut Element<'_>, &mut Attributes<'_>),
114    {
115        _ = self.query(&mut f, true);
116    }
117
118    fn query<F, U>(self, f: &mut F, continuous: bool) -> ControlFlow<U>
119    where
120        F: FnMut(&mut Element<'_>, &mut Attributes<'_>) -> U,
121    {
122        let ret_val = self.query.elements.children.for_each(|_path, container, children| {
123            if let WidgetKind::Element(ref mut element) = container.kind {
124                if self.query.filter.filter(element, self.query.elements.attributes) {
125                    let attributes = self.query.elements.attributes.get_mut(element.id());
126                    let ret_val = f(element, attributes);
127                    element.invalidate_cache();
128                    *self.query.elements.needs_layout = true;
129
130                    if !continuous {
131                        return ControlFlow::Break(ret_val);
132                    }
133                }
134            }
135
136            let mut elements = Nodes::new(
137                children,
138                self.query.elements.attributes,
139                self.query.elements.needs_layout,
140            );
141
142            let query = ElementQuery {
143                query: Query {
144                    elements: &mut elements,
145                    filter: self.query.filter,
146                },
147            };
148
149            query.query(f, continuous)
150        });
151
152        match ret_val {
153            Some(val) => ControlFlow::Break(val),
154            None => ControlFlow::Continue(()),
155        }
156    }
157}
158
159// -----------------------------------------------------------------------------
160//   - Query kind -
161// -----------------------------------------------------------------------------
162#[derive(Debug, Copy, Clone)]
163pub enum Kind<'a> {
164    All,
165    ByTag(&'a str),
166    ByAttribute(&'a str, QueryValue<'a>),
167    AtPosition(Pos),
168    ById(WidgetId),
169}
170
171impl<'bp, 'a> Filter<'bp> for Kind<'a> {
172    type Kind = Element<'bp>;
173
174    fn filter(&self, el: &Element<'bp>, attributes: &mut AttributeStorage<'_>) -> bool {
175        match self {
176            Kind::All => true,
177            Kind::ByTag(tag) => el.ident == *tag,
178            Kind::ByAttribute(key, value) => {
179                let attribs = attributes.get(el.container.id);
180                attribs.get(key).map(|attribute| value.eq(attribute)).unwrap_or(false)
181            }
182            Kind::AtPosition(pos) => {
183                let region = Region::from((el.get_pos(), el.size()));
184                region.contains(*pos)
185            }
186            Kind::ById(id) => el.id() == *id,
187        }
188    }
189}