dioxus/packages/tui/src/hooks.rs

806 lines
30 KiB
Rust
Raw Normal View History

2022-02-05 22:28:19 +00:00
use crossterm::event::{
Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
};
2022-03-09 18:30:44 +00:00
use dioxus_core::*;
2022-03-27 01:10:15 +00:00
use fxhash::{FxHashMap, FxHashSet};
2022-02-05 22:28:19 +00:00
use dioxus_html::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
use dioxus_html::input::keyboard_types::Modifiers;
use dioxus_html::input::MouseButton as DioxusMouseButton;
use dioxus_html::input::MouseButtonSet as DioxusMouseButtons;
2022-02-05 22:28:19 +00:00
use dioxus_html::{on::*, KeyCode};
2022-01-12 14:40:36 +00:00
use std::{
2022-02-05 22:28:19 +00:00
any::Any,
cell::RefCell,
2022-01-12 14:40:36 +00:00
rc::Rc,
2022-02-05 22:28:19 +00:00
sync::Arc,
time::{Duration, Instant},
2022-01-12 14:40:36 +00:00
};
2022-05-07 12:32:19 +00:00
use stretch2::geometry::Point;
2022-02-07 11:57:57 +00:00
use stretch2::{prelude::Layout, Stretch};
2022-04-12 23:46:16 +00:00
use crate::{Dom, Node};
2022-01-12 14:40:36 +00:00
2022-02-05 22:28:19 +00:00
// a wrapper around the input state for easier access
// todo: fix loop
// pub struct InputState(Rc<Rc<RefCell<InnerInputState>>>);
// impl InputState {
// pub fn get(cx: &ScopeState) -> InputState {
// let inner = cx
// .consume_context::<Rc<RefCell<InnerInputState>>>()
// .expect("Rink InputState can only be used in Rink apps!");
// (**inner).borrow_mut().subscribe(cx.schedule_update());
// InputState(inner)
// }
// pub fn mouse(&self) -> Option<MouseData> {
// let data = (**self.0).borrow();
2022-04-17 12:29:35 +00:00
// data.mouse.as_ref().map(|m| m.clone())
2022-02-05 22:28:19 +00:00
// }
// pub fn wheel(&self) -> Option<WheelData> {
// let data = (**self.0).borrow();
2022-04-17 12:29:35 +00:00
// data.wheel.as_ref().map(|w| w.clone())
2022-02-05 22:28:19 +00:00
// }
// pub fn screen(&self) -> Option<(u16, u16)> {
// let data = (**self.0).borrow();
// data.screen.as_ref().map(|m| m.clone())
// }
// pub fn last_key_pressed(&self) -> Option<KeyboardData> {
// let data = (**self.0).borrow();
// data.last_key_pressed
// .as_ref()
2022-04-17 12:29:35 +00:00
// .map(|k| &k.0.clone())
2022-02-05 22:28:19 +00:00
// }
// }
type EventCore = (&'static str, EventData);
#[derive(Debug)]
enum EventData {
Mouse(MouseData),
Wheel(WheelData),
Screen((u16, u16)),
Keyboard(KeyboardData),
}
impl EventData {
fn into_any(self) -> Arc<dyn Any + Send + Sync> {
match self {
Self::Mouse(m) => Arc::new(m),
Self::Wheel(w) => Arc::new(w),
Self::Screen(s) => Arc::new(s),
Self::Keyboard(k) => Arc::new(k),
}
}
2022-01-12 14:40:36 +00:00
}
2022-02-05 22:28:19 +00:00
const MAX_REPEAT_TIME: Duration = Duration::from_millis(100);
2022-02-04 20:52:01 +00:00
2022-02-05 22:28:19 +00:00
pub struct InnerInputState {
2022-02-06 13:08:15 +00:00
mouse: Option<(MouseData, Vec<u16>)>,
2022-02-05 22:28:19 +00:00
wheel: Option<WheelData>,
last_key_pressed: Option<(KeyboardData, Instant)>,
screen: Option<(u16, u16)>,
// subscribers: Vec<Rc<dyn Fn() + 'static>>,
}
2022-02-04 20:52:01 +00:00
2022-02-05 22:28:19 +00:00
impl InnerInputState {
fn new() -> Self {
2022-01-12 14:40:36 +00:00
Self {
2022-02-05 22:28:19 +00:00
mouse: None,
wheel: None,
last_key_pressed: None,
screen: None,
// subscribers: Vec::new(),
2022-01-12 14:40:36 +00:00
}
}
2022-02-04 20:52:01 +00:00
2022-02-05 22:28:19 +00:00
// stores current input state and transforms events based on that state
fn apply_event(&mut self, evt: &mut EventCore) {
match evt.1 {
2022-02-06 13:08:15 +00:00
// limitations: only two buttons may be held at once
2022-02-05 22:28:19 +00:00
EventData::Mouse(ref mut m) => match &mut self.mouse {
Some(state) => {
2022-02-06 13:08:15 +00:00
let mut buttons = state.0.buttons;
2022-04-17 12:29:35 +00:00
state.0 = m.clone();
2022-02-06 13:08:15 +00:00
match evt.0 {
// this code only runs when there are no buttons down
"mouseup" => {
buttons = 0;
state.1 = Vec::new();
}
"mousedown" => {
if state.1.contains(&m.buttons) {
// if we already pressed a button and there is another button released the button crossterm sends is the button remaining
if state.1.len() > 1 {
2022-02-07 11:57:57 +00:00
evt.0 = "mouseup";
2022-02-06 13:08:15 +00:00
state.1 = vec![m.buttons];
}
// otherwise some other button was pressed. In testing it was consistantly this mapping
else {
match m.buttons {
0x01 => state.1.push(0x02),
0x02 => state.1.push(0x01),
0x04 => state.1.push(0x01),
_ => (),
}
}
} else {
state.1.push(m.buttons);
}
buttons = state.1.iter().copied().reduce(|a, b| a | b).unwrap();
}
_ => (),
}
state.0.buttons = buttons;
m.buttons = buttons;
2022-02-05 22:28:19 +00:00
}
None => {
2022-02-06 13:08:15 +00:00
self.mouse = Some((
2022-04-17 12:29:35 +00:00
m.clone(),
2022-02-06 13:08:15 +00:00
if m.buttons == 0 {
Vec::new()
} else {
vec![m.buttons]
},
));
2022-02-05 22:28:19 +00:00
}
},
2022-04-17 12:29:35 +00:00
EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
2022-03-09 18:30:44 +00:00
EventData::Screen(ref s) => self.screen = Some(*s),
2022-02-05 22:28:19 +00:00
EventData::Keyboard(ref mut k) => {
let repeat = self
.last_key_pressed
.as_ref()
.filter(|k2| k2.0.key == k.key && k2.1.elapsed() < MAX_REPEAT_TIME)
.is_some();
k.repeat = repeat;
2022-04-17 12:29:35 +00:00
let new = k.clone();
2022-02-05 22:28:19 +00:00
self.last_key_pressed = Some((new, Instant::now()));
}
}
2022-02-04 20:52:01 +00:00
}
2022-04-02 21:46:46 +00:00
fn update(
2022-02-07 11:57:57 +00:00
&mut self,
2022-04-17 13:43:15 +00:00
evts: &mut [EventCore],
2022-02-07 11:57:57 +00:00
resolved_events: &mut Vec<UserEvent>,
layout: &Stretch,
2022-04-12 23:46:16 +00:00
dom: &mut Dom,
2022-03-23 19:18:17 +00:00
) {
2022-04-17 12:29:35 +00:00
let previous_mouse = self.mouse.as_ref().map(|m| (m.0.clone(), m.1.clone()));
2022-03-23 19:18:17 +00:00
self.wheel = None;
for e in evts.iter_mut() {
self.apply_event(e);
}
self.resolve_mouse_events(previous_mouse, resolved_events, layout, dom);
2022-03-23 19:18:17 +00:00
// for s in &self.subscribers {
// s();
// }
}
fn resolve_mouse_events(
&self,
previous_mouse: Option<(MouseData, Vec<u16>)>,
resolved_events: &mut Vec<UserEvent>,
layout: &Stretch,
2022-04-12 23:46:16 +00:00
dom: &mut Dom,
2022-02-07 11:57:57 +00:00
) {
fn layout_contains_point(layout: &Layout, point: (i32, i32)) -> bool {
layout.location.x as i32 <= point.0
&& layout.location.x as i32 + layout.size.width as i32 >= point.0
&& layout.location.y as i32 <= point.1
&& layout.location.y as i32 + layout.size.height as i32 >= point.1
2022-02-04 20:52:01 +00:00
}
2022-02-07 11:57:57 +00:00
2022-03-23 19:18:17 +00:00
fn try_create_event(
name: &'static str,
data: Arc<dyn Any + Send + Sync>,
2022-03-27 01:10:15 +00:00
will_bubble: &mut FxHashSet<ElementId>,
2022-02-07 11:57:57 +00:00
resolved_events: &mut Vec<UserEvent>,
2022-04-12 23:46:16 +00:00
node: &Node,
dom: &Dom,
2022-03-23 19:18:17 +00:00
) {
// only trigger event if the event was not triggered already by a child
if will_bubble.insert(node.id) {
let mut parent = node.parent;
while let Some(parent_id) = parent {
will_bubble.insert(parent_id);
parent = dom[parent_id.0].parent;
2022-02-07 11:57:57 +00:00
}
2022-03-23 19:18:17 +00:00
resolved_events.push(UserEvent {
scope_id: None,
priority: EventPriority::Medium,
name,
element: Some(node.id),
2022-04-02 21:46:46 +00:00
data,
2022-03-23 19:18:17 +00:00
})
2022-02-07 11:57:57 +00:00
}
}
2022-05-04 18:58:48 +00:00
fn prepare_mouse_data(mouse_data: &MouseData, layout: &Layout) -> MouseData {
2022-05-07 12:32:19 +00:00
let Point { x, y } = layout.location;
let node_origin = ClientPoint::new(x.into(), y.into());
let new_client_coordinates = (mouse_data.client_coordinates() - node_origin)
.to_point()
.cast_unit();
let coordinates = Coordinates::new(
mouse_data.screen_coordinates(),
mouse_data.client_coordinates(),
new_client_coordinates,
mouse_data.page_coordinates(),
);
MouseData::new(
coordinates,
mouse_data.trigger_button(),
mouse_data.held_buttons(),
mouse_data.modifiers(),
)
2022-05-04 18:58:48 +00:00
}
2022-02-07 11:57:57 +00:00
if let Some(mouse) = &self.mouse {
let new_pos = (mouse.0.screen_x, mouse.0.screen_y);
let old_pos = previous_mouse
.as_ref()
.map(|m| (m.0.screen_x, m.0.screen_y));
2022-05-07 00:35:55 +00:00
// the a mouse button is pressed if a button was not down and is now down
let pressed =
(mouse.0.buttons & !previous_mouse.as_ref().map(|m| m.0.buttons).unwrap_or(0)) > 0;
// the a mouse button is pressed if a button was down and is now not down
2022-02-07 11:57:57 +00:00
let released =
2022-05-07 00:35:55 +00:00
(!mouse.0.buttons & previous_mouse.map(|m| m.0.buttons).unwrap_or(0)) > 0;
2022-02-07 11:57:57 +00:00
let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y);
let mouse_data = &mouse.0;
let wheel_data = &self.wheel;
2022-03-23 19:18:17 +00:00
{
// mousemove
if old_pos != Some(new_pos) {
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mousemove") {
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
let previously_contained = old_pos
.filter(|pos| layout_contains_point(node_layout, *pos))
.is_some();
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
if currently_contains && previously_contained {
try_create_event(
"mousemove",
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
&mut will_bubble,
resolved_events,
node,
dom,
);
}
2022-03-23 19:18:17 +00:00
}
}
}
{
// mouseenter
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mouseenter") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
.filter(|pos| layout_contains_point(node_layout, *pos))
.is_some();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
2022-04-02 21:46:46 +00:00
if currently_contains && !previously_contained {
try_create_event(
"mouseenter",
2022-05-04 18:58:48 +00:00
Arc::new(mouse_data.clone()),
2022-04-02 21:46:46 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-04-02 21:46:46 +00:00
);
2022-03-23 19:18:17 +00:00
}
}
}
{
// mouseover
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mouseover") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
.filter(|pos| layout_contains_point(node_layout, *pos))
.is_some();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
2022-04-02 21:46:46 +00:00
if currently_contains && !previously_contained {
try_create_event(
"mouseover",
2022-05-04 18:58:48 +00:00
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
2022-04-02 21:46:46 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-04-02 21:46:46 +00:00
);
2022-03-23 19:18:17 +00:00
}
}
}
2022-05-07 00:35:55 +00:00
// mousedown
if pressed {
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mousedown") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
2022-05-07 00:48:57 +00:00
if currently_contains {
2022-04-02 21:46:46 +00:00
try_create_event(
"mousedown",
2022-05-04 18:58:48 +00:00
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
2022-04-02 21:46:46 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-04-02 21:46:46 +00:00
);
2022-03-23 19:18:17 +00:00
}
}
}
{
// mouseup
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mouseup") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
2022-05-04 18:58:48 +00:00
if currently_contains && released {
2022-04-02 21:46:46 +00:00
try_create_event(
"mouseup",
2022-05-04 18:58:48 +00:00
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
2022-04-02 21:46:46 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-04-02 21:46:46 +00:00
);
2022-03-23 19:18:17 +00:00
}
}
}
{
// click
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("click") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
2022-05-04 18:58:48 +00:00
if currently_contains && released && mouse_data.button == 0 {
2022-04-02 21:46:46 +00:00
try_create_event(
"click",
2022-05-04 18:58:48 +00:00
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
2022-04-02 21:46:46 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-04-02 21:46:46 +00:00
);
2022-03-23 19:18:17 +00:00
}
}
}
{
// contextmenu
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("contextmenu") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
2022-05-04 18:58:48 +00:00
if currently_contains && released && mouse_data.button == 2 {
2022-04-02 21:46:46 +00:00
try_create_event(
"contextmenu",
2022-05-04 18:58:48 +00:00
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
2022-04-02 21:46:46 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-04-02 21:46:46 +00:00
);
2022-03-23 19:18:17 +00:00
}
}
}
{
// wheel
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("wheel") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
2022-05-04 18:58:48 +00:00
if let Some(w) = wheel_data {
if currently_contains && wheel_delta != 0.0 {
2022-04-02 21:46:46 +00:00
try_create_event(
"wheel",
2022-04-17 12:29:35 +00:00
Arc::new(w.clone()),
2022-04-02 21:46:46 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-04-02 21:46:46 +00:00
);
2022-03-23 19:18:17 +00:00
}
}
}
}
{
// mouseleave
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mouseleave") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
.filter(|pos| layout_contains_point(node_layout, *pos))
.is_some();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
if !currently_contains && previously_contained {
try_create_event(
"mouseleave",
2022-05-04 18:58:48 +00:00
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
2022-03-23 19:18:17 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-03-23 19:18:17 +00:00
);
}
}
}
{
// mouseout
2022-03-27 01:10:15 +00:00
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mouseout") {
2022-04-12 23:46:16 +00:00
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
2022-05-04 18:58:48 +00:00
let previously_contained = old_pos
2022-03-23 19:18:17 +00:00
.filter(|pos| layout_contains_point(node_layout, *pos))
.is_some();
2022-05-04 18:58:48 +00:00
let currently_contains = layout_contains_point(node_layout, new_pos);
2022-03-23 19:18:17 +00:00
if !currently_contains && previously_contained {
try_create_event(
"mouseout",
2022-05-04 18:58:48 +00:00
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
2022-03-23 19:18:17 +00:00
&mut will_bubble,
resolved_events,
node,
dom,
2022-03-23 19:18:17 +00:00
);
}
}
}
}
2022-01-12 14:40:36 +00:00
}
2022-02-05 22:28:19 +00:00
// fn subscribe(&mut self, f: Rc<dyn Fn() + 'static>) {
// self.subscribers.push(f)
// }
}
2022-01-12 14:40:36 +00:00
2022-02-05 22:28:19 +00:00
pub struct RinkInputHandler {
state: Rc<RefCell<InnerInputState>>,
queued_events: Rc<RefCell<Vec<EventCore>>>,
2022-01-12 14:40:36 +00:00
}
2022-02-05 22:28:19 +00:00
impl RinkInputHandler {
/// global context that handles events
2022-02-06 13:08:15 +00:00
/// limitations: GUI key modifier is never detected, key up events are not detected, and only two mouse buttons may be pressed at once
2022-04-02 21:46:46 +00:00
pub fn new() -> (
Self,
Rc<RefCell<InnerInputState>>,
impl FnMut(crossterm::event::Event),
) {
2022-02-05 22:28:19 +00:00
let queued_events = Rc::new(RefCell::new(Vec::new()));
2022-03-23 19:18:17 +00:00
let queued_events2 = Rc::downgrade(&queued_events);
2022-02-05 22:28:19 +00:00
2022-04-02 21:46:46 +00:00
let regester_event = move |evt: crossterm::event::Event| {
if let Some(evt) = get_event(evt) {
if let Some(v) = queued_events2.upgrade() {
(*v).borrow_mut().push(evt);
2022-02-04 20:52:01 +00:00
}
2022-02-05 22:28:19 +00:00
}
2022-04-02 21:46:46 +00:00
};
2022-02-05 22:28:19 +00:00
let state = Rc::new(RefCell::new(InnerInputState::new()));
(
Self {
state: state.clone(),
queued_events,
},
state,
2022-04-02 21:46:46 +00:00
regester_event,
2022-02-05 22:28:19 +00:00
)
}
2022-04-12 23:46:16 +00:00
pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> Vec<UserEvent> {
2022-02-05 22:28:19 +00:00
let mut resolved_events = Vec::new();
2022-02-07 11:57:57 +00:00
(*self.state).borrow_mut().update(
&mut (*self.queued_events).borrow_mut(),
&mut resolved_events,
layout,
dom,
2022-02-07 11:57:57 +00:00
);
2022-02-05 22:28:19 +00:00
2022-03-23 19:18:17 +00:00
let events = self
2022-02-05 22:28:19 +00:00
.queued_events
.replace(Vec::new())
.into_iter()
2022-02-07 11:57:57 +00:00
// these events were added in the update stage
2022-03-23 19:18:17 +00:00
.filter(|e| {
![
"mouseenter",
"mouseover",
"mouseleave",
"mouseout",
"mousedown",
"mouseup",
"mousemove",
"drag",
"wheel",
"click",
"contextmenu",
]
.contains(&e.0)
})
.map(|evt| (evt.0, evt.1.into_any()));
2022-02-05 22:28:19 +00:00
2022-03-23 19:18:17 +00:00
// todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
2022-03-27 01:10:15 +00:00
let mut hm: FxHashMap<&'static str, Vec<Arc<dyn Any + Send + Sync>>> = FxHashMap::default();
2022-03-23 19:18:17 +00:00
for (event, data) in events {
if let Some(v) = hm.get_mut(event) {
v.push(data);
} else {
hm.insert(event, vec![data]);
}
}
for (event, datas) in hm {
for node in dom.get_listening_sorted(event) {
2022-03-23 19:18:17 +00:00
for data in &datas {
resolved_events.push(UserEvent {
scope_id: None,
priority: EventPriority::Medium,
name: event,
element: Some(node.id),
data: data.clone(),
});
}
}
}
2022-02-05 22:28:19 +00:00
2022-02-07 11:57:57 +00:00
resolved_events
2022-02-05 22:28:19 +00:00
}
}
2022-02-07 11:57:57 +00:00
// translate crossterm events into dioxus events
2022-02-05 22:28:19 +00:00
fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
let (name, data): (&str, EventData) = match evt {
2022-03-10 03:06:45 +00:00
TermEvent::Key(k) => ("keydown", translate_key_event(k)?),
2022-02-05 22:28:19 +00:00
TermEvent::Mouse(m) => {
let (x, y) = (m.column.into(), m.row.into());
let alt = m.modifiers.contains(KeyModifiers::ALT);
let shift = m.modifiers.contains(KeyModifiers::SHIFT);
let ctrl = m.modifiers.contains(KeyModifiers::CONTROL);
let meta = false;
let get_mouse_data = |crossterm_button: Option<MouseButton>| {
let button = crossterm_button.map(|b| match b {
MouseButton::Left => DioxusMouseButton::Primary,
MouseButton::Right => DioxusMouseButton::Secondary,
MouseButton::Middle => DioxusMouseButton::Auxiliary,
});
let button_set = match button {
None => DioxusMouseButtons::empty(),
Some(button) => DioxusMouseButtons::only(button),
2022-02-05 22:28:19 +00:00
};
2022-02-05 22:28:19 +00:00
// from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
2022-05-04 18:58:48 +00:00
// The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively.
// todo?
// But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway.
let coordinates = Coordinates::new(
ScreenPoint::new(x, y),
ClientPoint::new(x, y),
2022-05-04 18:58:48 +00:00
// offset x/y are set when the origin of the event is assigned to an element
ElementPoint::new(0., 0.),
PagePoint::new(x, y),
);
let mut modifiers = Modifiers::empty();
if shift {
modifiers.insert(Modifiers::SHIFT);
}
if ctrl {
modifiers.insert(Modifiers::CONTROL);
}
if meta {
modifiers.insert(Modifiers::META);
}
if alt {
modifiers.insert(Modifiers::ALT);
}
EventData::Mouse(MouseData::new(coordinates, button, button_set, modifiers))
2022-02-05 22:28:19 +00:00
};
let get_wheel_data = |up| {
// from https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
EventData::Wheel(WheelData {
delta_mode: 0x01,
delta_x: 0.0,
delta_y: if up { -1.0 } else { 1.0 },
delta_z: 0.0,
})
};
match m.kind {
MouseEventKind::Down(b) => ("mousedown", get_mouse_data(Some(b))),
MouseEventKind::Up(b) => ("mouseup", get_mouse_data(Some(b))),
MouseEventKind::Drag(b) => ("drag", get_mouse_data(Some(b))),
MouseEventKind::Moved => ("mousemove", get_mouse_data(None)),
2022-02-05 22:45:40 +00:00
MouseEventKind::ScrollDown => ("wheel", get_wheel_data(false)),
MouseEventKind::ScrollUp => ("wheel", get_wheel_data(true)),
2022-01-12 14:40:36 +00:00
}
}
2022-02-05 22:28:19 +00:00
TermEvent::Resize(x, y) => ("resize", EventData::Screen((x, y))),
};
Some((name, data))
}
2022-03-10 03:06:45 +00:00
fn translate_key_event(event: crossterm::event::KeyEvent) -> Option<EventData> {
let (code, key_str);
if let TermKeyCode::Char(c) = event.code {
code = match c {
'A'..='Z' | 'a'..='z' => match c.to_ascii_uppercase() {
'A' => KeyCode::A,
'B' => KeyCode::B,
'C' => KeyCode::C,
'D' => KeyCode::D,
'E' => KeyCode::E,
'F' => KeyCode::F,
'G' => KeyCode::G,
'H' => KeyCode::H,
'I' => KeyCode::I,
'J' => KeyCode::J,
'K' => KeyCode::K,
'L' => KeyCode::L,
'M' => KeyCode::M,
'N' => KeyCode::N,
'O' => KeyCode::O,
'P' => KeyCode::P,
'Q' => KeyCode::Q,
'R' => KeyCode::R,
'S' => KeyCode::S,
'T' => KeyCode::T,
'U' => KeyCode::U,
'V' => KeyCode::V,
'W' => KeyCode::W,
'X' => KeyCode::X,
'Y' => KeyCode::Y,
'Z' => KeyCode::Z,
_ => return None,
},
' ' => KeyCode::Space,
'[' => KeyCode::OpenBracket,
'{' => KeyCode::OpenBracket,
']' => KeyCode::CloseBraket,
'}' => KeyCode::CloseBraket,
';' => KeyCode::Semicolon,
':' => KeyCode::Semicolon,
',' => KeyCode::Comma,
'<' => KeyCode::Comma,
'.' => KeyCode::Period,
'>' => KeyCode::Period,
'1' => KeyCode::Num1,
'2' => KeyCode::Num2,
'3' => KeyCode::Num3,
'4' => KeyCode::Num4,
'5' => KeyCode::Num5,
'6' => KeyCode::Num6,
'7' => KeyCode::Num7,
'8' => KeyCode::Num8,
'9' => KeyCode::Num9,
'0' => KeyCode::Num0,
'!' => KeyCode::Num1,
'@' => KeyCode::Num2,
'#' => KeyCode::Num3,
'$' => KeyCode::Num4,
'%' => KeyCode::Num5,
'^' => KeyCode::Num6,
'&' => KeyCode::Num7,
'*' => KeyCode::Num8,
'(' => KeyCode::Num9,
')' => KeyCode::Num0,
// numpad charicter are ambiguous to tui
// '*' => KeyCode::Multiply,
// '/' => KeyCode::Divide,
// '-' => KeyCode::Subtract,
// '+' => KeyCode::Add,
'+' => KeyCode::EqualSign,
'-' => KeyCode::Dash,
'_' => KeyCode::Dash,
'\'' => KeyCode::SingleQuote,
'"' => KeyCode::SingleQuote,
'\\' => KeyCode::BackSlash,
'|' => KeyCode::BackSlash,
'/' => KeyCode::ForwardSlash,
'?' => KeyCode::ForwardSlash,
'=' => KeyCode::EqualSign,
'`' => KeyCode::GraveAccent,
'~' => KeyCode::GraveAccent,
_ => return None,
};
key_str = c.to_string();
} else {
code = match event.code {
TermKeyCode::Esc => KeyCode::Escape,
TermKeyCode::Backspace => KeyCode::Backspace,
TermKeyCode::Enter => KeyCode::Enter,
TermKeyCode::Left => KeyCode::LeftArrow,
TermKeyCode::Right => KeyCode::RightArrow,
TermKeyCode::Up => KeyCode::UpArrow,
TermKeyCode::Down => KeyCode::DownArrow,
TermKeyCode::Home => KeyCode::Home,
TermKeyCode::End => KeyCode::End,
TermKeyCode::PageUp => KeyCode::PageUp,
TermKeyCode::PageDown => KeyCode::PageDown,
TermKeyCode::Tab => KeyCode::Tab,
TermKeyCode::Delete => KeyCode::Delete,
TermKeyCode::Insert => KeyCode::Insert,
TermKeyCode::F(fn_num) => match fn_num {
1 => KeyCode::F1,
2 => KeyCode::F2,
3 => KeyCode::F3,
4 => KeyCode::F4,
5 => KeyCode::F5,
6 => KeyCode::F6,
7 => KeyCode::F7,
8 => KeyCode::F8,
9 => KeyCode::F9,
10 => KeyCode::F10,
11 => KeyCode::F11,
12 => KeyCode::F12,
_ => return None,
},
TermKeyCode::BackTab => return None,
TermKeyCode::Null => return None,
_ => return None,
};
key_str = if let KeyCode::BackSlash = code {
"\\".to_string()
} else {
format!("{code:?}")
}
};
// from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
Some(EventData::Keyboard(KeyboardData {
char_code: code.raw_code(),
2022-04-02 21:46:46 +00:00
key: key_str,
2022-03-10 03:06:45 +00:00
key_code: code,
alt_key: event.modifiers.contains(KeyModifiers::ALT),
ctrl_key: event.modifiers.contains(KeyModifiers::CONTROL),
meta_key: false,
shift_key: event.modifiers.contains(KeyModifiers::SHIFT),
locale: Default::default(),
location: 0x00,
repeat: Default::default(),
which: Default::default(),
}))
2022-02-05 22:28:19 +00:00
}