diff --git a/README.md b/README.md
index 5e87587e3..9c0a6e69a 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,6 @@ If you know React, then you already know Dioxus.
Desktop |
SSR |
Mobile |
- State |
diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md
index 8060bbe0b..12a0b60cf 100644
--- a/docs/guide/src/SUMMARY.md
+++ b/docs/guide/src/SUMMARY.md
@@ -32,18 +32,12 @@
- [Working with Async](async/index.md)
- [UseFuture](async/use_future.md)
- [UseCoroutine](async/coroutines.md)
-
-
-
+ - [Updating State](async/loading_state.md)
+ - [Tasks](async/asynctasks.md)
diff --git a/docs/guide/src/advanced-guides/custom-renderer.md b/docs/guide/src/advanced-guides/custom-renderer.md
index d2aa40bc9..a8bec614c 100644
--- a/docs/guide/src/advanced-guides/custom-renderer.md
+++ b/docs/guide/src/advanced-guides/custom-renderer.md
@@ -204,7 +204,6 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
fn ctrl_key(&self) -> bool { self.0.ctrl_key() }
fn key(&self) -> String { self.0.key() }
fn key_code(&self) -> usize { self.0.key_code() }
- fn locale(&self) -> String { self.0.locale() }
fn location(&self) -> usize { self.0.location() }
fn meta_key(&self) -> bool { self.0.meta_key() }
fn repeat(&self) -> bool { self.0.repeat() }
diff --git a/docs/guide/src/async/fetching.md b/docs/guide/src/async/fetching.md
deleted file mode 100644
index f4cd29a36..000000000
--- a/docs/guide/src/async/fetching.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Fetching
-
diff --git a/docs/guide/src/async/sockets.md b/docs/guide/src/async/sockets.md
deleted file mode 100644
index 7627a1570..000000000
--- a/docs/guide/src/async/sockets.md
+++ /dev/null
@@ -1 +0,0 @@
-# WebSockets
diff --git a/docs/guide/src/state/channels.md b/docs/guide/src/state/channels.md
deleted file mode 100644
index a3c35c63e..000000000
--- a/docs/guide/src/state/channels.md
+++ /dev/null
@@ -1 +0,0 @@
-# Channels
diff --git a/docs/guide/src/tutorial/advanced_guides.md b/docs/guide/src/tutorial/advanced_guides.md
deleted file mode 100644
index 53d2cb54f..000000000
--- a/docs/guide/src/tutorial/advanced_guides.md
+++ /dev/null
@@ -1 +0,0 @@
-# Advanced Guides
diff --git a/docs/guide/src/tutorial/components.md b/docs/guide/src/tutorial/components.md
deleted file mode 100644
index 82bae6cd7..000000000
--- a/docs/guide/src/tutorial/components.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Defining Components
-
-This section is currently under construction! 🏗
diff --git a/docs/guide/src/tutorial/state.md b/docs/guide/src/tutorial/state.md
deleted file mode 100644
index 6026c7546..000000000
--- a/docs/guide/src/tutorial/state.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Defining State
-
-This section is currently under construction! 🏗
diff --git a/docs/guide/src/tutorial/structure.md b/docs/guide/src/tutorial/structure.md
deleted file mode 100644
index eaf8afe43..000000000
--- a/docs/guide/src/tutorial/structure.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Structuring our app
-
-
-This section is currently under construction! 🏗
diff --git a/docs/guide/src/tutorial/styling.md b/docs/guide/src/tutorial/styling.md
deleted file mode 100644
index b25db14d2..000000000
--- a/docs/guide/src/tutorial/styling.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Styling
-
-
-This section is currently under construction! 🏗
diff --git a/docs/reference/src/SUMMARY.md b/docs/reference/src/SUMMARY.md
index 3918268d3..9f9371b12 100644
--- a/docs/reference/src/SUMMARY.md
+++ b/docs/reference/src/SUMMARY.md
@@ -11,35 +11,5 @@
- [Advanced Guides](guide/index.md)
- [RSX in Depth](guide/rsx_in_depth.md)
- - [Components](guide/components.md)
- - [Props](guide/props.md)
- - [Memoization](guide/memoization.md)
- - [Performance](guide/performance.md)
- - [Testing](guide/testing.md)
- - [Building Elements with NodeFactory](guide/rsx.md)
- - [Custom Elements](guide/custom_elements.md)
- [Custom Renderer](guide/custom_renderer.md)
- - [Server-side components](guide/server_side_components.md)
- - [Bundling and Distributing](guide/bundline.md)
- - [Hot Reloading Rsx](guide/hot_reloading.md)
-
-- [Reference Guide](reference/reference.md)
- - [Anti-patterns](reference/anti.md)
- - [Children](reference/children.md)
- - [Conditional Rendering](reference/conditional.md)
- - [Controlled Inputs](reference/controlled.md)
- - [Custom Elements](reference/custom.md)
- - [Empty Components](reference/empty.md)
- - [Error Handling](reference/error.md)
- - [Fragments](reference/fragments.md)
- - [Global CSS](reference/global.md)
- - [Inline Styles](reference/inline.md)
- - [Iterators](reference/iterators.md)
- - [Listeners](reference/listeners.md)
- - [Memoization](reference/memoization.md)
- - [Node Refs](reference/node.md)
- - [Spread Pattern](reference/spread.md)
- - [State Management](reference/state.md)
- - [Suspense](reference/suspense.md)
- - [task](reference/task.md)
- - [Testing](reference/testing.md)
+ - [Hot Reloading Rsx](guide/hot_reloading.md)
\ No newline at end of file
diff --git a/docs/reference/src/depth/components.md b/docs/reference/src/depth/components.md
deleted file mode 100644
index 0eac4eb52..000000000
--- a/docs/reference/src/depth/components.md
+++ /dev/null
@@ -1 +0,0 @@
-# Components
diff --git a/docs/reference/src/depth/memoization.md b/docs/reference/src/depth/memoization.md
deleted file mode 100644
index 3ed477f68..000000000
--- a/docs/reference/src/depth/memoization.md
+++ /dev/null
@@ -1 +0,0 @@
-# Memoization
diff --git a/docs/reference/src/depth/performance.md b/docs/reference/src/depth/performance.md
deleted file mode 100644
index 6babb314a..000000000
--- a/docs/reference/src/depth/performance.md
+++ /dev/null
@@ -1 +0,0 @@
-# Performance
diff --git a/docs/reference/src/depth/props.md b/docs/reference/src/depth/props.md
deleted file mode 100644
index d4b69bda7..000000000
--- a/docs/reference/src/depth/props.md
+++ /dev/null
@@ -1 +0,0 @@
-# Props
diff --git a/docs/reference/src/depth/rsx.md b/docs/reference/src/depth/rsx.md
deleted file mode 100644
index 1f349a7ef..000000000
--- a/docs/reference/src/depth/rsx.md
+++ /dev/null
@@ -1 +0,0 @@
-# RSX
diff --git a/docs/reference/src/depth/testing.md b/docs/reference/src/depth/testing.md
deleted file mode 100644
index f00b526a9..000000000
--- a/docs/reference/src/depth/testing.md
+++ /dev/null
@@ -1 +0,0 @@
-# Testing
diff --git a/docs/reference/src/depth/topics.md b/docs/reference/src/depth/topics.md
deleted file mode 100644
index 0cf65091f..000000000
--- a/docs/reference/src/depth/topics.md
+++ /dev/null
@@ -1 +0,0 @@
-# Topics in Depth
diff --git a/docs/reference/src/guide/bundline.md b/docs/reference/src/guide/bundline.md
deleted file mode 100644
index 963cf5490..000000000
--- a/docs/reference/src/guide/bundline.md
+++ /dev/null
@@ -1 +0,0 @@
-# Bundling and Distributing
diff --git a/docs/reference/src/guide/components.md b/docs/reference/src/guide/components.md
deleted file mode 100644
index 0eac4eb52..000000000
--- a/docs/reference/src/guide/components.md
+++ /dev/null
@@ -1 +0,0 @@
-# Components
diff --git a/docs/reference/src/guide/custom_elements.md b/docs/reference/src/guide/custom_elements.md
deleted file mode 100644
index 3b24ad9fe..000000000
--- a/docs/reference/src/guide/custom_elements.md
+++ /dev/null
@@ -1 +0,0 @@
-# Custom Elements
diff --git a/docs/reference/src/guide/custom_renderer.md b/docs/reference/src/guide/custom_renderer.md
index 9a73dd7f7..35187e2e3 100644
--- a/docs/reference/src/guide/custom_renderer.md
+++ b/docs/reference/src/guide/custom_renderer.md
@@ -206,7 +206,6 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
ctrl_key: event.ctrl_key(),
meta_key: event.meta_key(),
shift_key: event.shift_key(),
- locale: "".to_string(),
location: event.location(),
repeat: event.repeat(),
which: event.which(),
diff --git a/docs/reference/src/guide/memoization.md b/docs/reference/src/guide/memoization.md
deleted file mode 100644
index 3ed477f68..000000000
--- a/docs/reference/src/guide/memoization.md
+++ /dev/null
@@ -1 +0,0 @@
-# Memoization
diff --git a/docs/reference/src/guide/performance.md b/docs/reference/src/guide/performance.md
deleted file mode 100644
index 6babb314a..000000000
--- a/docs/reference/src/guide/performance.md
+++ /dev/null
@@ -1 +0,0 @@
-# Performance
diff --git a/docs/reference/src/guide/props.md b/docs/reference/src/guide/props.md
deleted file mode 100644
index d4b69bda7..000000000
--- a/docs/reference/src/guide/props.md
+++ /dev/null
@@ -1 +0,0 @@
-# Props
diff --git a/docs/reference/src/guide/rsx.md b/docs/reference/src/guide/rsx.md
deleted file mode 100644
index 189f5934c..000000000
--- a/docs/reference/src/guide/rsx.md
+++ /dev/null
@@ -1 +0,0 @@
-# Building Elements with NodeFactory
diff --git a/docs/reference/src/guide/server_side_components.md b/docs/reference/src/guide/server_side_components.md
deleted file mode 100644
index a8c4f6182..000000000
--- a/docs/reference/src/guide/server_side_components.md
+++ /dev/null
@@ -1 +0,0 @@
-# Server-side components
diff --git a/docs/reference/src/guide/testing.md b/docs/reference/src/guide/testing.md
deleted file mode 100644
index f00b526a9..000000000
--- a/docs/reference/src/guide/testing.md
+++ /dev/null
@@ -1 +0,0 @@
-# Testing
diff --git a/examples/README.md b/examples/README.md
index e1897db8b..732b19ba8 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,189 +1,176 @@
# Examples
-Most of these examples are run through webview so you don't need the Dioxus CLI installed to preview the functionality.
+These examples are fully-fledged mini Dioxus apps.
-These examples are fully-fledged micro apps. They can be ran with the `cargo run --example XYZ`
+You can run them with `cargo run --example EXAMPLE_NAME`. Example:
-| Example | What it does | Status |
-| --------------------------------------------------- | ------------------------------------------- | ------ |
-| [The basics](./basics.rs) | A few basic examples to preview Dioxus | 🛠 |
-| [fine grained reactivity](./signals.rs) | Escape `diffing` by writing values directly | 🛠 |
-| [Global State Management](./statemanagement.rs) | Share state between components | 🛠 |
-| [Virtual Refs]() | Cross-platform imperative elements | 🛠 |
-| [Inline Styles](./inline-styles.rs) | Define styles for elements inline | 🛠 |
-| [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals | ✅ |
-
-These examples are not necessarily meant to be run, but rather serve as a reference for the given functionality.
-
-| Example | What it does | Status |
-| --------------------------------------------------- | ----------------------------------------------- | ------ |
-| [The basics](./basics.rs) | A few basic examples to preview Dioxus | 🛠 |
-| [fine grained reactivity](./signals.rs) | Escape `diffing` by writing values directly | 🛠 |
-| [Global State Management](./statemanagement.rs) | Share state between components | 🛠 |
-| [Virtual Refs]() | Cross-platform imperative elements | 🛠 |
-| [Inline Styles](./inline-styles.rs) | Define styles for elements inline | 🛠 |
-| [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals | ✅ |
-| [Maps/Iterators](./iterators.rs) | Use iterators in the rsx! macro | ✅ |
-| [Render To string](./tostring.rs) | Render a mounted virtualdom to a string | 🛠 |
-| [Component Children](./children.rs) | Pass children into child components | 🛠 |
-| [Function Driven children]() | Pass functions to make VNodes | 🛠 |
-| [Memoization & Borrowed Data](./memo.rs) | Suppress renders, borrow from parents | ✅ |
-| [Fragments](./fragments.rs) | Support root-less element groups | ✅ |
-| [Null/None Components](./empty.rs) | Return nothing! | 🛠 |
-| [Spread Pattern for props](./spreadpattern.rs) | Manually specify and override props | ✅ |
-| [Controlled Inputs](./controlled-inputs.rs) | this does | 🛠 |
-| [Custom Elements]() | Define custom elements | 🛠 |
-| [Web Components]() | Custom elements to interface with WebComponents | 🛠 |
-| [Testing And debugging]() | this does | 🛠 |
-| [Asynchronous Data]() | Using suspense to wait for data | 🛠 |
-| [Fiber/Scheduled Rendering]() | this does | 🛠 |
-| [CSS Compiled Styles]() | this does | 🛠 |
-| [Anti-patterns](./antipatterns.rs) | A collection of discouraged patterns | ✅ |
-| [Complete rsx reference](./rsx_usage.rs) | A complete reference for all rsx! usage | ✅ |
-| [Event Listeners](./listener.rs) | Attach closures to events on elements | ✅ |
-| [Inline Props](./inlineprops.rs) | Using the `#[inline_props]` macro | ✅ |
-| [Eval](./eval.rs) | Evaluate dynamic JavaScript code | ✅ |
-
-
-## Show me some examples!
-
-In our collection of examples, guides, and tutorials, we have:
-- The book (an introductory course)
-- The guide (an in-depth analysis of everything in Dioxus)
-- The reference (a collection of examples with heavy documentation)
-- The general examples
-- The platform-specific examples (web, ssr, desktop, mobile, server)
-
-Here's what a few common tasks look like in Dioxus:
-
-Nested components with children and internal state:
-```rust
-fn App(cx: Scope) -> Element {
- cx.render(rsx!( Toggle { "Toggle me" } ))
-}
-
-#[derive(PartialEq, Props)]
-struct ToggleProps { children: Element }
-
-fn Toggle(cx: Scope) -> Element {
- let mut toggled = use_state(&cx, || false);
- cx.render(rsx!{
- div {
- &cx.props.children
- button { onclick: move |_| toggled.set(true),
- toggled.and_then(|| "On").or_else(|| "Off")
- }
- }
- })
-}
+```shell
+cargo run --example hello_world
```
-Controlled inputs:
-```rust
-fn App(cx: Scope) -> Element {
- let value = use_state(&cx, String::new);
- cx.render(rsx!(
- input {
- "type": "text",
- value: "{value}",
- oninput: move |evt| value.set(evt.value.clone())
- }
- ))
-}
-```
+(Most of these examples are run through webview, so you don't need the Dioxus CLI installed)
-Lists and Conditional rendering:
-```rust
-fn App(cx: Scope) -> Element {
- let list = (0..10).map(|i| {
- rsx!(li { key: "{i}", "Value: {i}" })
- });
+## Basic Features
- let title = match list.len() {
- 0 => rsx!("Not enough"),
- _ => rsx!("Plenty!"),
- };
+[hello_world](./hello_world.rs) - Most basic example
- if should_show {
- cx.render(rsx!(
- title,
- ul { list }
- ))
- } else {
- None
- }
-}
-```
+[readme](./readme.rs) - Counter example from the Readme
-Tiny components:
-```rust
-static App: Component = |cx| rsx!(cx, div {"hello world!"});
-```
+[custom_assets](./custom_assets.rs) - Include images
-Borrowed prop contents:
-```rust
-fn App(cx: Scope) -> Element {
- let name = use_state(&cx, || String::from("example"));
- rsx!(cx, Child { title: name.as_str() })
-}
+[custom_element](./custom_element.rs) - Render webcomponents
-#[derive(Props)]
-struct ChildProps<'a> { title: &'a str }
+[custom_html](./custom_html.rs) - Customize wrapper HTML
-fn Child(cx: Scope) -> Element {
- rsx!(cx, "Hello {cx.props.title}")
-}
-```
+[eval](./eval.rs) - Evaluate JS expressions
-Global State
-```rust
-struct GlobalState { name: String }
+### RSX
-fn App(cx: Scope) -> Element {
- use_provide_shared_state(cx, || GlobalState { name: String::from("Toby") })
- rsx!(cx, Leaf {})
-}
+[rsx_usage](./rsx_usage.rs) - Demo of all RSX features
-fn Leaf(cx: Scope) -> Element {
- let state = use_consume_shared_state::(cx)?;
- rsx!(cx, "Hello {state.name}")
-}
-```
+[xss_safety](./xss_safety.rs) - You can include text without worrying about injections by default
-Router (inspired by Yew-Router)
-```rust
-#[derive(PartialEq, Clone, Hash, Eq, Routable)]
-enum Route {
- #[at("/")]
- Home,
- #[at("/post/{id}")]
- Post(id)
-}
+### Props
-fn App(cx: Scope) -> Element {
- let route = use_router(cx, Route::parse);
- cx.render(rsx!(div {
- match route {
- Route::Home => rsx!( Home {} ),
- Route::Post(id) => rsx!( Post { id: id })
- }
- }))
-}
-```
+[borrowed](./borrowed.rs) - Borrowed props
-Suspense
-```rust
-fn App(cx: Scope) -> Element {
- let doggo = use_suspense(cx,
- || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::().await.unwrap() },
- |response| cx.render(rsx!( img { src: "{response.message}" }))
- );
+[inlineprops](./inlineprops.rs) - Demo of `inline_props` macro
- cx.render(rsx!{
- div {
- "One doggo coming right up:",
- doggo
- }
- })
-}
-```
+[optional_props](./optional_props.rs) - Optional props
+
+### CSS
+
+[all_css](./all_css.rs) - You can specify any CSS attribute
+
+[tailwind](./tailwind.rs) - You can use a library for styling
+
+## Input Handling
+
+[all_events](./all_events.rs) - Basic event handling demo
+
+[filedragdrop](./filedragdrop.rs) - Handle dropped files
+
+[form](./form.rs) - Handle form submission
+
+[inputs](./inputs.rs) - Input values
+
+[nested_listeners](./nested_listeners.rs) - Nested handlers and bubbling
+
+[textarea](textarea.rs) - Text area input
+
+### State Management
+
+[fermi](./fermi.rs) - Fermi library for state management
+
+[pattern_reducer](./pattern_reducer.rs) - The reducer pattern with `use_state`
+
+[rsx_compile_fail](./rsx_compile_fail.rs)
+
+### Async
+
+[login_form](./login_form.rs) - Login endpoint example
+
+[suspense](./suspense.rs) - Render placeholders while data is loading
+
+[tasks](./tasks.rs) - Continuously run future
+
+### SVG
+
+[svg_basic](./svg_basic.rs)
+
+[svg](./svg.rs)
+
+### Performance
+
+[framework_benchmark](./framework_benchmark.rs) - Renders a huge list
+
+> Note: The benchmark should be run in release mode:
+>
+>```shell
+> cargo run --example framework_benchmark --release
+>```
+
+[heavy_compute](./heavy_compute.rs) - How to deal with expensive operations
+
+## Server-side rendering
+
+[ssr](./ssr.rs) - Rendering RSX server-side
+
+[hydration](./hydration.rs) - Pre-rendering with hydration
+
+## Common Patterns
+
+[disabled](./disabled.rs) - Disable buttons conditionally
+
+[error_handle](./error_handle.rs) - Handle errors with early return
+
+## Routing
+
+[flat_router](./flat_router.rs) - Basic, flat route example
+
+[router](./router.rs) - Router example
+
+[link](./link.rs) - Internal, external and custom links
+
+## Platform Features
+
+[window_event](./window_event.rs) - Window decorations, fullscreen, minimization, etc.
+
+[window_zoom](./window_zoom.rs) – Zoom in or out
+
+## Example Apps
+
+[calculator](./calculator.rs) - Simple calculator
+
+[pattern_model](./pattern_model.rs) - Simple calculator, but using a custom struct as the model
+
+[crm](./crm.rs) - Toy multi-page customer management app
+
+[dog_app](./dog_app.rs) - Accesses dog API
+
+[file_explorer](./file_explorer.rs) - File browser that uses `use_ref` to interact with the model
+
+[todomvc](./todomvc.rs) - Todo task list example
+
+## Terminal UI
+
+[tui_border](./tui_border.rs)
+
+[tui_buttons](./tui_buttons.rs)
+
+[tui_color_test](./tui_color_test.rs)
+
+[tui_components](./tui_components.rs)
+
+[tui_frame](./tui_frame.rs)
+
+[tui_hover](./tui_hover.rs)
+
+[tui_keys](./tui_keys.rs)
+
+[tui_list](./tui_list.rs)
+
+[tui_margin](./tui_margin.rs)
+
+[tui_quadrants](./tui_quadrants.rs)
+
+[tui_readme](./tui_readme.rs)
+
+[tui_task](./tui_task.rs)
+
+[tui_text](./tui_text.rs)
+
+# TODO
+Missing Features
+- Fine-grained reactivity
+- Refs - imperative handles to elements
+- Function-driven children: Pass functions to make VNodes
+
+Missing examples
+- Shared state
+- Root-less element groups
+- Spread props
+- Custom elements
+- Component Children: Pass children into child components
+- Render To string: Render a mounted virtualdom to a string
+- Testing and Debugging
\ No newline at end of file
diff --git a/examples/all_events.rs b/examples/all_events.rs
new file mode 100644
index 000000000..5430bdbf5
--- /dev/null
+++ b/examples/all_events.rs
@@ -0,0 +1,85 @@
+use dioxus::prelude::*;
+use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData};
+use std::sync::Arc;
+
+fn main() {
+ dioxus::desktop::launch(app);
+}
+
+#[derive(Debug)]
+enum Event {
+ MouseMove(Arc),
+ MouseClick(Arc),
+ MouseDoubleClick(Arc),
+ MouseDown(Arc),
+ MouseUp(Arc),
+
+ Wheel(Arc),
+
+ KeyDown(Arc),
+ KeyUp(Arc),
+ KeyPress(Arc),
+
+ FocusIn(Arc),
+ FocusOut(Arc),
+}
+
+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 },
+ },
+ ))
+}
diff --git a/examples/calculator.rs b/examples/calculator.rs
index f63b8a754..75da8f1d3 100644
--- a/examples/calculator.rs
+++ b/examples/calculator.rs
@@ -5,6 +5,7 @@ This calculator version uses React-style state management. All state is held as
use dioxus::events::*;
use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Key;
fn main() {
use dioxus::desktop::tao::dpi::LogicalSize;
@@ -29,33 +30,38 @@ fn app(cx: Scope) -> Element {
let input_operator = move |key: &str| val.make_mut().push_str(key);
+ let handle_key_down_event = move |evt: KeyboardEvent| match evt.key() {
+ Key::Backspace => {
+ if !val.len() != 0 {
+ val.make_mut().pop();
+ }
+ }
+ Key::Character(character) => match character.as_str() {
+ "+" => input_operator("+"),
+ "-" => input_operator("-"),
+ "/" => input_operator("/"),
+ "*" => input_operator("*"),
+ "0" => input_digit(0),
+ "1" => input_digit(1),
+ "2" => input_digit(2),
+ "3" => input_digit(3),
+ "4" => input_digit(4),
+ "5" => input_digit(5),
+ "6" => input_digit(6),
+ "7" => input_digit(7),
+ "8" => input_digit(8),
+ "9" => input_digit(9),
+ _ => {}
+ },
+ _ => {}
+ };
+
cx.render(rsx!(
style { [include_str!("./assets/calculator.css")] }
div { id: "wrapper",
div { class: "app",
div { class: "calculator",
- onkeydown: move |evt| match evt.key_code {
- KeyCode::Add => input_operator("+"),
- KeyCode::Subtract => input_operator("-"),
- KeyCode::Divide => input_operator("/"),
- KeyCode::Multiply => input_operator("*"),
- KeyCode::Num0 => input_digit(0),
- KeyCode::Num1 => input_digit(1),
- KeyCode::Num2 => input_digit(2),
- KeyCode::Num3 => input_digit(3),
- KeyCode::Num4 => input_digit(4),
- KeyCode::Num5 => input_digit(5),
- KeyCode::Num6 => input_digit(6),
- KeyCode::Num7 => input_digit(7),
- KeyCode::Num8 => input_digit(8),
- KeyCode::Num9 => input_digit(9),
- KeyCode::Backspace => {
- if !val.len() != 0 {
- val.make_mut().pop();
- }
- }
- _ => {}
- },
+ onkeydown: handle_key_down_event,
div { class: "calculator-display", [val.to_string()] }
div { class: "calculator-keypad",
div { class: "input-keys",
diff --git a/examples/events.rs b/examples/events.rs
deleted file mode 100644
index 0a82e9bd7..000000000
--- a/examples/events.rs
+++ /dev/null
@@ -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!')"
- }
- }
- })
-}
diff --git a/examples/mouse_event.rs b/examples/mouse_event.rs
deleted file mode 100644
index 165d9445c..000000000
--- a/examples/mouse_event.rs
+++ /dev/null
@@ -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| {
- 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}"},
- }
- ))
-}
diff --git a/examples/order.rs b/examples/order.rs
deleted file mode 100644
index fee6f9184..000000000
--- a/examples/order.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-#![allow(non_snake_case)]
-
-//! This example proves that instantly resolving futures don't cause issues
-
-use dioxus::prelude::*;
-
-fn main() {
- dioxus::desktop::launch(App);
-}
-
-fn App(cx: Scope) -> Element {
- cx.render(rsx!(Demo {}))
-}
-
-fn Demo(cx: Scope) -> Element {
- let fut1 = use_future(&cx, (), |_| async move {
- std::thread::sleep(std::time::Duration::from_millis(100));
- 10
- });
-
- cx.render(match fut1.value() {
- Some(value) => {
- let content = format!("content : {:?}", value);
- rsx!(div{ "{content}" })
- }
- None => rsx!(div{"computing!"}),
- })
-}
diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs
index a7b821750..c88bc78fb 100644
--- a/examples/pattern_model.rs
+++ b/examples/pattern_model.rs
@@ -20,6 +20,7 @@
use dioxus::desktop::wry::application::dpi::LogicalSize;
use dioxus::events::*;
use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Key;
fn main() {
dioxus::desktop::launch_cfg(app, |cfg| {
@@ -212,22 +213,26 @@ impl Calculator {
self.waiting_for_operand = true;
}
fn handle_keydown(&mut self, evt: KeyboardEvent) {
- match evt.key_code {
- KeyCode::Backspace => self.backspace(),
- KeyCode::Num0 => self.input_digit(0),
- KeyCode::Num1 => self.input_digit(1),
- KeyCode::Num2 => self.input_digit(2),
- KeyCode::Num3 => self.input_digit(3),
- KeyCode::Num4 => self.input_digit(4),
- KeyCode::Num5 => self.input_digit(5),
- KeyCode::Num6 => self.input_digit(6),
- KeyCode::Num7 => self.input_digit(7),
- KeyCode::Num8 => self.input_digit(8),
- KeyCode::Num9 => self.input_digit(9),
- KeyCode::Add => self.operator = Some(Operator::Add),
- KeyCode::Subtract => self.operator = Some(Operator::Sub),
- KeyCode::Divide => self.operator = Some(Operator::Div),
- KeyCode::Multiply => self.operator = Some(Operator::Mul),
+ match evt.key() {
+ Key::Backspace => self.backspace(),
+ Key::Character(c) => match c.as_str() {
+ "0" => self.input_digit(0),
+ "1" => self.input_digit(1),
+ "2" => self.input_digit(2),
+ "3" => self.input_digit(3),
+ "4" => self.input_digit(4),
+ "5" => self.input_digit(5),
+ "6" => self.input_digit(6),
+ "7" => self.input_digit(7),
+ "8" => self.input_digit(8),
+ "9" => self.input_digit(9),
+ "+" => self.operator = Some(Operator::Add),
+ "-" => self.operator = Some(Operator::Sub),
+ "/" => self.operator = Some(Operator::Div),
+ "*" => self.operator = Some(Operator::Mul),
+ _ => {}
+ },
+
_ => {}
}
}
diff --git a/examples/todomvc.rs b/examples/todomvc.rs
index 99cf67a69..cf9a49410 100644
--- a/examples/todomvc.rs
+++ b/examples/todomvc.rs
@@ -1,4 +1,5 @@
use dioxus::prelude::*;
+use dioxus_elements::input_data::keyboard_types::Key;
fn main() {
dioxus::desktop::launch(app);
@@ -56,7 +57,7 @@ pub fn app(cx: Scope<()>) -> Element {
autofocus: "true",
oninput: move |evt| draft.set(evt.value.clone()),
onkeydown: move |evt| {
- if evt.key == "Enter" && !draft.is_empty() {
+ if evt.key() == Key::Enter && !draft.is_empty() {
todos.make_mut().insert(
**todo_id,
TodoItem {
@@ -148,8 +149,8 @@ pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
autofocus: "true",
onfocusout: move |_| is_editing.set(false),
onkeydown: move |evt| {
- match evt.key.as_str() {
- "Enter" | "Escape" | "Tab" => is_editing.set(false),
+ match evt.key() {
+ Key::Enter | Key::Escape | Key::Tab => is_editing.set(false),
_ => {}
}
},
diff --git a/examples/tui_all_events.rs b/examples/tui_all_events.rs
new file mode 100644
index 000000000..732b62532
--- /dev/null
+++ b/examples/tui_all_events.rs
@@ -0,0 +1,85 @@
+use dioxus::prelude::*;
+use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData};
+use std::sync::Arc;
+
+fn main() {
+ dioxus::tui::launch(app);
+}
+
+#[derive(Debug)]
+enum Event {
+ MouseMove(Arc),
+ MouseClick(Arc),
+ MouseDoubleClick(Arc),
+ MouseDown(Arc),
+ MouseUp(Arc),
+
+ Wheel(Arc),
+
+ KeyDown(Arc),
+ KeyUp(Arc),
+ KeyPress(Arc),
+
+ FocusIn(Arc),
+ FocusOut(Arc),
+}
+
+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,
+ },
+ },
+ })
+}
diff --git a/examples/tui_border.rs b/examples/tui_border.rs
index 041df4a8b..3e1c01849 100644
--- a/examples/tui_border.rs
+++ b/examples/tui_border.rs
@@ -14,7 +14,7 @@ fn app(cx: Scope) -> Element {
justify_content: "center",
align_items: "center",
background_color: "hsl(248, 53%, 58%)",
- onwheel: move |w| radius.modify(|r| (r + w.delta_y as i8).abs()),
+ onwheel: move |w| radius.modify(|r| (r + w.delta().strip_units().y as i8).abs()),
border_style: "solid none solid double",
border_width: "thick",
diff --git a/examples/tui_buttons.rs b/examples/tui_buttons.rs
index 31d2e1ce0..f0c4b3948 100644
--- a/examples/tui_buttons.rs
+++ b/examples/tui_buttons.rs
@@ -1,4 +1,5 @@
-use dioxus::{events::KeyCode, prelude::*};
+use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Code;
fn main() {
dioxus::tui::launch(app);
@@ -27,7 +28,7 @@ fn Button(cx: Scope) -> 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);
}
},
diff --git a/examples/tui_hover.rs b/examples/tui_hover.rs
index 136213c25..5c3b2d832 100644
--- a/examples/tui_hover.rs
+++ b/examples/tui_hover.rs
@@ -65,7 +65,7 @@ fn app(cx: Scope) -> Element {
onmouseenter: move |m| q1_color.set([get_brightness(m.data), 0, 0]),
onmousedown: move |m| q1_color.set([get_brightness(m.data), 0, 0]),
onmouseup: move |m| q1_color.set([get_brightness(m.data), 0, 0]),
- onwheel: move |w| q1_color.set([q1_color[0] + (10.0*w.delta_y) as i32, 0, 0]),
+ onwheel: move |w| q1_color.set([q1_color[0] + (10.0*w.delta().strip_units().y) as i32, 0, 0]),
onmouseleave: move |_| q1_color.set([200; 3]),
onmousemove: update_data,
"click me"
@@ -79,7 +79,7 @@ fn app(cx: Scope) -> Element {
onmouseenter: move |m| q2_color.set([get_brightness(m.data); 3]),
onmousedown: move |m| q2_color.set([get_brightness(m.data); 3]),
onmouseup: move |m| q2_color.set([get_brightness(m.data); 3]),
- onwheel: move |w| q2_color.set([q2_color[0] + (10.0*w.delta_y) as i32;3]),
+ onwheel: move |w| q2_color.set([q2_color[0] + (10.0*w.delta().strip_units().y) as i32;3]),
onmouseleave: move |_| q2_color.set([200; 3]),
onmousemove: update_data,
"click me"
@@ -99,7 +99,7 @@ fn app(cx: Scope) -> Element {
onmouseenter: move |m| q3_color.set([0, get_brightness(m.data), 0]),
onmousedown: move |m| q3_color.set([0, get_brightness(m.data), 0]),
onmouseup: move |m| q3_color.set([0, get_brightness(m.data), 0]),
- onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0*w.delta_y) as i32, 0]),
+ onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0*w.delta().strip_units().y) as i32, 0]),
onmouseleave: move |_| q3_color.set([200; 3]),
onmousemove: update_data,
"click me"
@@ -113,7 +113,7 @@ fn app(cx: Scope) -> Element {
onmouseenter: move |m| q4_color.set([0, 0, get_brightness(m.data)]),
onmousedown: move |m| q4_color.set([0, 0, get_brightness(m.data)]),
onmouseup: move |m| q4_color.set([0, 0, get_brightness(m.data)]),
- onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0*w.delta_y) as i32]),
+ onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0*w.delta().strip_units().y) as i32]),
onmouseleave: move |_| q4_color.set([200; 3]),
onmousemove: update_data,
"click me"
diff --git a/examples/tui_keys.rs b/examples/tui_keys.rs
index 91c42e30e..f947f97d0 100644
--- a/examples/tui_keys.rs
+++ b/examples/tui_keys.rs
@@ -1,9 +1,9 @@
use dioxus::events::WheelEvent;
use dioxus::prelude::*;
use dioxus_html::geometry::ScreenPoint;
+use dioxus_html::input_data::keyboard_types::Code;
use dioxus_html::input_data::MouseButtonSet;
use dioxus_html::on::{KeyboardEvent, MouseEvent};
-use dioxus_html::KeyCode;
fn main() {
dioxus::tui::launch(app);
@@ -16,6 +16,21 @@ fn app(cx: Scope) -> Element {
let buttons = use_state(&cx, MouseButtonSet::empty);
let mouse_clicked = use_state(&cx, || false);
+ let key_down_handler = move |evt: KeyboardEvent| {
+ match evt.data.code() {
+ Code::ArrowLeft => count.set(count + 1),
+ Code::ArrowRight => count.set(count - 1),
+ Code::ArrowUp => count.set(count + 10),
+ Code::ArrowDown => count.set(count - 10),
+ _ => {}
+ }
+ key.set(format!(
+ "{:?} repeating: {:?}",
+ evt.key(),
+ evt.is_auto_repeating()
+ ));
+ };
+
cx.render(rsx! {
div {
width: "100%",
@@ -24,18 +39,9 @@ fn app(cx: Scope) -> Element {
justify_content: "center",
align_items: "center",
flex_direction: "column",
- onkeydown: move |evt: KeyboardEvent| {
- match evt.data.key_code {
- KeyCode::LeftArrow => count.set(count + 1),
- KeyCode::RightArrow => count.set(count - 1),
- KeyCode::UpArrow => count.set(count + 10),
- KeyCode::DownArrow => count.set(count - 10),
- _ => {},
- }
- key.set(format!("{:?} repeating: {:?}", evt.key, evt.repeat));
- },
+ onkeydown: key_down_handler,
onwheel: move |evt: WheelEvent| {
- count.set(count + evt.data.delta_y as i64);
+ count.set(count + evt.data.delta().strip_units().y as i64);
},
ondrag: move |evt: MouseEvent| {
mouse.set(evt.data.screen_coordinates());
diff --git a/examples/tui_text.rs b/examples/tui_text.rs
index b8f04d99a..a2992a8a0 100644
--- a/examples/tui_text.rs
+++ b/examples/tui_text.rs
@@ -12,7 +12,7 @@ fn app(cx: Scope) -> Element {
width: "100%",
height: "100%",
flex_direction: "column",
- onwheel: move |evt| alpha.set((**alpha + evt.data.delta_y as i64).min(100).max(0)),
+ onwheel: move |evt| alpha.set((**alpha + evt.data.delta().strip_units().y as i64).min(100).max(0)),
p {
background_color: "black",
diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs
index ba6afcfe2..c54e88bdd 100644
--- a/packages/core/src/scopes.rs
+++ b/packages/core/src/scopes.rs
@@ -835,10 +835,7 @@ impl ScopeState {
/// }
/// ```
#[allow(clippy::mut_from_ref)]
- pub fn use_hook<'src, State: 'static>(
- &'src self,
- initializer: impl FnOnce(usize) -> State,
- ) -> &'src mut State {
+ pub fn use_hook(&self, initializer: impl FnOnce(usize) -> State) -> &mut State {
let mut vals = self.hook_vals.borrow_mut();
let hook_len = vals.len();
diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs
index 6c0c7feef..717819182 100644
--- a/packages/desktop/src/controller.rs
+++ b/packages/desktop/src/controller.rs
@@ -86,6 +86,8 @@ impl DesktopController {
(serde_json::to_string(&err).unwrap() + "\n").as_bytes(),
)
.unwrap();
+ } else {
+ panic!("{}", err);
}
}
}
diff --git a/packages/hooks/src/use_shared_state.rs b/packages/hooks/src/use_shared_state.rs
index 78ac671c1..2d9f9eb60 100644
--- a/packages/hooks/src/use_shared_state.rs
+++ b/packages/hooks/src/use_shared_state.rs
@@ -60,7 +60,7 @@ impl ProvidedStateInner {
///
///
///
-pub fn use_context<'a, T: 'static>(cx: &'a ScopeState) -> Option> {
+pub fn use_context(cx: &ScopeState) -> Option> {
let state = cx.use_hook(|_| {
let scope_id = cx.scope_id();
let root = cx.consume_context::>();
diff --git a/packages/hooks/src/useref.rs b/packages/hooks/src/useref.rs
index 6a384a5ff..73ef99a59 100644
--- a/packages/hooks/src/useref.rs
+++ b/packages/hooks/src/useref.rs
@@ -110,10 +110,7 @@ use std::{
/// }
/// })
/// ```
-pub fn use_ref<'a, T: 'static>(
- cx: &'a ScopeState,
- initialize_refcell: impl FnOnce() -> T,
-) -> &'a UseRef {
+pub fn use_ref(cx: &ScopeState, initialize_refcell: impl FnOnce() -> T) -> &UseRef {
let hook = cx.use_hook(|_| UseRef {
update: cx.schedule_update(),
value: Rc::new(RefCell::new(initialize_refcell())),
diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs
index 60dc172b3..68440ca52 100644
--- a/packages/hooks/src/usestate.rs
+++ b/packages/hooks/src/usestate.rs
@@ -30,10 +30,10 @@ use std::{
/// ))
/// }
/// ```
-pub fn use_state<'a, T: 'static>(
- cx: &'a ScopeState,
+pub fn use_state(
+ cx: &ScopeState,
initial_state_fn: impl FnOnce() -> T,
-) -> &'a UseState {
+) -> &UseState {
let hook = cx.use_hook(move |_| {
let current_val = Rc::new(initial_state_fn());
let update_callback = cx.schedule_update();
@@ -304,13 +304,13 @@ impl Clone for UseState {
}
}
-impl<'a, T: 'static + Display> std::fmt::Display for UseState {
+impl std::fmt::Display for UseState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.current_val)
}
}
-impl<'a, T: std::fmt::Binary> std::fmt::Binary for UseState {
+impl std::fmt::Binary for UseState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:b}", self.current_val.as_ref())
}
@@ -341,7 +341,7 @@ impl Debug for UseState {
}
}
-impl<'a, T> std::ops::Deref for UseState {
+impl std::ops::Deref for UseState {
type Target = T;
fn deref(&self) -> &Self::Target {
diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml
index ac5cd340f..8464189ea 100644
--- a/packages/html/Cargo.toml
+++ b/packages/html/Cargo.toml
@@ -40,5 +40,5 @@ features = [
[features]
default = []
-serialize = ["serde", "serde_repr"]
+serialize = ["serde", "serde_repr", "euclid/serde", "keyboard-types/serde"]
wasm-bind = ["web-sys", "wasm-bindgen"]
diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs
index 67522e87a..d2fa8d3ba 100644
--- a/packages/html/src/events.rs
+++ b/packages/html/src/events.rs
@@ -5,12 +5,20 @@ use dioxus_core::*;
pub mod on {
//! Input events and associated data
- use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
- use crate::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;
#[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:
- ///
- ///
- /// # 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;
@@ -502,7 +581,7 @@ pub mod on {
pub type MouseEvent = UiEvent;
#[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;
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
@@ -738,14 +828,75 @@ pub mod on {
pub type WheelEvent = UiEvent;
#[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::()),
+ WheelDelta::Lines(v) => (1, v.cast_unit::()),
+ WheelDelta::Pages(v) => (2, v.cast_unit::()),
+ };
+
+ #[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;
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
diff --git a/packages/html/src/geometry.rs b/packages/html/src/geometry.rs
index 5a7c8112e..990ca5b5e 100644
--- a/packages/html/src/geometry.rs
+++ b/packages/html/src/geometry.rs
@@ -25,7 +25,78 @@ pub struct PageSpace;
/// A point in PageSpace
pub type PagePoint = Point2D;
+/// A pixel unit: one unit corresponds to 1 pixel
+pub struct Pixels;
+/// A vector expressed in Pixels
+pub type PixelsVector = Vector3D;
+
+/// 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;
+
+/// 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;
+
+/// 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 {
+ 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,
diff --git a/packages/html/src/input_data.rs b/packages/html/src/input_data.rs
index b5bc73a44..6b994d5f0 100644
--- a/packages/html/src/input_data.rs
+++ b/packages/html/src/input_data.rs
@@ -3,6 +3,7 @@ use enumset::{EnumSet, EnumSetType};
/// A re-export of keyboard_types
pub use keyboard_types;
+use keyboard_types::Location;
/// A mouse button type (such as Primary/Secondary)
// note: EnumSetType also derives Copy and Clone for some reason
@@ -118,3 +119,25 @@ pub fn encode_mouse_button_set(set: MouseButtonSet) -> u16 {
code
}
+
+pub fn decode_key_location(code: usize) -> Location {
+ match code {
+ 0 => Location::Standard,
+ 1 => Location::Left,
+ 2 => Location::Right,
+ 3 => Location::Numpad,
+ // keyboard_types doesn't yet support mobile/joystick locations
+ 4 | 5 => Location::Standard,
+ // unknown location; Standard seems better than panicking
+ _ => Location::Standard,
+ }
+}
+
+pub fn encode_key_location(location: Location) -> usize {
+ match location {
+ Location::Standard => 0,
+ Location::Left => 1,
+ Location::Right => 2,
+ Location::Numpad => 3,
+ }
+}
diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs
index 670b211c0..197eed737 100644
--- a/packages/html/src/web_sys_bind/events.rs
+++ b/packages/html/src/web_sys_bind/events.rs
@@ -1,11 +1,12 @@
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
-use crate::input_data::{decode_mouse_button_set, MouseButton};
+use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
use crate::on::{
AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
TransitionData, WheelData,
};
-use crate::KeyCode;
-use keyboard_types::Modifiers;
+use keyboard_types::{Code, Key, Modifiers};
+use std::convert::TryInto;
+use std::str::FromStr;
use wasm_bindgen::JsCast;
use web_sys::{
AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent,
@@ -56,19 +57,32 @@ impl From<&CompositionEvent> for CompositionData {
impl From<&KeyboardEvent> for KeyboardData {
fn from(e: &KeyboardEvent) -> Self {
- Self {
- alt_key: e.alt_key(),
- char_code: e.char_code(),
- key: e.key(),
- key_code: KeyCode::from_raw_code(e.key_code() as u8),
- ctrl_key: e.ctrl_key(),
- locale: "not implemented".to_string(),
- location: e.location() as usize,
- meta_key: e.meta_key(),
- repeat: e.repeat(),
- shift_key: e.shift_key(),
- which: e.which() as usize,
+ let mut modifiers = Modifiers::empty();
+
+ if e.alt_key() {
+ modifiers.insert(Modifiers::ALT);
}
+ if e.ctrl_key() {
+ modifiers.insert(Modifiers::CONTROL);
+ }
+ if e.meta_key() {
+ modifiers.insert(Modifiers::META);
+ }
+ if e.shift_key() {
+ modifiers.insert(Modifiers::SHIFT);
+ }
+
+ Self::new(
+ Key::from_str(&e.key()).expect("could not parse key"),
+ Code::from_str(&e.code()).expect("could not parse code"),
+ decode_key_location(
+ e.location()
+ .try_into()
+ .expect("could not convert location to u32"),
+ ),
+ e.repeat(),
+ modifiers,
+ )
}
}
@@ -146,12 +160,7 @@ impl From<&PointerEvent> for PointerData {
impl From<&WheelEvent> for WheelData {
fn from(e: &WheelEvent) -> Self {
- Self {
- delta_x: e.delta_x(),
- delta_y: e.delta_y(),
- delta_z: e.delta_z(),
- delta_mode: e.delta_mode(),
- }
+ WheelData::from_web_attributes(e.delta_mode(), e.delta_x(), e.delta_y(), e.delta_z())
}
}
diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js
index 759e83c9f..759774e53 100644
--- a/packages/interpreter/src/interpreter.js
+++ b/packages/interpreter/src/interpreter.js
@@ -15,43 +15,45 @@ class ListenerMap {
this.root = root;
}
- createBubbling(event_name, handler) {
- 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++;
+ 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);
}
}
- createNonBubbling(event_name, element, handler) {
- const id = element.getAttribute("data-dioxus-id");
- if (!this.local[id]) {
- this.local[id] = {};
+ 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];
+ }
}
- this.local[id][event_name] = handler;
- element.addEventListener(event_name, handler);
- }
-
- removeBubbling(event_name) {
- 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);
}
}
- removeNonBubbling(element, event_name) {
- 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];
@@ -93,7 +95,9 @@ export class Interpreter {
ReplaceWith(root_id, m) {
let root = this.nodes[root_id];
let els = this.stack.splice(this.stack.length - m);
- this.listeners.removeAllNonBubbling(root);
+ if (is_element_node(root.nodeType)) {
+ this.listeners.removeAllNonBubbling(root);
+ }
root.replaceWith(...els);
}
InsertAfter(root, n) {
@@ -108,8 +112,10 @@ export class Interpreter {
}
Remove(root) {
let node = this.nodes[root];
- this.listeners.removeAllNonBubbling(node);
if (node !== undefined) {
+ if (is_element_node(node)) {
+ this.listeners.removeAllNonBubbling(node);
+ }
node.remove();
}
}
@@ -137,22 +143,12 @@ export class Interpreter {
NewEventListener(event_name, root, handler, bubbles) {
const element = this.nodes[root];
element.setAttribute("data-dioxus-id", `${root}`);
- if (bubbles) {
- this.listeners.createBubbling(event_name, handler);
- }
- else {
- this.listeners.createNonBubbling(event_name, element, handler);
- }
+ this.listeners.create(event_name, element, handler, bubbles);
}
RemoveEventListener(root, event_name, bubbles) {
const element = this.nodes[root];
element.removeAttribute(`data-dioxus-id`);
- if (bubbles) {
- this.listeners.removeBubbling(event_name)
- }
- else {
- this.listeners.removeNonBubbling(element, event_name);
- }
+ this.listeners.remove(element, event_name, bubbles);
}
SetText(root, text) {
this.nodes[root].textContent = text;
@@ -388,6 +384,7 @@ export function serialize_event(event) {
location,
repeat,
which,
+ code,
} = event;
return {
char_code: charCode,
@@ -400,7 +397,7 @@ export function serialize_event(event) {
location: location,
repeat: repeat,
which: which,
- locale: "locale",
+ code,
};
}
case "focus":
@@ -660,6 +657,10 @@ const bool_attrs = {
truespeed: true,
};
+function is_element_node(node) {
+ return node.nodeType == 1;
+}
+
function event_bubbles(event) {
switch (event) {
case "copy":
diff --git a/packages/liveview/src/interpreter.js b/packages/liveview/src/interpreter.js
index 383f33a5c..f1eec3fde 100644
--- a/packages/liveview/src/interpreter.js
+++ b/packages/liveview/src/interpreter.js
@@ -358,7 +358,6 @@ function serialize_event(event) {
location: location,
repeat: repeat,
which: which,
- locale: "locale",
};
}
case "focus":
diff --git a/packages/router/tests/web_router.rs b/packages/router/tests/web_router.rs
index 94f57e445..8987ef3b5 100644
--- a/packages/router/tests/web_router.rs
+++ b/packages/router/tests/web_router.rs
@@ -1,9 +1,9 @@
#![cfg(target_arch = "wasm32")]
+#![allow(non_snake_case)]
use dioxus::prelude::*;
use dioxus_router::*;
use gloo_utils::document;
-use serde::{Deserialize, Serialize};
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
@@ -47,7 +47,7 @@ fn simple_test() {
}
fn BlogPost(cx: Scope) -> Element {
- let id = use_route(&cx).parse_segment::("id")?;
+ let _id = use_route(&cx).parse_segment::("id")?;
cx.render(rsx! {
div {
@@ -58,5 +58,5 @@ fn simple_test() {
main();
- let element = gloo_utils::document();
+ let _ = document();
}
diff --git a/packages/rsx_interpreter/src/captuered_context.rs b/packages/rsx_interpreter/src/captuered_context.rs
index 8473771e9..665d8ed34 100644
--- a/packages/rsx_interpreter/src/captuered_context.rs
+++ b/packages/rsx_interpreter/src/captuered_context.rs
@@ -113,9 +113,9 @@ impl ToTokens for CapturedContextBuilder {
let expr = segment.to_token_stream();
let as_string = expr.to_string();
let format_expr = if format_args.is_empty() {
- "{".to_string() + format_args + "}"
+ "{".to_string() + &format_args + "}"
} else {
- "{".to_string() + ":" + format_args + "}"
+ "{".to_string() + ":" + &format_args + "}"
};
Some(quote! {
FormattedArg{
diff --git a/packages/rsx_interpreter/src/error.rs b/packages/rsx_interpreter/src/error.rs
index 368d81d6f..82e5d4fb5 100644
--- a/packages/rsx_interpreter/src/error.rs
+++ b/packages/rsx_interpreter/src/error.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
use serde::{Deserialize, Serialize};
use crate::CodeLocation;
@@ -37,3 +39,18 @@ impl ParseError {
ParseError { message, location }
}
}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Error::ParseError(error) => write!(
+ f,
+ "parse error:\n--> at {}:{}:{}\n\t{:?}\n",
+ error.location.file_path, error.location.line, error.location.column, error.message
+ ),
+ Error::RecompileRequiredError(reason) => {
+ write!(f, "recompile required: {:?}\n", reason)
+ }
+ }
+ }
+}
diff --git a/packages/rsx_interpreter/src/interperter.rs b/packages/rsx_interpreter/src/interperter.rs
index 45b89cfc2..8eca45720 100644
--- a/packages/rsx_interpreter/src/interperter.rs
+++ b/packages/rsx_interpreter/src/interperter.rs
@@ -39,7 +39,7 @@ fn resolve_ifmt(ifmt: &IfmtInput, captured: &IfmtArgs) -> Result
}
}
}
- Segment::Literal(lit) => result.push_str(lit),
+ Segment::Literal(lit) => result.push_str(&lit),
}
}
Ok(result)
@@ -126,12 +126,12 @@ fn build_node<'a>(
ElementAttr::AttrExpression { .. }
| ElementAttr::CustomAttrExpression { .. } => {
- let (name, value) = match &attr.attr {
+ let (name, value, span) = match &attr.attr {
ElementAttr::AttrExpression { name, value } => {
- (name.to_string(), value)
+ (name.to_string(), value, name.span())
}
ElementAttr::CustomAttrExpression { name, value } => {
- (name.value(), value)
+ (name.value(), value, name.span())
}
_ => unreachable!(),
};
@@ -151,6 +151,14 @@ fn build_node<'a>(
is_volatile: false,
namespace,
});
+ } else {
+ return Err(Error::ParseError(ParseError::new(
+ syn::Error::new(
+ span,
+ format!("unknown attribute: {}", name),
+ ),
+ ctx.location.clone(),
+ )));
}
} else {
return Err(Error::RecompileRequiredError(
diff --git a/packages/rsx_interpreter/src/lib.rs b/packages/rsx_interpreter/src/lib.rs
index ff50dcdb2..b5e596865 100644
--- a/packages/rsx_interpreter/src/lib.rs
+++ b/packages/rsx_interpreter/src/lib.rs
@@ -150,6 +150,8 @@ impl RsxContext {
fn report_error(&self, error: Error) {
if let Some(handler) = &self.data.write().unwrap().error_handler {
handler.handle_error(error)
+ } else {
+ panic!("no error handler set for this platform...\n{}", error);
}
}
diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs
index 6d633eab8..59aa7a7e3 100644
--- a/packages/tui/src/hooks.rs
+++ b/packages/tui/src/hooks.rs
@@ -5,11 +5,13 @@ use dioxus_core::*;
use fxhash::{FxHashMap, FxHashSet};
use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D};
-use dioxus_html::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
-use dioxus_html::input_data::keyboard_types::Modifiers;
+use dioxus_html::geometry::{
+ ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint, WheelDelta,
+};
+use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
-use dioxus_html::{event_bubbles, on::*, KeyCode};
+use dioxus_html::{event_bubbles, on::*};
use std::{
any::Any,
cell::{RefCell, RefMut},
@@ -140,14 +142,20 @@ impl InnerInputState {
EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
EventData::Screen(ref s) => self.screen = Some(*s),
EventData::Keyboard(ref mut k) => {
- let repeat = self
+ let is_repeating = self
.last_key_pressed
.as_ref()
- .filter(|k2| k2.0.key == k.key && k2.1.elapsed() < MAX_REPEAT_TIME)
+ // heuristic for guessing which presses are auto-repeating. not necessarily accurate
+ .filter(|(last_data, last_instant)| {
+ last_data.key() == k.key() && last_instant.elapsed() < MAX_REPEAT_TIME
+ })
.is_some();
- k.repeat = repeat;
- let new = k.clone();
- self.last_key_pressed = Some((new, Instant::now()));
+
+ if is_repeating {
+ *k = KeyboardData::new(k.key(), k.code(), k.location(), true, k.modifiers());
+ }
+
+ self.last_key_pressed = Some((k.clone(), Instant::now()));
}
}
}
@@ -166,8 +174,10 @@ impl InnerInputState {
let old_focus = self.focus_state.last_focused_id;
evts.retain(|e| match &e.1 {
- EventData::Keyboard(k) => match k.key_code {
- KeyCode::Tab => !self.focus_state.progress(dom, !k.shift_key),
+ EventData::Keyboard(k) => match k.code() {
+ Code::Tab => !self
+ .focus_state
+ .progress(dom, !k.modifiers().contains(Modifiers::SHIFT)),
_ => true,
},
_ => true,
@@ -293,7 +303,10 @@ impl InnerInputState {
// a mouse button is released if a button was down and is now not down
let was_released = !(previous_buttons - mouse_data.held_buttons()).is_empty();
- let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y);
+ let was_scrolled = self
+ .wheel
+ .as_ref()
+ .map_or(false, |data| !data.delta().is_zero());
let wheel_data = &self.wheel;
{
@@ -457,7 +470,7 @@ impl InnerInputState {
{
// wheel
if let Some(w) = wheel_data {
- if wheel_delta != 0.0 {
+ if was_scrolled {
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("wheel") {
let node_layout = get_abs_layout(node, dom, layout);
@@ -723,13 +736,8 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
};
let get_wheel_data = |up| {
- // from https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
- EventData::Wheel(WheelData {
- delta_mode: 0x01,
- delta_x: 0.0,
- delta_y: if up { -1.0 } else { 1.0 },
- delta_z: 0.0,
- })
+ let y = if up { -1.0 } else { 1.0 };
+ EventData::Wheel(WheelData::new(WheelDelta::lines(0., y, 0.)))
};
match m.kind {
@@ -748,147 +756,222 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
}
fn translate_key_event(event: crossterm::event::KeyEvent) -> Option {
- let (code, key_str);
- let mut shift_key = event.modifiers.contains(KeyModifiers::SHIFT);
- if let TermKeyCode::Char(c) = event.code {
- code = match c {
+ let key = key_from_crossterm_key_code(event.code);
+ // crossterm does not provide code. we make a guess as to which key might have been pressed
+ // this is probably garbage if the user has a custom keyboard layout
+ let code = guess_code_from_crossterm_key_code(event.code)?;
+ let modifiers = modifiers_from_crossterm_modifiers(event.modifiers);
+
+ Some(EventData::Keyboard(KeyboardData::new(
+ key,
+ code,
+ Location::Standard,
+ false,
+ modifiers,
+ )))
+}
+
+/// The crossterm key_code nicely represents the meaning of the key and we can mostly convert it without any issues
+///
+/// Exceptions:
+/// BackTab is converted to Key::Tab, and Null is converted to Key::Unidentified
+fn key_from_crossterm_key_code(key_code: TermKeyCode) -> Key {
+ match key_code {
+ TermKeyCode::Backspace => Key::Backspace,
+ TermKeyCode::Enter => Key::Enter,
+ TermKeyCode::Left => Key::ArrowLeft,
+ TermKeyCode::Right => Key::ArrowRight,
+ TermKeyCode::Up => Key::ArrowUp,
+ TermKeyCode::Down => Key::ArrowDown,
+ TermKeyCode::Home => Key::Home,
+ TermKeyCode::End => Key::End,
+ TermKeyCode::PageUp => Key::PageUp,
+ TermKeyCode::PageDown => Key::PageDown,
+ TermKeyCode::Tab => Key::Tab,
+ // ? no corresponding Key
+ TermKeyCode::BackTab => Key::Tab,
+ TermKeyCode::Delete => Key::Delete,
+ TermKeyCode::Insert => Key::Insert,
+ TermKeyCode::F(1) => Key::F1,
+ TermKeyCode::F(2) => Key::F2,
+ TermKeyCode::F(3) => Key::F3,
+ TermKeyCode::F(4) => Key::F4,
+ TermKeyCode::F(5) => Key::F5,
+ TermKeyCode::F(6) => Key::F6,
+ TermKeyCode::F(7) => Key::F7,
+ TermKeyCode::F(8) => Key::F8,
+ TermKeyCode::F(9) => Key::F9,
+ TermKeyCode::F(10) => Key::F10,
+ TermKeyCode::F(11) => Key::F11,
+ TermKeyCode::F(12) => Key::F12,
+ TermKeyCode::F(13) => Key::F13,
+ TermKeyCode::F(14) => Key::F14,
+ TermKeyCode::F(15) => Key::F15,
+ TermKeyCode::F(16) => Key::F16,
+ TermKeyCode::F(17) => Key::F17,
+ TermKeyCode::F(18) => Key::F18,
+ TermKeyCode::F(19) => Key::F19,
+ TermKeyCode::F(20) => Key::F20,
+ TermKeyCode::F(21) => Key::F21,
+ TermKeyCode::F(22) => Key::F22,
+ TermKeyCode::F(23) => Key::F23,
+ TermKeyCode::F(24) => Key::F24,
+ TermKeyCode::F(other) => {
+ panic!("Unexpected function key: {other:?}")
+ }
+ TermKeyCode::Char(c) => Key::Character(c.to_string()),
+ TermKeyCode::Null => Key::Unidentified,
+ TermKeyCode::Esc => Key::Escape,
+ }
+}
+
+// Crossterm does not provide a way to get the `code` (physical key on keyboard)
+// So we make a guess based on their `key_code`, but this is probably going to break on anything other than a very standard european keyboard
+// It may look fine, but it's a horrible hack. But there's nothing better we can do.
+fn guess_code_from_crossterm_key_code(key_code: TermKeyCode) -> Option {
+ let code = match key_code {
+ TermKeyCode::Backspace => Code::Backspace,
+ TermKeyCode::Enter => Code::Enter,
+ TermKeyCode::Left => Code::ArrowLeft,
+ TermKeyCode::Right => Code::ArrowRight,
+ TermKeyCode::Up => Code::ArrowUp,
+ TermKeyCode::Down => Code::ArrowDown,
+ TermKeyCode::Home => Code::Home,
+ TermKeyCode::End => Code::End,
+ TermKeyCode::PageUp => Code::PageUp,
+ TermKeyCode::PageDown => Code::PageDown,
+ TermKeyCode::Tab => Code::Tab,
+ // ? Apparently you get BackTab by pressing Tab
+ TermKeyCode::BackTab => Code::Tab,
+ TermKeyCode::Delete => Code::Delete,
+ TermKeyCode::Insert => Code::Insert,
+ TermKeyCode::F(1) => Code::F1,
+ TermKeyCode::F(2) => Code::F2,
+ TermKeyCode::F(3) => Code::F3,
+ TermKeyCode::F(4) => Code::F4,
+ TermKeyCode::F(5) => Code::F5,
+ TermKeyCode::F(6) => Code::F6,
+ TermKeyCode::F(7) => Code::F7,
+ TermKeyCode::F(8) => Code::F8,
+ TermKeyCode::F(9) => Code::F9,
+ TermKeyCode::F(10) => Code::F10,
+ TermKeyCode::F(11) => Code::F11,
+ TermKeyCode::F(12) => Code::F12,
+ TermKeyCode::F(13) => Code::F13,
+ TermKeyCode::F(14) => Code::F14,
+ TermKeyCode::F(15) => Code::F15,
+ TermKeyCode::F(16) => Code::F16,
+ TermKeyCode::F(17) => Code::F17,
+ TermKeyCode::F(18) => Code::F18,
+ TermKeyCode::F(19) => Code::F19,
+ TermKeyCode::F(20) => Code::F20,
+ TermKeyCode::F(21) => Code::F21,
+ TermKeyCode::F(22) => Code::F22,
+ TermKeyCode::F(23) => Code::F23,
+ TermKeyCode::F(24) => Code::F24,
+ TermKeyCode::F(other) => {
+ panic!("Unexpected function key: {other:?}")
+ }
+ // this is a horrible way for crossterm to represent keys but we have to deal with it
+ TermKeyCode::Char(c) => match c {
'A'..='Z' | 'a'..='z' => match c.to_ascii_uppercase() {
- 'A' => KeyCode::A,
- 'B' => KeyCode::B,
- 'C' => KeyCode::C,
- 'D' => KeyCode::D,
- 'E' => KeyCode::E,
- 'F' => KeyCode::F,
- 'G' => KeyCode::G,
- 'H' => KeyCode::H,
- 'I' => KeyCode::I,
- 'J' => KeyCode::J,
- 'K' => KeyCode::K,
- 'L' => KeyCode::L,
- 'M' => KeyCode::M,
- 'N' => KeyCode::N,
- 'O' => KeyCode::O,
- 'P' => KeyCode::P,
- 'Q' => KeyCode::Q,
- 'R' => KeyCode::R,
- 'S' => KeyCode::S,
- 'T' => KeyCode::T,
- 'U' => KeyCode::U,
- 'V' => KeyCode::V,
- 'W' => KeyCode::W,
- 'X' => KeyCode::X,
- 'Y' => KeyCode::Y,
- 'Z' => KeyCode::Z,
- _ => return None,
+ '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"),
},
- ' ' => 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,
+ ' ' => Code::Space,
+ '[' | '{' => Code::BracketLeft,
+ ']' | '}' => Code::BracketRight,
+ ';' => Code::Semicolon,
+ ':' => Code::Semicolon,
+ ',' => Code::Comma,
+ '<' => Code::Comma,
+ '.' => Code::Period,
+ '>' => Code::Period,
+ '1' => Code::Digit1,
+ '2' => Code::Digit2,
+ '3' => Code::Digit3,
+ '4' => Code::Digit4,
+ '5' => Code::Digit5,
+ '6' => Code::Digit6,
+ '7' => Code::Digit7,
+ '8' => Code::Digit8,
+ '9' => Code::Digit9,
+ '0' => Code::Digit0,
+ '!' => Code::Digit1,
+ '@' => Code::Digit2,
+ '#' => Code::Digit3,
+ '$' => Code::Digit4,
+ '%' => Code::Digit5,
+ '^' => Code::Digit6,
+ '&' => Code::Digit7,
+ '*' => Code::Digit8,
+ '(' => Code::Digit9,
+ ')' => Code::Digit0,
+ // numpad characters are ambiguous; we don't know which key was really pressed
+ // it could be also:
+ // '*' => Code::Multiply,
+ // '/' => Code::Divide,
+ // '-' => Code::Subtract,
+ // '+' => Code::Add,
+ '+' => Code::Equal,
+ '-' | '_' => Code::Minus,
+ '\'' => Code::Quote,
+ '"' => Code::Quote,
+ '\\' => Code::Backslash,
+ '|' => Code::Backslash,
+ '/' => Code::Slash,
+ '?' => Code::Slash,
+ '=' => Code::Equal,
+ '`' => Code::Backquote,
+ '~' => Code::Backquote,
_ => return None,
- };
- key_str = 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:?}")
- }
+ },
+ TermKeyCode::Null => return None,
+ TermKeyCode::Esc => Code::Escape,
};
- // from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
- Some(EventData::Keyboard(KeyboardData {
- char_code: code.raw_code(),
- key: key_str,
- key_code: code,
- alt_key: event.modifiers.contains(KeyModifiers::ALT),
- ctrl_key: event.modifiers.contains(KeyModifiers::CONTROL),
- meta_key: false,
- shift_key,
- locale: Default::default(),
- location: 0x00,
- repeat: Default::default(),
- which: Default::default(),
- }))
+
+ Some(code)
+}
+
+fn modifiers_from_crossterm_modifiers(src: KeyModifiers) -> Modifiers {
+ let mut modifiers = Modifiers::empty();
+
+ if src.contains(KeyModifiers::SHIFT) {
+ modifiers.insert(Modifiers::SHIFT);
+ }
+
+ if src.contains(KeyModifiers::ALT) {
+ modifiers.insert(Modifiers::ALT);
+ }
+
+ if src.contains(KeyModifiers::CONTROL) {
+ modifiers.insert(Modifiers::CONTROL);
+ }
+
+ modifiers
}
diff --git a/packages/tui/tests/events.rs b/packages/tui/tests/events.rs
index 217959531..dfaedab40 100644
--- a/packages/tui/tests/events.rs
+++ b/packages/tui/tests/events.rs
@@ -1,5 +1,6 @@
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent};
use dioxus::prelude::*;
+use dioxus_html::input_data::keyboard_types::Code;
use dioxus_tui::TuiContext;
use std::future::Future;
use std::pin::Pin;
@@ -56,7 +57,7 @@ fn key_down() {
width: "100%",
height: "100%",
onkeydown: move |evt| {
- assert_eq!(evt.data.key_code, dioxus_html::KeyCode::A);
+ assert_eq!(evt.data.code(), Code::KeyA);
tui_ctx.quit();
},
}
@@ -286,7 +287,7 @@ fn wheel() {
width: "100%",
height: "100%",
onwheel: move |evt| {
- assert!(evt.data.delta_y > 0.0);
+ assert!(evt.data.delta().strip_units().y > 0.0);
tui_ctx.quit();
},
}
diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs
index 115e70fe5..e7a4d098d 100644
--- a/packages/web/src/lib.rs
+++ b/packages/web/src/lib.rs
@@ -62,6 +62,7 @@ use dioxus_core::prelude::Component;
use dioxus_core::SchedulerMsg;
use dioxus_core::VirtualDom;
use futures_util::FutureExt;
+use web_sys::console;
mod cache;
mod cfg;
@@ -172,50 +173,6 @@ pub fn launch_with_props(
pub async fn run_with_props(root: Component, root_props: T, cfg: WebConfig) {
let mut dom = VirtualDom::new_with_props(root, root_props);
- for s in crate::cache::BUILTIN_INTERNED_STRINGS {
- wasm_bindgen::intern(s);
- }
- for s in &cfg.cached_strings {
- wasm_bindgen::intern(s);
- }
-
- let tasks = dom.get_scheduler_channel();
-
- let sender_callback: Rc =
- Rc::new(move |event| tasks.unbounded_send(event).unwrap());
-
- let should_hydrate = cfg.hydrate;
-
- let mut websys_dom = dom::WebsysDom::new(cfg, sender_callback);
-
- log::trace!("rebuilding app");
-
- if should_hydrate {
- // todo: we need to split rebuild and initialize into two phases
- // it's a waste to produce edits just to get the vdom loaded
- let _ = dom.rebuild();
-
- if let Err(err) = websys_dom.rehydrate(&dom) {
- log::error!(
- "Rehydration failed {:?}. Rebuild DOM into element from scratch",
- &err
- );
-
- websys_dom.root.set_text_content(None);
-
- // errrrr we should split rebuild into two phases
- // one that initializes things and one that produces edits
- let edits = dom.rebuild();
-
- websys_dom.apply_edits(edits.edits);
- }
- } else {
- let edits = dom.rebuild();
- websys_dom.apply_edits(edits.edits);
- }
-
- let mut work_loop = ric_raf::RafLoop::new();
-
#[cfg(feature = "hot-reload")]
{
use dioxus_rsx_interpreter::error::Error;
@@ -274,12 +231,61 @@ pub async fn run_with_props(root: Component, root_props: T
// forward stream to the websocket
dom.base_scope().spawn_forever(async move {
while let Some(err) = error_channel_receiver.next().await {
- ws.send_with_str(serde_json::to_string(&err).unwrap().as_str())
- .unwrap();
+ if ws.ready_state() == WebSocket::OPEN {
+ ws.send_with_str(serde_json::to_string(&err).unwrap().as_str())
+ .unwrap();
+ } else {
+ console::warn_1(&"WebSocket is not open, cannot send error. Run with dioxus serve --hot-reload to enable hot reloading.".into());
+ panic!("{}", err);
+ }
}
});
}
+ for s in crate::cache::BUILTIN_INTERNED_STRINGS {
+ wasm_bindgen::intern(s);
+ }
+ for s in &cfg.cached_strings {
+ wasm_bindgen::intern(s);
+ }
+
+ let tasks = dom.get_scheduler_channel();
+
+ let sender_callback: Rc =
+ Rc::new(move |event| tasks.unbounded_send(event).unwrap());
+
+ let should_hydrate = cfg.hydrate;
+
+ let mut websys_dom = dom::WebsysDom::new(cfg, sender_callback);
+
+ log::trace!("rebuilding app");
+
+ if should_hydrate {
+ // todo: we need to split rebuild and initialize into two phases
+ // it's a waste to produce edits just to get the vdom loaded
+ let _ = dom.rebuild();
+
+ if let Err(err) = websys_dom.rehydrate(&dom) {
+ log::error!(
+ "Rehydration failed {:?}. Rebuild DOM into element from scratch",
+ &err
+ );
+
+ websys_dom.root.set_text_content(None);
+
+ // errrrr we should split rebuild into two phases
+ // one that initializes things and one that produces edits
+ let edits = dom.rebuild();
+
+ websys_dom.apply_edits(edits.edits);
+ }
+ } else {
+ let edits = dom.rebuild();
+ websys_dom.apply_edits(edits.edits);
+ }
+
+ let mut work_loop = ric_raf::RafLoop::new();
+
loop {
log::trace!("waiting for work");
// if virtualdom has nothing, wait for it to have something before requesting idle time
diff --git a/packages/web/src/olddom.rs b/packages/web/src/olddom.rs
index 8654ced90..d6035d5ab 100644
--- a/packages/web/src/olddom.rs
+++ b/packages/web/src/olddom.rs
@@ -521,7 +521,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc