use std::cell::RefCell;
use std::ops::Range;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Instant;
pub use slate_platform::{Key, KeyCode, Modifiers, MouseButton, NamedKey, WindowId};
use crate::ime::{ImeRegistry, ImeState};
use crate::types::ElementId;
#[inline]
pub fn is_command_modifier(m: &Modifiers) -> bool {
#[cfg(target_os = "macos")]
{
m.meta
}
#[cfg(not(target_os = "macos"))]
{
m.ctrl
}
}
#[inline]
pub fn is_line_edge_modifier(m: &Modifiers) -> bool {
#[cfg(target_os = "macos")]
{
m.meta && !m.ctrl && !m.alt
}
#[cfg(not(target_os = "macos"))]
{
let _ = m;
false
}
}
pub(crate) type MouseHandler = Arc<dyn Fn(&MouseEvent, &mut EventCtx) + Send + Sync + 'static>;
pub(crate) type ElementKeyHandler = Arc<dyn Fn(&KeyEvent, &mut EventCtx) + Send + Sync + 'static>;
pub(crate) type ElementTextInputHandler =
Arc<dyn Fn(&TextInputEvent, &mut EventCtx) + Send + Sync + 'static>;
pub(crate) type ElementImePreeditHandler =
Arc<dyn Fn(&ImePreeditEvent, &mut EventCtx) + Send + Sync + 'static>;
pub(crate) type ElementImeCommitHandler =
Arc<dyn Fn(&ImeCommitEvent, &mut EventCtx) + Send + Sync + 'static>;
pub(crate) type ElementImeLifecycleHandler =
Arc<dyn Fn(&ImeLifecycleEvent, &mut EventCtx) + Send + Sync + 'static>;
#[allow(dead_code)] #[derive(Clone, Default)]
#[doc(hidden)]
pub struct ImeHandlers {
pub on_ime_preedit: Option<ElementImePreeditHandler>,
pub on_ime_commit: Option<ElementImeCommitHandler>,
pub on_ime_enabled: Option<ElementImeLifecycleHandler>,
pub on_ime_disabled: Option<ElementImeLifecycleHandler>,
}
impl ImeHandlers {
#[allow(dead_code)] pub fn has_any(&self) -> bool {
self.on_ime_preedit.is_some()
|| self.on_ime_commit.is_some()
|| self.on_ime_enabled.is_some()
|| self.on_ime_disabled.is_some()
}
}
#[allow(dead_code)] #[derive(Clone, Default)]
#[doc(hidden)]
pub struct MouseHandlers {
pub on_mouse_down: Option<MouseHandler>,
pub on_mouse_move: Option<MouseHandler>,
pub on_mouse_up: Option<MouseHandler>,
}
impl MouseHandlers {
#[allow(dead_code)] pub fn has_any(&self) -> bool {
self.on_mouse_down.is_some() || self.on_mouse_move.is_some() || self.on_mouse_up.is_some()
}
}
#[allow(dead_code)] #[derive(Clone, Default)]
#[doc(hidden)]
pub struct KeyHandlers {
pub on_key_down: Option<ElementKeyHandler>,
pub on_key_up: Option<ElementKeyHandler>,
pub on_text_input: Option<ElementTextInputHandler>,
}
impl KeyHandlers {
#[allow(dead_code)] pub fn has_any(&self) -> bool {
self.on_key_down.is_some() || self.on_key_up.is_some() || self.on_text_input.is_some()
}
}
pub(crate) type ScrollHandler = Arc<dyn Fn(&ScrollEvent, &mut EventCtx) + Send + Sync + 'static>;
pub(crate) type PointerHandler = Arc<dyn Fn(&PointerEvent, &mut EventCtx) + Send + Sync + 'static>;
#[derive(Clone, Default)]
pub(crate) struct Handlers {
pub on_click: Option<MouseHandler>,
pub on_mouse_down: Option<MouseHandler>,
pub on_mouse_up: Option<MouseHandler>,
pub on_mouse_move: Option<MouseHandler>,
pub on_mouse_scrolled: Option<ScrollHandler>,
pub on_pointer_event: Option<PointerHandler>,
pub on_pointer_enter: Option<PointerHandler>,
pub on_pointer_leave: Option<PointerHandler>,
}
impl Handlers {
pub fn has_any(&self) -> bool {
self.on_click.is_some()
|| self.on_mouse_down.is_some()
|| self.on_mouse_up.is_some()
|| self.on_mouse_move.is_some()
|| self.on_mouse_scrolled.is_some()
|| self.on_pointer_event.is_some()
|| self.on_pointer_enter.is_some()
|| self.on_pointer_leave.is_some()
}
}
#[derive(Clone, Copy, Debug)]
pub struct MouseEvent {
pub position: (f32, f32),
pub button: Option<MouseButton>,
pub modifiers: Modifiers,
pub timestamp: Instant,
}
#[derive(Clone, Copy, Debug)]
pub struct ScrollEvent {
pub position: (f32, f32),
pub delta_x: f32,
pub delta_y: f32,
pub precise: bool,
pub modifiers: Modifiers,
pub timestamp: Instant,
}
#[derive(Clone, Copy, Debug)]
pub struct PointerEvent {
pub kind: PointerEventKind,
pub position: (f32, f32),
pub button: Option<MouseButton>,
pub modifiers: Modifiers,
pub timestamp: Instant,
}
pub type KeyHandler = Box<dyn FnMut(&KeyEvent, &mut EventCtx)>;
pub type TextInputHandler = Box<dyn FnMut(&TextInputEvent, &mut EventCtx)>;
pub type ImePreeditHandler = Box<dyn FnMut(&ImePreeditEvent, &mut EventCtx)>;
pub type ImeCommitHandler = Box<dyn FnMut(&ImeCommitEvent, &mut EventCtx)>;
pub type ImeLifecycleHandler = Box<dyn FnMut(&ImeLifecycleEvent, &mut EventCtx)>;
#[derive(Clone, Debug)]
pub struct KeyEvent {
pub code: KeyCode,
pub key: Key,
pub modifiers: Modifiers,
pub is_repeat: bool,
pub timestamp: Instant,
}
#[derive(Clone, Debug)]
pub struct TextInputEvent {
pub text: String,
pub timestamp: Instant,
}
#[derive(Clone, Debug)]
pub struct ImePreeditEvent {
pub text: String,
pub cursor_byte_offset: usize,
pub selection: Option<Range<usize>>,
pub timestamp: Instant,
}
#[derive(Clone, Debug)]
pub struct ImeCommitEvent {
pub text: String,
pub timestamp: Instant,
}
#[derive(Clone, Copy, Debug)]
pub struct ImeLifecycleEvent {
pub timestamp: Instant,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerEventKind {
Down,
Up,
Move,
Enter,
Leave,
}
#[derive(Clone, Copy, Debug)]
pub enum PendingFocusOp {
Focus(ElementId),
Blur,
}
#[derive(Clone, Copy, Debug)]
pub enum PendingCaptureOp {
Set(ElementId),
Release,
}
pub struct EventCtx<'a> {
propagation_stopped: &'a mut bool,
pending_focus_op: &'a mut Option<PendingFocusOp>,
pending_capture_op: &'a mut Option<PendingCaptureOp>,
window_id: WindowId,
focused: Option<ElementId>,
element_id: Option<ElementId>,
ime_registry: Option<&'a RefCell<ImeRegistry>>,
}
impl<'a> EventCtx<'a> {
pub(crate) fn new(
propagation_stopped: &'a mut bool,
pending_focus_op: &'a mut Option<PendingFocusOp>,
pending_capture_op: &'a mut Option<PendingCaptureOp>,
window_id: WindowId,
focused: Option<ElementId>,
) -> EventCtx<'a> {
EventCtx {
propagation_stopped,
pending_focus_op,
pending_capture_op,
window_id,
focused,
element_id: None,
ime_registry: None,
}
}
pub(crate) fn with_ime(
mut self,
element_id: ElementId,
ime_registry: &'a RefCell<ImeRegistry>,
) -> EventCtx<'a> {
self.element_id = Some(element_id);
self.ime_registry = Some(ime_registry);
self
}
pub fn element_id(&self) -> Option<ElementId> {
self.element_id
}
pub fn ime_state(&self, id: ElementId) -> Option<Rc<RefCell<ImeState>>> {
let registry = self.ime_registry?;
registry.try_borrow().ok()?.get(id)
}
pub fn stop_propagation(&mut self) {
*self.propagation_stopped = true;
}
pub fn is_propagation_stopped(&self) -> bool {
*self.propagation_stopped
}
pub fn request_focus(&mut self, id: ElementId) {
*self.pending_focus_op = Some(PendingFocusOp::Focus(id));
}
pub fn blur(&mut self) {
*self.pending_focus_op = Some(PendingFocusOp::Blur);
}
pub fn set_capture(&mut self, id: ElementId) {
*self.pending_capture_op = Some(PendingCaptureOp::Set(id));
}
pub fn release_capture(&mut self) {
*self.pending_capture_op = Some(PendingCaptureOp::Release);
}
pub fn window_id(&self) -> WindowId {
self.window_id
}
pub fn focused_element(&self) -> Option<ElementId> {
self.focused
}
#[allow(dead_code)] pub(crate) fn take_pending_focus_op(&mut self) -> Option<PendingFocusOp> {
self.pending_focus_op.take()
}
#[allow(dead_code)] pub(crate) fn take_pending_capture_op(&mut self) -> Option<PendingCaptureOp> {
self.pending_capture_op.take()
}
}
impl std::fmt::Debug for EventCtx<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventCtx")
.field("propagation_stopped", &*self.propagation_stopped)
.field("pending_focus_op", &*self.pending_focus_op)
.field("pending_capture_op", &*self.pending_capture_op)
.field("window_id", &self.window_id)
.field("focused", &self.focused)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn mods(shift: bool, ctrl: bool, alt: bool, meta: bool) -> Modifiers {
Modifiers {
shift,
ctrl,
alt,
meta,
}
}
#[cfg(target_os = "macos")]
const MAC: bool = true;
#[cfg(not(target_os = "macos"))]
const MAC: bool = false;
#[test]
fn line_edge_modifier_meta_only_is_macos_only() {
assert_eq!(is_line_edge_modifier(&mods(false, false, false, true)), MAC);
}
#[test]
fn line_edge_modifier_meta_plus_shift_keeps_line_edge() {
assert_eq!(is_line_edge_modifier(&mods(true, false, false, true)), MAC);
}
#[test]
fn line_edge_modifier_meta_plus_ctrl_is_false() {
assert!(!is_line_edge_modifier(&mods(false, true, false, true)));
}
#[test]
fn line_edge_modifier_meta_plus_alt_is_false() {
assert!(!is_line_edge_modifier(&mods(false, false, true, true)));
}
#[test]
fn line_edge_modifier_ctrl_alone_is_false() {
assert!(!is_line_edge_modifier(&mods(false, true, false, false)));
}
#[test]
fn line_edge_modifier_bare_is_false() {
assert!(!is_line_edge_modifier(&mods(false, false, false, false)));
}
}