use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use inset_embedder::{Offset, PointerDataPacket, ViewId};
use inset_foundation::{App, Handle, Listener};
use crate::arena::GestureArenaManager;
use crate::converter::PointerEventConverter;
use crate::debug::{debug_print_hit_test_results, debug_print_mouse_hover_events};
use crate::events::{PointerCancelEvent, PointerEvent};
use crate::hit_test::{HitTestEntry, HitTestResult, HitTestTarget, HitTestable};
use crate::pointer_router::PointerRouter;
use crate::pointer_signal_resolver::PointerSignalResolver;
pub trait GestureBindingOverrides: HitTestable {
fn will_dispatch_event(
&self,
app: &mut App,
event: &PointerEvent,
hit_test_result: Option<&HitTestResult>,
);
}
pub trait GestureBindingOverridesObject: Sized + 'static {
fn hit_test_in_view(
self: Handle<Self>,
app: &mut App,
result: &mut HitTestResult,
position: Offset,
view_id: ViewId,
);
fn will_dispatch_event(
self: Handle<Self>,
app: &mut App,
event: &PointerEvent,
hit_test_result: Option<&HitTestResult>,
);
}
impl<T: GestureBindingOverridesObject> HitTestable for Handle<T> {
fn hit_test_in_view(
&self,
app: &mut App,
result: &mut HitTestResult,
position: Offset,
view_id: ViewId,
) {
T::hit_test_in_view(*self, app, result, position, view_id);
}
}
impl<T: GestureBindingOverridesObject> GestureBindingOverrides for Handle<T> {
fn will_dispatch_event(
&self,
app: &mut App,
event: &PointerEvent,
hit_test_result: Option<&HitTestResult>,
) {
T::will_dispatch_event(*self, app, event, hit_test_result);
}
}
#[derive(Default)]
pub struct GestureBinding {
pending_pointer_events: VecDeque<PointerEvent>,
pointer_router: Option<Handle<PointerRouter>>,
gesture_arena: Option<Handle<GestureArenaManager>>,
pointer_signal_resolver: Option<Handle<PointerSignalResolver>>,
hit_tests: HashMap<i64, HitTestResult>,
overrides: Option<Rc<dyn GestureBindingOverrides>>,
}
impl GestureBinding {
pub fn instance(app: &mut App) -> Handle<GestureBinding> {
let this: Handle<GestureBinding> = app.singleton();
if app.get(this).pointer_router.is_none() {
let router = PointerRouter::new(app);
let arena = GestureArenaManager::new(app);
let resolver = PointerSignalResolver::new(app);
let binding = app.get_mut(this);
binding.pointer_router = Some(router);
binding.gesture_arena = Some(arena);
binding.pointer_signal_resolver = Some(resolver);
}
this
}
pub fn pointer_router(self: Handle<Self>, app: &App) -> Handle<PointerRouter> {
app.get(self)
.pointer_router
.expect("GestureBinding::instance must run first")
}
pub fn gesture_arena(self: Handle<Self>, app: &App) -> Handle<GestureArenaManager> {
app.get(self)
.gesture_arena
.expect("GestureBinding::instance must run first")
}
pub fn pointer_signal_resolver(self: Handle<Self>, app: &App) -> Handle<PointerSignalResolver> {
app.get(self)
.pointer_signal_resolver
.expect("GestureBinding::instance must run first")
}
pub fn handle_pointer_data_packet(
self: Handle<Self>,
app: &mut App,
packet: PointerDataPacket,
) {
let platform = app.platform();
let events: Vec<PointerEvent> = PointerEventConverter::expand(packet.data, |view_id| {
platform
.view(view_id)
.map(|view| view.metrics().device_pixel_ratio)
})
.collect();
app.get_mut(self).pending_pointer_events.extend(events);
self.flush_pointer_event_queue(app);
}
pub fn cancel_pointer(self: Handle<Self>, app: &mut App, pointer: i64) {
if app.get(self).pending_pointer_events.is_empty() {
app.schedule_microtask(Listener::handle_method(
self,
GestureBinding::flush_pointer_event_queue,
));
}
app.get_mut(self)
.pending_pointer_events
.push_front(PointerEvent::Cancel(PointerCancelEvent {
pointer,
..PointerCancelEvent::default()
}));
}
fn flush_pointer_event_queue(self: Handle<Self>, app: &mut App) {
while let Some(event) = app.get_mut(self).pending_pointer_events.pop_front() {
self.handle_pointer_event(app, event);
}
}
pub fn handle_pointer_event(self: Handle<Self>, app: &mut App, event: PointerEvent) {
self.handle_pointer_event_immediately(app, event);
}
fn handle_pointer_event_immediately(self: Handle<Self>, app: &mut App, event: PointerEvent) {
let pointer = event.pointer();
let is_down_or_pan_zoom_start =
matches!(event, PointerEvent::Down(_) | PointerEvent::PanZoomStart(_));
let is_up_or_cancel_or_pan_zoom_end = matches!(
event,
PointerEvent::Up(_) | PointerEvent::Cancel(_) | PointerEvent::PanZoomEnd(_)
);
let is_new_hit_test = matches!(
event,
PointerEvent::Down(_)
| PointerEvent::Hover(_)
| PointerEvent::Scroll(_)
| PointerEvent::ScrollInertiaCancel(_)
| PointerEvent::Scale(_)
| PointerEvent::PanZoomStart(_)
);
let (hit_test_result, cache_after) = if is_new_hit_test {
debug_assert!(
!app.get(self).hit_tests.contains_key(&pointer),
"Pointer unexpectedly has a HitTestResult associated with it."
);
let mut hit_test_result = HitTestResult::new();
self.hit_test_in_view(app, &mut hit_test_result, event.position(), event.view_id());
(Some(hit_test_result), is_down_or_pan_zoom_start)
} else if is_up_or_cancel_or_pan_zoom_end {
(app.get_mut(self).hit_tests.remove(&pointer), false)
} else if event.down() || matches!(event, PointerEvent::PanZoomUpdate(_)) {
(app.get_mut(self).hit_tests.remove(&pointer), true)
} else {
(None, false)
};
if cfg!(debug_assertions)
&& debug_print_hit_test_results()
&& let Some(result) = &hit_test_result
{
eprintln!("{event:?}: {result:?}");
}
if cfg!(debug_assertions)
&& debug_print_mouse_hover_events()
&& matches!(event, PointerEvent::Hover(_))
{
eprintln!("{event:?}");
}
if hit_test_result.is_some()
|| matches!(event, PointerEvent::Added(_) | PointerEvent::Removed(_))
{
self.dispatch_event(app, event, hit_test_result.as_ref());
}
if let Some(mut result) = hit_test_result {
if cache_after {
result.retain_targets(app);
app.get_mut(self).hit_tests.insert(pointer, result);
} else {
result.release_targets(app);
}
}
}
pub fn set_overrides(
self: Handle<Self>,
app: &mut App,
overrides: Rc<dyn GestureBindingOverrides>,
) {
app.get_mut(self).overrides = Some(overrides);
}
pub fn hit_test_in_view(
self: Handle<Self>,
app: &mut App,
result: &mut HitTestResult,
position: Offset,
view_id: ViewId,
) {
let overrides = app.get(self).overrides.clone();
if let Some(overrides) = overrides {
overrides.hit_test_in_view(app, result, position, view_id);
}
result.add(HitTestEntry::new(self));
}
pub fn dispatch_event(
self: Handle<Self>,
app: &mut App,
event: PointerEvent,
hit_test_result: Option<&HitTestResult>,
) {
let overrides = app.get(self).overrides.clone();
if let Some(overrides) = overrides {
overrides.will_dispatch_event(app, &event, hit_test_result);
}
let Some(hit_test_result) = hit_test_result else {
debug_assert!(matches!(
event,
PointerEvent::Added(_) | PointerEvent::Removed(_)
));
let router = self.pointer_router(app);
router.route(app, event);
return;
};
for entry in hit_test_result.path() {
entry
.target()
.handle_event(app, &event.transformed(entry.transform()), entry);
}
}
pub fn reset_gesture_binding(self: Handle<Self>, app: &mut App) {
let hit_tests = std::mem::take(&mut app.get_mut(self).hit_tests);
for mut result in hit_tests.into_values() {
result.release_targets(app);
}
}
}
impl HitTestTarget for Handle<GestureBinding> {
fn handle_event(&self, app: &mut App, event: &PointerEvent, _entry: &HitTestEntry) {
let this = *self;
let router = this.pointer_router(app);
router.route(app, event.clone());
let arena = this.gesture_arena(app);
if matches!(event, PointerEvent::Down(_) | PointerEvent::PanZoomStart(_)) {
arena.close(app, event.pointer());
} else if matches!(event, PointerEvent::Up(_) | PointerEvent::PanZoomEnd(_)) {
arena.sweep(app, event.pointer());
} else if matches!(
event,
PointerEvent::Scroll(_) | PointerEvent::ScrollInertiaCancel(_) | PointerEvent::Scale(_)
) {
this.pointer_signal_resolver(app).resolve(app);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use inset_foundation::AppCell;
use std::cell::Cell;
use std::rc::Rc;
use crate::events::{PointerDownEvent, PointerMoveEvent, PointerUpEvent};
use crate::pointer_router::PointerRoute;
#[test]
fn down_routes_through_the_binding_and_closes_the_arena() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let binding = GestureBinding::instance(&mut app);
let ran = Rc::new(Cell::new(0));
let ran_flag = Rc::clone(&ran);
binding.pointer_router(&app).add_route(
&mut app,
1,
PointerRoute::new(move |_app, _event| {
ran_flag.set(ran_flag.get() + 1);
}),
None,
);
binding.handle_pointer_event(
&mut app,
PointerEvent::Down(PointerDownEvent {
pointer: 1,
..PointerDownEvent::default()
}),
);
assert_eq!(ran.get(), 1);
binding.handle_pointer_event(
&mut app,
PointerEvent::Move(PointerMoveEvent {
pointer: 1,
down: true,
..PointerMoveEvent::default()
}),
);
assert_eq!(ran.get(), 2);
binding.handle_pointer_event(
&mut app,
PointerEvent::Up(PointerUpEvent {
pointer: 1,
..PointerUpEvent::default()
}),
);
assert_eq!(ran.get(), 3);
}
}