use std::rc::Rc;
use gloo::timers::callback::Timeout;
use yew::prelude::*;
use super::{use_event, use_event_with_window, use_mut_latest};
#[derive(Default)]
pub struct UseLongPressOptions {
pub onstart: Option<Box<dyn FnMut(MouseEvent)>>,
pub onend: Option<Box<dyn FnMut(MouseEvent)>>,
pub onlongpress: Option<Box<dyn FnMut(MouseEvent)>>,
pub threshold: Option<u32>,
pub move_threshold: Option<f64>,
pub prevent_default: Option<bool>,
}
pub struct UseLongPressHandle {
pub pressing: UseStateHandle<bool>,
pub long_pressed: UseStateHandle<bool>,
cancel: Rc<dyn Fn()>,
}
impl UseLongPressHandle {
pub fn cancel(&self) {
(self.cancel)();
}
}
impl Clone for UseLongPressHandle {
fn clone(&self) -> Self {
Self {
pressing: self.pressing.clone(),
long_pressed: self.long_pressed.clone(),
cancel: self.cancel.clone(),
}
}
}
#[hook]
pub fn use_long_press<F>(node: NodeRef, threshold: u32, onlongpress: F) -> UseLongPressHandle
where
F: FnMut(MouseEvent) + 'static,
{
let options = UseLongPressOptions {
threshold: Some(threshold),
onlongpress: Some(Box::new(onlongpress)),
..Default::default()
};
use_long_press_with_options(node, options)
}
#[hook]
pub fn use_long_press_with_options(
node: NodeRef,
options: UseLongPressOptions,
) -> UseLongPressHandle {
let pressing = use_state(|| false);
let long_pressed = use_state(|| false);
let timeout_ref = use_mut_ref(|| None::<Timeout>);
let onstart_ref = use_mut_latest(options.onstart);
let onend_ref = use_mut_latest(options.onend);
let onlongpress_ref = use_mut_latest(options.onlongpress);
let threshold = options.threshold.unwrap_or(500);
let move_threshold = options.move_threshold;
let prevent_default = options.prevent_default.unwrap_or(true);
let start_coords = use_mut_ref(|| (0.0, 0.0));
let cancel = {
let timeout_ref = timeout_ref.clone();
let pressing = pressing.clone();
let long_pressed = long_pressed.clone();
Rc::new(move || {
*timeout_ref.borrow_mut() = None;
if *pressing {
pressing.set(false);
}
if *long_pressed {
long_pressed.set(false);
}
})
};
{
let pressing = pressing.clone();
let long_pressed = long_pressed.clone();
let timeout_ref = timeout_ref.clone();
let onstart_ref = onstart_ref.clone();
let onlongpress_ref = onlongpress_ref.clone();
let start_coords_clone = start_coords.clone();
use_event(node.clone(), "mousedown", move |e: MouseEvent| {
if prevent_default {
e.prevent_default();
}
pressing.set(true);
long_pressed.set(false);
#[allow(clippy::unnecessary_cast)]
{
*start_coords_clone.borrow_mut() = (e.client_x() as f64, e.client_y() as f64);
}
let onstart_ref = onstart_ref.current();
let onstart = &mut *onstart_ref.borrow_mut();
if let Some(onstart) = onstart {
onstart(e.clone());
}
*timeout_ref.borrow_mut() = None;
let e_clone = e.clone();
let long_pressed_clone = long_pressed.clone();
let onlongpress_ref_clone = onlongpress_ref.clone();
let start_coords_clone2 = start_coords_clone.clone();
let move_threshold = move_threshold;
let timeout = Timeout::new(threshold, move || {
if let Some(move_threshold) = move_threshold {
let (start_x, start_y) = *start_coords_clone2.borrow();
#[allow(clippy::unnecessary_cast)]
let current_x = e_clone.client_x() as f64;
#[allow(clippy::unnecessary_cast)]
let current_y = e_clone.client_y() as f64;
let distance =
((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
if distance > move_threshold {
return; }
}
long_pressed_clone.set(true);
let onlongpress_ref = onlongpress_ref_clone.current();
let onlongpress = &mut *onlongpress_ref.borrow_mut();
if let Some(onlongpress) = onlongpress {
onlongpress(e_clone);
}
});
*timeout_ref.borrow_mut() = Some(timeout);
});
}
{
let pressing = pressing.clone();
let long_pressed = long_pressed.clone();
let timeout_ref = timeout_ref.clone();
let onstart_ref = onstart_ref.clone();
let onlongpress_ref = onlongpress_ref.clone();
let start_coords_clone3 = start_coords.clone();
use_event(node.clone(), "touchstart", move |e: TouchEvent| {
if prevent_default {
e.prevent_default();
}
let mouse_event = MouseEvent::new("mousedown").unwrap();
pressing.set(true);
long_pressed.set(false);
if let Some(touch) = e.touches().get(0) {
*start_coords_clone3.borrow_mut() =
(touch.client_x() as f64, touch.client_y() as f64);
}
let onstart_ref = onstart_ref.current();
let onstart = &mut *onstart_ref.borrow_mut();
if let Some(onstart) = onstart {
onstart(mouse_event.clone());
}
*timeout_ref.borrow_mut() = None;
let mouse_event_clone = mouse_event.clone();
let long_pressed_clone = long_pressed.clone();
let onlongpress_ref_clone = onlongpress_ref.clone();
let start_coords_clone4 = start_coords_clone3.clone();
let move_threshold = move_threshold;
let timeout = Timeout::new(threshold, move || {
if let Some(move_threshold) = move_threshold {
let (start_x, start_y) = *start_coords_clone4.borrow();
let current_x = e
.changed_touches()
.get(0)
.map_or(start_x, |t| t.client_x() as f64);
let current_y = e
.changed_touches()
.get(0)
.map_or(start_y, |t| t.client_y() as f64);
let distance =
((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
if distance > move_threshold {
return; }
}
long_pressed_clone.set(true);
let onlongpress_ref = onlongpress_ref_clone.current();
let onlongpress = &mut *onlongpress_ref.borrow_mut();
if let Some(onlongpress) = onlongpress {
onlongpress(mouse_event_clone);
}
});
*timeout_ref.borrow_mut() = Some(timeout);
});
}
{
let pressing = pressing.clone();
let long_pressed = long_pressed.clone();
let timeout_ref = timeout_ref.clone();
let onend_ref = onend_ref.clone();
let pressing_clone = pressing.clone();
let timeout_ref_clone = timeout_ref.clone();
let long_pressed_clone = long_pressed.clone();
let start_coords_clone5 = start_coords.clone();
use_event_with_window("mousemove", move |e: MouseEvent| {
if *pressing_clone && move_threshold.is_some() {
let (start_x, start_y) = *start_coords_clone5.borrow();
#[allow(clippy::unnecessary_cast)]
let current_x = e.client_x() as f64;
#[allow(clippy::unnecessary_cast)]
let current_y = e.client_y() as f64;
let distance =
((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
if let Some(move_threshold) = move_threshold {
if distance > move_threshold {
*timeout_ref_clone.borrow_mut() = None;
pressing_clone.set(false);
long_pressed_clone.set(false);
}
}
}
});
let pressing_clone2 = pressing.clone();
let long_pressed_clone2 = long_pressed.clone();
let timeout_ref_clone2 = timeout_ref.clone();
let onend_ref_clone = onend_ref.clone();
use_event_with_window("mouseup", move |e: MouseEvent| {
if *pressing_clone2 {
*timeout_ref_clone2.borrow_mut() = None;
pressing_clone2.set(false);
if !*long_pressed_clone2 {
let onend_ref = onend_ref_clone.current();
let onend = &mut *onend_ref.borrow_mut();
if let Some(onend) = onend {
onend(e);
}
}
long_pressed_clone2.set(false);
}
});
}
{
let pressing = pressing.clone();
let long_pressed = long_pressed.clone();
let timeout_ref = timeout_ref.clone();
let onend_ref = onend_ref.clone();
let pressing_clone3 = pressing.clone();
let timeout_ref_clone3 = timeout_ref.clone();
let long_pressed_clone3 = long_pressed.clone();
let start_coords_clone6 = start_coords.clone();
use_event_with_window("touchmove", move |e: TouchEvent| {
if *pressing_clone3 && move_threshold.is_some() {
let (start_x, start_y) = *start_coords_clone6.borrow();
if let Some(touch) = e.touches().get(0) {
let current_x = touch.client_x() as f64;
let current_y = touch.client_y() as f64;
let distance =
((current_x - start_x).powi(2) + (current_y - start_y).powi(2)).sqrt();
if let Some(move_threshold) = move_threshold {
if distance > move_threshold {
*timeout_ref_clone3.borrow_mut() = None;
pressing_clone3.set(false);
long_pressed_clone3.set(false);
}
}
}
}
});
let pressing_clone4 = pressing.clone();
let long_pressed_clone4 = long_pressed.clone();
let timeout_ref_clone4 = timeout_ref.clone();
let onend_ref_clone2 = onend_ref.clone();
use_event_with_window("touchend", move |_e: TouchEvent| {
if *pressing_clone4 {
let mouse_event = MouseEvent::new("mouseup").unwrap();
*timeout_ref_clone4.borrow_mut() = None;
pressing_clone4.set(false);
if !*long_pressed_clone4 {
let onend_ref = onend_ref_clone2.current();
let onend = &mut *onend_ref.borrow_mut();
if let Some(onend) = onend {
onend(mouse_event);
}
}
long_pressed_clone4.set(false);
}
});
}
{
let cancel = cancel.clone();
use_event(node.clone(), "mouseleave", move |_: MouseEvent| {
cancel();
});
}
{
let cancel = cancel.clone();
use_event(node.clone(), "touchcancel", move |_: TouchEvent| {
cancel();
});
}
{
let cancel = cancel.clone();
use_effect_with((), move |_| {
move || {
cancel();
}
});
}
UseLongPressHandle {
pressing,
long_pressed,
cancel,
}
}