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 {
817 continue;
818 }
819 let (ex, ey) = (((t - (h.x1 - h.x0)) / 2).max(0), ((t - (h.y1 - h.y0)) / 2).max(0));
820 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 {
821 continue;
822 }
823 let q = [p[0].clamp(h.x0, h.x1 - 1), p[1].clamp(h.y0, h.y1 - 1)];
825 let d = (q[0] - p[0]).abs().max((q[1] - p[1]).abs());
826 if best.is_none_or(|b| d < b.0) && hit_index(list, c.modal_prev, q[0], q[1]) == Some(i) {
827 best = Some((d, q));
828 }
829 }
830 if let Some((_, q)) = best {
831 let raw = self.touch_logical(q); let s = &mut self.m.touch;
833 s.off = [raw[0] - s.down[0], raw[1] - s.down[1]];
834 }
835 }
836
837 fn touch_find_again(&mut self) {
841 let id = self.m.touch.down_id;
842 if id == 0 || self.uiw_inert_now() {
843 return;
844 }
845 let c = &self.wp.ctx;
846 let list = &c.hits[c.hit_cur ^ 1];
847 let p = self.touch_logical(self.touch_aimed(self.m.touch.down));
848 if hit_index(list, c.modal_prev, p[0], p[1]).is_some_and(|i| list[i].id == id) {
849 return;
850 }
851 let Some(h) = list.iter().rev().find(|h| h.id == id && h.x1 > h.x0 && h.y1 > h.y0) else { return };
852 let q = [p[0].clamp(h.x0, h.x1 - 1), p[1].clamp(h.y0, h.y1 - 1)];
853 if hit_index(list, c.modal_prev, q[0], q[1]).is_some_and(|i| list[i].id == id) {
854 let raw = self.touch_logical(q);
855 let s = &mut self.m.touch;
856 s.off = [raw[0] - s.down[0], raw[1] - s.down[1]];
857 }
858 }
859
860 fn touch_aimed(&self, p: [i32; 2]) -> [i32; 2] {
862 let o = self.m.touch.off;
863 [p[0].wrapping_add(o[0]), p[1].wrapping_add(o[1])]
864 }
865
866 fn touch_under(&self) -> (Option<usize>, u8) {
868 if self.uiw_inert_now() {
869 return (None, 0);
870 }
871 let c = &self.wp.ctx;
872 let p = self.touch_logical(self.touch_aimed(self.m.touch.down));
873 let list = &c.hits[c.hit_cur ^ 1];
874 let i = hit_index(list, c.modal_prev, p[0], p[1]);
875 let mut grab = i.map_or(0, |i| if list[i].flags as u32 & HB_DISABLED != 0 { 0 } else { list[i].grab });
876 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]) {
877 grab = AX_ALL;
878 }
879 (i, grab)
880 }
881
882 fn touch_press(&mut self) {
884 self.uiw_use(self.m.touch.win);
885 self.touch_find_again();
886 let [x, y] = self.touch_aimed(self.m.touch.down);
887 let now = self.now_ms;
888 let s = &mut self.m.touch;
889 s.mode = Mode::Widget;
890 s.press_t = now;
891 self.touch_out(plat::E_MOVE, 0, x, y);
892 self.touch_out(plat::E_DOWN, BTN_LEFT as u8, x, y);
893 plat::touch_reached(true);
894 }
895
896 fn touch_move(&mut self, ev: &Event, t: u32) {
897 let (ox, oy) = (self.m.touch.cur[0], self.m.touch.cur[1]);
898 self.m.touch.cur = [ev.x, ev.y];
899 self.m.touch.last_t = t;
900 self.m.touch.data = ev.data;
901 self.touch_sample(t, ev.x, ev.y);
902 match self.m.touch.mode {
903 Mode::Wait => {
904 let s = &self.m.touch;
905 let (dx, dy) = (ev.x.wrapping_sub(s.down[0]), ev.y.wrapping_sub(s.down[1]));
906 let slop = self.dp(s.conf[0]);
907 if (dx as i64) * (dx as i64) + (dy as i64) * (dy as i64) > (slop as i64) * (slop as i64) {
908 self.uiw_use(self.m.touch.win);
909 self.touch_decide(dx, dy);
910 self.touch_after_decide();
911 }
912 }
913 Mode::Pan => self.touch_pan(ev.x.wrapping_sub(ox), ev.y.wrapping_sub(oy)),
914 Mode::Widget => {
915 let [x, y] = self.touch_aimed([ev.x, ev.y]);
916 self.touch_out(plat::E_MOVE, 0, x, y);
917 }
918 _ => {}
919 }
920 }
921
922 fn touch_after_decide(&mut self) {
924 if self.m.touch.mode == Mode::Widget {
925 let [x, y] = self.touch_aimed(self.m.touch.cur);
926 self.touch_out(plat::E_MOVE, 0, x, y);
927 }
928 }
929
930 fn touch_decide(&mut self, dx: i32, dy: i32) {
932 let (hit, grab) = self.touch_under();
933 let s = &self.m.touch;
934 if s.long_done {
935 let c = &self.wp.ctx;
938 let id = hit.map_or(0, |i| c.hits[c.hit_cur ^ 1][i].id);
939 if id != s.long_id {
940 self.m.touch.mode = Mode::Gone;
941 return;
942 }
943 }
944 let (adx, ady) = (dx.wrapping_abs(), dy.wrapping_abs());
945 let axis = if adx > ady { AX_H } else { AX_V };
946 let waits = grab & (G_HOLD | G_DECLINE) != 0; if grab & axis != 0 && !waits {
948 self.touch_press();
949 return;
950 }
951 let mut chain = core::mem::take(&mut self.m.touch.chain);
953 chain.clear();
954 let mut can = 0u8; let mut both = false;
956 {
957 let c = &self.wp.ctx;
958 if let Some(i) = hit {
959 let layer = c.hits[c.hit_cur ^ 1][i].layer;
960 for v in c.pans[c.hit_cur ^ 1].iter() {
961 if v.layer == layer && i as u32 >= v.hit0 && (i as u32) < v.hit1 {
962 chain.push(v.id);
963 let mut a = 0;
964 if v.axes & AX_H != 0 && v.max[0] > 0 {
965 a |= AX_H;
966 }
967 if v.axes & AX_V != 0 && v.max[1] > 0 {
968 a |= AX_V;
969 }
970 if can & axis == 0 && a & axis != 0 && a == AX_ALL {
972 both = true;
973 }
974 can |= a;
975 }
976 }
977 }
978 }
979 self.m.touch.chain = chain;
980 if can & axis == 0 {
981 if waits {
982 self.m.touch.mode = Mode::Gone;
983 } else {
984 self.touch_press();
985 }
986 return;
987 }
988 let free = both && adx.min(ady).wrapping_mul(2) >= adx.max(ady);
990 let hovered = self.m.touch.hovered;
991 let slop = self.dp(self.m.touch.conf[0]);
992 let s = &mut self.m.touch;
993 s.mode = Mode::Pan;
994 s.axes = if free { AX_ALL } else { axis };
995 for (k, d, ax) in [(0, dx, AX_H), (1, dy, AX_V)] {
997 if s.axes & ax != 0 {
998 let c = d.clamp(-slop, slop);
999 s.catchup[k] = c;
1000 s.pend[k] = s.pend[k].wrapping_add(d.wrapping_sub(c));
1001 }
1002 }
1003 if hovered {
1004 let (x, y) = (-1, -1);
1005 self.touch_out(plat::E_LEAVE, 0, x, y);
1006 }
1007 }
1008
1009 fn touch_pan(&mut self, dx: i32, dy: i32) {
1011 let s = &mut self.m.touch;
1012 for (k, d, ax) in [(0, dx, AX_H), (1, dy, AX_V)] {
1013 if s.axes & ax == 0 {
1014 continue;
1015 }
1016 let c = s.catchup[k];
1017 let extra = (d.wrapping_abs() / 2).min(c.wrapping_abs());
1018 let extra = if d != 0 && extra == 0 && c != 0 { 1 } else { extra };
1019 let extra = if c < 0 { -extra } else { extra };
1020 s.catchup[k] = c - extra;
1021 s.pend[k] = s.pend[k].wrapping_add(d).wrapping_add(extra);
1022 }
1023 }
1024
1025 fn touch_up(&mut self, ev: &Event, t: u32) {
1026 let cancel = ev.data & plat::PD_CANCEL != 0;
1027 let mode = self.m.touch.mode;
1028 self.m.touch.mode = Mode::Idle;
1029 match mode {
1030 Mode::Wait => {
1031 if !cancel && !self.m.touch.no_click && !self.m.touch.long_done {
1032 self.uiw_use(self.m.touch.win);
1035 self.touch_find_again();
1036 let [x, y] = self.touch_aimed(self.m.touch.down);
1037 self.touch_out(plat::E_MOVE, 0, x, y);
1038 self.touch_out(plat::E_DOWN, BTN_LEFT as u8, x, y);
1039 self.touch_out(plat::E_UP, BTN_LEFT as u8, x, y);
1040 plat::touch_reached(true);
1041 }
1042 if self.m.touch.hovered || !self.m.touch.no_click {
1043 self.uiw_use(self.m.touch.win);
1045 let ms = if self.tip_showing() { TIP_LEAVE_MS } else { LEAVE_MS };
1046 self.m.touch.leave_at = Some(t.wrapping_add(ms));
1047 }
1048 }
1049 Mode::Widget => {
1050 let [ax, ay] = self.touch_aimed([ev.x, ev.y]);
1051 let (x, y) = if cancel { (OUTSIDE, OUTSIDE) } else { (ax, ay) };
1052 self.touch_out(plat::E_UP, BTN_LEFT as u8, x, y);
1053 self.m.touch.leave_at = Some(t.wrapping_add(LEAVE_MS));
1054 }
1055 Mode::Pan if !cancel => {
1056 self.uiw_use(self.m.touch.win);
1057 self.touch_fling(t);
1058 let s = &mut self.m.touch;
1060 if s.fling.is_none() {
1061 s.pend[0] = s.pend[0].wrapping_add(core::mem::take(&mut s.catchup[0]));
1062 s.pend[1] = s.pend[1].wrapping_add(core::mem::take(&mut s.catchup[1]));
1063 }
1064 }
1065 Mode::Gone if self.m.touch.hovered => self.m.touch.leave_at = Some(t.wrapping_add(LEAVE_MS)),
1066 _ => {}
1067 }
1068 }
1069
1070 fn touch_velocity(&self, k: usize, up: u32) -> f64 {
1073 let s = &self.m.touch;
1074 if s.n < 2 || up.wrapping_sub(s.samples[s.n - 1].0) > VEL_STOPPED_MS {
1075 return 0.0;
1076 }
1077 let last = s.samples[s.n - 1];
1078 let at = |e: &(u32, i32, i32)| if k == 0 { e.1 } else { e.2 };
1079 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);
1080 let mut first = 0.0f64;
1081 let mut stamps = 0;
1082 let mut prev = u32::MAX;
1083 for e in s.samples[..s.n].iter().rev() {
1084 let age = last.0.wrapping_sub(e.0);
1085 if age > VEL_HORIZON_MS {
1086 break;
1087 }
1088 let t = -(age as f64) / 1000.0;
1089 let x = (at(e) - at(&last)) as f64;
1090 first = t;
1091 if age != prev {
1092 stamps += 1;
1093 prev = age;
1094 }
1095 n += 1.0;
1096 st += t;
1097 st2 += t * t;
1098 st3 += t * t * t;
1099 st4 += t * t * t * t;
1100 sx += x;
1101 stx += t * x;
1102 st2x += t * t * x;
1103 }
1104 if stamps < 2 || first == 0.0 {
1105 return 0.0;
1106 }
1107 if stamps >= 3 {
1108 let det = n * (st2 * st4 - st3 * st3) - st * (st * st4 - st3 * st2) + st2 * (st * st3 - st2 * st2);
1110 let d = if det < 0.0 { -det } else { det };
1111 if d > 1e-18 {
1112 let db = n * (stx * st4 - st3 * st2x) - sx * (st * st4 - st3 * st2) + st2 * (st * st2x - st2 * stx);
1113 return db / det;
1114 }
1115 }
1116 let det = n * st2 - st * st;
1118 if det == 0.0 { 0.0 } else { (n * stx - st * sx) / det }
1119 }
1120
1121 fn touch_fling(&mut self, up: u32) {
1123 let (vx, vy) = (
1124 if self.m.touch.axes & AX_H != 0 { self.touch_velocity(0, up) } else { 0.0 },
1125 if self.m.touch.axes & AX_V != 0 { self.touch_velocity(1, up) } else { 0.0 },
1126 );
1127 let mut speed = sqrt(vx * vx + vy * vy);
1128 let (lo, hi) = (self.dp(self.m.touch.conf[1]) as f64, self.dp(self.m.touch.conf[2]) as f64);
1129 if !(speed >= lo) {
1130 return;
1131 }
1132 let (ux, uy) = (vx / speed, vy / speed);
1133 if speed > hi {
1134 speed = hi;
1135 }
1136 let ppi = self.wp.rt.dpi as f64 * 160.0 / 96.0;
1138 let phys = G_INCH * ppi;
1139 let r = INFLEXION * speed / (FRICTION * phys);
1140 let dur = 1000.0 * rt::x87::pow(r, 1.0 / (DECEL - 1.0));
1141 let way = FRICTION * phys * rt::x87::pow(r, DECEL / (DECEL - 1.0));
1142 if self.m.touch.spline.is_empty() {
1143 self.m.touch.spline = spline_table();
1144 }
1145 let s = &mut self.m.touch;
1146 s.fling = Some(Fling {
1147 t0: up,
1148 dur,
1149 dist: [way * ux + s.catchup[0] as f64, way * uy + s.catchup[1] as f64],
1151 done: [0.0; 2],
1152 });
1153 self.animating = true;
1154 }
1155
1156 pub(crate) fn touch_tick(&mut self) {
1158 let now = self.now_ms;
1159 let s = &self.m.touch;
1160 if s.mode == Mode::Wait && !s.no_click && !s.long_done {
1161 let held = now.wrapping_sub(s.down_t);
1162 let [x, y] = self.touch_aimed(s.down);
1163 let s = &mut self.m.touch;
1164 if s.grab & G_HOLD_LONG != 0 {
1165 s.repaint = true; }
1167 if held >= HOVER_MS && !s.hovered {
1168 self.m.touch.hovered = true;
1169 self.touch_out(plat::E_MOVE, 0, x, y);
1170 }
1171 let long = if self.m.touch.grab & G_HOLD_LONG != 0 { HOLD_LONG_MS } else { self.m.touch.conf[3] as u32 };
1172 if held >= long {
1173 self.uiw_use(self.m.touch.win);
1174 let (hit, grab) = self.touch_under();
1175 if grab & (G_HOLD | G_REST) != 0 {
1176 if grab & G_HOLD != 0 {
1178 plat::touch_long_press();
1179 }
1180 self.touch_press();
1181 return;
1182 }
1183 let c = &self.wp.ctx;
1184 let id = hit.map_or(0, |i| c.hits[c.hit_cur ^ 1][i].id);
1185 self.m.touch.long_id = id;
1186 self.m.touch.long_done = true;
1187 self.touch_out(plat::E_MOVE, 0, x, y);
1188 self.touch_out(plat::E_DOWN, BTN_RIGHT as u8, x, y);
1189 self.touch_out(plat::E_UP, BTN_RIGHT as u8, x, y);
1190 plat::touch_long_press();
1191 }
1192 } else if self.m.touch.mode == Mode::Idle {
1193 if let Some(at) = self.m.touch.leave_at {
1194 if (now.wrapping_sub(at) as i32) >= 0 {
1195 self.m.touch.leave_at = None;
1196 self.touch_out(plat::E_LEAVE, 0, -1, -1);
1197 }
1198 }
1199 }
1200 }
1201
1202 pub(crate) fn touch_busy(&self) -> bool {
1205 let s = &self.m.touch;
1206 s.mode != Mode::Idle || s.fling.is_some() || s.leave_at.is_some() || !s.out.is_empty() || s.pend != [0; 2] || s.repaint
1207 }
1208
1209 pub(crate) fn touch_moved(&self) -> bool {
1211 self.m.touch.pend != [0; 2] || self.m.touch.repaint
1212 }
1213
1214 pub(crate) fn touch_apply(&mut self) {
1217 let s = &mut self.m.touch;
1218 s.take.clear();
1219 s.repaint = false;
1220 let mut d = core::mem::take(&mut s.pend);
1221 let mut flinging = false;
1222 if let Some(f) = s.fling.as_mut() {
1223 flinging = true;
1224 let el = self.now_ms.wrapping_sub(f.t0) as f64;
1225 let part = if el >= f.dur {
1226 1.0
1227 } else {
1228 let p = el / f.dur * 100.0;
1229 let k = p as usize;
1230 s.spline[k] + (p - k as f64) * (s.spline[k + 1] - s.spline[k])
1231 };
1232 for k in 0..2 {
1233 let step = (f.dist[k] * part - f.done[k]) as i32; f.done[k] += step as f64;
1235 d[k] = d[k].wrapping_add(step);
1236 }
1237 if part >= 1.0 {
1238 s.fling = None;
1239 }
1240 }
1241 if d == [0; 2] {
1242 return;
1243 }
1244 let win = s.win;
1245 if win as usize >= WIN_MAX || (win != 0 && !self.uiw_drawable(win)) {
1246 self.m.touch.fling = None;
1247 return;
1248 }
1249 self.uiw_use(win);
1250 if self.rtl {
1251 d[0] = d[0].wrapping_neg(); }
1253 let mut moved = false;
1254 let Ui { wp, m, slots, .. } = self;
1255 let W { pans, hit_cur, .. } = &wp.ctx;
1256 let views = &pans[*hit_cur ^ 1];
1257 let t = &mut m.touch;
1258 for &id in t.chain.iter() {
1259 let Some(v) = views.iter().find(|v| v.id == id) else { continue };
1260 let mut np = v.pos;
1261 for (k, ax) in [(0, AX_H), (1, AX_V)] {
1262 if v.axes & ax != 0 && d[k] != 0 {
1263 np[k] = v.pos[k].wrapping_sub(d[k]).clamp(0, v.max[k].max(0));
1264 d[k] = d[k].wrapping_sub(v.pos[k].wrapping_sub(np[k]));
1265 }
1266 }
1267 if np == v.pos {
1268 continue;
1269 }
1270 moved = true;
1271 if v.own {
1272 if t.take.len() < 16 {
1273 t.take.push((id, np[0].wrapping_sub(v.pos[0]), np[1].wrapping_sub(v.pos[1])));
1274 }
1275 } else if let Some(sl) = slots.get_mut(&id) {
1276 if v.axes & AX_H != 0 {
1277 sl.scrollx = np[0];
1278 sl.at0 = np[0] as u32;
1279 }
1280 if v.axes & AX_V != 0 {
1281 sl.scrolly = np[1];
1282 sl.adur = np[1] as u32;
1283 }
1284 }
1285 }
1286 if flinging && !moved {
1288 t.fling = None;
1289 }
1290 self.uiw_use(0);
1291 }
1292
1293 pub(crate) fn touch_after_passes(&mut self) {
1296 if self.m.touch.fling.is_some() {
1297 self.animating = true;
1298 }
1299 let ask = core::mem::take(&mut self.m.touch.kb_ask);
1300 if !ask && !self.m.touch.kb_on {
1301 return;
1302 }
1303 let win = self.m.win.active;
1304 self.uiw_use(win);
1305 let c = &self.wp.ctx;
1306 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);
1307 let s = &mut self.m.touch;
1308 let want = text && (s.kb_on || (ask && s.on));
1309 if want != s.kb_on {
1310 s.kb_on = want;
1311 s.kb_win = win;
1312 if !self.headless {
1313 plat::touch_keyboard(self.uiw_pw(self.m.touch.kb_win), want);
1314 }
1315 }
1316 self.uiw_use(0);
1317 }
1318
1319 pub(crate) fn touch_hold_show(&mut self, id: u32) {
1322 let Some(part) = self.touch_hold_part(id) else { return };
1323 let [x, y, w, h] = self.wp.ctx.last_rect;
1324 let t = self.dp(3);
1325 let c = self.color(T_ACCENT);
1326 self.draw_color(c);
1327 self.draw_rect(x, y + h - t, (w as i64 * part as i64 / 1000) as i32, t);
1328 }
1329
1330 pub(crate) fn touch_window_drag(&mut self, i: u32, pressed: bool) {
1333 let c = &self.wp.ctx;
1334 let (dx, dy) = (c.mx.wrapping_sub(c.press_x), c.my.wrapping_sub(c.press_y));
1335 let dpi = self.wp.rt.dpi;
1336 let (dx, dy) = (px2dip_at(dx, dpi), px2dip_at(dy, dpi));
1337 let iu = (i as usize) & (WIN_MAX - 1);
1338 let rc = self.m.win.recs[iu].rect.get();
1339 if pressed {
1340 self.m.touch.win_base = [rc.x, rc.y];
1341 }
1342 let [bx, by] = self.m.touch.win_base;
1343 let fb = self.win_fb(0);
1344 let mdpi = self.win_dpi(0);
1345 let (sw, sh) = (px2dip_at(fb.w, mdpi), px2dip_at(fb.h, mdpi));
1346 let wfb = self.win_fb(i);
1347 let w = px2dip_at(wfb.w, self.win_dpi(i));
1348 let (sx, sy) = self.uiw_snap_dip(i, bx.wrapping_add(dx), by.wrapping_add(dy));
1349 let [x, y] = plat::reach_clamp([sx, sy], w, [sw, sh], 96);
1350 if x != rc.x || y != rc.y {
1351 self.uiw_user_rect(i, x, y, 0, 0);
1352 }
1353 }
1354}