/ bin / app / src / ui / gesture.rs
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  }