1use densa_core::Point;
4
5use crate::access::{AccessAction, AccessId, AccessNode, AccessRequest, AccessTree, Role};
6use crate::{EventCx, Handled, Overflow, PaintCx, Ui, WidgetId};
7
8const SCROLL_STEP: f32 = 48.0;
10
11impl Ui {
12 pub fn access_id(&self, id: WidgetId) -> Option<AccessId> {
14 self.state
15 .node(id)
16 .map(|node| AccessId::widget(node.access))
17 }
18
19 pub fn access_tree(&mut self) -> AccessTree {
22 self.layout();
23 let mut nodes = Vec::new();
24 let root = self.state.root;
25 let root_id = self.access_node(root, &mut nodes);
26 for index in 0..self.state.nodes[self.state.layer.slot()].children.len() {
28 let popup = self.state.nodes[self.state.layer.slot()].children[index];
29 if !self.state.nodes[popup.slot()].style.hidden {
30 let id = self.access_node(popup, &mut nodes);
31 if let Some((_, window)) = nodes.iter_mut().find(|(i, _)| *i == root_id) {
32 window.push_child(id);
33 }
34 }
35 }
36 let focus = match self.state.focus.filter(|f| self.state.is_shown(*f)) {
37 Some(widget) => {
38 let id = AccessId::widget(self.state.nodes[widget.slot()].access);
39 let part = nodes
40 .iter()
41 .find(|(i, _)| *i == id)
42 .and_then(|(_, node)| node.focused_part())
43 .map(|part| id.part(part));
44 part.filter(|p| nodes.iter().any(|(i, _)| i == p))
46 .unwrap_or(id)
47 }
48 None => root_id,
49 };
50 AccessTree {
51 nodes,
52 root: root_id,
53 focus,
54 }
55 }
56
57 fn access_node(&self, id: WidgetId, out: &mut Vec<(AccessId, AccessNode)>) -> AccessId {
59 let state = &self.state.nodes[id.slot()];
60 let node_id = AccessId::widget(state.access);
61 let role = if id == self.state.root {
62 Role::Window
63 } else {
64 Role::Group
65 };
66 let mut node = AccessNode::new(role);
67 node.set_bounds(state.rect);
68 node.set_clips_children(state.style.overflow != Overflow::Visible);
69 if let Some(widget) = self.widgets.get(id.slot()).and_then(Option::as_ref) {
70 widget.accessibility(
71 &PaintCx {
72 state: &self.state,
73 id,
74 },
75 &mut node,
76 );
77 }
78 if self.accepts_focus(id) {
79 node.add_action(AccessAction::Focus);
80 }
81 node.add_action(AccessAction::ScrollIntoView);
82 let at = out.len();
83 out.push((node_id, AccessNode::default()));
84 let mut seen = Vec::new();
85 for (part, sub) in node.take_parts() {
86 if let Some(child) = Self::access_part(node_id, part, sub, &mut seen, out) {
87 node.push_child(child);
88 }
89 }
90 for index in 0..self.state.nodes[id.slot()].children.len() {
91 let child = self.state.nodes[id.slot()].children[index];
92 if !self.state.nodes[child.slot()].style.hidden {
93 let child = self.access_node(child, out);
94 node.push_child(child);
95 }
96 }
97 out[at].1 = node;
98 node_id
99 }
100
101 fn access_part(
105 widget: AccessId,
106 part: u32,
107 mut node: AccessNode,
108 seen: &mut Vec<u32>,
109 out: &mut Vec<(AccessId, AccessNode)>,
110 ) -> Option<AccessId> {
111 if part > crate::MAX_PART || seen.contains(&part) {
112 return None;
113 }
114 seen.push(part);
115 let node_id = widget.part(part);
116 let at = out.len();
117 out.push((node_id, AccessNode::default()));
118 for (inner, sub) in node.take_parts() {
119 if let Some(child) = Self::access_part(widget, inner, sub, seen, out) {
120 node.push_child(child);
121 }
122 }
123 out[at].1 = node;
124 Some(node_id)
125 }
126
127 pub fn access_action(
132 &mut self,
133 target: AccessId,
134 action: AccessAction,
135 value: Option<&str>,
136 ) -> bool {
137 let (serial, part) = target.split();
138 let Some(id) = self.widget_numbered(serial) else {
139 return false;
140 };
141 self.layout();
142 let request = AccessRequest {
143 action,
144 part,
145 value,
146 };
147 let done = match action {
148 AccessAction::Focus if part.is_none() && self.accepts_focus(id) => {
149 self.set_focus(Some(id));
150 true
151 }
152 AccessAction::ScrollIntoView if part.is_none() => {
153 self.scroll_into_view(id);
154 true
155 }
156 AccessAction::ScrollUp
157 | AccessAction::ScrollDown
158 | AccessAction::ScrollLeft
159 | AccessAction::ScrollRight => self.access_scroll(id, &request),
160 _ => self.deliver_access(id, &request) == Handled::Yes,
161 };
162 self.settle();
163 done
164 }
165
166 fn widget_numbered(&self, serial: u64) -> Option<WidgetId> {
168 let slot = self
169 .state
170 .nodes
171 .iter()
172 .position(|node| node.access == serial)?;
173 let id = WidgetId {
174 index: u32::try_from(slot).ok()?,
175 generation: self.state.nodes[slot].generation,
176 };
177 self.state.is_shown(id).then_some(id)
178 }
179
180 fn deliver_access(&mut self, id: WidgetId, request: &AccessRequest<'_>) -> Handled {
181 let Some(widget) = self.widgets.get_mut(id.slot()).and_then(Option::as_mut) else {
182 return Handled::No;
183 };
184 let mut cx = EventCx {
185 state: &mut self.state,
186 id,
187 };
188 widget.access_action(&mut cx, request)
189 }
190
191 fn access_scroll(&mut self, id: WidgetId, request: &AccessRequest<'_>) -> bool {
193 let mut at = Some(id);
194 while let Some(current) = at {
195 if self.deliver_access(current, request) == Handled::Yes {
196 return true;
197 }
198 let node = &self.state.nodes[current.slot()];
199 if node.style.overflow == Overflow::Scroll {
200 let step = SCROLL_STEP * self.state.scale;
201 let (dx, dy) = match request.action {
202 AccessAction::ScrollUp => (0.0, -step),
203 AccessAction::ScrollDown => (0.0, step),
204 AccessAction::ScrollLeft => (-step, 0.0),
205 _ => (step, 0.0),
206 };
207 let to = Point::new(node.scroll.x + dx, node.scroll.y + dy);
208 let before = node.scroll;
209 return self.state.scroll_to(current, to) != before;
210 }
211 at = node.parent;
212 }
213 false
214 }
215
216 fn scroll_into_view(&mut self, id: WidgetId) {
218 let mut at = self.state.nodes[id.slot()].parent;
219 while let Some(container) = at {
220 self.layout();
221 let target = self.state.nodes[id.slot()].rect;
222 let node = &self.state.nodes[container.slot()];
223 if node.style.overflow == Overflow::Scroll {
224 let view = node.content;
225 let shift = |start: f32, end: f32, view_start: f32, view_end: f32| {
226 if start < view_start {
227 start - view_start
228 } else if end > view_end {
229 (end - view_end).min(start - view_start)
230 } else {
231 0.0
232 }
233 };
234 let dx = shift(target.left(), target.right(), view.left(), view.right());
235 let dy = shift(target.top(), target.bottom(), view.top(), view.bottom());
236 if dx != 0.0 || dy != 0.0 {
237 let to = Point::new(node.scroll.x + dx, node.scroll.y + dy);
238 self.state.scroll_to(container, to);
239 }
240 }
241 at = self.state.nodes[container.slot()].parent;
242 }
243 }
244}