ferroui_base/input/gesture_recognizers/
pinch_gesture_recognizer.rs1use super::{GestureRecognizer, GestureRecognizerImpl};
2use crate::input::{
3 IPointer, PinchEndedEventArgs, PinchEventArgs, PointerEventArgs, PointerPressedEventArgs,
4 PointerReleasedEventArgs, PointerType,
5};
6use crate::{ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, Point, Ref, StyledElementImpl, Vector};
7use std::cell::{Cell, RefCell};
8use std::rc::Rc;
9
10fn same_pointer(a: &Option<Rc<dyn IPointer>>, b: &Rc<dyn IPointer>) -> bool {
11 a.as_ref().is_some_and(|a| std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b)))
12}
13
14#[repr(C)]
17pub struct PinchGestureRecognizer {
18 base: GestureRecognizer,
19 initial_distance: Cell<f32>,
20 first_contact: RefCell<Option<Rc<dyn IPointer>>>,
21 first_point: Cell<Point>,
22 second_contact: RefCell<Option<Rc<dyn IPointer>>>,
23 second_point: Cell<Point>,
24 origin: Cell<Point>,
25 previous_angle: Cell<f64>,
26}
27
28ferro_class!(PinchGestureRecognizer: GestureRecognizer);
29crate::ferro_class_info!(PinchGestureRecognizer { new: PinchGestureRecognizer::new });
30ferro_impl_classes!(PinchGestureRecognizer: FerroObjectImpl, StyledElementImpl);
31
32impl GestureRecognizerImpl for PinchGestureRecognizer {
33 fn pointer_capture_lost(this: &Self, pointer: &Rc<dyn IPointer>) {
34 this.remove_contact(pointer);
35 }
36
37 fn pointer_moved(this: &Self, e: &PointerEventArgs) {
38 let Some(target) = this.target() else { return };
39
40 if same_pointer(&this.first_contact.borrow(), e.pointer()) {
41 this.first_point.set(e.get_position(Some(&target)));
42 } else if same_pointer(&this.second_contact.borrow(), e.pointer()) {
43 this.second_point.set(e.get_position(Some(&target)));
44 } else {
45 return;
46 }
47
48 if this.first_contact.borrow().is_some() && this.second_contact.borrow().is_some() {
49 let distance = Self::get_distance(this.first_point.get(), this.second_point.get());
50 let scale = (distance / this.initial_distance.get()) as f64;
51 let degree = Self::get_angle_degree_from_points(this.first_point.get(), this.second_point.get());
52
53 let pinch_event_args =
54 PinchEventArgs::with_angle(scale, this.origin.get(), degree, this.previous_angle.get() - degree);
55 this.previous_angle.set(degree);
56 target.raise_event(&pinch_event_args);
57
58 e.set_handled(pinch_event_args.handled());
59 e.prevent_gesture_recognition();
60 }
61 }
62
63 fn pointer_pressed(this: &Self, e: &PointerPressedEventArgs) {
64 let Some(target) = this.target() else { return };
65
66 if e.pointer().type_() != PointerType::Touch && e.pointer().type_() != PointerType::Pen {
67 return;
68 }
69
70 if this.first_contact.borrow().is_none() {
71 *this.first_contact.borrow_mut() = Some(e.pointer().clone());
72 this.first_point.set(e.get_position(Some(&target)));
73 return;
74 } else if this.second_contact.borrow().is_none() && !same_pointer(&this.first_contact.borrow(), e.pointer()) {
75 *this.second_contact.borrow_mut() = Some(e.pointer().clone());
76 this.second_point.set(e.get_position(Some(&target)));
77 } else {
78 return;
79 }
80
81 let first = this.first_contact.borrow().clone();
82 let second = this.second_contact.borrow().clone();
83
84 if let (Some(first), Some(second)) = (first, second) {
85 let (first_point, second_point) = (this.first_point.get(), this.second_point.get());
86 this.initial_distance.set(Self::get_distance(first_point, second_point));
87
88 this.origin
89 .set(Point::new((first_point.x + second_point.x) / 2.0, (first_point.y + second_point.y) / 2.0));
90
91 this.previous_angle.set(Self::get_angle_degree_from_points(first_point, second_point));
92
93 this.capture(&first);
94 this.capture(&second);
95 e.prevent_gesture_recognition();
96 }
97 }
98
99 fn pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
100 if this.remove_contact(e.pointer()) {
101 e.prevent_gesture_recognition();
102 }
103 }
104}
105
106impl PinchGestureRecognizer {
107 pub fn new() -> Ref<Self> {
108 instantiate(Self {
109 base: GestureRecognizer::construct(),
110 initial_distance: Cell::new(0.0),
111 first_contact: RefCell::new(None),
112 first_point: Cell::new(Point::default()),
113 second_contact: RefCell::new(None),
114 second_point: Cell::new(Point::default()),
115 origin: Cell::new(Point::default()),
116 previous_angle: Cell::new(0.0),
117 })
118 }
119
120 fn remove_contact(&self, pointer: &Rc<dyn IPointer>) -> bool {
121 let is_first = same_pointer(&self.first_contact.borrow(), pointer);
122 let is_second = same_pointer(&self.second_contact.borrow(), pointer);
123
124 if is_first || is_second {
125 if is_second {
126 drop(self.second_contact.replace(None));
127 }
128
129 if is_first {
130 let second = self.second_contact.replace(None);
131 drop(self.first_contact.replace(second));
132 }
133
134 if let Some(target) = self.target() {
135 target.raise_event(&PinchEndedEventArgs::new());
136 }
137
138 return true;
139 }
140
141 false
142 }
143
144 fn get_distance(a: Point, b: Point) -> f32 {
145 let length = b - a;
146 Vector::new(length.x, length.y).length() as f32
147 }
148
149 fn get_angle_degree_from_points(a: Point, b: Point) -> f64 {
150 let delta_x = a.x - b.x;
151 let delta_y = -(a.y - b.y);
154 let rad = delta_x.atan2(delta_y);
156 (rad * (180.0 / std::f64::consts::PI)) + 180.0
159 }
160}