1
0

hooks.rs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. use crossterm::event::{
  2. Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
  3. };
  4. use dioxus_core::*;
  5. use dioxus_native_core::tree::TreeView;
  6. use dioxus_native_core::NodeId;
  7. use rustc_hash::{FxHashMap, FxHashSet};
  8. use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D};
  9. use dioxus_html::geometry::{
  10. ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta,
  11. };
  12. use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
  13. use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
  14. use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
  15. use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData};
  16. use std::{
  17. any::Any,
  18. cell::{RefCell, RefMut},
  19. rc::Rc,
  20. time::{Duration, Instant},
  21. };
  22. use taffy::geometry::{Point, Size};
  23. use taffy::{prelude::Layout, Taffy};
  24. use crate::{layout_to_screen_space, FocusState};
  25. use crate::{TuiDom, TuiNode};
  26. pub(crate) struct Event {
  27. pub id: ElementId,
  28. pub name: &'static str,
  29. pub data: Rc<dyn Any>,
  30. pub bubbles: bool,
  31. }
  32. // a wrapper around the input state for easier access
  33. // todo: fix loop
  34. // pub struct InputState(Rc<Rc<RefCell<InnerInputState>>>);
  35. // impl InputState {
  36. // pub fn get(cx: &ScopeState) -> InputState {
  37. // let inner = cx
  38. // .consume_context::<Rc<RefCell<InnerInputState>>>()
  39. // .expect("Rink InputState can only be used in Rink apps!");
  40. // (**inner).borrow_mut().subscribe(cx.schedule_update());
  41. // InputState(inner)
  42. // }
  43. // pub fn mouse(&self) -> Option<MouseData> {
  44. // let data = (**self.0).borrow();
  45. // mouse.as_ref().map(|m| m.clone())
  46. // }
  47. // pub fn wheel(&self) -> Option<WheelData> {
  48. // let data = (**self.0).borrow();
  49. // wheel.as_ref().map(|w| w.clone())
  50. // }
  51. // pub fn screen(&self) -> Option<(u16, u16)> {
  52. // let data = (**self.0).borrow();
  53. // screen.as_ref().map(|m| m.clone())
  54. // }
  55. // pub fn last_key_pressed(&self) -> Option<KeyboardData> {
  56. // let data = (**self.0).borrow();
  57. // last_key_pressed
  58. // .as_ref()
  59. // .map(|k| &k.0.clone())
  60. // }
  61. // }
  62. type EventCore = (&'static str, EventData);
  63. #[derive(Debug)]
  64. enum EventData {
  65. Mouse(MouseData),
  66. Wheel(WheelData),
  67. Screen((u16, u16)),
  68. Keyboard(KeyboardData),
  69. }
  70. impl EventData {
  71. fn into_any(self) -> Rc<dyn Any + Send + Sync> {
  72. match self {
  73. Self::Mouse(m) => Rc::new(m),
  74. Self::Wheel(w) => Rc::new(w),
  75. Self::Screen(s) => Rc::new(s),
  76. Self::Keyboard(k) => Rc::new(k),
  77. }
  78. }
  79. }
  80. const MAX_REPEAT_TIME: Duration = Duration::from_millis(100);
  81. pub struct InnerInputState {
  82. mouse: Option<MouseData>,
  83. wheel: Option<WheelData>,
  84. last_key_pressed: Option<(KeyboardData, Instant)>,
  85. screen: Option<(u16, u16)>,
  86. pub(crate) focus_state: FocusState,
  87. // subscribers: Vec<Rc<dyn Fn() + 'static>>,
  88. }
  89. impl InnerInputState {
  90. fn new() -> Self {
  91. Self {
  92. mouse: None,
  93. wheel: None,
  94. last_key_pressed: None,
  95. screen: None,
  96. // subscribers: Vec::new(),
  97. focus_state: FocusState::default(),
  98. }
  99. }
  100. // stores current input state and transforms events based on that state
  101. fn apply_event(&mut self, evt: &mut EventCore) {
  102. match evt.1 {
  103. // limitations: only two buttons may be held at once
  104. EventData::Mouse(ref mut m) => {
  105. let mut held_buttons = match &self.mouse {
  106. Some(previous_data) => previous_data.held_buttons(),
  107. None => MouseButtonSet::empty(),
  108. };
  109. match evt.0 {
  110. "mousedown" => {
  111. held_buttons.insert(
  112. m.trigger_button()
  113. .expect("No trigger button for mousedown event"),
  114. );
  115. }
  116. "mouseup" => {
  117. held_buttons.remove(
  118. m.trigger_button()
  119. .expect("No trigger button for mouseup event"),
  120. );
  121. }
  122. _ => {}
  123. }
  124. let new_mouse_data = MouseData::new(
  125. m.coordinates(),
  126. m.trigger_button(),
  127. held_buttons,
  128. m.modifiers(),
  129. );
  130. self.mouse = Some(new_mouse_data.clone());
  131. *m = new_mouse_data;
  132. }
  133. EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
  134. EventData::Screen(ref s) => self.screen = Some(*s),
  135. EventData::Keyboard(ref mut k) => {
  136. let is_repeating = self
  137. .last_key_pressed
  138. .as_ref()
  139. // heuristic for guessing which presses are auto-repeating. not necessarily accurate
  140. .filter(|(last_data, last_instant)| {
  141. last_data.key() == k.key() && last_instant.elapsed() < MAX_REPEAT_TIME
  142. })
  143. .is_some();
  144. if is_repeating {
  145. *k = KeyboardData::new(k.key(), k.code(), k.location(), true, k.modifiers());
  146. }
  147. self.last_key_pressed = Some((k.clone(), Instant::now()));
  148. }
  149. }
  150. }
  151. fn update(
  152. &mut self,
  153. evts: &mut Vec<EventCore>,
  154. resolved_events: &mut Vec<Event>,
  155. layout: &Taffy,
  156. dom: &mut TuiDom,
  157. ) {
  158. let previous_mouse = self.mouse.clone();
  159. self.wheel = None;
  160. let old_focus = self.focus_state.last_focused_id;
  161. evts.retain(|e| match &e.1 {
  162. EventData::Keyboard(k) => match k.code() {
  163. Code::Tab => !self
  164. .focus_state
  165. .progress(dom, !k.modifiers().contains(Modifiers::SHIFT)),
  166. _ => true,
  167. },
  168. _ => true,
  169. });
  170. for e in evts.iter_mut() {
  171. self.apply_event(e);
  172. }
  173. self.resolve_mouse_events(previous_mouse, resolved_events, layout, dom);
  174. if old_focus != self.focus_state.last_focused_id {
  175. // elements with listeners will always have a element id
  176. if let Some(id) = self.focus_state.last_focused_id {
  177. let element = dom.tree.get(id).unwrap();
  178. if let Some(id) = element.node_data.element_id {
  179. resolved_events.push(Event {
  180. name: "focus",
  181. id,
  182. data: Rc::new(FocusData {}),
  183. bubbles: event_bubbles("focus"),
  184. });
  185. resolved_events.push(Event {
  186. name: "focusin",
  187. id,
  188. data: Rc::new(FocusData {}),
  189. bubbles: event_bubbles("focusin"),
  190. });
  191. }
  192. }
  193. if let Some(id) = old_focus {
  194. let element = dom.tree.get(id).unwrap();
  195. if let Some(id) = element.node_data.element_id {
  196. resolved_events.push(Event {
  197. name: "focusout",
  198. id,
  199. data: Rc::new(FocusData {}),
  200. bubbles: event_bubbles("focusout"),
  201. });
  202. }
  203. }
  204. }
  205. // for s in &self.subscribers {
  206. // s();
  207. // }
  208. }
  209. fn resolve_mouse_events(
  210. &mut self,
  211. previous_mouse: Option<MouseData>,
  212. resolved_events: &mut Vec<Event>,
  213. layout: &Taffy,
  214. dom: &mut TuiDom,
  215. ) {
  216. fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
  217. let Point { x, y } = layout.location;
  218. let (x, y) = (
  219. layout_to_screen_space(x).round(),
  220. layout_to_screen_space(y).round(),
  221. );
  222. let Size { width, height } = layout.size;
  223. let (width, height) = (
  224. layout_to_screen_space(width).round(),
  225. layout_to_screen_space(height).round(),
  226. );
  227. let layout_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height));
  228. layout_rect.contains(point.cast())
  229. }
  230. fn try_create_event(
  231. name: &'static str,
  232. data: Rc<dyn Any>,
  233. will_bubble: &mut FxHashSet<NodeId>,
  234. resolved_events: &mut Vec<Event>,
  235. node: &TuiNode,
  236. dom: &TuiDom,
  237. ) {
  238. // only trigger event if the event was not triggered already by a child
  239. let id = node.node_data.node_id;
  240. if will_bubble.insert(id) {
  241. let mut parent = dom.parent(id);
  242. while let Some(current_parent) = parent {
  243. let parent_id = current_parent.node_data.node_id;
  244. will_bubble.insert(parent_id);
  245. parent = dom.parent(parent_id);
  246. }
  247. if let Some(id) = node.mounted_id() {
  248. resolved_events.push(Event {
  249. name,
  250. id,
  251. data,
  252. bubbles: event_bubbles(name),
  253. })
  254. }
  255. }
  256. }
  257. fn prepare_mouse_data(mouse_data: &MouseData, layout: &Layout) -> MouseData {
  258. let Point { x, y } = layout.location;
  259. let node_origin = ClientPoint::new(
  260. layout_to_screen_space(x).into(),
  261. layout_to_screen_space(y).into(),
  262. );
  263. let new_client_coordinates = (mouse_data.client_coordinates() - node_origin)
  264. .to_point()
  265. .cast_unit();
  266. let coordinates = Coordinates::new(
  267. mouse_data.screen_coordinates(),
  268. mouse_data.client_coordinates(),
  269. new_client_coordinates,
  270. mouse_data.page_coordinates(),
  271. );
  272. MouseData::new(
  273. coordinates,
  274. mouse_data.trigger_button(),
  275. mouse_data.held_buttons(),
  276. mouse_data.modifiers(),
  277. )
  278. }
  279. if let Some(mouse_data) = &self.mouse {
  280. let new_pos = mouse_data.screen_coordinates();
  281. let old_pos = previous_mouse.as_ref().map(|m| m.screen_coordinates());
  282. // a mouse button is pressed if a button was not down and is now down
  283. let previous_buttons = previous_mouse
  284. .map_or(MouseButtonSet::empty(), |previous_data| {
  285. previous_data.held_buttons()
  286. });
  287. let was_pressed = !(mouse_data.held_buttons() - previous_buttons).is_empty();
  288. // a mouse button is released if a button was down and is now not down
  289. let was_released = !(previous_buttons - mouse_data.held_buttons()).is_empty();
  290. let was_scrolled = self
  291. .wheel
  292. .as_ref()
  293. .map_or(false, |data| !data.delta().is_zero());
  294. let wheel_data = &self.wheel;
  295. {
  296. // mousemove
  297. if old_pos != Some(new_pos) {
  298. let mut will_bubble = FxHashSet::default();
  299. for node in dom.get_listening_sorted("mousemove") {
  300. let node_layout = get_abs_layout(node, dom, layout);
  301. let previously_contained = old_pos
  302. .filter(|pos| layout_contains_point(&node_layout, *pos))
  303. .is_some();
  304. let currently_contains = layout_contains_point(&node_layout, new_pos);
  305. if currently_contains && previously_contained {
  306. try_create_event(
  307. "mousemove",
  308. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  309. &mut will_bubble,
  310. resolved_events,
  311. node,
  312. dom,
  313. );
  314. }
  315. }
  316. }
  317. }
  318. {
  319. // mouseenter
  320. let mut will_bubble = FxHashSet::default();
  321. for node in dom.get_listening_sorted("mouseenter") {
  322. let node_layout = get_abs_layout(node, dom, layout);
  323. let previously_contained = old_pos
  324. .filter(|pos| layout_contains_point(&node_layout, *pos))
  325. .is_some();
  326. let currently_contains = layout_contains_point(&node_layout, new_pos);
  327. if currently_contains && !previously_contained {
  328. try_create_event(
  329. "mouseenter",
  330. Rc::new(mouse_data.clone()),
  331. &mut will_bubble,
  332. resolved_events,
  333. node,
  334. dom,
  335. );
  336. }
  337. }
  338. }
  339. {
  340. // mouseover
  341. let mut will_bubble = FxHashSet::default();
  342. for node in dom.get_listening_sorted("mouseover") {
  343. let node_layout = get_abs_layout(node, dom, layout);
  344. let previously_contained = old_pos
  345. .filter(|pos| layout_contains_point(&node_layout, *pos))
  346. .is_some();
  347. let currently_contains = layout_contains_point(&node_layout, new_pos);
  348. if currently_contains && !previously_contained {
  349. try_create_event(
  350. "mouseover",
  351. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  352. &mut will_bubble,
  353. resolved_events,
  354. node,
  355. dom,
  356. );
  357. }
  358. }
  359. }
  360. // mousedown
  361. if was_pressed {
  362. let mut will_bubble = FxHashSet::default();
  363. for node in dom.get_listening_sorted("mousedown") {
  364. let node_layout = get_abs_layout(node, dom, layout);
  365. let currently_contains = layout_contains_point(&node_layout, new_pos);
  366. if currently_contains {
  367. try_create_event(
  368. "mousedown",
  369. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  370. &mut will_bubble,
  371. resolved_events,
  372. node,
  373. dom,
  374. );
  375. }
  376. }
  377. }
  378. {
  379. // mouseup
  380. if was_released {
  381. let mut will_bubble = FxHashSet::default();
  382. for node in dom.get_listening_sorted("mouseup") {
  383. let node_layout = get_abs_layout(node, dom, layout);
  384. let currently_contains = layout_contains_point(&node_layout, new_pos);
  385. if currently_contains {
  386. try_create_event(
  387. "mouseup",
  388. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  389. &mut will_bubble,
  390. resolved_events,
  391. node,
  392. dom,
  393. );
  394. }
  395. }
  396. }
  397. }
  398. {
  399. // click
  400. if mouse_data.trigger_button() == Some(DioxusMouseButton::Primary) && was_released {
  401. let mut will_bubble = FxHashSet::default();
  402. for node in dom.get_listening_sorted("click") {
  403. let node_layout = get_abs_layout(node, dom, layout);
  404. let currently_contains = layout_contains_point(&node_layout, new_pos);
  405. if currently_contains {
  406. try_create_event(
  407. "click",
  408. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  409. &mut will_bubble,
  410. resolved_events,
  411. node,
  412. dom,
  413. );
  414. }
  415. }
  416. }
  417. }
  418. {
  419. // contextmenu
  420. if mouse_data.trigger_button() == Some(DioxusMouseButton::Secondary) && was_released
  421. {
  422. let mut will_bubble = FxHashSet::default();
  423. for node in dom.get_listening_sorted("contextmenu") {
  424. let node_layout = get_abs_layout(node, dom, layout);
  425. let currently_contains = layout_contains_point(&node_layout, new_pos);
  426. if currently_contains {
  427. try_create_event(
  428. "contextmenu",
  429. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  430. &mut will_bubble,
  431. resolved_events,
  432. node,
  433. dom,
  434. );
  435. }
  436. }
  437. }
  438. }
  439. {
  440. // wheel
  441. if let Some(w) = wheel_data {
  442. if was_scrolled {
  443. let mut will_bubble = FxHashSet::default();
  444. for node in dom.get_listening_sorted("wheel") {
  445. let node_layout = get_abs_layout(node, dom, layout);
  446. let currently_contains = layout_contains_point(&node_layout, new_pos);
  447. if currently_contains {
  448. try_create_event(
  449. "wheel",
  450. Rc::new(w.clone()),
  451. &mut will_bubble,
  452. resolved_events,
  453. node,
  454. dom,
  455. );
  456. }
  457. }
  458. }
  459. }
  460. }
  461. {
  462. // mouseleave
  463. let mut will_bubble = FxHashSet::default();
  464. for node in dom.get_listening_sorted("mouseleave") {
  465. let node_layout = get_abs_layout(node, dom, layout);
  466. let previously_contained = old_pos
  467. .filter(|pos| layout_contains_point(&node_layout, *pos))
  468. .is_some();
  469. let currently_contains = layout_contains_point(&node_layout, new_pos);
  470. if !currently_contains && previously_contained {
  471. try_create_event(
  472. "mouseleave",
  473. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  474. &mut will_bubble,
  475. resolved_events,
  476. node,
  477. dom,
  478. );
  479. }
  480. }
  481. }
  482. {
  483. // mouseout
  484. let mut will_bubble = FxHashSet::default();
  485. for node in dom.get_listening_sorted("mouseout") {
  486. let node_layout = get_abs_layout(node, dom, layout);
  487. let previously_contained = old_pos
  488. .filter(|pos| layout_contains_point(&node_layout, *pos))
  489. .is_some();
  490. let currently_contains = layout_contains_point(&node_layout, new_pos);
  491. if !currently_contains && previously_contained {
  492. try_create_event(
  493. "mouseout",
  494. Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
  495. &mut will_bubble,
  496. resolved_events,
  497. node,
  498. dom,
  499. );
  500. }
  501. }
  502. }
  503. // update focus
  504. if was_released {
  505. let mut focus_id = None;
  506. dom.traverse_depth_first(|node| {
  507. let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
  508. let currently_contains = layout_contains_point(node_layout, new_pos);
  509. if currently_contains && node.state.focus.level.focusable() {
  510. focus_id = Some(node.node_data.node_id);
  511. }
  512. });
  513. if let Some(id) = focus_id {
  514. self.focus_state.set_focus(dom, id);
  515. }
  516. }
  517. }
  518. }
  519. // fn subscribe(&mut self, f: Rc<dyn Fn() + 'static>) {
  520. // self.subscribers.push(f)
  521. // }
  522. }
  523. fn get_abs_layout(node: &TuiNode, dom: &TuiDom, taffy: &Taffy) -> Layout {
  524. let mut node_layout = *taffy.layout(node.state.layout.node.unwrap()).unwrap();
  525. let mut current = node;
  526. while let Some(parent) = dom.parent(current.node_data.node_id) {
  527. current = parent;
  528. let parent_layout = taffy.layout(parent.state.layout.node.unwrap()).unwrap();
  529. node_layout.location.x += parent_layout.location.x;
  530. node_layout.location.y += parent_layout.location.y;
  531. }
  532. node_layout
  533. }
  534. pub struct RinkInputHandler {
  535. state: Rc<RefCell<InnerInputState>>,
  536. queued_events: Rc<RefCell<Vec<EventCore>>>,
  537. }
  538. impl RinkInputHandler {
  539. /// global context that handles events
  540. /// limitations: GUI key modifier is never detected, key up events are not detected, and only two mouse buttons may be pressed at once
  541. pub fn new() -> (
  542. Self,
  543. Rc<RefCell<InnerInputState>>,
  544. impl FnMut(crossterm::event::Event),
  545. ) {
  546. let queued_events = Rc::new(RefCell::new(Vec::new()));
  547. let queued_events2 = Rc::downgrade(&queued_events);
  548. let regester_event = move |evt: crossterm::event::Event| {
  549. if let Some(evt) = get_event(evt) {
  550. if let Some(v) = queued_events2.upgrade() {
  551. (*v).borrow_mut().push(evt);
  552. }
  553. }
  554. };
  555. let state = Rc::new(RefCell::new(InnerInputState::new()));
  556. (
  557. Self {
  558. state: state.clone(),
  559. queued_events,
  560. },
  561. state,
  562. regester_event,
  563. )
  564. }
  565. pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &TuiDom) {
  566. self.state.borrow_mut().focus_state.prune(mutations, rdom);
  567. }
  568. pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut TuiDom) -> Vec<Event> {
  569. let mut resolved_events = Vec::new();
  570. (*self.state).borrow_mut().update(
  571. &mut (*self.queued_events).borrow_mut(),
  572. &mut resolved_events,
  573. layout,
  574. dom,
  575. );
  576. let events = self
  577. .queued_events
  578. .replace(Vec::new())
  579. .into_iter()
  580. // these events were added in the update stage
  581. .filter(|e| {
  582. ![
  583. "mouseenter",
  584. "mouseover",
  585. "mouseleave",
  586. "mouseout",
  587. "mousedown",
  588. "mouseup",
  589. "mousemove",
  590. "drag",
  591. "wheel",
  592. "click",
  593. "contextmenu",
  594. ]
  595. .contains(&e.0)
  596. })
  597. .map(|evt| (evt.0, evt.1.into_any()));
  598. let mut hm: FxHashMap<&'static str, Vec<Rc<dyn Any + Send + Sync>>> = FxHashMap::default();
  599. for (event, data) in events {
  600. if let Some(v) = hm.get_mut(event) {
  601. v.push(data);
  602. } else {
  603. hm.insert(event, vec![data]);
  604. }
  605. }
  606. for (event, datas) in hm {
  607. for node in dom.get_listening_sorted(event) {
  608. for data in &datas {
  609. if node.state.focused {
  610. if let Some(id) = node.mounted_id() {
  611. resolved_events.push(Event {
  612. name: event,
  613. id,
  614. data: data.clone(),
  615. bubbles: event_bubbles(event),
  616. });
  617. }
  618. }
  619. }
  620. }
  621. }
  622. resolved_events
  623. }
  624. pub(crate) fn state(&self) -> RefMut<InnerInputState> {
  625. self.state.borrow_mut()
  626. }
  627. }
  628. // translate crossterm events into dioxus events
  629. fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
  630. let (name, data): (&str, EventData) = match evt {
  631. TermEvent::Key(k) => ("keydown", translate_key_event(k)?),
  632. TermEvent::Mouse(m) => {
  633. let (x, y) = (m.column.into(), m.row.into());
  634. let alt = m.modifiers.contains(KeyModifiers::ALT);
  635. let shift = m.modifiers.contains(KeyModifiers::SHIFT);
  636. let ctrl = m.modifiers.contains(KeyModifiers::CONTROL);
  637. let meta = false;
  638. let get_mouse_data = |crossterm_button: Option<MouseButton>| {
  639. let button = crossterm_button.map(|b| match b {
  640. MouseButton::Left => DioxusMouseButton::Primary,
  641. MouseButton::Right => DioxusMouseButton::Secondary,
  642. MouseButton::Middle => DioxusMouseButton::Auxiliary,
  643. });
  644. // from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
  645. // 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.
  646. // todo?
  647. // 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.
  648. let coordinates = Coordinates::new(
  649. ScreenPoint::new(x, y),
  650. ClientPoint::new(x, y),
  651. // offset x/y are set when the origin of the event is assigned to an element
  652. ElementPoint::new(0., 0.),
  653. PagePoint::new(x, y),
  654. );
  655. let mut modifiers = Modifiers::empty();
  656. if shift {
  657. modifiers.insert(Modifiers::SHIFT);
  658. }
  659. if ctrl {
  660. modifiers.insert(Modifiers::CONTROL);
  661. }
  662. if meta {
  663. modifiers.insert(Modifiers::META);
  664. }
  665. if alt {
  666. modifiers.insert(Modifiers::ALT);
  667. }
  668. // held mouse buttons get set later by maintaining state, as crossterm does not provide them
  669. EventData::Mouse(MouseData::new(
  670. coordinates,
  671. button,
  672. DioxusMouseButtons::empty(),
  673. modifiers,
  674. ))
  675. };
  676. let get_wheel_data = |up| {
  677. let y = if up { -1.0 } else { 1.0 };
  678. EventData::Wheel(WheelData::new(WheelDelta::lines(0., y, 0.)))
  679. };
  680. match m.kind {
  681. MouseEventKind::Down(b) => ("mousedown", get_mouse_data(Some(b))),
  682. MouseEventKind::Up(b) => ("mouseup", get_mouse_data(Some(b))),
  683. MouseEventKind::Drag(b) => ("drag", get_mouse_data(Some(b))),
  684. MouseEventKind::Moved => ("mousemove", get_mouse_data(None)),
  685. MouseEventKind::ScrollDown => ("wheel", get_wheel_data(false)),
  686. MouseEventKind::ScrollUp => ("wheel", get_wheel_data(true)),
  687. }
  688. }
  689. TermEvent::Resize(x, y) => ("resize", EventData::Screen((x, y))),
  690. };
  691. Some((name, data))
  692. }
  693. fn translate_key_event(event: crossterm::event::KeyEvent) -> Option<EventData> {
  694. let key = key_from_crossterm_key_code(event.code);
  695. // crossterm does not provide code. we make a guess as to which key might have been pressed
  696. // this is probably garbage if the user has a custom keyboard layout
  697. let code = guess_code_from_crossterm_key_code(event.code)?;
  698. let modifiers = modifiers_from_crossterm_modifiers(event.modifiers);
  699. Some(EventData::Keyboard(KeyboardData::new(
  700. key,
  701. code,
  702. Location::Standard,
  703. false,
  704. modifiers,
  705. )))
  706. }
  707. /// The crossterm key_code nicely represents the meaning of the key and we can mostly convert it without any issues
  708. ///
  709. /// Exceptions:
  710. /// BackTab is converted to Key::Tab, and Null is converted to Key::Unidentified
  711. fn key_from_crossterm_key_code(key_code: TermKeyCode) -> Key {
  712. match key_code {
  713. TermKeyCode::Backspace => Key::Backspace,
  714. TermKeyCode::Enter => Key::Enter,
  715. TermKeyCode::Left => Key::ArrowLeft,
  716. TermKeyCode::Right => Key::ArrowRight,
  717. TermKeyCode::Up => Key::ArrowUp,
  718. TermKeyCode::Down => Key::ArrowDown,
  719. TermKeyCode::Home => Key::Home,
  720. TermKeyCode::End => Key::End,
  721. TermKeyCode::PageUp => Key::PageUp,
  722. TermKeyCode::PageDown => Key::PageDown,
  723. TermKeyCode::Tab => Key::Tab,
  724. // ? no corresponding Key
  725. TermKeyCode::BackTab => Key::Tab,
  726. TermKeyCode::Delete => Key::Delete,
  727. TermKeyCode::Insert => Key::Insert,
  728. TermKeyCode::F(1) => Key::F1,
  729. TermKeyCode::F(2) => Key::F2,
  730. TermKeyCode::F(3) => Key::F3,
  731. TermKeyCode::F(4) => Key::F4,
  732. TermKeyCode::F(5) => Key::F5,
  733. TermKeyCode::F(6) => Key::F6,
  734. TermKeyCode::F(7) => Key::F7,
  735. TermKeyCode::F(8) => Key::F8,
  736. TermKeyCode::F(9) => Key::F9,
  737. TermKeyCode::F(10) => Key::F10,
  738. TermKeyCode::F(11) => Key::F11,
  739. TermKeyCode::F(12) => Key::F12,
  740. TermKeyCode::F(13) => Key::F13,
  741. TermKeyCode::F(14) => Key::F14,
  742. TermKeyCode::F(15) => Key::F15,
  743. TermKeyCode::F(16) => Key::F16,
  744. TermKeyCode::F(17) => Key::F17,
  745. TermKeyCode::F(18) => Key::F18,
  746. TermKeyCode::F(19) => Key::F19,
  747. TermKeyCode::F(20) => Key::F20,
  748. TermKeyCode::F(21) => Key::F21,
  749. TermKeyCode::F(22) => Key::F22,
  750. TermKeyCode::F(23) => Key::F23,
  751. TermKeyCode::F(24) => Key::F24,
  752. TermKeyCode::F(other) => {
  753. panic!("Unexpected function key: {other:?}")
  754. }
  755. TermKeyCode::Char(c) => Key::Character(c.to_string()),
  756. TermKeyCode::Null => Key::Unidentified,
  757. TermKeyCode::Esc => Key::Escape,
  758. }
  759. }
  760. // Crossterm does not provide a way to get the `code` (physical key on keyboard)
  761. // So we make a guess based on their `key_code`, but this is probably going to break on anything other than a very standard european keyboard
  762. // It may look fine, but it's a horrible hack. But there's nothing better we can do.
  763. fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option<Code> {
  764. let code = match key_code {
  765. TermKeyCode::Backspace => Code::Backspace,
  766. TermKeyCode::Enter => Code::Enter,
  767. TermKeyCode::Left => Code::ArrowLeft,
  768. TermKeyCode::Right => Code::ArrowRight,
  769. TermKeyCode::Up => Code::ArrowUp,
  770. TermKeyCode::Down => Code::ArrowDown,
  771. TermKeyCode::Home => Code::Home,
  772. TermKeyCode::End => Code::End,
  773. TermKeyCode::PageUp => Code::PageUp,
  774. TermKeyCode::PageDown => Code::PageDown,
  775. TermKeyCode::Tab => Code::Tab,
  776. // ? Apparently you get BackTab by pressing Tab
  777. TermKeyCode::BackTab => Code::Tab,
  778. TermKeyCode::Delete => Code::Delete,
  779. TermKeyCode::Insert => Code::Insert,
  780. TermKeyCode::F(1) => Code::F1,
  781. TermKeyCode::F(2) => Code::F2,
  782. TermKeyCode::F(3) => Code::F3,
  783. TermKeyCode::F(4) => Code::F4,
  784. TermKeyCode::F(5) => Code::F5,
  785. TermKeyCode::F(6) => Code::F6,
  786. TermKeyCode::F(7) => Code::F7,
  787. TermKeyCode::F(8) => Code::F8,
  788. TermKeyCode::F(9) => Code::F9,
  789. TermKeyCode::F(10) => Code::F10,
  790. TermKeyCode::F(11) => Code::F11,
  791. TermKeyCode::F(12) => Code::F12,
  792. TermKeyCode::F(13) => Code::F13,
  793. TermKeyCode::F(14) => Code::F14,
  794. TermKeyCode::F(15) => Code::F15,
  795. TermKeyCode::F(16) => Code::F16,
  796. TermKeyCode::F(17) => Code::F17,
  797. TermKeyCode::F(18) => Code::F18,
  798. TermKeyCode::F(19) => Code::F19,
  799. TermKeyCode::F(20) => Code::F20,
  800. TermKeyCode::F(21) => Code::F21,
  801. TermKeyCode::F(22) => Code::F22,
  802. TermKeyCode::F(23) => Code::F23,
  803. TermKeyCode::F(24) => Code::F24,
  804. TermKeyCode::F(other) => {
  805. panic!("Unexpected function key: {other:?}")
  806. }
  807. // this is a horrible way for crossterm to represent keys but we have to deal with it
  808. TermKeyCode::Char(c) => match c {
  809. 'A'..='Z' | 'a'..='z' => match c.to_ascii_uppercase() {
  810. 'A' => Code::KeyA,
  811. 'B' => Code::KeyB,
  812. 'C' => Code::KeyC,
  813. 'D' => Code::KeyD,
  814. 'E' => Code::KeyE,
  815. 'F' => Code::KeyF,
  816. 'G' => Code::KeyG,
  817. 'H' => Code::KeyH,
  818. 'I' => Code::KeyI,
  819. 'J' => Code::KeyJ,
  820. 'K' => Code::KeyK,
  821. 'L' => Code::KeyL,
  822. 'M' => Code::KeyM,
  823. 'N' => Code::KeyN,
  824. 'O' => Code::KeyO,
  825. 'P' => Code::KeyP,
  826. 'Q' => Code::KeyQ,
  827. 'R' => Code::KeyR,
  828. 'S' => Code::KeyS,
  829. 'T' => Code::KeyT,
  830. 'U' => Code::KeyU,
  831. 'V' => Code::KeyV,
  832. 'W' => Code::KeyW,
  833. 'X' => Code::KeyX,
  834. 'Y' => Code::KeyY,
  835. 'Z' => Code::KeyZ,
  836. _ => unreachable!("Exhaustively checked all characters in range A..Z"),
  837. },
  838. ' ' => Code::Space,
  839. '[' | '{' => Code::BracketLeft,
  840. ']' | '}' => Code::BracketRight,
  841. ';' => Code::Semicolon,
  842. ':' => Code::Semicolon,
  843. ',' => Code::Comma,
  844. '<' => Code::Comma,
  845. '.' => Code::Period,
  846. '>' => Code::Period,
  847. '1' => Code::Digit1,
  848. '2' => Code::Digit2,
  849. '3' => Code::Digit3,
  850. '4' => Code::Digit4,
  851. '5' => Code::Digit5,
  852. '6' => Code::Digit6,
  853. '7' => Code::Digit7,
  854. '8' => Code::Digit8,
  855. '9' => Code::Digit9,
  856. '0' => Code::Digit0,
  857. '!' => Code::Digit1,
  858. '@' => Code::Digit2,
  859. '#' => Code::Digit3,
  860. '$' => Code::Digit4,
  861. '%' => Code::Digit5,
  862. '^' => Code::Digit6,
  863. '&' => Code::Digit7,
  864. '*' => Code::Digit8,
  865. '(' => Code::Digit9,
  866. ')' => Code::Digit0,
  867. // numpad characters are ambiguous; we don't know which key was really pressed
  868. // it could be also:
  869. // '*' => Code::Multiply,
  870. // '/' => Code::Divide,
  871. // '-' => Code::Subtract,
  872. // '+' => Code::Add,
  873. '+' => Code::Equal,
  874. '-' | '_' => Code::Minus,
  875. '\'' => Code::Quote,
  876. '"' => Code::Quote,
  877. '\\' => Code::Backslash,
  878. '|' => Code::Backslash,
  879. '/' => Code::Slash,
  880. '?' => Code::Slash,
  881. '=' => Code::Equal,
  882. '`' => Code::Backquote,
  883. '~' => Code::Backquote,
  884. _ => return None,
  885. },
  886. TermKeyCode::Null => return None,
  887. TermKeyCode::Esc => Code::Escape,
  888. };
  889. Some(code)
  890. }
  891. fn modifiers_from_crossterm_modifiers(src: KeyModifiers) -> Modifiers {
  892. let mut modifiers = Modifiers::empty();
  893. if src.contains(KeyModifiers::SHIFT) {
  894. modifiers.insert(Modifiers::SHIFT);
  895. }
  896. if src.contains(KeyModifiers::ALT) {
  897. modifiers.insert(Modifiers::ALT);
  898. }
  899. if src.contains(KeyModifiers::CONTROL) {
  900. modifiers.insert(Modifiers::CONTROL);
  901. }
  902. modifiers
  903. }