Documentation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Event handling
//!
//! Event handling uses *event* messages, passed from the parent into a widget,
//! with responses passed back to the parent. This model is simpler than that
//! commonly used by GUI frameworks: widgets do not need a pointer to their
//! parent and any result is pushed back up the call stack. The model allows
//! type-safety while allowing user-defined result types.
//!
//! We deliver events only on a "need to know" basis: typically, only one widget
//! will receive an event.
//!
//! ## Event delivery
//!
//! Events can be addressed only to a [`WidgetId`], so the first step (for
//! mouse and touch events) is to use [`kas::Layout::find_id`] to translate a
//! coordinate to a [`WidgetId`].
//!
//! Events then process from root to leaf. [`SendEvent::send`] is responsible
//! for forwarding an event to the appropriate child. Once the target widget is
//! reached, `send` (usually) calls [`Manager::handle_generic`] which may apply
//! some transformations to events, then calls [`Handler::handle`] on target
//! widget. Finally, a [`Response`] is emitted.
//!
//! The [`Response`] enum has a few variants; most important is `Msg(msg)`
//! which passes a user-defined payload up to a parent widget. The
//! `Unhandled(event)` and `Focus(rect)` variants may be trapped by any parent
//! for secondary purposes, e.g. to adjust a `ScrollRegion`.
//!
//! ## Mouse and touch events
//!
//! Mouse events and touch events are unified: both have a "press" which starts
//! somewhere, moves, and ends somewhere. The main difference concerns move
//! events, which may occur with any number of mouse buttons pressed.
//!
//! Motion and release events are only delivered when a "press grab" is active.
//! This is achieved by calling [`Manager::request_grab`] and allows receiving
//! both relative and absolute press coordinates.
//! A special "pan" grab allows receiving two-finger scroll/scale/rotate input.
//!
//! Each touch event is considered independent. The mouse cursor and multiple
//! fingers may all interact with different parts of a UI simultaneously. The
//! same is partly true of keyboard input, though some actions force keyboard
//! focus.
//!
//! ### Pop-ups
//!
//! When a pop-up widget is created, this forces keyboard focus to that widget
//! and receives a "weak" grab on press actions, meaning that the widget
//! receives this input first, but if returned via `Response::Unhandled` the
//! input passes immediately to the next target. This allows pop-up menus to
//! get first chance of handling input and to dismiss themselves when input is
//! for other widgets without blocking other widgets from accepting that input.
//! (This "weak grab" behaviour is intentional to align UI response with a
//! user's intuition that any visible non-grey part of the UI is interactive.)
//!
//! ## Drawing
//!
//! Widgets do not usually track input events for the purpose of drawn effects
//! such as mouse-hover. Instead, a widget calls [`WidgetCore::input_state`]
//! with a reference to the [`ManagerState`] (which is passed to
//! [`Layout::draw`] calls) in order to obtain an [`InputState`] instance.
//!
//! [`WidgetId`]: crate::WidgetId

mod callback;
#[cfg(not(feature = "winit"))]
mod enums;
mod events;
mod handler;
mod manager;
mod response;
mod update;

use smallvec::SmallVec;
use std::fmt::Debug;

// doc imports
#[allow(unused)]
use kas::{draw::InputState, Layout, WidgetCore};

#[cfg(feature = "winit")]
pub use winit::event::{ModifiersState, MouseButton, VirtualKeyCode};
#[cfg(feature = "winit")]
pub use winit::window::CursorIcon;

pub use callback::Callback;
#[cfg(not(feature = "winit"))]
pub use enums::{CursorIcon, ModifiersState, MouseButton, VirtualKeyCode};
pub use events::*;
pub use handler::{Handler, SendEvent};
pub use manager::{ConfigureManager, GrabMode, Manager, ManagerState};
pub use response::Response;
pub use update::UpdateHandle;

/// A type supporting a small number of key bindings
///
/// This type may be used where it is desirable to support a small number of
/// key bindings. The type is allowed to silently ignore extra bindings beyond
/// some *small* number of at least 3. (Currently numbers over 5 are accepted
/// but cause allocation.)
pub type VirtualKeyCodes = SmallVec<[VirtualKeyCode; 5]>;

#[test]
fn size_of_virtual_key_codes() {
    // Currently sized to maximise use of available space on 64-bit platforms
    assert!(std::mem::size_of::<VirtualKeyCodes>() <= 32);
}

/// A void message
///
/// This type is not constructible, therefore `Response<VoidMsg>` is known at
/// compile-time not to contain a `Response::Msg(..)` variant.
///
/// It is trivial to implement `From<VoidMsg>` for any type `T`; unfortunately
/// Rust's type system is too restrictive for us to provide a blanket
/// implementation (due both to orphan rules for trait implementations and to
/// conflicting implementations; it is possible that this may change in the
/// future).
///
/// `From<VoidMsg>` is implemented for a number of language types;
/// custom message types are required to implement this via the
/// [`derive(VoidMsg)`](../macros/index.html#the-derivevoidmsg-macro) macro.
#[derive(Clone, Debug)]
pub enum VoidMsg {}

/// Alias for `Response<VoidMsg>`
pub type VoidResponse = Response<VoidMsg>;

// TODO(specialization): replace below impls with impl<T> From<VoidMsg> for T
macro_rules! impl_void_msg {
    () => {};
    ($t:ty) => {
        impl From<VoidMsg> for $t {
            fn from(_: VoidMsg) -> $t {
                unreachable!()
            }
        }
    };
    ($t:ty, $($tt:ty,)*) => {
        impl_void_msg!($t);
        impl_void_msg!($($tt,)*);
    };
}
impl_void_msg!(bool, char,);
impl_void_msg!(u8, u16, u32, u64, u128, usize,);
impl_void_msg!(i8, i16, i32, i64, i128, isize,);
impl_void_msg!(f32, f64,);
impl_void_msg!(&'static str, String, kas::string::CowString,);