2026-03-03 17:46:51 +01:00
|
|
|
use std::{
|
|
|
|
|
collections::{HashMap, VecDeque},
|
|
|
|
|
ops::Sub,
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-28 22:24:42 +01:00
|
|
|
use dioxus::{
|
2026-03-02 22:06:15 +01:00
|
|
|
html::{
|
|
|
|
|
geometry::{ElementSpace, euclid::Point2D},
|
|
|
|
|
input_data::MouseButton,
|
|
|
|
|
},
|
2026-02-28 22:24:42 +01:00
|
|
|
logger::tracing,
|
|
|
|
|
prelude::*,
|
|
|
|
|
};
|
2026-03-03 17:46:51 +01:00
|
|
|
use dioxus_html::geometry::euclid::Vector2D;
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
use crate::{components::controls::ClientEvent, utils::mouse_filter_buffer::MouseFilterBuffer};
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
const FLING_START_THRESHOLD_VELOCITY: f64 = 500.0;
|
|
|
|
|
const FLING_STOP_THRESHOLD_VELOCITY: f64 = 100.0;
|
|
|
|
|
const FLING_DAMPENING: f64 = 0.98;
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
pub struct PointerRegistryData {
|
|
|
|
|
initial_position: Point2D<f64, ElementSpace>,
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
last_positions: MouseFilterBuffer,
|
|
|
|
|
}
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
pub struct FlingerData {
|
|
|
|
|
velocity: Vector2D<f64, ElementSpace>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct PointerRegistry {
|
|
|
|
|
pointers: HashMap<i32, PointerRegistryData>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct FlingerRegistry {
|
|
|
|
|
flinger: Option<FlingerData>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[component]
|
|
|
|
|
pub fn MouseArea(onevent: EventHandler<ClientEvent>) -> Element {
|
|
|
|
|
#[css_module("/assets/styling/mouse_area.module.css")]
|
|
|
|
|
struct Styles;
|
|
|
|
|
|
|
|
|
|
let mut registry = use_signal::<PointerRegistry>(PointerRegistry::default);
|
|
|
|
|
let mut flingers = use_signal::<FlingerRegistry>(FlingerRegistry::default);
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-02 22:06:15 +01:00
|
|
|
let pointer_move_handler = use_callback(move |evt: Event<PointerData>| {
|
2026-03-03 17:46:51 +01:00
|
|
|
let mut registry = registry.write();
|
|
|
|
|
if let Some(data) = registry.pointers.get_mut(&evt.pointer_id()) {
|
2026-03-02 22:06:15 +01:00
|
|
|
evt.prevent_default();
|
|
|
|
|
let point = evt.element_coordinates();
|
2026-03-03 17:46:51 +01:00
|
|
|
let last_position = data.last_positions.back();
|
|
|
|
|
let delta = point - last_position.position;
|
|
|
|
|
|
|
|
|
|
data.last_positions
|
|
|
|
|
.push(point, wasmtimer::std::SystemTime::now());
|
|
|
|
|
|
|
|
|
|
if registry.pointers.len() == 1 {
|
|
|
|
|
onevent.call(ClientEvent::MouseMove {
|
|
|
|
|
dx: delta.x,
|
|
|
|
|
dy: delta.y,
|
|
|
|
|
});
|
|
|
|
|
} else if registry.pointers.len() == 2 {
|
|
|
|
|
onevent.call(ClientEvent::MouseScroll {
|
|
|
|
|
dx: -delta.x,
|
|
|
|
|
dy: -delta.y,
|
2026-03-02 22:06:15 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let pointer_down_handler = use_callback(move |evt: Event<PointerData>| {
|
2026-03-03 17:46:51 +01:00
|
|
|
//If any pointer is down, we cancel the flingers
|
|
|
|
|
flingers.write().flinger.take();
|
|
|
|
|
|
2026-03-02 22:06:15 +01:00
|
|
|
let point = evt.element_coordinates();
|
|
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
registry.write().pointers.insert(
|
|
|
|
|
evt.pointer_id(),
|
|
|
|
|
PointerRegistryData {
|
|
|
|
|
last_positions: MouseFilterBuffer::new(
|
|
|
|
|
10,
|
|
|
|
|
point,
|
|
|
|
|
wasmtimer::std::SystemTime::now(),
|
|
|
|
|
),
|
|
|
|
|
initial_position: point,
|
|
|
|
|
},
|
|
|
|
|
);
|
2026-03-02 22:06:15 +01:00
|
|
|
});
|
2026-03-03 17:46:51 +01:00
|
|
|
let pointer_up_handler = use_callback(move |evt: Event<PointerData>| {
|
|
|
|
|
let point = evt.element_coordinates();
|
|
|
|
|
let mut registry = registry.write();
|
|
|
|
|
let data = registry.pointers.remove(&evt.pointer_id());
|
|
|
|
|
if let Some(data) = data {
|
|
|
|
|
let distance_moved = data.initial_position - point;
|
|
|
|
|
let release_velocity = data.last_positions.average_velocity_since(
|
|
|
|
|
wasmtimer::std::SystemTime::now().sub(Duration::from_millis(100)),
|
|
|
|
|
);
|
|
|
|
|
tracing::info!("Release Velocity: {:?}", release_velocity.length());
|
|
|
|
|
|
|
|
|
|
if distance_moved.length() <= 1.0 {
|
|
|
|
|
match registry.pointers.len() {
|
|
|
|
|
0 => {
|
|
|
|
|
onevent.call(ClientEvent::Click {
|
|
|
|
|
button: MouseButton::Primary,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
1 => {
|
|
|
|
|
onevent.call(ClientEvent::Click {
|
|
|
|
|
button: MouseButton::Secondary,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
_ => {},
|
|
|
|
|
}
|
|
|
|
|
} else if release_velocity.length() > FLING_START_THRESHOLD_VELOCITY {
|
|
|
|
|
//We only fling if there are no other pointers
|
|
|
|
|
if registry.pointers.is_empty() {
|
|
|
|
|
flingers.write().flinger = Some(FlingerData {
|
|
|
|
|
velocity: release_velocity,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-02 22:06:15 +01:00
|
|
|
});
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
use_future(move || async move {
|
|
|
|
|
let mut last_frame_time = wasmtimer::std::SystemTime::now();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
wasmtimer::tokio::sleep(Duration::from_millis(16)).await;
|
|
|
|
|
let new_frame_time = wasmtimer::std::SystemTime::now();
|
|
|
|
|
let delta_seconds = new_frame_time
|
|
|
|
|
.duration_since(last_frame_time)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.as_secs_f64();
|
|
|
|
|
last_frame_time = new_frame_time;
|
|
|
|
|
|
|
|
|
|
let mut flinger = flingers.write();
|
|
|
|
|
let new_flinger = flinger.flinger.as_ref().and_then(|flinger| {
|
|
|
|
|
if flinger.velocity.length() < FLING_STOP_THRESHOLD_VELOCITY {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
onevent.call(ClientEvent::MouseMove {
|
|
|
|
|
dx: flinger.velocity.x * delta_seconds,
|
|
|
|
|
dy: flinger.velocity.y * delta_seconds,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//tracing::info!("Fling: {:?}", flinger.velocity);
|
|
|
|
|
Some(FlingerData {
|
|
|
|
|
velocity: flinger.velocity * FLING_DAMPENING,
|
2026-03-03 14:11:50 +01:00
|
|
|
})
|
2026-03-03 17:46:51 +01:00
|
|
|
}
|
2026-03-03 14:11:50 +01:00
|
|
|
});
|
2026-03-03 17:46:51 +01:00
|
|
|
flinger.flinger = new_flinger;
|
2026-03-03 14:11:50 +01:00
|
|
|
}
|
|
|
|
|
});
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-02 22:06:15 +01:00
|
|
|
rsx! {
|
|
|
|
|
div {
|
2026-03-03 17:46:51 +01:00
|
|
|
class: Styles::mouse_area,
|
2026-02-28 22:24:42 +01:00
|
|
|
|
2026-03-03 17:46:51 +01:00
|
|
|
onpointermove: pointer_move_handler,
|
|
|
|
|
onpointerdown: pointer_down_handler,
|
|
|
|
|
onpointerup: pointer_up_handler,
|
2026-02-28 22:24:42 +01:00
|
|
|
}
|
2026-03-03 17:46:51 +01:00
|
|
|
}
|
2026-02-28 22:24:42 +01:00
|
|
|
}
|