mod mouse;
mod touch;
pub(crate) mod velocity;
#[allow(unused)] use super::{Event, EventState}; use super::{EventCx, IsUsed};
#[allow(unused)] use crate::Events; use crate::Id;
use crate::event::{CursorIcon, MouseButton, Unused, Used};
use crate::geom::{Coord, DVec2, Offset, Vec2};
use cast::{Cast, CastApprox, Conv};
pub(crate) use mouse::Mouse;
pub(crate) use touch::Touch;
use winit::event::FingerId;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum GrabMode {
Click,
Grab,
Pan { scale: bool, rotate: bool },
}
impl GrabMode {
pub const PAN_NONE: GrabMode = GrabMode::Pan {
scale: false,
rotate: false,
};
pub const PAN_SCALE: GrabMode = GrabMode::Pan {
scale: true,
rotate: false,
};
pub const PAN_ROTATE: GrabMode = GrabMode::Pan {
scale: false,
rotate: true,
};
pub const PAN_FULL: GrabMode = GrabMode::Pan {
scale: true,
rotate: true,
};
#[inline]
pub fn is_pan(self) -> bool {
matches!(self, GrabMode::Pan { .. })
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PressSource(u64);
impl PressSource {
const FLAG_TOUCH: u64 = 1 << 63;
pub(crate) fn mouse(button: Option<MouseButton>, repetitions: u32) -> Self {
let r = (repetitions as u64) << 32;
debug_assert!(r & Self::FLAG_TOUCH == 0);
let b = button.map(|b| b as u8).unwrap_or(u8::MAX);
Self(r | b as u64)
}
pub(crate) fn touch(finger_id: FingerId) -> Self {
let id = u64::conv(finger_id.into_raw());
assert!(id & Self::FLAG_TOUCH == 0);
Self(Self::FLAG_TOUCH | id)
}
#[inline]
pub fn is_mouse(self) -> bool {
self.0 & Self::FLAG_TOUCH == 0
}
#[inline]
pub fn is_touch(self) -> bool {
self.0 & Self::FLAG_TOUCH != 0
}
fn finger_id(self) -> Option<FingerId> {
if self.is_touch() {
let id = self.0 & !Self::FLAG_TOUCH;
Some(FingerId::from_raw(id.cast()))
} else {
None
}
}
pub fn mouse_button(self) -> Option<MouseButton> {
if self.is_mouse() {
let b = self.0 as u8;
MouseButton::try_from_u8(b)
} else {
None
}
}
#[inline]
pub fn is_primary(self) -> bool {
self.is_touch() || self.mouse_button() == Some(MouseButton::Left)
}
#[inline]
pub fn is_secondary(self) -> bool {
self.mouse_button() == Some(MouseButton::Right)
}
#[inline]
pub fn is_tertiary(self) -> bool {
self.mouse_button() == Some(MouseButton::Middle)
}
#[inline]
pub fn repetitions(self) -> u32 {
if self.is_mouse() {
(self.0 >> 32) as u32
} else {
1
}
}
}
#[crate::autoimpl(Deref using self.source)]
#[derive(Clone, Debug, PartialEq)]
pub struct PressStart {
pub source: PressSource,
pub id: Option<Id>,
position: DVec2,
}
impl std::ops::AddAssign<Offset> for PressStart {
#[inline]
fn add_assign(&mut self, offset: Offset) {
self.position += DVec2::conv(offset);
}
}
impl PressStart {
#[inline]
pub fn coord(&self) -> Coord {
self.position.cast_approx()
}
#[inline]
pub fn grab(&self, id: Id, mode: GrabMode) -> GrabBuilder {
GrabBuilder {
id,
source: self.source,
position: self.position,
mode,
icon: None,
}
}
#[inline]
pub fn grab_click(&self, id: Id) -> GrabBuilder {
self.grab(id, GrabMode::Click)
}
#[inline]
pub fn grab_move(&self, id: Id) -> GrabBuilder {
self.grab(id, GrabMode::Grab)
}
}
#[crate::autoimpl(Deref using self.source)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Press {
pub source: PressSource,
pub id: Option<Id>,
pub coord: Coord,
}
#[must_use]
pub struct GrabBuilder {
id: Id,
source: PressSource,
position: DVec2,
mode: GrabMode,
icon: Option<CursorIcon>,
}
impl GrabBuilder {
#[inline]
pub fn with_icon(self, icon: CursorIcon) -> Self {
self.with_opt_icon(Some(icon))
}
#[inline]
pub fn with_opt_icon(mut self, icon: Option<CursorIcon>) -> Self {
self.icon = icon;
self
}
pub fn complete(self, cx: &mut EventCx) -> IsUsed {
let GrabBuilder {
id,
source,
position,
mode,
icon,
} = self;
log::trace!(target: "kas_core::event", "grab_press: start_id={id}, source={source:?}");
let success = if let Some(button) = source.mouse_button() {
cx.mouse.start_grab(
button,
source.repetitions(),
id.clone(),
position,
mode,
icon.unwrap_or_default(),
)
} else if let Some(finger_id) = source.finger_id() {
cx.touch.start_grab(finger_id, id.clone(), position, mode)
} else {
false
};
if success {
cx.redraw();
Used
} else {
Unused
}
}
}
impl EventState {
pub fn is_depressed(&self, w_id: &Id) -> bool {
for (_, id) in &self.key_depress {
if *id == w_id {
return true;
}
}
if self
.mouse
.grab
.as_ref()
.map(|grab| *w_id == grab.depress)
.unwrap_or(false)
{
return true;
}
for grab in self.touch.touch_grab.iter() {
if *w_id == grab.depress {
return true;
}
}
for popup in &self.popups {
if *w_id == popup.desc.parent {
return true;
}
}
false
}
#[inline]
pub fn is_under_mouse(&self, w_id: &Id) -> bool {
*w_id == self.mouse.over
&& self
.mouse
.grab
.as_ref()
.is_none_or(|grab| grab.start_id == w_id)
}
pub fn any_grab_on(&self, id: &Id) -> bool {
if self
.mouse
.grab
.as_ref()
.map(|grab| grab.start_id == id)
.unwrap_or(false)
{
return true;
}
self.touch.touch_grab.iter().any(|grab| grab.start_id == id)
}
pub fn press_velocity(&self, source: PressSource) -> Option<Vec2> {
let evc = self.config().event();
if source.is_mouse() {
Some(self.mouse.samples.velocity(evc.kinetic_timeout()))
} else if let Some(finger_id) = source.finger_id() {
self.touch.velocity(finger_id, evc)
} else {
unreachable!()
}
}
}
impl<'a> EventCx<'a> {
pub fn set_mouse_over_icon(&mut self, icon: CursorIcon) {
self.mouse.icon = icon;
}
pub fn set_grab_depress(&mut self, source: PressSource, target: Option<Id>) -> bool {
let mut old = None;
let mut redraw = false;
if source.is_mouse() {
if let Some(grab) = self.mouse.grab.as_mut() {
redraw = grab.depress != target;
old = grab.depress.take();
grab.depress = target.clone();
}
} else if let Some(finger_id) = source.finger_id() {
if let Some(grab) = self.touch.get_touch(finger_id) {
redraw = grab.depress != target;
old = grab.depress.take();
grab.depress = target.clone();
}
} else {
unreachable!()
}
if redraw {
log::trace!(target: "kas_core::event", "set_grab_depress: target={target:?}");
self.opt_redraw(old);
self.opt_redraw(target);
}
redraw
}
pub fn set_grab_icon(&mut self, id: &Id, icon: CursorIcon) {
if let Some(grab) = self.mouse.grab.as_mut()
&& grab.start_id == *id
{
grab.icon = icon;
}
}
}