yew_hooks/hooks/
use_long_press.rs1use std::rc::Rc;
2
3use gloo::timers::callback::Timeout;
4use yew::prelude::*;
5
6use super::{use_event, use_event_with_window, use_mut_latest};
7
8#[derive(Default)]
10pub struct UseLongPressOptions {
11 pub onstart: Option<Box<dyn FnMut(MouseEvent)>>,
13 pub onend: Option<Box<dyn FnMut(MouseEvent)>>,
15 pub onlongpress: Option<Box<dyn FnMut(MouseEvent)>>,
17 pub threshold: Option<u32>,
19 pub move_threshold: Option<f64>,
21 pub prevent_default: Option<bool>,
23}
24
25pub struct UseLongPressHandle {
27 pub pressing: UseStateHandle<bool>,
29 pub long_pressed: UseStateHandle<bool>,
31 cancel: Rc<dyn Fn()>,
33}
34
35impl UseLongPressHandle {
36 pub fn cancel(&self) {
38 (self.cancel)();
39 }
40}
41
42impl Clone for UseLongPressHandle {
43 fn clone(&self) -> Self {
44 Self {
45 pressing: self.pressing.clone(),
46 long_pressed: self.long_pressed.clone(),
47 cancel: self.cancel.clone(),
48 }
49 }
50}
51
52#[hook]
96pub fn use_long_press<F>(node: NodeRef, threshold: u32, onlongpress: F) -> UseLongPressHandle
97where
98 F: FnMut(MouseEvent) + 'static,
99{
100 let options = UseLongPressOptions {
101 threshold: Some(threshold),
102 onlongpress: Some(Box::new(onlongpress)),
103 ..Default::default()
104 };
105
106 use_long_press_with_options(node, options)
107}
108
109#[hook]
154pub fn use_long_press_with_options(
155 node: NodeRef,
156 options: UseLongPressOptions,
157) -> UseLongPressHandle {
158 let pressing = use_state(|| false);
159 let long_pressed = use_state(|| false);
160 let timeout_ref = use_mut_ref(|| None::<Timeout>);
161
162 let onstart_ref = use_mut_latest(options.onstart);
163 let onend_ref = use_mut_latest(options.onend);
164 let onlongpress_ref = use_mut_latest(options.onlongpress);
165 let threshold = options.threshold.unwrap_or(500);
166 let move_threshold = options.move_threshold;
167 let prevent_default = options.prevent_default.unwrap_or(true);
168
169 let start_coords = use_mut_ref(|| (0.0, 0.0));
170
171 let cancel = {
172 let timeout_ref = timeout_ref.clone();
173 let pressing = pressing.clone();
174 let long_pressed = long_pressed.clone();
175 Rc::new(move || {
178 *timeout_ref.borrow_mut() = None;
180
181 if *pressing {
183 pressing.set(false);
184 }
185 if *long_pressed {
186 long_pressed.set(false);
187 }
188 })
189 };
190
191 {
193 let pressing = pressing.clone();
194 let long_pressed = long_pressed.clone();
195 let timeout_ref = timeout_ref.clone();
196 let onstart_ref = onstart_ref.clone();
197 let onlongpress_ref = onlongpress_ref.clone();
198 let start_coords_clone = start_coords.clone();
201 use_event(node.clone(), "mousedown", move |e: MouseEvent| {
202 if prevent_default {
203 e.prevent_default();
204 }
205
206 pressing.set(true);
208 long_pressed.set(false);
209
210 *start_coords_clone.borrow_mut() = (e.client_x() as f64, e.client_y() as f64);
212
213 let onstart_ref = onstart_ref.current();
215 let onstart = &mut *onstart_ref.borrow_mut();
216 if let Some(onstart) = onstart {
217 onstart(e.clone());
218 }
219
220 *timeout_ref.borrow_mut() = None;
222
223 let e_clone = e.clone();
225 let long_pressed_clone = long_pressed.clone();
226 let onlongpress_ref_clone = onlongpress_ref.clone();
227 let start_coords_clone2 = start_coords_clone.clone();
228 let move_threshold = move_threshold;
229 let timeout = Timeout::new(threshold, move || {
230 if let Some(move_threshold) = move_threshold {
232 let (start_x, start_y) = *start_coords_clone2.borrow();
233 let current_x = e_clone.client_x() as f64;
234 let current_y = e_clone.client_y() as f64;
235 let distance =
236 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
237
238 if distance > move_threshold {
239 return; }
241 }
242
243 long_pressed_clone.set(true);
244
245 let onlongpress_ref = onlongpress_ref_clone.current();
247 let onlongpress = &mut *onlongpress_ref.borrow_mut();
248 if let Some(onlongpress) = onlongpress {
249 onlongpress(e_clone);
250 }
251 });
252
253 *timeout_ref.borrow_mut() = Some(timeout);
254 });
255 }
256
257 {
259 let pressing = pressing.clone();
260 let long_pressed = long_pressed.clone();
261 let timeout_ref = timeout_ref.clone();
262 let onstart_ref = onstart_ref.clone();
263 let onlongpress_ref = onlongpress_ref.clone();
264 let start_coords_clone3 = start_coords.clone();
267 use_event(node.clone(), "touchstart", move |e: TouchEvent| {
268 if prevent_default {
269 e.prevent_default();
270 }
271
272 let mouse_event = MouseEvent::new("mousedown").unwrap();
274
275 pressing.set(true);
277 long_pressed.set(false);
278
279 if let Some(touch) = e.touches().get(0) {
281 *start_coords_clone3.borrow_mut() =
282 (touch.client_x() as f64, touch.client_y() as f64);
283 }
284
285 let onstart_ref = onstart_ref.current();
287 let onstart = &mut *onstart_ref.borrow_mut();
288 if let Some(onstart) = onstart {
289 onstart(mouse_event.clone());
290 }
291
292 *timeout_ref.borrow_mut() = None;
294
295 let mouse_event_clone = mouse_event.clone();
297 let long_pressed_clone = long_pressed.clone();
298 let onlongpress_ref_clone = onlongpress_ref.clone();
299 let start_coords_clone4 = start_coords_clone3.clone();
300 let move_threshold = move_threshold;
301 let timeout = Timeout::new(threshold, move || {
302 if let Some(move_threshold) = move_threshold {
304 let (start_x, start_y) = *start_coords_clone4.borrow();
305 let current_x = e
307 .changed_touches()
308 .get(0)
309 .map_or(start_x, |t| t.client_x() as f64);
310 let current_y = e
311 .changed_touches()
312 .get(0)
313 .map_or(start_y, |t| t.client_y() as f64);
314 let distance =
315 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
316
317 if distance > move_threshold {
318 return; }
320 }
321
322 long_pressed_clone.set(true);
323
324 let onlongpress_ref = onlongpress_ref_clone.current();
326 let onlongpress = &mut *onlongpress_ref.borrow_mut();
327 if let Some(onlongpress) = onlongpress {
328 onlongpress(mouse_event_clone);
329 }
330 });
331
332 *timeout_ref.borrow_mut() = Some(timeout);
333 });
334 }
335
336 {
338 let pressing = pressing.clone();
339 let long_pressed = long_pressed.clone();
340 let timeout_ref = timeout_ref.clone();
341 let onend_ref = onend_ref.clone();
342 let pressing_clone = pressing.clone();
345 let timeout_ref_clone = timeout_ref.clone();
346 let long_pressed_clone = long_pressed.clone();
347 let start_coords_clone5 = start_coords.clone();
348 use_event_with_window("mousemove", move |e: MouseEvent| {
349 if *pressing_clone && move_threshold.is_some() {
350 let (start_x, start_y) = *start_coords_clone5.borrow();
351 let current_x = e.client_x() as f64;
352 let current_y = e.client_y() as f64;
353 let distance =
354 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
355
356 if let Some(move_threshold) = move_threshold {
357 if distance > move_threshold {
358 *timeout_ref_clone.borrow_mut() = None;
360 pressing_clone.set(false);
361 long_pressed_clone.set(false);
362 }
363 }
364 }
365 });
366
367 let pressing_clone2 = pressing.clone();
368 let long_pressed_clone2 = long_pressed.clone();
369 let timeout_ref_clone2 = timeout_ref.clone();
370 let onend_ref_clone = onend_ref.clone();
371 use_event_with_window("mouseup", move |e: MouseEvent| {
372 if *pressing_clone2 {
373 *timeout_ref_clone2.borrow_mut() = None;
375
376 pressing_clone2.set(false);
378
379 if !*long_pressed_clone2 {
381 let onend_ref = onend_ref_clone.current();
382 let onend = &mut *onend_ref.borrow_mut();
383 if let Some(onend) = onend {
384 onend(e);
385 }
386 }
387
388 long_pressed_clone2.set(false);
390 }
391 });
392 }
393
394 {
396 let pressing = pressing.clone();
397 let long_pressed = long_pressed.clone();
398 let timeout_ref = timeout_ref.clone();
399 let onend_ref = onend_ref.clone();
400 let pressing_clone3 = pressing.clone();
403 let timeout_ref_clone3 = timeout_ref.clone();
404 let long_pressed_clone3 = long_pressed.clone();
405 let start_coords_clone6 = start_coords.clone();
406 use_event_with_window("touchmove", move |e: TouchEvent| {
407 if *pressing_clone3 && move_threshold.is_some() {
408 let (start_x, start_y) = *start_coords_clone6.borrow();
409 if let Some(touch) = e.touches().get(0) {
410 let current_x = touch.client_x() as f64;
411 let current_y = touch.client_y() as f64;
412 let distance =
413 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
414
415 if let Some(move_threshold) = move_threshold {
416 if distance > move_threshold {
417 *timeout_ref_clone3.borrow_mut() = None;
419 pressing_clone3.set(false);
420 long_pressed_clone3.set(false);
421 }
422 }
423 }
424 }
425 });
426
427 let pressing_clone4 = pressing.clone();
428 let long_pressed_clone4 = long_pressed.clone();
429 let timeout_ref_clone4 = timeout_ref.clone();
430 let onend_ref_clone2 = onend_ref.clone();
431 use_event_with_window("touchend", move |_e: TouchEvent| {
432 if *pressing_clone4 {
433 let mouse_event = MouseEvent::new("mouseup").unwrap();
435
436 *timeout_ref_clone4.borrow_mut() = None;
438
439 pressing_clone4.set(false);
441
442 if !*long_pressed_clone4 {
444 let onend_ref = onend_ref_clone2.current();
445 let onend = &mut *onend_ref.borrow_mut();
446 if let Some(onend) = onend {
447 onend(mouse_event);
448 }
449 }
450
451 long_pressed_clone4.set(false);
453 }
454 });
455 }
456
457 {
459 let cancel = cancel.clone();
460
461 use_event(node.clone(), "mouseleave", move |_: MouseEvent| {
462 cancel();
463 });
464 }
465
466 {
467 let cancel = cancel.clone();
468
469 use_event(node.clone(), "touchcancel", move |_: TouchEvent| {
470 cancel();
471 });
472 }
473
474 {
476 let cancel = cancel.clone();
477 use_effect_with((), move |_| {
478 move || {
479 cancel();
480 }
481 });
482 }
483
484 UseLongPressHandle {
485 pressing,
486 long_pressed,
487 cancel,
488 }
489}