mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
Merge branch 'master' into jk/autofmt
This commit is contained in:
commit
e627a66acc
73 changed files with 1474 additions and 556 deletions
|
@ -49,10 +49,9 @@ desktop = ["dioxus-desktop"]
|
|||
router = ["dioxus-router"]
|
||||
tui = ["dioxus-tui"]
|
||||
liveview = ["dioxus-liveview"]
|
||||
hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload", "dioxus-web?/hot_reload"]
|
||||
hot-reload = ["dioxus-core-macro/hot-reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot-reload", "dioxus-web?/hot-reload", "dioxus-router?/hot-reload"]
|
||||
native-core = ["dioxus-native-core", "dioxus-native-core-macro"]
|
||||
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"packages/core",
|
||||
|
@ -72,6 +71,7 @@ members = [
|
|||
"packages/rsx_interpreter",
|
||||
"packages/native-core",
|
||||
"packages/native-core-macro",
|
||||
"packages/rsx-prelude",
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -86,7 +86,7 @@ serde_json = "1.0.79"
|
|||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
tokio = { version = "1.16.1", features = ["full"] }
|
||||
reqwest = { version = "0.11.9", features = ["json"] }
|
||||
dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui"] }
|
||||
dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui", "hot-reload"] }
|
||||
fern = { version = "0.6.0", features = ["colored"] }
|
||||
criterion = "0.3.5"
|
||||
thiserror = "1.0.30"
|
||||
|
|
|
@ -13,10 +13,7 @@
|
|||
//! These numbers don't represent Dioxus with the heuristic engine installed, so I assume it'll be even faster.
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use rand::prelude::*;
|
||||
|
||||
criterion_group!(mbenches, create_rows);
|
||||
|
|
|
@ -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() }
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
# Setup
|
||||
Install [dioxus-cli](https://github.com/DioxusLabs/cli).
|
||||
Enable the hot_reload feature on dioxus:
|
||||
Enable the hot-reload feature on dioxus:
|
||||
```toml
|
||||
dioxus = { version = "*", features = ["web", "hot_reload"] }
|
||||
dioxus = { version = "*", features = ["web", "hot-reload"] }
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
|
85
examples/all_events.rs
Normal file
85
examples/all_events.rs
Normal file
|
@ -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 },
|
||||
},
|
||||
))
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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!')"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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}"},
|
||||
}
|
||||
))
|
||||
}
|
|
@ -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),
|
||||
_ => {}
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
examples/tui_all_events.rs
Normal file
85
examples/tui_all_events.rs
Normal file
|
@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -28,4 +28,4 @@ trybuild = "1.0"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
hot_reload = ["dioxus-rsx-interpreter"]
|
||||
hot-reload = ["dioxus-rsx-interpreter"]
|
|
@ -34,12 +34,12 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn rsx(s: TokenStream) -> TokenStream {
|
||||
#[cfg(feature = "hot_reload")]
|
||||
#[cfg(feature = "hot-reload")]
|
||||
let rsx_text = s.to_string();
|
||||
match syn::parse::<rsx::CallBody>(s) {
|
||||
Err(err) => err.to_compile_error().into(),
|
||||
Ok(body) => {
|
||||
#[cfg(feature = "hot_reload")]
|
||||
#[cfg(feature = "hot-reload")]
|
||||
{
|
||||
use dioxus_rsx_interpreter::captuered_context::CapturedContextBuilder;
|
||||
|
||||
|
@ -66,8 +66,7 @@ pub fn rsx(s: TokenStream) -> TokenStream {
|
|||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "hot_reload"))]
|
||||
#[cfg(not(feature = "hot-reload"))]
|
||||
body.to_token_stream().into()
|
||||
}
|
||||
}
|
||||
|
|
29
packages/core-macro/tests/ifmt.rs
Normal file
29
packages/core-macro/tests/ifmt.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use std::borrow::Borrow;
|
||||
|
||||
use dioxus_core_macro::*;
|
||||
|
||||
#[test]
|
||||
fn formatting_compiles() {
|
||||
let x = (0, 1);
|
||||
// escape sequences work
|
||||
assert_eq!(
|
||||
format_args_f!("{x:?} {{}}}}").to_string(),
|
||||
format!("{:?} {{}}}}", x).to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format_args_f!("{{{{}} {x:?}").to_string(),
|
||||
format!("{{{{}} {:?}", x).to_string()
|
||||
);
|
||||
|
||||
// paths in formating works
|
||||
assert_eq!(
|
||||
format_args_f!("{x.0}").to_string(),
|
||||
format!("{}", x.0).to_string()
|
||||
);
|
||||
|
||||
// function calls in formatings work
|
||||
assert_eq!(
|
||||
format_args_f!("{x.borrow():?}").to_string(),
|
||||
format!("{:?}", x.borrow()).to_string()
|
||||
);
|
||||
}
|
|
@ -402,12 +402,12 @@ impl<'b> DiffState<'b> {
|
|||
|
||||
if old.listeners.len() == new.listeners.len() {
|
||||
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
||||
new_l.mounted_node.set(old_l.mounted_node.get());
|
||||
if old_l.event != new_l.event {
|
||||
self.mutations
|
||||
.remove_event_listener(old_l.event, root.as_u64());
|
||||
self.mutations.new_event_listener(new_l, cur_scope_id);
|
||||
}
|
||||
new_l.mounted_node.set(old_l.mounted_node.get());
|
||||
}
|
||||
} else {
|
||||
for listener in old.listeners {
|
||||
|
|
|
@ -63,6 +63,9 @@ pub struct UserEvent {
|
|||
/// The event type IE "onclick" or "onmouseover"
|
||||
pub name: &'static str,
|
||||
|
||||
/// If the event is bubbles up through the vdom
|
||||
pub bubbles: bool,
|
||||
|
||||
/// The event data to be passed onto the event handler
|
||||
pub data: Arc<dyn Any + Send + Sync>,
|
||||
}
|
||||
|
|
|
@ -335,7 +335,7 @@ impl ScopeArena {
|
|||
log::trace!("calling listener {:?}", listener.event);
|
||||
if state.canceled.get() {
|
||||
// stop bubbling if canceled
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut cb = listener.callback.borrow_mut();
|
||||
|
@ -349,6 +349,10 @@ impl ScopeArena {
|
|||
data: event.data.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
if !event.bubbles {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ tokio_runtime = ["tokio"]
|
|||
fullscreen = ["wry/fullscreen"]
|
||||
transparent = ["wry/transparent"]
|
||||
tray = ["wry/tray"]
|
||||
hot_reload = ["dioxus-rsx-interpreter", "interprocess"]
|
||||
hot-reload = ["dioxus-rsx-interpreter", "interprocess"]
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus-core-macro = { path = "../core-macro" }
|
||||
|
|
|
@ -51,7 +51,7 @@ impl DesktopController {
|
|||
dom.base_scope().provide_context(window_context);
|
||||
|
||||
// allow other proccesses to send the new rsx text to the @dioxusin ipc channel and recieve erros on the @dioxusout channel
|
||||
#[cfg(feature = "hot_reload")]
|
||||
#[cfg(feature = "hot-reload")]
|
||||
{
|
||||
use dioxus_rsx_interpreter::{
|
||||
error::Error, ErrorHandler, SetManyRsxMessage, RSX_CONTEXT,
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::any::Any;
|
|||
use std::sync::Arc;
|
||||
|
||||
use dioxus_core::{ElementId, EventPriority, UserEvent};
|
||||
use dioxus_html::event_bubbles;
|
||||
use dioxus_html::on::*;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
|
@ -47,8 +48,8 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
|
|||
} = serde_json::from_value(val).unwrap();
|
||||
|
||||
let mounted_dom_id = Some(ElementId(mounted_dom_id as usize));
|
||||
|
||||
let name = event_name_from_type(&event);
|
||||
|
||||
let event = make_synthetic_event(&event, contents);
|
||||
|
||||
UserEvent {
|
||||
|
@ -56,6 +57,7 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
|
|||
priority: EventPriority::Low,
|
||||
scope_id: None,
|
||||
element: mounted_dom_id,
|
||||
bubbles: event_bubbles(name),
|
||||
data: event,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ It's that simple!
|
|||
Fermi is currently under construction, so you have to use the `master` branch to get started.
|
||||
|
||||
```rust
|
||||
[depdencies]
|
||||
[dependencies]
|
||||
fermi = { git = "https://github.com/dioxuslabs/fermi" }
|
||||
```
|
||||
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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::input_data::{
|
||||
decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet,
|
||||
use crate::geometry::{
|
||||
ClientPoint, Coordinates, ElementPoint, LinesVector, PagePoint, PagesVector, PixelsVector,
|
||||
ScreenPoint, WheelDelta,
|
||||
};
|
||||
use keyboard_types::Modifiers;
|
||||
use crate::input_data::{
|
||||
decode_key_location, decode_mouse_button_set, encode_key_location, encode_mouse_button_set,
|
||||
MouseButton, MouseButtonSet,
|
||||
};
|
||||
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)]
|
||||
|
@ -1324,3 +1475,91 @@ pub(crate) fn _event_meta(event: &UserEvent) -> (bool, EventPriority) {
|
|||
_ => (true, Low),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_bubbles(evt: &str) -> bool {
|
||||
match evt {
|
||||
"copy" => true,
|
||||
"cut" => true,
|
||||
"paste" => true,
|
||||
"compositionend" => true,
|
||||
"compositionstart" => true,
|
||||
"compositionupdate" => true,
|
||||
"keydown" => true,
|
||||
"keypress" => true,
|
||||
"keyup" => true,
|
||||
"focus" => false,
|
||||
"focusout" => true,
|
||||
"focusin" => true,
|
||||
"blur" => false,
|
||||
"change" => true,
|
||||
"input" => true,
|
||||
"invalid" => true,
|
||||
"reset" => true,
|
||||
"submit" => true,
|
||||
"click" => true,
|
||||
"contextmenu" => true,
|
||||
"doubleclick" => true,
|
||||
"dblclick" => true,
|
||||
"drag" => true,
|
||||
"dragend" => true,
|
||||
"dragenter" => false,
|
||||
"dragexit" => false,
|
||||
"dragleave" => true,
|
||||
"dragover" => true,
|
||||
"dragstart" => true,
|
||||
"drop" => true,
|
||||
"mousedown" => true,
|
||||
"mouseenter" => false,
|
||||
"mouseleave" => false,
|
||||
"mousemove" => true,
|
||||
"mouseout" => true,
|
||||
"scroll" => false,
|
||||
"mouseover" => true,
|
||||
"mouseup" => true,
|
||||
"pointerdown" => true,
|
||||
"pointermove" => true,
|
||||
"pointerup" => true,
|
||||
"pointercancel" => true,
|
||||
"gotpointercapture" => true,
|
||||
"lostpointercapture" => true,
|
||||
"pointerenter" => false,
|
||||
"pointerleave" => false,
|
||||
"pointerover" => true,
|
||||
"pointerout" => true,
|
||||
"select" => true,
|
||||
"touchcancel" => true,
|
||||
"touchend" => true,
|
||||
"touchmove" => true,
|
||||
"touchstart" => true,
|
||||
"wheel" => true,
|
||||
"abort" => false,
|
||||
"canplay" => true,
|
||||
"canplaythrough" => true,
|
||||
"durationchange" => true,
|
||||
"emptied" => true,
|
||||
"encrypted" => true,
|
||||
"ended" => true,
|
||||
"error" => false,
|
||||
"loadeddata" => true,
|
||||
"loadedmetadata" => true,
|
||||
"loadstart" => false,
|
||||
"pause" => true,
|
||||
"play" => true,
|
||||
"playing" => true,
|
||||
"progress" => false,
|
||||
"ratechange" => true,
|
||||
"seeked" => true,
|
||||
"seeking" => true,
|
||||
"stalled" => true,
|
||||
"suspend" => true,
|
||||
"timeupdate" => true,
|
||||
"volumechange" => true,
|
||||
"waiting" => true,
|
||||
"animationstart" => true,
|
||||
"animationend" => true,
|
||||
"animationiteration" => true,
|
||||
"transitionend" => true,
|
||||
"toggle" => true,
|
||||
_ => panic!("unsupported event type {:?}", evt),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,10 +48,16 @@ extern "C" {
|
|||
pub fn CreatePlaceholder(this: &Interpreter, root: u64);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn NewEventListener(this: &Interpreter, name: &str, root: u64, handler: &Function);
|
||||
pub fn NewEventListener(
|
||||
this: &Interpreter,
|
||||
name: &str,
|
||||
root: u64,
|
||||
handler: &Function,
|
||||
bubbles: bool,
|
||||
);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn RemoveEventListener(this: &Interpreter, root: u64, name: &str);
|
||||
pub fn RemoveEventListener(this: &Interpreter, root: u64, name: &str, bubbles: bool);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn SetText(this: &Interpreter, root: u64, text: JsValue);
|
||||
|
|
|
@ -5,11 +5,66 @@ export function main() {
|
|||
window.ipc.postMessage(serializeIpcMessage("initialize"));
|
||||
}
|
||||
}
|
||||
|
||||
class ListenerMap {
|
||||
constructor(root) {
|
||||
// bubbling events can listen at the root element
|
||||
this.global = {};
|
||||
// non bubbling events listen at the element the listener was created at
|
||||
this.local = {};
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
create(event_name, element, handler, bubbles) {
|
||||
if (bubbles) {
|
||||
if (this.global[event_name] === undefined) {
|
||||
this.global[event_name] = {};
|
||||
this.global[event_name].active = 1;
|
||||
this.global[event_name].callback = handler;
|
||||
this.root.addEventListener(event_name, handler);
|
||||
} else {
|
||||
this.global[event_name].active++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const id = element.getAttribute("data-dioxus-id");
|
||||
if (!this.local[id]) {
|
||||
this.local[id] = {};
|
||||
}
|
||||
this.local[id][event_name] = handler;
|
||||
element.addEventListener(event_name, handler);
|
||||
}
|
||||
}
|
||||
|
||||
remove(element, event_name, bubbles) {
|
||||
if (bubbles) {
|
||||
this.global[event_name].active--;
|
||||
if (this.global[event_name].active === 0) {
|
||||
this.root.removeEventListener(event_name, this.global[event_name].callback);
|
||||
delete this.global[event_name];
|
||||
}
|
||||
}
|
||||
else {
|
||||
const id = element.getAttribute("data-dioxus-id");
|
||||
delete this.local[id][event_name];
|
||||
if (this.local[id].length === 0) {
|
||||
delete this.local[id];
|
||||
}
|
||||
element.removeEventListener(event_name, handler);
|
||||
}
|
||||
}
|
||||
|
||||
removeAllNonBubbling(element) {
|
||||
const id = element.getAttribute("data-dioxus-id");
|
||||
delete this.local[id];
|
||||
}
|
||||
}
|
||||
|
||||
export class Interpreter {
|
||||
constructor(root) {
|
||||
this.root = root;
|
||||
this.stack = [root];
|
||||
this.listeners = {};
|
||||
this.listeners = new ListenerMap(root);
|
||||
this.handlers = {};
|
||||
this.lastNodeWasText = false;
|
||||
this.nodes = [root];
|
||||
|
@ -40,6 +95,9 @@ export class Interpreter {
|
|||
ReplaceWith(root_id, m) {
|
||||
let root = this.nodes[root_id];
|
||||
let els = this.stack.splice(this.stack.length - m);
|
||||
if (is_element_node(root.nodeType)) {
|
||||
this.listeners.removeAllNonBubbling(root);
|
||||
}
|
||||
root.replaceWith(...els);
|
||||
}
|
||||
InsertAfter(root, n) {
|
||||
|
@ -55,6 +113,9 @@ export class Interpreter {
|
|||
Remove(root) {
|
||||
let node = this.nodes[root];
|
||||
if (node !== undefined) {
|
||||
if (is_element_node(node)) {
|
||||
this.listeners.removeAllNonBubbling(node);
|
||||
}
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
|
@ -79,26 +140,15 @@ export class Interpreter {
|
|||
this.stack.push(el);
|
||||
this.nodes[root] = el;
|
||||
}
|
||||
NewEventListener(event_name, root, handler) {
|
||||
NewEventListener(event_name, root, handler, bubbles) {
|
||||
const element = this.nodes[root];
|
||||
element.setAttribute("data-dioxus-id", `${root}`);
|
||||
if (this.listeners[event_name] === undefined) {
|
||||
this.listeners[event_name] = 1;
|
||||
this.handlers[event_name] = handler;
|
||||
this.root.addEventListener(event_name, handler);
|
||||
} else {
|
||||
this.listeners[event_name]++;
|
||||
}
|
||||
this.listeners.create(event_name, element, handler, bubbles);
|
||||
}
|
||||
RemoveEventListener(root, event_name) {
|
||||
RemoveEventListener(root, event_name, bubbles) {
|
||||
const element = this.nodes[root];
|
||||
element.removeAttribute(`data-dioxus-id`);
|
||||
this.listeners[event_name]--;
|
||||
if (this.listeners[event_name] === 0) {
|
||||
this.root.removeEventListener(event_name, this.handlers[event_name]);
|
||||
delete this.listeners[event_name];
|
||||
delete this.handlers[event_name];
|
||||
}
|
||||
this.listeners.remove(element, event_name, bubbles);
|
||||
}
|
||||
SetText(root, text) {
|
||||
this.nodes[root].textContent = text;
|
||||
|
@ -198,12 +248,9 @@ export class Interpreter {
|
|||
this.RemoveEventListener(edit.root, edit.event_name);
|
||||
break;
|
||||
case "NewEventListener":
|
||||
console.log(this.listeners);
|
||||
|
||||
// this handler is only provided on desktop implementations since this
|
||||
// method is not used by the web implementation
|
||||
let handler = (event) => {
|
||||
console.log(event);
|
||||
|
||||
let target = event.target;
|
||||
if (target != null) {
|
||||
|
@ -292,7 +339,8 @@ export class Interpreter {
|
|||
);
|
||||
}
|
||||
};
|
||||
this.NewEventListener(edit.event_name, edit.root, handler);
|
||||
this.NewEventListener(edit.event_name, edit.root, handler, event_bubbles(edit.event_name));
|
||||
|
||||
break;
|
||||
case "SetText":
|
||||
this.SetText(edit.root, edit.text);
|
||||
|
@ -336,6 +384,7 @@ export function serialize_event(event) {
|
|||
location,
|
||||
repeat,
|
||||
which,
|
||||
code,
|
||||
} = event;
|
||||
return {
|
||||
char_code: charCode,
|
||||
|
@ -348,7 +397,7 @@ export function serialize_event(event) {
|
|||
location: location,
|
||||
repeat: repeat,
|
||||
which: which,
|
||||
locale: "locale",
|
||||
code,
|
||||
};
|
||||
}
|
||||
case "focus":
|
||||
|
@ -607,3 +656,176 @@ const bool_attrs = {
|
|||
selected: true,
|
||||
truespeed: true,
|
||||
};
|
||||
|
||||
function is_element_node(node) {
|
||||
return node.nodeType == 1;
|
||||
}
|
||||
|
||||
function event_bubbles(event) {
|
||||
switch (event) {
|
||||
case "copy":
|
||||
return true;
|
||||
case "cut":
|
||||
return true;
|
||||
case "paste":
|
||||
return true;
|
||||
case "compositionend":
|
||||
return true;
|
||||
case "compositionstart":
|
||||
return true;
|
||||
case "compositionupdate":
|
||||
return true;
|
||||
case "keydown":
|
||||
return true;
|
||||
case "keypress":
|
||||
return true;
|
||||
case "keyup":
|
||||
return true;
|
||||
case "focus":
|
||||
return false;
|
||||
case "focusout":
|
||||
return true;
|
||||
case "focusin":
|
||||
return true;
|
||||
case "blur":
|
||||
return false;
|
||||
case "change":
|
||||
return true;
|
||||
case "input":
|
||||
return true;
|
||||
case "invalid":
|
||||
return true;
|
||||
case "reset":
|
||||
return true;
|
||||
case "submit":
|
||||
return true;
|
||||
case "click":
|
||||
return true;
|
||||
case "contextmenu":
|
||||
return true;
|
||||
case "doubleclick":
|
||||
return true;
|
||||
case "dblclick":
|
||||
return true;
|
||||
case "drag":
|
||||
return true;
|
||||
case "dragend":
|
||||
return true;
|
||||
case "dragenter":
|
||||
return false;
|
||||
case "dragexit":
|
||||
return false;
|
||||
case "dragleave":
|
||||
return true;
|
||||
case "dragover":
|
||||
return true;
|
||||
case "dragstart":
|
||||
return true;
|
||||
case "drop":
|
||||
return true;
|
||||
case "mousedown":
|
||||
return true;
|
||||
case "mouseenter":
|
||||
return false;
|
||||
case "mouseleave":
|
||||
return false;
|
||||
case "mousemove":
|
||||
return true;
|
||||
case "mouseout":
|
||||
return true;
|
||||
case "scroll":
|
||||
return false;
|
||||
case "mouseover":
|
||||
return true;
|
||||
case "mouseup":
|
||||
return true;
|
||||
case "pointerdown":
|
||||
return true;
|
||||
case "pointermove":
|
||||
return true;
|
||||
case "pointerup":
|
||||
return true;
|
||||
case "pointercancel":
|
||||
return true;
|
||||
case "gotpointercapture":
|
||||
return true;
|
||||
case "lostpointercapture":
|
||||
return true;
|
||||
case "pointerenter":
|
||||
return false;
|
||||
case "pointerleave":
|
||||
return false;
|
||||
case "pointerover":
|
||||
return true;
|
||||
case "pointerout":
|
||||
return true;
|
||||
case "select":
|
||||
return true;
|
||||
case "touchcancel":
|
||||
return true;
|
||||
case "touchend":
|
||||
return true;
|
||||
case "touchmove":
|
||||
return true;
|
||||
case "touchstart":
|
||||
return true;
|
||||
case "wheel":
|
||||
return true;
|
||||
case "abort":
|
||||
return false;
|
||||
case "canplay":
|
||||
return true;
|
||||
case "canplaythrough":
|
||||
return true;
|
||||
case "durationchange":
|
||||
return true;
|
||||
case "emptied":
|
||||
return true;
|
||||
case "encrypted":
|
||||
return true;
|
||||
case "ended":
|
||||
return true;
|
||||
case "error":
|
||||
return false;
|
||||
case "loadeddata":
|
||||
return true;
|
||||
case "loadedmetadata":
|
||||
return true;
|
||||
case "loadstart":
|
||||
return false;
|
||||
case "pause":
|
||||
return true;
|
||||
case "play":
|
||||
return true;
|
||||
case "playing":
|
||||
return true;
|
||||
case "progress":
|
||||
return false;
|
||||
case "ratechange":
|
||||
return true;
|
||||
case "seeked":
|
||||
return true;
|
||||
case "seeking":
|
||||
return true;
|
||||
case "stalled":
|
||||
return true;
|
||||
case "suspend":
|
||||
return true;
|
||||
case "timeupdate":
|
||||
return true;
|
||||
case "volumechange":
|
||||
return true;
|
||||
case "waiting":
|
||||
return true;
|
||||
case "animationstart":
|
||||
return true;
|
||||
case "animationend":
|
||||
return true;
|
||||
case "animationiteration":
|
||||
return true;
|
||||
case "transitionend":
|
||||
return true;
|
||||
case "toggle":
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use std::any::Any;
|
|||
use std::sync::Arc;
|
||||
|
||||
use dioxus_core::{ElementId, EventPriority, UserEvent};
|
||||
use dioxus_html::event_bubbles;
|
||||
use dioxus_html::on::*;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
|
@ -46,6 +47,7 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
|
|||
scope_id: None,
|
||||
element: mounted_dom_id,
|
||||
data: event,
|
||||
bubbles: event_bubbles(name),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -358,7 +358,6 @@ function serialize_event(event) {
|
|||
location: location,
|
||||
repeat: repeat,
|
||||
which: which,
|
||||
locale: "locale",
|
||||
};
|
||||
}
|
||||
case "focus":
|
||||
|
|
|
@ -20,9 +20,7 @@ quote = "1.0"
|
|||
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus-core = { path = "../core", version = "^0.2.1" }
|
||||
dioxus-html = { path = "../html", version = "^0.2.1" }
|
||||
dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
||||
dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" }
|
||||
|
||||
smallvec = "1.6"
|
||||
fxhash = "0.2"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use dioxus_core::VNode;
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::core as dioxus_core;
|
||||
use dioxus::core::{ElementId, VElement};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_native_core::real_dom::RealDom;
|
||||
use dioxus_native_core::state::State;
|
||||
use dioxus_native_core_macro::State;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use std::cell::Cell;
|
||||
|
||||
use dioxus_core::VNode;
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::core as dioxus_core;
|
||||
use dioxus::core::{ElementId, VElement, VText};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_native_core::real_dom::RealDom;
|
||||
use dioxus_native_core::state::State;
|
||||
use dioxus_native_core_macro::State;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use dioxus::core as dioxus_core;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_native_core::{
|
||||
real_dom::{NodeType, RealDom},
|
||||
state::State,
|
||||
|
@ -11,10 +13,6 @@ struct Empty {}
|
|||
#[test]
|
||||
#[allow(unused_variables)]
|
||||
fn traverse() {
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn Base(cx: Scope) -> Element {
|
||||
rsx!(cx, div {})
|
||||
|
@ -108,11 +106,6 @@ fn traverse() {
|
|||
#[test]
|
||||
#[allow(unused_variables)]
|
||||
fn persist_removes() {
|
||||
use dioxus_core::VNode;
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn Base(cx: Scope) -> Element {
|
||||
rsx!(cx, div {})
|
||||
|
@ -201,10 +194,6 @@ fn persist_removes() {
|
|||
#[test]
|
||||
#[allow(unused_variables)]
|
||||
fn persist_instertions_before() {
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn Base(cx: Scope) -> Element {
|
||||
rsx!(cx, div {})
|
||||
|
@ -271,10 +260,6 @@ fn persist_instertions_before() {
|
|||
#[test]
|
||||
#[allow(unused_variables)]
|
||||
fn persist_instertions_after() {
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn Base(cx: Scope) -> Element {
|
||||
rsx!(cx, div {})
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use anymap::AnyMap;
|
||||
use dioxus_core::AttributeValue;
|
||||
use dioxus_core::VNode;
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::core as dioxus_core;
|
||||
use dioxus::core::{AttributeValue, DomEdit, Mutations};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_native_core::node_ref::*;
|
||||
use dioxus_native_core::real_dom::*;
|
||||
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
|
||||
|
|
|
@ -11,9 +11,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus-core = { path = "../core", version = "^0.2.1", default-features = false }
|
||||
dioxus-html = { path = "../html", version = "^0.2.1", default-features = false }
|
||||
dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
||||
dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" }
|
||||
futures-channel = "0.3.21"
|
||||
url = { version = "2.2.2", default-features = false }
|
||||
|
||||
|
@ -44,6 +42,7 @@ default = ["query"]
|
|||
web = ["web-sys", "gloo-events", "js-sys", "wasm-bindgen"]
|
||||
query = ["serde", "serde_urlencoded"]
|
||||
wasm_test = []
|
||||
hot-reload = ["dioxus/hot-reload"]
|
||||
|
||||
[dev-dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::*;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{use_route, RouterCore};
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::{format_args_f, rsx, Props};
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
|
||||
/// Props for the [`Link`](struct.Link.html) component.
|
||||
#[derive(Props)]
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::Props;
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::use_router;
|
||||
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
use dioxus::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::Props;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
use crate::{RouteContext, RouterCore};
|
||||
|
||||
/// Props for the [`Route`](struct.Route.html) component.
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use crate::ParsedRoute;
|
||||
use crate::{cfg::RouterCfg, RouteEvent, RouterCore};
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use futures_util::stream::StreamExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{ParsedRoute, RouteContext, RouterCore, RouterService};
|
||||
use dioxus_core::{ScopeId, ScopeState};
|
||||
use dioxus::core::{ScopeId, ScopeState};
|
||||
use std::{borrow::Cow, str::FromStr, sync::Arc};
|
||||
use url::Url;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::RouterService;
|
||||
use dioxus_core::ScopeState;
|
||||
use dioxus::core::ScopeState;
|
||||
|
||||
/// This hook provides access to the `RouterService` for the app.
|
||||
pub fn use_router(cx: &ScopeState) -> &RouterService {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// does each window have its own router? probably, lol
|
||||
|
||||
use crate::cfg::RouterCfg;
|
||||
use dioxus_core::ScopeId;
|
||||
use dioxus::core::ScopeId;
|
||||
use futures_channel::mpsc::UnboundedSender;
|
||||
use std::any::Any;
|
||||
use std::{
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::*;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
#![cfg(target_arch = "wasm32")]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
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);
|
||||
|
@ -49,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 {
|
||||
|
@ -60,5 +58,5 @@ fn simple_test() {
|
|||
|
||||
main();
|
||||
|
||||
let element = gloo_utils::document();
|
||||
let _ = document();
|
||||
}
|
||||
|
|
22
packages/rsx-prelude/Cargo.toml
Normal file
22
packages/rsx-prelude/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "rsx-prelude"
|
||||
version = "0.1.0"
|
||||
authors = ["Jonathan Kelley"]
|
||||
edition = "2021"
|
||||
description = "Basic functionality for the dioxus rsx macro."
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/DioxusLabs/dioxus/"
|
||||
homepage = "https://dioxuslabs.com"
|
||||
documentation = "https://dioxuslabs.com"
|
||||
keywords = ["dom", "ui", "gui", "react", "wasm"]
|
||||
rust-version = "1.60.0"
|
||||
|
||||
[dependencies]
|
||||
dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
||||
dioxus-core = { path = "../core", version = "^0.2.1" }
|
||||
dioxus-html = { path = "../html", version = "^0.2.1" }
|
||||
dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "^0.1.0", optional = true }
|
||||
dioxus-hooks = { path = "../hooks", version = "^0.2.1" }
|
||||
|
||||
[features]
|
||||
hot-reload = ["dioxus-core-macro/hot-reload", "dioxus-rsx-interpreter"]
|
30
packages/rsx-prelude/src/lib.rs
Normal file
30
packages/rsx-prelude/src/lib.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
//! This package is meant for internal use within dioxus. It provides a prelude that enables basic components to work.
|
||||
|
||||
pub use dioxus_core as core;
|
||||
|
||||
pub mod hooks {
|
||||
pub use dioxus_hooks::*;
|
||||
}
|
||||
|
||||
pub use hooks::*;
|
||||
|
||||
pub mod events {
|
||||
pub use dioxus_html::{on::*, KeyCode};
|
||||
}
|
||||
|
||||
#[cfg(feature = "hot-reload")]
|
||||
pub use dioxus_rsx_interpreter as rsx_interpreter;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::hooks::*;
|
||||
pub use dioxus_core::prelude::*;
|
||||
pub use dioxus_core_macro::{format_args_f, inline_props, rsx, Props};
|
||||
pub use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||
pub use dioxus_html as dioxus_elements;
|
||||
|
||||
#[cfg(feature = "hot-reload")]
|
||||
pub use dioxus_rsx_interpreter::{
|
||||
captuered_context::{CapturedContext, FormattedArg, IfmtArgs},
|
||||
get_line_num, resolve_scope, CodeLocation, RsxContext,
|
||||
};
|
||||
}
|
|
@ -14,7 +14,7 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
|
|||
let mut expr_counter = 0;
|
||||
for segment in input.segments.iter() {
|
||||
match segment {
|
||||
Segment::Literal(s) => format_literal += s,
|
||||
Segment::Literal(s) => format_literal += &s.replace('{', "{{").replace('}', "}}"),
|
||||
Segment::Formatted {
|
||||
format_args,
|
||||
segment,
|
||||
|
@ -116,6 +116,17 @@ impl FromStr for IfmtInput {
|
|||
current_captured.push(c);
|
||||
}
|
||||
} else {
|
||||
if '}' == c {
|
||||
if let Some(c) = chars.next_if(|c| *c == '}') {
|
||||
current_literal.push(c);
|
||||
continue;
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
Span::call_site(),
|
||||
"unmatched closing '}' in format string",
|
||||
));
|
||||
}
|
||||
}
|
||||
current_literal.push(c);
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +157,6 @@ impl FormattedSegment {
|
|||
return Ok(Self::Ident(ident));
|
||||
}
|
||||
}
|
||||
// if let Ok(expr) = parse_str(&("{".to_string() + input + "}")) {
|
||||
if let Ok(expr) = parse_str(input) {
|
||||
Ok(Self::Expr(Box::new(expr)))
|
||||
} else {
|
||||
|
|
|
@ -21,3 +21,4 @@ dioxus-hooks = { path = "../hooks"}
|
|||
[dev-dependencies]
|
||||
dioxus-core-macro = { path = "../core-macro" }
|
||||
bumpalo = { version = "3.6", features = ["collections", "boxed"] }
|
||||
dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude", features = ["hot-reload"] }
|
|
@ -191,7 +191,7 @@ fn build_node<'a>(
|
|||
None,
|
||||
)),
|
||||
Some(lit) => {
|
||||
let ifmt: IfmtInput = parse_str(&lit.value()).map_err(|err| {
|
||||
let ifmt: IfmtInput = lit.value().parse().map_err(|err| {
|
||||
Error::ParseError(ParseError::new(err, ctx.location.clone()))
|
||||
})?;
|
||||
let key = bump.alloc(resolve_ifmt(&ifmt, &ctx.captured)?);
|
||||
|
|
|
@ -24,7 +24,8 @@ lazy_static! {
|
|||
// the location of the code relative to the current crate based on [std::panic::Location]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CodeLocation {
|
||||
pub file: String,
|
||||
pub crate_path: String,
|
||||
pub file_path: String,
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
}
|
||||
|
@ -83,14 +84,12 @@ macro_rules! get_line_num {
|
|||
() => {{
|
||||
let line = line!();
|
||||
let column = column!();
|
||||
let file = file!();
|
||||
let file_path = file!().to_string();
|
||||
let crate_path = env!("CARGO_MANIFEST_DIR").to_string();
|
||||
|
||||
#[cfg(windows)]
|
||||
let file = env!("CARGO_MANIFEST_DIR").to_string() + "\\" + file!();
|
||||
#[cfg(unix)]
|
||||
let file = env!("CARGO_MANIFEST_DIR").to_string() + "/" + file!();
|
||||
CodeLocation {
|
||||
file: file.to_string(),
|
||||
crate_path,
|
||||
file_path,
|
||||
line: line,
|
||||
column: column,
|
||||
}
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_rsx_interpreter::{
|
||||
captuered_context::{CapturedContext, FormattedArg, IfmtArgs},
|
||||
CodeLocation,
|
||||
};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -16,7 +10,8 @@ fn render_basic() {
|
|||
let dom = VirtualDom::new(Base);
|
||||
let static_vnodes = rsx!(div{"hello world"});
|
||||
let location = CodeLocation {
|
||||
file: String::new(),
|
||||
file_path: String::new(),
|
||||
crate_path: String::new(),
|
||||
line: 0,
|
||||
column: 0,
|
||||
};
|
||||
|
@ -61,7 +56,8 @@ fn render_nested() {
|
|||
}
|
||||
};
|
||||
let location = CodeLocation {
|
||||
file: String::new(),
|
||||
file_path: String::new(),
|
||||
crate_path: String::new(),
|
||||
line: 1,
|
||||
column: 0,
|
||||
};
|
||||
|
@ -112,7 +108,8 @@ fn render_component() {
|
|||
}
|
||||
};
|
||||
let location = CodeLocation {
|
||||
file: String::new(),
|
||||
file_path: String::new(),
|
||||
crate_path: String::new(),
|
||||
line: 2,
|
||||
column: 0,
|
||||
};
|
||||
|
@ -163,7 +160,8 @@ fn render_iterator() {
|
|||
}
|
||||
};
|
||||
let location = CodeLocation {
|
||||
file: String::new(),
|
||||
file_path: String::new(),
|
||||
crate_path: String::new(),
|
||||
line: 3,
|
||||
column: 0,
|
||||
};
|
||||
|
@ -216,7 +214,8 @@ fn render_captured_variable() {
|
|||
}
|
||||
};
|
||||
let location = CodeLocation {
|
||||
file: String::new(),
|
||||
file_path: String::new(),
|
||||
crate_path: String::new(),
|
||||
line: 4,
|
||||
column: 0,
|
||||
};
|
||||
|
@ -267,7 +266,8 @@ fn render_listener() {
|
|||
}
|
||||
};
|
||||
let location = CodeLocation {
|
||||
file: String::new(),
|
||||
file_path: String::new(),
|
||||
crate_path: String::new(),
|
||||
line: 5,
|
||||
column: 0,
|
||||
};
|
||||
|
|
|
@ -15,11 +15,8 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
|
|||
[dependencies]
|
||||
dioxus-core = { path = "../core", version = "^0.2.1", features = ["serialize"] }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus-hooks = { path = "../hooks" }
|
||||
dioxus-html = { path = "../html" }
|
||||
dioxus-core-macro = { path = "../core-macro" }
|
||||
dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" }
|
||||
thiserror = "1.0.23"
|
||||
log = "0.4.13"
|
||||
fern = { version = "0.6.0", features = ["colored"] }
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_ssr::{render_lazy, render_vdom, render_vdom_cfg, SsrConfig, SsrRenderer, TextRenderer};
|
||||
|
||||
static SIMPLE_APP: Component = |cx| {
|
||||
|
|
|
@ -29,6 +29,5 @@ fxhash = "0.2"
|
|||
anymap = "0.12.1"
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
||||
dioxus-hooks = { path = "../hooks", version = "^0.2.1" }
|
||||
dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" }
|
||||
tokio = { version = "1" }
|
|
@ -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::{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,
|
||||
|
@ -187,6 +197,7 @@ impl InnerInputState {
|
|||
name: "focus",
|
||||
element: Some(id),
|
||||
data: Arc::new(FocusData {}),
|
||||
bubbles: event_bubbles("focus"),
|
||||
});
|
||||
resolved_events.push(UserEvent {
|
||||
scope_id: None,
|
||||
|
@ -194,6 +205,7 @@ impl InnerInputState {
|
|||
name: "focusin",
|
||||
element: Some(id),
|
||||
data: Arc::new(FocusData {}),
|
||||
bubbles: event_bubbles("focusin"),
|
||||
});
|
||||
}
|
||||
if let Some(id) = old_focus {
|
||||
|
@ -203,6 +215,7 @@ impl InnerInputState {
|
|||
name: "focusout",
|
||||
element: Some(id),
|
||||
data: Arc::new(FocusData {}),
|
||||
bubbles: event_bubbles("focusout"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +261,7 @@ impl InnerInputState {
|
|||
name,
|
||||
element: Some(node.id),
|
||||
data,
|
||||
bubbles: event_bubbles(name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -289,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;
|
||||
|
||||
{
|
||||
|
@ -453,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);
|
||||
|
@ -649,6 +666,7 @@ impl RinkInputHandler {
|
|||
name: event,
|
||||
element: Some(node.id),
|
||||
data: data.clone(),
|
||||
bubbles: event_bubbles(event),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -718,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 {
|
||||
|
@ -743,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 {
|
||||
'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,
|
||||
},
|
||||
// backtab is Shift + Tab
|
||||
TermKeyCode::BackTab => {
|
||||
shift_key = true;
|
||||
KeyCode::Tab
|
||||
}
|
||||
TermKeyCode::Null => return None,
|
||||
_ => return None,
|
||||
};
|
||||
key_str = if let KeyCode::BackSlash = code {
|
||||
"\\".to_string()
|
||||
} else {
|
||||
format!("{code:?}")
|
||||
}
|
||||
};
|
||||
// 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(),
|
||||
}))
|
||||
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' => 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"),
|
||||
},
|
||||
' ' => 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,
|
||||
},
|
||||
TermKeyCode::Null => return None,
|
||||
TermKeyCode::Esc => Code::Escape,
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,12 +1,32 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent};
|
||||
use dioxus_core::VNode;
|
||||
use dioxus_core::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html::input_data::keyboard_types::Code;
|
||||
use dioxus_tui::TuiContext;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// The tui renderer will look for any event that has occured or any future that has resolved in a loop.
|
||||
/// It will resolve at most one event per loop.
|
||||
/// This future will resolve after a certain number of polls. If the number of polls is greater than the number of events triggered, and the event has not been recieved there is an issue with the event system.
|
||||
struct PollN(usize);
|
||||
impl PollN {
|
||||
fn new(n: usize) -> Self {
|
||||
PollN(n)
|
||||
}
|
||||
}
|
||||
impl Future for PollN {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.0 == 0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.0 -= 1;
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_down() {
|
||||
|
@ -17,12 +37,17 @@ fn key_down() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
panic!("Event was not received");
|
||||
}
|
||||
// focus the element
|
||||
tui_ctx.inject_event(Event::Key(KeyEvent {
|
||||
code: KeyCode::Tab,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}));
|
||||
tui_ctx.inject_event(Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('a'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
|
@ -32,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();
|
||||
},
|
||||
}
|
||||
|
@ -49,7 +74,7 @@ fn mouse_down() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(2).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
@ -83,7 +108,7 @@ fn mouse_up() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
@ -122,7 +147,7 @@ fn mouse_enter() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
@ -161,7 +186,7 @@ fn mouse_exit() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
@ -200,7 +225,7 @@ fn mouse_move() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
@ -239,7 +264,7 @@ fn wheel() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
@ -262,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();
|
||||
},
|
||||
}
|
||||
|
@ -279,7 +304,7 @@ fn click() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
@ -318,7 +343,7 @@ fn context_menu() {
|
|||
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||
let render_count_handle = render_count.clone();
|
||||
cx.spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
PollN::new(3).await;
|
||||
render_count_handle.modify(|x| *x + 1);
|
||||
});
|
||||
if *render_count.get() > 2 {
|
||||
|
|
|
@ -16,7 +16,7 @@ dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] }
|
|||
dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [
|
||||
"web"
|
||||
] }
|
||||
dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "*", optional = true }
|
||||
dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "^0.1.0", optional = true }
|
||||
|
||||
js-sys = "0.3.56"
|
||||
wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] }
|
||||
|
@ -77,10 +77,10 @@ features = [
|
|||
[features]
|
||||
default = ["panic_hook"]
|
||||
panic_hook = ["console_error_panic_hook"]
|
||||
hot_reload = ["dioxus-rsx-interpreter", "web-sys/WebSocket", "web-sys/Location", "web-sys/MessageEvent", "web-sys/console", "serde_json"]
|
||||
hot-reload = ["dioxus-rsx-interpreter", "web-sys/WebSocket", "web-sys/Location", "web-sys/MessageEvent", "web-sys/console", "serde_json"]
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus-core-macro = { path = "../core-macro" }
|
||||
dioxus = { path = "../rsx-prelude", version = "^0.1.0", package = "rsx-prelude" }
|
||||
wasm-bindgen-test = "0.3.29"
|
||||
dioxus-ssr = { path = "../ssr" }
|
||||
wasm-logger = "0.2.0"
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use web_sys::window;
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
//! - Partial delegation?>
|
||||
|
||||
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, UserEvent};
|
||||
use dioxus_html::event_bubbles;
|
||||
use dioxus_interpreter_js::Interpreter;
|
||||
use js_sys::Function;
|
||||
use std::{any::Any, rc::Rc, sync::Arc};
|
||||
|
@ -45,6 +46,7 @@ impl WebsysDom {
|
|||
element: Some(ElementId(id)),
|
||||
scope_id: None,
|
||||
priority: dioxus_core::EventPriority::Medium,
|
||||
bubbles: event.bubbles(),
|
||||
});
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
|
@ -64,6 +66,7 @@ impl WebsysDom {
|
|||
element: None,
|
||||
scope_id: None,
|
||||
priority: dioxus_core::EventPriority::Low,
|
||||
bubbles: event.bubbles(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -121,12 +124,17 @@ impl WebsysDom {
|
|||
event_name, root, ..
|
||||
} => {
|
||||
let handler: &Function = self.handler.as_ref().unchecked_ref();
|
||||
self.interpreter.NewEventListener(event_name, root, handler);
|
||||
self.interpreter.NewEventListener(
|
||||
event_name,
|
||||
root,
|
||||
handler,
|
||||
event_bubbles(event_name),
|
||||
);
|
||||
}
|
||||
|
||||
DomEdit::RemoveEventListener { root, event } => {
|
||||
self.interpreter.RemoveEventListener(root, event)
|
||||
}
|
||||
DomEdit::RemoveEventListener { root, event } => self
|
||||
.interpreter
|
||||
.RemoveEventListener(root, event, event_bubbles(event)),
|
||||
|
||||
DomEdit::RemoveAttribute { root, name, ns } => {
|
||||
self.interpreter.RemoveAttribute(root, name, ns)
|
||||
|
|
|
@ -58,10 +58,9 @@ use std::rc::Rc;
|
|||
|
||||
pub use crate::cfg::WebConfig;
|
||||
pub use crate::util::use_eval;
|
||||
use dioxus::SchedulerMsg;
|
||||
use dioxus::VirtualDom;
|
||||
pub use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::Component;
|
||||
use dioxus_core::SchedulerMsg;
|
||||
use dioxus_core::VirtualDom;
|
||||
use futures_util::FutureExt;
|
||||
|
||||
mod cache;
|
||||
|
@ -217,7 +216,7 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
|
|||
|
||||
let mut work_loop = ric_raf::RafLoop::new();
|
||||
|
||||
#[cfg(feature = "hot_reload")]
|
||||
#[cfg(feature = "hot-reload")]
|
||||
{
|
||||
use dioxus_rsx_interpreter::error::Error;
|
||||
use dioxus_rsx_interpreter::{ErrorHandler, SetManyRsxMessage, RSX_CONTEXT};
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::dom::WebsysDom;
|
||||
use dioxus_core::{VNode, VirtualDom};
|
||||
use dioxus_html::event_bubbles;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Comment, Element, Node, Text};
|
||||
|
||||
|
@ -111,6 +112,7 @@ impl WebsysDom {
|
|||
listener.event,
|
||||
listener.mounted_node.get().unwrap().as_u64(),
|
||||
self.handler.as_ref().unchecked_ref(),
|
||||
event_bubbles(listener.event),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus::prelude::*;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
use web_sys::window;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ pub mod events {
|
|||
|
||||
pub use dioxus_rsx as rsx;
|
||||
|
||||
#[cfg(feature = "hot_reload")]
|
||||
#[cfg(feature = "hot-reload")]
|
||||
pub use dioxus_rsx_interpreter as rsx_interpreter;
|
||||
|
||||
pub mod prelude {
|
||||
|
@ -62,7 +62,7 @@ pub mod prelude {
|
|||
#[cfg(feature = "fermi")]
|
||||
pub use fermi::{use_atom_ref, use_init_atom_root, use_read, use_set, Atom, AtomRef};
|
||||
|
||||
#[cfg(feature = "hot_reload")]
|
||||
#[cfg(feature = "hot-reload")]
|
||||
pub use dioxus_rsx_interpreter::{
|
||||
captuered_context::{CapturedContext, FormattedArg, IfmtArgs},
|
||||
get_line_num, resolve_scope, CodeLocation, RsxContext,
|
||||
|
|
|
@ -756,3 +756,79 @@ fn add_nested_elements() {
|
|||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_listeners() {
|
||||
let vdom = new_dom();
|
||||
|
||||
let (_create, change) = vdom.diff_lazynodes(
|
||||
rsx! {
|
||||
div{}
|
||||
},
|
||||
rsx! {
|
||||
div{
|
||||
onkeyup: |_| {},
|
||||
onkeydown: |_| {},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
change.edits,
|
||||
[
|
||||
NewEventListener { event_name: "keyup", scope: ScopeId(0), root: 1 },
|
||||
NewEventListener { event_name: "keydown", scope: ScopeId(0), root: 1 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_listeners() {
|
||||
let vdom = new_dom();
|
||||
|
||||
let (_create, change) = vdom.diff_lazynodes(
|
||||
rsx! {
|
||||
div{
|
||||
onkeyup: |_| {},
|
||||
onkeydown: |_| {},
|
||||
}
|
||||
},
|
||||
rsx! {
|
||||
div{}
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
change.edits,
|
||||
[
|
||||
RemoveEventListener { event: "keyup", root: 1 },
|
||||
RemoveEventListener { event: "keydown", root: 1 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_listeners() {
|
||||
let vdom = new_dom();
|
||||
|
||||
let (_create, change) = vdom.diff_lazynodes(
|
||||
rsx! {
|
||||
div{
|
||||
onkeydown: |_| {},
|
||||
}
|
||||
},
|
||||
rsx! {
|
||||
div{
|
||||
onkeyup: |_| {},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
change.edits,
|
||||
[
|
||||
RemoveEventListener { root: 1, event: "keydown" },
|
||||
NewEventListener { event_name: "keyup", scope: ScopeId(0), root: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue