1use std::time::Duration;
4
5use densa_core::Point;
6
7use crate::ui::Press;
8use crate::{
9 Event, EventCx, Handled, Ime, Key, KeyEvent, NamedKey, Overflow, PointerButton, PointerEvent,
10 PointerKind, Ui, WidgetId,
11};
12
13const MULTI_CLICK_TIME: Duration = Duration::from_millis(500);
15const MULTI_CLICK_DISTANCE: f32 = 4.0;
17
18impl Ui {
19 pub fn pointer(&mut self, mut event: PointerEvent) -> Handled {
40 self.layout();
41 match event.kind {
42 PointerKind::Down(button) => event.clicks = self.count_clicks(&event, button),
43 PointerKind::Enter | PointerKind::Leave | PointerKind::Cancel => return Handled::No,
45 _ => {}
46 }
47 self.state.pointer = Some(event.position);
48 let (hit, in_popup) = self.hit_top(event.position);
49 if self.state.capture.is_none() {
50 self.set_hover(hit, event.position);
51 }
52 let outside = !in_popup
53 && !self.state.popups.is_empty()
54 && self.state.capture.is_none()
55 && matches!(event.kind, PointerKind::Down(_) | PointerKind::Scroll(_));
56 if outside {
57 let on_opener = hit.is_some_and(|hit| {
58 self.state.popups.iter().any(|popup| {
59 popup
60 .opener
61 .is_some_and(|opener| self.state.is_within(hit, opener))
62 })
63 });
64 if !on_opener {
65 self.state.close_from(0);
66 self.state.last_press = None;
68 if event.kind == PointerKind::Down(PointerButton::Secondary) {
71 self.context_menu(hit.unwrap_or(self.state.root), event.position);
72 }
73 self.settle();
74 return Handled::Yes;
75 }
76 }
77 let delivered = Event::Pointer(event);
78 let handled = match (event.kind, self.state.capture) {
79 (PointerKind::Up(_), holder) => {
80 let handled =
82 holder.is_some_and(|holder| self.deliver(holder, &delivered) == Handled::Yes);
83 self.state.capture = None;
84 self.set_hover(hit, event.position);
85 handled
86 }
87 (_, Some(holder)) => self.deliver(holder, &delivered) == Handled::Yes,
88 (PointerKind::Down(button), None) => {
89 self.focus_from(hit);
90 let handler = self.bubble(hit, &delivered);
91 self.state.capture = handler;
92 if button == PointerButton::Secondary && !in_popup {
93 let source = handler.or(hit).unwrap_or(self.state.root);
94 self.context_menu(source, event.position);
95 }
96 if outside && handler.is_none() {
97 self.state.close_from(0);
99 }
100 handler.is_some()
101 }
102 (_, None) => {
103 let handled = self.bubble(hit, &delivered).is_some();
104 if outside && !handled {
105 self.state.close_from(0);
106 }
107 handled
108 }
109 };
110 self.settle();
111 handled.into()
112 }
113
114 pub fn pointer_left(&mut self, position: Point) {
117 self.state.pointer = None;
118 if self.state.capture.is_none() {
119 self.set_hover(None, position);
120 }
121 }
122
123 pub fn cancel_pointer(&mut self) {
127 let at = self.state.pointer.unwrap_or(Point::ORIGIN);
128 if let Some(holder) = self.state.capture {
129 let cancel = PointerEvent::new(PointerKind::Cancel, at);
130 self.deliver(holder, &Event::Pointer(cancel));
131 self.state.capture = None;
132 }
133 self.state.pointer = None;
134 self.set_hover(None, at);
135 self.settle();
136 }
137
138 pub fn key(&mut self, event: &KeyEvent<'_>) -> Handled {
144 self.layout();
145 let target = self.state.focus.or(Some(self.state.root));
146 let mut handled = self.bubble_key(target, &Event::Key(*event)).is_some();
147 let menu_key = event.key == Key::Named(NamedKey::ContextMenu)
148 || (event.key == Key::Named(NamedKey::F(10)) && event.modifiers.shift());
149 if !handled && event.pressed && menu_key {
150 let in_popup = self.state.focus.is_some_and(|focus| {
151 self.state
152 .popups
153 .iter()
154 .any(|p| self.state.is_within(focus, p.id))
155 });
156 if let Some(focus) = self.state.focus.filter(|_| !in_popup) {
157 let rect = self.state.nodes[focus.slot()].rect;
158 self.context_menu(focus, Point::new(rect.left(), rect.top()));
159 handled = true;
160 }
161 }
162 if !handled && event.pressed && event.key == Key::Named(NamedKey::Tab) {
163 let next = self.next_focus(!event.modifiers.shift());
164 if next.is_some() {
165 self.give_focus(next);
166 }
167 handled = true;
168 }
169 self.settle();
170 handled.into()
171 }
172
173 pub fn ime(&mut self, ime: &Ime<'_>) -> Handled {
175 self.layout();
176 let Some(focus) = self.state.focus else {
177 return Handled::No;
178 };
179 let handled = self.bubble(Some(focus), &Event::Ime(*ime)).is_some();
180 self.settle();
181 handled.into()
182 }
183
184 pub fn focus(&self) -> Option<WidgetId> {
186 self.state.focus
187 }
188
189 pub fn set_focus(&mut self, id: Option<WidgetId>) {
192 self.give_focus(id);
193 self.settle();
194 }
195
196 pub(crate) fn give_focus(&mut self, id: Option<WidgetId>) {
197 let id = id.filter(|id| self.state.is_shown(*id));
198 if id == self.state.focus {
199 return;
200 }
201 let old = std::mem::replace(&mut self.state.focus, id);
202 if let Some(old) = old {
203 self.deliver(old, &Event::FocusLost);
204 }
205 if let Some(new) = id {
206 self.deliver(new, &Event::FocusGained);
207 }
208 self.state.needs_paint = true;
209 }
210
211 pub fn hovered(&self) -> Option<WidgetId> {
213 self.state.hover
214 }
215
216 pub fn hit_test(&mut self, at: Point) -> Option<WidgetId> {
219 self.layout();
220 self.hit_top(at).0
221 }
222
223 pub(crate) fn hit_top(&self, at: Point) -> (Option<WidgetId>, bool) {
225 let layer = &self.state.nodes[self.state.layer.slot()];
226 for &popup in layer.children.iter().rev() {
227 if let Some(hit) = self.hit(popup, at) {
228 return (Some(hit), true);
229 }
230 }
231 (self.hit(self.state.root, at), false)
232 }
233
234 fn context_menu(&mut self, source: WidgetId, at: Point) {
236 self.state.actions.push_back(crate::Action {
237 source,
238 kind: crate::ActionKind::ContextMenu { at },
239 });
240 }
241
242 pub(crate) fn hit(&self, id: WidgetId, at: Point) -> Option<WidgetId> {
243 let node = &self.state.nodes[id.slot()];
244 if node.style.hidden {
245 return None;
246 }
247 let clips = node.style.overflow != Overflow::Visible;
248 if clips && !node.rect.contains(at) {
249 return None;
250 }
251 for &child in node.children.iter().rev() {
252 if let Some(hit) = self.hit(child, at) {
253 return Some(hit);
254 }
255 }
256 node.rect.contains(at).then_some(id)
257 }
258
259 fn deliver(&mut self, id: WidgetId, event: &Event<'_>) -> Handled {
260 if !self.state.is_live(id) {
261 return Handled::No;
262 }
263 let Some(widget) = self.widgets.get_mut(id.slot()).and_then(Option::as_mut) else {
264 return Handled::No;
265 };
266 let mut cx = EventCx {
267 state: &mut self.state,
268 id,
269 };
270 widget.event(&mut cx, event)
271 }
272
273 fn bubble_key(&mut self, mut target: Option<WidgetId>, event: &Event<'_>) -> Option<WidgetId> {
277 while let Some(id) = target.filter(|id| self.state.is_live(*id)) {
278 if self.deliver(id, event) == Handled::Yes {
279 return Some(id);
280 }
281 let parent = self.state.nodes[id.slot()].parent;
282 target = match parent {
283 Some(layer) if layer == self.state.layer => self
284 .state
285 .popups
286 .iter()
287 .find(|popup| popup.id == id)
288 .and_then(|popup| popup.opener),
289 _ => parent,
290 };
291 }
292 None
293 }
294
295 fn bubble(&mut self, mut target: Option<WidgetId>, event: &Event<'_>) -> Option<WidgetId> {
297 while let Some(id) = target.filter(|id| self.state.is_live(*id)) {
298 if self.deliver(id, event) == Handled::Yes {
299 return Some(id);
300 }
301 target = self.state.nodes[id.slot()].parent;
302 }
303 None
304 }
305
306 pub(crate) fn set_hover(&mut self, hover: Option<WidgetId>, position: Point) {
307 let old = self.state.hover;
308 if old == hover {
309 return;
310 }
311 self.mark_hovered(old, false);
312 self.mark_hovered(hover, true);
313 self.state.hover = hover;
314 if let Some(old) = old {
315 let leave = PointerEvent::new(PointerKind::Leave, position);
316 self.deliver(old, &Event::Pointer(leave));
317 }
318 if let Some(new) = hover {
319 let enter = PointerEvent::new(PointerKind::Enter, position);
320 self.deliver(new, &Event::Pointer(enter));
321 }
322 self.state.needs_paint = true;
323 }
324
325 pub(crate) fn mark_hovered(&mut self, from: Option<WidgetId>, hovered: bool) {
327 let mut at = from;
328 while let Some(id) = at.filter(|id| self.state.is_live(*id)) {
329 let node = &mut self.state.nodes[id.slot()];
330 node.hovered = hovered;
331 at = node.parent;
332 }
333 }
334
335 fn focus_from(&mut self, from: Option<WidgetId>) {
337 let mut at = from;
338 while let Some(id) = at.filter(|id| self.state.is_live(*id)) {
339 let widget = self.widgets.get(id.slot()).and_then(Option::as_deref);
340 if widget.is_some_and(|w| w.accepts_focus() && w.focus_on_press()) {
341 self.give_focus(Some(id));
342 return;
343 }
344 at = self.state.nodes[id.slot()].parent;
345 }
346 }
347
348 pub(crate) fn accepts_focus(&self, id: WidgetId) -> bool {
349 self.widgets
350 .get(id.slot())
351 .and_then(Option::as_deref)
352 .is_some_and(|w| w.accepts_focus())
353 }
354
355 fn count_clicks(&mut self, event: &PointerEvent, button: crate::PointerButton) -> u8 {
358 let near = MULTI_CLICK_DISTANCE * self.state.scale;
359 let clicks = match self.state.last_press {
360 Some(last)
361 if last.button == button
362 && event.time >= last.time
363 && event.time - last.time <= MULTI_CLICK_TIME
364 && (event.position.x - last.position.x).abs() <= near
365 && (event.position.y - last.position.y).abs() <= near =>
366 {
367 last.clicks.saturating_add(1)
368 }
369 _ => 1,
370 };
371 self.state.last_press = Some(Press {
372 time: event.time,
373 position: event.position,
374 button,
375 clicks,
376 });
377 clicks
378 }
379
380 fn next_focus(&self, forward: bool) -> Option<WidgetId> {
383 let current = self.state.focus;
384 let mut order = FocusOrder {
385 current,
386 seen: false,
387 first: None,
388 last: None,
389 before: None,
390 after: None,
391 };
392 let within = self
394 .state
395 .popups
396 .last()
397 .map_or(self.state.root, |popup| popup.id);
398 self.visit_focusable(within, &mut order);
399 if forward {
400 order.after.or(order.first)
401 } else {
402 order.before.or(order.last)
403 }
404 }
405
406 fn visit_focusable(&self, id: WidgetId, order: &mut FocusOrder) {
407 let node = &self.state.nodes[id.slot()];
408 if node.style.hidden {
409 return;
410 }
411 if self.accepts_focus(id) {
412 order.visit(id);
413 }
414 for &child in &node.children {
415 self.visit_focusable(child, order);
416 }
417 }
418}
419
420struct FocusOrder {
422 current: Option<WidgetId>,
423 seen: bool,
424 first: Option<WidgetId>,
425 last: Option<WidgetId>,
426 before: Option<WidgetId>,
427 after: Option<WidgetId>,
428}
429
430impl FocusOrder {
431 fn visit(&mut self, id: WidgetId) {
432 self.first = self.first.or(Some(id));
433 self.last = Some(id);
434 if Some(id) == self.current {
435 self.seen = true;
436 } else if self.seen {
437 self.after = self.after.or(Some(id));
438 } else {
439 self.before = Some(id);
440 }
441 }
442}