events.rs 16 KB

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