1use alloc::collections::VecDeque;
64use alloc::vec::Vec;
65
66use super::consts::*;
67use super::ctx::{Hit, W};
68use super::layout::px2dip_at;
69use super::Ui;
70use plat::Event;
71
72pub(crate) const AX_V: u8 = TF_V as u8;
74pub(crate) const AX_H: u8 = TF_H as u8;
75pub(crate) const AX_ALL: u8 = TF_BOTH as u8;
76const G_HOLD: u8 = TF_HOLD as u8;
77const G_TWO: u8 = TF_TWO as u8;
78const G_SLOP: u8 = TF_SLOP as u8;
79const G_DECLINE: u8 = TF_DECLINE as u8;
80const G_REST: u8 = TF_REST as u8;
81const G_HOLD_LONG: u8 = TF_HOLD_LONG as u8;
82
83const SLOP_DIP: i32 = 8;
85const LONG_PRESS_MS: u32 = 400;
86const FLING_MIN_DIP: i32 = 50;
87const FLING_MAX_DIP: i32 = 8000;
88const HOVER_MS: u32 = 100;
90const LEAVE_MS: u32 = 120;
92const TIP_LEAVE_MS: u32 = 1500;
93const VEL_HORIZON_MS: u32 = 100;
96const VEL_STOPPED_MS: u32 = 40;
97const SAMPLES: usize = 20;
98const FRICTION: f64 = 0.015;
101const DECEL: f64 = 2.358_201_815_154_572;
102const INFLEXION: f64 = 0.35;
103const G_INCH: f64 = 9.80665 * 39.37 * 0.84;
104pub(crate) const TITLE_DIP: i32 = 40;
107pub(crate) const CAPTION_DIP: i32 = 44;
108pub(crate) const CAPTION_MIN_W: i32 = 120;
109const OUTSIDE: i32 = -10000;
111const HOLD_LONG_MS: u32 = 600;
114const HOLD_SHOW_MS: u32 = 120;
115const PALM_MS: u32 = 500;
117const TAKE_BACK_MS: u32 = 200;
119pub(crate) const TARGET_DIP: i32 = 44;
121
122#[derive(Clone, Copy, PartialEq, Eq, Default)]
123enum Mode {
124 #[default]
125 Idle,
126 Wait,
128 Pan,
129 Widget,
131 Gone,
133}
134
135#[derive(Clone, Copy)]
137pub(crate) struct PanView {
138 id: u32,
139 hit0: u32,
140 hit1: u32,
141 layer: u8,
142 own: bool,
144 axes: u8,
145 pos: [i32; 2],
147 max: [i32; 2],
148}
149
150#[derive(Clone, Copy, Default)]
151struct Fling {
152 t0: u32,
153 dur: f64,
155 dist: [f64; 2],
156 done: [f64; 2],
157}
158
159#[derive(Clone, Copy, Debug, PartialEq, Eq)]
161pub struct Finger {
162 pub id: u8,
164 pub x: i32,
165 pub y: i32,
166 pub down_x: i32,
168 pub down_y: i32,
169 pub time_ms: u32,
171 win: u8,
172}
173
174#[derive(Clone, Copy, Debug, PartialEq)]
176pub struct TwoFingers {
177 pub x: i32,
179 pub y: i32,
180 pub start_x: i32,
182 pub start_y: i32,
183 pub scale: f64,
185 pub began: bool,
187 pub taken_back: bool,
190}
191
192#[derive(Clone, Copy)]
194struct Two {
195 ids: [u8; 2],
196 start: [i32; 2],
197 dist: f64,
198}
199
200pub(crate) struct S {
202 pub(crate) on: bool,
204 mode: Mode,
205 fingers: Vec<Finger>,
207 primary: u8,
208 grab: u8,
210 win: u32,
211 down: [i32; 2],
213 cur: [i32; 2],
214 off: [i32; 2],
216 down_t: u32,
217 press_t: u32,
219 down_id: u32,
221 last_t: u32,
222 no_click: bool,
224 hovered: bool,
225 long_done: bool,
226 long_id: u32,
228 data: u32,
229 mods: u8,
230 leave_at: Option<u32>,
231 axes: u8,
232 chain: Vec<u32>,
234 pend: [i32; 2],
236 repaint: bool,
238 catchup: [i32; 2],
240 samples: [(u32, i32, i32); SAMPLES],
241 n: usize,
242 fling: Option<Fling>,
243 out: VecDeque<Event>,
245 take: Vec<(u32, i32, i32)>,
247 spline: Vec<f64>,
248 conf: [i32; 4],
250 conf_read: bool,
251 pub(crate) win_base: [i32; 2],
253 two: Option<Two>,
255 took_back: bool,
256 pen_down: bool,
258 pen_t: Option<u32>,
259 kb_ask: bool,
262 pub(crate) kb_on: bool,
263 kb_win: u32,
264}
265
266impl Default for S {
267 fn default() -> Self {
268 S {
269 on: plat::touch_primary(),
270 mode: Mode::Idle,
271 fingers: Vec::new(),
272 primary: 0,
273 grab: 0,
274 win: 0,
275 down: [0; 2],
276 cur: [0; 2],
277 off: [0; 2],
278 down_t: 0,
279 press_t: 0,
280 down_id: 0,
281 last_t: 0,
282 no_click: false,
283 hovered: false,
284 long_done: false,
285 long_id: 0,
286 data: 0,
287 mods: 0,
288 leave_at: None,
289 axes: 0,
290 chain: Vec::new(),
291 pend: [0; 2],
292 repaint: false,
293 catchup: [0; 2],
294 samples: [(0, 0, 0); SAMPLES],
295 n: 0,
296 fling: None,
297 out: VecDeque::new(),
298 take: Vec::new(),
299 spline: Vec::new(),
300 conf: [SLOP_DIP, FLING_MIN_DIP, FLING_MAX_DIP, LONG_PRESS_MS as i32],
301 conf_read: false,
302 win_base: [0; 2],
303 two: None,
304 took_back: false,
305 pen_down: false,
306 pen_t: None,
307 kb_ask: false,
308 kb_on: false,
309 kb_win: 0,
310 }
311 }
312}
313
314pub(crate) fn hit_grab(hbflags: u32) -> u8 {
316 let mut g = 0;
317 if hbflags & HB_GRAB_H != 0 {
318 g |= AX_H;
319 }
320 if hbflags & HB_GRAB_V != 0 {
321 g |= AX_V;
322 }
323 if hbflags & HB_DRAG != 0 {
324 g |= AX_ALL;
325 }
326 g | match (hbflags >> HB_CURSOR_SHIFT) & 15 {
327 CUR_SIZE_WE | CUR_IBEAM => AX_H,
328 CUR_SIZE_NS => AX_V,
329 CUR_SIZE_NWSE | CUR_SIZE_NESW | CUR_MOVE | CUR_CROSS => AX_ALL,
330 _ => 0,
331 }
332}
333
334fn hit_index(list: &[Hit], modal: u32, x: i32, y: i32) -> Option<usize> {
336 let mut layer = super::consts::L_COUNT as i32 - 1;
337 while layer >= modal as i32 {
338 for (i, h) in list.iter().enumerate().rev() {
339 if h.layer as i32 == layer && x >= h.x0 && x < h.x1 && y >= h.y0 && y < h.y1 {
340 return Some(i);
341 }
342 }
343 layer -= 1;
344 }
345 None
346}
347
348fn spline_table() -> Vec<f64> {
350 const P1: f64 = 0.5 * INFLEXION;
351 const P2: f64 = 1.0 - (1.0 - INFLEXION);
352 let mut table = Vec::with_capacity(101);
353 let mut x_min = 0.0f64;
354 for i in 0..100 {
355 let alpha = i as f64 / 100.0;
356 let mut x_max = 1.0f64;
357 let (mut x, mut coef);
358 loop {
359 x = x_min + (x_max - x_min) / 2.0;
360 coef = 3.0 * x * (1.0 - x);
361 let tx = coef * ((1.0 - x) * P1 + x * P2) + x * x * x;
362 let d = tx - alpha;
363 if (if d < 0.0 { -d } else { d }) < 1e-5 {
364 break;
365 }
366 if tx > alpha {
367 x_max = x;
368 } else {
369 x_min = x;
370 }
371 }
372 table.push(coef * ((1.0 - x) * 0.5 + x) + x * x * x);
373 }
374 table.push(1.0);
375 table
376}
377
378fn sqrt(v: f64) -> f64 {
379 rt::x87::pow(v, 0.5)
380}
381
382impl Ui {
383 pub fn touch_mode(&self) -> bool {
386 self.m.touch.on
387 }
388
389 pub fn touch_drag(&mut self) {
394 let c = &mut self.wp.ctx;
395 let [x, y, w, h] = c.last_rect;
396 let k = c.uiclip;
397 let r = [x.max(k[0]), y.max(k[1]), (x + w).min(k[2]), (y + h).min(k[3])];
398 let cur = c.hit_cur;
399 if r[2] > r[0] && r[3] > r[1] && c.grabs[cur].len() < 256 {
400 c.grabs[cur].push(r);
401 }
402 }
403
404 pub fn touch_take(&mut self, flags: u32) {
407 let c = &mut self.wp.ctx;
408 let [x, y, w, h] = c.last_rect;
409 let k = c.uiclip;
410 let r = [x.max(k[0]), y.max(k[1]), (x + w).min(k[2]), (y + h).min(k[3])];
411 let cur = c.hit_cur;
412 for (i, hit) in c.hits[cur].iter_mut().rev().enumerate() {
413 if i != 0 && !(hit.x0 >= r[0] && hit.y0 >= r[1] && hit.x1 <= r[2] && hit.y1 <= r[3]) {
414 break;
415 }
416 hit.grab = flags as u8;
417 }
418 }
419
420 pub fn touch_fingers(&self) -> Vec<Finger> {
423 let win = self.uiw_cur as u8;
424 let fb = self.wp.rt.fb.w.wrapping_sub(1);
425 let rtl = self.rtl;
426 let m = |x: i32| if rtl { fb.wrapping_sub(x) } else { x };
427 self.m.touch.fingers.iter().filter(|f| f.win == win).map(|f| Finger { x: m(f.x), down_x: m(f.down_x), ..*f }).collect()
428 }
429
430 pub fn touch_two(&mut self) -> Option<TwoFingers> {
434 let win = self.uiw_cur;
435 let [x0, y0, w, h] = self.wp.ctx.last_rect;
436 let s = &self.m.touch;
437 if s.grab & G_TWO == 0 || s.win != win || s.mode == Mode::Widget {
438 return None;
439 }
440 let mut it = s.fingers.iter().filter(|f| f.win as u32 == win);
441 let (a, b) = (*it.next()?, *it.next()?);
442 let d = self.touch_logical([a.down_x, a.down_y]);
444 if d[0] < x0 || d[1] < y0 || d[0] >= x0.wrapping_add(w) || d[1] >= y0.wrapping_add(h) {
445 return None;
446 }
447 let m = self.touch_logical([a.x.wrapping_add(b.x) >> 1, a.y.wrapping_add(b.y) >> 1]);
448 let (dx, dy) = (a.x.wrapping_sub(b.x) as f64, a.y.wrapping_sub(b.y) as f64);
449 let dist = sqrt(dx * dx + dy * dy).max(1.0);
450 let s = &mut self.m.touch;
451 let began = s.two.is_none_or(|t| t.ids != [a.id, b.id]);
452 if began {
453 s.two = Some(Two { ids: [a.id, b.id], start: m, dist });
454 }
455 let t = s.two?;
456 let taken_back = began && core::mem::take(&mut s.took_back);
457 Some(TwoFingers { x: m[0], y: m[1], start_x: t.start[0], start_y: t.start[1], scale: dist / t.dist, began, taken_back })
458 }
459
460 pub fn touch_hold_part(&self, id: u32) -> Option<u32> {
464 let s = &self.m.touch;
465 if s.grab & G_HOLD_LONG == 0 || s.win != self.uiw_cur || s.down_id != id || id == 0 {
466 return None;
467 }
468 match s.mode {
469 Mode::Wait if !s.no_click => {
470 let held = self.now_ms.wrapping_sub(s.down_t);
471 (held >= HOLD_SHOW_MS).then(|| ((held - HOLD_SHOW_MS) * 1000 / (HOLD_LONG_MS - HOLD_SHOW_MS)).min(1000))
472 }
473 Mode::Widget => Some(1000),
474 _ => None,
475 }
476 }
477
478 pub(crate) fn touch_grab(&mut self, axes: u8) {
480 let c = &mut self.wp.ctx;
481 let cur = c.hit_cur;
482 if let Some(h) = c.hits[cur].last_mut() {
483 h.grab |= axes;
484 }
485 }
486
487 pub(crate) fn pan_view(&mut self, id: u32, hit0: usize, axes: u8, pos: [i32; 2], max: [i32; 2]) {
490 let layer = self.wp.ctx.cmd_layer_ui as u8;
491 self.pan_push(id, hit0, layer, true, axes, pos, max);
492 }
493
494 #[allow(clippy::too_many_arguments)]
496 pub(crate) fn pan_push(&mut self, id: u32, hit0: usize, layer: u8, own: bool, axes: u8, pos: [i32; 2], max: [i32; 2]) {
497 let c = &mut self.wp.ctx;
498 let cur = c.hit_cur;
499 if c.pans[cur].len() < 64 {
500 let hit1 = c.hits[cur].len() as u32;
501 c.pans[cur].push(PanView { id, hit0: hit0 as u32, hit1, layer, own, axes, pos, max });
502 }
503 }
504
505 pub(crate) fn pan_take(&mut self, id: u32) -> (i32, i32) {
507 let take = &mut self.m.touch.take;
508 match take.iter().position(|t| t.0 == id) {
509 Some(i) => {
510 let t = take.swap_remove(i);
511 (t.1, t.2)
512 }
513 None => (0, 0),
514 }
515 }
516
517 fn touch_conf(&mut self) {
520 if core::mem::replace(&mut self.m.touch.conf_read, true) {
521 return;
522 }
523 let Some(v) = plat::env("jfgui_TOUCH") else { return };
524 let mut r = v.as_bytes();
525 for k in 0..4 {
526 let (n, rest) = super::runtime::parse_int(r);
527 if n > 0 {
528 self.m.touch.conf[k] = n;
529 }
530 r = rest;
531 }
532 }
533
534 pub(crate) fn touch_takes(&self, ev: &Event) -> bool {
536 matches!(ev.typ, plat::E_MOVE | plat::E_DOWN | plat::E_UP) && (ev.data >> plat::PD_TYPE_SHIFT) & 3 == PTR_TOUCH
537 }
538
539 pub(crate) fn touch_kind(&mut self, ev: &Event) {
541 let kind = (ev.data >> plat::PD_TYPE_SHIFT) & 3;
542 if matches!(ev.typ, plat::E_MOVE | plat::E_DOWN | plat::E_UP) && kind != PTR_TOUCH {
543 self.touch_pen(ev, kind);
544 }
545 if !matches!(ev.typ, plat::E_MOVE | plat::E_DOWN) {
546 return;
547 }
548 let finger = kind == PTR_TOUCH;
549 if finger && self.touch_palm() {
550 return; }
552 if self.m.touch.on != finger {
553 self.touch_mode_set(finger);
554 }
555 if !finger {
556 self.m.touch.fling = None; }
558 }
559
560 fn touch_pen(&mut self, ev: &Event, kind: u32) {
562 let now = self.now_ms;
563 let s = &mut self.m.touch;
564 if kind == PTR_MOUSE {
565 if ev.typ == plat::E_UP {
566 s.pen_down = false; }
568 return;
569 }
570 s.pen_t = Some(now);
571 match ev.typ {
572 plat::E_DOWN => {
573 s.pen_down = true;
574 self.touch_end_all();
575 }
576 plat::E_UP => s.pen_down = false,
577 _ => {}
578 }
579 }
580
581 fn touch_palm(&self) -> bool {
583 let s = &self.m.touch;
584 s.pen_down || s.pen_t.is_some_and(|t| self.now_ms.wrapping_sub(t) < PALM_MS)
585 }
586
587 fn touch_end_all(&mut self) {
590 let s = &mut self.m.touch;
591 if s.fingers.is_empty() {
592 return;
593 }
594 s.fingers.clear();
595 s.two = None;
596 s.took_back = false;
597 s.repaint = true;
598 let mode = core::mem::replace(&mut s.mode, Mode::Idle);
599 if mode == Mode::Widget {
600 self.touch_out(plat::E_UP, BTN_LEFT as u8, OUTSIDE, OUTSIDE);
601 }
602 if self.m.touch.hovered {
603 self.m.touch.leave_at = Some(self.now_ms.wrapping_add(LEAVE_MS));
604 }
605 }
606
607 pub(crate) fn touch_mode_set(&mut self, finger: bool) {
609 self.m.touch.on = finger;
610 self.m.touch.repaint = true;
611 }
612
613 pub(crate) fn touch_next(&mut self) -> Option<Event> {
615 self.m.touch.out.pop_front()
616 }
617
618 fn touch_out(&mut self, typ: u8, btn: u8, x: i32, y: i32) {
620 let t = &mut self.m.touch;
621 t.kb_ask |= typ == plat::E_DOWN;
622 let data = (t.data & plat::PD_PRESSURE) | PTR_TOUCH << plat::PD_TYPE_SHIFT;
623 let (win, mods) = (t.win as u8, t.mods);
624 t.out.push_back(Event { typ, btn, mods, win, x, y, data });
625 }
626
627 fn touch_time(&self, ev: &Event) -> u32 {
629 self.now_ms.wrapping_sub((ev.data >> plat::PD_AGE_SHIFT) & plat::PD_AGE_MASK)
630 }
631
632 fn touch_sample(&mut self, t: u32, x: i32, y: i32) {
633 let s = &mut self.m.touch;
634 if s.n == SAMPLES {
635 s.samples.copy_within(1.., 0);
636 s.n -= 1;
637 }
638 s.samples[s.n] = (t, x, y);
639 s.n += 1;
640 }
641
642 fn touch_logical(&self, p: [i32; 2]) -> [i32; 2] {
644 if self.rtl { [self.wp.rt.fb.w.wrapping_sub(1).wrapping_sub(p[0]), p[1]] } else { p }
645 }
646
647 pub(crate) fn touch_input(&mut self, ev: &Event) {
649 self.touch_conf();
650 let t = self.touch_time(ev);
651 let id = ev.btn;
652 let s = &mut self.m.touch;
653 match ev.typ {
654 plat::E_DOWN => {
655 s.fingers.retain(|f| f.id != id); s.two = None;
657 if self.touch_palm() {
658 return; }
660 let s = &mut self.m.touch;
661 let first = s.fingers.is_empty();
662 s.fingers.push(Finger { id, x: ev.x, y: ev.y, down_x: ev.x, down_y: ev.y, time_ms: t, win: ev.win });
663 if first {
664 s.primary = id;
665 self.touch_down(ev, t);
666 } else {
667 self.touch_second(ev, t);
668 }
669 }
670 plat::E_MOVE => {
671 let Some(f) = s.fingers.iter_mut().find(|f| f.id == id) else { return };
672 f.x = ev.x;
673 f.y = ev.y;
674 f.time_ms = t;
675 if s.grab & G_TWO != 0 {
676 s.repaint = true;
677 }
678 if id == s.primary && s.mode != Mode::Idle && ev.win as u32 == s.win {
679 self.touch_move(ev, t);
680 }
681 }
682 _ => {
683 let Some(k) = s.fingers.iter().position(|f| f.id == id) else { return };
684 s.fingers.remove(k);
685 s.two = None;
686 if s.grab & G_TWO != 0 {
687 s.repaint = true;
688 }
689 if id != s.primary {
690 return;
691 }
692 match s.fingers.first().copied() {
693 Some(next) => self.touch_handover(ev, next),
694 None if s.mode != Mode::Idle => self.touch_up(ev, t),
695 None => {}
696 }
697 }
698 }
699 }
700
701 fn touch_second(&mut self, ev: &Event, t: u32) {
703 let s = &mut self.m.touch;
704 if s.grab & G_TWO != 0 && ev.win as u32 == s.win {
705 s.repaint = true;
706 match s.mode {
707 Mode::Wait => s.mode = Mode::Gone, Mode::Widget if t.wrapping_sub(s.press_t) < TAKE_BACK_MS => {
709 s.mode = Mode::Gone;
712 s.took_back = true;
713 let [x, y] = [s.cur[0].wrapping_add(s.off[0]), s.cur[1].wrapping_add(s.off[1])];
714 self.touch_out(plat::E_UP, BTN_LEFT as u8, x, y);
715 }
716 _ => {}
717 }
718 } else if s.mode == Mode::Wait {
719 s.no_click = true; }
721 }
722
723 fn touch_handover(&mut self, ev: &Event, next: Finger) {
725 let cancel = ev.data & plat::PD_CANCEL != 0;
726 let s = &mut self.m.touch;
727 s.primary = next.id;
728 match s.mode {
729 Mode::Pan => {
730 s.cur = [next.x, next.y];
732 s.n = 0;
733 self.touch_sample(next.time_ms, next.x, next.y);
734 }
735 Mode::Widget => {
736 s.mode = Mode::Gone;
737 let [ax, ay] = self.touch_aimed([ev.x, ev.y]);
738 let (x, y) = if cancel { (OUTSIDE, OUTSIDE) } else { (ax, ay) };
739 self.touch_out(plat::E_UP, BTN_LEFT as u8, x, y);
740 }
741 Mode::Wait => s.mode = Mode::Gone,
742 _ => {}
743 }
744 }
745
746 fn touch_down(&mut self, ev: &Event, t: u32) {
747 let win = ev.win as u32;
748 if win as usize >= WIN_MAX || (win != 0 && !self.uiw_drawable(win)) {
749 return;
750 }
751 if self.m.touch.mode == Mode::Widget {
753 self.touch_out(plat::E_UP, BTN_LEFT as u8, OUTSIDE, OUTSIDE);
754 }
755 let stopped = self.m.touch.fling.take().is_some();
756 let s = &mut self.m.touch;
757 s.mode = Mode::Wait;
758 s.grab = 0;
759 s.win = win;
760 s.down = [ev.x, ev.y];
761 s.cur = s.down;
762 s.off = [0; 2];
763 s.took_back = false;
764 s.down_t = t;
765 s.last_t = t;
766 s.no_click = stopped;
767 s.hovered = false;
768 s.long_done = false;
769 s.data = ev.data;
770 s.mods = ev.mods;
771 s.leave_at = None;
772 s.pend = [0; 2];
773 s.catchup = [0; 2];
774 s.n = 0;
775 self.touch_sample(t, ev.x, ev.y);
776 plat::touch_reached(false);
777 self.uiw_use(win);
778 self.touch_aim();
779 let (hit, grab) = self.touch_under();
780 let c = &self.wp.ctx;
781 self.m.touch.down_id = hit.map_or(0, |i| c.hits[c.hit_cur ^ 1][i].id);
782 self.m.touch.grab = grab;
783 if stopped {
784 return;
785 }
786 if grab & AX_ALL == AX_ALL && grab & (G_HOLD | G_SLOP | G_DECLINE) == 0 {
788 self.touch_press();
789 }
790 }
791
792 fn touch_aim(&mut self) {
797 if self.uiw_inert_now() {
798 return;
799 }
800 let t = self.dp(TARGET_DIP);
801 let c = &self.wp.ctx;
802 let p = self.touch_logical(self.m.touch.down);
803 let list = &c.hits[c.hit_cur ^ 1];
804 let real = |h: &Hit| h.id != 0 && h.flags as u32 & (HB_BLOCK | HB_DRAG) == 0;
806 let grip = |h: &Hit| matches!((h.flags as u32 >> HB_CURSOR_SHIFT) & 15, CUR_SIZE_WE | CUR_SIZE_NS | CUR_SIZE_NWSE | CUR_SIZE_NESW);
808 let usable = |h: &Hit| real(h) && h.flags as u32 & HB_DISABLED == 0 && !grip(h);
809 let exact = hit_index(list, c.modal_prev, p[0], p[1]);
810 if exact.is_some_and(|i| real(&list[i]) && list[i].grab & G_TWO == 0) {
811 return;
812 }
813 let floor = exact.map_or(c.modal_prev as i32, |i| list[i].layer as i32).max(c.modal_prev as i32);
814 let mut best: Option<(i32, [i32; 2])> = None;
815 for (i, h) in list.iter().enumerate() {
816 if !usable(h) || (h.layer as i32) < floor || h.x1 <= h.x0 || h.y1 <= h.y0 {
818 continue;
819 }
820 let (ex, ey) = (((t - (h.x1 - h.x0)) / 2).max(0), ((t - (h.y1 - h.y0)) / 2).max(0));
821 if ex == 0 && ey == 0 || p[0] < h.x0 - ex || p[0] >= h.x1 + ex || p[1] < h.y0 - ey || p[1] >= h.y1 + ey {
822 continue;
823 }
824 let q = [p[0].clamp(h.x0, h.x1 - 1), p[1].clamp(h.y0, h.y1 - 1)];
826 let d = (q[0] - p[0]).abs().max((q[1] - p[1]).abs());
827 if best.is_none_or(|b| d < b.0) && hit_index(list, c.modal_prev, q[0], q[1]) == Some(i) {
828 best = Some((d, q));
829 }
830 }
831 if let Some((_, q)) = best {
832 let raw = self.touch_logical(q); let s = &mut self.m.touch;
834 s.off = [raw[0] - s.down[0], raw[1] - s.down[1]];
835 }
836 }
837
838 fn touch_find_again(&mut self) {
842 let id = self.m.touch.down_id;
843 if id == 0 || self.uiw_inert_now() {
844 return;
845 }
846 let c = &self.wp.ctx;
847 let list = &c.hits[c.hit_cur ^ 1];
848 let p = self.touch_logical(self.touch_aimed(self.m.touch.down));
849 if hit_index(list, c.modal_prev, p[0], p[1]).is_some_and(|i| list[i].id == id) {
850 return;
851 }
852 let Some(h) = list.iter().rev().find(|h| h.id == id && h.x1 > h.x0 && h.y1 > h.y0) else { return };
853 let q = [p[0].clamp(h.x0, h.x1 - 1), p[1].clamp(h.y0, h.y1 - 1)];
854 if hit_index(list, c.modal_prev, q[0], q[1]).is_some_and(|i| list[i].id == id) {
855 let raw = self.touch_logical(q);
856 let s = &mut self.m.touch;
857 s.off = [raw[0] - s.down[0], raw[1] - s.down[1]];
858 }
859 }
860
861 fn touch_aimed(&self, p: [i32; 2]) -> [i32; 2] {
863 let o = self.m.touch.off;
864 [p[0].wrapping_add(o[0]), p[1].wrapping_add(o[1])]
865 }
866
867 fn touch_under(&self) -> (Option<usize>, u8) {
869 if self.uiw_inert_now() {
870 return (None, 0);
871 }
872 let c = &self.wp.ctx;
873 let p = self.touch_logical(self.touch_aimed(self.m.touch.down));
874 let list = &c.hits[c.hit_cur ^ 1];
875 let i = hit_index(list, c.modal_prev, p[0], p[1]);
876 let mut grab = i.map_or(0, |i| if list[i].flags as u32 & HB_DISABLED != 0 { 0 } else { list[i].grab });
877 if c.grabs[c.hit_cur ^ 1].iter().any(|r| p[0] >= r[0] && p[1] >= r[1] && p[0] < r[2] && p[1] < r[3]) {
878 grab = AX_ALL;
879 }
880 (i, grab)
881 }
882
883 fn touch_press(&mut self) {
885 self.uiw_use(self.m.touch.win);
886 self.touch_find_again();
887 let [x, y] = self.touch_aimed(self.m.touch.down);
888 let now = self.now_ms;
889 let s = &mut self.m.touch;
890 s.mode = Mode::Widget;
891 s.press_t = now;
892 self.touch_out(plat::E_MOVE, 0, x, y);
893 self.touch_out(plat::E_DOWN, BTN_LEFT as u8, x, y);
894 plat::touch_reached(true);
895 }
896
897 fn touch_move(&mut self, ev: &Event, t: u32) {
898 let (ox, oy) = (self.m.touch.cur[0], self.m.touch.cur[1]);
899 self.m.touch.cur = [ev.x, ev.y];
900 self.m.touch.last_t = t;
901 self.m.touch.data = ev.data;
902 self.touch_sample(t, ev.x, ev.y);
903 match self.m.touch.mode {
904 Mode::Wait => {
905 let s = &self.m.touch;
906 let (dx, dy) = (ev.x.wrapping_sub(s.down[0]), ev.y.wrapping_sub(s.down[1]));
907 let slop = self.dp(s.conf[0]);
908 if (dx as i64) * (dx as i64) + (dy as i64) * (dy as i64) > (slop as i64) * (slop as i64) {
909 self.uiw_use(self.m.touch.win);
910 self.touch_decide(dx, dy);
911 self.touch_after_decide();
912 }
913 }
914 Mode::Pan => self.touch_pan(ev.x.wrapping_sub(ox), ev.y.wrapping_sub(oy)),
915 Mode::Widget => {
916 let [x, y] = self.touch_aimed([ev.x, ev.y]);
917 self.touch_out(plat::E_MOVE, 0, x, y);
918 }
919 _ => {}
920 }
921 }
922
923 fn touch_after_decide(&mut self) {
925 if self.m.touch.mode == Mode::Widget {
926 let [x, y] = self.touch_aimed(self.m.touch.cur);
927 self.touch_out(plat::E_MOVE, 0, x, y);
928 }
929 }
930
931 fn touch_decide(&mut self, dx: i32, dy: i32) {
933 let (hit, grab) = self.touch_under();
934 let s = &self.m.touch;
935 if s.long_done {
936 let c = &self.wp.ctx;
939 let id = hit.map_or(0, |i| c.hits[c.hit_cur ^ 1][i].id);
940 if id != s.long_id {
941 self.m.touch.mode = Mode::Gone;
942 return;
943 }
944 }
945 let (adx, ady) = (dx.wrapping_abs(), dy.wrapping_abs());
946 let axis = if adx > ady { AX_H } else { AX_V };
947 let waits = grab & (G_HOLD | G_DECLINE) != 0; if grab & axis != 0 && !waits {
949 self.touch_press();
950 return;
951 }
952 let mut chain = core::mem::take(&mut self.m.touch.chain);
954 chain.clear();
955 let mut can = 0u8; let mut both = false;
957 {
958 let c = &self.wp.ctx;
959 if let Some(i) = hit {
960 let layer = c.hits[c.hit_cur ^ 1][i].layer;
961 for v in c.pans[c.hit_cur ^ 1].iter() {
962 if v.layer == layer && i as u32 >= v.hit0 && (i as u32) < v.hit1 {
963 chain.push(v.id);
964 let mut a = 0;
965 if v.axes & AX_H != 0 && v.max[0] > 0 {
966 a |= AX_H;
967 }
968 if v.axes & AX_V != 0 && v.max[1] > 0 {
969 a |= AX_V;
970 }
971 if can & axis == 0 && a & axis != 0 && a == AX_ALL {
973 both = true;
974 }
975 can |= a;
976 }
977 }
978 }
979 }
980 self.m.touch.chain = chain;
981 if can & axis == 0 {
982 if waits {
983 self.m.touch.mode = Mode::Gone;
984 } else {
985 self.touch_press();
986 }
987 return;
988 }
989 let free = both && adx.min(ady).wrapping_mul(2) >= adx.max(ady);
991 let hovered = self.m.touch.hovered;
992 let slop = self.dp(self.m.touch.conf[0]);
993 let s = &mut self.m.touch;
994 s.mode = Mode::Pan;
995 s.axes = if free { AX_ALL } else { axis };
996 for (k, d, ax) in [(0, dx, AX_H), (1, dy, AX_V)] {
998 if s.axes & ax != 0 {
999 let c = d.clamp(-slop, slop);
1000 s.catchup[k] = c;
1001 s.pend[k] = s.pend[k].wrapping_add(d.wrapping_sub(c));
1002 }
1003 }
1004 if hovered {
1005 let (x, y) = (-1, -1);
1006 self.touch_out(plat::E_LEAVE, 0, x, y);
1007 }
1008 }
1009
1010 fn touch_pan(&mut self, dx: i32, dy: i32) {
1012 let s = &mut self.m.touch;
1013 for (k, d, ax) in [(0, dx, AX_H), (1, dy, AX_V)] {
1014 if s.axes & ax == 0 {
1015 continue;
1016 }
1017 let c = s.catchup[k];
1018 let extra = (d.wrapping_abs() / 2).min(c.wrapping_abs());
1019 let extra = if d != 0 && extra == 0 && c != 0 { 1 } else { extra };
1020 let extra = if c < 0 { -extra } else { extra };
1021 s.catchup[k] = c - extra;
1022 s.pend[k] = s.pend[k].wrapping_add(d).wrapping_add(extra);
1023 }
1024 }
1025
1026 fn touch_up(&mut self, ev: &Event, t: u32) {
1027 let cancel = ev.data & plat::PD_CANCEL != 0;
1028 let mode = self.m.touch.mode;
1029 self.m.touch.mode = Mode::Idle;
1030 match mode {
1031 Mode::Wait => {
1032 if !cancel && !self.m.touch.no_click && !self.m.touch.long_done {
1033 self.uiw_use(self.m.touch.win);
1036 self.touch_find_again();
1037 let [x, y] = self.touch_aimed(self.m.touch.down);
1038 self.touch_out(plat::E_MOVE, 0, x, y);
1039 self.touch_out(plat::E_DOWN, BTN_LEFT as u8, x, y);
1040 self.touch_out(plat::E_UP, BTN_LEFT as u8, x, y);
1041 plat::touch_reached(true);
1042 }
1043 if self.m.touch.hovered || !self.m.touch.no_click {
1044 self.uiw_use(self.m.touch.win);
1046 let ms = if self.tip_showing() { TIP_LEAVE_MS } else { LEAVE_MS };
1047 self.m.touch.leave_at = Some(t.wrapping_add(ms));
1048 }
1049 }
1050 Mode::Widget => {
1051 let [ax, ay] = self.touch_aimed([ev.x, ev.y]);
1052 let (x, y) = if cancel { (OUTSIDE, OUTSIDE) } else { (ax, ay) };
1053 self.touch_out(plat::E_UP, BTN_LEFT as u8, x, y);
1054 self.m.touch.leave_at = Some(t.wrapping_add(LEAVE_MS));
1055 }
1056 Mode::Pan if !cancel => {
1057 self.uiw_use(self.m.touch.win);
1058 self.touch_fling(t);
1059 let s = &mut self.m.touch;
1061 if s.fling.is_none() {
1062 s.pend[0] = s.pend[0].wrapping_add(core::mem::take(&mut s.catchup[0]));
1063 s.pend[1] = s.pend[1].wrapping_add(core::mem::take(&mut s.catchup[1]));
1064 }
1065 }
1066 Mode::Gone if self.m.touch.hovered => self.m.touch.leave_at = Some(t.wrapping_add(LEAVE_MS)),
1067 _ => {}
1068 }
1069 }
1070
1071 fn touch_velocity(&self, k: usize, up: u32) -> f64 {
1074 let s = &self.m.touch;
1075 if s.n < 2 || up.wrapping_sub(s.samples[s.n - 1].0) > VEL_STOPPED_MS {
1076 return 0.0;
1077 }
1078 let last = s.samples[s.n - 1];
1079 let at = |e: &(u32, i32, i32)| if k == 0 { e.1 } else { e.2 };
1080 let (mut n, mut st, mut st2, mut st3, mut st4, mut sx, mut stx, mut st2x) = (0.0f64, 0.0f64, 0.0f64, 0.0f64, 0.0f64, 0.0f64, 0.0f64, 0.0f64);
1081 let mut first = 0.0f64;
1082 let mut stamps = 0;
1083 let mut prev = u32::MAX;
1084 for e in s.samples[..s.n].iter().rev() {
1085 let age = last.0.wrapping_sub(e.0);
1086 if age > VEL_HORIZON_MS {
1087 break;
1088 }
1089 let t = -(age as f64) / 1000.0;
1090 let x = (at(e) - at(&last)) as f64;
1091 first = t;
1092 if age != prev {
1093 stamps += 1;
1094 prev = age;
1095 }
1096 n += 1.0;
1097 st += t;
1098 st2 += t * t;
1099 st3 += t * t * t;
1100 st4 += t * t * t * t;
1101 sx += x;
1102 stx += t * x;
1103 st2x += t * t * x;
1104 }
1105 if stamps < 2 || first == 0.0 {
1106 return 0.0;
1107 }
1108 if stamps >= 3 {
1109 let det = n * (st2 * st4 - st3 * st3) - st * (st * st4 - st3 * st2) + st2 * (st * st3 - st2 * st2);
1111 let d = if det < 0.0 { -det } else { det };
1112 if d > 1e-18 {
1113 let db = n * (stx * st4 - st3 * st2x) - sx * (st * st4 - st3 * st2) + st2 * (st * st2x - st2 * stx);
1114 return db / det;
1115 }
1116 }
1117 let det = n * st2 - st * st;
1119 if det == 0.0 { 0.0 } else { (n * stx - st * sx) / det }
1120 }
1121
1122 fn touch_fling(&mut self, up: u32) {
1124 let (vx, vy) = (
1125 if self.m.touch.axes & AX_H != 0 { self.touch_velocity(0, up) } else { 0.0 },
1126 if self.m.touch.axes & AX_V != 0 { self.touch_velocity(1, up) } else { 0.0 },
1127 );
1128 let mut speed = sqrt(vx * vx + vy * vy);
1129 let (lo, hi) = (self.dp(self.m.touch.conf[1]) as f64, self.dp(self.m.touch.conf[2]) as f64);
1130 if !(speed >= lo) {
1131 return;
1132 }
1133 let (ux, uy) = (vx / speed, vy / speed);
1134 if speed > hi {
1135 speed = hi;
1136 }
1137 let ppi = self.wp.rt.dpi as f64 * 160.0 / 96.0;
1139 let phys = G_INCH * ppi;
1140 let r = INFLEXION * speed / (FRICTION * phys);
1141 let dur = 1000.0 * rt::x87::pow(r, 1.0 / (DECEL - 1.0));
1142 let way = FRICTION * phys * rt::x87::pow(r, DECEL / (DECEL - 1.0));
1143 if self.m.touch.spline.is_empty() {
1144 self.m.touch.spline = spline_table();
1145 }
1146 let s = &mut self.m.touch;
1147 s.fling = Some(Fling {
1148 t0: up,
1149 dur,
1150 dist: [way * ux + s.catchup[0] as f64, way * uy + s.catchup[1] as f64],
1152 done: [0.0; 2],
1153 });
1154 self.animating = true;
1155 }
1156
1157 pub(crate) fn touch_tick(&mut self) {
1159 let now = self.now_ms;
1160 let s = &self.m.touch;
1161 if s.mode == Mode::Wait && !s.no_click && !s.long_done {
1162 let held = now.wrapping_sub(s.down_t);
1163 let [x, y] = self.touch_aimed(s.down);
1164 let s = &mut self.m.touch;
1165 if s.grab & G_HOLD_LONG != 0 {
1166 s.repaint = true; }
1168 if held >= HOVER_MS && !s.hovered {
1169 self.m.touch.hovered = true;
1170 self.touch_out(plat::E_MOVE, 0, x, y);
1171 }
1172 let long = if self.m.touch.grab & G_HOLD_LONG != 0 { HOLD_LONG_MS } else { self.m.touch.conf[3] as u32 };
1173 if held >= long {
1174 self.uiw_use(self.m.touch.win);
1175 let (hit, grab) = self.touch_under();
1176 if grab & (G_HOLD | G_REST) != 0 {
1177 if grab & G_HOLD != 0 {
1179 plat::touch_long_press();
1180 }
1181 self.touch_press();
1182 return;
1183 }
1184 let c = &self.wp.ctx;
1185 let id = hit.map_or(0, |i| c.hits[c.hit_cur ^ 1][i].id);
1186 self.m.touch.long_id = id;
1187 self.m.touch.long_done = true;
1188 self.touch_out(plat::E_MOVE, 0, x, y);
1189 self.touch_out(plat::E_DOWN, BTN_RIGHT as u8, x, y);
1190 self.touch_out(plat::E_UP, BTN_RIGHT as u8, x, y);
1191 plat::touch_long_press();
1192 }
1193 } else if self.m.touch.mode == Mode::Idle {
1194 if let Some(at) = self.m.touch.leave_at {
1195 if (now.wrapping_sub(at) as i32) >= 0 {
1196 self.m.touch.leave_at = None;
1197 self.touch_out(plat::E_LEAVE, 0, -1, -1);
1198 }
1199 }
1200 }
1201 }
1202
1203 pub(crate) fn touch_busy(&self) -> bool {
1206 let s = &self.m.touch;
1207 s.mode != Mode::Idle || s.fling.is_some() || s.leave_at.is_some() || !s.out.is_empty() || s.pend != [0; 2] || s.repaint
1208 }
1209
1210 pub(crate) fn touch_moved(&self) -> bool {
1212 self.m.touch.pend != [0; 2] || self.m.touch.repaint
1213 }
1214
1215 pub(crate) fn touch_apply(&mut self) {
1218 let s = &mut self.m.touch;
1219 s.take.clear();
1220 s.repaint = false;
1221 let mut d = core::mem::take(&mut s.pend);
1222 let mut flinging = false;
1223 if let Some(f) = s.fling.as_mut() {
1224 flinging = true;
1225 let el = self.now_ms.wrapping_sub(f.t0) as f64;
1226 let part = if el >= f.dur {
1227 1.0
1228 } else {
1229 let p = el / f.dur * 100.0;
1230 let k = p as usize;
1231 s.spline[k] + (p - k as f64) * (s.spline[k + 1] - s.spline[k])
1232 };
1233 for k in 0..2 {
1234 let step = (f.dist[k] * part - f.done[k]) as i32; f.done[k] += step as f64;
1236 d[k] = d[k].wrapping_add(step);
1237 }
1238 if part >= 1.0 {
1239 s.fling = None;
1240 }
1241 }
1242 if d == [0; 2] {
1243 return;
1244 }
1245 let win = s.win;
1246 if win as usize >= WIN_MAX || (win != 0 && !self.uiw_drawable(win)) {
1247 self.m.touch.fling = None;
1248 return;
1249 }
1250 self.uiw_use(win);
1251 if self.rtl {
1252 d[0] = d[0].wrapping_neg(); }
1254 let mut moved = false;
1255 let Ui { wp, m, .. } = self;
1256 let W { pans, hit_cur, slots, .. } = &mut wp.ctx;
1257 let views = &pans[*hit_cur ^ 1];
1258 let t = &mut m.touch;
1259 for &id in t.chain.iter() {
1260 let Some(v) = views.iter().find(|v| v.id == id) else { continue };
1261 let mut np = v.pos;
1262 for (k, ax) in [(0, AX_H), (1, AX_V)] {
1263 if v.axes & ax != 0 && d[k] != 0 {
1264 np[k] = v.pos[k].wrapping_sub(d[k]).clamp(0, v.max[k].max(0));
1265 d[k] = d[k].wrapping_sub(v.pos[k].wrapping_sub(np[k]));
1266 }
1267 }
1268 if np == v.pos {
1269 continue;
1270 }
1271 moved = true;
1272 if v.own {
1273 if t.take.len() < 16 {
1274 t.take.push((id, np[0].wrapping_sub(v.pos[0]), np[1].wrapping_sub(v.pos[1])));
1275 }
1276 } else if let Some(sl) = slots.get_mut(&id) {
1277 if v.axes & AX_H != 0 {
1278 sl.scrollx = np[0];
1279 sl.at0 = np[0] as u32;
1280 }
1281 if v.axes & AX_V != 0 {
1282 sl.scrolly = np[1];
1283 sl.adur = np[1] as u32;
1284 }
1285 }
1286 }
1287 if flinging && !moved {
1289 t.fling = None;
1290 }
1291 self.uiw_use(0);
1292 }
1293
1294 pub(crate) fn touch_after_passes(&mut self) {
1297 if self.m.touch.fling.is_some() {
1298 self.animating = true;
1299 }
1300 let ask = core::mem::take(&mut self.m.touch.kb_ask);
1301 if !ask && !self.m.touch.kb_on {
1302 return;
1303 }
1304 let win = self.m.win.active;
1305 self.uiw_use(win);
1306 let c = &self.wp.ctx;
1307 let text = c.focus != 0 && c.hits[c.hit_cur ^ 1].iter().any(|h| h.id == c.focus && h.flags as u32 & HB_TEXT != 0);
1308 let s = &mut self.m.touch;
1309 let want = text && (s.kb_on || (ask && s.on));
1310 if want != s.kb_on {
1311 s.kb_on = want;
1312 s.kb_win = win;
1313 if !self.headless {
1314 plat::touch_keyboard(self.uiw_pw(self.m.touch.kb_win), want);
1315 }
1316 }
1317 self.uiw_use(0);
1318 }
1319
1320 pub(crate) fn touch_hold_show(&mut self, id: u32) {
1323 let Some(part) = self.touch_hold_part(id) else { return };
1324 let [x, y, w, h] = self.wp.ctx.last_rect;
1325 let t = self.dp(3);
1326 let c = self.color(T_ACCENT);
1327 self.draw_color(c);
1328 self.draw_rect(x, y + h - t, (w as i64 * part as i64 / 1000) as i32, t);
1329 }
1330
1331 pub(crate) fn touch_window_drag(&mut self, i: u32, pressed: bool) {
1334 let c = &self.wp.ctx;
1335 let (dx, dy) = (c.mx.wrapping_sub(c.press_x), c.my.wrapping_sub(c.press_y));
1336 let dpi = self.wp.rt.dpi;
1337 let (dx, dy) = (px2dip_at(dx, dpi), px2dip_at(dy, dpi));
1338 let iu = (i as usize) & (WIN_MAX - 1);
1339 let rc = self.m.win.recs[iu].rect.get();
1340 if pressed {
1341 self.m.touch.win_base = [rc.x, rc.y];
1342 }
1343 let [bx, by] = self.m.touch.win_base;
1344 let fb = self.win_fb(0);
1345 let mdpi = self.win_dpi(0);
1346 let (sw, sh) = (px2dip_at(fb.w, mdpi), px2dip_at(fb.h, mdpi));
1347 let wfb = self.win_fb(i);
1348 let w = px2dip_at(wfb.w, self.win_dpi(i));
1349 let (sx, sy) = self.uiw_snap_dip(i, bx.wrapping_add(dx), by.wrapping_add(dy));
1350 let [x, y] = plat::reach_clamp([sx, sy], w, [sw, sh], 96);
1351 if x != rc.x || y != rc.y {
1352 self.uiw_user_rect(i, x, y, 0, 0);
1353 }
1354 }
1355}