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