Merge branch 'master' into stretch2_relative_layout

This commit is contained in:
Demonthos 2022-06-10 17:41:51 -05:00 committed by GitHub
commit c1112cfd3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 648 additions and 185 deletions

View file

@ -64,7 +64,7 @@ The most common hook you'll use for storing state is `use_state`. `use_state` pr
```rust
fn App(cx: Scope)-> Element {
let (post, set_post) = use_state(&cx, || {
let post = use_state(&cx, || {
PostData {
id: Uuid::new_v4(),
score: 10,
@ -112,11 +112,11 @@ For example, let's say we provide a button to generate a new post. Whenever the
```rust
fn App(cx: Scope)-> Element {
let (post, set_post) = use_state(&cx, || PostData::new());
let post = use_state(&cx, || PostData::new());
cx.render(rsx!{
button {
onclick: move |_| set_post(PostData::random())
onclick: move |_| post.set(PostData::random())
"Generate a random post"
}
Post { props: &post }
@ -141,19 +141,19 @@ We can use tasks in our components to build a tiny stopwatch that ticks every se
```rust
fn App(cx: Scope)-> Element {
let (elapsed, set_elapsed) = use_state(&cx, || 0);
use_future(&cx, || {
to_owned![set_elapsed]; // explicitly capture this hook for use in async
let elapsed = use_state(&cx, || 0);
use_future(&cx, (), |()| {
to_owned![elapsed]; // explicitly capture this hook for use in async
async move {
loop {
TimeoutFuture::from_ms(1000).await;
set_elapsed.modify(|i| i + 1)
gloo_timers::future::TimeoutFuture::new(1_000).await;
elapsed.modify(|i| i + 1)
}
}
});
rsx!(cx, div { "Current stopwatch time: {sec_elapsed}" })
rsx!(cx, div { "Current stopwatch time: {elapsed}" })
}
```

View file

@ -11,7 +11,7 @@ Getting Set up with Dioxus-Desktop is quite easy. Make sure you have Rust and Ca
```shell
$ cargo new --bin demo
$ cd app
$ cd demo
```
Add Dioxus with the `desktop` feature:

View file

@ -9,7 +9,9 @@ fn main() {
fn app(cx: Scope) -> Element {
let page_coordinates = use_state(&cx, || "".to_string());
let screen_coordinates = use_state(&cx, || "".to_string());
let offset_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;
@ -25,11 +27,14 @@ fn app(cx: Scope) -> Element {
let update_mouse_position = move |event: UiEvent<MouseData>| {
let mouse_data = event.data;
page_coordinates.set(format!("{:?}", (mouse_data.page_x, mouse_data.page_y)));
screen_coordinates.set(format!("{:?}", (mouse_data.screen_x, mouse_data.screen_y)));
offset_coordinates.set(format!("{:?}", (mouse_data.offset_x, mouse_data.offset_y)));
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! (
@ -39,10 +44,13 @@ fn app(cx: Scope) -> Element {
div {
style: "{rect_style}",
onmousemove: update_mouse_position,
prevent_default: "mousedown",
}
div {"Page coordinates: {page_coordinates}"},
div {"Screen coordinates: {screen_coordinates}"},
div {"Offset coordinates: {offset_coordinates}"},
div {"Element coordinates: {element_coordinates}"},
div {"Buttons: {buttons}"},
div {"Modifiers: {modifiers}"},
}
))
}

View file

@ -1,6 +1,7 @@
use std::{convert::TryInto, sync::Arc};
use dioxus::{events::MouseData, prelude::*};
use dioxus_core::UiEvent;
fn main() {
dioxus::tui::launch(app);
@ -12,7 +13,7 @@ fn app(cx: Scope) -> Element {
}
fn get_brightness(m: Arc<MouseData>) -> i32 {
let b: i32 = m.buttons.count_ones().try_into().unwrap();
let b: i32 = m.held_buttons().len().try_into().unwrap();
127 * b
}
@ -26,6 +27,24 @@ fn app(cx: Scope) -> Element {
let q3_color_str = to_str(q3_color);
let q4_color_str = to_str(q4_color);
let page_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 update_data = move |event: UiEvent<MouseData>| {
let mouse_data = event.data;
page_coordinates.set(format!("{:?}", mouse_data.page_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.
// There are also screen coordinates, but they are currently the same as client coordinates due to technical limitations
buttons.set(format!("{:?}", mouse_data.held_buttons()));
modifiers.set(format!("{:?}", mouse_data.modifiers()));
};
cx.render(rsx! {
div {
width: "100%",
@ -48,6 +67,7 @@ fn app(cx: Scope) -> Element {
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]),
onmouseleave: move |_| q1_color.set([200; 3]),
onmousemove: update_data,
"click me"
}
div {
@ -61,6 +81,7 @@ fn app(cx: Scope) -> Element {
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]),
onmouseleave: move |_| q2_color.set([200; 3]),
onmousemove: update_data,
"click me"
}
}
@ -80,6 +101,7 @@ fn app(cx: Scope) -> Element {
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]),
onmouseleave: move |_| q3_color.set([200; 3]),
onmousemove: update_data,
"click me"
}
div {
@ -93,9 +115,14 @@ fn app(cx: Scope) -> Element {
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]),
onmouseleave: move |_| q4_color.set([200; 3]),
onmousemove: update_data,
"click me"
}
}
},
div {"Page coordinates: {page_coordinates}"},
div {"Element coordinates: {element_coordinates}"},
div {"Buttons: {buttons}"},
div {"Modifiers: {modifiers}"},
}
})
}

View file

@ -1,5 +1,7 @@
use dioxus::events::WheelEvent;
use dioxus::prelude::*;
use dioxus_html::geometry::ScreenPoint;
use dioxus_html::input_data::MouseButtonSet;
use dioxus_html::on::{KeyboardEvent, MouseEvent};
use dioxus_html::KeyCode;
@ -9,9 +11,9 @@ fn main() {
fn app(cx: Scope) -> Element {
let key = use_state(&cx, || "".to_string());
let mouse = use_state(&cx, || (0, 0));
let mouse = use_state(&cx, || ScreenPoint::zero());
let count = use_state(&cx, || 0);
let buttons = use_state(&cx, || 0);
let buttons = use_state(&cx, || MouseButtonSet::empty());
let mouse_clicked = use_state(&cx, || false);
cx.render(rsx! {
@ -36,21 +38,21 @@ fn app(cx: Scope) -> Element {
count.set(count + evt.data.delta_y as i64);
},
ondrag: move |evt: MouseEvent| {
mouse.set((evt.data.screen_x, evt.data.screen_y));
mouse.set(evt.data.screen_coordinates());
},
onmousedown: move |evt: MouseEvent| {
mouse.set((evt.data.screen_x, evt.data.screen_y));
buttons.set(evt.data.buttons);
mouse.set(evt.data.screen_coordinates());
buttons.set(evt.data.held_buttons());
mouse_clicked.set(true);
},
onmouseup: move |evt: MouseEvent| {
buttons.set(evt.data.buttons);
buttons.set(evt.data.held_buttons());
mouse_clicked.set(false);
},
"count: {count:?}",
"key: {key}",
"mouse buttons: {buttons:b}",
"mouse buttons: {buttons:?}",
"mouse pos: {mouse:?}",
"mouse button pressed: {mouse_clicked}"
}

23
examples/window_zoom.rs Normal file
View file

@ -0,0 +1,23 @@
use dioxus::desktop::use_window;
use dioxus::prelude::*;
fn main() {
dioxus::desktop::launch(app);
}
fn app(cx: Scope) -> Element {
let window = use_window(&cx);
let level = use_state(&cx, || 1.0);
cx.render(rsx! {
input {
r#type: "number",
value: "{level}",
oninput: |e| {
let num = e.value.parse::<f64>().unwrap_or(1.0);
level.set(num);
window.set_zoom_level(num);
}
}
})
}

View file

@ -1,7 +1,6 @@
//! Parse components into the VComponent VNode
//! Parse components into the VNode::Component variant
//! ==========================================
//!
//! This parsing path emerges from [`AmbiguousElement`] which supports validation of the vcomponent format.
//! We can be reasonably sure that whatever enters this parsing path is in the right format.
//! This feature must support
//! - [x] Namespaced components

View file

@ -1,26 +1,26 @@
#![warn(clippy::pedantic)]
#![allow(clippy::cast_possible_truncation)]
//! This module contains the stateful [`DiffState`] and all methods to diff [`VNodes`], their properties, and their children.
//! This module contains the stateful [`DiffState`] and all methods to diff [`VNode`]s, their properties, and their children.
//!
//! The [`DiffState`] calculates the diffs between the old and new frames, updates the new nodes, and generates a set
//! of mutations for the [`RealDom`] to apply.
//! of mutations for the renderer to apply.
//!
//! ## Notice:
//!
//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and then modified to support
//! Components, Fragments, Suspense, [`SubTree`] memoization, incremental diffing, cancellation, [`NodeRefs`], pausing, priority
//! Components, Fragments, Suspense, `SubTree` memoization, incremental diffing, cancellation, pausing, priority
//! scheduling, and additional batching operations.
//!
//! ## Implementation Details:
//!
//! ### IDs for elements
//! --------------------
//! All nodes are addressed by their IDs. The [`RealDom`] provides an imperative interface for making changes to these nodes.
//! All nodes are addressed by their IDs.
//! We don't necessarily require that DOM changes happen instantly during the diffing process, so the implementor may choose
//! to batch nodes if it is more performant for their application. The element IDs are indices into the internal element
//! array. The expectation is that implementors will use the ID as an index into a Vec of real nodes, allowing for passive
//! garbage collection as the [`VirtualDOM`] replaces old nodes.
//! garbage collection as the [`crate::VirtualDom`] replaces old nodes.
//!
//! When new vnodes are created through `cx.render`, they won't know which real node they correspond to. During diffing,
//! we always make sure to copy over the ID. If we don't do this properly, the [`ElementId`] will be populated incorrectly
@ -30,7 +30,7 @@
//! --------------------
//! Fragments (nodes without a parent) are supported through a combination of "replace with" and anchor vnodes. Fragments
//! can be particularly challenging when they are empty, so the anchor node lets us "reserve" a spot for the empty
//! fragment to be replaced with when it is no longer empty. This is guaranteed by logic in the [`NodeFactory`] - it is
//! fragment to be replaced with when it is no longer empty. This is guaranteed by logic in the [`crate::innerlude::NodeFactory`] - it is
//! impossible to craft a fragment with 0 elements - they must always have at least a single placeholder element. Adding
//! "dummy" nodes _is_ inefficient, but it makes our diffing algorithm faster and the implementation is completely up to
//! the platform.
@ -44,13 +44,13 @@
//! into a promise-like value. React will then work on the next "ready" fiber, checking back on the previous fiber once
//! it has finished its new work. In Dioxus, we use a similar approach, but try to completely render the tree before
//! switching sub-fibers. Instead, each future is submitted into a futures-queue and the node is manually loaded later on.
//! Due to the frequent calls to [`yield_now`] we can get the pure "fetch-as-you-render" behavior of React Fiber.
//! Due to the frequent calls to [`crate::virtual_dom::VirtualDom::work_with_deadline`] we can get the pure "fetch-as-you-render" behavior of React Fiber.
//!
//! We're able to use this approach because we use placeholder nodes - futures that aren't ready still get submitted to
//! DOM, but as a placeholder.
//!
//! Right now, the "suspense" queue is intertwined with hooks. In the future, we should allow any future to drive attributes
//! and contents, without the need for the [`use_suspense`] hook. In the interim, this is the quickest way to get Suspense working.
//! and contents, without the need for a `use_suspense` hook. In the interim, this is the quickest way to get Suspense working.
//!
//! ## Subtree Memoization
//! -----------------------

View file

@ -150,7 +150,7 @@ impl AnyEvent {
/// You should prefer to use the name of the event directly, rather than
/// the UiEvent<T> generic type.
///
/// For the HTML crate, this would include [`MouseEvent`], [`FormEvent`] etc.
/// For the HTML crate, this would include MouseEvent, FormEvent etc.
pub struct UiEvent<T> {
/// The internal data of the event
/// This is wrapped in an Arc so that it can be sent across threads

View file

@ -550,7 +550,7 @@ impl<'a> NodeFactory<'a> {
}))
}
/// Create a new [`VNode::VElement`]
/// Create a new [`VNode::Element`]
pub fn element(
&self,
el: impl DioxusElement,
@ -569,7 +569,7 @@ impl<'a> NodeFactory<'a> {
)
}
/// Create a new [`VNode::VElement`] without the trait bound
/// Create a new [`VNode::Element`] without the trait bound
///
/// IE pass in "div" instead of `div`
pub fn raw_element(
@ -637,7 +637,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Create a new [`VNode::VComponent`]
/// Create a new [`VNode::Component`]
pub fn component<P>(
&self,
component: fn(Scope<'a, P>) -> Element,
@ -684,7 +684,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Create a new [`VNode::VFragment`] from a root of the rsx! call
/// Create a new [`VNode::Fragment`] from a root of the rsx! call
pub fn fragment_root<'b, 'c>(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
@ -705,7 +705,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Create a new [`VNode::VFragment`] from any iterator
/// Create a new [`VNode::Fragment`] from any iterator
pub fn fragment_from_iter<'b, 'c>(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,

View file

@ -33,7 +33,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> {
/// fn App(cx: Scope) -> Element {
/// cx.render(rsx!{
/// CustomCard {
/// h1 {}2
/// h1 {}
/// p {}
/// }
/// })

View file

@ -1,7 +1,7 @@
use crate::innerlude::*;
/// An iterator that only yields "real" [`Element`]s. IE only Elements that are
/// not [`VNode::VComponent`] or [`VNode::VFragment`], .
/// not [`VNode::Component`] or [`VNode::Fragment`], .
pub struct ElementIdIterator<'a> {
vdom: &'a VirtualDom,

View file

@ -583,7 +583,7 @@ impl VirtualDom {
///
/// *value.borrow_mut() = "goodbye";
///
/// let edits = dom.diff();
/// let edits = dom.hard_diff(ScopeId(0));
/// ```
pub fn hard_diff(&mut self, scope_id: ScopeId) -> Mutations {
let mut diff_machine = DiffState::new(&self.scopes);

View file

@ -115,6 +115,16 @@ impl DesktopContext {
let _ = self.proxy.send_event(SetDecorations(decoration));
}
/// set window zoom level
pub fn set_zoom_level(&self, scale_factor: f64) {
let _ = self.proxy.send_event(SetZoomLevel(scale_factor));
}
/// launch print modal
pub fn print(&self) {
let _ = self.proxy.send_event(Print);
}
/// opens DevTool window
pub fn devtool(&self) {
let _ = self.proxy.send_event(DevTool);
@ -148,6 +158,9 @@ pub enum UserWindowEvent {
SetTitle(String),
SetDecorations(bool),
SetZoomLevel(f64),
Print,
DevTool,
Eval(String),
@ -191,11 +204,27 @@ pub(super) fn handler(
SetTitle(content) => window.set_title(&content),
SetDecorations(state) => window.set_decorations(state),
DevTool => {}
SetZoomLevel(scale_factor) => webview.zoom(scale_factor),
Eval(code) => webview
.evaluate_script(code.as_str())
.expect("eval shouldn't panic"),
Print => {
if let Err(e) = webview.print() {
// we can't panic this error.
log::warn!("Open print modal failed: {e}");
}
}
DevTool => {
#[cfg(debug_assertions)]
webview.open_devtools();
#[cfg(not(debug_assertions))]
log::warn!("Devtools are disabled in release builds");
}
Eval(code) => {
if let Err(e) = webview.evaluate_script(code.as_str()) {
// we can't panic this error.
log::warn!("Eval script error: {e}");
}
}
}
}

View file

@ -24,7 +24,7 @@ use std::{
/// val.write().insert(1, "hello".to_string());
/// ```
///
/// You can avoid this defualt behavior with `write_silent`
/// You can avoid this default behavior with `write_silent`
///
/// ```
/// // with `write_silent`, the component will not be re-rendered

View file

@ -15,6 +15,9 @@ dioxus-core = { path = "../core", version = "^0.2.1" }
serde = { version = "1", features = ["derive"], optional = true }
serde_repr = { version = "0.1", optional = true }
wasm-bindgen = { version = "0.2.79", optional = true }
euclid = "0.22.7"
enumset = "1.0.11"
keyboard-types = "0.6.2"
[dependencies.web-sys]
optional = true

View file

@ -3,6 +3,13 @@ use dioxus_core::exports::bumpalo;
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 keyboard_types::Modifiers;
use std::collections::HashMap;
use super::*;
@ -440,7 +447,7 @@ pub mod on {
/// 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 [`KeyboardEvent::key`] method - this will return a string identifier instead of a limited enum.
/// To match on unicode sequences, use the [`KeyboardData::key`] method - this will return a string identifier instead of a limited enum.
///
///
/// ## Example
@ -496,10 +503,15 @@ pub mod on {
pub type MouseEvent = UiEvent<MouseData>;
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
/// Data associated with a mouse event
///
/// Do not use the deprecated fields; they may change or become private in the future.
pub struct MouseData {
/// True if the alt key was down when the mouse event was fired.
#[deprecated(since = "0.3.0", note = "use modifiers() instead")]
pub alt_key: bool,
/// The button number that was pressed (if applicable) when the mouse event was fired.
#[deprecated(since = "0.3.0", note = "use trigger_button() instead")]
pub button: i16,
/// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered.
///
@ -510,40 +522,171 @@ pub mod on {
/// - 4: Auxiliary button (usually the mouse wheel button or middle button)
/// - 8: 4th button (typically the "Browser Back" button)
/// - 16 : 5th button (typically the "Browser Forward" button)
#[deprecated(since = "0.3.0", note = "use held_buttons() instead")]
pub buttons: u16,
/// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
///
/// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally.
#[deprecated(since = "0.3.0", note = "use client_coordinates() instead")]
pub client_x: i32,
/// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
///
/// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically.
#[deprecated(since = "0.3.0", note = "use client_coordinates() instead")]
pub client_y: i32,
/// True if the control key was down when the mouse event was fired.
#[deprecated(since = "0.3.0", note = "use modifiers() instead")]
pub ctrl_key: bool,
/// True if the meta key was down when the mouse event was fired.
#[deprecated(since = "0.3.0", note = "use modifiers() instead")]
pub meta_key: bool,
/// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
#[deprecated(since = "0.3.0", note = "use element_coordinates() instead")]
pub offset_x: i32,
/// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node.
#[deprecated(since = "0.3.0", note = "use element_coordinates() instead")]
pub offset_y: i32,
/// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible.
///
/// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300.
#[deprecated(since = "0.3.0", note = "use page_coordinates() instead")]
pub page_x: i32,
/// The Y (vertical) coordinate in pixels of the event relative to the whole document.
///
/// See `page_x`.
#[deprecated(since = "0.3.0", note = "use page_coordinates() instead")]
pub page_y: i32,
/// The X coordinate of the mouse pointer in global (screen) coordinates.
#[deprecated(since = "0.3.0", note = "use screen_coordinates() instead")]
pub screen_x: i32,
/// The Y coordinate of the mouse pointer in global (screen) coordinates.
#[deprecated(since = "0.3.0", note = "use screen_coordinates() instead")]
pub screen_y: i32,
/// True if the shift key was down when the mouse event was fired.
#[deprecated(since = "0.3.0", note = "use modifiers() instead")]
pub shift_key: bool,
// fn get_modifier_state(&self, key_code: &str) -> bool;
}
impl MouseData {
/// Construct MouseData with the specified properties
///
/// Note: the current implementation truncates coordinates. In the future, when we change the internal representation, it may also support a fractional part.
pub fn new(
coordinates: Coordinates,
trigger_button: Option<MouseButton>,
held_buttons: MouseButtonSet,
modifiers: Modifiers,
) -> Self {
let alt_key = modifiers.contains(Modifiers::ALT);
let ctrl_key = modifiers.contains(Modifiers::CONTROL);
let meta_key = modifiers.contains(Modifiers::META);
let shift_key = modifiers.contains(Modifiers::SHIFT);
let [client_x, client_y]: [i32; 2] = coordinates.client().cast().into();
let [offset_x, offset_y]: [i32; 2] = coordinates.element().cast().into();
let [page_x, page_y]: [i32; 2] = coordinates.page().cast().into();
let [screen_x, screen_y]: [i32; 2] = coordinates.screen().cast().into();
#[allow(deprecated)]
Self {
alt_key,
ctrl_key,
meta_key,
shift_key,
button: trigger_button.map_or(0, |b| b.into_web_code()),
buttons: encode_mouse_button_set(held_buttons),
client_x,
client_y,
offset_x,
offset_y,
page_x,
page_y,
screen_x,
screen_y,
}
}
/// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page).
///
/// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally.
pub fn client_coordinates(&self) -> ClientPoint {
#[allow(deprecated)]
ClientPoint::new(self.client_x.into(), self.client_y.into())
}
/// The event's coordinates relative to the padding edge of the target element
///
/// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.)
pub fn element_coordinates(&self) -> ElementPoint {
#[allow(deprecated)]
ElementPoint::new(self.offset_x.into(), self.offset_y.into())
}
/// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible.
///
/// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.)
pub fn page_coordinates(&self) -> PagePoint {
#[allow(deprecated)]
PagePoint::new(self.page_x.into(), self.page_y.into())
}
/// The event's coordinates relative to the entire screen. This takes into account the window's offset.
pub fn screen_coordinates(&self) -> ScreenPoint {
#[allow(deprecated)]
ScreenPoint::new(self.screen_x.into(), self.screen_y.into())
}
pub fn coordinates(&self) -> Coordinates {
Coordinates::new(
self.screen_coordinates(),
self.client_coordinates(),
self.element_coordinates(),
self.page_coordinates(),
)
}
/// 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 set of mouse buttons which were held when the event occurred.
pub fn held_buttons(&self) -> MouseButtonSet {
#[allow(deprecated)]
decode_mouse_button_set(self.buttons)
}
/// The mouse button that triggered the event
///
// todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here
/// This is only guaranteed to indicate which button was pressed during events caused by pressing or releasing a button. As such, it is not reliable for events such as mouseenter, mouseleave, mouseover, mouseout, or mousemove. For example, a value of MouseButton::Primary may also indicate that no button was pressed.
pub fn trigger_button(&self) -> Option<MouseButton> {
#[allow(deprecated)]
Some(MouseButton::from_web_code(self.button))
}
}
pub type PointerEvent = UiEvent<PointerData>;
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]

View file

@ -0,0 +1,73 @@
//! Geometry primitives for representing e.g. mouse events
/// A re-export of euclid, which we use for geometry primitives
pub use euclid;
use euclid::*;
/// Coordinate space relative to the screen
pub struct ScreenSpace;
/// A point in ScreenSpace
pub type ScreenPoint = Point2D<f64, ScreenSpace>;
/// Coordinate space relative to the viewport
pub struct ClientSpace;
/// A point in ClientSpace
pub type ClientPoint = Point2D<f64, ClientSpace>;
/// Coordinate space relative to an element
pub struct ElementSpace;
/// A point in ElementSpace
pub type ElementPoint = Point2D<f64, ElementSpace>;
/// Coordinate space relative to the page
pub struct PageSpace;
/// A point in PageSpace
pub type PagePoint = Point2D<f64, PageSpace>;
/// Coordinates of a point in the app's interface
pub struct Coordinates {
screen: ScreenPoint,
client: ClientPoint,
element: ElementPoint,
page: PagePoint,
}
impl Coordinates {
/// Construct new coordinates with the specified screen-, client-, element- and page-relative points
pub fn new(
screen: ScreenPoint,
client: ClientPoint,
element: ElementPoint,
page: PagePoint,
) -> Self {
Self {
screen,
client,
element,
page,
}
}
/// Coordinates relative to the entire screen. This takes into account the window's offset.
pub fn screen(&self) -> ScreenPoint {
self.screen
}
/// Coordinates relative to the application's viewport (as opposed to the coordinate within the page).
///
/// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally.
pub fn client(&self) -> ClientPoint {
self.client
}
/// Coordinates relative to the padding edge of the target element
///
/// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.)
pub fn element(&self) -> ElementPoint {
self.element
}
/// Coordinates relative to the entire document. This includes any portion of the document not currently visible.
///
/// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.)
pub fn page(&self) -> PagePoint {
self.page
}
}

View file

@ -0,0 +1,120 @@
//! Data structures representing user input, such as modifier keys and mouse buttons
use enumset::{EnumSet, EnumSetType};
/// A re-export of keyboard_types
pub use keyboard_types;
/// A mouse button type (such as Primary/Secondary)
// note: EnumSetType also derives Copy and Clone for some reason
#[derive(EnumSetType, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum MouseButton {
/// Primary button (typically the left button)
Primary,
/// Secondary button (typically the right button)
Secondary,
/// Auxiliary button (typically the middle button)
Auxiliary,
/// Fourth button (typically the "Browser Back" button)
Fourth,
/// Fifth button (typically the "Browser Forward" button)
Fifth,
/// A button with an unknown code
Unknown,
}
impl MouseButton {
/// Constructs a MouseButton for the specified button code
///
/// E.g. 0 => Primary; 1 => Auxiliary
///
/// Unknown codes get mapped to MouseButton::Unknown.
pub fn from_web_code(code: i16) -> Self {
match code {
0 => MouseButton::Primary,
// not a typo; auxiliary and secondary are swapped unlike in the `buttons` field.
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
1 => MouseButton::Auxiliary,
2 => MouseButton::Secondary,
3 => MouseButton::Fourth,
4 => MouseButton::Fifth,
_ => MouseButton::Unknown,
}
}
/// Converts MouseButton into the corresponding button code
///
/// MouseButton::Unknown will get mapped to -1
pub fn into_web_code(self) -> i16 {
match self {
MouseButton::Primary => 0,
// not a typo; auxiliary and secondary are swapped unlike in the `buttons` field.
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
MouseButton::Auxiliary => 1,
MouseButton::Secondary => 2,
MouseButton::Fourth => 3,
MouseButton::Fifth => 4,
MouseButton::Unknown => -1,
}
}
}
/// A set of mouse buttons
pub type MouseButtonSet = EnumSet<MouseButton>;
pub fn decode_mouse_button_set(code: u16) -> MouseButtonSet {
let mut set = EnumSet::empty();
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
#[allow(deprecated)]
{
if code & 0b1 != 0 {
set |= MouseButton::Primary;
}
if code & 0b10 != 0 {
set |= MouseButton::Secondary;
}
if code & 0b100 != 0 {
set |= MouseButton::Auxiliary;
}
if code & 0b1000 != 0 {
set |= MouseButton::Fourth;
}
if code & 0b10000 != 0 {
set |= MouseButton::Fifth;
}
if code & (!0b11111) != 0 {
set |= MouseButton::Unknown;
}
}
set
}
pub fn encode_mouse_button_set(set: MouseButtonSet) -> u16 {
let mut code = 0;
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
{
if set.contains(MouseButton::Primary) {
code |= 0b1;
}
if set.contains(MouseButton::Secondary) {
code |= 0b10;
}
if set.contains(MouseButton::Auxiliary) {
code |= 0b100;
}
if set.contains(MouseButton::Fourth) {
code |= 0b1000;
}
if set.contains(MouseButton::Fifth) {
code |= 0b10000;
}
if set.contains(MouseButton::Unknown) {
code |= 0b100000;
}
}
code
}

View file

@ -15,7 +15,9 @@
mod elements;
mod events;
pub mod geometry;
mod global_attributes;
pub mod input_data;
#[cfg(feature = "wasm-bind")]
mod web_sys_bind;

View file

@ -1,8 +1,11 @@
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
use crate::input_data::{decode_mouse_button_set, MouseButton};
use crate::on::{
AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
TransitionData, WheelData,
};
use crate::KeyCode;
use keyboard_types::Modifiers;
use wasm_bindgen::JsCast;
use web_sys::{
AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent,
@ -71,22 +74,32 @@ impl From<&KeyboardEvent> for KeyboardData {
impl From<&MouseEvent> for MouseData {
fn from(e: &MouseEvent) -> Self {
Self {
alt_key: e.alt_key(),
button: e.button(),
buttons: e.buttons(),
client_x: e.client_x(),
client_y: e.client_y(),
ctrl_key: e.ctrl_key(),
meta_key: e.meta_key(),
offset_x: e.offset_x(),
offset_y: e.offset_y(),
screen_x: e.screen_x(),
screen_y: e.screen_y(),
shift_key: e.shift_key(),
page_x: e.page_x(),
page_y: e.page_y(),
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);
}
MouseData::new(
Coordinates::new(
ScreenPoint::new(e.screen_x().into(), e.screen_y().into()),
ClientPoint::new(e.client_x().into(), e.client_y().into()),
ElementPoint::new(e.offset_x().into(), e.offset_y().into()),
PagePoint::new(e.page_x().into(), e.page_y().into()),
),
Some(MouseButton::from_web_code(e.button().into())),
decode_mouse_button_set(e.buttons()),
modifiers,
)
}
}

View file

@ -140,7 +140,9 @@ export class Interpreter {
RemoveAttribute(root, field, ns) {
const name = field;
const node = this.nodes[root];
if (ns !== null || ns !== undefined) {
if (ns == "style") {
node.style.removeProperty(name);
} else if (ns !== null || ns !== undefined) {
node.removeAttributeNS(ns, name);
} else if (name === "value") {
node.value = "";

View file

@ -15,7 +15,7 @@ use crate::{
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
/// The render state passes from parent to children and or accumulates state from children to parents.
/// To get started implement [PushedDownState] and or [BubbledUpState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes.
/// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes.
#[derive(Debug)]
pub struct RealDom<S: State> {
root: usize,
@ -712,7 +712,7 @@ impl<S: State> IndexMut<ElementId> for RealDom<S> {
}
}
/// The node is stored client side and stores only basic data about the node. For more complete information about the node see [`domNode::element`].
/// The node is stored client side and stores only basic data about the node.
#[derive(Debug, Clone)]
pub struct Node<S: State> {
/// The id of the node this node was created from.

View file

@ -44,10 +44,10 @@ pub(crate) fn union_ordered_iter<T: Ord + Debug>(
}
/// This state is derived from children. For example a node's size could be derived from the size of children.
/// Called when the current node's node properties are modified, a child's [BubbledUpState] is modified or a child is removed.
/// Called when the current node's node properties are modified, a child's [ChildDepState] is modified or a child is removed.
/// Called at most once per update.
pub trait ChildDepState {
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
/// The context is passed to the [ChildDepState::reduce] when it is pushed down.
/// This is sometimes nessisary for lifetime purposes.
type Ctx;
/// This must be either a [ChildDepState] or [NodeDepState]
@ -64,10 +64,10 @@ pub trait ChildDepState {
}
/// This state that is passed down to children. For example text properties (`<b>` `<i>` `<u>`) would be passed to children.
/// Called when the current node's node properties are modified or a parrent's [PushedDownState] is modified.
/// Called when the current node's node properties are modified or a parrent's [ParentDepState] is modified.
/// Called at most once per update.
pub trait ParentDepState {
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
/// The context is passed to the [ParentDepState::reduce] when it is pushed down.
/// This is sometimes nessisary for lifetime purposes.
type Ctx;
/// This must be either a [ParentDepState] or [NodeDepState]
@ -77,7 +77,7 @@ pub trait ParentDepState {
}
/// This state that is upadated lazily. For example any propertys that do not effect other parts of the dom like bg-color.
/// Called when the current node's node properties are modified or a parrent's [PushedDownState] is modified.
/// Called when the current node's node properties are modified or a sibling's [NodeDepState] is modified.
/// Called at most once per update.
pub trait NodeDepState {
type Ctx;

View file

@ -43,9 +43,9 @@
<span> | </span>
<a href="https://github.com/DioxusLabs/example-projects"> Examples </a>
<span> | </span>
<a href="https://dioxuslabs.com/guide"> Guide (0.1.8) </a>
<a href="https://dioxuslabs.com/router"> Guide (Latest) </a>
<span> | </span>
<a href="https://dioxuslabs.com/nightly/guide"> Guide (Master) </a>
<a href="https://dioxuslabs.com/nightly/router"> Guide (Master) </a>
</h3>
</div>

View file

@ -4,6 +4,11 @@ use crossterm::event::{
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::input_data::MouseButtonSet as DioxusMouseButtons;
use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
use dioxus_html::{on::*, KeyCode};
use std::{
any::Any,
@ -13,6 +18,7 @@ use std::{
time::{Duration, Instant},
};
use taffy::{prelude::Layout, Taffy};
use taffy::geometry::{Point, Size};
use crate::{Dom, Node};
@ -74,7 +80,7 @@ impl EventData {
const MAX_REPEAT_TIME: Duration = Duration::from_millis(100);
pub struct InnerInputState {
mouse: Option<(MouseData, Vec<u16>)>,
mouse: Option<MouseData>,
wheel: Option<WheelData>,
last_key_pressed: Option<(KeyboardData, Instant)>,
screen: Option<(u16, u16)>,
@ -96,54 +102,38 @@ impl InnerInputState {
fn apply_event(&mut self, evt: &mut EventCore) {
match evt.1 {
// limitations: only two buttons may be held at once
EventData::Mouse(ref mut m) => match &mut self.mouse {
Some(state) => {
let mut buttons = state.0.buttons;
state.0 = m.clone();
match evt.0 {
// this code only runs when there are no buttons down
"mouseup" => {
buttons = 0;
state.1 = Vec::new();
}
"mousedown" => {
if state.1.contains(&m.buttons) {
// if we already pressed a button and there is another button released the button crossterm sends is the button remaining
if state.1.len() > 1 {
evt.0 = "mouseup";
state.1 = vec![m.buttons];
}
// otherwise some other button was pressed. In testing it was consistantly this mapping
else {
match m.buttons {
0x01 => state.1.push(0x02),
0x02 => state.1.push(0x01),
0x04 => state.1.push(0x01),
_ => (),
}
}
} else {
state.1.push(m.buttons);
}
EventData::Mouse(ref mut m) => {
let mut held_buttons = match &self.mouse {
Some(previous_data) => previous_data.held_buttons(),
None => MouseButtonSet::empty(),
};
buttons = state.1.iter().copied().reduce(|a, b| a | b).unwrap();
}
_ => (),
match evt.0 {
"mousedown" => {
held_buttons.insert(
m.trigger_button()
.expect("No trigger button for mousedown event"),
);
}
state.0.buttons = buttons;
m.buttons = buttons;
"mouseup" => {
held_buttons.remove(
m.trigger_button()
.expect("No trigger button for mouseup event"),
);
}
_ => {}
}
None => {
self.mouse = Some((
m.clone(),
if m.buttons == 0 {
Vec::new()
} else {
vec![m.buttons]
},
));
}
},
let new_mouse_data = MouseData::new(
m.coordinates(),
m.trigger_button(),
held_buttons,
m.modifiers(),
);
self.mouse = Some(new_mouse_data.clone());
*m = new_mouse_data;
}
EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
EventData::Screen(ref s) => self.screen = Some(*s),
EventData::Keyboard(ref mut k) => {
@ -166,7 +156,7 @@ impl InnerInputState {
layout: &Taffy,
dom: &mut Dom,
) {
let previous_mouse = self.mouse.as_ref().map(|m| (m.0.clone(), m.1.clone()));
let previous_mouse = self.mouse.clone();
self.wheel = None;
@ -183,16 +173,17 @@ impl InnerInputState {
fn resolve_mouse_events(
&self,
previous_mouse: Option<(MouseData, Vec<u16>)>,
previous_mouse: Option<MouseData>,
resolved_events: &mut Vec<UserEvent>,
layout: &Taffy,
dom: &mut Dom,
) {
fn layout_contains_point(layout: &Layout, point: (i32, i32)) -> bool {
layout.location.x as i32 <= point.0
&& layout.location.x as i32 + layout.size.width as i32 >= point.0
&& layout.location.y as i32 <= point.1
&& layout.location.y as i32 + layout.size.height as i32 >= point.1
fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
let Point { x, y } = layout.location;
let Size { width, height } = layout.size;
let layout_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height));
layout_rect.contains(point.cast())
}
fn try_create_event(
@ -221,25 +212,43 @@ impl InnerInputState {
}
fn prepare_mouse_data(mouse_data: &MouseData, layout: &Layout) -> MouseData {
let mut data = mouse_data.clone();
data.offset_x = data.client_x - layout.location.x as i32;
data.offset_y = data.client_y - layout.location.y as i32;
data
let Point { x, y } = layout.location;
let node_origin = ClientPoint::new(x.into(), y.into());
let new_client_coordinates = (mouse_data.client_coordinates() - node_origin)
.to_point()
.cast_unit();
let coordinates = Coordinates::new(
mouse_data.screen_coordinates(),
mouse_data.client_coordinates(),
new_client_coordinates,
mouse_data.page_coordinates(),
);
MouseData::new(
coordinates,
mouse_data.trigger_button(),
mouse_data.held_buttons(),
mouse_data.modifiers(),
)
}
if let Some(mouse) = &self.mouse {
let new_pos = (mouse.0.screen_x, mouse.0.screen_y);
let old_pos = previous_mouse
.as_ref()
.map(|m| (m.0.screen_x, m.0.screen_y));
// the a mouse button is pressed if a button was not down and is now down
let pressed =
(mouse.0.buttons & !previous_mouse.as_ref().map(|m| m.0.buttons).unwrap_or(0)) > 0;
// the a mouse button is pressed if a button was down and is now not down
let released =
(!mouse.0.buttons & previous_mouse.map(|m| m.0.buttons).unwrap_or(0)) > 0;
if let Some(mouse_data) = &self.mouse {
let new_pos = mouse_data.screen_coordinates();
let old_pos = previous_mouse.as_ref().map(|m| m.screen_coordinates());
// a mouse button is pressed if a button was not down and is now down
let previous_buttons = previous_mouse
.map_or(MouseButtonSet::empty(), |previous_data| {
previous_data.held_buttons()
});
let was_pressed = !(mouse_data.held_buttons() - previous_buttons).is_empty();
// 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 mouse_data = &mouse.0;
let wheel_data = &self.wheel;
{
@ -314,7 +323,7 @@ impl InnerInputState {
}
// mousedown
if pressed {
if was_pressed {
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mousedown") {
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
@ -335,7 +344,7 @@ impl InnerInputState {
{
// mouseup
if released {
if was_released {
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("mouseup") {
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
@ -357,7 +366,7 @@ impl InnerInputState {
{
// click
if mouse_data.button == 0 && released {
if mouse_data.trigger_button() == Some(DioxusMouseButton::Primary) && was_released {
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("click") {
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
@ -379,7 +388,8 @@ impl InnerInputState {
{
// contextmenu
if mouse_data.button == 2 && released {
if mouse_data.trigger_button() == Some(DioxusMouseButton::Secondary) && was_released
{
let mut will_bubble = FxHashSet::default();
for node in dom.get_listening_sorted("contextmenu") {
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
@ -584,41 +594,47 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
let ctrl = m.modifiers.contains(KeyModifiers::CONTROL);
let meta = false;
let get_mouse_data = |b| {
let buttons = match b {
None => 0,
Some(MouseButton::Left) => 1,
Some(MouseButton::Right) => 2,
Some(MouseButton::Middle) => 4,
};
let button_state = match b {
None => 0,
Some(MouseButton::Left) => 0,
Some(MouseButton::Middle) => 1,
Some(MouseButton::Right) => 2,
};
let get_mouse_data = |crossterm_button: Option<MouseButton>| {
let button = crossterm_button.map(|b| match b {
MouseButton::Left => DioxusMouseButton::Primary,
MouseButton::Right => DioxusMouseButton::Secondary,
MouseButton::Middle => DioxusMouseButton::Auxiliary,
});
// from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
// The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively.
// todo?
// But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway.
EventData::Mouse(MouseData {
alt_key: alt,
button: button_state,
buttons,
client_x: x,
client_y: y,
ctrl_key: ctrl,
meta_key: meta,
let coordinates = Coordinates::new(
ScreenPoint::new(x, y),
ClientPoint::new(x, y),
// offset x/y are set when the origin of the event is assigned to an element
offset_x: 0,
offset_y: 0,
page_x: x,
page_y: y,
screen_x: x,
screen_y: y,
shift_key: shift,
})
ElementPoint::new(0., 0.),
PagePoint::new(x, y),
);
let mut modifiers = Modifiers::empty();
if shift {
modifiers.insert(Modifiers::SHIFT);
}
if ctrl {
modifiers.insert(Modifiers::CONTROL);
}
if meta {
modifiers.insert(Modifiers::META);
}
if alt {
modifiers.insert(Modifiers::ALT);
}
// held mouse buttons get set later by maintaining state, as crossterm does not provide them
EventData::Mouse(MouseData::new(
coordinates,
button,
DioxusMouseButtons::empty(),
modifiers,
))
};
let get_wheel_data = |up| {

View file

@ -11,7 +11,7 @@ documentation = "https://dioxuslabs.com"
keywords = ["dom", "ui", "gui", "react", "wasm"]
[dependencies]
dioxus-core = { path = "../core", version = "^0.2.1" }
dioxus-core = { path = "../core", version = "^0.2.1", features = ["serialize"] }
dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] }
dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [
"web"

View file

@ -178,7 +178,7 @@ fn virtual_event_from_websys_event(
})
}
"keydown" | "keypress" | "keyup" => Arc::new(KeyboardData::from(event)),
"focus" | "blur" => Arc::new(FocusData {}),
"focus" | "blur" | "focusout" | "focusin" => Arc::new(FocusData {}),
// todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
// don't have a good solution with the serialized event problem
@ -258,9 +258,9 @@ fn virtual_event_from_websys_event(
Arc::new(FormData { value, values })
}
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
"click" | "contextmenu" | "dblclick" | "doubleclick" | "drag" | "dragend" | "dragenter"
| "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown"
| "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
Arc::new(MouseData::from(event))
}
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
@ -305,6 +305,8 @@ fn event_name_from_typ(typ: &str) -> &'static str {
"keypress" => "keypress",
"keyup" => "keyup",
"focus" => "focus",
"focusout" => "focusout",
"focusin" => "focusin",
"blur" => "blur",
"change" => "change",
"input" => "input",
@ -314,6 +316,7 @@ fn event_name_from_typ(typ: &str) -> &'static str {
"click" => "click",
"contextmenu" => "contextmenu",
"doubleclick" => "doubleclick",
"dblclick" => "dblclick",
"drag" => "drag",
"dragend" => "dragend",
"dragenter" => "dragenter",
@ -374,8 +377,8 @@ fn event_name_from_typ(typ: &str) -> &'static str {
"volumechange" => "volumechange",
"waiting" => "waiting",
"toggle" => "toggle",
_ => {
panic!("unsupported event type")
a => {
panic!("unsupported event type {:?}", a);
}
}
}

View file

@ -26,7 +26,7 @@
// main thread.
//
// React solves this problem by breaking up the rendering process into a "diff" phase and a "render" phase. In Dioxus,
// the diff phase is non-blocking, using "yield_now" to allow the browser to process other events. When the diff phase
// the diff phase is non-blocking, using "work_with_deadline" to allow the browser to process other events. When the diff phase
// is finally complete, the VirtualDOM will return a set of "Mutations" for this crate to apply.
//
// Here, we schedule the "diff" phase during the browser's idle period, achieved by calling RequestIdleCallback and then