gesture.rs
1 /* This file is part of DarkFi (https://dark.fi) 2 * 3 * Copyright (C) 2020-2025 Dyne.org foundation 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation, either version 3 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19 use async_trait::async_trait; 20 use darkfi_serial::serialize; 21 use miniquad::TouchPhase; 22 use std::sync::{Arc, Mutex as SyncMutex}; 23 24 use crate::{ 25 gfx::Point, 26 prop::{PropertyUint32, Role}, 27 scene::{Pimpl, SceneNodeWeak}, 28 }; 29 30 use super::UIObject; 31 32 macro_rules! d { ($($arg:tt)*) => { debug!(target: "ui::gesture", $($arg)*); } } 33 macro_rules! t { ($($arg:tt)*) => { trace!(target: "ui::gesture", $($arg)*); } } 34 35 /// Maximum number of simultaneous touch events. 36 /// Put 3 here because any more is ridiculous. 37 const MAX_TOUCH: usize = 3; 38 39 #[derive(Clone)] 40 struct GestureState { 41 start: [Option<Point>; MAX_TOUCH], 42 curr: [Option<Point>; MAX_TOUCH], 43 } 44 45 pub type GesturePtr = Arc<Gesture>; 46 47 pub struct Gesture { 48 node: SceneNodeWeak, 49 priority: PropertyUint32, 50 state: SyncMutex<GestureState>, 51 } 52 53 impl Gesture { 54 pub async fn new(node: SceneNodeWeak) -> Pimpl { 55 t!("Gesture::new()"); 56 57 let node_ref = &node.upgrade().unwrap(); 58 let priority = PropertyUint32::wrap(node_ref, Role::Internal, "priority", 0).unwrap(); 59 60 let state = GestureState { start: [None; MAX_TOUCH], curr: [None; MAX_TOUCH] }; 61 62 let self_ = Arc::new(Self { node, priority, state: SyncMutex::new(state) }); 63 64 Pimpl::Gesture(self_) 65 } 66 67 fn handle_update(&self, state: GestureState) -> Option<f32> { 68 let Some(start_1) = state.start[0] else { return None }; 69 let curr_1 = state.curr[0].unwrap(); 70 71 let Some(start_2) = state.start[1] else { return None }; 72 let curr_2 = state.curr[1].unwrap(); 73 74 let start_dist_sq = start_1.dist_sq(start_2); 75 let curr_dist_sq = curr_1.dist_sq(curr_2); 76 let r = (curr_dist_sq / start_dist_sq).sqrt(); 77 78 Some(r) 79 } 80 } 81 82 #[async_trait] 83 impl UIObject for Gesture { 84 fn priority(&self) -> u32 { 85 self.priority.get() 86 } 87 88 async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool { 89 //t!("handle_touch({phase:?}, {id}, {touch_pos:?})"); 90 let id = id as usize; 91 if id >= MAX_TOUCH { 92 return false 93 } 94 95 match phase { 96 TouchPhase::Started => { 97 let mut state = self.state.lock().unwrap(); 98 state.start[id] = Some(touch_pos); 99 state.curr[id] = Some(touch_pos); 100 false 101 } 102 TouchPhase::Moved => { 103 let state = { 104 let mut state = self.state.lock().unwrap(); 105 state.curr[id] = Some(touch_pos); 106 state.clone() 107 }; 108 109 if let Some(update) = self.handle_update(state) { 110 let node = self.node.upgrade().unwrap(); 111 d!("Gesture invoked: {update}"); 112 node.trigger("gesture", serialize(&update)).await.unwrap(); 113 } 114 115 false 116 } 117 TouchPhase::Ended | TouchPhase::Cancelled => { 118 let mut state = self.state.lock().unwrap(); 119 state.start = [None; MAX_TOUCH]; 120 state.curr = [None; MAX_TOUCH]; 121 false 122 } 123 } 124 } 125 }