events.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. use std::collections::HashMap;
  2. use dioxus::html::geometry::euclid::Vector3D;
  3. use dioxus::prelude::*;
  4. use dioxus_desktop::DesktopContext;
  5. #[path = "./utils.rs"]
  6. mod utils;
  7. pub fn main() {
  8. #[cfg(not(windows))]
  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
  164. .data
  165. .held_buttons()
  166. .contains(dioxus_html::input_data::MouseButton::Primary),
  167. );
  168. assert!(
  169. event
  170. .data
  171. .held_buttons()
  172. .contains(dioxus_html::input_data::MouseButton::Secondary),
  173. );
  174. assert_eq!(
  175. event.data.trigger_button(),
  176. Some(dioxus_html::input_data::MouseButton::Secondary),
  177. );
  178. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  179. }
  180. }
  181. }
  182. }
  183. fn test_mouse_down_div() -> Element {
  184. utils::mock_event(
  185. "mouse_down_div",
  186. r#"new MouseEvent("mousedown", {
  187. view: window,
  188. bubbles: true,
  189. cancelable: true,
  190. buttons: 2,
  191. button: 2,
  192. })"#,
  193. );
  194. rsx! {
  195. div {
  196. id: "mouse_down_div",
  197. onmousedown: move |event| {
  198. println!("{:?}", event.data);
  199. assert!(event.data.modifiers().is_empty());
  200. assert!(
  201. event
  202. .data
  203. .held_buttons()
  204. .contains(dioxus_html::input_data::MouseButton::Secondary),
  205. );
  206. assert_eq!(
  207. event.data.trigger_button(),
  208. Some(dioxus_html::input_data::MouseButton::Secondary),
  209. );
  210. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  211. }
  212. }
  213. }
  214. }
  215. fn test_mouse_up_div() -> Element {
  216. utils::mock_event(
  217. "mouse_up_div",
  218. r#"new MouseEvent("mouseup", {
  219. view: window,
  220. bubbles: true,
  221. cancelable: true,
  222. buttons: 0,
  223. button: 0,
  224. })"#,
  225. );
  226. rsx! {
  227. div {
  228. id: "mouse_up_div",
  229. onmouseup: move |event| {
  230. println!("{:?}", event.data);
  231. assert!(event.data.modifiers().is_empty());
  232. assert!(event.data.held_buttons().is_empty());
  233. assert_eq!(
  234. event.data.trigger_button(),
  235. Some(dioxus_html::input_data::MouseButton::Primary),
  236. );
  237. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  238. }
  239. }
  240. }
  241. }
  242. fn test_mouse_scroll_div() -> Element {
  243. utils::mock_event(
  244. "wheel_div",
  245. r#"new WheelEvent("wheel", {
  246. view: window,
  247. deltaX: 1.0,
  248. deltaY: 2.0,
  249. deltaZ: 3.0,
  250. deltaMode: 0x00,
  251. bubbles: true,
  252. })"#,
  253. );
  254. rsx! {
  255. div {
  256. id: "wheel_div",
  257. width: "100px",
  258. height: "100px",
  259. background_color: "red",
  260. onwheel: move |event| {
  261. println!("{:?}", event.data);
  262. let dioxus_html::geometry::WheelDelta::Pixels(delta) = event.data.delta() else {
  263. panic!("Expected delta to be in pixels")
  264. };
  265. assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0));
  266. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  267. }
  268. }
  269. }
  270. }
  271. fn test_key_down_div() -> Element {
  272. utils::mock_event(
  273. "key_down_div",
  274. r#"new KeyboardEvent("keydown", {
  275. key: "a",
  276. code: "KeyA",
  277. location: 0,
  278. repeat: true,
  279. keyCode: 65,
  280. charCode: 97,
  281. char: "a",
  282. charCode: 0,
  283. altKey: false,
  284. ctrlKey: false,
  285. metaKey: false,
  286. shiftKey: false,
  287. isComposing: true,
  288. which: 65,
  289. bubbles: true,
  290. })"#,
  291. );
  292. rsx! {
  293. input {
  294. id: "key_down_div",
  295. onkeydown: move |event| {
  296. println!("{:?}", event.data);
  297. assert!(event.data.modifiers().is_empty());
  298. assert_eq!(event.data.key().to_string(), "a");
  299. assert_eq!(event.data.code().to_string(), "KeyA");
  300. assert_eq!(event.data.location(), Location::Standard);
  301. assert!(event.data.is_auto_repeating());
  302. assert!(event.data.is_composing());
  303. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  304. }
  305. }
  306. }
  307. }
  308. fn test_key_up_div() -> Element {
  309. utils::mock_event(
  310. "key_up_div",
  311. r#"new KeyboardEvent("keyup", {
  312. key: "a",
  313. code: "KeyA",
  314. location: 0,
  315. repeat: false,
  316. keyCode: 65,
  317. charCode: 97,
  318. char: "a",
  319. charCode: 0,
  320. altKey: false,
  321. ctrlKey: false,
  322. metaKey: false,
  323. shiftKey: false,
  324. isComposing: false,
  325. which: 65,
  326. bubbles: true,
  327. })"#,
  328. );
  329. rsx! {
  330. input {
  331. id: "key_up_div",
  332. onkeyup: move |event| {
  333. println!("{:?}", event.data);
  334. assert!(event.data.modifiers().is_empty());
  335. assert_eq!(event.data.key().to_string(), "a");
  336. assert_eq!(event.data.code().to_string(), "KeyA");
  337. assert_eq!(event.data.location(), Location::Standard);
  338. assert!(!event.data.is_auto_repeating());
  339. assert!(!event.data.is_composing());
  340. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  341. }
  342. }
  343. }
  344. }
  345. fn test_key_press_div() -> Element {
  346. utils::mock_event(
  347. "key_press_div",
  348. r#"new KeyboardEvent("keypress", {
  349. key: "a",
  350. code: "KeyA",
  351. location: 0,
  352. repeat: false,
  353. keyCode: 65,
  354. charCode: 97,
  355. char: "a",
  356. charCode: 0,
  357. altKey: false,
  358. ctrlKey: false,
  359. metaKey: false,
  360. shiftKey: false,
  361. isComposing: false,
  362. which: 65,
  363. bubbles: true,
  364. })"#,
  365. );
  366. rsx! {
  367. input {
  368. id: "key_press_div",
  369. onkeypress: move |event| {
  370. println!("{:?}", event.data);
  371. assert!(event.data.modifiers().is_empty());
  372. assert_eq!(event.data.key().to_string(), "a");
  373. assert_eq!(event.data.code().to_string(), "KeyA");
  374. assert_eq!(event.data.location(), Location::Standard);
  375. assert!(!event.data.is_auto_repeating());
  376. assert!(!event.data.is_composing());
  377. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  378. }
  379. }
  380. }
  381. }
  382. fn test_focus_in_div() -> Element {
  383. utils::mock_event(
  384. "focus_in_div",
  385. r#"new FocusEvent("focusin", {bubbles: true})"#,
  386. );
  387. rsx! {
  388. input {
  389. id: "focus_in_div",
  390. onfocusin: move |event| {
  391. println!("{:?}", event.data);
  392. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  393. }
  394. }
  395. }
  396. }
  397. fn test_focus_out_div() -> Element {
  398. utils::mock_event(
  399. "focus_out_div",
  400. r#"new FocusEvent("focusout",{bubbles: true})"#,
  401. );
  402. rsx! {
  403. input {
  404. id: "focus_out_div",
  405. onfocusout: move |event| {
  406. println!("{:?}", event.data);
  407. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  408. }
  409. }
  410. }
  411. }
  412. fn test_form_input() -> Element {
  413. let mut values = use_signal(HashMap::new);
  414. utils::mock_event_with_extra(
  415. "form-username",
  416. r#"new Event("input", { bubbles: true, cancelable: true, composed: true })"#,
  417. r#"element.value = "hello";"#,
  418. );
  419. let set_username = move |ev: FormEvent| {
  420. values.set(ev.values());
  421. // The value of the input should match
  422. assert_eq!(ev.value(), "hello");
  423. // And then the value the form gives us should also match
  424. values.with_mut(|x| {
  425. assert_eq!(x.get("username").unwrap(), "hello");
  426. assert_eq!(x.get("full-name").unwrap(), "lorem");
  427. assert_eq!(x.get("password").unwrap(), "ipsum");
  428. assert_eq!(x.get("color").unwrap(), "red");
  429. });
  430. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  431. };
  432. rsx! {
  433. div {
  434. h1 { "Form" }
  435. form {
  436. id: "form",
  437. oninput: move |ev| {
  438. values.set(ev.values());
  439. },
  440. onsubmit: move |ev| {
  441. println!("{:?}", ev);
  442. },
  443. input {
  444. r#type: "text",
  445. name: "username",
  446. id: "form-username",
  447. oninput: set_username
  448. }
  449. input { r#type: "text", name: "full-name", value: "lorem" }
  450. input { r#type: "password", name: "password", value: "ipsum" }
  451. input {
  452. r#type: "radio",
  453. name: "color",
  454. value: "red",
  455. checked: true
  456. }
  457. input { r#type: "radio", name: "color", value: "blue" }
  458. button { r#type: "submit", value: "Submit", "Submit the form" }
  459. }
  460. }
  461. }
  462. }
  463. fn test_form_submit() -> Element {
  464. let mut values = use_signal(HashMap::new);
  465. utils::mock_event_with_extra(
  466. "form-submitter",
  467. r#"new Event("submit", { bubbles: true, cancelable: true, composed: true })"#,
  468. r#"element.submit();"#,
  469. );
  470. let set_values = move |ev: FormEvent| {
  471. values.set(ev.values());
  472. values.with_mut(|x| {
  473. assert_eq!(x.get("username").unwrap(), "goodbye");
  474. assert_eq!(x.get("full-name").unwrap(), "lorem");
  475. assert_eq!(x.get("password").unwrap(), "ipsum");
  476. assert_eq!(x.get("color").unwrap(), "red");
  477. });
  478. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  479. };
  480. rsx! {
  481. div {
  482. h1 { "Form" }
  483. form { id: "form-submitter", onsubmit: set_values,
  484. input {
  485. r#type: "text",
  486. name: "username",
  487. id: "username",
  488. value: "goodbye"
  489. }
  490. input { r#type: "text", name: "full-name", value: "lorem" }
  491. input { r#type: "password", name: "password", value: "ipsum" }
  492. input {
  493. r#type: "radio",
  494. name: "color",
  495. value: "red",
  496. checked: true
  497. }
  498. input { r#type: "radio", name: "color", value: "blue" }
  499. button { r#type: "submit", value: "Submit", "Submit the form" }
  500. }
  501. }
  502. }
  503. }
  504. fn test_select_multiple_options() -> Element {
  505. utils::mock_event_with_extra(
  506. "select-many",
  507. r#"new Event("input", { bubbles: true, cancelable: true, composed: true })"#,
  508. r#"
  509. document.getElementById('usa').selected = true;
  510. document.getElementById('canada').selected = true;
  511. document.getElementById('mexico').selected = false;
  512. "#,
  513. );
  514. rsx! {
  515. select {
  516. id: "select-many",
  517. name: "country",
  518. multiple: true,
  519. oninput: move |ev| {
  520. let values = ev.value();
  521. let values = values.split(',').collect::<Vec<_>>();
  522. assert_eq!(values, vec!["usa", "canada"]);
  523. RECEIVED_EVENTS.with_mut(|x| *x += 1);
  524. },
  525. option { id: "usa", value: "usa", "USA" }
  526. option { id: "canada", value: "canada", "Canada" }
  527. option { id: "mexico", value: "mexico", selected: true, "Mexico" }
  528. }
  529. }
  530. }