events.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. use std::collections::HashMap;
  2. use dioxus::html::geometry::euclid::Vector3D;
  3. use dioxus::prelude::*;
  4. use dioxus_core::prelude::consume_context;
  5. use dioxus_desktop::DesktopContext;
  6. #[path = "./utils.rs"]
  7. mod utils;
  8. pub fn main() {
  9. utils::check_app_exits(app);
  10. }
  11. static RECEIVED_EVENTS: GlobalSignal<usize> = Signal::global(|| 0);
  12. fn app() -> Element {
  13. let desktop_context: DesktopContext = consume_context();
  14. let received = RECEIVED_EVENTS();
  15. let expected = utils::EXPECTED_EVENTS();
  16. use_effect(move || {
  17. println!("expecting {} events", utils::EXPECTED_EVENTS());
  18. });
  19. if expected != 0 && received == expected {
  20. println!("all events recieved");
  21. desktop_context.close();
  22. }
  23. rsx! {
  24. div {
  25. test_mounted {}
  26. test_button {}
  27. test_mouse_move_div {}
  28. test_mouse_click_div {}
  29. test_mouse_dblclick_div {}
  30. test_mouse_down_div {}
  31. test_mouse_up_div {}
  32. test_mouse_scroll_div {}
  33. test_key_down_div {}
  34. test_key_up_div {}
  35. test_key_press_div {}
  36. test_focus_in_div {}
  37. test_focus_out_div {}
  38. test_form_input {}
  39. test_form_submit {}
  40. test_select_multiple_options {}
  41. }
  42. }
  43. }
  44. fn test_mounted() -> Element {
  45. use_hook(|| utils::EXPECTED_EVENTS.with_mut(|x| *x += 1));
  46. rsx! {
  47. div {
  48. width: "100px",
  49. height: "100px",
  50. onmounted: move |evt| async move {
  51. let rect = evt.get_client_rect().await.unwrap();
  52. println!("rect: {:?}", rect);
  53. assert_eq!(rect.width(), 100.0);
  54. assert_eq!(rect.height(), 100.0);
  55. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  56. }
  57. }
  58. }
  59. }
  60. fn test_button() -> Element {
  61. utils::mock_event(
  62. "button",
  63. r#"new MouseEvent("click", {
  64. view: window,
  65. bubbles: true,
  66. cancelable: true,
  67. button: 0,
  68. })"#,
  69. );
  70. rsx! {
  71. button {
  72. id: "button",
  73. onclick: move |event| {
  74. println!("{:?}", event.data);
  75. assert!(event.data.modifiers().is_empty());
  76. assert!(event.data.held_buttons().is_empty());
  77. assert_eq!(
  78. event.data.trigger_button(),
  79. Some(dioxus_html::input_data::MouseButton::Primary),
  80. );
  81. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  82. }
  83. }
  84. }
  85. }
  86. fn test_mouse_move_div() -> Element {
  87. utils::mock_event(
  88. "mouse_move_div",
  89. r#"new MouseEvent("mousemove", {
  90. view: window,
  91. bubbles: true,
  92. cancelable: true,
  93. buttons: 2,
  94. })"#,
  95. );
  96. rsx! {
  97. div {
  98. id: "mouse_move_div",
  99. onmousemove: move |event| {
  100. println!("{:?}", event.data);
  101. assert!(event.data.modifiers().is_empty());
  102. assert!(
  103. event
  104. .data
  105. .held_buttons()
  106. .contains(dioxus_html::input_data::MouseButton::Secondary),
  107. );
  108. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  109. }
  110. }
  111. }
  112. }
  113. fn test_mouse_click_div() -> Element {
  114. utils::mock_event(
  115. "mouse_click_div",
  116. r#"new MouseEvent("click", {
  117. view: window,
  118. bubbles: true,
  119. cancelable: true,
  120. buttons: 2,
  121. button: 2,
  122. })"#,
  123. );
  124. rsx! {
  125. div {
  126. id: "mouse_click_div",
  127. onclick: move |event| {
  128. println!("{:?}", event.data);
  129. assert!(event.data.modifiers().is_empty());
  130. assert!(
  131. event
  132. .data
  133. .held_buttons()
  134. .contains(dioxus_html::input_data::MouseButton::Secondary),
  135. );
  136. assert_eq!(
  137. event.data.trigger_button(),
  138. Some(dioxus_html::input_data::MouseButton::Secondary),
  139. );
  140. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  141. }
  142. }
  143. }
  144. }
  145. fn test_mouse_dblclick_div() -> Element {
  146. utils::mock_event(
  147. "mouse_dblclick_div",
  148. r#"new MouseEvent("dblclick", {
  149. view: window,
  150. bubbles: true,
  151. cancelable: true,
  152. buttons: 1|2,
  153. button: 2,
  154. })"#,
  155. );
  156. rsx! {
  157. div {
  158. id: "mouse_dblclick_div",
  159. ondoubleclick: move |event| {
  160. println!("{:?}", event.data);
  161. assert!(event.data.modifiers().is_empty());
  162. assert!(
  163. event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary),
  164. );
  165. assert!(
  166. event
  167. .data
  168. .held_buttons()
  169. .contains(dioxus_html::input_data::MouseButton::Secondary),
  170. );
  171. assert_eq!(
  172. event.data.trigger_button(),
  173. Some(dioxus_html::input_data::MouseButton::Secondary),
  174. );
  175. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  176. }
  177. }
  178. }
  179. }
  180. fn test_mouse_down_div() -> Element {
  181. utils::mock_event(
  182. "mouse_down_div",
  183. r#"new MouseEvent("mousedown", {
  184. view: window,
  185. bubbles: true,
  186. cancelable: true,
  187. buttons: 2,
  188. button: 2,
  189. })"#,
  190. );
  191. rsx! {
  192. div {
  193. id: "mouse_down_div",
  194. onmousedown: move |event| {
  195. println!("{:?}", event.data);
  196. assert!(event.data.modifiers().is_empty());
  197. assert!(
  198. event
  199. .data
  200. .held_buttons()
  201. .contains(dioxus_html::input_data::MouseButton::Secondary),
  202. );
  203. assert_eq!(
  204. event.data.trigger_button(),
  205. Some(dioxus_html::input_data::MouseButton::Secondary),
  206. );
  207. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  208. }
  209. }
  210. }
  211. }
  212. fn test_mouse_up_div() -> Element {
  213. utils::mock_event(
  214. "mouse_up_div",
  215. r#"new MouseEvent("mouseup", {
  216. view: window,
  217. bubbles: true,
  218. cancelable: true,
  219. buttons: 0,
  220. button: 0,
  221. })"#,
  222. );
  223. rsx! {
  224. div {
  225. id: "mouse_up_div",
  226. onmouseup: move |event| {
  227. println!("{:?}", event.data);
  228. assert!(event.data.modifiers().is_empty());
  229. assert!(event.data.held_buttons().is_empty());
  230. assert_eq!(
  231. event.data.trigger_button(),
  232. Some(dioxus_html::input_data::MouseButton::Primary),
  233. );
  234. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  235. }
  236. }
  237. }
  238. }
  239. fn test_mouse_scroll_div() -> Element {
  240. utils::mock_event(
  241. "wheel_div",
  242. r#"new WheelEvent("wheel", {
  243. view: window,
  244. deltaX: 1.0,
  245. deltaY: 2.0,
  246. deltaZ: 3.0,
  247. deltaMode: 0x00,
  248. bubbles: true,
  249. })"#,
  250. );
  251. rsx! {
  252. div {
  253. id: "wheel_div",
  254. width: "100px",
  255. height: "100px",
  256. background_color: "red",
  257. onwheel: move |event| {
  258. println!("{:?}", event.data);
  259. let dioxus_html::geometry::WheelDelta::Pixels(delta) = event.data.delta() else {
  260. panic!("Expected delta to be in pixels") };
  261. assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0));
  262. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  263. }
  264. }
  265. }
  266. }
  267. fn test_key_down_div() -> Element {
  268. utils::mock_event(
  269. "key_down_div",
  270. r#"new KeyboardEvent("keydown", {
  271. key: "a",
  272. code: "KeyA",
  273. location: 0,
  274. repeat: true,
  275. keyCode: 65,
  276. charCode: 97,
  277. char: "a",
  278. charCode: 0,
  279. altKey: false,
  280. ctrlKey: false,
  281. metaKey: false,
  282. shiftKey: false,
  283. isComposing: true,
  284. which: 65,
  285. bubbles: true,
  286. })"#,
  287. );
  288. rsx! {
  289. input {
  290. id: "key_down_div",
  291. onkeydown: move |event| {
  292. println!("{:?}", event.data);
  293. assert!(event.data.modifiers().is_empty());
  294. assert_eq!(event.data.key().to_string(), "a");
  295. assert_eq!(event.data.code().to_string(), "KeyA");
  296. assert_eq!(event.data.location(), Location::Standard);
  297. assert!(event.data.is_auto_repeating());
  298. assert!(event.data.is_composing());
  299. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  300. }
  301. }
  302. }
  303. }
  304. fn test_key_up_div() -> Element {
  305. utils::mock_event(
  306. "key_up_div",
  307. r#"new KeyboardEvent("keyup", {
  308. key: "a",
  309. code: "KeyA",
  310. location: 0,
  311. repeat: false,
  312. keyCode: 65,
  313. charCode: 97,
  314. char: "a",
  315. charCode: 0,
  316. altKey: false,
  317. ctrlKey: false,
  318. metaKey: false,
  319. shiftKey: false,
  320. isComposing: false,
  321. which: 65,
  322. bubbles: true,
  323. })"#,
  324. );
  325. rsx! {
  326. input {
  327. id: "key_up_div",
  328. onkeyup: move |event| {
  329. println!("{:?}", event.data);
  330. assert!(event.data.modifiers().is_empty());
  331. assert_eq!(event.data.key().to_string(), "a");
  332. assert_eq!(event.data.code().to_string(), "KeyA");
  333. assert_eq!(event.data.location(), Location::Standard);
  334. assert!(!event.data.is_auto_repeating());
  335. assert!(!event.data.is_composing());
  336. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  337. }
  338. }
  339. }
  340. }
  341. fn test_key_press_div() -> Element {
  342. utils::mock_event(
  343. "key_press_div",
  344. r#"new KeyboardEvent("keypress", {
  345. key: "a",
  346. code: "KeyA",
  347. location: 0,
  348. repeat: false,
  349. keyCode: 65,
  350. charCode: 97,
  351. char: "a",
  352. charCode: 0,
  353. altKey: false,
  354. ctrlKey: false,
  355. metaKey: false,
  356. shiftKey: false,
  357. isComposing: false,
  358. which: 65,
  359. bubbles: true,
  360. })"#,
  361. );
  362. rsx! {
  363. input {
  364. id: "key_press_div",
  365. onkeypress: move |event| {
  366. println!("{:?}", event.data);
  367. assert!(event.data.modifiers().is_empty());
  368. assert_eq!(event.data.key().to_string(), "a");
  369. assert_eq!(event.data.code().to_string(), "KeyA");
  370. assert_eq!(event.data.location(), Location::Standard);
  371. assert!(!event.data.is_auto_repeating());
  372. assert!(!event.data.is_composing());
  373. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  374. }
  375. }
  376. }
  377. }
  378. fn test_focus_in_div() -> Element {
  379. utils::mock_event(
  380. "focus_in_div",
  381. r#"new FocusEvent("focusin", {bubbles: true})"#,
  382. );
  383. rsx! {
  384. input {
  385. id: "focus_in_div",
  386. onfocusin: move |event| {
  387. println!("{:?}", event.data);
  388. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  389. }
  390. }
  391. }
  392. }
  393. fn test_focus_out_div() -> Element {
  394. utils::mock_event(
  395. "focus_out_div",
  396. r#"new FocusEvent("focusout",{bubbles: true})"#,
  397. );
  398. rsx! {
  399. input {
  400. id: "focus_out_div",
  401. onfocusout: move |event| {
  402. println!("{:?}", event.data);
  403. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  404. }
  405. }
  406. }
  407. }
  408. fn test_form_input() -> Element {
  409. let mut values = use_signal(HashMap::new);
  410. utils::mock_event_with_extra(
  411. "form-username",
  412. r#"new Event("input", { bubbles: true, cancelable: true, composed: true })"#,
  413. r#"element.value = "hello";"#,
  414. );
  415. let set_username = move |ev: FormEvent| {
  416. values.set(ev.values());
  417. // The value of the input should match
  418. assert_eq!(ev.value(), "hello");
  419. // And then the value the form gives us should also match
  420. values.with_mut(|x| {
  421. assert_eq!(x.get("username").unwrap(), "hello");
  422. assert_eq!(x.get("full-name").unwrap(), "lorem");
  423. assert_eq!(x.get("password").unwrap(), "ipsum");
  424. assert_eq!(x.get("color").unwrap(), "red");
  425. });
  426. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  427. };
  428. rsx! {
  429. div {
  430. h1 { "Form" }
  431. form {
  432. id: "form",
  433. oninput: move |ev| {
  434. values.set(ev.values());
  435. },
  436. onsubmit: move |ev| {
  437. println!("{:?}", ev);
  438. },
  439. input {
  440. r#type: "text",
  441. name: "username",
  442. id: "form-username",
  443. oninput: set_username,
  444. }
  445. input { r#type: "text", name: "full-name", value: "lorem" }
  446. input { r#type: "password", name: "password", value: "ipsum" }
  447. input { r#type: "radio", name: "color", value: "red", checked: true }
  448. input { r#type: "radio", name: "color", value: "blue" }
  449. button { r#type: "submit", value: "Submit", "Submit the form" }
  450. }
  451. }
  452. }
  453. }
  454. fn test_form_submit() -> Element {
  455. let mut values = use_signal(HashMap::new);
  456. utils::mock_event_with_extra(
  457. "form-submitter",
  458. r#"new Event("submit", { bubbles: true, cancelable: true, composed: true })"#,
  459. r#"element.submit();"#,
  460. );
  461. let set_values = move |ev: FormEvent| {
  462. values.set(ev.values());
  463. values.with_mut(|x| {
  464. assert_eq!(x.get("username").unwrap(), "goodbye");
  465. assert_eq!(x.get("full-name").unwrap(), "lorem");
  466. assert_eq!(x.get("password").unwrap(), "ipsum");
  467. assert_eq!(x.get("color").unwrap(), "red");
  468. });
  469. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  470. };
  471. rsx! {
  472. div {
  473. h1 { "Form" }
  474. form {
  475. id: "form-submitter",
  476. onsubmit: set_values,
  477. input { r#type: "text", name: "username", id: "username", value: "goodbye" }
  478. input { r#type: "text", name: "full-name", value: "lorem" }
  479. input { r#type: "password", name: "password", value: "ipsum" }
  480. input { r#type: "radio", name: "color", value: "red", checked: true }
  481. input { r#type: "radio", name: "color", value: "blue" }
  482. button { r#type: "submit", value: "Submit", "Submit the form" }
  483. }
  484. }
  485. }
  486. }
  487. fn test_select_multiple_options() -> Element {
  488. utils::mock_event_with_extra(
  489. "select-many",
  490. r#"new Event("input", { bubbles: true, cancelable: true, composed: true })"#,
  491. r#"
  492. document.getElementById('usa').selected = true;
  493. document.getElementById('canada').selected = true;
  494. document.getElementById('mexico').selected = false;
  495. "#,
  496. );
  497. rsx! {
  498. select {
  499. id: "select-many",
  500. name: "country",
  501. multiple: true,
  502. oninput: move |ev| {
  503. let values = ev.value();
  504. let values = values.split(',').collect::<Vec<_>>();
  505. assert_eq!(values, vec!["usa", "canada"]);
  506. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  507. },
  508. option { id: "usa", value: "usa", "USA" }
  509. option { id: "canada", value: "canada", "Canada" }
  510. option { id: "mexico", value: "mexico", selected: true, "Mexico" }
  511. }
  512. }
  513. }