1use alloc::string::String;
29use alloc::vec::Vec;
30
31use super::consts::*;
32use super::keys::*;
33use super::Ui;
34use plat::Event;
35
36pub(crate) const KEYQ_MAX: usize = 1024;
44pub(crate) const TEXTQ_MAX: usize = 4096;
47pub(crate) const PTS_MAX: usize = 1 << 16;
49pub(crate) const CMDQ_MAX: usize = 1 << 16;
51pub(crate) const TIMERS_MAX: usize = 4096;
53pub(crate) const UICLIP_MAX: usize = 1024;
55pub(crate) const HITS_MAX: usize = 1 << 16;
57pub(crate) const SEMS_MAX: usize = 1 << 16;
59pub(crate) const SEMTEXT_MAX: usize = 4 << 20;
60
61pub(crate) const LIM_KEYS: usize = 0;
64pub(crate) const LIM_CMDS: usize = 1;
65pub(crate) const LIM_TIMERS: usize = 2;
66pub(crate) const LIM_POINTS: usize = 3;
67pub(crate) const LIM_HITS: usize = 4;
68pub(crate) const LIM_ZONES: usize = 5;
69pub(crate) const LIM_SEMS: usize = 6;
70pub(crate) const LIM_SEMTEXT: usize = 7;
71pub(crate) const LIM_CLIP: usize = 8;
72pub(crate) const LIM_WINNEST: usize = 9;
73pub(crate) const LIM_POPUP_SLOTS: usize = 10;
74pub(crate) const LIM_POPUPS: usize = 11;
75pub(crate) const LIM_FLYOUTS: usize = 12;
76pub(crate) const LIM_MENU_ROWS: usize = 13;
77pub(crate) const LIM_MENUBAR: usize = 14;
78pub(crate) const LIM_SCROLLS: usize = 15;
79pub(crate) const LIM_LISTS: usize = 16;
80pub(crate) const LIM_EXPANDERS: usize = 17;
81pub(crate) const LIM_WRAP: usize = 18;
82pub(crate) const LIM_N: usize = 19;
83const LIM_TEXT: [&str; LIM_N] = [
84 "more than 1024 keys in one pass: the rest dropped",
85 "more than 65536 commands waiting: the rest dropped",
86 "more than 4096 timers at once: new ones dropped",
87 "more than 65536 pointer positions in one pass: the rest dropped",
88 "more than 65536 hit boxes in one window pass: the rest cannot be hit",
89 "more than 65536 drop targets in one window pass: the rest take no drops",
90 "more than 65536 widget records in one pass: the rest missing (dump, expect, screen readers)",
91 "more than 4 MiB of record labels in one pass: the rest without labels",
92 "clip_push nested more than 1024 deep: deeper levels clip no hit boxes",
93 "window_begin nested more than 16 deep: deeper windows not shown",
94 "more than 4 popups outside their windows: the rest drawn clipped to the window",
95 "popups nested more than 6 deep: the deepest one replaced",
96 "flyouts nested more than 8 deep: deeper ones not shown",
97 "a menu with more than 250 rows: the rest not shown",
98 "a menu bar with more than 16 menus: the rest not shown",
99 "scroll views nested more than 8 deep: deeper ones share one record",
100 "lists nested more than 4 deep: deeper ones share one record",
101 "expanders nested more than 16 deep: deeper ones lose their content box",
102 "a wrapped text of more than 65536 lines: the rest not shown",
103];
104
105#[derive(Clone, Copy, Default)]
107pub(crate) struct Hit {
108 pub(crate) id: u32,
109 pub(crate) continuation: bool,
111 pub(crate) flags: u16,
113 pub(crate) grab: u8,
115 pub(crate) layer: u8,
116 #[allow(dead_code)]
117 pub(crate) kind: u8,
118 pub(crate) group: u32,
120 pub(crate) pane: u32,
121 pub(crate) x0: i32,
122 pub(crate) y0: i32,
123 pub(crate) x1: i32,
124 pub(crate) y1: i32,
125}
126
127#[derive(Clone, Default)]
129pub struct Sem {
130 pub id: u32,
131 pub kind: u16,
132 pub state: u16,
134 pub rect: [i32; 4],
136 pub label: Option<String>,
137 pub value: i32,
138 pub max: i32,
139 pub min: i32,
141 pub layer: u8,
142 pub depth: u8,
143 pub win: u8,
145}
146
147#[derive(Clone, Copy, Default)]
149pub struct Slot {
150 pub seen: u32,
151 pub afrom: u16,
152 pub ato: u16,
153 pub at0: u32,
154 pub adur: u32,
155 pub measw: i32,
156 pub meash: i32,
157 pub fixed: i32,
158 pub wsum: i32,
159 pub scrollx: i32,
160 pub scrolly: i32,
161 pub aux: i32,
162}
163
164pub fn ease(curve: u32, t: i32) -> i32 {
167 let t = t.clamp(0, 256);
168 let u = 256 - t;
169 match curve {
170 EASE_LINEAR => t,
171 EASE_IN => (t * t) >> 8,
172 EASE_IN_OUT => {
173 if t < 128 {
175 ((4 * t * t >> 8) * t) >> 8
176 } else {
177 let v = 2 * u;
178 256 - (((v * v >> 8) * v) >> 9)
179 }
180 }
181 EASE_OUT_CUBIC => 256 - (((u * u >> 8) * u) >> 8),
182 EASE_OUT_BACK => {
183 let x = t - 256;
185 let x2 = x * x >> 8;
186 let x3 = x2 * x >> 8;
187 256 + (x3 * 691 >> 8) + (x2 * 436 >> 8)
188 }
189 _ => 256 - ((u * u) >> 8),
191 }
192}
193
194pub(crate) struct W {
196 pub(crate) last_rect: [i32; 4],
197 pub(crate) uiclip: [i32; 4],
199 pub(crate) uiclip_stack: Vec<[i32; 4]>,
200 pub(crate) uiclip_over: u32,
202 pub(crate) hits: [Vec<Hit>; 2],
204 pub(crate) zones: [Vec<super::dnd::Zone>; 2],
206 pub(crate) pans: [Vec<super::touch::PanView>; 2],
209 pub(crate) grabs: [Vec<[i32; 4]>; 2],
210 pub(crate) keyq: Vec<u32>,
211 pub(crate) textq: Vec<u32>,
212 pub(crate) hit_cur: usize,
213 pub(crate) mx: i32,
214 pub(crate) my: i32,
215 pub(crate) mbtn: u32,
216 pub(crate) pressed: u32,
217 pub(crate) released: u32,
218 pub(crate) wheel_x: i32,
219 pub(crate) wheel_y: i32,
220 pub(crate) moved: bool,
221 pub(crate) press_x: i32,
222 pub(crate) press_y: i32,
223 pub(crate) press_ms: u32, pub(crate) hot: u32,
225 pub(crate) hot_flags: u32,
226 pub(crate) active: u32,
227 pub(crate) active_seen: bool,
228 pub(crate) ractive: u32,
229 pub(crate) focus: u32,
230 pub(crate) focus_visible: bool,
231 pub(crate) press_used: bool,
232 pub(crate) rpress_used: bool,
233 pub(crate) last_click_id: u32,
234 pub(crate) last_click_ms: u32,
235 pub(crate) last_state: u32,
236 pub(crate) modal_cur: u32,
237 pub(crate) modal_prev: u32,
238 pub(crate) disabled_depth: u32,
239 pub(crate) cmd_layer_ui: u32,
240 pub(crate) kb: super::kbnav::W,
242}
243
244impl W {
245 pub(crate) fn new() -> W {
247 W {
248 last_rect: [0; 4],
249 uiclip: [0; 4],
250 uiclip_stack: Vec::new(),
251 uiclip_over: 0,
252 hits: [Vec::new(), Vec::new()],
253 zones: [Vec::new(), Vec::new()],
254 pans: [Vec::new(), Vec::new()],
255 grabs: [Vec::new(), Vec::new()],
256 keyq: Vec::new(),
257 textq: Vec::new(),
258 hit_cur: 0,
259 mx: -1,
260 my: -1,
261 mbtn: 0,
262 pressed: 0,
263 released: 0,
264 wheel_x: 0,
265 wheel_y: 0,
266 moved: false,
267 press_x: 0,
268 press_y: 0,
269 press_ms: 0,
270 hot: 0,
271 hot_flags: 0,
272 active: 0,
273 active_seen: false,
274 ractive: 0,
275 focus: 0,
276 focus_visible: false,
277 press_used: false,
278 rpress_used: false,
279 last_click_id: 0,
280 last_click_ms: 0,
281 last_state: 0,
282 modal_cur: 0,
283 modal_prev: 0,
284 disabled_depth: 0,
285 cmd_layer_ui: 0,
286 kb: Default::default(),
287 }
288 }
289}
290
291pub(crate) fn hit_find_list(list: &[Hit], modal_layer: u32, x: i32, y: i32) -> (u32, u32) {
295 if x == -1 && y == -1 {
298 return (0, 0);
299 }
300 let mut layer = L_COUNT as i32 - 1;
301 while layer >= modal_layer as i32 {
302 for h in list.iter().rev() {
303 if h.layer as i32 != layer || x < h.x0 || x >= h.x1 || y < h.y0 || y >= h.y1 {
304 continue;
305 }
306 let flags = h.flags as u32;
307 let id = if flags & (HB_BLOCK | HB_DISABLED) != 0 { 0 } else { h.id };
308 return (id, flags);
309 }
310 layer -= 1;
311 }
312 (0, 0)
313}
314
315impl Ui {
316 pub fn now(&self) -> u32 {
320 self.now_ms
321 }
322
323 pub fn mods(&self) -> u32 {
325 self.mods
326 }
327
328 pub fn mouse(&self) -> (i32, i32) {
330 (self.wp.ctx.mx, self.wp.ctx.my)
331 }
332
333 pub fn pen(&self) -> (u32, u32) {
335 (self.mpress, self.mptype)
336 }
337
338 pub fn focus(&mut self, id: u32) {
341 self.focus_req_old = self.wp.ctx.focus;
342 self.focus_req = id;
343 self.focus_req_win = self.uiw_cur;
344 self.focus_req_frame = self.frame;
345 self.wp.ctx.focus = id;
346 self.wp.ctx.focus_visible = true;
347 self.repass = true;
348 }
349
350 pub fn focus_quiet(&mut self, id: u32) {
353 self.wp.ctx.focus = id;
354 self.wp.ctx.focus_visible = false;
355 self.repass = true;
356 }
357
358 pub(crate) fn focus_req_expire(&mut self) {
360 if self.focus_req_frame < self.frame {
361 self.focus_req = 0;
362 }
363 }
364
365 pub fn focused(&self) -> u32 {
367 self.wp.ctx.focus
368 }
369
370 fn key_find(&self, vk: u32) -> Option<usize> {
372 self.wp.ctx.keyq.iter().position(|&k| (k as i32) >= 0 && k & 0xFFFF == vk)
373 }
374
375 pub fn key(&mut self, vk: u32) -> bool {
377 let k = self.key_find(vk);
378 match self.text_key_filter(k) {
379 Some(i) => {
380 self.wp.ctx.keyq[i] |= 0x8000_0000;
381 true
382 }
383 None => false,
384 }
385 }
386
387 pub fn capture_shortcut(&mut self, vk: u32, mods: u32) -> bool {
392 if let Some(i) = self.wp.ctx.keyq.iter().position(|&k| {
393 (k as i32) >= 0 && k & 0xffff == vk && (k >> 16) == mods
394 }) {
395 self.wp.ctx.keyq[i] |= 0x8000_0000;
396 true
397 } else {
398 false
399 }
400 }
401
402 pub fn key_peek(&mut self, vk: u32) -> bool {
404 let k = self.key_find(vk);
405 self.text_key_filter(k).is_some()
406 }
407
408 pub fn key_down(&self, vk: u32) -> bool {
410 self.keydown[(vk & 255) as usize] != 0
411 }
412
413 pub fn key_first(&self) -> u32 {
415 self.wp.ctx.keyq.iter().copied().find(|&k| (k as i32) >= 0).unwrap_or(0)
416 }
417
418 pub fn text_take(&mut self, cap: usize) -> Vec<u32> {
421 let c = &mut self.wp.ctx;
422 let n = c.textq.len().min(cap);
423 let v: Vec<u32> = c.textq.drain(..n).collect();
424 if n > 0 && !c.textq.is_empty() {
425 let rest = core::mem::take(&mut c.textq);
426 self.text_defer(rest);
427 }
428 self.wp.ctx.textq.clear(); v
430 }
431
432 fn text_defer(&mut self, rest: Vec<u32>) {
435 let w = self.uiw_cur;
436 match self.text_more.first_mut() {
437 Some((m, q)) if *m == w => drop(q.splice(0..0, rest)),
438 _ => self.text_more.insert(0, (w, rest)),
439 }
440 }
441
442 fn text_push(&mut self, cp: u32) {
445 if self.text_more.is_empty() && self.wp.ctx.textq.len() < TEXTQ_MAX {
446 self.wp.ctx.textq.push(cp);
447 return;
448 }
449 let w = self.uiw_cur;
450 match self.text_more.last_mut() {
451 Some((m, q)) if *m == w => q.push(cp),
452 _ => self.text_more.push((w, alloc::vec![cp])),
453 }
454 }
455
456 pub(crate) fn text_refill(&mut self) -> bool {
459 while let Some((w, q)) = self.text_more.first() {
460 let w = *w;
461 if q.is_empty() || (w != 0 && !self.uiw_drawable(w)) {
462 self.text_more.remove(0);
463 continue;
464 }
465 let cur = self.uiw_cur;
466 self.uiw_use(w);
467 let q = &mut self.text_more[0].1;
468 let n = q.len().min(TEXTQ_MAX.saturating_sub(self.wp.ctx.textq.len()));
469 self.wp.ctx.textq.extend(q.drain(..n));
470 if q.is_empty() {
471 self.text_more.remove(0);
472 }
473 self.m.win.edges |= 1 << (w & (WIN_MAX as u32 - 1)); self.uiw_fwd_arm(w);
475 self.uiw_use(cur);
476 return true;
477 }
478 false
479 }
480
481 #[allow(dead_code)] pub(crate) fn keys_take_rest(&mut self, cap: usize) -> Vec<u32> {
485 let mut out = Vec::new();
486 for k in self.wp.ctx.keyq.iter_mut() {
487 if (*k as i32) < 0 {
488 continue;
489 }
490 if out.len() >= cap {
491 break;
492 }
493 out.push(*k);
494 *k |= 0x8000_0000;
495 }
496 out
497 }
498
499 pub(crate) fn keys_inject(&mut self, keys: &[u32]) {
502 for &k in keys {
503 self.key_push(k & 0x7FFF_FFFF);
504 }
505 }
506
507 fn key_push(&mut self, k: u32) {
509 if self.wp.ctx.keyq.len() < KEYQ_MAX {
510 self.wp.ctx.keyq.push(k);
511 } else {
512 self.limit_hit(LIM_KEYS);
513 }
514 }
515
516 pub(crate) fn limit_hit(&mut self, lim: usize) {
519 let n = self.limits[lim];
520 self.limits[lim] = n.saturating_add(1);
521 if n == 0 {
522 self.warn(LIM_TEXT[lim]);
523 }
524 }
525
526 #[allow(dead_code)] pub(crate) fn text_inject(&mut self, cps: &[u32]) {
529 let n = TEXTQ_MAX.saturating_sub(self.wp.ctx.textq.len()).min(cps.len());
531 self.wp.ctx.textq.extend_from_slice(&cps[..n]);
532 if n < cps.len() {
533 self.text_defer(cps[n..].to_vec());
534 }
535 }
536
537 pub(crate) fn input_event(&mut self, ev: &Event) -> u32 {
541 let mut e = *ev;
542 self.rtl_input(&mut e); let ev = &e;
544 self.mods = ev.mods as u32;
545 match ev.typ {
546 plat::E_MOVE => {
547 self.wp.ctx.mx = ev.x;
548 self.wp.ctx.my = ev.y;
549 self.pen_decode(ev.data);
550 self.pts_add();
551 self.wp.ctx.moved = true;
552 self.dnd_moved(); 0
554 }
555 plat::E_DOWN => {
556 if ev.x != -1 {
557 self.wp.ctx.mx = ev.x;
558 self.wp.ctx.my = ev.y;
559 }
560 self.pen_decode(ev.data);
561 let b = (ev.btn & 31) as u32;
562 let c = &mut self.wp.ctx;
563 c.mbtn |= 1 << b;
564 c.pressed |= 1 << b;
565 c.press_x = c.mx;
566 c.press_y = c.my;
567 self.mpts.clear();
568 self.mprs.clear();
569 self.pts_add();
570 1
571 }
572 plat::E_UP => {
573 self.dnd_input(ev); let c = &mut self.wp.ctx;
575 if ev.x != -1 {
576 c.mx = ev.x;
577 c.my = ev.y;
578 }
579 let b = (ev.btn & 31) as u32;
580 c.mbtn &= !(1 << b);
581 c.released |= 1 << b;
582 1
583 }
584 plat::E_WHEEL => {
585 let c = &mut self.wp.ctx;
586 c.wheel_y = c.wheel_y.wrapping_add(ev.data as u16 as i16 as i32);
587 c.wheel_x = c.wheel_x.wrapping_add((ev.data as i32) >> 16);
588 1
589 }
590 plat::E_KEY_DOWN => {
591 if self.dnd_input(ev) {
592 return 1; }
594 let vk = ev.data & 0xFFFF;
595 self.keydown[(vk & 255) as usize] = 1;
596 self.key_push((vk | self.mods << 16) & 0x7FFF_FFFF);
597 1
598 }
599 plat::E_KEY_UP => {
600 let k = ev.data & 255;
601 self.keydown[k as usize] = 0;
602 (k == VK_MENU) as u32
604 }
605 plat::E_TEXT => {
606 self.text_push(ev.data);
607 2 }
609 plat::E_PREEDIT => 1,
610 plat::E_IME_COMMIT => {
611 if let Some(text) = plat::ime_commit_take(ev.data) {
612 self.hr_commit(&text); for c in text.chars() {
614 self.text_push(c as u32);
615 }
616 }
617 1 }
619 plat::E_LEAVE => {
620 let c = &mut self.wp.ctx;
621 c.mx = -1;
622 c.my = -1;
623 c.moved = true;
624 0
625 }
626 plat::E_FOCUS_OUT => {
627 self.wp.ctx.mbtn = 0;
628 self.keydown = [0; 256];
629 1
630 }
631 _ => 0,
632 }
633 }
634
635 fn pen_decode(&mut self, d: u32) {
637 self.mpress = d & plat::PD_PRESSURE;
638 self.mptype = (d >> plat::PD_TYPE_SHIFT) & 3;
639 self.mtilt[0] = ((d << (32 - plat::PD_TILTY_SHIFT)) as i32) >> 25;
640 self.mtilt[1] = (d as i32) >> plat::PD_TILTY_SHIFT;
641 if self.mptype == PTR_TOUCH {
642 self.mtilt = [0, 0]; }
644 }
645
646 fn pts_add(&mut self) {
648 if self.uiw_cur != self.mpts_win {
649 self.mpts_win = self.uiw_cur;
650 self.mpts.clear();
651 self.mprs.clear();
652 }
653 if self.mpts.len() < PTS_MAX {
654 self.mpts.push([self.wp.ctx.mx, self.wp.ctx.my]);
655 self.mprs.push(self.mpress);
656 } else {
657 self.limit_hit(LIM_POINTS);
658 }
659 }
660
661 pub(crate) fn input_clear_edges(&mut self) {
663 let c = &mut self.wp.ctx;
664 c.pressed = 0;
665 c.released = 0;
666 c.wheel_x = 0;
667 c.wheel_y = 0;
668 c.keyq.clear();
669 c.textq.clear();
670 c.moved = false;
671 if self.uiw_cur == self.mpts_win && !self.mpts.is_empty() {
673 let n = self.mpts.len();
674 let (p, r) = (self.mpts[n - 1], self.mprs[n - 1]);
675 self.mpts.clear();
676 self.mprs.clear();
677 self.mpts.push(p);
678 self.mprs.push(r);
679 }
680 }
681
682 pub(crate) fn set_time(&mut self, ms: u32) {
684 self.now_ms = ms;
685 if self.headless {
686 plat::task::clock_set(Some(ms as u64 * 1000)); }
688 }
689
690 pub(crate) fn hot_find(&self, x: i32, y: i32) -> (u32, u32) {
695 if self.uiw_inert_now() {
696 return (0, 0);
697 }
698 let c = &self.wp.ctx;
699 hit_find_list(&c.hits[c.hit_cur ^ 1], c.modal_prev, x, y)
700 }
701
702 pub fn hit_flags_at(&mut self, x: i32, y: i32) -> u32 {
705 self.hit_flags_win(0, x, y)
706 }
707
708 pub(crate) fn hot_update(&mut self) -> bool {
710 let (id, flags) = self.hot_find(self.wp.ctx.mx, self.wp.ctx.my);
711 if self.wp.ctx.active == 0 {
713 let mut shape = (flags >> HB_CURSOR_SHIFT) & 15;
714 if flags & HB_DRAG != 0 {
715 shape = 0;
716 }
717 let s = self.cursor_resolve(shape);
718 plat::set_cursor(s);
719 }
720 let c = &mut self.wp.ctx;
721 let changed = id != c.hot;
722 c.hot = id;
723 c.hot_flags = flags;
724 changed
725 }
726
727 pub(crate) fn needs_move_pass(&mut self) -> bool {
729 self.hot_update() || self.wp.ctx.active != 0 || self.wp.ctx.hot_flags & HB_MOVES != 0 || self.dnd_follows_pointer()
730 }
731
732 pub fn hit(&mut self, id: u32, hbflags: u32) {
734 let c = &mut self.wp.ctx;
735 let (group, pane, notab) = c.kb.hit_keys(hbflags);
736 if c.hits[c.hit_cur].len() >= HITS_MAX {
737 self.limit_hit(LIM_HITS);
738 return;
739 }
740 let c = &mut self.wp.ctx;
741 let list = &mut c.hits[c.hit_cur];
742 let r = c.last_rect;
743 let k = c.uiclip;
744 list.push(Hit {
745 id,
746 continuation: false,
747 flags: (hbflags | notab) as u16,
748 grab: super::touch::hit_grab(hbflags),
749 layer: c.cmd_layer_ui as u8,
750 kind: 0,
751 group,
752 pane,
753 x0: r[0].max(k[0]),
754 y0: r[1].max(k[1]),
755 x1: r[0].wrapping_add(r[2]).min(k[2]),
756 y1: r[1].wrapping_add(r[3]).min(k[3]),
757 });
758 }
759
760 pub fn behave(&mut self, id: u32, hbflags: u32) -> u32 {
763 if self.focus_req == id && id != 0 {
765 self.uiw_focus_arrived();
766 }
767 let mut flags = hbflags;
768 if self.wp.ctx.disabled_depth != 0 {
770 flags |= HB_DISABLED;
771 }
772 self.hit(id, flags);
773 self.wp.ctx.last_state = 0;
774 if flags & HB_DISABLED != 0 {
775 self.wp.ctx.last_state = SS_DISABLED as u32;
776 return 0;
777 }
778 let now = self.now_ms;
779 let lim = self.dp(4);
780 let dbl_ms = self.dbl_ms; let mut ev = 0;
782 let c = &mut self.wp.ctx;
783 if c.hot == id {
784 ev |= EV_HOVER;
785 if c.pressed & (1 << BTN_LEFT) != 0 && !c.press_used {
786 c.press_used = true;
787 c.active = id;
788 c.press_ms = now;
789 if flags & HB_FOCUS != 0 {
790 c.focus = id;
791 c.focus_visible = false;
792 }
793 if c.last_click_id == id && now.wrapping_sub(c.last_click_ms) <= dbl_ms {
794 ev |= EV_DOUBLE;
795 c.last_click_id = 0;
796 } else {
797 c.last_click_id = id;
798 c.last_click_ms = now;
799 }
800 }
801 if c.pressed & (1 << BTN_RIGHT) != 0 && !c.rpress_used {
802 c.rpress_used = true;
803 c.ractive = id;
804 }
805 }
806 if c.active == id {
808 c.active_seen = true;
809 if c.mbtn & (1 << BTN_LEFT) != 0 {
810 ev |= EV_HELD;
811 let dx = c.mx.wrapping_sub(c.press_x).wrapping_abs();
813 let dy = c.my.wrapping_sub(c.press_y).wrapping_abs();
814 if dx > lim || dy > lim {
815 ev |= EV_DRAG;
816 }
817 } else {
818 c.active = 0;
819 ev |= EV_COMMIT;
820 if ev & EV_HOVER != 0 {
821 ev |= EV_ACT;
822 }
823 }
824 }
825 if c.ractive == id && c.mbtn & (1 << BTN_RIGHT) == 0 {
826 c.ractive = 0;
827 if ev & EV_HOVER != 0 {
828 ev |= EV_RCLICK;
829 }
830 }
831 if c.focus == id {
832 ev |= EV_FOCUS;
833 if flags & (HB_TEXT | HB_OWNKEYS) == 0 && flags & HB_FOCUS != 0 && (self.key(VK_SPACE) || flags & HB_SPACE == 0 && self.key(VK_RETURN)) {
834 ev |= EV_ACT;
835 self.wp.ctx.focus_visible = true;
836 }
837 }
838 if self.m.kbnav.ctx == id && id != 0 {
841 ev |= self.kb_ctx_take();
842 }
843 if self.m.a11y.pending() && flags & HB_TEXT == 0 && self.a11y_click(id) {
845 ev |= EV_ACT;
846 }
847 if ev & (EV_ACT | EV_COMMIT | EV_DOUBLE | EV_RCLICK) != 0 {
850 self.repass = true;
851 }
852 let mut st = 0;
854 if ev & EV_HOVER != 0 {
855 st |= SS_HOVER;
856 }
857 if ev & EV_HELD != 0 {
858 st |= SS_PRESSED;
859 }
860 if ev & EV_FOCUS != 0 {
861 st |= SS_FOCUSED;
862 }
863 self.wp.ctx.last_state = st as u32;
864 ev
865 }
866
867 pub fn hit_rect(&mut self, id: u32, x: i32, y: i32, w: i32, h: i32, hbflags: u32) -> u32 {
869 self.wp.ctx.last_rect = [x, y, w, h];
870 self.behave(id, hbflags)
871 }
872
873 pub fn focus_ring(&self, ev: u32) -> bool {
876 ev & EV_FOCUS != 0 && self.wp.ctx.focus_visible
877 }
878
879 pub fn slot(&mut self, id: u32) -> &mut Slot {
883 let frame = self.frame;
884 let s = self.slots.entry(id).or_default();
885 s.seen = frame;
886 s
887 }
888
889 pub fn anim(&mut self, id: u32, target: u32, ms: u32) -> u32 {
892 self.anim_ex(id, target, ms, EASE_OUT)
893 }
894
895 pub fn anim_ex(&mut self, id: u32, target: u32, ms: u32, curve: u32) -> u32 {
898 let now = self.now_ms;
899 let off = self.anim_off;
900 let s = self.slot(id | 0x4000_0000);
902 s.aux = curve as i32;
903 fn value(s: &Slot, now: u32) -> u32 {
905 let to = s.ato as u32;
906 let dur = s.adur;
907 if dur == 0 {
908 return to;
909 }
910 let el = now.wrapping_sub(s.at0);
911 if el >= dur {
912 return to;
913 }
914 let t = ((el << 8) / dur) as i32;
915 let e = ease(s.aux as u32, t);
916 let from = s.afrom as i32;
917 let d = ((to as i32 - from).wrapping_mul(e)) >> 8;
918 (from + d).max(0) as u32
919 }
920 if off || s.adur == 0 {
921 s.afrom = target as u16;
923 s.ato = target as u16;
924 s.adur = if ms == 0 { 1 } else { ms };
925 return target;
926 }
927 if s.ato as u32 != target {
928 let v = value(s, now);
930 s.afrom = v as u16;
931 s.ato = target as u16;
932 s.at0 = now;
933 s.adur = ms;
934 }
935 let v = value(s, now);
936 if v != s.ato as u32 {
937 self.animating = true;
938 }
939 v
940 }
941
942 pub fn anim_color(&mut self, id: u32, argb: u32, ms: u32, curve: u32) -> u32 {
945 let now = self.now_ms;
946 let off = self.anim_off;
947 let s = self.slot(id | 0x2000_0000);
949 let at = |s: &Slot| -> u32 {
950 let el = now.wrapping_sub(s.at0);
951 if s.adur == 0 || el >= s.adur {
952 return s.meash as u32;
953 }
954 let t = ((el << 8) / s.adur) as i32;
955 super::theme::color_mix(s.measw as u32, s.meash as u32, ease(s.aux as u32, t).clamp(0, 256))
956 };
957 if off || s.adur == 0 {
958 s.measw = argb as i32;
959 s.meash = argb as i32;
960 s.adur = ms.max(1);
961 s.at0 = now;
962 return argb;
963 }
964 if s.meash as u32 != argb {
965 let c = at(s);
966 s.measw = c as i32;
967 s.meash = argb as i32;
968 s.at0 = now;
969 s.adur = ms.max(1);
970 s.aux = curve as i32;
971 }
972 let c = at(s);
973 if c != argb {
974 self.animating = true;
975 }
976 c
977 }
978
979 pub fn cmd_run(&mut self, id: u32, arg: u32) {
983 if self.cmdq.len() < CMDQ_MAX {
984 self.cmdq.push_back((id, arg));
985 } else {
986 self.limit_hit(LIM_CMDS);
987 }
988 }
989
990 pub(crate) fn cmd_take(&mut self) -> Option<(u32, u32)> {
992 self.cmdq.pop_front()
993 }
994
995 pub fn timer(&mut self, id: u32, ms: u32) {
998 let deadline = self.now_ms.wrapping_add(ms);
999 if let Some(i) = self.timers.iter().position(|t| t.0 == id) {
1001 self.timers.remove(i);
1002 } else if self.timers.len() >= TIMERS_MAX {
1003 self.limit_hit(LIM_TIMERS);
1004 return;
1005 }
1006 self.timers.push((id, deadline));
1007 }
1008
1009 pub(crate) fn timer_due(&mut self) -> (u32, u32) {
1012 match self.timer_scan() {
1013 (Some(i), next) => (self.timers.remove(i).0, next),
1014 (None, next) => (u32::MAX, next),
1015 }
1016 }
1017
1018 pub(crate) fn timer_scan(&self) -> (Option<usize>, u32) {
1021 let now = self.now_ms;
1022 let (mut due, mut due_at) = (None, 0i32);
1023 let (mut next, mut left) = (u32::MAX, u32::MAX);
1024 for (i, &(_, d)) in self.timers.iter().enumerate() {
1025 let rel = d.wrapping_sub(now) as i32; if rel <= 0 {
1027 if due.is_none() || rel < due_at {
1028 due = Some(i);
1029 due_at = rel;
1030 }
1031 } else if (rel as u32) < left {
1032 left = rel as u32;
1033 next = d;
1034 }
1035 }
1036 (due, next)
1037 }
1038
1039 pub fn last_reset(&mut self) {
1044 self.wp.ctx.last_rect = [0; 4];
1045 self.wp.ctx.last_state = 0;
1046 }
1047
1048 pub fn last_set(&mut self, rect: [i32; 4], state: u32) {
1051 self.wp.ctx.last_rect = rect;
1052 self.wp.ctx.last_state = state;
1053 }
1054
1055 pub fn last_state(&self) -> u32 {
1057 self.wp.ctx.last_state
1058 }
1059
1060 pub fn mbtn(&self) -> u32 {
1063 self.wp.ctx.mbtn
1064 }
1065 pub fn pressed(&self) -> u32 {
1066 self.wp.ctx.pressed
1067 }
1068 pub fn released(&self) -> u32 {
1069 self.wp.ctx.released
1070 }
1071
1072 #[cfg_attr(jfgui_dev, track_caller)]
1075 pub fn sem(&mut self, kind: u16, id: u32, label: Option<&str>, value: i32) -> &mut Sem {
1076 if self.sems.len() >= SEMS_MAX {
1077 self.limit_hit(LIM_SEMS);
1078 return &mut self.sem_dummy;
1079 }
1080 let c = &self.wp.ctx;
1081 let mut r = Sem {
1082 id,
1083 kind,
1084 state: c.last_state as u16,
1085 rect: self.phys_rect(c.last_rect), label: None,
1087 value,
1088 max: 0,
1089 min: 0,
1090 layer: c.cmd_layer_ui as u8,
1091 depth: self.wp.lay.boxes.len() as u8,
1092 win: self.uiw_cur as u8,
1093 };
1094 if let Some(l) = label {
1095 let l = l.split('\0').next().unwrap_or("");
1097 let n = self.semtext_len + l.len() + 1;
1098 if n <= SEMTEXT_MAX {
1099 self.semtext_len = n;
1100 r.label = Some(String::from(l));
1101 } else {
1102 self.limit_hit(LIM_SEMTEXT);
1103 }
1104 }
1105 self.sems.push(r);
1106 let n = self.sems.len();
1107 self.src_sem(n - 1); &mut self.sems[n - 1]
1109 }
1110
1111 pub fn clip_push(&mut self, x: i32, y: i32, w: i32, h: i32) {
1115 self.draw_clip(x, y, w, h);
1116 let c = &mut self.wp.ctx;
1117 if c.uiclip_stack.len() >= UICLIP_MAX {
1118 c.uiclip_over += 1;
1119 self.limit_hit(LIM_CLIP);
1120 return;
1121 }
1122 c.uiclip_stack.push(c.uiclip);
1123 let k = &mut c.uiclip;
1124 k[0] = x.max(k[0]);
1125 k[1] = y.max(k[1]);
1126 k[2] = x.wrapping_add(w).min(k[2]);
1127 k[3] = y.wrapping_add(h).min(k[3]);
1128 }
1129
1130 pub fn clip_pop(&mut self) {
1132 self.draw_unclip();
1133 let c = &mut self.wp.ctx;
1134 if c.uiclip_over != 0 {
1135 c.uiclip_over -= 1; } else if let Some(k) = c.uiclip_stack.pop() {
1137 c.uiclip = k;
1138 }
1139 }
1140
1141 pub fn layer_set(&mut self, layer: u32) -> u32 {
1143 let prev = self.wp.ctx.cmd_layer_ui;
1144 self.wp.ctx.cmd_layer_ui = layer & 3;
1145 self.cmd_layer(layer);
1146 prev
1147 }
1148
1149 pub fn begin_pass(&mut self, w: i32, h: i32) {
1157 self.frame_begin();
1158 self.uiw_pass_begin(w, h);
1159 }
1160
1161 pub fn end_pass(&mut self) -> bool {
1163 self.uiw_pass_end();
1164 self.frame_end()
1165 }
1166
1167 pub(crate) fn frame_begin(&mut self) {
1169 self.repass = false;
1170 self.sems.clear();
1171 self.semtext_len = 0;
1172 self.animating = false;
1173 self.frame = self.frame.wrapping_add(1);
1174 if self.m.ins.on {
1175 self.m.ins.boxes.clear(); }
1177 self.src_clear(); self.rtl_update();
1179 self.ids_frame_begin();
1180 self.fonts_frame(); self.uiw_frame_begin();
1182 }
1183
1184 pub(crate) fn frame_end(&mut self) -> bool {
1186 self.uiw_frame_end();
1187 #[cfg(target_os = "android")]
1188 self.ime_frame_end();
1189 self.repass
1190 }
1191
1192 pub(crate) fn uiw_pass_begin(&mut self, w: i32, h: i32) {
1195 self.cmd_reset();
1196 let c = &mut self.wp.ctx;
1197 c.cmd_layer_ui = 0;
1198 c.press_used = false;
1199 c.rpress_used = false;
1200 c.active_seen = false;
1201 c.modal_cur = 0;
1202 c.disabled_depth = 0;
1203 c.kb.pass_begin();
1204 c.uiclip_stack.clear();
1205 c.uiclip_over = 0;
1206 c.uiclip = [0, 0, w, h];
1207 let cur = c.hit_cur;
1208 c.hits[cur].clear();
1209 self.wp.chrome.cap_n = 0; self.wp.chrome.tb_rect = None;
1211 let c = &mut self.wp.ctx;
1212 c.zones[cur].clear();
1213 c.pans[cur].clear();
1214 c.grabs[cur].clear();
1215 self.hot_update();
1216 self.dnd_pass_begin(); let st = self.style;
1219 st.window_bg(self, w, h);
1220 let b = self.uiw_border();
1222 self.layout_root(b, b, w - b - b, h - b - b);
1223 }
1224
1225 pub(crate) fn uiw_pass_end(&mut self) {
1228 self.uiw_fwd_take(); self.accel_dispatch(); self.layout_end_root();
1231 if !self.wp.ctx.active_seen {
1233 self.wp.ctx.active = 0;
1234 }
1235 self.kb_nav();
1238 if self.key(VK_ESCAPE) {
1240 self.uiw_escape();
1241 }
1242 let c = &mut self.wp.ctx;
1243 if c.modal_cur != c.modal_prev {
1244 c.modal_prev = c.modal_cur;
1245 self.repass = true;
1246 }
1247 if self.dup_check {
1248 self.dup_ids();
1249 }
1250 self.dnd_pass_end(); self.wp.ctx.hit_cur ^= 1;
1253 self.uiw_publish_hits(); }
1255
1256 fn dup_ids(&mut self) {
1259 let c = &self.wp.ctx;
1260 let mut ids: Vec<(u32, i32, i32, i32, i32)> =
1262 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();
1263 ids.sort_unstable();
1264 ids.dedup();
1265 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();
1266 for (a, b) in dups {
1267 if self.dup_warned.insert(a.0) {
1268 let name = self.id_name(a.0).map_or(String::new(), |n| alloc::format!(" (\"{}\")", n));
1269 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));
1270 }
1271 }
1272 }
1273
1274 pub fn repass(&mut self) {
1276 self.repass = true;
1277 }
1278
1279 pub fn modal(&mut self, layer: u32) {
1281 if layer as i32 > self.wp.ctx.modal_cur as i32 {
1282 self.wp.ctx.modal_cur = layer;
1283 }
1284 }
1285
1286 pub fn modal_active(&self) -> u32 {
1288 self.wp.ctx.modal_prev
1289 }
1290
1291 pub fn disabled_push(&mut self) {
1293 self.wp.ctx.disabled_depth += 1;
1294 }
1295
1296 pub fn disabled_pop(&mut self) {
1297 if self.wp.ctx.disabled_depth != 0 {
1298 self.wp.ctx.disabled_depth -= 1;
1299 }
1300 }
1301
1302 pub fn warn(&mut self, msg: &str) {
1304 self.warns = self.warns.wrapping_add(1);
1305 let s = alloc::format!("UIWARN f{}: {}\n", self.frame, msg);
1306 plat::write_stderr(s.as_bytes());
1307 plat::log_quiet(plat::LOG_WARN, "ui", msg); }
1309}