use linear_map::{LinearMap, set::LinearSet};
use smallvec::SmallVec;
use std::any::TypeId;
use std::collections::{HashMap, VecDeque};
use std::fmt::Debug;
use std::future::Future;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::time::Instant;
use super::*;
use crate::cast::{Cast, Conv};
use crate::config::{ConfigMsg, WindowConfig};
use crate::draw::DrawShared;
use crate::geom::{Offset, Vec2};
use crate::messages::Erased;
use crate::runner::{Platform, RunnerT, WindowDataErased};
#[allow(unused)] use crate::theme::SizeCx;
use crate::theme::ThemeSize;
use crate::window::{PopupDescriptor, WindowId};
use crate::{ActionClose, ActionMoved, ActionRedraw, ActionResize, ConfigAction, HasId, Id, Node};
use key::Input;
use nav::NavFocus;
#[cfg(feature = "accesskit")] mod accessibility;
mod key;
mod nav;
mod press;
mod send;
mod timer;
mod window;
pub use nav::NavAdvance;
pub use press::{GrabBuilder, GrabMode, Press, PressSource, PressStart};
pub(crate) use press::{Mouse, Touch};
pub use timer::TimerHandle;
struct PopupState {
id: WindowId,
desc: PopupDescriptor,
old_nav_focus: Option<Id>,
is_sized: bool,
}
pub struct EventState {
pub(crate) window_id: WindowId,
pub(crate) config: WindowConfig,
platform: Platform,
disabled: Vec<Id>,
window_has_focus: bool,
#[cfg(feature = "accesskit")]
accesskit_is_enabled: bool,
modifiers: ModifiersState,
input: Input,
nav: NavFocus,
key_depress: LinearMap<PhysicalKey, Id>,
mouse: Mouse,
touch: Touch,
access_keys: HashMap<Key, Id>,
popups: SmallVec<[PopupState; 16]>,
popup_removed: SmallVec<[(Id, WindowId); 16]>,
time_updates: Vec<(Instant, Id, TimerHandle)>,
frame_updates: LinearSet<(Id, TimerHandle)>,
need_frame_update: bool,
send_queue: VecDeque<(Id, Erased)>,
fut_messages: Vec<(Id, Pin<Box<dyn Future<Output = Erased>>>)>,
pub(super) pending_send_targets: Vec<(TypeId, Id)>,
action_moved: Option<ActionMoved>,
pub(crate) action_redraw: Option<ActionRedraw>,
action_close: Option<ActionClose>,
}
impl EventState {
#[inline]
pub(crate) fn new(window_id: WindowId, config: WindowConfig, platform: Platform) -> Self {
EventState {
window_id,
config,
platform,
disabled: vec![],
window_has_focus: false,
#[cfg(feature = "accesskit")]
accesskit_is_enabled: false,
modifiers: ModifiersState::empty(),
input: Input::default(),
nav: NavFocus::default(),
key_depress: Default::default(),
mouse: Default::default(),
touch: Default::default(),
access_keys: Default::default(),
popups: Default::default(),
popup_removed: Default::default(),
time_updates: vec![],
frame_updates: Default::default(),
need_frame_update: false,
send_queue: Default::default(),
pending_send_targets: vec![],
fut_messages: vec![],
action_moved: None,
action_redraw: None,
action_close: None,
}
}
pub(crate) fn update_config(&mut self, scale_factor: f32) {
self.config.update(scale_factor);
}
#[must_use]
pub(crate) fn full_configure(
&mut self,
sizer: &dyn ThemeSize,
node: Node,
) -> Option<ActionResize> {
let id = Id::ROOT.make_child(self.window_id.get().cast());
log::debug!(target: "kas_core::event", "full_configure of Window{id}");
self.nav.fallback = None;
let mut cx = ConfigCx::new(sizer, self);
cx.configure(node, id);
let resize = cx.resize;
self.action_moved = Some(ActionMoved);
resize
}
#[inline]
#[must_use]
pub(crate) fn with<'a, F: FnOnce(&mut EventCx)>(
&'a mut self,
runner: &'a mut dyn RunnerT,
theme: &'a dyn ThemeSize,
window: &'a dyn WindowDataErased,
f: F,
) -> Option<ActionResize> {
let mut cx = EventCx {
runner,
window,
cx: ConfigCx::new(theme, self),
target_is_disabled: false,
last_child: None,
scroll: Scroll::None,
resize_window: None,
};
f(&mut cx);
let resize = cx.resize.or(cx.resize_window);
let redraw = cx.redraw;
self.action_redraw = self.action_redraw.or(redraw);
resize
}
fn cancel_event_focus(&mut self, target: &Id) {
self.input.clear_sel_socus_on(target);
self.clear_nav_focus_on(target);
self.mouse.cancel_event_focus(target);
self.touch.cancel_event_focus(target);
}
#[inline]
pub fn is_disabled(&self, w_id: &Id) -> bool {
for id in &self.disabled {
if id.is_ancestor_of(w_id) {
return true;
}
}
false
}
#[inline]
pub fn config(&self) -> &WindowConfig {
&self.config
}
#[inline]
pub fn config_enable_pan(&self, source: PressSource) -> bool {
source.is_touch()
|| source.is_primary()
&& self
.config
.event()
.mouse_pan()
.is_enabled_with(self.modifiers())
}
#[inline]
pub fn config_enable_mouse_text_pan(&self) -> bool {
self.config
.event()
.mouse_text_pan()
.is_enabled_with(self.modifiers())
}
#[inline]
pub fn config_test_pan_thresh(&self, dist: Offset) -> bool {
Vec2::conv(dist).abs().max_comp() >= self.config.event().pan_dist_thresh()
}
#[inline]
pub fn redraw(&mut self, id: impl HasId) {
let _ = id;
self.action_redraw = Some(ActionRedraw);
}
#[inline]
pub(crate) fn opt_redraw(&mut self, id: Option<Id>) {
if let Some(id) = id {
self.redraw(id);
}
}
#[inline]
pub fn region_moved(&mut self) {
self.action_moved = Some(ActionMoved);
}
#[inline]
pub fn close_own_window(&mut self) {
self.action_close = Some(ActionClose);
}
#[inline]
pub fn action_moved(&mut self, action: impl Into<Option<ActionMoved>>) {
self.action_moved = self.action_moved.or(action.into());
}
#[inline]
pub fn action_redraw(&mut self, action: impl Into<Option<ActionRedraw>>) {
self.action_redraw = self.action_redraw.or(action.into());
}
}
#[must_use]
pub struct ConfigCx<'a> {
theme: &'a dyn ThemeSize,
state: &'a mut EventState,
resize: Option<ActionResize>,
redraw: Option<ActionRedraw>,
}
impl<'a> Deref for ConfigCx<'a> {
type Target = EventState;
fn deref(&self) -> &EventState {
self.state
}
}
impl<'a> DerefMut for ConfigCx<'a> {
fn deref_mut(&mut self) -> &mut EventState {
self.state
}
}
impl<'a> ConfigCx<'a> {
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
pub fn new(sh: &'a dyn ThemeSize, ev: &'a mut EventState) -> Self {
ConfigCx {
theme: sh,
state: ev,
resize: None,
redraw: None,
}
}
#[inline]
pub fn size_cx<'b>(&'b mut self) -> SizeCx<'b>
where
'a: 'b,
{
SizeCx::new(self.state, self.theme)
}
#[inline]
pub fn configure(&mut self, mut widget: Node<'_>, id: Id) {
let start_resize = std::mem::take(&mut self.resize);
widget._configure(self, id);
self.resize = self.resize.or(start_resize);
}
#[inline]
pub fn update(&mut self, mut widget: Node<'_>) {
let start_resize = std::mem::take(&mut self.resize);
widget._update(self);
self.resize = self.resize.or(start_resize);
}
pub fn set_disabled(&mut self, target: Id, disable: bool) {
if disable {
self.cancel_event_focus(&target);
}
for (i, id) in self.disabled.iter().enumerate() {
if target == id {
if !disable {
self.redraw();
self.disabled.remove(i);
}
return;
}
}
if disable {
self.redraw();
self.disabled.push(target);
}
}
pub fn set_send_target_for<M: Debug + 'static>(&mut self, id: Id) {
let type_id = TypeId::of::<M>();
self.pending_send_targets.push((type_id, id));
}
#[inline]
pub fn redraw(&mut self) {
self.redraw = Some(ActionRedraw);
}
#[inline]
pub fn resize(&mut self) {
self.resize = Some(ActionResize);
}
#[inline]
pub(crate) fn needs_redraw(&self) -> bool {
self.redraw.is_some()
}
#[inline]
pub(crate) fn needs_resize(&self) -> bool {
self.resize.is_some()
}
#[inline]
pub(crate) fn set_resize(&mut self, resize: Option<ActionResize>) {
self.resize = resize;
}
}
#[must_use]
pub struct EventCx<'a> {
runner: &'a mut dyn RunnerT,
window: &'a dyn WindowDataErased,
cx: ConfigCx<'a>,
pub(crate) target_is_disabled: bool,
last_child: Option<usize>,
scroll: Scroll,
resize_window: Option<ActionResize>,
}
impl<'a> Deref for EventCx<'a> {
type Target = ConfigCx<'a>;
fn deref(&self) -> &Self::Target {
&self.cx
}
}
impl<'a> DerefMut for EventCx<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cx
}
}
impl<'a> EventCx<'a> {
pub fn draw_shared(&mut self) -> &mut dyn DrawShared {
self.runner.draw_shared()
}
#[inline]
pub fn change_config(&mut self, msg: ConfigMsg) {
let action = self.config.change_config(msg);
if !action.is_empty() {
self.runner.config_update(action);
}
}
pub fn with_config(&mut self, f: impl FnOnce(&WindowConfig) -> ConfigAction) {
let action = f(&self.config);
if !action.is_empty() {
self.runner.config_update(action);
}
}
#[inline]
pub fn exit(&mut self) {
self.runner.exit();
}
fn pre_recursion(&self) {
debug_assert!(self.resize.is_none());
debug_assert!(self.scroll == Scroll::None);
debug_assert!(self.last_child.is_none());
}
fn post_recursion(&mut self) {
self.last_child = None;
self.scroll = Scroll::None;
self.resize_window = self.resize_window.or(self.resize);
self.resize = None;
}
}