1
0

events.rs 16 KB

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