use std::time::Duration;
use inset_embedder::{Matrix4, Offset, PointerDeviceKind, ViewId};
use inset_foundation::ValueChanged;
use crate::constants::{
K_PAN_SLOP, K_PRECISE_POINTER_HIT_SLOP, K_PRECISE_POINTER_PAN_SLOP, K_TOUCH_SLOP,
};
use crate::gesture_settings::DeviceGestureSettings;
pub const K_PRIMARY_BUTTON: i64 = 0x01;
pub const K_SECONDARY_BUTTON: i64 = 0x02;
pub const K_PRIMARY_MOUSE_BUTTON: i64 = K_PRIMARY_BUTTON;
pub const K_SECONDARY_MOUSE_BUTTON: i64 = K_SECONDARY_BUTTON;
pub const K_STYLUS_CONTACT: i64 = K_PRIMARY_BUTTON;
pub const K_PRIMARY_STYLUS_BUTTON: i64 = K_SECONDARY_BUTTON;
pub const K_TERTIARY_BUTTON: i64 = 0x04;
pub const K_MIDDLE_MOUSE_BUTTON: i64 = K_TERTIARY_BUTTON;
pub const K_SECONDARY_STYLUS_BUTTON: i64 = K_TERTIARY_BUTTON;
pub const K_BACK_MOUSE_BUTTON: i64 = 0x08;
pub const K_FORWARD_MOUSE_BUTTON: i64 = 0x10;
pub const K_TOUCH_CONTACT: i64 = K_PRIMARY_BUTTON;
const K_MAX_UNSIGNED_SMI: i64 = 0x3FFF_FFFF_FFFF_FFFF;
pub fn nth_mouse_button(number: i64) -> i64 {
K_PRIMARY_MOUSE_BUTTON.wrapping_shl((number - 1) as u32) & K_MAX_UNSIGNED_SMI
}
pub fn nth_stylus_button(number: i64) -> i64 {
K_PRIMARY_STYLUS_BUTTON.wrapping_shl((number - 1) as u32) & K_MAX_UNSIGNED_SMI
}
pub fn smallest_button(buttons: i64) -> i64 {
buttons.isolate_lowest_one()
}
pub fn is_single_button(buttons: i64) -> bool {
buttons != 0 && smallest_button(buttons) == buttons
}
macro_rules! pointer_event_struct {
(
$(#[$meta:meta])*
pub struct $name:ident {
$($(#[$extra_meta:meta])* $extra_vis:vis $extra:ident: $extra_ty:ty = $extra_default:expr),* $(,)?
}
down = $down:expr,
pressure = $pressure:expr,
distance = $distance:expr,
buttons = $buttons:expr,
kind = $kind:expr $(,)?
) => {
$(#[$meta])*
#[derive(Clone, Debug)]
pub struct $name {
/// The ID of the `FlutterView` which this event originated from.
pub view_id: ViewId,
pub embedder_id: i64,
pub time_stamp: Duration,
pub pointer: i64,
pub kind: PointerDeviceKind,
pub device: i64,
pub position: Offset,
pub delta: Offset,
pub buttons: i64,
pub down: bool,
pub obscured: bool,
pub pressure: f64,
pub pressure_min: f64,
pub pressure_max: f64,
pub distance: f64,
pub distance_max: f64,
pub size: f64,
pub radius_major: f64,
pub radius_minor: f64,
pub radius_min: f64,
pub radius_max: f64,
pub orientation: f64,
pub tilt: f64,
pub platform_data: i64,
pub synthesized: bool,
pub transform: Option<Matrix4>,
pub original: Option<Box<$name>>,
$($(#[$extra_meta])* $extra_vis $extra: $extra_ty,)*
}
impl Default for $name {
fn default() -> $name {
$name {
view_id: ViewId(0),
embedder_id: 0,
time_stamp: Duration::ZERO,
pointer: 0,
kind: $kind,
device: 0,
position: Offset::ZERO,
delta: Offset::ZERO,
buttons: $buttons,
down: $down,
obscured: false,
pressure: $pressure,
pressure_min: 1.0,
pressure_max: 1.0,
distance: $distance,
distance_max: 0.0,
size: 0.0,
radius_major: 0.0,
radius_minor: 0.0,
radius_min: 0.0,
radius_max: 0.0,
orientation: 0.0,
tilt: 0.0,
platform_data: 0,
synthesized: false,
transform: None,
original: None,
$($extra: $extra_default,)*
}
}
}
impl $name {
pub fn local_position(&self) -> Offset {
transform_position(self.transform, self.position)
}
pub fn local_delta(&self) -> Offset {
transform_delta_via_positions(
self.position,
None,
self.delta,
self.transform,
)
}
pub fn distance_min(&self) -> f64 {
0.0
}
pub fn transformed(&self, transform: Option<Matrix4>) -> $name {
if transform.is_none() || self.transform == transform {
return self.clone();
}
let original = self.original.clone().unwrap_or_else(|| {
let mut orig = self.clone();
orig.transform = None;
orig.original = None;
Box::new(orig)
});
let mut event = (*original).clone();
event.original = Some(original);
event.transform = transform;
event
}
pub fn copy_with(&self) -> $name {
self.clone()
}
}
};
}
pointer_event_struct! {
pub struct PointerAddedEvent {}
down = false,
pressure = 0.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerRemovedEvent {}
down = false,
pressure = 0.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerHoverEvent {}
down = false,
pressure = 0.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerEnterEvent {}
down = false,
pressure = 0.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerExitEvent {}
down = false,
pressure = 0.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerDownEvent {}
down = true,
pressure = 1.0,
distance = 0.0,
buttons = K_PRIMARY_BUTTON,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerMoveEvent {}
down = true,
pressure = 1.0,
distance = 0.0,
buttons = K_PRIMARY_BUTTON,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerUpEvent {}
down = false,
pressure = 0.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerCancelEvent {}
down = false,
pressure = 0.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerScrollEvent {
pub scroll_delta: Offset = Offset::ZERO,
}
down = false,
pressure = 1.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerScrollInertiaCancelEvent {}
down = false,
pressure = 1.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerScaleEvent {
pub scale: f64 = 1.0,
}
down = false,
pressure = 1.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Touch,
}
pointer_event_struct! {
pub struct PointerPanZoomStartEvent {}
down = false,
pressure = 1.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Trackpad,
}
pointer_event_struct! {
pub struct PointerPanZoomUpdateEvent {
pub pan: Offset = Offset::ZERO,
pub pan_delta: Offset = Offset::ZERO,
pub scale: f64 = 1.0,
pub rotation: f64 = 0.0,
}
down = false,
pressure = 1.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Trackpad,
}
pointer_event_struct! {
pub struct PointerPanZoomEndEvent {}
down = false,
pressure = 1.0,
distance = 0.0,
buttons = 0,
kind = PointerDeviceKind::Trackpad,
}
impl PointerPanZoomUpdateEvent {
pub fn local_pan(&self) -> Offset {
transform_position(self.transform, self.pan)
}
pub fn local_pan_delta(&self) -> Offset {
transform_delta_via_positions(
self.pan,
Some(self.local_pan()),
self.pan_delta,
self.transform,
)
}
}
macro_rules! with_variant {
($event:expr, $e:ident => $body:expr) => {
match $event {
PointerEvent::Added($e) => $body,
PointerEvent::Removed($e) => $body,
PointerEvent::Hover($e) => $body,
PointerEvent::Enter($e) => $body,
PointerEvent::Exit($e) => $body,
PointerEvent::Down($e) => $body,
PointerEvent::Move($e) => $body,
PointerEvent::Up($e) => $body,
PointerEvent::Cancel($e) => $body,
PointerEvent::Scroll($e) => $body,
PointerEvent::ScrollInertiaCancel($e) => $body,
PointerEvent::Scale($e) => $body,
PointerEvent::PanZoomStart($e) => $body,
PointerEvent::PanZoomUpdate($e) => $body,
PointerEvent::PanZoomEnd($e) => $body,
}
};
}
macro_rules! map_variant {
($event:expr, $e:ident => $body:expr) => {
match $event {
PointerEvent::Added($e) => PointerEvent::Added($body),
PointerEvent::Removed($e) => PointerEvent::Removed($body),
PointerEvent::Hover($e) => PointerEvent::Hover($body),
PointerEvent::Enter($e) => PointerEvent::Enter($body),
PointerEvent::Exit($e) => PointerEvent::Exit($body),
PointerEvent::Down($e) => PointerEvent::Down($body),
PointerEvent::Move($e) => PointerEvent::Move($body),
PointerEvent::Up($e) => PointerEvent::Up($body),
PointerEvent::Cancel($e) => PointerEvent::Cancel($body),
PointerEvent::Scroll($e) => PointerEvent::Scroll($body),
PointerEvent::ScrollInertiaCancel($e) => PointerEvent::ScrollInertiaCancel($body),
PointerEvent::Scale($e) => PointerEvent::Scale($body),
PointerEvent::PanZoomStart($e) => PointerEvent::PanZoomStart($body),
PointerEvent::PanZoomUpdate($e) => PointerEvent::PanZoomUpdate($body),
PointerEvent::PanZoomEnd($e) => PointerEvent::PanZoomEnd($body),
}
};
}
macro_rules! from_mouse_event {
($target:ident, $event:expr) => {
with_variant!($event, e => $target {
view_id: e.view_id,
time_stamp: e.time_stamp,
pointer: e.pointer,
kind: e.kind,
device: e.device,
position: e.position,
delta: e.delta,
buttons: e.buttons,
obscured: e.obscured,
pressure_min: e.pressure_min,
pressure_max: e.pressure_max,
distance: e.distance,
distance_max: e.distance_max,
size: e.size,
radius_major: e.radius_major,
radius_minor: e.radius_minor,
radius_min: e.radius_min,
radius_max: e.radius_max,
orientation: e.orientation,
tilt: e.tilt,
down: e.down,
synthesized: e.synthesized,
..$target::default()
})
.transformed($event.transform())
};
}
impl PointerEnterEvent {
pub fn from_mouse_event(event: &PointerEvent) -> PointerEnterEvent {
from_mouse_event!(PointerEnterEvent, event)
}
}
impl PointerExitEvent {
pub fn from_mouse_event(event: &PointerEvent) -> PointerExitEvent {
from_mouse_event!(PointerExitEvent, event)
}
}
#[derive(Clone, Debug)]
pub enum PointerEvent {
Added(PointerAddedEvent),
Removed(PointerRemovedEvent),
Hover(PointerHoverEvent),
Enter(PointerEnterEvent),
Exit(PointerExitEvent),
Down(PointerDownEvent),
Move(PointerMoveEvent),
Up(PointerUpEvent),
Cancel(PointerCancelEvent),
Scroll(PointerScrollEvent),
ScrollInertiaCancel(PointerScrollInertiaCancelEvent),
Scale(PointerScaleEvent),
PanZoomStart(PointerPanZoomStartEvent),
PanZoomUpdate(PointerPanZoomUpdateEvent),
PanZoomEnd(PointerPanZoomEndEvent),
}
impl PointerEvent {
pub fn view_id(&self) -> ViewId {
with_variant!(self, e => e.view_id)
}
pub fn device(&self) -> i64 {
with_variant!(self, e => e.device)
}
pub fn transform(&self) -> Option<Matrix4> {
with_variant!(self, e => e.transform)
}
pub fn position(&self) -> Offset {
with_variant!(self, e => e.position)
}
pub fn local_position(&self) -> Offset {
with_variant!(self, e => e.local_position())
}
pub fn local_delta(&self) -> Offset {
with_variant!(self, e => e.local_delta())
}
pub fn delta(&self) -> Offset {
with_variant!(self, e => e.delta)
}
pub fn pointer(&self) -> i64 {
with_variant!(self, e => e.pointer)
}
pub fn kind(&self) -> PointerDeviceKind {
with_variant!(self, e => e.kind)
}
pub fn buttons(&self) -> i64 {
with_variant!(self, e => e.buttons)
}
pub fn pressure(&self) -> f64 {
with_variant!(self, e => e.pressure)
}
pub fn pressure_min(&self) -> f64 {
with_variant!(self, e => e.pressure_min)
}
pub fn pressure_max(&self) -> f64 {
with_variant!(self, e => e.pressure_max)
}
pub fn time_stamp(&self) -> Duration {
with_variant!(self, e => e.time_stamp)
}
pub fn synthesized(&self) -> bool {
with_variant!(self, e => e.synthesized)
}
pub fn down(&self) -> bool {
with_variant!(self, e => e.down)
}
pub fn transformed(&self, transform: Option<Matrix4>) -> PointerEvent {
map_variant!(self, e => e.transformed(transform))
}
}
pub fn transform_position(transform: Option<Matrix4>, position: Offset) -> Offset {
match transform {
None => position,
Some(transform) => Offset::from(transform.map_point(position.into())),
}
}
pub fn transform_delta_via_positions(
untransformed_end_position: Offset,
transformed_end_position: Option<Offset>,
untransformed_delta: Offset,
transform: Option<Matrix4>,
) -> Offset {
if transform.is_none() {
return untransformed_delta;
}
let transformed_end_position = transformed_end_position
.unwrap_or_else(|| transform_position(transform, untransformed_end_position));
let transformed_start_position =
transform_position(transform, untransformed_end_position - untransformed_delta);
transformed_end_position - transformed_start_position
}
pub fn compute_hit_slop(kind: PointerDeviceKind, settings: Option<DeviceGestureSettings>) -> f64 {
match kind {
PointerDeviceKind::Mouse => K_PRECISE_POINTER_HIT_SLOP,
PointerDeviceKind::Stylus
| PointerDeviceKind::InvertedStylus
| PointerDeviceKind::Unknown
| PointerDeviceKind::Touch
| PointerDeviceKind::Trackpad => settings
.and_then(|settings| settings.touch_slop)
.unwrap_or(K_TOUCH_SLOP),
}
}
pub fn compute_pan_slop(kind: PointerDeviceKind, settings: Option<DeviceGestureSettings>) -> f64 {
match kind {
PointerDeviceKind::Mouse => K_PRECISE_POINTER_PAN_SLOP,
PointerDeviceKind::Stylus
| PointerDeviceKind::InvertedStylus
| PointerDeviceKind::Unknown
| PointerDeviceKind::Touch
| PointerDeviceKind::Trackpad => settings
.and_then(|settings| settings.pan_slop())
.unwrap_or(K_PAN_SLOP),
}
}
pub type PointerDownEventListener = ValueChanged<PointerDownEvent>;
pub type PointerMoveEventListener = ValueChanged<PointerMoveEvent>;
pub type PointerUpEventListener = ValueChanged<PointerUpEvent>;
pub type PointerHoverEventListener = ValueChanged<PointerHoverEvent>;
pub type PointerEnterEventListener = ValueChanged<PointerEnterEvent>;
pub type PointerExitEventListener = ValueChanged<PointerExitEvent>;
pub type PointerCancelEventListener = ValueChanged<PointerCancelEvent>;
pub type PointerPanZoomStartEventListener = ValueChanged<PointerPanZoomStartEvent>;
pub type PointerPanZoomUpdateEventListener = ValueChanged<PointerPanZoomUpdateEvent>;
pub type PointerPanZoomEndEventListener = ValueChanged<PointerPanZoomEndEvent>;
pub type PointerSignalEventListener = ValueChanged<PointerEvent>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smallest_button_picks_the_least_significant_set_bit() {
assert_eq!(smallest_button(0x01), 0x01);
assert_eq!(smallest_button(0x11), 0x01);
assert_eq!(smallest_button(0x10), 0x10);
assert_eq!(smallest_button(0), 0);
}
#[test]
fn is_single_button_rejects_zero_and_multi() {
assert!(is_single_button(0x1));
assert!(!is_single_button(0x11));
assert!(!is_single_button(0));
}
#[test]
fn transformed_down_keeps_global_position() {
let event = PointerDownEvent {
position: Offset::new(10.0, 20.0),
..PointerDownEvent::default()
};
let transformed = event.transformed(Some(Matrix4::translation(5.0, 7.0)));
assert_eq!(transformed.position, Offset::new(10.0, 20.0));
assert_eq!(transformed.local_position(), Offset::new(15.0, 27.0));
assert!(transformed.original.is_some());
}
}