hooks.rs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. use crossterm::event::{
  2. Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
  3. };
  4. use dioxus_core::*;
  5. use fxhash::{FxHashMap, FxHashSet};
  6. use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D};
  7. use dioxus_html::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
  8. use dioxus_html::input_data::keyboard_types::Modifiers;
  9. use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
  10. use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
  11. use dioxus_html::{on::*, KeyCode};
  12. use std::{
  13. any::Any,
  14. cell::RefCell,
  15. rc::Rc,
  16. sync::Arc,
  17. time::{Duration, Instant},
  18. };
  19. use stretch2::geometry::{Point, Size};
  20. use stretch2::{prelude::Layout, Stretch};
  21. use crate::{Dom, Node};
  22. // a wrapper around the input state for easier access
  23. // todo: fix loop
  24. // pub struct InputState(Rc<Rc<RefCell<InnerInputState>>>);
  25. // impl InputState {
  26. // pub fn get(cx: &ScopeState) -> InputState {
  27. // let inner = cx
  28. // .consume_context::<Rc<RefCell<InnerInputState>>>()
  29. // .expect("Rink InputState can only be used in Rink apps!");
  30. // (**inner).borrow_mut().subscribe(cx.schedule_update());
  31. // InputState(inner)
  32. // }
  33. // pub fn mouse(&self) -> Option<MouseData> {
  34. // let data = (**self.0).borrow();
  35. // data.mouse.as_ref().map(|m| m.clone())
  36. // }
  37. // pub fn wheel(&self) -> Option<WheelData> {
  38. // let data = (**self.0).borrow();
  39. // data.wheel.as_ref().map(|w| w.clone())
  40. // }
  41. // pub fn screen(&self) -> Option<(u16, u16)> {
  42. // let data = (**self.0).borrow();
  43. // data.screen.as_ref().map(|m| m.clone())
  44. // }
  45. // pub fn last_key_pressed(&self) -> Option<KeyboardData> {
  46. // let data = (**self.0).borrow();
  47. // data.last_key_pressed
  48. // .as_ref()
  49. // .map(|k| &k.0.clone())
  50. // }
  51. // }
  52. type EventCore = (&'static str, EventData);
  53. #[derive(Debug)]
  54. enum EventData {
  55. Mouse(MouseData),
  56. Wheel(WheelData),
  57. Screen((u16, u16)),
  58. Keyboard(KeyboardData),
  59. }
  60. impl EventData {
  61. fn into_any(self) -> Arc<dyn Any + Send + Sync> {
  62. match self {
  63. Self::Mouse(m) => Arc::new(m),
  64. Self::Wheel(w) => Arc::new(w),
  65. Self::Screen(s) => Arc::new(s),
  66. Self::Keyboard(k) => Arc::new(k),
  67. }
  68. }
  69. }
  70. const MAX_REPEAT_TIME: Duration = Duration::from_millis(100);
  71. pub struct InnerInputState {
  72. mouse: Option<MouseData>,
  73. wheel: Option<WheelData>,
  74. last_key_pressed: Option<(KeyboardData, Instant)>,
  75. screen: Option<(u16, u16)>,
  76. // subscribers: Vec<Rc<dyn Fn() + 'static>>,
  77. }
  78. impl InnerInputState {
  79. fn new() -> Self {
  80. Self {
  81. mouse: None,
  82. wheel: None,
  83. last_key_pressed: None,
  84. screen: None,
  85. // subscribers: Vec::new(),
  86. }
  87. }
  88. // stores current input state and transforms events based on that state
  89. fn apply_event(&mut self, evt: &mut EventCore) {
  90. match evt.1 {
  91. // limitations: only two buttons may be held at once
  92. EventData::Mouse(ref mut m) => {
  93. let mut held_buttons = match &self.mouse {
  94. Some(previous_data) => previous_data.held_buttons(),
  95. None => MouseButtonSet::empty(),
  96. };
  97. match evt.0 {
  98. "mousedown" => {
  99. held_buttons.insert(
  100. m.trigger_button()
  101. .expect("No trigger button for mousedown event"),
  102. );
  103. }
  104. "mouseup" => {
  105. held_buttons.remove(
  106. m.trigger_button()
  107. .expect("No trigger button for mouseup event"),
  108. );
  109. }
  110. _ => {}
  111. }
  112. let new_mouse_data = MouseData::new(
  113. m.coordinates(),
  114. m.trigger_button(),
  115. held_buttons,
  116. m.modifiers(),
  117. );
  118. self.mouse = Some(new_mouse_data.clone());
  119. *m = new_mouse_data;
  120. }
  121. EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
  122. EventData::Screen(ref s) => self.screen = Some(*s),
  123. EventData::Keyboard(ref mut k) => {
  124. let repeat = self
  125. .last_key_pressed
  126. .as_ref()
  127. .filter(|k2| k2.0.key == k.key && k2.1.elapsed() < MAX_REPEAT_TIME)
  128. .is_some();
  129. k.repeat = repeat;
  130. let new = k.clone();
  131. self.last_key_pressed = Some((new, Instant::now()));
  132. }
  133. }
  134. }
  135. fn update(
  136. &mut self,
  137. evts: &mut [EventCore],
  138. resolved_events: &mut Vec<UserEvent>,
  139. layout: &Stretch,
  140. dom: &mut Dom,
  141. ) {
  142. let previous_mouse = self.mouse.clone();
  143. self.wheel = None;
  144. for e in evts.iter_mut() {
  145. self.apply_event(e);
  146. }
  147. self.resolve_mouse_events(previous_mouse, resolved_events, layout, dom);
  148. // for s in &self.subscribers {
  149. // s();
  150. // }
  151. }
  152. fn resolve_mouse_events(
  153. &self,
  154. previous_mouse: Option<MouseData>,
  155. resolved_events: &mut Vec<UserEvent>,
  156. layout: &Stretch,
  157. dom: &mut Dom,
  158. ) {
  159. fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
  160. let Point { x, y } = layout.location;
  161. let Size { width, height } = layout.size;
  162. let layout_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height));
  163. layout_rect.contains(point.cast())
  164. }
  165. fn try_create_event(
  166. name: &'static str,
  167. data: Arc<dyn Any + Send + Sync>,
  168. will_bubble: &mut FxHashSet<ElementId>,
  169. resolved_events: &mut Vec<UserEvent>,
  170. node: &Node,
  171. dom: &Dom,
  172. ) {
  173. // only trigger event if the event was not triggered already by a child
  174. if will_bubble.insert(node.id) {
  175. let mut parent = node.parent;
  176. while let Some(parent_id) = parent {
  177. will_bubble.insert(parent_id);
  178. parent = dom[parent_id.0].parent;
  179. }
  180. resolved_events.push(UserEvent {
  181. scope_id: None,
  182. priority: EventPriority::Medium,
  183. name,
  184. element: Some(node.id),
  185. data,
  186. })
  187. }
  188. }
  189. fn prepare_mouse_data(mouse_data: &MouseData, layout: &Layout) -> MouseData {
  190. let Point { x, y } = layout.location;
  191. let node_origin = ClientPoint::new(x.into(), y.into());
  192. let new_client_coordinates = (mouse_data.client_coordinates() - node_origin)
  193. .to_point()
  194. .cast_unit();
  195. let coordinates = Coordinates::new(
  196. mouse_data.screen_coordinates(),
  197. mouse_data.client_coordinates(),
  198. new_client_coordinates,
  199. mouse_data.page_coordinates(),
  200. );
  201. MouseData::new(
  202. coordinates,
  203. mouse_data.trigger_button(),
  204. mouse_data.held_buttons(),
  205. mouse_data.modifiers(),
  206. )
  207. }
  208. if let Some(mouse_data) = &self.mouse {
  209. let new_pos = mouse_data.screen_coordinates();
  210. let old_pos = previous_mouse.as_ref().map(|m| m.screen_coordinates());
  211. // a mouse button is pressed if a button was not down and is now down
  212. let previous_buttons = previous_mouse
  213. .map_or(MouseButtonSet::empty(), |previous_data| {
  214. previous_data.held_buttons()
  215. });
  216. let was_pressed = !(mouse_data.held_buttons() - previous_buttons).is_empty();
  217. // a mouse button is released if a button was down and is now not down
  218. let was_released = !(previous_buttons - mouse_data.held_buttons()).is_empty();
  219. let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y);
  220. let wheel_data = &self.wheel;
  221. {
  222. // mousemove
  223. if old_pos != Some(new_pos) {
  224. let mut will_bubble = FxHashSet::default();
  225. for node in dom.get_listening_sorted("mousemove") {
  226. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  227. let previously_contained = old_pos
  228. .filter(|pos| layout_contains_point(node_layout, *pos))
  229. .is_some();
  230. let currently_contains = layout_contains_point(node_layout, new_pos);
  231. if currently_contains && previously_contained {
  232. try_create_event(
  233. "mousemove",
  234. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  235. &mut will_bubble,
  236. resolved_events,
  237. node,
  238. dom,
  239. );
  240. }
  241. }
  242. }
  243. }
  244. {
  245. // mouseenter
  246. let mut will_bubble = FxHashSet::default();
  247. for node in dom.get_listening_sorted("mouseenter") {
  248. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  249. let previously_contained = old_pos
  250. .filter(|pos| layout_contains_point(node_layout, *pos))
  251. .is_some();
  252. let currently_contains = layout_contains_point(node_layout, new_pos);
  253. if currently_contains && !previously_contained {
  254. try_create_event(
  255. "mouseenter",
  256. Arc::new(mouse_data.clone()),
  257. &mut will_bubble,
  258. resolved_events,
  259. node,
  260. dom,
  261. );
  262. }
  263. }
  264. }
  265. {
  266. // mouseover
  267. let mut will_bubble = FxHashSet::default();
  268. for node in dom.get_listening_sorted("mouseover") {
  269. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  270. let previously_contained = old_pos
  271. .filter(|pos| layout_contains_point(node_layout, *pos))
  272. .is_some();
  273. let currently_contains = layout_contains_point(node_layout, new_pos);
  274. if currently_contains && !previously_contained {
  275. try_create_event(
  276. "mouseover",
  277. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  278. &mut will_bubble,
  279. resolved_events,
  280. node,
  281. dom,
  282. );
  283. }
  284. }
  285. }
  286. // mousedown
  287. if was_pressed {
  288. let mut will_bubble = FxHashSet::default();
  289. for node in dom.get_listening_sorted("mousedown") {
  290. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  291. let currently_contains = layout_contains_point(node_layout, new_pos);
  292. if currently_contains {
  293. try_create_event(
  294. "mousedown",
  295. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  296. &mut will_bubble,
  297. resolved_events,
  298. node,
  299. dom,
  300. );
  301. }
  302. }
  303. }
  304. {
  305. // mouseup
  306. if was_released {
  307. let mut will_bubble = FxHashSet::default();
  308. for node in dom.get_listening_sorted("mouseup") {
  309. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  310. let currently_contains = layout_contains_point(node_layout, new_pos);
  311. if currently_contains {
  312. try_create_event(
  313. "mouseup",
  314. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  315. &mut will_bubble,
  316. resolved_events,
  317. node,
  318. dom,
  319. );
  320. }
  321. }
  322. }
  323. }
  324. {
  325. // click
  326. if mouse_data.trigger_button() == Some(DioxusMouseButton::Primary) && was_released {
  327. let mut will_bubble = FxHashSet::default();
  328. for node in dom.get_listening_sorted("click") {
  329. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  330. let currently_contains = layout_contains_point(node_layout, new_pos);
  331. if currently_contains {
  332. try_create_event(
  333. "click",
  334. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  335. &mut will_bubble,
  336. resolved_events,
  337. node,
  338. dom,
  339. );
  340. }
  341. }
  342. }
  343. }
  344. {
  345. // contextmenu
  346. if mouse_data.trigger_button() == Some(DioxusMouseButton::Secondary) && was_released
  347. {
  348. let mut will_bubble = FxHashSet::default();
  349. for node in dom.get_listening_sorted("contextmenu") {
  350. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  351. let currently_contains = layout_contains_point(node_layout, new_pos);
  352. if currently_contains {
  353. try_create_event(
  354. "contextmenu",
  355. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  356. &mut will_bubble,
  357. resolved_events,
  358. node,
  359. dom,
  360. );
  361. }
  362. }
  363. }
  364. }
  365. {
  366. // wheel
  367. if let Some(w) = wheel_data {
  368. if wheel_delta != 0.0 {
  369. let mut will_bubble = FxHashSet::default();
  370. for node in dom.get_listening_sorted("wheel") {
  371. let node_layout =
  372. layout.layout(node.state.layout.node.unwrap()).unwrap();
  373. let currently_contains = layout_contains_point(node_layout, new_pos);
  374. if currently_contains {
  375. try_create_event(
  376. "wheel",
  377. Arc::new(w.clone()),
  378. &mut will_bubble,
  379. resolved_events,
  380. node,
  381. dom,
  382. );
  383. }
  384. }
  385. }
  386. }
  387. }
  388. {
  389. // mouseleave
  390. let mut will_bubble = FxHashSet::default();
  391. for node in dom.get_listening_sorted("mouseleave") {
  392. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  393. let previously_contained = old_pos
  394. .filter(|pos| layout_contains_point(node_layout, *pos))
  395. .is_some();
  396. let currently_contains = layout_contains_point(node_layout, new_pos);
  397. if !currently_contains && previously_contained {
  398. try_create_event(
  399. "mouseleave",
  400. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  401. &mut will_bubble,
  402. resolved_events,
  403. node,
  404. dom,
  405. );
  406. }
  407. }
  408. }
  409. {
  410. // mouseout
  411. let mut will_bubble = FxHashSet::default();
  412. for node in dom.get_listening_sorted("mouseout") {
  413. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  414. let previously_contained = old_pos
  415. .filter(|pos| layout_contains_point(node_layout, *pos))
  416. .is_some();
  417. let currently_contains = layout_contains_point(node_layout, new_pos);
  418. if !currently_contains && previously_contained {
  419. try_create_event(
  420. "mouseout",
  421. Arc::new(prepare_mouse_data(mouse_data, node_layout)),
  422. &mut will_bubble,
  423. resolved_events,
  424. node,
  425. dom,
  426. );
  427. }
  428. }
  429. }
  430. }
  431. }
  432. // fn subscribe(&mut self, f: Rc<dyn Fn() + 'static>) {
  433. // self.subscribers.push(f)
  434. // }
  435. }
  436. pub struct RinkInputHandler {
  437. state: Rc<RefCell<InnerInputState>>,
  438. queued_events: Rc<RefCell<Vec<EventCore>>>,
  439. }
  440. impl RinkInputHandler {
  441. /// global context that handles events
  442. /// limitations: GUI key modifier is never detected, key up events are not detected, and only two mouse buttons may be pressed at once
  443. pub fn new() -> (
  444. Self,
  445. Rc<RefCell<InnerInputState>>,
  446. impl FnMut(crossterm::event::Event),
  447. ) {
  448. let queued_events = Rc::new(RefCell::new(Vec::new()));
  449. let queued_events2 = Rc::downgrade(&queued_events);
  450. let regester_event = move |evt: crossterm::event::Event| {
  451. if let Some(evt) = get_event(evt) {
  452. if let Some(v) = queued_events2.upgrade() {
  453. (*v).borrow_mut().push(evt);
  454. }
  455. }
  456. };
  457. let state = Rc::new(RefCell::new(InnerInputState::new()));
  458. (
  459. Self {
  460. state: state.clone(),
  461. queued_events,
  462. },
  463. state,
  464. regester_event,
  465. )
  466. }
  467. pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> Vec<UserEvent> {
  468. let mut resolved_events = Vec::new();
  469. (*self.state).borrow_mut().update(
  470. &mut (*self.queued_events).borrow_mut(),
  471. &mut resolved_events,
  472. layout,
  473. dom,
  474. );
  475. let events = self
  476. .queued_events
  477. .replace(Vec::new())
  478. .into_iter()
  479. // these events were added in the update stage
  480. .filter(|e| {
  481. ![
  482. "mouseenter",
  483. "mouseover",
  484. "mouseleave",
  485. "mouseout",
  486. "mousedown",
  487. "mouseup",
  488. "mousemove",
  489. "drag",
  490. "wheel",
  491. "click",
  492. "contextmenu",
  493. ]
  494. .contains(&e.0)
  495. })
  496. .map(|evt| (evt.0, evt.1.into_any()));
  497. // todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
  498. let mut hm: FxHashMap<&'static str, Vec<Arc<dyn Any + Send + Sync>>> = FxHashMap::default();
  499. for (event, data) in events {
  500. if let Some(v) = hm.get_mut(event) {
  501. v.push(data);
  502. } else {
  503. hm.insert(event, vec![data]);
  504. }
  505. }
  506. for (event, datas) in hm {
  507. for node in dom.get_listening_sorted(event) {
  508. for data in &datas {
  509. resolved_events.push(UserEvent {
  510. scope_id: None,
  511. priority: EventPriority::Medium,
  512. name: event,
  513. element: Some(node.id),
  514. data: data.clone(),
  515. });
  516. }
  517. }
  518. }
  519. resolved_events
  520. }
  521. }
  522. // translate crossterm events into dioxus events
  523. fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
  524. let (name, data): (&str, EventData) = match evt {
  525. TermEvent::Key(k) => ("keydown", translate_key_event(k)?),
  526. TermEvent::Mouse(m) => {
  527. let (x, y) = (m.column.into(), m.row.into());
  528. let alt = m.modifiers.contains(KeyModifiers::ALT);
  529. let shift = m.modifiers.contains(KeyModifiers::SHIFT);
  530. let ctrl = m.modifiers.contains(KeyModifiers::CONTROL);
  531. let meta = false;
  532. let get_mouse_data = |crossterm_button: Option<MouseButton>| {
  533. let button = crossterm_button.map(|b| match b {
  534. MouseButton::Left => DioxusMouseButton::Primary,
  535. MouseButton::Right => DioxusMouseButton::Secondary,
  536. MouseButton::Middle => DioxusMouseButton::Auxiliary,
  537. });
  538. // from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
  539. // 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.
  540. // todo?
  541. // 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.
  542. let coordinates = Coordinates::new(
  543. ScreenPoint::new(x, y),
  544. ClientPoint::new(x, y),
  545. // offset x/y are set when the origin of the event is assigned to an element
  546. ElementPoint::new(0., 0.),
  547. PagePoint::new(x, y),
  548. );
  549. let mut modifiers = Modifiers::empty();
  550. if shift {
  551. modifiers.insert(Modifiers::SHIFT);
  552. }
  553. if ctrl {
  554. modifiers.insert(Modifiers::CONTROL);
  555. }
  556. if meta {
  557. modifiers.insert(Modifiers::META);
  558. }
  559. if alt {
  560. modifiers.insert(Modifiers::ALT);
  561. }
  562. // held mouse buttons get set later by maintaining state, as crossterm does not provide them
  563. EventData::Mouse(MouseData::new(
  564. coordinates,
  565. button,
  566. DioxusMouseButtons::empty(),
  567. modifiers,
  568. ))
  569. };
  570. let get_wheel_data = |up| {
  571. // from https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
  572. EventData::Wheel(WheelData {
  573. delta_mode: 0x01,
  574. delta_x: 0.0,
  575. delta_y: if up { -1.0 } else { 1.0 },
  576. delta_z: 0.0,
  577. })
  578. };
  579. match m.kind {
  580. MouseEventKind::Down(b) => ("mousedown", get_mouse_data(Some(b))),
  581. MouseEventKind::Up(b) => ("mouseup", get_mouse_data(Some(b))),
  582. MouseEventKind::Drag(b) => ("drag", get_mouse_data(Some(b))),
  583. MouseEventKind::Moved => ("mousemove", get_mouse_data(None)),
  584. MouseEventKind::ScrollDown => ("wheel", get_wheel_data(false)),
  585. MouseEventKind::ScrollUp => ("wheel", get_wheel_data(true)),
  586. }
  587. }
  588. TermEvent::Resize(x, y) => ("resize", EventData::Screen((x, y))),
  589. };
  590. Some((name, data))
  591. }
  592. fn translate_key_event(event: crossterm::event::KeyEvent) -> Option<EventData> {
  593. let (code, key_str);
  594. if let TermKeyCode::Char(c) = event.code {
  595. code = match c {
  596. 'A'..='Z' | 'a'..='z' => match c.to_ascii_uppercase() {
  597. 'A' => KeyCode::A,
  598. 'B' => KeyCode::B,
  599. 'C' => KeyCode::C,
  600. 'D' => KeyCode::D,
  601. 'E' => KeyCode::E,
  602. 'F' => KeyCode::F,
  603. 'G' => KeyCode::G,
  604. 'H' => KeyCode::H,
  605. 'I' => KeyCode::I,
  606. 'J' => KeyCode::J,
  607. 'K' => KeyCode::K,
  608. 'L' => KeyCode::L,
  609. 'M' => KeyCode::M,
  610. 'N' => KeyCode::N,
  611. 'O' => KeyCode::O,
  612. 'P' => KeyCode::P,
  613. 'Q' => KeyCode::Q,
  614. 'R' => KeyCode::R,
  615. 'S' => KeyCode::S,
  616. 'T' => KeyCode::T,
  617. 'U' => KeyCode::U,
  618. 'V' => KeyCode::V,
  619. 'W' => KeyCode::W,
  620. 'X' => KeyCode::X,
  621. 'Y' => KeyCode::Y,
  622. 'Z' => KeyCode::Z,
  623. _ => return None,
  624. },
  625. ' ' => KeyCode::Space,
  626. '[' => KeyCode::OpenBracket,
  627. '{' => KeyCode::OpenBracket,
  628. ']' => KeyCode::CloseBraket,
  629. '}' => KeyCode::CloseBraket,
  630. ';' => KeyCode::Semicolon,
  631. ':' => KeyCode::Semicolon,
  632. ',' => KeyCode::Comma,
  633. '<' => KeyCode::Comma,
  634. '.' => KeyCode::Period,
  635. '>' => KeyCode::Period,
  636. '1' => KeyCode::Num1,
  637. '2' => KeyCode::Num2,
  638. '3' => KeyCode::Num3,
  639. '4' => KeyCode::Num4,
  640. '5' => KeyCode::Num5,
  641. '6' => KeyCode::Num6,
  642. '7' => KeyCode::Num7,
  643. '8' => KeyCode::Num8,
  644. '9' => KeyCode::Num9,
  645. '0' => KeyCode::Num0,
  646. '!' => KeyCode::Num1,
  647. '@' => KeyCode::Num2,
  648. '#' => KeyCode::Num3,
  649. '$' => KeyCode::Num4,
  650. '%' => KeyCode::Num5,
  651. '^' => KeyCode::Num6,
  652. '&' => KeyCode::Num7,
  653. '*' => KeyCode::Num8,
  654. '(' => KeyCode::Num9,
  655. ')' => KeyCode::Num0,
  656. // numpad charicter are ambiguous to tui
  657. // '*' => KeyCode::Multiply,
  658. // '/' => KeyCode::Divide,
  659. // '-' => KeyCode::Subtract,
  660. // '+' => KeyCode::Add,
  661. '+' => KeyCode::EqualSign,
  662. '-' => KeyCode::Dash,
  663. '_' => KeyCode::Dash,
  664. '\'' => KeyCode::SingleQuote,
  665. '"' => KeyCode::SingleQuote,
  666. '\\' => KeyCode::BackSlash,
  667. '|' => KeyCode::BackSlash,
  668. '/' => KeyCode::ForwardSlash,
  669. '?' => KeyCode::ForwardSlash,
  670. '=' => KeyCode::EqualSign,
  671. '`' => KeyCode::GraveAccent,
  672. '~' => KeyCode::GraveAccent,
  673. _ => return None,
  674. };
  675. key_str = c.to_string();
  676. } else {
  677. code = match event.code {
  678. TermKeyCode::Esc => KeyCode::Escape,
  679. TermKeyCode::Backspace => KeyCode::Backspace,
  680. TermKeyCode::Enter => KeyCode::Enter,
  681. TermKeyCode::Left => KeyCode::LeftArrow,
  682. TermKeyCode::Right => KeyCode::RightArrow,
  683. TermKeyCode::Up => KeyCode::UpArrow,
  684. TermKeyCode::Down => KeyCode::DownArrow,
  685. TermKeyCode::Home => KeyCode::Home,
  686. TermKeyCode::End => KeyCode::End,
  687. TermKeyCode::PageUp => KeyCode::PageUp,
  688. TermKeyCode::PageDown => KeyCode::PageDown,
  689. TermKeyCode::Tab => KeyCode::Tab,
  690. TermKeyCode::Delete => KeyCode::Delete,
  691. TermKeyCode::Insert => KeyCode::Insert,
  692. TermKeyCode::F(fn_num) => match fn_num {
  693. 1 => KeyCode::F1,
  694. 2 => KeyCode::F2,
  695. 3 => KeyCode::F3,
  696. 4 => KeyCode::F4,
  697. 5 => KeyCode::F5,
  698. 6 => KeyCode::F6,
  699. 7 => KeyCode::F7,
  700. 8 => KeyCode::F8,
  701. 9 => KeyCode::F9,
  702. 10 => KeyCode::F10,
  703. 11 => KeyCode::F11,
  704. 12 => KeyCode::F12,
  705. _ => return None,
  706. },
  707. TermKeyCode::BackTab => return None,
  708. TermKeyCode::Null => return None,
  709. _ => return None,
  710. };
  711. key_str = if let KeyCode::BackSlash = code {
  712. "\\".to_string()
  713. } else {
  714. format!("{code:?}")
  715. }
  716. };
  717. // from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
  718. Some(EventData::Keyboard(KeyboardData {
  719. char_code: code.raw_code(),
  720. key: key_str,
  721. key_code: code,
  722. alt_key: event.modifiers.contains(KeyModifiers::ALT),
  723. ctrl_key: event.modifiers.contains(KeyModifiers::CONTROL),
  724. meta_key: false,
  725. shift_key: event.modifiers.contains(KeyModifiers::SHIFT),
  726. locale: Default::default(),
  727. location: 0x00,
  728. repeat: Default::default(),
  729. which: Default::default(),
  730. }))
  731. }