#![cfg_attr(not(feature = "winit"), allow(unused))]
use linear_map::LinearMap;
use smallvec::SmallVec;
use std::any::Any;
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::ops::{Deref, DerefMut};
use std::time::Instant;
use std::u16;
use super::config::WindowConfig;
use super::*;
use crate::cast::Cast;
use crate::geom::{Coord, Offset};
use crate::{ShellWindow, TkAction, Widget, WidgetExt, WidgetId, WindowId};
mod config_mgr;
mod mgr_pub;
mod mgr_shell;
pub use config_mgr::ConfigMgr;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GrabMode {
Grab,
PanFull,
PanScale,
PanRotate,
PanOnly,
}
#[derive(Clone, Debug)]
struct MouseGrab {
button: MouseButton,
repetitions: u32,
start_id: WidgetId,
cur_id: Option<WidgetId>,
depress: Option<WidgetId>,
mode: GrabMode,
pan_grab: (u16, u16),
coord: Coord,
delta: Offset,
}
impl MouseGrab {
fn flush_move(&mut self) -> Option<(WidgetId, Event)> {
if self.delta != Offset::ZERO {
let event = Event::PressMove {
source: PressSource::Mouse(self.button, self.repetitions),
cur_id: self.cur_id.clone(),
coord: self.coord,
delta: self.delta,
};
self.delta = Offset::ZERO;
Some((self.start_id.clone(), event))
} else {
None
}
}
}
#[derive(Clone, Debug)]
struct TouchGrab {
id: u64,
start_id: WidgetId,
depress: Option<WidgetId>,
cur_id: Option<WidgetId>,
last_move: Coord,
coord: Coord,
mode: GrabMode,
pan_grab: (u16, u16),
}
impl TouchGrab {
fn flush_move(&mut self) -> Option<(WidgetId, Event)> {
if self.last_move != self.coord {
let delta = self.coord - self.last_move;
let target = self.start_id.clone();
let event = Event::PressMove {
source: PressSource::Touch(self.id),
cur_id: self.cur_id.clone(),
coord: self.coord,
delta,
};
self.last_move = self.coord;
Some((target, event))
} else {
None
}
}
}
const MAX_PAN_GRABS: usize = 2;
#[derive(Clone, Debug)]
struct PanGrab {
id: WidgetId,
mode: GrabMode,
source_is_touch: bool,
n: u16,
coords: [(Coord, Coord); MAX_PAN_GRABS],
}
#[derive(Clone, Debug)]
#[allow(clippy::enum_variant_names)] enum Pending {
SetNavFocus(WidgetId, bool),
MouseHover(WidgetId),
LostNavFocus(WidgetId),
LostMouseHover(WidgetId),
LostCharFocus(WidgetId),
LostSelFocus(WidgetId),
}
type AccelLayer = (bool, HashMap<VirtualKeyCode, WidgetId>);
#[derive(Debug)]
pub struct EventState {
config: WindowConfig,
disabled: Vec<WidgetId>,
window_has_focus: bool,
modifiers: ModifiersState,
char_focus: bool,
sel_focus: Option<WidgetId>,
nav_focus: Option<WidgetId>,
nav_fallback: Option<WidgetId>,
hover: Option<WidgetId>,
hover_icon: CursorIcon,
key_depress: LinearMap<u32, WidgetId>,
last_mouse_coord: Coord,
last_click_button: MouseButton,
last_click_repetitions: u32,
last_click_timeout: Instant,
mouse_grab: Option<MouseGrab>,
touch_grab: SmallVec<[TouchGrab; 8]>,
pan_grab: SmallVec<[PanGrab; 4]>,
accel_layers: BTreeMap<WidgetId, AccelLayer>,
popups: SmallVec<[(WindowId, crate::Popup, Option<WidgetId>); 16]>,
popup_removed: SmallVec<[(WidgetId, WindowId); 16]>,
time_updates: Vec<(Instant, WidgetId, u64)>,
pending: VecDeque<Pending>,
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub action: TkAction,
}
impl EventState {
#[inline]
fn char_focus(&self) -> Option<WidgetId> {
if self.char_focus {
self.sel_focus.clone()
} else {
None
}
}
fn set_pan_on(
&mut self,
id: WidgetId,
mode: GrabMode,
source_is_touch: bool,
coord: Coord,
) -> (u16, u16) {
for (gi, grab) in self.pan_grab.iter_mut().enumerate() {
if grab.id == id {
if grab.source_is_touch != source_is_touch {
self.remove_pan(gi);
break;
}
let index = grab.n;
if usize::from(index) < MAX_PAN_GRABS {
grab.coords[usize::from(index)] = (coord, coord);
}
grab.n = index + 1;
return (gi.cast(), index);
}
}
let gj = self.pan_grab.len().cast();
let n = 1;
let mut coords: [(Coord, Coord); MAX_PAN_GRABS] = Default::default();
coords[0] = (coord, coord);
log::trace!("set_pan_on: index={}, id={id}", self.pan_grab.len());
self.pan_grab.push(PanGrab {
id,
mode,
source_is_touch,
n,
coords,
});
(gj, 0)
}
fn remove_pan(&mut self, index: usize) {
log::trace!("remove_pan: index={index}");
self.pan_grab.remove(index);
if let Some(grab) = &mut self.mouse_grab {
let p0 = grab.pan_grab.0;
if usize::from(p0) >= index && p0 != u16::MAX {
grab.pan_grab.0 = p0 - 1;
}
}
for grab in self.touch_grab.iter_mut() {
let p0 = grab.pan_grab.0;
if usize::from(p0) >= index && p0 != u16::MAX {
grab.pan_grab.0 = p0 - 1;
}
}
}
fn remove_pan_grab(&mut self, g: (u16, u16)) {
if let Some(grab) = self.pan_grab.get_mut(usize::from(g.0)) {
grab.n -= 1;
if grab.n == 0 {
return self.remove_pan(g.0.into());
}
assert!(grab.source_is_touch);
for i in (usize::from(g.1))..(usize::from(grab.n) - 1) {
grab.coords[i] = grab.coords[i + 1];
}
} else {
return;
}
for grab in self.touch_grab.iter_mut() {
if grab.pan_grab.0 == g.0 && grab.pan_grab.1 > g.1 {
grab.pan_grab.1 -= 1;
if usize::from(grab.pan_grab.1) == MAX_PAN_GRABS - 1 {
let v = grab.coord;
self.pan_grab[usize::from(g.0)].coords[usize::from(grab.pan_grab.1)] = (v, v);
}
}
}
}
fn add_key_depress(&mut self, scancode: u32, id: WidgetId) {
if self.key_depress.values().any(|v| *v == id) {
return;
}
self.key_depress.insert(scancode, id);
self.send_action(TkAction::REDRAW);
}
fn end_key_event(&mut self, scancode: u32) {
if let Some(id) = self.key_depress.remove(&scancode) {
self.redraw(id);
}
}
fn mouse_grab(&mut self) -> Option<&mut MouseGrab> {
self.mouse_grab.as_mut()
}
#[inline]
fn get_touch(&mut self, touch_id: u64) -> Option<&mut TouchGrab> {
self.touch_grab.iter_mut().find(|grab| grab.id == touch_id)
}
fn remove_touch(&mut self, touch_id: u64) -> Option<TouchGrab> {
for i in 0..self.touch_grab.len() {
if self.touch_grab[i].id == touch_id {
let grab = self.touch_grab.remove(i);
log::trace!(
"remove_touch: touch_id={touch_id}, start_id={}",
grab.start_id
);
self.send_action(TkAction::REDRAW); self.remove_pan_grab(grab.pan_grab);
return Some(grab);
}
}
None
}
fn clear_char_focus(&mut self) {
if let Some(id) = self.char_focus() {
log::trace!("clear_char_focus");
self.char_focus = false;
self.pending.push_back(Pending::LostCharFocus(id));
}
}
fn set_sel_focus(&mut self, wid: WidgetId, char_focus: bool) {
log::trace!("set_sel_focus: wid={wid}, char_focus={char_focus}");
self.set_nav_focus(wid.clone(), true);
if wid == self.sel_focus {
self.char_focus = self.char_focus || char_focus;
return;
}
if let Some(id) = self.sel_focus.clone() {
if self.char_focus {
self.pending.push_back(Pending::LostCharFocus(id.clone()));
}
self.pending.push_back(Pending::LostSelFocus(id));
}
self.char_focus = char_focus;
self.sel_focus = Some(wid);
}
}
struct Message {
any: Box<dyn Any>,
#[cfg(debug_assertions)]
fmt: String,
}
impl Message {
fn new<M: Any + Debug>(msg: Box<M>) -> Self {
#[cfg(debug_assertions)]
let fmt = format!("{}::{:?}", std::any::type_name::<M>(), &msg);
#[cfg(debug_assertions)]
log::debug!(target: "kas_core::event::manager::messages", "push_msg: {fmt}");
let any = msg;
Message {
#[cfg(debug_assertions)]
fmt,
any,
}
}
fn is<T: 'static>(&self) -> bool {
self.any.is::<T>()
}
fn downcast<T: 'static>(self) -> Result<Box<T>, Box<dyn Any>> {
self.any.downcast::<T>()
}
fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.any.downcast_ref::<T>()
}
}
impl std::fmt::Debug for Message {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
#[cfg(debug_assertions)]
let r = f.write_str(&self.fmt);
#[cfg(not(debug_assertions))]
let r = f.write_str("[use debug build to see value]");
r
}
}
#[must_use]
pub struct EventMgr<'a> {
state: &'a mut EventState,
shell: &'a mut dyn ShellWindow,
messages: Vec<Message>,
scroll: Scroll,
action: TkAction,
}
impl<'a> Deref for EventMgr<'a> {
type Target = EventState;
fn deref(&self) -> &Self::Target {
self.state
}
}
impl<'a> DerefMut for EventMgr<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.state
}
}
impl<'a> Drop for EventMgr<'a> {
fn drop(&mut self) {
for msg in self.messages.drain(..) {
log::warn!(target: "kas_core::event::manager::messages", "unhandled: {msg:?}");
}
}
}
impl<'a> EventMgr<'a> {
fn set_hover(&mut self, w_id: Option<WidgetId>) {
if self.hover != w_id {
log::trace!("set_hover: w_id={w_id:?}");
if let Some(id) = self.hover.take() {
self.pending.push_back(Pending::LostMouseHover(id));
}
self.hover = w_id.clone();
if let Some(id) = w_id {
self.pending.push_back(Pending::MouseHover(id));
}
}
}
fn start_key_event(&mut self, widget: &mut dyn Widget, vkey: VirtualKeyCode, scancode: u32) {
log::trace!(
"start_key_event: widget={}, vkey={vkey:?}, scancode={scancode}",
widget.id()
);
use VirtualKeyCode as VK;
let opt_command = self.config.shortcuts(|s| s.get(self.modifiers, vkey));
if let Some(cmd) = opt_command {
let mut targets = vec![];
let mut send = |_self: &mut Self, id: WidgetId, cmd| -> bool {
if !targets.contains(&id) {
let used = _self.send_event(widget, id.clone(), Event::Command(cmd));
if used {
_self.add_key_depress(scancode, id.clone());
}
targets.push(id);
used
} else {
false
}
};
if self.char_focus || cmd.suitable_for_sel_focus() {
if let Some(id) = self.sel_focus.clone() {
if send(self, id, cmd) {
return;
}
}
}
if !self.modifiers.alt() {
if let Some(id) = self.nav_focus.clone() {
if send(self, id, cmd) {
return;
}
}
}
if let Some(id) = self.popups.last().map(|popup| popup.1.parent.clone()) {
if send(self, id, cmd) {
return;
}
}
if let Some(id) = self.nav_fallback.clone() {
if send(self, id, cmd) {
return;
}
}
}
let mut target = None;
let mut n = 0;
for (i, id) in (self.popups.iter().rev())
.map(|(_, popup, _)| popup.parent.clone())
.chain(std::iter::once(widget.id()))
.enumerate()
{
if let Some(layer) = self.accel_layers.get(&id) {
if self.modifiers == ModifiersState::ALT
|| layer.0 && self.modifiers == ModifiersState::empty()
{
if let Some(id) = layer.1.get(&vkey).cloned() {
target = Some(id);
n = i;
break;
}
}
}
}
if n > 0 {
let len = self.popups.len();
for i in ((len - n)..len).rev() {
let id = self.popups[i].0;
self.close_window(id, false);
}
}
if let Some(id) = target {
if widget
.find_widget(&id)
.map(|w| w.navigable())
.unwrap_or(false)
{
self.set_nav_focus(id.clone(), true);
}
self.add_key_depress(scancode, id.clone());
self.send_event(widget, id, Event::Command(Command::Activate));
} else if self.config.nav_focus && vkey == VK::Tab {
self.clear_char_focus();
let shift = self.modifiers.shift();
self.next_nav_focus(widget, shift, true);
} else if vkey == VK::Escape {
if let Some(id) = self.popups.last().map(|(id, _, _)| *id) {
self.close_window(id, true);
}
}
}
fn remove_mouse_grab(&mut self) -> Option<MouseGrab> {
if let Some(grab) = self.mouse_grab.take() {
log::trace!("remove_mouse_grab: start_id={}", grab.start_id);
self.shell.set_cursor_icon(self.hover_icon);
self.send_action(TkAction::REDRAW); self.remove_pan_grab(grab.pan_grab);
Some(grab)
} else {
None
}
}
fn send_recurse(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
disabled: bool,
event: Event,
) -> Response {
let mut response = Response::Unused;
if widget.steal_event(self, &id, &event) == Response::Used {
response = Response::Used;
} else if let Some(index) = widget.find_child_index(&id) {
let translation = widget.translation();
if let Some(w) = widget.get_child_mut(index) {
response = self.send_recurse(w, id, disabled, event.clone() + translation);
if self.scroll != Scroll::None {
widget.handle_scroll(self, self.scroll);
}
} else {
log::warn!(
"send_recurse: {} found index {index} for {id} but not child",
widget.identify()
);
}
if matches!(response, Response::Unused) {
response = widget.handle_unused(self, index, event);
} else if self.has_msg() {
widget.handle_message(self, index);
}
} else if disabled {
} else if id == widget.id_ref() {
if event == Event::NavFocus(true) {
self.set_scroll(Scroll::Rect(widget.rect()));
response = Response::Used;
}
response |= widget.pre_handle_event(self, event)
} else {
log::warn!(
"send_recurse: Widget {} cannot find path to {id}",
widget.identify()
);
}
response
}
fn send_all(&mut self, widget: &mut dyn Widget, event: Event) -> usize {
let child_event = event.clone() + widget.translation();
widget.pre_handle_event(self, event);
let mut count = 1;
for index in 0..widget.num_children() {
if let Some(w) = widget.get_child_mut(index) {
count += self.send_all(w, child_event.clone());
}
}
count
}
#[inline]
fn send_event(&mut self, widget: &mut dyn Widget, id: WidgetId, event: Event) -> bool {
self.send(widget, id, event) == Response::Used
}
fn send_popup_first(&mut self, widget: &mut dyn Widget, id: Option<WidgetId>, event: Event) {
while let Some((wid, parent)) = self
.popups
.last()
.map(|(wid, p, _)| (*wid, p.parent.clone()))
{
log::trace!("send_popup_first: parent={parent}: {event:?}");
match self.send(widget, parent, event.clone()) {
Response::Unused => (),
_ => return,
}
self.close_window(wid, false);
}
if let Some(id) = id {
self.send_event(widget, id, event);
}
}
}