1use alloc::collections::BTreeMap;
29use alloc::string::String;
30use alloc::vec::Vec;
31
32use super::consts::*;
33use super::keys::*;
34use super::Ui;
35use plat::Event;
36
37pub(crate) const KEYQ_MAX: usize = 1024;
45pub(crate) const TEXTQ_MAX: usize = 4096;
48pub(crate) const PTS_MAX: usize = 1 << 16;
50pub(crate) const CMDQ_MAX: usize = 1 << 16;
52pub(crate) const TIMERS_MAX: usize = 4096;
54pub(crate) const UICLIP_MAX: usize = 1024;
56pub(crate) const HITS_MAX: usize = 1 << 16;
58pub(crate) const SEMS_MAX: usize = 1 << 16;
60pub(crate) const SEMTEXT_MAX: usize = 4 << 20;
61
62pub(crate) const LIM_KEYS: usize = 0;
65pub(crate) const LIM_CMDS: usize = 1;
66pub(crate) const LIM_TIMERS: usize = 2;
67pub(crate) const LIM_POINTS: usize = 3;
68pub(crate) const LIM_HITS: usize = 4;
69pub(crate) const LIM_ZONES: usize = 5;
70pub(crate) const LIM_SEMS: usize = 6;
71pub(crate) const LIM_SEMTEXT: usize = 7;
72pub(crate) const LIM_CLIP: usize = 8;
73pub(crate) const LIM_WINNEST: usize = 9;
74pub(crate) const LIM_POPUP_SLOTS: usize = 10;
75pub(crate) const LIM_POPUPS: usize = 11;
76pub(crate) const LIM_FLYOUTS: usize = 12;
77pub(crate) const LIM_MENU_ROWS: usize = 13;
78pub(crate) const LIM_MENUBAR: usize = 14;
79pub(crate) const LIM_SCROLLS: usize = 15;
80pub(crate) const LIM_LISTS: usize = 16;
81pub(crate) const LIM_EXPANDERS: usize = 17;
82pub(crate) const LIM_WRAP: usize = 18;
83pub(crate) const LIM_N: usize = 19;
84const LIM_TEXT: [&str; LIM_N] = [
85 "more than 1024 keys in one pass: the rest dropped",
86 "more than 65536 commands waiting: the rest dropped",
87 "more than 4096 timers at once: new ones dropped",
88 "more than 65536 pointer positions in one pass: the rest dropped",
89 "more than 65536 hit boxes in one window pass: the rest cannot be hit",
90 "more than 65536 drop targets in one window pass: the rest take no drops",
91 "more than 65536 widget records in one pass: the rest missing (dump, expect, screen readers)",
92 "more than 4 MiB of record labels in one pass: the rest without labels",
93 "clip_push nested more than 1024 deep: deeper levels clip no hit boxes",
94 "window_begin nested more than 16 deep: deeper windows not shown",
95 "more than 4 popups outside their windows: the rest drawn clipped to the window",
96 "popups nested more than 6 deep: the deepest one replaced",
97 "flyouts nested more than 8 deep: deeper ones not shown",
98 "a menu with more than 250 rows: the rest not shown",
99 "a menu bar with more than 16 menus: the rest not shown",
100 "scroll views nested more than 8 deep: deeper ones share one record",
101 "lists nested more than 4 deep: deeper ones share one record",
102 "expanders nested more than 16 deep: deeper ones lose their content box",
103 "a wrapped text of more than 65536 lines: the rest not shown",
104];
105
106#[derive(Clone, Copy, Default)]
108pub(crate) struct Hit {
109 pub(crate) id: u32,
110 pub(crate) continuation: bool,
112 pub(crate) flags: u16,
114 pub(crate) keys: bool,
116 pub(crate) cursor: u8,
118 pub(crate) grab: u8,
120 pub(crate) layer: u8,
121 #[allow(dead_code)]
122 pub(crate) kind: u8,
123 pub(crate) group: u32,
125 pub(crate) pane: u32,
126 pub(crate) x0: i32,
127 pub(crate) y0: i32,
128 pub(crate) x1: i32,
129 pub(crate) y1: i32,
130}
131
132#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
135#[non_exhaustive]
136pub struct KeyEvent {
137 pub vk: u32,
139 pub mods: u32,
141 pub down: bool,
143 pub repeat: bool,
145 pub scancode: u32,
149 pub location: u32,
151}
152
153#[derive(Clone, Copy)]
155pub(crate) struct RawKey {
156 pub(crate) ev: KeyEvent,
157 pub(crate) kq: i32,
158 pub(crate) taken: bool,
159}
160
161#[derive(Clone, Default)]
163pub struct Sem {
164 pub id: u32,
165 pub kind: u16,
166 pub state: u16,
168 pub rect: [i32; 4],
170 pub label: Option<String>,
171 pub value: i32,
172 pub max: i32,
173 pub min: i32,
175 pub layer: u8,
176 pub depth: u8,
177 pub win: u8,
179 pub ax: Option<alloc::boxed::Box<SemAx>>,
183}
184
185#[derive(Clone, Debug, Default, PartialEq)]
188#[non_exhaustive]
189pub struct SemAx {
190 pub id: u64,
193 pub parent: u64,
196 pub name: Option<String>,
198 pub desc: Option<String>,
200 pub url: Option<String>,
202 pub value_text: Option<String>,
204 pub states: u32,
206 pub set: [u32; 2],
208 pub level: u32,
210 pub cell: [u32; 4],
212 pub grid: [u32; 2],
214 pub live: u32,
216 pub range: Option<[f64; 4]>,
219 pub sel: Option<[u32; 2]>,
222}
223
224#[derive(Clone, Copy, Default)]
226pub struct Slot {
227 pub seen: u32,
228 pub afrom: u16,
229 pub ato: u16,
230 pub at0: u32,
231 pub adur: u32,
232 pub measw: i32,
233 pub meash: i32,
234 pub fixed: i32,
235 pub wsum: i32,
236 pub scrollx: i32,
237 pub scrolly: i32,
238 pub aux: i32,
239}
240
241pub fn ease(curve: u32, t: i32) -> i32 {
244 let t = t.clamp(0, 256);
245 let u = 256 - t;
246 match curve {
247 EASE_LINEAR => t,
248 EASE_IN => (t * t) >> 8,
249 EASE_IN_OUT => {
250 if t < 128 {
252 ((4 * t * t >> 8) * t) >> 8
253 } else {
254 let v = 2 * u;
255 256 - (((v * v >> 8) * v) >> 9)
256 }
257 }
258 EASE_OUT_CUBIC => 256 - (((u * u >> 8) * u) >> 8),
259 EASE_OUT_BACK => {
260 let x = t - 256;
262 let x2 = x * x >> 8;
263 let x3 = x2 * x >> 8;
264 256 + (x3 * 691 >> 8) + (x2 * 436 >> 8)
265 }
266 _ => 256 - ((u * u) >> 8),
268 }
269}
270
271pub(crate) struct W {
273 pub(crate) last_rect: [i32; 4],
274 pub(crate) uiclip: [i32; 4],
276 pub(crate) uiclip_stack: Vec<[i32; 4]>,
277 pub(crate) uiclip_over: u32,
279 pub(crate) hits: [Vec<Hit>; 2],
281 pub(crate) zones: [Vec<super::dnd::Zone>; 2],
283 pub(crate) pans: [Vec<super::touch::PanView>; 2],
286 pub(crate) grabs: [Vec<[i32; 4]>; 2],
287 pub(crate) keyq: Vec<u32>,
288 pub(crate) rawq: Vec<RawKey>,
290 pub(crate) textq: Vec<u32>,
291 pub(crate) hit_cur: usize,
292 pub(crate) mx: i32,
293 pub(crate) my: i32,
294 pub(crate) mbtn: u32,
295 pub(crate) pressed: u32,
296 pub(crate) released: u32,
297 pub(crate) wheel_x: i32,
298 pub(crate) wheel_y: i32,
299 pub(crate) moved: bool,
300 pub(crate) press_x: i32,
301 pub(crate) press_y: i32,
302 pub(crate) press_ms: u32, pub(crate) hot: u32,
304 pub(crate) hot_flags: u32,
305 pub(crate) active: u32,
306 pub(crate) active_seen: bool,
307 pub(crate) ractive: u32,
308 pub(crate) focus: u32,
309 pub(crate) focus_visible: bool,
310 pub(crate) press_used: bool,
311 pub(crate) rpress_used: bool,
312 pub(crate) last_click_id: u32,
313 pub(crate) last_click_ms: u32,
314 pub(crate) last_state: u32,
315 pub(crate) modal_cur: u32,
316 pub(crate) modal_prev: u32,
317 pub(crate) disabled_depth: u32,
318 pub(crate) cmd_layer_ui: u32,
319 pub(crate) kb: super::kbnav::W,
321 pub(crate) slots: BTreeMap<u32, Slot>,
324}
325
326impl W {
327 pub(crate) fn new() -> W {
329 W {
330 last_rect: [0; 4],
331 uiclip: [0; 4],
332 uiclip_stack: Vec::new(),
333 uiclip_over: 0,
334 hits: [Vec::new(), Vec::new()],
335 zones: [Vec::new(), Vec::new()],
336 pans: [Vec::new(), Vec::new()],
337 grabs: [Vec::new(), Vec::new()],
338 keyq: Vec::new(),
339 rawq: Vec::new(),
340 textq: Vec::new(),
341 hit_cur: 0,
342 mx: -1,
343 my: -1,
344 mbtn: 0,
345 pressed: 0,
346 released: 0,
347 wheel_x: 0,
348 wheel_y: 0,
349 moved: false,
350 press_x: 0,
351 press_y: 0,
352 press_ms: 0,
353 hot: 0,
354 hot_flags: 0,
355 active: 0,
356 active_seen: false,
357 ractive: 0,
358 focus: 0,
359 focus_visible: false,
360 press_used: false,
361 rpress_used: false,
362 last_click_id: 0,
363 last_click_ms: 0,
364 last_state: 0,
365 modal_cur: 0,
366 modal_prev: 0,
367 disabled_depth: 0,
368 cmd_layer_ui: 0,
369 kb: Default::default(),
370 slots: BTreeMap::new(),
371 }
372 }
373}
374
375pub(crate) fn hit_find_list(list: &[Hit], modal_layer: u32, x: i32, y: i32) -> (u32, u32) {
379 match hit_find(list, modal_layer, x, y) {
380 Some(h) => {
381 let flags = h.flags as u32;
382 (if flags & (HB_BLOCK | HB_DISABLED) != 0 { 0 } else { h.id }, flags)
383 }
384 None => (0, 0),
385 }
386}
387
388pub(crate) fn hit_find(list: &[Hit], modal_layer: u32, x: i32, y: i32) -> Option<&Hit> {
390 if x == -1 && y == -1 {
393 return None;
394 }
395 let mut layer = L_COUNT as i32 - 1;
396 while layer >= modal_layer as i32 {
397 for h in list.iter().rev() {
398 if h.layer as i32 != layer || x < h.x0 || x >= h.x1 || y < h.y0 || y >= h.y1 {
399 continue;
400 }
401 return Some(h);
402 }
403 layer -= 1;
404 }
405 None
406}
407
408pub(crate) fn hit_cursor(h: Option<&Hit>) -> u32 {
411 match h {
412 Some(h) if h.flags as u32 & HB_DRAG != 0 => CUR_ARROW,
413 Some(h) if h.cursor != 0 => h.cursor as u32,
414 Some(h) => (h.flags as u32 >> HB_CURSOR_SHIFT) & 15,
415 None => CUR_ARROW,
416 }
417}
418
419impl Ui {
420 pub fn now(&self) -> u32 {
424 self.now_ms
425 }
426
427 pub fn mods(&self) -> u32 {
429 self.mods
430 }
431
432 pub fn mouse(&self) -> (i32, i32) {
434 (self.wp.ctx.mx, self.wp.ctx.my)
435 }
436
437 pub fn pen(&self) -> (u32, u32) {
439 (self.mpress, self.mptype)
440 }
441
442 pub fn focus(&mut self, id: u32) {
445 self.focus_req_old = self.wp.ctx.focus;
446 self.focus_req = id;
447 self.focus_req_win = self.uiw_cur;
448 self.focus_req_frame = self.frame;
449 self.wp.ctx.focus = id;
450 self.wp.ctx.focus_visible = true;
451 self.repass = true;
452 }
453
454 pub fn focus_quiet(&mut self, id: u32) {
457 self.wp.ctx.focus = id;
458 self.wp.ctx.focus_visible = false;
459 self.repass = true;
460 }
461
462 pub(crate) fn focus_req_expire(&mut self) {
464 if self.focus_req_frame < self.frame {
465 self.focus_req = 0;
466 }
467 }
468
469 pub fn focused(&self) -> u32 {
471 self.wp.ctx.focus
472 }
473
474 fn key_find(&self, vk: u32) -> Option<usize> {
476 self.wp.ctx.keyq.iter().position(|&k| (k as i32) >= 0 && k & 0xFFFF == vk)
477 }
478
479 pub fn key(&mut self, vk: u32) -> bool {
481 let k = self.key_find(vk);
482 match self.text_key_filter(k) {
483 Some(i) => {
484 self.wp.ctx.keyq[i] |= 0x8000_0000;
485 true
486 }
487 None => false,
488 }
489 }
490
491 pub fn capture_shortcut(&mut self, vk: u32, mods: u32) -> bool {
496 if let Some(i) = self.wp.ctx.keyq.iter().position(|&k| {
497 (k as i32) >= 0 && k & 0xffff == vk && (k >> 16) == mods
498 }) {
499 self.wp.ctx.keyq[i] |= 0x8000_0000;
500 true
501 } else {
502 false
503 }
504 }
505
506 pub fn key_peek(&mut self, vk: u32) -> bool {
508 let k = self.key_find(vk);
509 self.text_key_filter(k).is_some()
510 }
511
512 pub fn key_down(&self, vk: u32) -> bool {
514 self.keydown[(vk & 255) as usize] != 0
515 }
516
517 pub fn key_first(&self) -> u32 {
519 self.wp.ctx.keyq.iter().copied().find(|&k| (k as i32) >= 0).unwrap_or(0)
520 }
521
522 pub fn key_take(&mut self) -> u32 {
527 if self.popup_count() != 0 {
528 return 0;
529 }
530 for i in 0..self.wp.ctx.keyq.len() {
531 let k = self.wp.ctx.keyq[i];
532 if (k as i32) >= 0 && self.text_key_filter(Some(i)).is_some() {
533 self.wp.ctx.keyq[i] |= 0x8000_0000;
534 return k;
535 }
536 }
537 0
538 }
539
540 pub fn key_event_take(&mut self) -> Option<KeyEvent> {
547 if self.popup_count() != 0 {
548 return None;
549 }
550 for i in 0..self.wp.ctx.rawq.len() {
551 let r = self.wp.ctx.rawq[i];
552 if r.taken {
553 continue;
554 }
555 self.wp.ctx.rawq[i].taken = true;
556 if r.kq >= 0 {
557 let k = r.kq as usize;
558 if self.wp.ctx.keyq.get(k).is_none_or(|&q| (q as i32) < 0) || self.text_key_filter(Some(k)).is_none() {
560 continue;
561 }
562 self.wp.ctx.keyq[k] |= 0x8000_0000;
563 }
564 return Some(r.ev);
565 }
566 None
567 }
568
569 pub fn text_take(&mut self, cap: usize) -> Vec<u32> {
572 let c = &mut self.wp.ctx;
573 let n = c.textq.len().min(cap);
574 let v: Vec<u32> = c.textq.drain(..n).collect();
575 if n > 0 && !c.textq.is_empty() {
576 let rest = core::mem::take(&mut c.textq);
577 self.text_defer(rest);
578 }
579 self.wp.ctx.textq.clear(); v
581 }
582
583 fn text_defer(&mut self, rest: Vec<u32>) {
586 let w = self.uiw_cur;
587 match self.text_more.first_mut() {
588 Some((m, q)) if *m == w => drop(q.splice(0..0, rest)),
589 _ => self.text_more.insert(0, (w, rest)),
590 }
591 }
592
593 fn text_push(&mut self, cp: u32) {
596 if self.text_more.is_empty() && self.wp.ctx.textq.len() < TEXTQ_MAX {
597 self.wp.ctx.textq.push(cp);
598 return;
599 }
600 let w = self.uiw_cur;
601 match self.text_more.last_mut() {
602 Some((m, q)) if *m == w => q.push(cp),
603 _ => self.text_more.push((w, alloc::vec![cp])),
604 }
605 }
606
607 pub(crate) fn text_refill(&mut self) -> bool {
610 while let Some((w, q)) = self.text_more.first() {
611 let w = *w;
612 if q.is_empty() || (w != 0 && !self.uiw_drawable(w)) {
613 self.text_more.remove(0);
614 continue;
615 }
616 let cur = self.uiw_cur;
617 self.uiw_use(w);
618 let q = &mut self.text_more[0].1;
619 let n = q.len().min(TEXTQ_MAX.saturating_sub(self.wp.ctx.textq.len()));
620 self.wp.ctx.textq.extend(q.drain(..n));
621 if q.is_empty() {
622 self.text_more.remove(0);
623 }
624 self.m.win.edges |= 1 << (w & (WIN_MAX as u32 - 1)); self.uiw_fwd_arm(w);
626 self.uiw_use(cur);
627 return true;
628 }
629 false
630 }
631
632 #[allow(dead_code)] pub(crate) fn keys_take_rest(&mut self, cap: usize) -> Vec<u32> {
636 let mut out = Vec::new();
637 for k in self.wp.ctx.keyq.iter_mut() {
638 if (*k as i32) < 0 {
639 continue;
640 }
641 if out.len() >= cap {
642 break;
643 }
644 out.push(*k);
645 *k |= 0x8000_0000;
646 }
647 out
648 }
649
650 pub(crate) fn keys_inject(&mut self, keys: &[u32]) {
653 for &k in keys {
654 self.key_push(k & 0x7FFF_FFFF);
655 }
656 }
657
658 fn raw_push(&mut self, ev: KeyEvent, kq: i32) {
661 if self.wp.ctx.rawq.len() < KEYQ_MAX {
662 self.wp.ctx.rawq.push(RawKey { ev, kq, taken: false });
663 }
664 }
665
666 fn key_push(&mut self, k: u32) {
667 if self.wp.ctx.keyq.len() < KEYQ_MAX {
668 self.wp.ctx.keyq.push(k);
669 } else {
670 self.limit_hit(LIM_KEYS);
671 }
672 }
673
674 pub(crate) fn limit_hit(&mut self, lim: usize) {
677 let n = self.limits[lim];
678 self.limits[lim] = n.saturating_add(1);
679 if n == 0 {
680 self.warn(LIM_TEXT[lim]);
681 }
682 }
683
684 #[allow(dead_code)] pub(crate) fn text_inject(&mut self, cps: &[u32]) {
687 let n = TEXTQ_MAX.saturating_sub(self.wp.ctx.textq.len()).min(cps.len());
689 self.wp.ctx.textq.extend_from_slice(&cps[..n]);
690 if n < cps.len() {
691 self.text_defer(cps[n..].to_vec());
692 }
693 }
694
695 pub(crate) fn input_event(&mut self, ev: &Event) -> u32 {
699 let mut e = *ev;
700 self.rtl_input(&mut e); let ev = &e;
702 self.mods = ev.mods as u32;
703 match ev.typ {
704 plat::E_MOVE => {
705 self.wp.ctx.mx = ev.x;
706 self.wp.ctx.my = ev.y;
707 self.pen_decode(ev.data);
708 self.pts_add();
709 self.wp.ctx.moved = true;
710 self.dnd_moved(); 0
712 }
713 plat::E_DOWN => {
714 if ev.x != -1 {
715 self.wp.ctx.mx = ev.x;
716 self.wp.ctx.my = ev.y;
717 }
718 self.pen_decode(ev.data);
719 let b = (ev.btn & 31) as u32;
720 let c = &mut self.wp.ctx;
721 c.mbtn |= 1 << b;
722 c.pressed |= 1 << b;
723 c.press_x = c.mx;
724 c.press_y = c.my;
725 self.mpts.clear();
726 self.mprs.clear();
727 self.pts_add();
728 1
729 }
730 plat::E_UP => {
731 self.dnd_input(ev); let c = &mut self.wp.ctx;
733 if ev.x != -1 {
734 c.mx = ev.x;
735 c.my = ev.y;
736 }
737 let b = (ev.btn & 31) as u32;
738 c.mbtn &= !(1 << b);
739 c.released |= 1 << b;
740 1
741 }
742 plat::E_WHEEL => {
743 let c = &mut self.wp.ctx;
744 c.wheel_y = c.wheel_y.wrapping_add(ev.data as u16 as i16 as i32);
745 c.wheel_x = c.wheel_x.wrapping_add((ev.data as i32) >> 16);
746 1
747 }
748 plat::E_KEY_DOWN => {
749 if self.dnd_input(ev) {
750 return 1; }
752 let vk = ev.data & 0xFFFF;
753 self.keydown[(vk & 255) as usize] = 1;
754 let n = self.wp.ctx.keyq.len();
755 self.key_push((vk | self.mods << 16) & 0x7FFF_FFFF);
756 let kq = if self.wp.ctx.keyq.len() > n { n as i32 } else { -1 };
758 let ev = KeyEvent { vk, mods: self.mods, down: true, repeat: ev.data >> 31 != 0, scancode: ev.x as u32, location: ev.y as u32 };
759 self.raw_push(ev, kq);
760 1
761 }
762 plat::E_KEY_UP => {
763 let k = ev.data & 255;
764 self.keydown[k as usize] = 0;
765 let keys = self.focus_has_keys();
767 let ev = KeyEvent { vk: ev.data & 0xFFFF, mods: self.mods, down: false, repeat: false, scancode: ev.x as u32, location: ev.y as u32 };
768 self.raw_push(ev, -1);
769 (k == VK_MENU || keys) as u32
771 }
772 plat::E_TEXT => {
773 self.text_push(ev.data);
774 2 }
776 plat::E_PREEDIT => 1,
777 plat::E_IME_COMMIT => {
778 if let Some(text) = plat::ime_commit_take(ev.data) {
779 self.hr_commit(&text); for c in text.chars() {
781 self.text_push(c as u32);
782 }
783 }
784 1 }
786 plat::E_LEAVE => {
787 let c = &mut self.wp.ctx;
788 c.mx = -1;
789 c.my = -1;
790 c.moved = true;
791 0
792 }
793 plat::E_FOCUS_OUT => {
794 self.wp.ctx.mbtn = 0;
795 self.keydown = [0; 256];
796 1
797 }
798 _ => 0,
799 }
800 }
801
802 fn pen_decode(&mut self, d: u32) {
804 self.mpress = d & plat::PD_PRESSURE;
805 self.mptype = (d >> plat::PD_TYPE_SHIFT) & 3;
806 self.mtilt[0] = ((d << (32 - plat::PD_TILTY_SHIFT)) as i32) >> 25;
807 self.mtilt[1] = (d as i32) >> plat::PD_TILTY_SHIFT;
808 if self.mptype == PTR_TOUCH {
809 self.mtilt = [0, 0]; }
811 }
812
813 fn pts_add(&mut self) {
815 if self.uiw_cur != self.mpts_win {
816 self.mpts_win = self.uiw_cur;
817 self.mpts.clear();
818 self.mprs.clear();
819 }
820 if self.mpts.len() < PTS_MAX {
821 self.mpts.push([self.wp.ctx.mx, self.wp.ctx.my]);
822 self.mprs.push(self.mpress);
823 } else {
824 self.limit_hit(LIM_POINTS);
825 }
826 }
827
828 pub(crate) fn input_clear_edges(&mut self) {
830 let c = &mut self.wp.ctx;
831 c.pressed = 0;
832 c.released = 0;
833 c.wheel_x = 0;
834 c.wheel_y = 0;
835 c.keyq.clear();
836 c.rawq.clear();
837 c.textq.clear();
838 c.moved = false;
839 if self.uiw_cur == self.mpts_win && !self.mpts.is_empty() {
841 let n = self.mpts.len();
842 let (p, r) = (self.mpts[n - 1], self.mprs[n - 1]);
843 self.mpts.clear();
844 self.mprs.clear();
845 self.mpts.push(p);
846 self.mprs.push(r);
847 }
848 }
849
850 pub(crate) fn set_time(&mut self, ms: u32) {
852 self.now_ms = ms;
853 if self.headless {
854 plat::task::clock_set(Some(ms as u64 * 1000)); }
856 }
857
858 pub(crate) fn hot_find(&self, x: i32, y: i32) -> (u32, u32) {
863 if self.uiw_inert_now() {
864 return (0, 0);
865 }
866 let c = &self.wp.ctx;
867 hit_find_list(&c.hits[c.hit_cur ^ 1], c.modal_prev, x, y)
868 }
869
870 pub fn cursor_at(&self, x: i32, y: i32) -> u32 {
874 let c = &self.wp.ctx;
875 hit_cursor(hit_find(&c.hits[c.hit_cur ^ 1], c.modal_prev, x, y))
876 }
877
878 pub fn hit_flags_at(&mut self, x: i32, y: i32) -> u32 {
881 self.hit_flags_win(0, x, y)
882 }
883
884 pub(crate) fn hot_update(&mut self) -> bool {
886 let (id, flags) = self.hot_find(self.wp.ctx.mx, self.wp.ctx.my);
887 if self.wp.ctx.active == 0 {
889 let shape = if self.uiw_inert_now() { CUR_ARROW } else { self.cursor_at(self.wp.ctx.mx, self.wp.ctx.my) };
890 let s = self.cursor_resolve(shape);
891 plat::set_cursor(s);
892 }
893 let c = &mut self.wp.ctx;
894 let changed = id != c.hot;
895 c.hot = id;
896 c.hot_flags = flags;
897 changed
898 }
899
900 pub(crate) fn needs_move_pass(&mut self) -> bool {
902 self.hot_update() || self.wp.ctx.active != 0 || self.wp.ctx.hot_flags & HB_MOVES != 0 || self.dnd_follows_pointer()
903 }
904
905 pub fn hit(&mut self, id: u32, hbflags: u32) {
907 let c = &mut self.wp.ctx;
908 let (group, pane, notab) = c.kb.hit_keys(hbflags);
909 if c.hits[c.hit_cur].len() >= HITS_MAX {
910 self.limit_hit(LIM_HITS);
911 return;
912 }
913 let c = &mut self.wp.ctx;
914 let list = &mut c.hits[c.hit_cur];
915 let r = c.last_rect;
916 let k = c.uiclip;
917 let cursor = core::mem::take(&mut self.wp.lay.next_cursor).min(255) as u8;
918 list.push(Hit {
919 id,
920 continuation: false,
921 flags: (hbflags | notab) as u16,
922 keys: hbflags & HB_KEYS != 0,
923 cursor,
924 grab: super::touch::hit_grab(hbflags),
925 layer: c.cmd_layer_ui as u8,
926 kind: 0,
927 group,
928 pane,
929 x0: r[0].max(k[0]),
930 y0: r[1].max(k[1]),
931 x1: r[0].wrapping_add(r[2]).min(k[2]),
932 y1: r[1].wrapping_add(r[3]).min(k[3]),
933 });
934 }
935
936 pub fn behave(&mut self, id: u32, hbflags: u32) -> u32 {
939 if self.focus_req == id && id != 0 {
941 self.uiw_focus_arrived();
942 }
943 let mut flags = hbflags;
944 if self.wp.ctx.disabled_depth != 0 {
946 flags |= HB_DISABLED;
947 }
948 self.hit(id, flags);
949 self.wp.ctx.last_state = 0;
950 if flags & HB_DISABLED != 0 {
951 self.wp.ctx.last_state = SS_DISABLED as u32;
952 return 0;
953 }
954 let now = self.now_ms;
955 let lim = self.dp(4);
956 let dbl_ms = self.dbl_ms; let mut ev = 0;
958 let c = &mut self.wp.ctx;
959 if c.hot == id {
960 ev |= EV_HOVER;
961 if c.pressed & (1 << BTN_LEFT) != 0 && !c.press_used {
962 c.press_used = true;
963 c.active = id;
964 c.press_ms = now;
965 if flags & HB_FOCUS != 0 {
966 c.focus = id;
967 c.focus_visible = false;
968 }
969 if c.last_click_id == id && now.wrapping_sub(c.last_click_ms) <= dbl_ms {
970 ev |= EV_DOUBLE;
971 c.last_click_id = 0;
972 } else {
973 c.last_click_id = id;
974 c.last_click_ms = now;
975 }
976 }
977 if c.pressed & (1 << BTN_RIGHT) != 0 && !c.rpress_used {
978 c.rpress_used = true;
979 c.ractive = id;
980 }
981 }
982 if c.active == id {
984 c.active_seen = true;
985 if c.mbtn & (1 << BTN_LEFT) != 0 {
986 ev |= EV_HELD;
987 let dx = c.mx.wrapping_sub(c.press_x).wrapping_abs();
989 let dy = c.my.wrapping_sub(c.press_y).wrapping_abs();
990 if dx > lim || dy > lim {
991 ev |= EV_DRAG;
992 }
993 } else {
994 c.active = 0;
995 ev |= EV_COMMIT;
996 if ev & EV_HOVER != 0 {
997 ev |= EV_ACT;
998 }
999 }
1000 }
1001 if c.ractive == id && c.mbtn & (1 << BTN_RIGHT) == 0 {
1002 c.ractive = 0;
1003 if ev & EV_HOVER != 0 {
1004 ev |= EV_RCLICK;
1005 }
1006 }
1007 if c.focus == id {
1008 ev |= EV_FOCUS;
1009 if flags & (HB_TEXT | HB_OWNKEYS | HB_KEYS) == 0 && flags & HB_FOCUS != 0 && (self.key(VK_SPACE) || flags & HB_SPACE == 0 && self.key(VK_RETURN)) {
1010 ev |= EV_ACT;
1011 self.wp.ctx.focus_visible = true;
1012 }
1013 }
1014 if self.m.kbnav.ctx == id && id != 0 {
1017 ev |= self.kb_ctx_take();
1018 }
1019 if self.m.a11y.pending() && flags & HB_TEXT == 0 && self.a11y_click(id) {
1021 ev |= EV_ACT;
1022 }
1023 if ev & (EV_ACT | EV_COMMIT | EV_DOUBLE | EV_RCLICK) != 0 {
1026 self.repass = true;
1027 }
1028 let mut st = 0;
1030 if ev & EV_HOVER != 0 {
1031 st |= SS_HOVER;
1032 }
1033 if ev & EV_HELD != 0 {
1034 st |= SS_PRESSED;
1035 }
1036 if ev & EV_FOCUS != 0 {
1037 st |= SS_FOCUSED;
1038 }
1039 self.wp.ctx.last_state = st as u32;
1040 ev
1041 }
1042
1043 pub fn hit_rect(&mut self, id: u32, x: i32, y: i32, w: i32, h: i32, hbflags: u32) -> u32 {
1045 self.wp.ctx.last_rect = [x, y, w, h];
1046 self.behave(id, hbflags)
1047 }
1048
1049 pub fn focus_ring(&self, ev: u32) -> bool {
1052 ev & EV_FOCUS != 0 && self.wp.ctx.focus_visible
1053 }
1054
1055 pub fn slot(&mut self, id: u32) -> &mut Slot {
1062 let frame = self.frame;
1063 let s = self.wp.ctx.slots.entry(id).or_default();
1064 s.seen = frame;
1065 s
1066 }
1067
1068 pub fn anim(&mut self, id: u32, target: u32, ms: u32) -> u32 {
1071 self.anim_ex(id, target, ms, EASE_OUT)
1072 }
1073
1074 pub fn anim_ex(&mut self, id: u32, target: u32, ms: u32, curve: u32) -> u32 {
1077 let now = self.now_ms;
1078 let off = self.anim_off;
1079 let s = self.slot(id | 0x4000_0000);
1081 s.aux = curve as i32;
1082 fn value(s: &Slot, now: u32) -> u32 {
1084 let to = s.ato as u32;
1085 let dur = s.adur;
1086 if dur == 0 {
1087 return to;
1088 }
1089 let el = now.wrapping_sub(s.at0);
1090 if el >= dur {
1091 return to;
1092 }
1093 let t = ((el << 8) / dur) as i32;
1094 let e = ease(s.aux as u32, t);
1095 let from = s.afrom as i32;
1096 let d = ((to as i32 - from).wrapping_mul(e)) >> 8;
1097 (from + d).max(0) as u32
1098 }
1099 if off || s.adur == 0 {
1100 s.afrom = target as u16;
1102 s.ato = target as u16;
1103 s.adur = if ms == 0 { 1 } else { ms };
1104 return target;
1105 }
1106 if s.ato as u32 != target {
1107 let v = value(s, now);
1109 s.afrom = v as u16;
1110 s.ato = target as u16;
1111 s.at0 = now;
1112 s.adur = ms;
1113 }
1114 let v = value(s, now);
1115 if v != s.ato as u32 {
1116 self.animating = true;
1117 }
1118 v
1119 }
1120
1121 pub fn anim_color(&mut self, id: u32, argb: u32, ms: u32, curve: u32) -> u32 {
1124 let now = self.now_ms;
1125 let off = self.anim_off;
1126 let s = self.slot(id | 0x2000_0000);
1128 let at = |s: &Slot| -> u32 {
1129 let el = now.wrapping_sub(s.at0);
1130 if s.adur == 0 || el >= s.adur {
1131 return s.meash as u32;
1132 }
1133 let t = ((el << 8) / s.adur) as i32;
1134 super::theme::color_mix(s.measw as u32, s.meash as u32, ease(s.aux as u32, t).clamp(0, 256))
1135 };
1136 if off || s.adur == 0 {
1137 s.measw = argb as i32;
1138 s.meash = argb as i32;
1139 s.adur = ms.max(1);
1140 s.at0 = now;
1141 return argb;
1142 }
1143 if s.meash as u32 != argb {
1144 let c = at(s);
1145 s.measw = c as i32;
1146 s.meash = argb as i32;
1147 s.at0 = now;
1148 s.adur = ms.max(1);
1149 s.aux = curve as i32;
1150 }
1151 let c = at(s);
1152 if c != argb {
1153 self.animating = true;
1154 }
1155 c
1156 }
1157
1158 pub fn cmd_run(&mut self, id: u32, arg: u32) {
1162 if self.cmdq.len() < CMDQ_MAX {
1163 self.cmdq.push_back((id, arg));
1164 } else {
1165 self.limit_hit(LIM_CMDS);
1166 }
1167 }
1168
1169 pub(crate) fn cmd_take(&mut self) -> Option<(u32, u32)> {
1171 self.cmdq.pop_front()
1172 }
1173
1174 pub fn timer(&mut self, id: u32, ms: u32) {
1177 let deadline = self.now_ms.wrapping_add(ms);
1178 if let Some(i) = self.timers.iter().position(|t| t.0 == id) {
1180 self.timers.remove(i);
1181 } else if self.timers.len() >= TIMERS_MAX {
1182 self.limit_hit(LIM_TIMERS);
1183 return;
1184 }
1185 self.timers.push((id, deadline));
1186 }
1187
1188 pub(crate) fn timer_due(&mut self) -> (u32, u32) {
1191 match self.timer_scan() {
1192 (Some(i), next) => (self.timers.remove(i).0, next),
1193 (None, next) => (u32::MAX, next),
1194 }
1195 }
1196
1197 pub(crate) fn timer_scan(&self) -> (Option<usize>, u32) {
1200 let now = self.now_ms;
1201 let (mut due, mut due_at) = (None, 0i32);
1202 let (mut next, mut left) = (u32::MAX, u32::MAX);
1203 for (i, &(_, d)) in self.timers.iter().enumerate() {
1204 let rel = d.wrapping_sub(now) as i32; if rel <= 0 {
1206 if due.is_none() || rel < due_at {
1207 due = Some(i);
1208 due_at = rel;
1209 }
1210 } else if (rel as u32) < left {
1211 left = rel as u32;
1212 next = d;
1213 }
1214 }
1215 (due, next)
1216 }
1217
1218 pub fn last_reset(&mut self) {
1223 self.wp.ctx.last_rect = [0; 4];
1224 self.wp.ctx.last_state = 0;
1225 }
1226
1227 pub fn last_set(&mut self, rect: [i32; 4], state: u32) {
1230 self.wp.ctx.last_rect = rect;
1231 self.wp.ctx.last_state = state;
1232 }
1233
1234 pub fn last_state(&self) -> u32 {
1236 self.wp.ctx.last_state
1237 }
1238
1239 pub fn mbtn(&self) -> u32 {
1242 self.wp.ctx.mbtn
1243 }
1244 pub fn pressed(&self) -> u32 {
1245 self.wp.ctx.pressed
1246 }
1247 pub fn released(&self) -> u32 {
1248 self.wp.ctx.released
1249 }
1250
1251 #[cfg_attr(jfgui_dev, track_caller)]
1254 pub fn sem(&mut self, kind: u16, id: u32, label: Option<&str>, value: i32) -> &mut Sem {
1255 if self.sems.len() >= SEMS_MAX {
1256 self.limit_hit(LIM_SEMS);
1257 return &mut self.sem_dummy;
1258 }
1259 let c = &self.wp.ctx;
1260 let mut r = Sem {
1261 id,
1262 kind,
1263 state: c.last_state as u16,
1264 rect: self.phys_rect(c.last_rect), label: None,
1266 value,
1267 max: 0,
1268 min: 0,
1269 layer: c.cmd_layer_ui as u8,
1270 depth: self.wp.lay.boxes.len() as u8,
1271 win: self.uiw_cur as u8,
1272 ax: None,
1273 };
1274 if let Some(l) = label {
1275 let l = l.split('\0').next().unwrap_or("");
1277 let n = self.semtext_len + l.len() + 1;
1278 if n <= SEMTEXT_MAX {
1279 self.semtext_len = n;
1280 r.label = Some(String::from(l));
1281 } else {
1282 self.limit_hit(LIM_SEMTEXT);
1283 }
1284 }
1285 self.sems.push(r);
1286 let n = self.sems.len();
1287 self.src_sem(n - 1); &mut self.sems[n - 1]
1289 }
1290
1291 #[cfg_attr(jfgui_dev, track_caller)]
1298 pub fn sem_rect(&mut self, kind: u16, id: u32, rect: [i32; 4], label: Option<&str>, value: i32) -> &mut Sem {
1299 let (lr, ls) = (self.wp.ctx.last_rect, self.wp.ctx.last_state);
1300 self.wp.ctx.last_rect = rect;
1301 self.wp.ctx.last_state = 0;
1302 let n = self.sems.len();
1303 self.sem(kind, id, label, value);
1304 self.wp.ctx.last_rect = lr;
1305 self.wp.ctx.last_state = ls;
1306 if self.sems.len() > n {
1307 &mut self.sems[n]
1308 } else {
1309 &mut self.sem_dummy
1310 }
1311 }
1312
1313 pub fn clip_push(&mut self, x: i32, y: i32, w: i32, h: i32) {
1317 self.draw_clip(x, y, w, h);
1318 let c = &mut self.wp.ctx;
1319 if c.uiclip_stack.len() >= UICLIP_MAX {
1320 c.uiclip_over += 1;
1321 self.limit_hit(LIM_CLIP);
1322 return;
1323 }
1324 c.uiclip_stack.push(c.uiclip);
1325 let k = &mut c.uiclip;
1326 k[0] = x.max(k[0]);
1327 k[1] = y.max(k[1]);
1328 k[2] = x.wrapping_add(w).min(k[2]);
1329 k[3] = y.wrapping_add(h).min(k[3]);
1330 }
1331
1332 pub fn clip_pop(&mut self) {
1334 self.draw_unclip();
1335 let c = &mut self.wp.ctx;
1336 if c.uiclip_over != 0 {
1337 c.uiclip_over -= 1; } else if let Some(k) = c.uiclip_stack.pop() {
1339 c.uiclip = k;
1340 }
1341 }
1342
1343 pub fn layer_set(&mut self, layer: u32) -> u32 {
1345 let prev = self.wp.ctx.cmd_layer_ui;
1346 self.wp.ctx.cmd_layer_ui = layer & 3;
1347 self.cmd_layer(layer);
1348 prev
1349 }
1350
1351 pub fn begin_pass(&mut self, w: i32, h: i32) {
1359 self.frame_begin();
1360 self.uiw_pass_begin(w, h);
1361 }
1362
1363 pub fn end_pass(&mut self) -> bool {
1365 self.uiw_pass_end();
1366 self.frame_end()
1367 }
1368
1369 pub(crate) fn frame_begin(&mut self) {
1371 self.repass = false;
1372 self.sems.clear();
1373 self.semtext_len = 0;
1374 self.animating = false;
1375 self.frame = self.frame.wrapping_add(1);
1376 if self.m.ins.on {
1377 self.m.ins.boxes.clear(); }
1379 self.src_clear(); self.rtl_update();
1381 self.ids_frame_begin();
1382 self.fonts_frame(); self.uiw_frame_begin();
1384 }
1385
1386 pub(crate) fn frame_end(&mut self) -> bool {
1388 self.uiw_frame_end();
1389 #[cfg(target_os = "android")]
1390 self.ime_frame_end();
1391 self.pw_frame_end(); self.repass
1393 }
1394
1395 pub(crate) fn uiw_pass_begin(&mut self, w: i32, h: i32) {
1398 if self.uiw_cur == 0 {
1399 self.form_update(w, h); }
1401 self.cmd_reset();
1402 let c = &mut self.wp.ctx;
1403 c.cmd_layer_ui = 0;
1404 c.press_used = false;
1405 c.rpress_used = false;
1406 c.active_seen = false;
1407 c.modal_cur = 0;
1408 c.disabled_depth = 0;
1409 c.kb.pass_begin();
1410 c.uiclip_stack.clear();
1411 c.uiclip_over = 0;
1412 c.uiclip = [0, 0, w, h];
1413 let cur = c.hit_cur;
1414 c.hits[cur].clear();
1415 self.wp.chrome.cap_n = 0; self.wp.chrome.tb_rect = None;
1417 let c = &mut self.wp.ctx;
1418 c.zones[cur].clear();
1419 c.pans[cur].clear();
1420 c.grabs[cur].clear();
1421 self.hot_update();
1422 self.dnd_pass_begin(); let st = self.style;
1425 st.window_bg(self, w, h);
1426 let b = self.uiw_border();
1428 self.layout_root(b, b, w - b - b, h - b - b);
1429 }
1430
1431 pub(crate) fn uiw_pass_end(&mut self) {
1434 self.uiw_fwd_take(); self.accel_dispatch(); self.layout_end_root();
1437 if !self.wp.ctx.active_seen {
1439 self.wp.ctx.active = 0;
1440 }
1441 self.kb_nav();
1444 if self.key(VK_ESCAPE) {
1446 self.uiw_escape();
1447 }
1448 let c = &mut self.wp.ctx;
1449 if c.modal_cur != c.modal_prev {
1450 c.modal_prev = c.modal_cur;
1451 self.repass = true;
1452 }
1453 if self.dup_check {
1454 self.dup_ids();
1455 }
1456 self.dnd_pass_end(); self.wp.ctx.hit_cur ^= 1;
1459 self.uiw_publish_hits(); }
1461
1462 fn dup_ids(&mut self) {
1465 let c = &self.wp.ctx;
1466 let mut ids: Vec<(u32, i32, i32, i32, i32)> =
1468 c.hits[c.hit_cur].iter().filter(|h| h.id != 0 && !h.continuation).map(|h| (h.id, h.x0, h.y0, h.x1, h.y1)).collect();
1469 ids.sort_unstable();
1470 ids.dedup();
1471 let dups: Vec<((u32, i32, i32, i32, i32), (u32, i32, i32, i32, i32))> = ids.windows(2).filter(|w| w[0].0 == w[1].0).map(|w| (w[0], w[1])).collect();
1472 for (a, b) in dups {
1473 if self.dup_warned.insert(a.0) {
1474 let name = self.id_name(a.0).map_or(String::new(), |n| alloc::format!(" (\"{}\")", n));
1475 self.warn(&alloc::format!("widget id {:#X}{} used twice in one pass (at {},{} and {},{})", a.0, name, a.1, a.2, b.1, b.2));
1476 }
1477 }
1478 }
1479
1480 pub fn repass(&mut self) {
1482 self.repass = true;
1483 }
1484
1485 pub fn modal(&mut self, layer: u32) {
1487 if layer as i32 > self.wp.ctx.modal_cur as i32 {
1488 self.wp.ctx.modal_cur = layer;
1489 }
1490 }
1491
1492 pub fn modal_active(&self) -> u32 {
1494 self.wp.ctx.modal_prev
1495 }
1496
1497 pub fn disabled_push(&mut self) {
1499 self.wp.ctx.disabled_depth += 1;
1500 }
1501
1502 pub fn disabled_pop(&mut self) {
1503 if self.wp.ctx.disabled_depth != 0 {
1504 self.wp.ctx.disabled_depth -= 1;
1505 }
1506 }
1507
1508 pub fn warn(&mut self, msg: &str) {
1510 self.warns = self.warns.wrapping_add(1);
1511 let s = alloc::format!("UIWARN f{}: {}\n", self.frame, msg);
1512 plat::write_stderr(s.as_bytes());
1513 plat::log_quiet(plat::LOG_WARN, "ui", msg); }
1515}