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 #[allow(clippy::unnecessary_cast)]
212 {
213 *start_coords_clone.borrow_mut() = (e.client_x() as f64, e.client_y() as f64);
214 }
215
216 let onstart_ref = onstart_ref.current();
218 let onstart = &mut *onstart_ref.borrow_mut();
219 if let Some(onstart) = onstart {
220 onstart(e.clone());
221 }
222
223 *timeout_ref.borrow_mut() = None;
225
226 let e_clone = e.clone();
228 let long_pressed_clone = long_pressed.clone();
229 let onlongpress_ref_clone = onlongpress_ref.clone();
230 let start_coords_clone2 = start_coords_clone.clone();
231 let move_threshold = move_threshold;
232 let timeout = Timeout::new(threshold, move || {
233 if let Some(move_threshold) = move_threshold {
235 let (start_x, start_y) = *start_coords_clone2.borrow();
236 #[allow(clippy::unnecessary_cast)]
237 let current_x = e_clone.client_x() as f64;
238 #[allow(clippy::unnecessary_cast)]
239 let current_y = e_clone.client_y() as f64;
240 let distance =
241 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
242
243 if distance > move_threshold {
244 return; }
246 }
247
248 long_pressed_clone.set(true);
249
250 let onlongpress_ref = onlongpress_ref_clone.current();
252 let onlongpress = &mut *onlongpress_ref.borrow_mut();
253 if let Some(onlongpress) = onlongpress {
254 onlongpress(e_clone);
255 }
256 });
257
258 *timeout_ref.borrow_mut() = Some(timeout);
259 });
260 }
261
262 {
264 let pressing = pressing.clone();
265 let long_pressed = long_pressed.clone();
266 let timeout_ref = timeout_ref.clone();
267 let onstart_ref = onstart_ref.clone();
268 let onlongpress_ref = onlongpress_ref.clone();
269 let start_coords_clone3 = start_coords.clone();
272 use_event(node.clone(), "touchstart", move |e: TouchEvent| {
273 if prevent_default {
274 e.prevent_default();
275 }
276
277 let mouse_event = MouseEvent::new("mousedown").unwrap();
279
280 pressing.set(true);
282 long_pressed.set(false);
283
284 if let Some(touch) = e.touches().get(0) {
286 *start_coords_clone3.borrow_mut() =
287 (touch.client_x() as f64, touch.client_y() as f64);
288 }
289
290 let onstart_ref = onstart_ref.current();
292 let onstart = &mut *onstart_ref.borrow_mut();
293 if let Some(onstart) = onstart {
294 onstart(mouse_event.clone());
295 }
296
297 *timeout_ref.borrow_mut() = None;
299
300 let mouse_event_clone = mouse_event.clone();
302 let long_pressed_clone = long_pressed.clone();
303 let onlongpress_ref_clone = onlongpress_ref.clone();
304 let start_coords_clone4 = start_coords_clone3.clone();
305 let move_threshold = move_threshold;
306 let timeout = Timeout::new(threshold, move || {
307 if let Some(move_threshold) = move_threshold {
309 let (start_x, start_y) = *start_coords_clone4.borrow();
310 let current_x = e
312 .changed_touches()
313 .get(0)
314 .map_or(start_x, |t| t.client_x() as f64);
315 let current_y = e
316 .changed_touches()
317 .get(0)
318 .map_or(start_y, |t| t.client_y() as f64);
319 let distance =
320 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
321
322 if distance > move_threshold {
323 return; }
325 }
326
327 long_pressed_clone.set(true);
328
329 let onlongpress_ref = onlongpress_ref_clone.current();
331 let onlongpress = &mut *onlongpress_ref.borrow_mut();
332 if let Some(onlongpress) = onlongpress {
333 onlongpress(mouse_event_clone);
334 }
335 });
336
337 *timeout_ref.borrow_mut() = Some(timeout);
338 });
339 }
340
341 {
343 let pressing = pressing.clone();
344 let long_pressed = long_pressed.clone();
345 let timeout_ref = timeout_ref.clone();
346 let onend_ref = onend_ref.clone();
347 let pressing_clone = pressing.clone();
350 let timeout_ref_clone = timeout_ref.clone();
351 let long_pressed_clone = long_pressed.clone();
352 let start_coords_clone5 = start_coords.clone();
353 use_event_with_window("mousemove", move |e: MouseEvent| {
354 if *pressing_clone && move_threshold.is_some() {
355 let (start_x, start_y) = *start_coords_clone5.borrow();
356 #[allow(clippy::unnecessary_cast)]
357 let current_x = e.client_x() as f64;
358 #[allow(clippy::unnecessary_cast)]
359 let current_y = e.client_y() as f64;
360 let distance =
361 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
362
363 if let Some(move_threshold) = move_threshold {
364 if distance > move_threshold {
365 *timeout_ref_clone.borrow_mut() = None;
367 pressing_clone.set(false);
368 long_pressed_clone.set(false);
369 }
370 }
371 }
372 });
373
374 let pressing_clone2 = pressing.clone();
375 let long_pressed_clone2 = long_pressed.clone();
376 let timeout_ref_clone2 = timeout_ref.clone();
377 let onend_ref_clone = onend_ref.clone();
378 use_event_with_window("mouseup", move |e: MouseEvent| {
379 if *pressing_clone2 {
380 *timeout_ref_clone2.borrow_mut() = None;
382
383 pressing_clone2.set(false);
385
386 if !*long_pressed_clone2 {
388 let onend_ref = onend_ref_clone.current();
389 let onend = &mut *onend_ref.borrow_mut();
390 if let Some(onend) = onend {
391 onend(e);
392 }
393 }
394
395 long_pressed_clone2.set(false);
397 }
398 });
399 }
400
401 {
403 let pressing = pressing.clone();
404 let long_pressed = long_pressed.clone();
405 let timeout_ref = timeout_ref.clone();
406 let onend_ref = onend_ref.clone();
407 let pressing_clone3 = pressing.clone();
410 let timeout_ref_clone3 = timeout_ref.clone();
411 let long_pressed_clone3 = long_pressed.clone();
412 let start_coords_clone6 = start_coords.clone();
413 use_event_with_window("touchmove", move |e: TouchEvent| {
414 if *pressing_clone3 && move_threshold.is_some() {
415 let (start_x, start_y) = *start_coords_clone6.borrow();
416 if let Some(touch) = e.touches().get(0) {
417 let current_x = touch.client_x() as f64;
418 let current_y = touch.client_y() as f64;
419 let distance =
420 ((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
421
422 if let Some(move_threshold) = move_threshold {
423 if distance > move_threshold {
424 *timeout_ref_clone3.borrow_mut() = None;
426 pressing_clone3.set(false);
427 long_pressed_clone3.set(false);
428 }
429 }
430 }
431 }
432 });
433
434 let pressing_clone4 = pressing.clone();
435 let long_pressed_clone4 = long_pressed.clone();
436 let timeout_ref_clone4 = timeout_ref.clone();
437 let onend_ref_clone2 = onend_ref.clone();
438 use_event_with_window("touchend", move |_e: TouchEvent| {
439 if *pressing_clone4 {
440 let mouse_event = MouseEvent::new("mouseup").unwrap();
442
443 *timeout_ref_clone4.borrow_mut() = None;
445
446 pressing_clone4.set(false);
448
449 if !*long_pressed_clone4 {
451 let onend_ref = onend_ref_clone2.current();
452 let onend = &mut *onend_ref.borrow_mut();
453 if let Some(onend) = onend {
454 onend(mouse_event);
455 }
456 }
457
458 long_pressed_clone4.set(false);
460 }
461 });
462 }
463
464 {
466 let cancel = cancel.clone();
467
468 use_event(node.clone(), "mouseleave", move |_: MouseEvent| {
469 cancel();
470 });
471 }
472
473 {
474 let cancel = cancel.clone();
475
476 use_event(node.clone(), "touchcancel", move |_: TouchEvent| {
477 cancel();
478 });
479 }
480
481 {
483 let cancel = cancel.clone();
484 use_effect_with((), move |_| {
485 move || {
486 cancel();
487 }
488 });
489 }
490
491 UseLongPressHandle {
492 pressing,
493 long_pressed,
494 cancel,
495 }
496}