Sfoglia il codice sorgente

Merge branch 'rusty-events' into proofread-readme

Reinis Mazeiks 3 anni fa
parent
commit
3b50a8e6b3

+ 0 - 1
docs/guide/src/advanced-guides/custom-renderer.md

@@ -204,7 +204,6 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
                 fn ctrl_key(&self) -> bool { self.0.ctrl_key() }
                 fn key(&self) -> String { self.0.key() }
                 fn key_code(&self) -> usize { self.0.key_code() }
-                fn locale(&self) -> String { self.0.locale() }
                 fn location(&self) -> usize { self.0.location() }
                 fn meta_key(&self) -> bool { self.0.meta_key() }
                 fn repeat(&self) -> bool { self.0.repeat() }

+ 0 - 1
docs/reference/src/guide/custom_renderer.md

@@ -206,7 +206,6 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
                     ctrl_key: event.ctrl_key(),
                     meta_key: event.meta_key(),
                     shift_key: event.shift_key(),
-                    locale: "".to_string(),
                     location: event.location(),
                     repeat: event.repeat(),
                     which: event.which(),

+ 85 - 0
examples/all_events.rs

@@ -0,0 +1,85 @@
+use dioxus::prelude::*;
+use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData};
+use std::sync::Arc;
+
+fn main() {
+    dioxus::desktop::launch(app);
+}
+
+#[derive(Debug)]
+enum Event {
+    MouseMove(Arc<MouseData>),
+    MouseClick(Arc<MouseData>),
+    MouseDoubleClick(Arc<MouseData>),
+    MouseDown(Arc<MouseData>),
+    MouseUp(Arc<MouseData>),
+
+    Wheel(Arc<WheelData>),
+
+    KeyDown(Arc<KeyboardData>),
+    KeyUp(Arc<KeyboardData>),
+    KeyPress(Arc<KeyboardData>),
+
+    FocusIn(Arc<FocusData>),
+    FocusOut(Arc<FocusData>),
+}
+
+const MAX_EVENTS: usize = 8;
+
+fn app(cx: Scope) -> Element {
+    let container_style = r#"
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+    "#;
+    let rect_style = r#"
+        background: deepskyblue;
+        height: 50vh;
+        width: 50vw;
+        color: white;
+        padding: 20px;
+        margin: 20px;
+        text-aligh: center;
+    "#;
+
+    let events = use_ref(&cx, || Vec::new());
+
+    let events_lock = events.read();
+    let first_index = events_lock.len().saturating_sub(MAX_EVENTS);
+    let events_rendered = events_lock[first_index..]
+        .iter()
+        .map(|event| cx.render(rsx!(div {"{event:?}"})));
+
+    let log_event = move |event: Event| {
+        events.write().push(event);
+    };
+
+    cx.render(rsx! (
+        div {
+            style: "{container_style}",
+            div {
+                style: "{rect_style}",
+                // focusing is necessary to catch keyboard events
+                tabindex: "0",
+
+                onmousemove: move |event| log_event(Event::MouseMove(event.data)),
+                onclick: move |event| log_event(Event::MouseClick(event.data)),
+                ondblclick: move |event| log_event(Event::MouseDoubleClick(event.data)),
+                onmousedown: move |event| log_event(Event::MouseDown(event.data)),
+                onmouseup: move |event| log_event(Event::MouseUp(event.data)),
+
+                onwheel: move |event| log_event(Event::Wheel(event.data)),
+
+                onkeydown: move |event| log_event(Event::KeyDown(event.data)),
+                onkeyup: move |event| log_event(Event::KeyUp(event.data)),
+                onkeypress: move |event| log_event(Event::KeyPress(event.data)),
+
+                onfocusin: move |event| log_event(Event::FocusIn(event.data)),
+                onfocusout: move |event| log_event(Event::FocusOut(event.data)),
+
+                "Hover, click, type or scroll to see the info down below"
+            }
+            div { events_rendered },
+        },
+    ))
+}

+ 28 - 22
examples/calculator.rs

@@ -5,6 +5,7 @@ This calculator version uses React-style state management. All state is held as
 
 use dioxus::events::*;
 use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Key;
 
 fn main() {
     use dioxus::desktop::tao::dpi::LogicalSize;
@@ -29,33 +30,38 @@ fn app(cx: Scope) -> Element {
 
     let input_operator = move |key: &str| val.make_mut().push_str(key);
 
+    let handle_key_down_event = move |evt: KeyboardEvent| match evt.key() {
+        Key::Backspace => {
+            if !val.len() != 0 {
+                val.make_mut().pop();
+            }
+        }
+        Key::Character(character) => match character.as_str() {
+            "+" => input_operator("+"),
+            "-" => input_operator("-"),
+            "/" => input_operator("/"),
+            "*" => input_operator("*"),
+            "0" => input_digit(0),
+            "1" => input_digit(1),
+            "2" => input_digit(2),
+            "3" => input_digit(3),
+            "4" => input_digit(4),
+            "5" => input_digit(5),
+            "6" => input_digit(6),
+            "7" => input_digit(7),
+            "8" => input_digit(8),
+            "9" => input_digit(9),
+            _ => {}
+        },
+        _ => {}
+    };
+
     cx.render(rsx!(
         style { [include_str!("./assets/calculator.css")] }
         div { id: "wrapper",
             div { class: "app",
                 div { class: "calculator",
-                    onkeydown: move |evt| match evt.key_code {
-                        KeyCode::Add => input_operator("+"),
-                        KeyCode::Subtract => input_operator("-"),
-                        KeyCode::Divide => input_operator("/"),
-                        KeyCode::Multiply => input_operator("*"),
-                        KeyCode::Num0 => input_digit(0),
-                        KeyCode::Num1 => input_digit(1),
-                        KeyCode::Num2 => input_digit(2),
-                        KeyCode::Num3 => input_digit(3),
-                        KeyCode::Num4 => input_digit(4),
-                        KeyCode::Num5 => input_digit(5),
-                        KeyCode::Num6 => input_digit(6),
-                        KeyCode::Num7 => input_digit(7),
-                        KeyCode::Num8 => input_digit(8),
-                        KeyCode::Num9 => input_digit(9),
-                        KeyCode::Backspace => {
-                            if !val.len() != 0 {
-                                val.make_mut().pop();
-                            }
-                        }
-                        _ => {}
-                    },
+                    onkeydown: handle_key_down_event,
                     div { class: "calculator-display", [val.to_string()] }
                     div { class: "calculator-keypad",
                         div { class: "input-keys",

+ 0 - 26
examples/events.rs

@@ -1,26 +0,0 @@
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus::desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
-        div {
-            button {
-                ondblclick: move |_| {
-                    //
-                    println!("double clicked!");
-                },
-                "Click me!"
-            }
-            input {
-                 onfocusin: move |_| {
-                    //
-                    println!("blurred!");
-                },
-                "onblur": "console.log('blurred!')"
-            }
-        }
-    })
-}

+ 0 - 56
examples/mouse_event.rs

@@ -1,56 +0,0 @@
-use dioxus::prelude::*;
-use dioxus_core::UiEvent;
-use dioxus_html::on::MouseData;
-
-fn main() {
-    dioxus::desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let page_coordinates = use_state(&cx, || "".to_string());
-    let screen_coordinates = use_state(&cx, || "".to_string());
-    let element_coordinates = use_state(&cx, || "".to_string());
-    let buttons = use_state(&cx, || "".to_string());
-    let modifiers = use_state(&cx, || "".to_string());
-
-    let container_style = r#"
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-    "#;
-    let rect_style = r#"
-        background: deepskyblue;
-        height: 50vh;
-        width: 50vw;
-    "#;
-
-    let update_mouse_position = move |event: UiEvent<MouseData>| {
-        let mouse_data = event.data;
-
-        page_coordinates.set(format!("{:?}", mouse_data.page_coordinates()));
-        screen_coordinates.set(format!("{:?}", mouse_data.screen_coordinates()));
-        element_coordinates.set(format!("{:?}", mouse_data.element_coordinates()));
-
-        // Note: client coordinates are also available, but they would be the same as the page coordinates in this example, because there is no scrolling.
-
-        buttons.set(format!("{:?}", mouse_data.held_buttons()));
-        modifiers.set(format!("{:?}", mouse_data.modifiers()));
-    };
-
-    cx.render(rsx! (
-        div {
-            style: "{container_style}",
-            "Hover over to display coordinates:",
-            div {
-                style: "{rect_style}",
-                onmousemove: update_mouse_position,
-                prevent_default: "mousedown",
-            }
-            div {"Page coordinates: {page_coordinates}"},
-            div {"Screen coordinates: {screen_coordinates}"},
-            div {"Element coordinates: {element_coordinates}"},
-            div {"Buttons: {buttons}"},
-            div {"Modifiers: {modifiers}"},
-        }
-    ))
-}

+ 21 - 16
examples/pattern_model.rs

@@ -20,6 +20,7 @@
 use dioxus::desktop::wry::application::dpi::LogicalSize;
 use dioxus::events::*;
 use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Key;
 
 fn main() {
     dioxus::desktop::launch_cfg(app, |cfg| {
@@ -212,22 +213,26 @@ impl Calculator {
         self.waiting_for_operand = true;
     }
     fn handle_keydown(&mut self, evt: KeyboardEvent) {
-        match evt.key_code {
-            KeyCode::Backspace => self.backspace(),
-            KeyCode::Num0 => self.input_digit(0),
-            KeyCode::Num1 => self.input_digit(1),
-            KeyCode::Num2 => self.input_digit(2),
-            KeyCode::Num3 => self.input_digit(3),
-            KeyCode::Num4 => self.input_digit(4),
-            KeyCode::Num5 => self.input_digit(5),
-            KeyCode::Num6 => self.input_digit(6),
-            KeyCode::Num7 => self.input_digit(7),
-            KeyCode::Num8 => self.input_digit(8),
-            KeyCode::Num9 => self.input_digit(9),
-            KeyCode::Add => self.operator = Some(Operator::Add),
-            KeyCode::Subtract => self.operator = Some(Operator::Sub),
-            KeyCode::Divide => self.operator = Some(Operator::Div),
-            KeyCode::Multiply => self.operator = Some(Operator::Mul),
+        match evt.key() {
+            Key::Backspace => self.backspace(),
+            Key::Character(c) => match c.as_str() {
+                "0" => self.input_digit(0),
+                "1" => self.input_digit(1),
+                "2" => self.input_digit(2),
+                "3" => self.input_digit(3),
+                "4" => self.input_digit(4),
+                "5" => self.input_digit(5),
+                "6" => self.input_digit(6),
+                "7" => self.input_digit(7),
+                "8" => self.input_digit(8),
+                "9" => self.input_digit(9),
+                "+" => self.operator = Some(Operator::Add),
+                "-" => self.operator = Some(Operator::Sub),
+                "/" => self.operator = Some(Operator::Div),
+                "*" => self.operator = Some(Operator::Mul),
+                _ => {}
+            },
+
             _ => {}
         }
     }

+ 4 - 3
examples/todomvc.rs

@@ -1,4 +1,5 @@
 use dioxus::prelude::*;
+use dioxus_elements::input_data::keyboard_types::Key;
 
 fn main() {
     dioxus::desktop::launch(app);
@@ -56,7 +57,7 @@ pub fn app(cx: Scope<()>) -> Element {
                         autofocus: "true",
                         oninput: move |evt| draft.set(evt.value.clone()),
                         onkeydown: move |evt| {
-                            if evt.key == "Enter" && !draft.is_empty() {
+                            if evt.key() == Key::Enter && !draft.is_empty() {
                                 todos.make_mut().insert(
                                     **todo_id,
                                     TodoItem {
@@ -148,8 +149,8 @@ pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
                     autofocus: "true",
                     onfocusout: move |_| is_editing.set(false),
                     onkeydown: move |evt| {
-                        match evt.key.as_str() {
-                            "Enter" | "Escape" | "Tab" => is_editing.set(false),
+                        match evt.key() {
+                            Key::Enter | Key::Escape | Key::Tab => is_editing.set(false),
                             _ => {}
                         }
                     },

+ 85 - 0
examples/tui_all_events.rs

@@ -0,0 +1,85 @@
+use dioxus::prelude::*;
+use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData};
+use std::sync::Arc;
+
+fn main() {
+    dioxus::tui::launch(app);
+}
+
+#[derive(Debug)]
+enum Event {
+    MouseMove(Arc<MouseData>),
+    MouseClick(Arc<MouseData>),
+    MouseDoubleClick(Arc<MouseData>),
+    MouseDown(Arc<MouseData>),
+    MouseUp(Arc<MouseData>),
+
+    Wheel(Arc<WheelData>),
+
+    KeyDown(Arc<KeyboardData>),
+    KeyUp(Arc<KeyboardData>),
+    KeyPress(Arc<KeyboardData>),
+
+    FocusIn(Arc<FocusData>),
+    FocusOut(Arc<FocusData>),
+}
+
+const MAX_EVENTS: usize = 8;
+
+fn app(cx: Scope) -> Element {
+    let events = use_ref(&cx, || Vec::new());
+
+    let events_lock = events.read();
+    let first_index = events_lock.len().saturating_sub(MAX_EVENTS);
+    let events_rendered = events_lock[first_index..].iter().map(|event| {
+        // TUI panics if text overflows (https://github.com/DioxusLabs/dioxus/issues/371)
+        // temporary hack: just trim the strings (and make sure viewport is big enough)
+        // todo: remove
+        let mut trimmed = format!("{event:?}");
+        trimmed.truncate(200);
+        cx.render(rsx!(p { "{trimmed}" }))
+    });
+
+    let log_event = move |event: Event| {
+        events.write().push(event);
+    };
+
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+            div {
+                width: "80%",
+                height: "50%",
+                border_width: "1px",
+                justify_content: "center",
+                align_items: "center",
+                background_color: "hsl(248, 53%, 58%)",
+
+                onmousemove: move |event| log_event(Event::MouseMove(event.data)),
+                onclick: move |event| log_event(Event::MouseClick(event.data)),
+                ondblclick: move |event| log_event(Event::MouseDoubleClick(event.data)),
+                onmousedown: move |event| log_event(Event::MouseDown(event.data)),
+                onmouseup: move |event| log_event(Event::MouseUp(event.data)),
+
+                onwheel: move |event| log_event(Event::Wheel(event.data)),
+
+                onkeydown: move |event| log_event(Event::KeyDown(event.data)),
+                onkeyup: move |event| log_event(Event::KeyUp(event.data)),
+                onkeypress: move |event| log_event(Event::KeyPress(event.data)),
+
+                onfocusin: move |event| log_event(Event::FocusIn(event.data)),
+                onfocusout: move |event| log_event(Event::FocusOut(event.data)),
+
+                "Hover, click, type or scroll to see the info down below"
+            },
+            div {
+                width: "80%",
+                height: "50%",
+                flex_direction: "column",
+                events_rendered,
+            },
+        },
+    })
+}

+ 1 - 1
examples/tui_border.rs

@@ -14,7 +14,7 @@ fn app(cx: Scope) -> Element {
             justify_content: "center",
             align_items: "center",
             background_color: "hsl(248, 53%, 58%)",
-            onwheel: move |w| radius.modify(|r| (r + w.delta_y as i8).abs()),
+            onwheel: move |w| radius.modify(|r| (r + w.delta().strip_units().y as i8).abs()),
 
             border_style: "solid none solid double",
             border_width: "thick",

+ 3 - 2
examples/tui_buttons.rs

@@ -1,4 +1,5 @@
-use dioxus::{events::KeyCode, prelude::*};
+use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Code;
 
 fn main() {
     dioxus::tui::launch(app);
@@ -27,7 +28,7 @@ fn Button(cx: Scope<ButtonProps>) -> Element {
             background_color: "{color}",
             tabindex: "{cx.props.layer}",
             onkeydown: |e| {
-                if let KeyCode::Space = e.data.key_code{
+                if let Code::Space = e.data.code() {
                     toggle.modify(|f| !f);
                 }
             },

+ 4 - 4
examples/tui_hover.rs

@@ -65,7 +65,7 @@ fn app(cx: Scope) -> Element {
                     onmouseenter: move |m| q1_color.set([get_brightness(m.data), 0, 0]),
                     onmousedown: move |m| q1_color.set([get_brightness(m.data), 0, 0]),
                     onmouseup: move |m| q1_color.set([get_brightness(m.data), 0, 0]),
-                    onwheel: move |w| q1_color.set([q1_color[0] + (10.0*w.delta_y) as i32, 0, 0]),
+                    onwheel: move |w| q1_color.set([q1_color[0] + (10.0*w.delta().strip_units().y) as i32, 0, 0]),
                     onmouseleave: move |_| q1_color.set([200; 3]),
                     onmousemove: update_data,
                     "click me"
@@ -79,7 +79,7 @@ fn app(cx: Scope) -> Element {
                     onmouseenter: move |m| q2_color.set([get_brightness(m.data); 3]),
                     onmousedown: move |m| q2_color.set([get_brightness(m.data); 3]),
                     onmouseup: move |m| q2_color.set([get_brightness(m.data); 3]),
-                    onwheel: move |w| q2_color.set([q2_color[0] + (10.0*w.delta_y) as i32;3]),
+                    onwheel: move |w| q2_color.set([q2_color[0] + (10.0*w.delta().strip_units().y) as i32;3]),
                     onmouseleave: move |_| q2_color.set([200; 3]),
                     onmousemove: update_data,
                     "click me"
@@ -99,7 +99,7 @@ fn app(cx: Scope) -> Element {
                     onmouseenter: move |m| q3_color.set([0, get_brightness(m.data), 0]),
                     onmousedown: move |m| q3_color.set([0, get_brightness(m.data), 0]),
                     onmouseup: move |m| q3_color.set([0, get_brightness(m.data), 0]),
-                    onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0*w.delta_y) as i32, 0]),
+                    onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0*w.delta().strip_units().y) as i32, 0]),
                     onmouseleave: move |_| q3_color.set([200; 3]),
                     onmousemove: update_data,
                     "click me"
@@ -113,7 +113,7 @@ fn app(cx: Scope) -> Element {
                     onmouseenter: move |m| q4_color.set([0, 0, get_brightness(m.data)]),
                     onmousedown: move |m| q4_color.set([0, 0, get_brightness(m.data)]),
                     onmouseup: move |m| q4_color.set([0, 0, get_brightness(m.data)]),
-                    onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0*w.delta_y) as i32]),
+                    onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0*w.delta().strip_units().y) as i32]),
                     onmouseleave: move |_| q4_color.set([200; 3]),
                     onmousemove: update_data,
                     "click me"

+ 18 - 12
examples/tui_keys.rs

@@ -1,9 +1,9 @@
 use dioxus::events::WheelEvent;
 use dioxus::prelude::*;
 use dioxus_html::geometry::ScreenPoint;
+use dioxus_html::input_data::keyboard_types::Code;
 use dioxus_html::input_data::MouseButtonSet;
 use dioxus_html::on::{KeyboardEvent, MouseEvent};
-use dioxus_html::KeyCode;
 
 fn main() {
     dioxus::tui::launch(app);
@@ -16,6 +16,21 @@ fn app(cx: Scope) -> Element {
     let buttons = use_state(&cx, MouseButtonSet::empty);
     let mouse_clicked = use_state(&cx, || false);
 
+    let key_down_handler = move |evt: KeyboardEvent| {
+        match evt.data.code() {
+            Code::ArrowLeft => count.set(count + 1),
+            Code::ArrowRight => count.set(count - 1),
+            Code::ArrowUp => count.set(count + 10),
+            Code::ArrowDown => count.set(count - 10),
+            _ => {}
+        }
+        key.set(format!(
+            "{:?} repeating: {:?}",
+            evt.key(),
+            evt.is_auto_repeating()
+        ));
+    };
+
     cx.render(rsx! {
         div {
             width: "100%",
@@ -24,18 +39,9 @@ fn app(cx: Scope) -> Element {
             justify_content: "center",
             align_items: "center",
             flex_direction: "column",
-            onkeydown: move |evt: KeyboardEvent| {
-                match evt.data.key_code {
-                    KeyCode::LeftArrow => count.set(count + 1),
-                    KeyCode::RightArrow => count.set(count - 1),
-                    KeyCode::UpArrow => count.set(count + 10),
-                    KeyCode::DownArrow => count.set(count - 10),
-                    _ => {},
-                }
-                key.set(format!("{:?} repeating: {:?}", evt.key, evt.repeat));
-            },
+            onkeydown: key_down_handler,
             onwheel: move |evt: WheelEvent| {
-                count.set(count + evt.data.delta_y as i64);
+                count.set(count + evt.data.delta().strip_units().y as i64);
             },
             ondrag: move |evt: MouseEvent| {
                 mouse.set(evt.data.screen_coordinates());

+ 1 - 1
examples/tui_text.rs

@@ -12,7 +12,7 @@ fn app(cx: Scope) -> Element {
             width: "100%",
             height: "100%",
             flex_direction: "column",
-            onwheel: move |evt| alpha.set((**alpha + evt.data.delta_y as i64).min(100).max(0)),
+            onwheel: move |evt| alpha.set((**alpha + evt.data.delta().strip_units().y as i64).min(100).max(0)),
 
             p {
                 background_color: "black",

+ 1 - 1
packages/html/Cargo.toml

@@ -40,5 +40,5 @@ features = [
 
 [features]
 default = []
-serialize = ["serde", "serde_repr"]
+serialize = ["serde", "serde_repr", "euclid/serde", "keyboard-types/serde"]
 wasm-bind = ["web-sys", "wasm-bindgen"]

+ 195 - 44
packages/html/src/events.rs

@@ -5,12 +5,20 @@ use dioxus_core::*;
 pub mod on {
     //! Input events and associated data
 
-    use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
+    use crate::geometry::{
+        ClientPoint, Coordinates, ElementPoint, LinesVector, PagePoint, PagesVector, PixelsVector,
+        ScreenPoint, WheelDelta,
+    };
     use crate::input_data::{
-        decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet,
+        decode_key_location, decode_mouse_button_set, encode_key_location, encode_mouse_button_set,
+        MouseButton, MouseButtonSet,
     };
-    use keyboard_types::Modifiers;
+    use euclid::UnknownUnit;
+    use keyboard_types::{Code, Key, Location, Modifiers};
     use std::collections::HashMap;
+    use std::convert::TryInto;
+    use std::fmt::{Debug, Formatter};
+    use std::str::FromStr;
 
     use super::*;
     macro_rules! event_directory {
@@ -419,71 +427,142 @@ pub mod on {
 
     pub type KeyboardEvent = UiEvent<KeyboardData>;
     #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-    #[derive(Debug, Clone)]
+    #[derive(Clone)]
     pub struct KeyboardData {
+        #[deprecated(
+            since = "0.3.0",
+            note = "This may not work in all environments. Use key() instead."
+        )]
         pub char_code: u32,
 
         /// Identify which "key" was entered.
-        ///
-        /// This is the best method to use for all languages. They key gets mapped to a String sequence which you can match on.
-        /// The key isn't an enum because there are just so many context-dependent keys.
-        ///
-        /// A full list on which keys to use is available at:
-        /// <https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values>
-        ///
-        /// # Example
-        ///
-        /// ```rust, ignore
-        /// match event.key().as_str() {
-        ///     "Esc" | "Escape" => {}
-        ///     "ArrowDown" => {}
-        ///     "ArrowLeft" => {}
-        ///      _ => {}
-        /// }
-        /// ```
-        ///
+        #[deprecated(since = "0.3.0", note = "use key() instead")]
         pub key: String,
 
         /// Get the key code as an enum Variant.
-        ///
-        /// This is intended for things like arrow keys, escape keys, function keys, and other non-international keys.
-        /// To match on unicode sequences, use the [`KeyboardData::key`] method - this will return a string identifier instead of a limited enum.
-        ///
-        ///
-        /// ## Example
-        ///
-        /// ```rust, ignore
-        /// use dioxus::KeyCode;
-        /// match event.key_code() {
-        ///     KeyCode::Escape => {}
-        ///     KeyCode::LeftArrow => {}
-        ///     KeyCode::RightArrow => {}
-        ///     _ => {}
-        /// }
-        /// ```
-        ///
+        #[deprecated(
+            since = "0.3.0",
+            note = "This may not work in all environments. Use code() instead."
+        )]
         pub key_code: KeyCode,
 
+        /// the physical key on the keyboard
+        code: Code,
+
         /// Indicate if the `alt` modifier key was pressed during this keyboard event
+        #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
         pub alt_key: bool,
 
         /// Indicate if the `ctrl` modifier key was pressed during this keyboard event
+        #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
         pub ctrl_key: bool,
 
         /// Indicate if the `meta` modifier key was pressed during this keyboard event
+        #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
         pub meta_key: bool,
 
         /// Indicate if the `shift` modifier key was pressed during this keyboard event
+        #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
         pub shift_key: bool,
 
-        pub locale: String,
-
+        #[deprecated(since = "0.3.0", note = "use location() instead")]
         pub location: usize,
 
+        #[deprecated(since = "0.3.0", note = "use is_auto_repeating() instead")]
         pub repeat: bool,
 
+        #[deprecated(since = "0.3.0", note = "use code() or key() instead")]
         pub which: usize,
-        // get_modifier_state: bool,
+    }
+
+    impl KeyboardData {
+        pub fn new(
+            key: Key,
+            code: Code,
+            location: Location,
+            is_auto_repeating: bool,
+            modifiers: Modifiers,
+        ) -> Self {
+            #[allow(deprecated)]
+            KeyboardData {
+                char_code: key.legacy_charcode(),
+                key: key.to_string(),
+                key_code: KeyCode::from_raw_code(
+                    key.legacy_keycode()
+                        .try_into()
+                        .expect("could not convert keycode to u8"),
+                ),
+                code,
+                alt_key: modifiers.contains(Modifiers::ALT),
+                ctrl_key: modifiers.contains(Modifiers::CONTROL),
+                meta_key: modifiers.contains(Modifiers::META),
+                shift_key: modifiers.contains(Modifiers::SHIFT),
+                location: encode_key_location(location),
+                repeat: is_auto_repeating,
+                which: key
+                    .legacy_charcode()
+                    .try_into()
+                    .expect("could not convert charcode to usize"),
+            }
+        }
+
+        /// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout.
+        pub fn key(&self) -> Key {
+            #[allow(deprecated)]
+            FromStr::from_str(&self.key).expect("could not parse")
+        }
+
+        /// A physical key on the keyboard (as opposed to the character generated by pressing the key). In other words, this property returns a value that isn't altered by keyboard layout or the state of the modifier keys.
+        pub fn code(&self) -> Code {
+            self.code
+        }
+
+        /// The set of modifier keys which were pressed when the event occurred
+        pub fn modifiers(&self) -> Modifiers {
+            let mut modifiers = Modifiers::empty();
+
+            #[allow(deprecated)]
+            {
+                if self.alt_key {
+                    modifiers.insert(Modifiers::ALT);
+                }
+                if self.ctrl_key {
+                    modifiers.insert(Modifiers::CONTROL);
+                }
+                if self.meta_key {
+                    modifiers.insert(Modifiers::META);
+                }
+                if self.shift_key {
+                    modifiers.insert(Modifiers::SHIFT);
+                }
+            }
+
+            modifiers
+        }
+
+        /// The location of the key on the keyboard or other input device.
+        pub fn location(&self) -> Location {
+            #[allow(deprecated)]
+            decode_key_location(self.location)
+        }
+
+        /// `true` iff the key is being held down such that it is automatically repeating.
+        pub fn is_auto_repeating(&self) -> bool {
+            #[allow(deprecated)]
+            self.repeat
+        }
+    }
+
+    impl Debug for KeyboardData {
+        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+            f.debug_struct("KeyboardData")
+                .field("key", &self.key())
+                .field("code", &self.code())
+                .field("modifiers", &self.modifiers())
+                .field("location", &self.location())
+                .field("is_auto_repeating", &self.is_auto_repeating())
+                .finish()
+        }
     }
 
     pub type FocusEvent = UiEvent<FocusData>;
@@ -502,7 +581,7 @@ pub mod on {
 
     pub type MouseEvent = UiEvent<MouseData>;
     #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-    #[derive(Debug, Clone)]
+    #[derive(Clone)]
     /// Data associated with a mouse event
     ///
     /// Do not use the deprecated fields; they may change or become private in the future.
@@ -687,6 +766,17 @@ pub mod on {
         }
     }
 
+    impl Debug for MouseData {
+        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+            f.debug_struct("MouseData")
+                .field("coordinates", &self.coordinates())
+                .field("modifiers", &self.modifiers())
+                .field("held_buttons", &self.held_buttons())
+                .field("trigger_button", &self.trigger_button())
+                .finish()
+        }
+    }
+
     pub type PointerEvent = UiEvent<PointerData>;
     #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     #[derive(Debug, Clone)]
@@ -738,14 +828,75 @@ pub mod on {
 
     pub type WheelEvent = UiEvent<WheelData>;
     #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-    #[derive(Debug, Clone)]
+    #[derive(Clone)]
     pub struct WheelData {
+        #[deprecated(since = "0.3.0", note = "use delta() instead")]
         pub delta_mode: u32,
+        #[deprecated(since = "0.3.0", note = "use delta() instead")]
         pub delta_x: f64,
+        #[deprecated(since = "0.3.0", note = "use delta() instead")]
         pub delta_y: f64,
+        #[deprecated(since = "0.3.0", note = "use delta() instead")]
         pub delta_z: f64,
     }
 
+    impl WheelData {
+        /// Construct a new WheelData with the specified wheel movement delta
+        pub fn new(delta: WheelDelta) -> Self {
+            let (delta_mode, vector) = match delta {
+                WheelDelta::Pixels(v) => (0, v.cast_unit::<UnknownUnit>()),
+                WheelDelta::Lines(v) => (1, v.cast_unit::<UnknownUnit>()),
+                WheelDelta::Pages(v) => (2, v.cast_unit::<UnknownUnit>()),
+            };
+
+            #[allow(deprecated)]
+            WheelData {
+                delta_mode,
+                delta_x: vector.x,
+                delta_y: vector.y,
+                delta_z: vector.z,
+            }
+        }
+
+        /// Construct from the attributes of the web wheel event
+        pub fn from_web_attributes(
+            delta_mode: u32,
+            delta_x: f64,
+            delta_y: f64,
+            delta_z: f64,
+        ) -> Self {
+            #[allow(deprecated)]
+            Self {
+                delta_mode,
+                delta_x,
+                delta_y,
+                delta_z,
+            }
+        }
+
+        /// The amount of wheel movement
+        #[allow(deprecated)]
+        pub fn delta(&self) -> WheelDelta {
+            let x = self.delta_x;
+            let y = self.delta_y;
+            let z = self.delta_z;
+            match self.delta_mode {
+                0 => WheelDelta::Pixels(PixelsVector::new(x, y, z)),
+                1 => WheelDelta::Lines(LinesVector::new(x, y, z)),
+                2 => WheelDelta::Pages(PagesVector::new(x, y, z)),
+                _ => panic!("Invalid delta mode, {:?}", self.delta_mode),
+            }
+        }
+    }
+
+    impl Debug for WheelData {
+        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+            f.debug_struct("WheelData")
+                .field("delta", &self.delta())
+                .finish()
+        }
+    }
+
     pub type MediaEvent = UiEvent<MediaData>;
     #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     #[derive(Debug, Clone)]

+ 71 - 0
packages/html/src/geometry.rs

@@ -25,7 +25,78 @@ pub struct PageSpace;
 /// A point in PageSpace
 pub type PagePoint = Point2D<f64, PageSpace>;
 
+/// A pixel unit: one unit corresponds to 1 pixel
+pub struct Pixels;
+/// A vector expressed in Pixels
+pub type PixelsVector = Vector3D<f64, Pixels>;
+
+/// A unit in terms of Lines
+///
+/// One unit is relative to the size of one line
+pub struct Lines;
+/// A vector expressed in Lines
+pub type LinesVector = Vector3D<f64, Lines>;
+
+/// A unit in terms of Screens:
+///
+/// One unit is relative to the size of a page
+pub struct Pages;
+/// A vector expressed in Pages
+pub type PagesVector = Vector3D<f64, Pages>;
+
+/// A vector representing the amount the mouse wheel was moved
+///
+/// This may be expressed in Pixels, Lines or Pages
+#[derive(Copy, Clone, Debug)]
+#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
+pub enum WheelDelta {
+    /// Movement in Pixels
+    Pixels(PixelsVector),
+    /// Movement in Lines
+    Lines(LinesVector),
+    /// Movement in Pages
+    Pages(PagesVector),
+}
+
+impl WheelDelta {
+    /// Convenience function for constructing a WheelDelta with pixel units
+    pub fn pixels(x: f64, y: f64, z: f64) -> Self {
+        WheelDelta::Pixels(PixelsVector::new(x, y, z))
+    }
+
+    /// Convenience function for constructing a WheelDelta with line units
+    pub fn lines(x: f64, y: f64, z: f64) -> Self {
+        WheelDelta::Lines(LinesVector::new(x, y, z))
+    }
+
+    /// Convenience function for constructing a WheelDelta with page units
+    pub fn pages(x: f64, y: f64, z: f64) -> Self {
+        WheelDelta::Pages(PagesVector::new(x, y, z))
+    }
+
+    /// Returns true iff there is no wheel movement
+    ///
+    /// i.e. the x, y and z delta is zero (disregards units)
+    pub fn is_zero(&self) -> bool {
+        self.strip_units() == Vector3D::new(0., 0., 0.)
+    }
+
+    /// A Vector3D proportional to the amount scrolled
+    ///
+    /// Note that this disregards the 3 possible units: this could be expressed in terms of pixels, lines, or pages.
+    ///
+    /// In most cases, to properly handle scrolling, you should handle all 3 possible enum variants instead of stripping units. Otherwise, if you assume that the units will always be pixels, the user may experience some unexpectedly slow scrolling if their mouse/OS sends values expressed in lines or pages.
+    pub fn strip_units(&self) -> Vector3D<f64, UnknownUnit> {
+        match self {
+            WheelDelta::Pixels(v) => v.cast_unit(),
+            WheelDelta::Lines(v) => v.cast_unit(),
+            WheelDelta::Pages(v) => v.cast_unit(),
+        }
+    }
+}
+
 /// Coordinates of a point in the app's interface
+#[derive(Debug)]
 pub struct Coordinates {
     screen: ScreenPoint,
     client: ClientPoint,

+ 23 - 0
packages/html/src/input_data.rs

@@ -3,6 +3,7 @@ use enumset::{EnumSet, EnumSetType};
 
 /// A re-export of keyboard_types
 pub use keyboard_types;
+use keyboard_types::Location;
 
 /// A mouse button type (such as Primary/Secondary)
 // note: EnumSetType also derives Copy and Clone for some reason
@@ -118,3 +119,25 @@ pub fn encode_mouse_button_set(set: MouseButtonSet) -> u16 {
 
     code
 }
+
+pub fn decode_key_location(code: usize) -> Location {
+    match code {
+        0 => Location::Standard,
+        1 => Location::Left,
+        2 => Location::Right,
+        3 => Location::Numpad,
+        // keyboard_types doesn't yet support mobile/joystick locations
+        4 | 5 => Location::Standard,
+        // unknown location; Standard seems better than panicking
+        _ => Location::Standard,
+    }
+}
+
+pub fn encode_key_location(location: Location) -> usize {
+    match location {
+        Location::Standard => 0,
+        Location::Left => 1,
+        Location::Right => 2,
+        Location::Numpad => 3,
+    }
+}

+ 30 - 21
packages/html/src/web_sys_bind/events.rs

@@ -1,11 +1,12 @@
 use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
-use crate::input_data::{decode_mouse_button_set, MouseButton};
+use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
 use crate::on::{
     AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
     TransitionData, WheelData,
 };
-use crate::KeyCode;
-use keyboard_types::Modifiers;
+use keyboard_types::{Code, Key, Modifiers};
+use std::convert::TryInto;
+use std::str::FromStr;
 use wasm_bindgen::JsCast;
 use web_sys::{
     AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent,
@@ -56,19 +57,32 @@ impl From<&CompositionEvent> for CompositionData {
 
 impl From<&KeyboardEvent> for KeyboardData {
     fn from(e: &KeyboardEvent) -> Self {
-        Self {
-            alt_key: e.alt_key(),
-            char_code: e.char_code(),
-            key: e.key(),
-            key_code: KeyCode::from_raw_code(e.key_code() as u8),
-            ctrl_key: e.ctrl_key(),
-            locale: "not implemented".to_string(),
-            location: e.location() as usize,
-            meta_key: e.meta_key(),
-            repeat: e.repeat(),
-            shift_key: e.shift_key(),
-            which: e.which() as usize,
+        let mut modifiers = Modifiers::empty();
+
+        if e.alt_key() {
+            modifiers.insert(Modifiers::ALT);
+        }
+        if e.ctrl_key() {
+            modifiers.insert(Modifiers::CONTROL);
         }
+        if e.meta_key() {
+            modifiers.insert(Modifiers::META);
+        }
+        if e.shift_key() {
+            modifiers.insert(Modifiers::SHIFT);
+        }
+
+        Self::new(
+            Key::from_str(&e.key()).expect("could not parse key"),
+            Code::from_str(&e.code()).expect("could not parse code"),
+            decode_key_location(
+                e.location()
+                    .try_into()
+                    .expect("could not convert location to u32"),
+            ),
+            e.repeat(),
+            modifiers,
+        )
     }
 }
 
@@ -146,12 +160,7 @@ impl From<&PointerEvent> for PointerData {
 
 impl From<&WheelEvent> for WheelData {
     fn from(e: &WheelEvent) -> Self {
-        Self {
-            delta_x: e.delta_x(),
-            delta_y: e.delta_y(),
-            delta_z: e.delta_z(),
-            delta_mode: e.delta_mode(),
-        }
+        WheelData::from_web_attributes(e.delta_mode(), e.delta_x(), e.delta_y(), e.delta_z())
     }
 }
 

+ 2 - 1
packages/interpreter/src/interpreter.js

@@ -388,6 +388,7 @@ export function serialize_event(event) {
         location,
         repeat,
         which,
+        code,
       } = event;
       return {
         char_code: charCode,
@@ -400,7 +401,7 @@ export function serialize_event(event) {
         location: location,
         repeat: repeat,
         which: which,
-        locale: "locale",
+        code,
       };
     }
     case "focus":

+ 0 - 1
packages/liveview/src/interpreter.js

@@ -358,7 +358,6 @@ function serialize_event(event) {
         location: location,
         repeat: repeat,
         which: which,
-        locale: "locale",
       };
     }
     case "focus":

+ 3 - 3
packages/router/tests/web_router.rs

@@ -1,9 +1,9 @@
 #![cfg(target_arch = "wasm32")]
+#![allow(non_snake_case)]
 
 use dioxus::prelude::*;
 use dioxus_router::*;
 use gloo_utils::document;
-use serde::{Deserialize, Serialize};
 use wasm_bindgen_test::*;
 
 wasm_bindgen_test_configure!(run_in_browser);
@@ -47,7 +47,7 @@ fn simple_test() {
     }
 
     fn BlogPost(cx: Scope) -> Element {
-        let id = use_route(&cx).parse_segment::<usize>("id")?;
+        let _id = use_route(&cx).parse_segment::<usize>("id")?;
 
         cx.render(rsx! {
             div {
@@ -58,5 +58,5 @@ fn simple_test() {
 
     main();
 
-    let element = gloo_utils::document();
+    let _ = document();
 }

+ 241 - 158
packages/tui/src/hooks.rs

@@ -5,11 +5,13 @@ use dioxus_core::*;
 use fxhash::{FxHashMap, FxHashSet};
 
 use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D};
-use dioxus_html::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
-use dioxus_html::input_data::keyboard_types::Modifiers;
+use dioxus_html::geometry::{
+    ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta,
+};
+use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
 use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
 use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
-use dioxus_html::{event_bubbles, on::*, KeyCode};
+use dioxus_html::{event_bubbles, on::*};
 use std::{
     any::Any,
     cell::{RefCell, RefMut},
@@ -140,14 +142,20 @@ impl InnerInputState {
             EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
             EventData::Screen(ref s) => self.screen = Some(*s),
             EventData::Keyboard(ref mut k) => {
-                let repeat = self
+                let is_repeating = self
                     .last_key_pressed
                     .as_ref()
-                    .filter(|k2| k2.0.key == k.key && k2.1.elapsed() < MAX_REPEAT_TIME)
+                    // heuristic for guessing which presses are auto-repeating. not necessarily accurate
+                    .filter(|(last_data, last_instant)| {
+                        last_data.key() == k.key() && last_instant.elapsed() < MAX_REPEAT_TIME
+                    })
                     .is_some();
-                k.repeat = repeat;
-                let new = k.clone();
-                self.last_key_pressed = Some((new, Instant::now()));
+
+                if is_repeating {
+                    *k = KeyboardData::new(k.key(), k.code(), k.location(), true, k.modifiers());
+                }
+
+                self.last_key_pressed = Some((k.clone(), Instant::now()));
             }
         }
     }
@@ -166,8 +174,10 @@ impl InnerInputState {
         let old_focus = self.focus_state.last_focused_id;
 
         evts.retain(|e| match &e.1 {
-            EventData::Keyboard(k) => match k.key_code {
-                KeyCode::Tab => !self.focus_state.progress(dom, !k.shift_key),
+            EventData::Keyboard(k) => match k.code() {
+                Code::Tab => !self
+                    .focus_state
+                    .progress(dom, !k.modifiers().contains(Modifiers::SHIFT)),
                 _ => true,
             },
             _ => true,
@@ -293,7 +303,10 @@ impl InnerInputState {
             // a mouse button is released if a button was down and is now not down
             let was_released = !(previous_buttons - mouse_data.held_buttons()).is_empty();
 
-            let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y);
+            let was_scrolled = self
+                .wheel
+                .as_ref()
+                .map_or(false, |data| !data.delta().is_zero());
             let wheel_data = &self.wheel;
 
             {
@@ -457,7 +470,7 @@ impl InnerInputState {
             {
                 // wheel
                 if let Some(w) = wheel_data {
-                    if wheel_delta != 0.0 {
+                    if was_scrolled {
                         let mut will_bubble = FxHashSet::default();
                         for node in dom.get_listening_sorted("wheel") {
                             let node_layout = get_abs_layout(node, dom, layout);
@@ -723,13 +736,8 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
             };
 
             let get_wheel_data = |up| {
-                // from https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
-                EventData::Wheel(WheelData {
-                    delta_mode: 0x01,
-                    delta_x: 0.0,
-                    delta_y: if up { -1.0 } else { 1.0 },
-                    delta_z: 0.0,
-                })
+                let y = if up { -1.0 } else { 1.0 };
+                EventData::Wheel(WheelData::new(WheelDelta::lines(0., y, 0.)))
             };
 
             match m.kind {
@@ -748,147 +756,222 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
 }
 
 fn translate_key_event(event: crossterm::event::KeyEvent) -> Option<EventData> {
-    let (code, key_str);
-    let mut shift_key = event.modifiers.contains(KeyModifiers::SHIFT);
-    if let TermKeyCode::Char(c) = event.code {
-        code = match c {
+    let key = key_from_crossterm_key_code(event.code);
+    // crossterm does not provide code. we make a guess as to which key might have been pressed
+    // this is probably garbage if the user has a custom keyboard layout
+    let code = guess_code_from_crossterm_key_code(event.code)?;
+    let modifiers = modifiers_from_crossterm_modifiers(event.modifiers);
+
+    Some(EventData::Keyboard(KeyboardData::new(
+        key,
+        code,
+        Location::Standard,
+        false,
+        modifiers,
+    )))
+}
+
+/// The crossterm key_code nicely represents the meaning of the key and we can mostly convert it without any issues
+///
+/// Exceptions:
+/// BackTab is converted to Key::Tab, and Null is converted to Key::Unidentified
+fn key_from_crossterm_key_code(key_code: TermKeyCode) -> Key {
+    match key_code {
+        TermKeyCode::Backspace => Key::Backspace,
+        TermKeyCode::Enter => Key::Enter,
+        TermKeyCode::Left => Key::ArrowLeft,
+        TermKeyCode::Right => Key::ArrowRight,
+        TermKeyCode::Up => Key::ArrowUp,
+        TermKeyCode::Down => Key::ArrowDown,
+        TermKeyCode::Home => Key::Home,
+        TermKeyCode::End => Key::End,
+        TermKeyCode::PageUp => Key::PageUp,
+        TermKeyCode::PageDown => Key::PageDown,
+        TermKeyCode::Tab => Key::Tab,
+        // ? no corresponding Key
+        TermKeyCode::BackTab => Key::Tab,
+        TermKeyCode::Delete => Key::Delete,
+        TermKeyCode::Insert => Key::Insert,
+        TermKeyCode::F(1) => Key::F1,
+        TermKeyCode::F(2) => Key::F2,
+        TermKeyCode::F(3) => Key::F3,
+        TermKeyCode::F(4) => Key::F4,
+        TermKeyCode::F(5) => Key::F5,
+        TermKeyCode::F(6) => Key::F6,
+        TermKeyCode::F(7) => Key::F7,
+        TermKeyCode::F(8) => Key::F8,
+        TermKeyCode::F(9) => Key::F9,
+        TermKeyCode::F(10) => Key::F10,
+        TermKeyCode::F(11) => Key::F11,
+        TermKeyCode::F(12) => Key::F12,
+        TermKeyCode::F(13) => Key::F13,
+        TermKeyCode::F(14) => Key::F14,
+        TermKeyCode::F(15) => Key::F15,
+        TermKeyCode::F(16) => Key::F16,
+        TermKeyCode::F(17) => Key::F17,
+        TermKeyCode::F(18) => Key::F18,
+        TermKeyCode::F(19) => Key::F19,
+        TermKeyCode::F(20) => Key::F20,
+        TermKeyCode::F(21) => Key::F21,
+        TermKeyCode::F(22) => Key::F22,
+        TermKeyCode::F(23) => Key::F23,
+        TermKeyCode::F(24) => Key::F24,
+        TermKeyCode::F(other) => {
+            panic!("Unexpected function key: {other:?}")
+        }
+        TermKeyCode::Char(c) => Key::Character(c.to_string()),
+        TermKeyCode::Null => Key::Unidentified,
+        TermKeyCode::Esc => Key::Escape,
+    }
+}
+
+// Crossterm does not provide a way to get the `code` (physical key on keyboard)
+// 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
+// It may look fine, but it's a horrible hack. But there's nothing better we can do.
+fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option<Code> {
+    let code = match key_code {
+        TermKeyCode::Backspace => Code::Backspace,
+        TermKeyCode::Enter => Code::Enter,
+        TermKeyCode::Left => Code::ArrowLeft,
+        TermKeyCode::Right => Code::ArrowRight,
+        TermKeyCode::Up => Code::ArrowUp,
+        TermKeyCode::Down => Code::ArrowDown,
+        TermKeyCode::Home => Code::Home,
+        TermKeyCode::End => Code::End,
+        TermKeyCode::PageUp => Code::PageUp,
+        TermKeyCode::PageDown => Code::PageDown,
+        TermKeyCode::Tab => Code::Tab,
+        // ? Apparently you get BackTab by pressing Tab
+        TermKeyCode::BackTab => Code::Tab,
+        TermKeyCode::Delete => Code::Delete,
+        TermKeyCode::Insert => Code::Insert,
+        TermKeyCode::F(1) => Code::F1,
+        TermKeyCode::F(2) => Code::F2,
+        TermKeyCode::F(3) => Code::F3,
+        TermKeyCode::F(4) => Code::F4,
+        TermKeyCode::F(5) => Code::F5,
+        TermKeyCode::F(6) => Code::F6,
+        TermKeyCode::F(7) => Code::F7,
+        TermKeyCode::F(8) => Code::F8,
+        TermKeyCode::F(9) => Code::F9,
+        TermKeyCode::F(10) => Code::F10,
+        TermKeyCode::F(11) => Code::F11,
+        TermKeyCode::F(12) => Code::F12,
+        TermKeyCode::F(13) => Code::F13,
+        TermKeyCode::F(14) => Code::F14,
+        TermKeyCode::F(15) => Code::F15,
+        TermKeyCode::F(16) => Code::F16,
+        TermKeyCode::F(17) => Code::F17,
+        TermKeyCode::F(18) => Code::F18,
+        TermKeyCode::F(19) => Code::F19,
+        TermKeyCode::F(20) => Code::F20,
+        TermKeyCode::F(21) => Code::F21,
+        TermKeyCode::F(22) => Code::F22,
+        TermKeyCode::F(23) => Code::F23,
+        TermKeyCode::F(24) => Code::F24,
+        TermKeyCode::F(other) => {
+            panic!("Unexpected function key: {other:?}")
+        }
+        // this is a horrible way for crossterm to represent keys but we have to deal with it
+        TermKeyCode::Char(c) => match c {
             'A'..='Z' | 'a'..='z' => match c.to_ascii_uppercase() {
-                'A' => KeyCode::A,
-                'B' => KeyCode::B,
-                'C' => KeyCode::C,
-                'D' => KeyCode::D,
-                'E' => KeyCode::E,
-                'F' => KeyCode::F,
-                'G' => KeyCode::G,
-                'H' => KeyCode::H,
-                'I' => KeyCode::I,
-                'J' => KeyCode::J,
-                'K' => KeyCode::K,
-                'L' => KeyCode::L,
-                'M' => KeyCode::M,
-                'N' => KeyCode::N,
-                'O' => KeyCode::O,
-                'P' => KeyCode::P,
-                'Q' => KeyCode::Q,
-                'R' => KeyCode::R,
-                'S' => KeyCode::S,
-                'T' => KeyCode::T,
-                'U' => KeyCode::U,
-                'V' => KeyCode::V,
-                'W' => KeyCode::W,
-                'X' => KeyCode::X,
-                'Y' => KeyCode::Y,
-                'Z' => KeyCode::Z,
-                _ => return None,
-            },
-            ' ' => KeyCode::Space,
-            '[' => KeyCode::OpenBracket,
-            '{' => KeyCode::OpenBracket,
-            ']' => KeyCode::CloseBraket,
-            '}' => KeyCode::CloseBraket,
-            ';' => KeyCode::Semicolon,
-            ':' => KeyCode::Semicolon,
-            ',' => KeyCode::Comma,
-            '<' => KeyCode::Comma,
-            '.' => KeyCode::Period,
-            '>' => KeyCode::Period,
-            '1' => KeyCode::Num1,
-            '2' => KeyCode::Num2,
-            '3' => KeyCode::Num3,
-            '4' => KeyCode::Num4,
-            '5' => KeyCode::Num5,
-            '6' => KeyCode::Num6,
-            '7' => KeyCode::Num7,
-            '8' => KeyCode::Num8,
-            '9' => KeyCode::Num9,
-            '0' => KeyCode::Num0,
-            '!' => KeyCode::Num1,
-            '@' => KeyCode::Num2,
-            '#' => KeyCode::Num3,
-            '$' => KeyCode::Num4,
-            '%' => KeyCode::Num5,
-            '^' => KeyCode::Num6,
-            '&' => KeyCode::Num7,
-            '*' => KeyCode::Num8,
-            '(' => KeyCode::Num9,
-            ')' => KeyCode::Num0,
-            // numpad charicter are ambiguous to tui
-            // '*' => KeyCode::Multiply,
-            // '/' => KeyCode::Divide,
-            // '-' => KeyCode::Subtract,
-            // '+' => KeyCode::Add,
-            '+' => KeyCode::EqualSign,
-            '-' => KeyCode::Dash,
-            '_' => KeyCode::Dash,
-            '\'' => KeyCode::SingleQuote,
-            '"' => KeyCode::SingleQuote,
-            '\\' => KeyCode::BackSlash,
-            '|' => KeyCode::BackSlash,
-            '/' => KeyCode::ForwardSlash,
-            '?' => KeyCode::ForwardSlash,
-            '=' => KeyCode::EqualSign,
-            '`' => KeyCode::GraveAccent,
-            '~' => KeyCode::GraveAccent,
-            _ => return None,
-        };
-        key_str = c.to_string();
-    } else {
-        code = match event.code {
-            TermKeyCode::Esc => KeyCode::Escape,
-            TermKeyCode::Backspace => KeyCode::Backspace,
-            TermKeyCode::Enter => KeyCode::Enter,
-            TermKeyCode::Left => KeyCode::LeftArrow,
-            TermKeyCode::Right => KeyCode::RightArrow,
-            TermKeyCode::Up => KeyCode::UpArrow,
-            TermKeyCode::Down => KeyCode::DownArrow,
-            TermKeyCode::Home => KeyCode::Home,
-            TermKeyCode::End => KeyCode::End,
-            TermKeyCode::PageUp => KeyCode::PageUp,
-            TermKeyCode::PageDown => KeyCode::PageDown,
-            TermKeyCode::Tab => KeyCode::Tab,
-            TermKeyCode::Delete => KeyCode::Delete,
-            TermKeyCode::Insert => KeyCode::Insert,
-            TermKeyCode::F(fn_num) => match fn_num {
-                1 => KeyCode::F1,
-                2 => KeyCode::F2,
-                3 => KeyCode::F3,
-                4 => KeyCode::F4,
-                5 => KeyCode::F5,
-                6 => KeyCode::F6,
-                7 => KeyCode::F7,
-                8 => KeyCode::F8,
-                9 => KeyCode::F9,
-                10 => KeyCode::F10,
-                11 => KeyCode::F11,
-                12 => KeyCode::F12,
-                _ => return None,
+                'A' => Code::KeyA,
+                'B' => Code::KeyB,
+                'C' => Code::KeyC,
+                'D' => Code::KeyD,
+                'E' => Code::KeyE,
+                'F' => Code::KeyF,
+                'G' => Code::KeyG,
+                'H' => Code::KeyH,
+                'I' => Code::KeyI,
+                'J' => Code::KeyJ,
+                'K' => Code::KeyK,
+                'L' => Code::KeyL,
+                'M' => Code::KeyM,
+                'N' => Code::KeyN,
+                'O' => Code::KeyO,
+                'P' => Code::KeyP,
+                'Q' => Code::KeyQ,
+                'R' => Code::KeyR,
+                'S' => Code::KeyS,
+                'T' => Code::KeyT,
+                'U' => Code::KeyU,
+                'V' => Code::KeyV,
+                'W' => Code::KeyW,
+                'X' => Code::KeyX,
+                'Y' => Code::KeyY,
+                'Z' => Code::KeyZ,
+                _ => unreachable!("Exhaustively checked all characters in range A..Z"),
             },
-            // backtab is Shift + Tab
-            TermKeyCode::BackTab => {
-                shift_key = true;
-                KeyCode::Tab
-            }
-            TermKeyCode::Null => return None,
+            ' ' => Code::Space,
+            '[' | '{' => Code::BracketLeft,
+            ']' | '}' => Code::BracketRight,
+            ';' => Code::Semicolon,
+            ':' => Code::Semicolon,
+            ',' => Code::Comma,
+            '<' => Code::Comma,
+            '.' => Code::Period,
+            '>' => Code::Period,
+            '1' => Code::Digit1,
+            '2' => Code::Digit2,
+            '3' => Code::Digit3,
+            '4' => Code::Digit4,
+            '5' => Code::Digit5,
+            '6' => Code::Digit6,
+            '7' => Code::Digit7,
+            '8' => Code::Digit8,
+            '9' => Code::Digit9,
+            '0' => Code::Digit0,
+            '!' => Code::Digit1,
+            '@' => Code::Digit2,
+            '#' => Code::Digit3,
+            '$' => Code::Digit4,
+            '%' => Code::Digit5,
+            '^' => Code::Digit6,
+            '&' => Code::Digit7,
+            '*' => Code::Digit8,
+            '(' => Code::Digit9,
+            ')' => Code::Digit0,
+            // numpad characters are ambiguous; we don't know which key was really pressed
+            // it could be also:
+            // '*' => Code::Multiply,
+            // '/' => Code::Divide,
+            // '-' => Code::Subtract,
+            // '+' => Code::Add,
+            '+' => Code::Equal,
+            '-' | '_' => Code::Minus,
+            '\'' => Code::Quote,
+            '"' => Code::Quote,
+            '\\' => Code::Backslash,
+            '|' => Code::Backslash,
+            '/' => Code::Slash,
+            '?' => Code::Slash,
+            '=' => Code::Equal,
+            '`' => Code::Backquote,
+            '~' => Code::Backquote,
             _ => return None,
-        };
-        key_str = if let KeyCode::BackSlash = code {
-            "\\".to_string()
-        } else {
-            format!("{code:?}")
-        }
+        },
+        TermKeyCode::Null => return None,
+        TermKeyCode::Esc => Code::Escape,
     };
-    // from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
-    Some(EventData::Keyboard(KeyboardData {
-        char_code: code.raw_code(),
-        key: key_str,
-        key_code: code,
-        alt_key: event.modifiers.contains(KeyModifiers::ALT),
-        ctrl_key: event.modifiers.contains(KeyModifiers::CONTROL),
-        meta_key: false,
-        shift_key,
-        locale: Default::default(),
-        location: 0x00,
-        repeat: Default::default(),
-        which: Default::default(),
-    }))
+
+    Some(code)
+}
+
+fn modifiers_from_crossterm_modifiers(src: KeyModifiers) -> Modifiers {
+    let mut modifiers = Modifiers::empty();
+
+    if src.contains(KeyModifiers::SHIFT) {
+        modifiers.insert(Modifiers::SHIFT);
+    }
+
+    if src.contains(KeyModifiers::ALT) {
+        modifiers.insert(Modifiers::ALT);
+    }
+
+    if src.contains(KeyModifiers::CONTROL) {
+        modifiers.insert(Modifiers::CONTROL);
+    }
+
+    modifiers
 }

+ 3 - 2
packages/tui/tests/events.rs

@@ -1,5 +1,6 @@
 use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent};
 use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Code;
 use dioxus_tui::TuiContext;
 use std::future::Future;
 use std::pin::Pin;
@@ -56,7 +57,7 @@ fn key_down() {
                 width: "100%",
                 height: "100%",
                 onkeydown: move |evt| {
-                    assert_eq!(evt.data.key_code, dioxus_html::KeyCode::A);
+                    assert_eq!(evt.data.code(), Code::KeyA);
                     tui_ctx.quit();
                 },
             }
@@ -286,7 +287,7 @@ fn wheel() {
                 width: "100%",
                 height: "100%",
                 onwheel: move |evt| {
-                    assert!(evt.data.delta_y > 0.0);
+                    assert!(evt.data.delta().strip_units().y > 0.0);
                     tui_ctx.quit();
                 },
             }

+ 0 - 1
packages/web/src/olddom.rs

@@ -521,7 +521,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
                 key: evt.key(),
                 key_code: KeyCode::from_raw_code(evt.key_code() as u8),
                 ctrl_key: evt.ctrl_key(),
-                locale: "not implemented".to_string(),
                 location: evt.location() as usize,
                 meta_key: evt.meta_key(),
                 repeat: evt.repeat(),