mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 14:44:12 +00:00
wip: overhaul event system
This commit is contained in:
parent
82953f2ac3
commit
7a03c1d2b4
34 changed files with 2489 additions and 1469 deletions
|
@ -85,6 +85,7 @@ members = [
|
|||
"packages/ssr",
|
||||
"packages/desktop",
|
||||
"packages/mobile",
|
||||
"packages/webview-client"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use dioxus::events::on::MouseEvent;
|
||||
use dioxus::events::DioxusEvent;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
|
@ -37,6 +39,9 @@ struct RowProps {
|
|||
label: Label,
|
||||
}
|
||||
fn Row<'a>(cx: Context<'a>, props: &'a RowProps) -> DomTree<'a> {
|
||||
let handler = move |evt: MouseEvent| {
|
||||
let g = evt.button;
|
||||
};
|
||||
cx.render(rsx! {
|
||||
tr {
|
||||
td { class:"col-md-1", "{props.row_id}" }
|
||||
|
@ -44,7 +49,7 @@ fn Row<'a>(cx: Context<'a>, props: &'a RowProps) -> DomTree<'a> {
|
|||
a { class: "lbl", "{props.label}" }
|
||||
}
|
||||
td { class: "col-md-1"
|
||||
a { class: "remove", onclick: move |_| {/* remove */}
|
||||
a { class: "remove", onclick: {handler}
|
||||
span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
|
||||
}
|
||||
}
|
||||
|
|
1023
packages/core/src/events.old.rs
Normal file
1023
packages/core/src/events.old.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,8 @@
|
|||
//! This module provides a set of common events for all Dioxus apps to target, regardless of host platform.
|
||||
//! -------------------------------------------------------------------------------------------------------
|
||||
//! An event system that's less confusing than Traits + RC;
|
||||
//! This should hopefully make it easier to port to other platforms.
|
||||
//!
|
||||
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
|
||||
//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
|
||||
//! Unfortunately, it is less efficient than the original, but hopefully it's negligible.
|
||||
|
||||
use crate::{
|
||||
innerlude::Listener,
|
||||
innerlude::{ElementId, NodeFactory, ScopeId},
|
||||
|
@ -34,6 +34,7 @@ pub struct UserEvent {
|
|||
pub event: SyntheticEvent,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SyntheticEvent {
|
||||
AnimationEvent(on::AnimationEvent),
|
||||
ClipboardEvent(on::ClipboardEvent),
|
||||
|
@ -41,7 +42,7 @@ pub enum SyntheticEvent {
|
|||
FocusEvent(on::FocusEvent),
|
||||
FormEvent(on::FormEvent),
|
||||
KeyboardEvent(on::KeyboardEvent),
|
||||
GenericEvent(on::GenericEvent),
|
||||
GenericEvent(DioxusEvent<()>),
|
||||
TouchEvent(on::TouchEvent),
|
||||
ToggleEvent(on::ToggleEvent),
|
||||
MediaEvent(on::MediaEvent),
|
||||
|
@ -51,31 +52,6 @@ pub enum SyntheticEvent {
|
|||
TransitionEvent(on::TransitionEvent),
|
||||
PointerEvent(on::PointerEvent),
|
||||
}
|
||||
// ImageEvent(event_data::ImageEvent),
|
||||
|
||||
impl std::fmt::Debug for SyntheticEvent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let name = match self {
|
||||
SyntheticEvent::ClipboardEvent(_) => "ClipboardEvent",
|
||||
SyntheticEvent::CompositionEvent(_) => "CompositionEvent",
|
||||
SyntheticEvent::KeyboardEvent(_) => "KeyboardEvent",
|
||||
SyntheticEvent::FocusEvent(_) => "FocusEvent",
|
||||
SyntheticEvent::FormEvent(_) => "FormEvent",
|
||||
SyntheticEvent::SelectionEvent(_) => "SelectionEvent",
|
||||
SyntheticEvent::TouchEvent(_) => "TouchEvent",
|
||||
SyntheticEvent::WheelEvent(_) => "WheelEvent",
|
||||
SyntheticEvent::MediaEvent(_) => "MediaEvent",
|
||||
SyntheticEvent::AnimationEvent(_) => "AnimationEvent",
|
||||
SyntheticEvent::TransitionEvent(_) => "TransitionEvent",
|
||||
SyntheticEvent::ToggleEvent(_) => "ToggleEvent",
|
||||
SyntheticEvent::MouseEvent(_) => "MouseEvent",
|
||||
SyntheticEvent::PointerEvent(_) => "PointerEvent",
|
||||
SyntheticEvent::GenericEvent(_) => "GenericEvent",
|
||||
};
|
||||
|
||||
f.debug_struct("VirtualEvent").field("type", &name).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Priority of Event Triggers.
|
||||
///
|
||||
|
@ -129,82 +105,101 @@ pub enum EventPriority {
|
|||
Low = 0,
|
||||
}
|
||||
|
||||
pub(crate) fn event_meta(event: &UserEvent) -> (bool, EventPriority) {
|
||||
use EventPriority::*;
|
||||
|
||||
match event.name {
|
||||
// clipboard
|
||||
"copy" | "cut" | "paste" => (true, Medium),
|
||||
|
||||
// Composition
|
||||
"compositionend" | "compositionstart" | "compositionupdate" => (true, Low),
|
||||
|
||||
// Keyboard
|
||||
"keydown" | "keypress" | "keyup" => (true, High),
|
||||
|
||||
// Focus
|
||||
"focus" | "blur" => (true, Low),
|
||||
|
||||
// Form
|
||||
"change" | "input" | "invalid" | "reset" | "submit" => (true, Medium),
|
||||
|
||||
// Mouse
|
||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||
| "mouseleave" | "mouseout" | "mouseover" | "mouseup" => (true, High),
|
||||
|
||||
"mousemove" => (false, Medium),
|
||||
|
||||
// Pointer
|
||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||
(true, Medium)
|
||||
#[derive(Debug)]
|
||||
pub struct DioxusEvent<T: Send> {
|
||||
inner: T,
|
||||
raw: Box<dyn Any + Send>,
|
||||
}
|
||||
|
||||
// Selection
|
||||
"select" | "touchcancel" | "touchend" => (true, Medium),
|
||||
impl<T: Send + Sync> DioxusEvent<T> {
|
||||
pub fn new<F: Send + 'static>(inner: T, raw: F) -> Self {
|
||||
let raw = Box::new(raw);
|
||||
Self { inner, raw }
|
||||
}
|
||||
|
||||
// Touch
|
||||
"touchmove" | "touchstart" => (true, Medium),
|
||||
/// Return a reference to the raw event. User will need to downcast the event to the right platform-specific type.
|
||||
pub fn native<E: 'static>(&self) -> Option<&E> {
|
||||
self.raw.downcast_ref()
|
||||
}
|
||||
|
||||
// Wheel
|
||||
"scroll" | "wheel" => (false, Medium),
|
||||
/// Returns whether or not a specific event is a bubbling event
|
||||
pub fn bubbles(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
/// Sets or returns whether the event should propagate up the hierarchy or not
|
||||
pub fn cancel_bubble(&self) {
|
||||
todo!()
|
||||
}
|
||||
/// Returns whether or not an event can have its default action prevented
|
||||
pub fn cancelable(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
/// Returns whether the event is composed or not
|
||||
pub fn composed(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Media
|
||||
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||
| "timeupdate" | "volumechange" | "waiting" => (true, Medium),
|
||||
// Currently not supported because those no way we could possibly support it
|
||||
// just cast the event to the right platform-specific type and return it
|
||||
// /// Returns the event's path
|
||||
// pub fn composed_path(&self) -> String {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// Animation
|
||||
"animationstart" | "animationend" | "animationiteration" => (true, Medium),
|
||||
/// Returns the element whose event listeners triggered the event
|
||||
pub fn current_target(&self) {
|
||||
todo!()
|
||||
}
|
||||
/// Returns whether or not the preventDefault method was called for the event
|
||||
pub fn default_prevented(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
/// Returns which phase of the event flow is currently being evaluated
|
||||
pub fn event_phase(&self) -> u16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Transition
|
||||
"transitionend" => (true, Medium),
|
||||
/// Returns whether or not an event is trusted
|
||||
pub fn is_trusted(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Toggle
|
||||
"toggle" => (true, Medium),
|
||||
/// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
|
||||
pub fn prevent_default(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
_ => (true, Low),
|
||||
/// Prevents other listeners of the same event from being called
|
||||
pub fn stop_immediate_propagation(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Prevents further propagation of an event during event flow
|
||||
pub fn stop_propagation(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the element that triggered the event
|
||||
pub fn target(&self) -> Option<Box<dyn Any>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the time (in milliseconds relative to the epoch) at which the event was created
|
||||
pub fn time_stamp(&self) -> f64 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub use on::{
|
||||
AnimationEvent, ClipboardEvent, CompositionEvent, FocusEvent, FormEvent, GenericEvent, KeyCode,
|
||||
KeyboardEvent, MediaEvent, MouseEvent, PointerEvent, SelectionEvent, ToggleEvent, TouchEvent,
|
||||
TransitionEvent, WheelEvent,
|
||||
};
|
||||
impl<T: Send + Sync> std::ops::Deref for DioxusEvent<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub mod on {
|
||||
//! This module defines the synthetic events that all Dioxus apps enable. No matter the platform, every dioxus renderer
|
||||
//! will implement the same events and same behavior (bubbling, cancelation, etc).
|
||||
//!
|
||||
//! Synthetic events are immutable and wrapped in Arc. It is the intention for Dioxus renderers to re-use the underyling
|
||||
//! Arc allocation through "get_mut"
|
||||
//!
|
||||
//! React recently dropped support for re-using event allocation and just passes the real event along.
|
||||
use super::*;
|
||||
|
||||
macro_rules! event_directory {
|
||||
( $(
|
||||
$( #[$attr:meta] )*
|
||||
|
@ -217,13 +212,14 @@ pub mod on {
|
|||
)* ) => {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
pub struct $wrapper(pub Arc<dyn $eventdata>);
|
||||
#[derive(Debug)]
|
||||
pub struct $wrapper(pub DioxusEvent<$eventdata>);
|
||||
|
||||
// todo: derefing to the event is fine (and easy) but breaks some IDE stuff like (go to source)
|
||||
// going to source in fact takes you to the source of Rc which is... less than useful
|
||||
// Either we ask for this to be changed in Rust-analyzer or manually impkement the trait
|
||||
impl Deref for $wrapper {
|
||||
type Target = Arc<dyn $eventdata>;
|
||||
type Target = DioxusEvent<$eventdata>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
|
@ -599,57 +595,20 @@ pub mod on {
|
|||
];
|
||||
}
|
||||
|
||||
pub struct GenericEvent(pub Arc<dyn GenericEventInner>);
|
||||
|
||||
pub trait GenericEventInner {
|
||||
/// Return a reference to the raw event. User will need to downcast the event to the right platform-specific type.
|
||||
fn raw_event(&self) -> &dyn Any;
|
||||
/// Returns whether or not a specific event is a bubbling event
|
||||
fn bubbles(&self) -> bool;
|
||||
/// Sets or returns whether the event should propagate up the hierarchy or not
|
||||
fn cancel_bubble(&self);
|
||||
/// Returns whether or not an event can have its default action prevented
|
||||
fn cancelable(&self) -> bool;
|
||||
/// Returns whether the event is composed or not
|
||||
fn composed(&self) -> bool;
|
||||
|
||||
// Currently not supported because those no way we could possibly support it
|
||||
// just cast the event to the right platform-specific type and return it
|
||||
// /// Returns the event's path
|
||||
// fn composed_path(&self) -> String;
|
||||
|
||||
/// Returns the element whose event listeners triggered the event
|
||||
fn current_target(&self);
|
||||
/// Returns whether or not the preventDefault method was called for the event
|
||||
fn default_prevented(&self) -> bool;
|
||||
/// Returns which phase of the event flow is currently being evaluated
|
||||
fn event_phase(&self) -> u16;
|
||||
/// Returns whether or not an event is trusted
|
||||
fn is_trusted(&self) -> bool;
|
||||
/// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
|
||||
fn prevent_default(&self);
|
||||
/// Prevents other listeners of the same event from being called
|
||||
fn stop_immediate_propagation(&self);
|
||||
/// Prevents further propagation of an event during event flow
|
||||
fn stop_propagation(&self);
|
||||
/// Returns the element that triggered the event
|
||||
fn target(&self);
|
||||
/// Returns the time (in milliseconds relative to the epoch) at which the event was created
|
||||
fn time_stamp(&self) -> f64;
|
||||
}
|
||||
|
||||
pub trait ClipboardEventInner {
|
||||
#[derive(Debug)]
|
||||
pub struct ClipboardEventInner(
|
||||
// DOMDataTransfer clipboardData
|
||||
);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompositionEventInner {
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
pub trait CompositionEventInner {
|
||||
fn data(&self) -> String;
|
||||
}
|
||||
|
||||
pub trait KeyboardEventInner {
|
||||
fn alt_key(&self) -> bool;
|
||||
|
||||
fn char_code(&self) -> u32;
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardEventInner {
|
||||
pub alt_key: bool,
|
||||
pub char_code: u32,
|
||||
|
||||
/// Identify which "key" was entered.
|
||||
///
|
||||
|
@ -670,7 +629,7 @@ pub mod on {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
fn key(&self) -> String;
|
||||
pub key: String,
|
||||
|
||||
/// Get the key code as an enum Variant.
|
||||
///
|
||||
|
@ -690,122 +649,122 @@ pub mod on {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
fn key_code(&self) -> KeyCode;
|
||||
|
||||
/// Check if the ctrl key was pressed down
|
||||
fn ctrl_key(&self) -> bool;
|
||||
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||
|
||||
fn locale(&self) -> String;
|
||||
fn location(&self) -> usize;
|
||||
fn meta_key(&self) -> bool;
|
||||
fn repeat(&self) -> bool;
|
||||
fn shift_key(&self) -> bool;
|
||||
fn which(&self) -> usize;
|
||||
pub key_code: KeyCode,
|
||||
pub ctrl_key: bool,
|
||||
pub locale: String,
|
||||
pub location: usize,
|
||||
pub meta_key: bool,
|
||||
pub repeat: bool,
|
||||
pub shift_key: bool,
|
||||
pub which: usize,
|
||||
// get_modifier_state: bool,
|
||||
}
|
||||
|
||||
pub trait FocusEventInner {
|
||||
/* DOMEventInnerTarget relatedTarget */
|
||||
#[derive(Debug)]
|
||||
pub struct FocusEventInner {/* DOMEventInner: Send + SyncTarget relatedTarget */}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FormEventInner {
|
||||
/* DOMEventInner: Send + SyncTarget relatedTarget */
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
pub trait FormEventInner {
|
||||
fn value(&self) -> String;
|
||||
#[derive(Debug)]
|
||||
pub struct MouseEventInner {
|
||||
pub alt_key: bool,
|
||||
pub button: i16,
|
||||
pub buttons: u16,
|
||||
pub client_x: i32,
|
||||
pub client_y: i32,
|
||||
pub ctrl_key: bool,
|
||||
pub meta_key: bool,
|
||||
pub page_x: i32,
|
||||
pub page_y: i32,
|
||||
pub screen_x: i32,
|
||||
pub screen_y: i32,
|
||||
pub shift_key: bool,
|
||||
// fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||
}
|
||||
|
||||
pub trait MouseEventInner {
|
||||
fn alt_key(&self) -> bool;
|
||||
fn button(&self) -> i16;
|
||||
fn buttons(&self) -> u16;
|
||||
/// Get the X coordinate of the mouse relative to the window
|
||||
fn client_x(&self) -> i32;
|
||||
fn client_y(&self) -> i32;
|
||||
fn ctrl_key(&self) -> bool;
|
||||
fn meta_key(&self) -> bool;
|
||||
fn page_x(&self) -> i32;
|
||||
fn page_y(&self) -> i32;
|
||||
fn screen_x(&self) -> i32;
|
||||
fn screen_y(&self) -> i32;
|
||||
fn shift_key(&self) -> bool;
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||
}
|
||||
|
||||
pub trait PointerEventInner {
|
||||
#[derive(Debug)]
|
||||
pub struct PointerEventInner {
|
||||
// Mouse only
|
||||
fn alt_key(&self) -> bool;
|
||||
fn button(&self) -> i16;
|
||||
fn buttons(&self) -> u16;
|
||||
fn client_x(&self) -> i32;
|
||||
fn client_y(&self) -> i32;
|
||||
fn ctrl_key(&self) -> bool;
|
||||
fn meta_key(&self) -> bool;
|
||||
fn page_x(&self) -> i32;
|
||||
fn page_y(&self) -> i32;
|
||||
fn screen_x(&self) -> i32;
|
||||
fn screen_y(&self) -> i32;
|
||||
fn shift_key(&self) -> bool;
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||
fn pointer_id(&self) -> i32;
|
||||
fn width(&self) -> i32;
|
||||
fn height(&self) -> i32;
|
||||
fn pressure(&self) -> f32;
|
||||
fn tangential_pressure(&self) -> f32;
|
||||
fn tilt_x(&self) -> i32;
|
||||
fn tilt_y(&self) -> i32;
|
||||
fn twist(&self) -> i32;
|
||||
fn pointer_type(&self) -> String;
|
||||
fn is_primary(&self) -> bool;
|
||||
pub alt_key: bool,
|
||||
pub button: i16,
|
||||
pub buttons: u16,
|
||||
pub client_x: i32,
|
||||
pub client_y: i32,
|
||||
pub ctrl_key: bool,
|
||||
pub meta_key: bool,
|
||||
pub page_x: i32,
|
||||
pub page_y: i32,
|
||||
pub screen_x: i32,
|
||||
pub screen_y: i32,
|
||||
pub shift_key: bool,
|
||||
pub pointer_id: i32,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub pressure: f32,
|
||||
pub tangential_pressure: f32,
|
||||
pub tilt_x: i32,
|
||||
pub tilt_y: i32,
|
||||
pub twist: i32,
|
||||
pub pointer_type: String,
|
||||
pub is_primary: bool,
|
||||
// pub get_modifier_state: bool,
|
||||
}
|
||||
|
||||
pub trait SelectionEventInner {}
|
||||
#[derive(Debug)]
|
||||
pub struct SelectionEventInner {}
|
||||
|
||||
pub trait TouchEventInner {
|
||||
fn alt_key(&self) -> bool;
|
||||
fn ctrl_key(&self) -> bool;
|
||||
fn meta_key(&self) -> bool;
|
||||
fn shift_key(&self) -> bool;
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||
#[derive(Debug)]
|
||||
pub struct TouchEventInner {
|
||||
pub alt_key: bool,
|
||||
pub ctrl_key: bool,
|
||||
pub meta_key: bool,
|
||||
pub shift_key: bool,
|
||||
// get_modifier_state: bool,
|
||||
// changedTouches: DOMTouchList,
|
||||
// targetTouches: DOMTouchList,
|
||||
// touches: DOMTouchList,
|
||||
}
|
||||
|
||||
pub trait UIEventInner {
|
||||
// DOMAbstractView view
|
||||
fn detail(&self) -> i32;
|
||||
#[derive(Debug)]
|
||||
pub struct WheelEventInner {
|
||||
pub delta_mode: u32,
|
||||
pub delta_x: f64,
|
||||
pub delta_y: f64,
|
||||
pub delta_z: f64,
|
||||
}
|
||||
|
||||
pub trait WheelEventInner {
|
||||
fn delta_mode(&self) -> u32;
|
||||
fn delta_x(&self) -> f64;
|
||||
fn delta_y(&self) -> f64;
|
||||
fn delta_z(&self) -> f64;
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct MediaEventInner {}
|
||||
|
||||
pub trait MediaEventInner {}
|
||||
|
||||
pub trait ImageEventInner {
|
||||
#[derive(Debug)]
|
||||
pub struct ImageEventInner {
|
||||
// load error
|
||||
pub load_error: bool,
|
||||
}
|
||||
|
||||
pub trait AnimationEventInner {
|
||||
fn animation_name(&self) -> String;
|
||||
fn pseudo_element(&self) -> String;
|
||||
fn elapsed_time(&self) -> f32;
|
||||
#[derive(Debug)]
|
||||
pub struct AnimationEventInner {
|
||||
pub animation_name: String,
|
||||
pub pseudo_element: String,
|
||||
pub elapsed_time: f32,
|
||||
}
|
||||
|
||||
pub trait TransitionEventInner {
|
||||
fn property_name(&self) -> String;
|
||||
fn pseudo_element(&self) -> String;
|
||||
fn elapsed_time(&self) -> f32;
|
||||
#[derive(Debug)]
|
||||
pub struct TransitionEventInner {
|
||||
pub property_name: String,
|
||||
pub pseudo_element: String,
|
||||
pub elapsed_time: f32,
|
||||
}
|
||||
|
||||
pub trait ToggleEventInner {}
|
||||
#[derive(Debug)]
|
||||
pub struct ToggleEventInner {}
|
||||
}
|
||||
|
||||
pub use util::KeyCode;
|
||||
mod util {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum KeyCode {
|
||||
Backspace = 8,
|
||||
Tab = 9,
|
||||
|
@ -1019,5 +978,63 @@ pub mod on {
|
|||
*self as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn event_meta(event: &UserEvent) -> (bool, EventPriority) {
|
||||
use EventPriority::*;
|
||||
|
||||
match event.name {
|
||||
// clipboard
|
||||
"copy" | "cut" | "paste" => (true, Medium),
|
||||
|
||||
// Composition
|
||||
"compositionend" | "compositionstart" | "compositionupdate" => (true, Low),
|
||||
|
||||
// Keyboard
|
||||
"keydown" | "keypress" | "keyup" => (true, High),
|
||||
|
||||
// Focus
|
||||
"focus" | "blur" => (true, Low),
|
||||
|
||||
// Form
|
||||
"change" | "input" | "invalid" | "reset" | "submit" => (true, Medium),
|
||||
|
||||
// Mouse
|
||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||
| "mouseleave" | "mouseout" | "mouseover" | "mouseup" => (true, High),
|
||||
|
||||
"mousemove" => (false, Medium),
|
||||
|
||||
// Pointer
|
||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||
(true, Medium)
|
||||
}
|
||||
|
||||
// Selection
|
||||
"select" | "touchcancel" | "touchend" => (true, Medium),
|
||||
|
||||
// Touch
|
||||
"touchmove" | "touchstart" => (true, Medium),
|
||||
|
||||
// Wheel
|
||||
"scroll" | "wheel" => (false, Medium),
|
||||
|
||||
// Media
|
||||
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||
| "timeupdate" | "volumechange" | "waiting" => (true, Medium),
|
||||
|
||||
// Animation
|
||||
"animationstart" | "animationend" | "animationiteration" => (true, Medium),
|
||||
|
||||
// Transition
|
||||
"transitionend" => (true, Medium),
|
||||
|
||||
// Toggle
|
||||
"toggle" => (true, Medium),
|
||||
|
||||
_ => (true, Low),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ pub mod context;
|
|||
pub mod diff;
|
||||
pub mod diff_stack;
|
||||
pub mod events;
|
||||
// pub mod events2;
|
||||
pub mod heuristics;
|
||||
pub mod hooklist;
|
||||
pub mod hooks;
|
||||
|
@ -61,8 +62,8 @@ pub(crate) mod innerlude {
|
|||
|
||||
pub use crate::innerlude::{
|
||||
Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority, LazyNodes, MountType,
|
||||
Mutations, NodeFactory, Properties, ScopeId, SuspendedContext, SyntheticEvent, TaskHandle,
|
||||
TestDom, ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC,
|
||||
Mutations, NodeFactory, Properties, SchedulerMsg, ScopeId, SuspendedContext, SyntheticEvent,
|
||||
TaskHandle, TestDom, ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC,
|
||||
};
|
||||
|
||||
pub mod prelude {
|
||||
|
@ -78,4 +79,5 @@ pub mod exports {
|
|||
//! Important dependencies that are used by the rest of the library
|
||||
// the foundation of this library
|
||||
pub use bumpalo;
|
||||
pub use futures_channel;
|
||||
}
|
||||
|
|
|
@ -185,7 +185,6 @@ pub enum DomEdit<'bump> {
|
|||
root: u64,
|
||||
},
|
||||
|
||||
RemoveAllChildren,
|
||||
CreateTextNode {
|
||||
text: &'bump str,
|
||||
id: u64,
|
||||
|
@ -232,7 +231,6 @@ impl DomEdit<'_> {
|
|||
DomEdit::AppendChildren { .. } => id == "AppendChildren",
|
||||
DomEdit::ReplaceWith { .. } => id == "ReplaceWith",
|
||||
DomEdit::Remove { .. } => id == "Remove",
|
||||
DomEdit::RemoveAllChildren => id == "RemoveAllChildren",
|
||||
DomEdit::CreateTextNode { .. } => id == "CreateTextNode",
|
||||
DomEdit::CreateElement { .. } => id == "CreateElement",
|
||||
DomEdit::CreateElementNs { .. } => id == "CreateElementNs",
|
||||
|
|
|
@ -43,16 +43,6 @@ impl ResourcePool {
|
|||
inner.get_mut(idx.0)
|
||||
}
|
||||
|
||||
// return a bumpframe with a lifetime attached to the arena borrow
|
||||
// this is useful for merging lifetimes
|
||||
pub fn with_scope_vnode<'b>(
|
||||
&self,
|
||||
_id: ScopeId,
|
||||
_f: impl FnOnce(&mut Scope) -> &VNode<'b>,
|
||||
) -> Option<&VNode<'b>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn try_remove(&self, id: ScopeId) -> Option<Scope> {
|
||||
let inner = unsafe { &mut *self.components.get() };
|
||||
Some(inner.remove(id.0))
|
||||
|
|
|
@ -106,7 +106,8 @@ pub enum SchedulerMsg {
|
|||
}
|
||||
|
||||
pub enum TaskMsg {
|
||||
SubmitTask(FiberTask, u64),
|
||||
// SubmitTask(FiberTask, u64),
|
||||
// SubmitTask(FiberTask, u64),
|
||||
ToggleTask(u64),
|
||||
PauseTask(u64),
|
||||
ResumeTask(u64),
|
||||
|
@ -163,7 +164,10 @@ pub(crate) struct Scheduler {
|
|||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(crate) fn new(
|
||||
sender: UnboundedSender<SchedulerMsg>,
|
||||
receiver: UnboundedReceiver<SchedulerMsg>,
|
||||
) -> Self {
|
||||
/*
|
||||
Preallocate 2000 elements and 100 scopes to avoid dynamic allocation.
|
||||
Perhaps this should be configurable from some external config?
|
||||
|
@ -173,7 +177,6 @@ impl Scheduler {
|
|||
|
||||
let heuristics = HeuristicsEngine::new();
|
||||
|
||||
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
|
||||
let task_counter = Rc::new(Cell::new(0));
|
||||
|
||||
let channel = EventChannel {
|
||||
|
@ -183,15 +186,19 @@ impl Scheduler {
|
|||
let sender = sender.clone();
|
||||
Rc::new(move |id| sender.unbounded_send(SchedulerMsg::Immediate(id)).unwrap())
|
||||
},
|
||||
// todo: we want to get the futures out of the scheduler message
|
||||
// the scheduler message should be send/sync
|
||||
submit_task: {
|
||||
Rc::new(move |fiber_task| {
|
||||
let task_id = task_counter.get();
|
||||
task_counter.set(task_id + 1);
|
||||
sender
|
||||
.unbounded_send(SchedulerMsg::Task(TaskMsg::SubmitTask(
|
||||
fiber_task, task_id,
|
||||
)))
|
||||
.unwrap();
|
||||
|
||||
todo!();
|
||||
// sender
|
||||
// .unbounded_send(SchedulerMsg::Task(TaskMsg::SubmitTask(
|
||||
// fiber_task, task_id,
|
||||
// )))
|
||||
// .unwrap();
|
||||
TaskHandle {
|
||||
our_id: task_id,
|
||||
sender: sender.clone(),
|
||||
|
|
|
@ -12,7 +12,8 @@ pub struct TestDom {
|
|||
impl TestDom {
|
||||
pub fn new() -> TestDom {
|
||||
let bump = Bump::new();
|
||||
let scheduler = Scheduler::new();
|
||||
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
|
||||
let scheduler = Scheduler::new(sender, receiver);
|
||||
TestDom { bump, scheduler }
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
//! A threadsafe wrapper for the VirtualDom
|
||||
|
||||
//!
|
||||
//! This is an experimental module, and must be explicitly opted-into.
|
||||
//!
|
||||
//! It's not guaranteed that this module produces safe results, so use at your own peril.
|
||||
//!
|
||||
//! The only real "right" answer to a Send VirtualDom is by ensuring all hook data is Send
|
||||
//!
|
||||
//!
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use crate::VirtualDom;
|
||||
|
@ -18,9 +25,17 @@ use crate::VirtualDom;
|
|||
/// directly. Even then, it's not possible to access any hook data. This means that non-Send types are only "in play"
|
||||
/// while the VirtualDom is locked with a non-Send marker.
|
||||
///
|
||||
/// Note that calling "wait for work" on the regular VirtualDom is inherently non-Send. If there are async tasks that
|
||||
/// need to be awaited, they must be done thread-local since we don't place any requirements on user tasks. This can be
|
||||
/// done with the function "spawn_local" in either tokio or async_std.
|
||||
/// Calling "wait for work" on the ThreadsafeVirtualDom does indeed work, because this method only accesses `Send` types.
|
||||
/// Otherwise, the VirtualDom must be unlocked on the current thread to modify any data.
|
||||
///
|
||||
/// Dioxus does have the concept of local tasks and non-local tasks.
|
||||
///
|
||||
/// For the ThreadsafeVirtualDom, non-Send tasks are not ran - and will error out during a Debug build if one is submitted.
|
||||
///
|
||||
///
|
||||
///
|
||||
/// When Tasks are submitted to a thread-local executor,
|
||||
///
|
||||
pub struct ThreadsafeVirtualDom {
|
||||
inner: Arc<Mutex<VirtualDom>>,
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@
|
|||
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||
//! Additional functionality is defined in the respective files.
|
||||
|
||||
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
|
||||
use crate::innerlude::*;
|
||||
use std::{any::Any, rc::Rc, sync::Arc};
|
||||
use std::{any::Any, rc::Rc};
|
||||
|
||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||
///
|
||||
|
@ -121,6 +123,20 @@ impl VirtualDom {
|
|||
/// let mutations = dom.rebuild();
|
||||
/// ```
|
||||
pub fn new_with_props<P: 'static + Send>(root: FC<P>, root_props: P) -> Self {
|
||||
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
|
||||
Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
|
||||
}
|
||||
|
||||
/// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler.
|
||||
///
|
||||
/// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
|
||||
/// VirtualDom to be created just to retrive its channel receiver.
|
||||
pub fn new_with_props_and_scheduler<P: 'static + Send>(
|
||||
root: FC<P>,
|
||||
root_props: P,
|
||||
sender: UnboundedSender<SchedulerMsg>,
|
||||
receiver: UnboundedReceiver<SchedulerMsg>,
|
||||
) -> Self {
|
||||
let root_fc = Box::new(root);
|
||||
|
||||
let root_props: Rc<dyn Any + Send> = Rc::new(root_props);
|
||||
|
@ -132,7 +148,7 @@ impl VirtualDom {
|
|||
std::mem::transmute(root(Context { scope }, props))
|
||||
});
|
||||
|
||||
let scheduler = Scheduler::new();
|
||||
let scheduler = Scheduler::new(sender, receiver);
|
||||
|
||||
let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
|
||||
Scope::new(
|
||||
|
@ -343,47 +359,46 @@ impl VirtualDom {
|
|||
/// Waits for the scheduler to have work
|
||||
/// This lets us poll async tasks during idle periods without blocking the main thread.
|
||||
pub async fn wait_for_work(&mut self) {
|
||||
todo!("making vdom send right now");
|
||||
// if self.scheduler.has_any_work() {
|
||||
// log::debug!("No need to wait for work, we already have some");
|
||||
// return;
|
||||
// }
|
||||
if self.scheduler.has_any_work() {
|
||||
log::debug!("No need to wait for work, we already have some");
|
||||
return;
|
||||
}
|
||||
|
||||
// log::debug!("No active work.... waiting for some...");
|
||||
// use futures_util::StreamExt;
|
||||
log::debug!("No active work.... waiting for some...");
|
||||
use futures_util::StreamExt;
|
||||
|
||||
// // right now this won't poll events if there is ongoing work
|
||||
// // in the future we want to prioritize some events over ongoing work
|
||||
// // this is coming in the "priorities" PR
|
||||
// right now this won't poll events if there is ongoing work
|
||||
// in the future we want to prioritize some events over ongoing work
|
||||
// this is coming in the "priorities" PR
|
||||
|
||||
// // Wait for any new events if we have nothing to do
|
||||
// // todo: poll the events once even if there is work to do to prevent starvation
|
||||
// futures_util::select! {
|
||||
// _ = self.scheduler.async_tasks.next() => {}
|
||||
// msg = self.scheduler.receiver.next() => {
|
||||
// match msg.unwrap() {
|
||||
// SchedulerMsg::Task(t) => todo!(),
|
||||
// SchedulerMsg::Immediate(im) => {
|
||||
// self.scheduler.dirty_scopes.insert(im);
|
||||
// }
|
||||
// SchedulerMsg::UiEvent(evt) => {
|
||||
// self.scheduler.ui_events.push_back(evt);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
// Wait for any new events if we have nothing to do
|
||||
// todo: poll the events once even if there is work to do to prevent starvation
|
||||
futures_util::select! {
|
||||
_ = self.scheduler.async_tasks.next() => {}
|
||||
msg = self.scheduler.receiver.next() => {
|
||||
match msg.unwrap() {
|
||||
SchedulerMsg::Task(t) => todo!(),
|
||||
SchedulerMsg::Immediate(im) => {
|
||||
self.scheduler.dirty_scopes.insert(im);
|
||||
}
|
||||
SchedulerMsg::UiEvent(evt) => {
|
||||
self.scheduler.ui_events.push_back(evt);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// while let Ok(Some(msg)) = self.scheduler.receiver.try_next() {
|
||||
// match msg {
|
||||
// SchedulerMsg::Task(t) => todo!(),
|
||||
// SchedulerMsg::Immediate(im) => {
|
||||
// self.scheduler.dirty_scopes.insert(im);
|
||||
// }
|
||||
// SchedulerMsg::UiEvent(evt) => {
|
||||
// self.scheduler.ui_events.push_back(evt);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
while let Ok(Some(msg)) = self.scheduler.receiver.try_next() {
|
||||
match msg {
|
||||
SchedulerMsg::Task(t) => todo!(),
|
||||
SchedulerMsg::Immediate(im) => {
|
||||
self.scheduler.dirty_scopes.insert(im);
|
||||
}
|
||||
SchedulerMsg::UiEvent(evt) => {
|
||||
self.scheduler.ui_events.push_back(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,14 +422,5 @@ impl std::fmt::Display for VirtualDom {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Send safety...
|
||||
|
||||
The VirtualDom can only be "send" if the internals exposed to user code are also "send".
|
||||
|
||||
IE it's okay to move an Rc from one thread to another thread as long as it's only used internally
|
||||
|
||||
*/
|
||||
|
||||
// we never actually use the contents of this root caller
|
||||
struct RootCaller(Rc<dyn for<'b> Fn(&'b Scope) -> DomTree<'b> + 'static>);
|
||||
|
|
|
@ -16,8 +16,4 @@ async fn event_queue_works() {
|
|||
|
||||
let mut dom = VirtualDom::new(App);
|
||||
let edits = dom.rebuild();
|
||||
|
||||
async_std::task::spawn_local(async move {
|
||||
// let mutations = dom.run_unbounded().await;
|
||||
});
|
||||
}
|
||||
|
|
3
packages/desktop/.vscode/settings.json
vendored
3
packages/desktop/.vscode/settings.json
vendored
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
"rust-analyzer.inlayHints.enable": true,
|
||||
"rust-analyzer.cargo.allFeatures": true
|
||||
}
|
||||
|
|
|
@ -19,15 +19,16 @@ thiserror = "1.0.23"
|
|||
log = "0.4.13"
|
||||
fern = { version = "0.6.0", features = ["colored"] }
|
||||
html-escape = "0.2.9"
|
||||
wry = "0.11.0"
|
||||
# wry = { version = "0.12.2", git = "https://github.com/jkelleyrtp/wry.git", branch = "jk/fnmut_rpc" }
|
||||
wry = "0.12.2"
|
||||
tokio = { version = "1.12.0", features = ["full"] }
|
||||
futures-channel = "0.3.16"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus-html = { path = "../html" }
|
||||
tide = "0.15.0"
|
||||
tide-websockets = "0.3.0"
|
||||
dioxus-core-macro = { path = "../core-macro" }
|
||||
dioxus-hooks = { path = "../hooks" }
|
||||
# thiserror = "1.0.23"
|
||||
# log = "0.4.13"
|
||||
# fern = { version = "0.6.0", features = ["colored"] }
|
||||
|
|
|
@ -6,26 +6,19 @@ Dioxus-webview is an attempt at making a simpler "Tauri" where creating desktop
|
|||
|
||||
```rust
|
||||
// main.rs
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
dioxus_desktop::new(|cx, props|{
|
||||
let (count, set_count) = use_state(cx, || 0);
|
||||
cx.render(html! {
|
||||
<div>
|
||||
<h1> "Dioxus Desktop Demo" </h1>
|
||||
<p> "Count is {count}"</p>
|
||||
<button onclick=|_| set_count(count + 1) >
|
||||
"Click to increment"
|
||||
</button>
|
||||
</div>
|
||||
})
|
||||
})
|
||||
.configure_webview(|view| {
|
||||
// custom webview config options
|
||||
})
|
||||
fn main() {
|
||||
dioxus_desktop::new(App, |c| c)
|
||||
.launch()
|
||||
.await;
|
||||
}
|
||||
static App: FC<()> = |cx, props|{
|
||||
let (count, set_count) = use_state(cx, || 0);
|
||||
rsx!(cx, div {
|
||||
h1 { "Dioxus Desktop Demo" }
|
||||
p { "Count is {count}"}
|
||||
button { onclick: move |_| count += 1}
|
||||
})
|
||||
};
|
||||
```
|
||||
|
||||
and then to create a native .app:
|
||||
|
@ -45,3 +38,8 @@ By bridging the native process, desktop apps can access full multithreading powe
|
|||
Dioxus-desktop is a pure liveview application where all of the state and event handlers are proxied through the liveview and into the native process. For pure server-based liveview, this would normally be too slow (in both render performance and latency), but because the VDom is local, desktop apps are just as fast as Electron.
|
||||
|
||||
Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime _is_ needed - we recommend async-std in Tokio mode.
|
||||
|
||||
|
||||
## Async Runtime
|
||||
|
||||
|
||||
|
|
33
packages/desktop/examples/core.rs
Normal file
33
packages/desktop/examples/core.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
let (window_loop, tasks) = dioxus_desktop::start(App, |c| c);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
//
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(async move {
|
||||
let mut vir = VirtualDom::new_with_props(root, props);
|
||||
let channel = vir.get_event_sender();
|
||||
loop {
|
||||
vir.wait_for_work().await;
|
||||
let edits = vir.run_with_deadline(|| false);
|
||||
let edit_string = serde_json::to_string(&edits[0].edits).unwrap();
|
||||
event_tx.send(edit_string).unwrap();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window_loop.run();
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
//
|
||||
cx.render(rsx!(div {}))
|
||||
};
|
111
packages/desktop/examples/crm.rs
Normal file
111
packages/desktop/examples/crm.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::set_up_logging(true);
|
||||
dioxus_desktop::launch(App, |c| c).unwrap();
|
||||
}
|
||||
|
||||
enum Scene {
|
||||
ClientsList,
|
||||
NewClientForm,
|
||||
Settings,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Client {
|
||||
pub first_name: String,
|
||||
pub last_name: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx, _| {
|
||||
let scene = use_state(cx, || Scene::ClientsList);
|
||||
let clients = use_ref(cx, || vec![] as Vec<Client>);
|
||||
|
||||
let firstname = use_state(cx, || String::new());
|
||||
let lastname = use_state(cx, || String::new());
|
||||
let description = use_state(cx, || String::new());
|
||||
|
||||
let scene = match *scene {
|
||||
Scene::ClientsList => {
|
||||
rsx!(cx, div { class: "crm"
|
||||
h2 { "List of clients" margin_bottom: "10px" }
|
||||
div { class: "clients" margin_left: "10px"
|
||||
{clients.read().iter().map(|client| rsx!(
|
||||
div { class: "client" style: "margin-bottom: 50px"
|
||||
p { "First Name: {client.first_name}" }
|
||||
p { "Last Name: {client.last_name}" }
|
||||
p {"Description: {client.description}"}
|
||||
})
|
||||
)}
|
||||
}
|
||||
button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
|
||||
button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" }
|
||||
})
|
||||
}
|
||||
Scene::NewClientForm => {
|
||||
let add_new = move |_| {
|
||||
clients.write().push(Client {
|
||||
description: (*description).clone(),
|
||||
first_name: (*firstname).clone(),
|
||||
last_name: (*lastname).clone(),
|
||||
});
|
||||
description.set(String::new());
|
||||
firstname.set(String::new());
|
||||
lastname.set(String::new());
|
||||
};
|
||||
rsx!(cx, div { class: "crm"
|
||||
h2 {"Add new client" margin_bottom: "10px" }
|
||||
form { class: "pure-form"
|
||||
input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
|
||||
oninput: move |evt| firstname.set(evt.value())
|
||||
}
|
||||
input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
|
||||
oninput: move |evt| lastname.set(evt.value())
|
||||
}
|
||||
textarea { class: "new-client description" placeholder: "Description" value: "{description}"
|
||||
oninput: move |evt| description.set(evt.value())
|
||||
}
|
||||
}
|
||||
button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
|
||||
button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" }
|
||||
})
|
||||
}
|
||||
Scene::Settings => {
|
||||
rsx!(cx, div {
|
||||
h2 { "Settings" margin_bottom: "10px" }
|
||||
button {
|
||||
background: "rgb(202, 60, 60)"
|
||||
class: "pure-button pure-button-primary"
|
||||
onclick: move |_| {
|
||||
clients.write().clear();
|
||||
scene.set(Scene::ClientsList);
|
||||
},
|
||||
"Remove all clients"
|
||||
}
|
||||
button {
|
||||
class: "pure-button pure-button-primary"
|
||||
onclick: move |_| scene.set(Scene::ClientsList),
|
||||
"Go Back"
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
rsx!(cx, body {
|
||||
link {
|
||||
rel: "stylesheet"
|
||||
href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
|
||||
integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
|
||||
crossorigin: "anonymous"
|
||||
}
|
||||
margin_left: "35%"
|
||||
h1 {"Dioxus CRM Example"}
|
||||
{scene}
|
||||
})
|
||||
};
|
|
@ -5,20 +5,14 @@ use dioxus_core_macro::*;
|
|||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
std::thread::spawn(|| {
|
||||
let mut vdom = VirtualDom::new(App);
|
||||
let f = async_std::task::block_on(vdom.wait_for_work());
|
||||
});
|
||||
let a = 10;
|
||||
// async_std::task::spawn_blocking(|| async move {
|
||||
// });
|
||||
dioxus_desktop::launch(App, |c| c).unwrap();
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx, props| {
|
||||
//
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
"hello world!"
|
||||
}
|
||||
{(0..10).map(|f| rsx!( div {"abc {f}"}))}
|
||||
))
|
||||
};
|
||||
|
|
11
packages/desktop/examples/tauri.rs
Normal file
11
packages/desktop/examples/tauri.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
fn main() {
|
||||
tauri::AppBuilder::default().setup(move |app, name| {
|
||||
//
|
||||
let window = app.get_window();
|
||||
|
||||
let window = app.get_window();
|
||||
tauri::spawn(|| async move {
|
||||
//
|
||||
});
|
||||
});
|
||||
}
|
|
@ -4,30 +4,36 @@ use dioxus_core::DomEdit;
|
|||
use wry::{
|
||||
application::{
|
||||
error::OsError,
|
||||
event_loop::EventLoopWindowTarget,
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
menu::MenuBar,
|
||||
window::{Fullscreen, Icon, Window, WindowBuilder},
|
||||
},
|
||||
webview::{RpcRequest, RpcResponse},
|
||||
webview::{RpcRequest, RpcResponse, WebView},
|
||||
};
|
||||
|
||||
pub struct DesktopConfig<'a> {
|
||||
pub window: WindowBuilder,
|
||||
pub(crate) manual_edits: Option<DomEdit<'a>>,
|
||||
pub(crate) manual_edits: Option<Vec<DomEdit<'a>>>,
|
||||
pub(crate) pre_rendered: Option<String>,
|
||||
pub(crate) event_handler: Option<Box<dyn Fn(&mut EventLoop<()>, &mut WebView)>>,
|
||||
}
|
||||
|
||||
impl DesktopConfig<'_> {
|
||||
impl<'a> DesktopConfig<'a> {
|
||||
/// Initializes a new `WindowBuilder` with default values.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
event_handler: None,
|
||||
window: Default::default(),
|
||||
pre_rendered: None,
|
||||
manual_edits: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_edits(&mut self, edits: Vec<DomEdit<'a>>) {
|
||||
self.manual_edits = Some(edits);
|
||||
}
|
||||
|
||||
pub fn with_prerendered(&mut self, content: String) -> &mut Self {
|
||||
self.pre_rendered = Some(content);
|
||||
self
|
||||
|
|
|
@ -22,69 +22,100 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
|
|||
let mut data: Vec<ImEvent> = serde_json::from_value(val).unwrap();
|
||||
let data = data.drain(..).next().unwrap();
|
||||
|
||||
let event = SyntheticEvent::MouseEvent(MouseEvent(Arc::new(WebviewMouseEvent)));
|
||||
let scope = ScopeId(data.scope as usize);
|
||||
let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize));
|
||||
|
||||
UserEvent {
|
||||
name: todo!(),
|
||||
name: "click",
|
||||
event,
|
||||
scope,
|
||||
mounted_dom_id,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WebviewMouseEvent;
|
||||
impl MouseEventInner for WebviewMouseEvent {
|
||||
fn alt_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn button(&self) -> i16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn buttons(&self) -> u16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn client_x(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn client_y(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ctrl_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn meta_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn page_x(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn page_y(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn screen_x(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn screen_y(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn shift_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool {
|
||||
todo!()
|
||||
fn event_name_from_typ(typ: &str) -> &'static str {
|
||||
match typ {
|
||||
"copy" => "copy",
|
||||
"cut" => "cut",
|
||||
"paste" => "paste",
|
||||
"compositionend" => "compositionend",
|
||||
"compositionstart" => "compositionstart",
|
||||
"compositionupdate" => "compositionupdate",
|
||||
"keydown" => "keydown",
|
||||
"keypress" => "keypress",
|
||||
"keyup" => "keyup",
|
||||
"focus" => "focus",
|
||||
"blur" => "blur",
|
||||
"change" => "change",
|
||||
"input" => "input",
|
||||
"invalid" => "invalid",
|
||||
"reset" => "reset",
|
||||
"submit" => "submit",
|
||||
"click" => "click",
|
||||
"contextmenu" => "contextmenu",
|
||||
"doubleclick" => "doubleclick",
|
||||
"drag" => "drag",
|
||||
"dragend" => "dragend",
|
||||
"dragenter" => "dragenter",
|
||||
"dragexit" => "dragexit",
|
||||
"dragleave" => "dragleave",
|
||||
"dragover" => "dragover",
|
||||
"dragstart" => "dragstart",
|
||||
"drop" => "drop",
|
||||
"mousedown" => "mousedown",
|
||||
"mouseenter" => "mouseenter",
|
||||
"mouseleave" => "mouseleave",
|
||||
"mousemove" => "mousemove",
|
||||
"mouseout" => "mouseout",
|
||||
"mouseover" => "mouseover",
|
||||
"mouseup" => "mouseup",
|
||||
"pointerdown" => "pointerdown",
|
||||
"pointermove" => "pointermove",
|
||||
"pointerup" => "pointerup",
|
||||
"pointercancel" => "pointercancel",
|
||||
"gotpointercapture" => "gotpointercapture",
|
||||
"lostpointercapture" => "lostpointercapture",
|
||||
"pointerenter" => "pointerenter",
|
||||
"pointerleave" => "pointerleave",
|
||||
"pointerover" => "pointerover",
|
||||
"pointerout" => "pointerout",
|
||||
"select" => "select",
|
||||
"touchcancel" => "touchcancel",
|
||||
"touchend" => "touchend",
|
||||
"touchmove" => "touchmove",
|
||||
"touchstart" => "touchstart",
|
||||
"scroll" => "scroll",
|
||||
"wheel" => "wheel",
|
||||
"animationstart" => "animationstart",
|
||||
"animationend" => "animationend",
|
||||
"animationiteration" => "animationiteration",
|
||||
"transitionend" => "transitionend",
|
||||
"abort" => "abort",
|
||||
"canplay" => "canplay",
|
||||
"canplaythrough" => "canplaythrough",
|
||||
"durationchange" => "durationchange",
|
||||
"emptied" => "emptied",
|
||||
"encrypted" => "encrypted",
|
||||
"ended" => "ended",
|
||||
"error" => "error",
|
||||
"loadeddata" => "loadeddata",
|
||||
"loadedmetadata" => "loadedmetadata",
|
||||
"loadstart" => "loadstart",
|
||||
"pause" => "pause",
|
||||
"play" => "play",
|
||||
"playing" => "playing",
|
||||
"progress" => "progress",
|
||||
"ratechange" => "ratechange",
|
||||
"seeked" => "seeked",
|
||||
"seeking" => "seeking",
|
||||
"stalled" => "stalled",
|
||||
"suspend" => "suspend",
|
||||
"timeupdate" => "timeupdate",
|
||||
"volumechange" => "volumechange",
|
||||
"waiting" => "waiting",
|
||||
"toggle" => "toggle",
|
||||
_ => {
|
||||
panic!("unsupported event type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,212 +9,7 @@
|
|||
<div id="_dioxusroot">
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
class Interpreter {
|
||||
constructor(root) {
|
||||
this.root = root;
|
||||
this.stack = [root];
|
||||
this.listeners = {
|
||||
"onclick": {}
|
||||
};
|
||||
this.lastNodeWasText = false;
|
||||
this.nodes = [root, root, root, root];
|
||||
}
|
||||
|
||||
top() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
}
|
||||
|
||||
pop() {
|
||||
return this.stack.pop();
|
||||
}
|
||||
|
||||
PushRoot(edit) {
|
||||
const id = edit.id;
|
||||
const node = this.nodes[id];
|
||||
console.log("pushing root ", node, "with id", id);
|
||||
this.stack.push(node);
|
||||
}
|
||||
|
||||
PopRoot(edit) {
|
||||
this.stack.pop();
|
||||
}
|
||||
|
||||
AppendChildren(edit) {
|
||||
let root = this.stack[this.stack.length - (edit.many + 1)];
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
console.log("popping ", i, edit.many);
|
||||
let node = this.pop();
|
||||
root.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
ReplaceWith(edit) {
|
||||
|
||||
let root = this.stack[this.stack.length - (edit.many + 1)];
|
||||
let els = [];
|
||||
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
els.push(this.pop());
|
||||
}
|
||||
|
||||
root.replaceWith(...els);
|
||||
}
|
||||
|
||||
Remove(edit) {
|
||||
const node = this.stack.pop();
|
||||
node.remove();
|
||||
}
|
||||
|
||||
RemoveAllChildren(edit) {}
|
||||
|
||||
CreateTextNode(edit) {
|
||||
const node = document.createTextNode(edit.text);
|
||||
this.nodes[edit.id] = node;
|
||||
this.stack.push(node);
|
||||
}
|
||||
|
||||
CreateElement(edit) {
|
||||
const tagName = edit.tag;
|
||||
const el = document.createElement(tagName);
|
||||
this.nodes[edit.id] = el;
|
||||
console.log(`creating element: `, edit);
|
||||
this.stack.push(el);
|
||||
}
|
||||
|
||||
CreateElementNs(edit) {
|
||||
const tagName = edit.tag;
|
||||
console.log(`creating namespaced element: `, edit);
|
||||
this.stack.push(document.createElementNS(edit.ns, edit.tag));
|
||||
}
|
||||
|
||||
CreatePlaceholder(edit) {
|
||||
const a = `this.stack.push(document.createElement(" pre"))`;
|
||||
this.stack.push(document.createComment("vroot"));
|
||||
}
|
||||
|
||||
NewEventListener(edit) {
|
||||
const element_id = edit.element_id;
|
||||
const event_name = edit.event_name;
|
||||
const mounted_node_id = edit.mounted_node_id;
|
||||
const scope = edit.scope;
|
||||
|
||||
const element = this.top();
|
||||
element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
|
||||
|
||||
console.log("listener map is", this.listeners);
|
||||
if (this.listeners[event_name] === undefined) {
|
||||
console.log("adding listener!");
|
||||
this.listeners[event_name] = "bla";
|
||||
this.root.addEventListener(event_name, (event) => {
|
||||
const target = event.target;
|
||||
const type = event.type;
|
||||
const val = target.getAttribute(`dioxus-event-${event_name}`);
|
||||
const fields = val.split(".");
|
||||
const scope_id = parseInt(fields[0]);
|
||||
const real_id = parseInt(fields[1]);
|
||||
|
||||
console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
|
||||
|
||||
rpc.call('user_event', {
|
||||
event: event_name,
|
||||
scope: scope_id,
|
||||
mounted_dom_id: real_id,
|
||||
}).then((reply) => {
|
||||
console.log(reply);
|
||||
this.stack.push(this.root);
|
||||
|
||||
let edits = reply.edits;
|
||||
|
||||
for (let x = 0; x < edits.length; x++) {
|
||||
let edit = edits[x];
|
||||
console.log(edit);
|
||||
|
||||
let f = this[edit.type];
|
||||
f.call(this, edit);
|
||||
}
|
||||
|
||||
console.log("initiated");
|
||||
}).catch((err) => {
|
||||
console.log("failed to initiate", err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RemoveEventListener(edit) {}
|
||||
|
||||
SetText(edit) {
|
||||
this.top().textContent = edit.text;
|
||||
}
|
||||
|
||||
SetAttribute(edit) {
|
||||
const name = edit.field;
|
||||
const value = edit.value;
|
||||
const ns = edit.ns;
|
||||
const node = this.top(this.stack);
|
||||
if (ns == "style") {
|
||||
node.style[name] = value;
|
||||
} else if (ns !== undefined) {
|
||||
node.setAttributeNS(ns, name, value);
|
||||
} else {
|
||||
node.setAttribute(name, value);
|
||||
}
|
||||
if (name === "value") {
|
||||
node.value = value;
|
||||
}
|
||||
if (name === "checked") {
|
||||
node.checked = true;
|
||||
}
|
||||
if (name === "selected") {
|
||||
node.selected = true;
|
||||
}
|
||||
}
|
||||
RemoveAttribute(edit) {
|
||||
const name = edit.field;
|
||||
const node = this.top(this.stack);
|
||||
node.removeAttribute(name);
|
||||
|
||||
if (name === "value") {
|
||||
node.value = null;
|
||||
}
|
||||
if (name === "checked") {
|
||||
node.checked = false;
|
||||
}
|
||||
if (name === "selected") {
|
||||
node.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function initialize() {
|
||||
const reply = await rpc.call('initiate');
|
||||
let root = window.document.getElementById("_dioxusroot");
|
||||
const interpreter = new Interpreter(root);
|
||||
console.log(reply);
|
||||
|
||||
let pre_rendered = reply.pre_rendered;
|
||||
if (pre_rendered !== undefined) {
|
||||
root.innerHTML = pre_rendered;
|
||||
}
|
||||
|
||||
const edits = reply.edits;
|
||||
|
||||
for (let x = 0; x < edits.length; x++) {
|
||||
let edit = edits[x];
|
||||
console.log(edit);
|
||||
|
||||
let f = interpreter[edit.type];
|
||||
f.call(interpreter, edit);
|
||||
}
|
||||
|
||||
console.log("stack completed: ", interpreter.stack);
|
||||
}
|
||||
console.log("initializing...");
|
||||
initialize();
|
||||
<script type="text/javascript" src="/index.js">
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
|
216
packages/desktop/src/index.js
Normal file
216
packages/desktop/src/index.js
Normal file
|
@ -0,0 +1,216 @@
|
|||
|
||||
class Interpreter {
|
||||
constructor(root) {
|
||||
this.root = root;
|
||||
this.stack = [root];
|
||||
this.listeners = {
|
||||
"onclick": {}
|
||||
};
|
||||
this.lastNodeWasText = false;
|
||||
this.nodes = [root, root, root, root];
|
||||
}
|
||||
|
||||
top() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
}
|
||||
|
||||
pop() {
|
||||
return this.stack.pop();
|
||||
}
|
||||
|
||||
PushRoot(edit) {
|
||||
const id = edit.id;
|
||||
const node = this.nodes[id];
|
||||
console.log("pushing root ", node, "with id", id);
|
||||
this.stack.push(node);
|
||||
}
|
||||
|
||||
PopRoot(_edit) {
|
||||
this.stack.pop();
|
||||
}
|
||||
|
||||
AppendChildren(edit) {
|
||||
let root = this.stack[this.stack.length - (1 + edit.many)];
|
||||
|
||||
let to_add = this.stack.splice(this.stack.length - edit.many);
|
||||
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
root.appendChild(to_add[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ReplaceWith(edit) {
|
||||
console.log(edit);
|
||||
let root = this.nodes[edit.root];
|
||||
let els = this.stack.splice(this.stack.length - edit.m);
|
||||
|
||||
console.log(root);
|
||||
console.log(els);
|
||||
|
||||
|
||||
root.replaceWith(...els);
|
||||
}
|
||||
|
||||
Remove(edit) {
|
||||
let node = this.nodes[edit.element_id];
|
||||
node.remove();
|
||||
}
|
||||
|
||||
CreateTextNode(edit) {
|
||||
const node = document.createTextNode(edit.text);
|
||||
this.nodes[edit.id] = node;
|
||||
this.stack.push(node);
|
||||
}
|
||||
|
||||
CreateElement(edit) {
|
||||
const tagName = edit.tag;
|
||||
const el = document.createElement(tagName);
|
||||
this.nodes[edit.id] = el;
|
||||
this.stack.push(el);
|
||||
}
|
||||
|
||||
CreateElementNs(edit) {
|
||||
let el = document.createElementNS(edit.ns, edit.tag);
|
||||
this.stack.push(el);
|
||||
this.nodes[edit.id] = el;
|
||||
}
|
||||
|
||||
CreatePlaceholder(edit) {
|
||||
let el = document.createElement("pre");
|
||||
// let el = document.createComment("vroot");
|
||||
this.stack.push(el);
|
||||
this.nodes[edit.id] = el;
|
||||
}
|
||||
|
||||
RemoveEventListener(edit) { }
|
||||
|
||||
SetText(edit) {
|
||||
this.top().textContent = edit.text;
|
||||
}
|
||||
|
||||
SetAttribute(edit) {
|
||||
const name = edit.field;
|
||||
const value = edit.value;
|
||||
const ns = edit.ns;
|
||||
const node = this.top(this.stack);
|
||||
if (ns == "style") {
|
||||
node.style[name] = value;
|
||||
} else if (ns !== undefined) {
|
||||
node.setAttributeNS(ns, name, value);
|
||||
} else {
|
||||
node.setAttribute(name, value);
|
||||
}
|
||||
if (name === "value") {
|
||||
node.value = value;
|
||||
}
|
||||
if (name === "checked") {
|
||||
node.checked = true;
|
||||
}
|
||||
if (name === "selected") {
|
||||
node.selected = true;
|
||||
}
|
||||
}
|
||||
RemoveAttribute(edit) {
|
||||
const name = edit.field;
|
||||
const node = this.top(this.stack);
|
||||
node.removeAttribute(name);
|
||||
|
||||
if (name === "value") {
|
||||
node.value = null;
|
||||
}
|
||||
if (name === "checked") {
|
||||
node.checked = false;
|
||||
}
|
||||
if (name === "selected") {
|
||||
node.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
InsertAfter(edit) {
|
||||
let old = this.nodes[edit.element_id];
|
||||
let new_nodes = this.stack.splice(edit.many);
|
||||
old.after(...new_nodes);
|
||||
}
|
||||
|
||||
InsertBefore(edit) {
|
||||
let old = this.nodes[edit.element_id];
|
||||
let new_nodes = this.stack.splice(edit.many);
|
||||
old.before(...new_nodes);
|
||||
}
|
||||
|
||||
NewEventListener(edit) {
|
||||
const event_name = edit.event_name;
|
||||
const mounted_node_id = edit.mounted_node_id;
|
||||
const scope = edit.scope;
|
||||
|
||||
const element = this.top();
|
||||
element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
|
||||
|
||||
console.log("listener map is", this.listeners);
|
||||
if (this.listeners[event_name] === undefined) {
|
||||
console.log("adding listener!");
|
||||
this.listeners[event_name] = "bla";
|
||||
this.root.addEventListener(event_name, (event) => {
|
||||
const target = event.target;
|
||||
const val = target.getAttribute(`dioxus-event-${event_name}`);
|
||||
const fields = val.split(".");
|
||||
const scope_id = parseInt(fields[0]);
|
||||
const real_id = parseInt(fields[1]);
|
||||
|
||||
console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
|
||||
|
||||
rpc.call('user_event', {
|
||||
event: event_name,
|
||||
scope: scope_id,
|
||||
mounted_dom_id: real_id,
|
||||
}).then((reply) => {
|
||||
console.log(reply);
|
||||
this.stack.push(this.root);
|
||||
|
||||
let edits = reply.edits;
|
||||
|
||||
for (let x = 0; x < edits.length; x++) {
|
||||
let edit = edits[x];
|
||||
console.log(edit);
|
||||
|
||||
let f = this[edit.type];
|
||||
f.call(this, edit);
|
||||
}
|
||||
|
||||
console.log("initiated");
|
||||
}).catch((err) => {
|
||||
console.log("failed to initiate", err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initialize() {
|
||||
const reply = await rpc.call('initiate');
|
||||
let root = window.document.getElementById("_dioxusroot");
|
||||
const interpreter = new Interpreter(root);
|
||||
console.log(reply);
|
||||
|
||||
let pre_rendered = reply.pre_rendered;
|
||||
if (pre_rendered !== undefined) {
|
||||
root.innerHTML = pre_rendered;
|
||||
}
|
||||
|
||||
const edits = reply.edits;
|
||||
|
||||
apply_edits(edits, interpreter);
|
||||
}
|
||||
|
||||
function apply_edits(edits, interpreter) {
|
||||
for (let x = 0; x < edits.length; x++) {
|
||||
let edit = edits[x];
|
||||
console.log(edit);
|
||||
let f = interpreter[edit.type];
|
||||
f.call(interpreter, edit);
|
||||
}
|
||||
|
||||
console.log("stack completed: ", interpreter.stack);
|
||||
}
|
||||
|
||||
initialize();
|
|
@ -4,20 +4,28 @@
|
|||
//!
|
||||
|
||||
use std::borrow::BorrowMut;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use cfg::DesktopConfig;
|
||||
use dioxus_core::scheduler::SchedulerMsg;
|
||||
use dioxus_core::*;
|
||||
// use futures_channel::mpsc::UnboundedSender;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod logging;
|
||||
|
||||
pub use logging::set_up_logging;
|
||||
pub use wry;
|
||||
|
||||
use wry::application::event::{Event, WindowEvent};
|
||||
use wry::application::event_loop::{ControlFlow, EventLoop};
|
||||
use wry::application::event_loop::{self, ControlFlow, EventLoop};
|
||||
use wry::application::window::Fullscreen;
|
||||
use wry::webview::WebViewBuilder;
|
||||
use wry::webview::{WebView, WebViewBuilder};
|
||||
use wry::{
|
||||
application::window::{Window, WindowBuilder},
|
||||
webview::{RpcRequest, RpcResponse},
|
||||
|
@ -45,10 +53,16 @@ pub fn launch_with_props<P: Properties + 'static + Send + Sync>(
|
|||
run(root, props, builder)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum RpcEvent<'a> {
|
||||
Initialize { edits: Vec<DomEdit<'a>> },
|
||||
}
|
||||
|
||||
enum BridgeEvent {
|
||||
Initialize(serde_json::Value),
|
||||
Update(serde_json::Value),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Response<'a> {
|
||||
pre_rendered: Option<String>,
|
||||
|
@ -60,72 +74,175 @@ pub fn run<T: Properties + 'static + Send + Sync>(
|
|||
props: T,
|
||||
user_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
|
||||
) -> anyhow::Result<()> {
|
||||
run_with_edits(root, props, user_builder, None)
|
||||
}
|
||||
|
||||
pub fn run_with_edits<
|
||||
F: for<'a, 'b> FnOnce(&'a mut DesktopConfig<'b>) -> &'a mut DesktopConfig<'b>,
|
||||
T: Properties + 'static + Send + Sync,
|
||||
>(
|
||||
root: FC<T>,
|
||||
props: T,
|
||||
user_builder: F,
|
||||
redits: Option<Vec<DomEdit<'static>>>,
|
||||
) -> anyhow::Result<()> {
|
||||
/*
|
||||
|
||||
|
||||
*/
|
||||
|
||||
let mut cfg = DesktopConfig::new();
|
||||
user_builder(&mut cfg);
|
||||
let DesktopConfig {
|
||||
window,
|
||||
manual_edits,
|
||||
pre_rendered,
|
||||
..
|
||||
} = cfg;
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let window = window.build(&event_loop)?;
|
||||
|
||||
let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
||||
let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
let sender = launch_vdom_with_tokio(root, props, event_tx.clone());
|
||||
|
||||
let locked_receiver = Rc::new(RefCell::new(event_rx));
|
||||
|
||||
let webview = WebViewBuilder::new(window)?
|
||||
.with_url("wry://src/index.html")?
|
||||
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
|
||||
match req.method.as_str() {
|
||||
"initiate" => {
|
||||
//
|
||||
let mut rx = (*locked_receiver).borrow_mut();
|
||||
|
||||
match rx.try_recv() {
|
||||
Ok(BridgeEvent::Initialize(edits)) => {
|
||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
"user_event" => {
|
||||
//
|
||||
let data = req.params.unwrap();
|
||||
log::debug!("Data: {:#?}", data);
|
||||
let event = events::trigger_from_serialized(data);
|
||||
sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
|
||||
|
||||
let mut rx = (*locked_receiver).borrow_mut();
|
||||
|
||||
match rx.blocking_recv() {
|
||||
Some(BridgeEvent::Update(edits)) => {
|
||||
log::info!("Passing response back");
|
||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
}
|
||||
None => {
|
||||
log::error!("Sender half is gone");
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
log::error!("No update event received");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => todo!("this message failed"),
|
||||
}
|
||||
})
|
||||
// this isn't quite portable unfortunately :(
|
||||
// todo: figure out a way to allow us to create the index.html with the index.js file separately
|
||||
// it's a bit easier to hack with
|
||||
.with_custom_protocol("wry".into(), move |request| {
|
||||
use std::fs::{canonicalize, read};
|
||||
use wry::http::ResponseBuilder;
|
||||
// Remove url scheme
|
||||
let path = request.uri().replace("wry://", "");
|
||||
// Read the file content from file path
|
||||
let content = read(canonicalize(&path)?)?;
|
||||
|
||||
// Return asset contents and mime types based on file extentions
|
||||
// If you don't want to do this manually, there are some crates for you.
|
||||
// Such as `infer` and `mime_guess`.
|
||||
let (data, meta) = if path.ends_with(".html") {
|
||||
(content, "text/html")
|
||||
} else if path.ends_with(".js") {
|
||||
(content, "text/javascript")
|
||||
} else if path.ends_with(".png") {
|
||||
(content, "image/png")
|
||||
} else {
|
||||
unimplemented!();
|
||||
};
|
||||
|
||||
ResponseBuilder::new().mimetype(meta).body(data)
|
||||
})
|
||||
.build()?;
|
||||
|
||||
run_event_loop(event_loop, webview, event_tx);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start<P: 'static + Send>(
|
||||
root: FC<P>,
|
||||
config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
|
||||
) -> ((), ()) {
|
||||
//
|
||||
((), ())
|
||||
}
|
||||
|
||||
// Create a new tokio runtime on a dedicated thread and then launch the apps VirtualDom.
|
||||
fn launch_vdom_with_tokio<C: Send + 'static>(
|
||||
root: FC<C>,
|
||||
props: C,
|
||||
event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
|
||||
) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
|
||||
// Spawn the virtualdom onto its own thread
|
||||
// if it wants to spawn multithreaded tasks, it can use the executor directly
|
||||
|
||||
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
|
||||
|
||||
let sender_2 = sender.clone();
|
||||
std::thread::spawn(move || {
|
||||
//
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
// We create the runtim as multithreaded, so you can still "spawn" onto multiple threads
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(async move {
|
||||
let mut vir = VirtualDom::new_with_props(root, props);
|
||||
let channel = vir.get_event_sender();
|
||||
let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
|
||||
let _ = vir.get_event_sender();
|
||||
|
||||
let edits = vir.rebuild();
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Evt<'a> {
|
||||
edits: Vec<DomEdit<'a>>,
|
||||
}
|
||||
|
||||
// let msg = RpcEvent::Initialize { edits: edits.edits };
|
||||
let edit_string = serde_json::to_value(Evt { edits: edits.edits }).unwrap();
|
||||
match event_tx.send(BridgeEvent::Initialize(edit_string)) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
loop {
|
||||
vir.wait_for_work().await;
|
||||
let edits = vir.run_with_deadline(|| false);
|
||||
let edit_string = serde_json::to_string(&edits[0].edits).unwrap();
|
||||
event_tx.send(edit_string).unwrap();
|
||||
log::info!("{}", vir);
|
||||
|
||||
let mut muts = vir.run_with_deadline(|| false);
|
||||
log::info!("muts {:#?}", muts);
|
||||
while let Some(edit) = muts.pop() {
|
||||
let edit_string = serde_json::to_value(Evt { edits: edit.edits }).unwrap();
|
||||
match event_tx.send(BridgeEvent::Update(edit_string)) {
|
||||
Ok(_) => {}
|
||||
Err(er) => {
|
||||
log::error!("Sending should not fail {}", er);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("mutations sent on channel");
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let dioxus_requsted = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let webview = WebViewBuilder::new(window)?
|
||||
.with_url(&format!("data:text/html,{}", HTML_CONTENT))?
|
||||
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
|
||||
match req.method.as_str() {
|
||||
"initiate" => {}
|
||||
"user_event" => {}
|
||||
_ => todo!("this message failed"),
|
||||
sender_2
|
||||
}
|
||||
todo!()
|
||||
})
|
||||
.build()?;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
fn run_event_loop(
|
||||
event_loop: EventLoop<()>,
|
||||
webview: WebView,
|
||||
event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
|
||||
) {
|
||||
let _ = event_tx.clone();
|
||||
event_loop.run(move |event, target, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
|
@ -146,74 +263,5 @@ pub fn run_with_edits<
|
|||
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
// let edits = if let Some(edits) = &redits {
|
||||
// serde_json::to_value(edits).unwrap()
|
||||
// } else {
|
||||
// let mut lock = vdom.write().unwrap();
|
||||
// // let mut reg_lock = registry.write().unwrap();
|
||||
// // Create the thin wrapper around the registry to collect the edits into
|
||||
// let mut real = dom::WebviewDom::new();
|
||||
// let pre = pre_rendered.clone();
|
||||
// let response = match pre {
|
||||
// Some(content) => {
|
||||
// lock.rebuild_in_place().unwrap();
|
||||
// Response {
|
||||
// edits: Vec::new(),
|
||||
// pre_rendered: Some(content),
|
||||
// }
|
||||
// }
|
||||
// None => {
|
||||
// //
|
||||
// let edits = {
|
||||
// // let mut edits = Vec::new();
|
||||
// todo!()
|
||||
// // lock.rebuild(&mut real, &mut edits).unwrap();
|
||||
// // edits
|
||||
// };
|
||||
// Response {
|
||||
// edits,
|
||||
// pre_rendered: None,
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// serde_json::to_value(&response).unwrap()
|
||||
// };
|
||||
// // Return the edits into the webview runtime
|
||||
// Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
|
||||
// log::debug!("User event received");
|
||||
// // let registry = registry.clone();
|
||||
// let vdom = vdom.clone();
|
||||
// let response = async_std::task::block_on(async move {
|
||||
// let mut lock = vdom.write().unwrap();
|
||||
// // let mut reg_lock = registry.write().unwrap();
|
||||
// // a deserialized event
|
||||
// let data = req.params.unwrap();
|
||||
// log::debug!("Data: {:#?}", data);
|
||||
// let event = trigger_from_serialized(data);
|
||||
// // lock.queue_event(event);
|
||||
// // Create the thin wrapper around the registry to collect the edits into
|
||||
// let mut real = dom::WebviewDom::new();
|
||||
// // Serialize the edit stream
|
||||
// //
|
||||
// let mut edits = Vec::new();
|
||||
// // lock.run(&mut real, &mut edits)
|
||||
// // .await
|
||||
// // .expect("failed to progress");
|
||||
// let response = Response {
|
||||
// edits,
|
||||
// pre_rendered: None,
|
||||
// };
|
||||
// let response = serde_json::to_value(&response).unwrap();
|
||||
// // Give back the registry into its slot
|
||||
// // *reg_lock = Some(real.consume());
|
||||
// // Return the edits into the webview runtime
|
||||
// Some(RpcResponse::new_result(req.id.take(), Some(response)))
|
||||
// });
|
||||
// response
|
||||
// // spawn a task to clean up the garbage
|
||||
|
|
51
packages/desktop/src/logging.rs
Normal file
51
packages/desktop/src/logging.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
pub fn set_up_logging(enabled: bool) {
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
|
||||
if !enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
// configure colors for the whole line
|
||||
let colors_line = ColoredLevelConfig::new()
|
||||
.error(Color::Red)
|
||||
.warn(Color::Yellow)
|
||||
// we actually don't need to specify the color for debug and info, they are white by default
|
||||
.info(Color::White)
|
||||
.debug(Color::White)
|
||||
// depending on the terminals color scheme, this is the same as the background color
|
||||
.trace(Color::BrightBlack);
|
||||
|
||||
// configure colors for the name of the level.
|
||||
// since almost all of them are the same as the color for the whole line, we
|
||||
// just clone `colors_line` and overwrite our changes
|
||||
let colors_level = colors_line.clone().info(Color::Green);
|
||||
// here we set up our fern Dispatch
|
||||
|
||||
// when running tests in batch, the logger is re-used, so ignore the logger error
|
||||
let _ = fern::Dispatch::new()
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{color_line}[{level}{color_line}] {message}\x1B[0m",
|
||||
color_line = format_args!(
|
||||
"\x1B[{}m",
|
||||
colors_line.get_color(&record.level()).to_fg_str()
|
||||
),
|
||||
level = colors_level.color(record.level()),
|
||||
message = message,
|
||||
));
|
||||
})
|
||||
// set the default log level. to filter out verbose log messages from dependencies, set
|
||||
// this to Warn and overwrite the log level for your crate.
|
||||
.level(log::LevelFilter::Debug)
|
||||
// .level(log::LevelFilter::Warn)
|
||||
// change log levels for individual modules. Note: This looks for the record's target
|
||||
// field which defaults to the module path but can be overwritten with the `target`
|
||||
// parameter:
|
||||
// `info!(target="special_target", "This log message is about special_target");`
|
||||
// .level_for("dioxus", log::LevelFilter::Debug)
|
||||
// .level_for("dioxus", log::LevelFilter::Info)
|
||||
// .level_for("pretty_colored", log::LevelFilter::Trace)
|
||||
// output to stdout
|
||||
.chain(std::io::stdout())
|
||||
.apply();
|
||||
}
|
|
@ -13,7 +13,7 @@ dioxus-core = { path = "../core", version = "0.1.2" }
|
|||
log = "0.4.14"
|
||||
serde = "1.0.126"
|
||||
serde_json = "1.0.64"
|
||||
wry = "0.11.0"
|
||||
wry = "0.12.2"
|
||||
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
|
|
|
@ -72,9 +72,9 @@ crate-type = ["cdylib", "rlib"]
|
|||
im-rc = "15.0.0"
|
||||
separator = "0.4.1"
|
||||
uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
|
||||
dioxus-hooks = { path = "../hooks" }
|
||||
serde = { version = "1.0.126", features = ["derive"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
dioxus-hooks = { path = "../hooks" }
|
||||
dioxus-core-macro = { path = "../core-macro" }
|
||||
# rand = { version="0.8.4", features=["small_rng"] }
|
||||
# surf = { version = "2.3.1", default-features = false, features = [
|
||||
|
|
|
@ -44,9 +44,9 @@ static App: FC<()> = |cx, props| {
|
|||
"dynamic subtree {state}"
|
||||
}
|
||||
div {
|
||||
button { onclick: move |_| state+=1, "incr" }
|
||||
button { onclick: move |e| state+=1, "incr" }
|
||||
br {}
|
||||
button { onclick: move |_| state-=1, "decr" }
|
||||
button { onclick: move |e| state-=1, "decr" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//! - Partial delegation?>
|
||||
|
||||
use dioxus_core::{
|
||||
events::{SyntheticEvent, UserEvent},
|
||||
events::{DioxusEvent, KeyCode, SyntheticEvent, UserEvent},
|
||||
mutations::NodeRefMutation,
|
||||
scheduler::SchedulerMsg,
|
||||
DomEdit, ElementId, ScopeId,
|
||||
|
@ -110,7 +110,6 @@ impl WebsysDom {
|
|||
DomEdit::AppendChildren { many } => self.append_children(many),
|
||||
DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
|
||||
DomEdit::Remove { root } => self.remove(root),
|
||||
DomEdit::RemoveAllChildren => self.remove_all_children(),
|
||||
DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
|
||||
DomEdit::CreateElement { tag, id } => self.create_element(tag, None, id),
|
||||
DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
|
||||
|
@ -208,10 +207,6 @@ impl WebsysDom {
|
|||
}
|
||||
}
|
||||
|
||||
fn remove_all_children(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn create_placeholder(&mut self, id: u64) {
|
||||
self.create_element("pre", None, id);
|
||||
self.set_attribute("hidden", "", None);
|
||||
|
@ -464,87 +459,213 @@ impl Stack {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct DioxusWebsysEvent(web_sys::Event);
|
||||
unsafe impl Send for DioxusWebsysEvent {}
|
||||
unsafe impl Sync for DioxusWebsysEvent {}
|
||||
|
||||
// trait MyTrait {}
|
||||
// impl MyTrait for web_sys::Event {}
|
||||
|
||||
// todo: some of these events are being casted to the wrong event type.
|
||||
// We need tests that simulate clicks/etc and make sure every event type works.
|
||||
fn virtual_event_from_websys_event(event: web_sys::Event) -> SyntheticEvent {
|
||||
use crate::events::*;
|
||||
use dioxus_core::events::on::*;
|
||||
match event.type_().as_str() {
|
||||
"copy" | "cut" | "paste" => {
|
||||
SyntheticEvent::ClipboardEvent(ClipboardEvent(Arc::new(WebsysClipboardEvent(event))))
|
||||
}
|
||||
"copy" | "cut" | "paste" => SyntheticEvent::ClipboardEvent(ClipboardEvent(
|
||||
DioxusEvent::new(ClipboardEventInner(), DioxusWebsysEvent(event)),
|
||||
)),
|
||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||
let evt: web_sys::CompositionEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::CompositionEvent(CompositionEvent(Arc::new(WebsysCompositionEvent(
|
||||
evt,
|
||||
))))
|
||||
let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::CompositionEvent(CompositionEvent(DioxusEvent::new(
|
||||
CompositionEventInner {
|
||||
data: evt.data().unwrap_or_default(),
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
"keydown" | "keypress" | "keyup" => {
|
||||
let evt: web_sys::KeyboardEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::KeyboardEvent(KeyboardEvent(Arc::new(WebsysKeyboardEvent(evt))))
|
||||
}
|
||||
"focus" | "blur" => {
|
||||
let evt: web_sys::FocusEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::FocusEvent(FocusEvent(Arc::new(WebsysFocusEvent(evt))))
|
||||
}
|
||||
"change" => {
|
||||
let evt = event.dyn_into().unwrap();
|
||||
SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
|
||||
let evt: &web_sys::KeyboardEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::KeyboardEvent(KeyboardEvent(DioxusEvent::new(
|
||||
KeyboardEventInner {
|
||||
alt_key: evt.alt_key(),
|
||||
char_code: evt.char_code(),
|
||||
key: evt.key(),
|
||||
key_code: KeyCode::from_raw_code(evt.key_code() as u8),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
locale: "not implemented".to_string(),
|
||||
location: evt.location() as usize,
|
||||
meta_key: evt.meta_key(),
|
||||
repeat: evt.repeat(),
|
||||
shift_key: evt.shift_key(),
|
||||
which: evt.which() as usize,
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
"focus" | "blur" => SyntheticEvent::FocusEvent(FocusEvent(DioxusEvent::new(
|
||||
FocusEventInner {},
|
||||
DioxusWebsysEvent(event),
|
||||
))),
|
||||
"change" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
|
||||
|
||||
// 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
|
||||
"input" | "invalid" | "reset" | "submit" => {
|
||||
let evt: web_sys::Event = event.dyn_into().unwrap();
|
||||
SyntheticEvent::FormEvent(FormEvent(Arc::new(WebsysFormEvent(evt))))
|
||||
let evt: &web_sys::Event = event.dyn_ref().unwrap();
|
||||
|
||||
let target: web_sys::EventTarget = evt.target().unwrap();
|
||||
let value: String = (&target)
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlInputElement| input.value())
|
||||
.or_else(|| {
|
||||
target
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlTextAreaElement| input.value())
|
||||
})
|
||||
// select elements are NOT input events - because - why woudn't they be??
|
||||
.or_else(|| {
|
||||
target
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlSelectElement| input.value())
|
||||
})
|
||||
.or_else(|| {
|
||||
target
|
||||
.dyn_ref::<web_sys::HtmlElement>()
|
||||
.unwrap()
|
||||
.text_content()
|
||||
})
|
||||
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
|
||||
|
||||
SyntheticEvent::FormEvent(FormEvent(DioxusEvent::new(
|
||||
FormEventInner { value },
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||
let evt: web_sys::MouseEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::MouseEvent(MouseEvent(Arc::new(WebsysMouseEvent(evt))))
|
||||
let evt: &web_sys::MouseEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::MouseEvent(MouseEvent(DioxusEvent::new(
|
||||
MouseEventInner {
|
||||
alt_key: evt.alt_key(),
|
||||
button: evt.button(),
|
||||
buttons: evt.buttons(),
|
||||
client_x: evt.client_x(),
|
||||
client_y: evt.client_y(),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
meta_key: evt.meta_key(),
|
||||
screen_x: evt.screen_x(),
|
||||
screen_y: evt.screen_y(),
|
||||
shift_key: evt.shift_key(),
|
||||
page_x: evt.page_x(),
|
||||
page_y: evt.page_y(),
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||
let evt: web_sys::PointerEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::PointerEvent(PointerEvent(Arc::new(WebsysPointerEvent(evt))))
|
||||
}
|
||||
"select" => {
|
||||
let evt: web_sys::UiEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::SelectionEvent(SelectionEvent(Arc::new(WebsysGenericUiEvent(evt))))
|
||||
let evt: &web_sys::PointerEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::PointerEvent(PointerEvent(DioxusEvent::new(
|
||||
PointerEventInner {
|
||||
alt_key: evt.alt_key(),
|
||||
button: evt.button(),
|
||||
buttons: evt.buttons(),
|
||||
client_x: evt.client_x(),
|
||||
client_y: evt.client_y(),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
meta_key: evt.meta_key(),
|
||||
page_x: evt.page_x(),
|
||||
page_y: evt.page_y(),
|
||||
screen_x: evt.screen_x(),
|
||||
screen_y: evt.screen_y(),
|
||||
shift_key: evt.shift_key(),
|
||||
pointer_id: evt.pointer_id(),
|
||||
width: evt.width(),
|
||||
height: evt.height(),
|
||||
pressure: evt.pressure(),
|
||||
tangential_pressure: evt.tangential_pressure(),
|
||||
tilt_x: evt.tilt_x(),
|
||||
tilt_y: evt.tilt_y(),
|
||||
twist: evt.twist(),
|
||||
pointer_type: evt.pointer_type(),
|
||||
is_primary: evt.is_primary(),
|
||||
// get_modifier_state: evt.get_modifier_state(),
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
"select" => SyntheticEvent::SelectionEvent(SelectionEvent(DioxusEvent::new(
|
||||
SelectionEventInner {},
|
||||
DioxusWebsysEvent(event),
|
||||
))),
|
||||
|
||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
||||
let evt: web_sys::TouchEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::TouchEvent(TouchEvent(Arc::new(WebsysTouchEvent(evt))))
|
||||
}
|
||||
"scroll" => {
|
||||
let evt: web_sys::UiEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
|
||||
let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::TouchEvent(TouchEvent(DioxusEvent::new(
|
||||
TouchEventInner {
|
||||
alt_key: evt.alt_key(),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
meta_key: evt.meta_key(),
|
||||
shift_key: evt.shift_key(),
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
|
||||
"scroll" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
|
||||
|
||||
"wheel" => {
|
||||
let evt: web_sys::WheelEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::WheelEvent(WheelEvent(Arc::new(WebsysWheelEvent(evt))))
|
||||
let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::WheelEvent(WheelEvent(DioxusEvent::new(
|
||||
WheelEventInner {
|
||||
delta_x: evt.delta_x(),
|
||||
delta_y: evt.delta_y(),
|
||||
delta_z: evt.delta_z(),
|
||||
delta_mode: evt.delta_mode(),
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
|
||||
"animationstart" | "animationend" | "animationiteration" => {
|
||||
let evt: web_sys::AnimationEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::AnimationEvent(AnimationEvent(Arc::new(WebsysAnimationEvent(evt))))
|
||||
let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::AnimationEvent(AnimationEvent(DioxusEvent::new(
|
||||
AnimationEventInner {
|
||||
elapsed_time: evt.elapsed_time(),
|
||||
animation_name: evt.animation_name(),
|
||||
pseudo_element: evt.pseudo_element(),
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
|
||||
"transitionend" => {
|
||||
let evt: web_sys::TransitionEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::TransitionEvent(TransitionEvent(Arc::new(WebsysTransitionEvent(evt))))
|
||||
let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
|
||||
SyntheticEvent::TransitionEvent(TransitionEvent(DioxusEvent::new(
|
||||
TransitionEventInner {
|
||||
elapsed_time: evt.elapsed_time(),
|
||||
property_name: evt.property_name(),
|
||||
pseudo_element: evt.pseudo_element(),
|
||||
},
|
||||
DioxusWebsysEvent(event),
|
||||
)))
|
||||
}
|
||||
|
||||
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||
| "timeupdate" | "volumechange" | "waiting" => {
|
||||
let evt: web_sys::UiEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::MediaEvent(MediaEvent(Arc::new(WebsysMediaEvent(evt))))
|
||||
}
|
||||
"toggle" => {
|
||||
let evt: web_sys::UiEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::ToggleEvent(ToggleEvent(Arc::new(WebsysToggleEvent(evt))))
|
||||
}
|
||||
_ => {
|
||||
let evt: web_sys::UiEvent = event.dyn_into().unwrap();
|
||||
SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
|
||||
}
|
||||
| "timeupdate" | "volumechange" | "waiting" => SyntheticEvent::MediaEvent(MediaEvent(
|
||||
DioxusEvent::new(MediaEventInner {}, DioxusWebsysEvent(event)),
|
||||
)),
|
||||
|
||||
"toggle" => SyntheticEvent::ToggleEvent(ToggleEvent(DioxusEvent::new(
|
||||
ToggleEventInner {},
|
||||
DioxusWebsysEvent(event),
|
||||
))),
|
||||
|
||||
_ => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,480 +3,3 @@
|
|||
use dioxus_core::events::on::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Event, UiEvent};
|
||||
|
||||
/// All events implement the generic event type - they're all `UI events`
|
||||
trait WebsysGenericEvent {
|
||||
fn as_ui_event(&self) -> &UiEvent;
|
||||
}
|
||||
|
||||
impl GenericEventInner for &dyn WebsysGenericEvent {
|
||||
/// On WebSys, this returns an &UiEvent which can be casted via dyn_ref into the correct sub type.
|
||||
fn raw_event(&self) -> &dyn std::any::Any {
|
||||
self.as_ui_event()
|
||||
}
|
||||
|
||||
fn bubbles(&self) -> bool {
|
||||
self.as_ui_event().bubbles()
|
||||
}
|
||||
|
||||
fn cancel_bubble(&self) {
|
||||
self.as_ui_event().cancel_bubble();
|
||||
}
|
||||
|
||||
fn cancelable(&self) -> bool {
|
||||
self.as_ui_event().cancelable()
|
||||
}
|
||||
|
||||
fn composed(&self) -> bool {
|
||||
self.as_ui_event().composed()
|
||||
}
|
||||
|
||||
fn current_target(&self) {
|
||||
if cfg!(debug_assertions) {
|
||||
todo!("Current target does not return anything useful.\nPlease try casting the event directly.");
|
||||
}
|
||||
// self.as_ui_event().current_target();
|
||||
}
|
||||
|
||||
fn default_prevented(&self) -> bool {
|
||||
self.as_ui_event().default_prevented()
|
||||
}
|
||||
|
||||
fn event_phase(&self) -> u16 {
|
||||
self.as_ui_event().event_phase()
|
||||
}
|
||||
|
||||
fn is_trusted(&self) -> bool {
|
||||
self.as_ui_event().is_trusted()
|
||||
}
|
||||
|
||||
fn prevent_default(&self) {
|
||||
self.as_ui_event().prevent_default()
|
||||
}
|
||||
|
||||
fn stop_immediate_propagation(&self) {
|
||||
self.as_ui_event().stop_immediate_propagation()
|
||||
}
|
||||
|
||||
fn stop_propagation(&self) {
|
||||
self.as_ui_event().stop_propagation()
|
||||
}
|
||||
|
||||
fn target(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn time_stamp(&self) -> f64 {
|
||||
self.as_ui_event().time_stamp()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! implement_generic_event {
|
||||
(
|
||||
$($event:ident),*
|
||||
) => {
|
||||
$(
|
||||
impl WebsysGenericEvent for $event {
|
||||
fn as_ui_event(&self) -> &UiEvent {
|
||||
self.0.dyn_ref().unwrap()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
implement_generic_event! {
|
||||
WebsysClipboardEvent,
|
||||
WebsysCompositionEvent,
|
||||
WebsysKeyboardEvent,
|
||||
WebsysGenericUiEvent,
|
||||
WebsysFocusEvent,
|
||||
WebsysFormEvent,
|
||||
WebsysMouseEvent,
|
||||
WebsysPointerEvent,
|
||||
WebsysWheelEvent,
|
||||
WebsysAnimationEvent,
|
||||
WebsysTransitionEvent,
|
||||
WebsysTouchEvent,
|
||||
WebsysMediaEvent,
|
||||
WebsysToggleEvent
|
||||
}
|
||||
|
||||
// unfortunately, currently experimental, and web_sys needs to be configured to use it :>(
|
||||
pub struct WebsysClipboardEvent(pub Event);
|
||||
|
||||
impl ClipboardEventInner for WebsysClipboardEvent {}
|
||||
|
||||
pub struct WebsysCompositionEvent(pub web_sys::CompositionEvent);
|
||||
|
||||
impl CompositionEventInner for WebsysCompositionEvent {
|
||||
fn data(&self) -> String {
|
||||
self.0.data().unwrap_or_else(|| String::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysKeyboardEvent(pub web_sys::KeyboardEvent);
|
||||
impl KeyboardEventInner for WebsysKeyboardEvent {
|
||||
fn alt_key(&self) -> bool {
|
||||
self.0.alt_key()
|
||||
}
|
||||
fn char_code(&self) -> u32 {
|
||||
self.0.char_code()
|
||||
}
|
||||
fn key(&self) -> String {
|
||||
self.0.key()
|
||||
}
|
||||
|
||||
fn key_code(&self) -> KeyCode {
|
||||
KeyCode::from_raw_code(self.0.key_code() as u8)
|
||||
}
|
||||
|
||||
fn ctrl_key(&self) -> bool {
|
||||
self.0.ctrl_key()
|
||||
}
|
||||
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool {
|
||||
self.0.get_modifier_state(key_code)
|
||||
}
|
||||
|
||||
fn locale(&self) -> String {
|
||||
if cfg!(debug_assertions) {
|
||||
todo!("Locale is currently not supported. :(")
|
||||
} else {
|
||||
String::from("en-US")
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> usize {
|
||||
self.0.location() as usize
|
||||
}
|
||||
|
||||
fn meta_key(&self) -> bool {
|
||||
self.0.meta_key()
|
||||
}
|
||||
|
||||
fn repeat(&self) -> bool {
|
||||
self.0.repeat()
|
||||
}
|
||||
|
||||
fn shift_key(&self) -> bool {
|
||||
self.0.shift_key()
|
||||
}
|
||||
|
||||
fn which(&self) -> usize {
|
||||
self.0.which() as usize
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysGenericUiEvent(pub UiEvent);
|
||||
impl GenericEventInner for WebsysGenericUiEvent {
|
||||
fn raw_event(&self) -> &dyn std::any::Any {
|
||||
// self.0.raw_event()
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn bubbles(&self) -> bool {
|
||||
self.0.bubbles()
|
||||
}
|
||||
|
||||
fn cancel_bubble(&self) {
|
||||
self.0.cancel_bubble();
|
||||
}
|
||||
|
||||
fn cancelable(&self) -> bool {
|
||||
self.0.cancelable()
|
||||
}
|
||||
|
||||
fn composed(&self) -> bool {
|
||||
self.0.composed()
|
||||
}
|
||||
|
||||
fn current_target(&self) {
|
||||
// self.0.current_target()
|
||||
}
|
||||
|
||||
fn default_prevented(&self) -> bool {
|
||||
self.0.default_prevented()
|
||||
}
|
||||
|
||||
fn event_phase(&self) -> u16 {
|
||||
self.0.event_phase()
|
||||
}
|
||||
|
||||
fn is_trusted(&self) -> bool {
|
||||
self.0.is_trusted()
|
||||
}
|
||||
|
||||
fn prevent_default(&self) {
|
||||
self.0.prevent_default()
|
||||
}
|
||||
|
||||
fn stop_immediate_propagation(&self) {
|
||||
self.0.stop_immediate_propagation()
|
||||
}
|
||||
|
||||
fn stop_propagation(&self) {
|
||||
self.0.stop_propagation()
|
||||
}
|
||||
|
||||
fn target(&self) {
|
||||
// self.0.target()
|
||||
}
|
||||
|
||||
fn time_stamp(&self) -> f64 {
|
||||
self.0.time_stamp()
|
||||
}
|
||||
}
|
||||
|
||||
impl UIEventInner for WebsysGenericUiEvent {
|
||||
fn detail(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionEventInner for WebsysGenericUiEvent {}
|
||||
|
||||
pub struct WebsysFocusEvent(pub web_sys::FocusEvent);
|
||||
impl FocusEventInner for WebsysFocusEvent {}
|
||||
|
||||
pub struct WebsysFormEvent(pub web_sys::Event);
|
||||
impl FormEventInner for WebsysFormEvent {
|
||||
// technically a controlled component, so we need to manually grab out the target data
|
||||
fn value(&self) -> String {
|
||||
let this: web_sys::EventTarget = self.0.target().unwrap();
|
||||
(&this)
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlInputElement| input.value())
|
||||
.or_else(|| {
|
||||
this
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlTextAreaElement| input.value())
|
||||
})
|
||||
// select elements are NOT input events - because - why woudn't they be??
|
||||
.or_else(|| {
|
||||
this
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlSelectElement| input.value())
|
||||
})
|
||||
.or_else(|| {
|
||||
this
|
||||
.dyn_ref::<web_sys::HtmlElement>()
|
||||
.unwrap()
|
||||
.text_content()
|
||||
})
|
||||
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysMouseEvent(pub web_sys::MouseEvent);
|
||||
impl MouseEventInner for WebsysMouseEvent {
|
||||
fn alt_key(&self) -> bool {
|
||||
self.0.alt_key()
|
||||
}
|
||||
fn button(&self) -> i16 {
|
||||
self.0.button()
|
||||
}
|
||||
fn buttons(&self) -> u16 {
|
||||
self.0.buttons()
|
||||
}
|
||||
fn client_x(&self) -> i32 {
|
||||
self.0.client_x()
|
||||
}
|
||||
fn client_y(&self) -> i32 {
|
||||
self.0.client_y()
|
||||
}
|
||||
fn ctrl_key(&self) -> bool {
|
||||
self.0.ctrl_key()
|
||||
}
|
||||
fn meta_key(&self) -> bool {
|
||||
self.0.meta_key()
|
||||
}
|
||||
fn page_x(&self) -> i32 {
|
||||
self.0.page_x()
|
||||
}
|
||||
fn page_y(&self) -> i32 {
|
||||
self.0.page_y()
|
||||
}
|
||||
fn screen_x(&self) -> i32 {
|
||||
self.0.screen_x()
|
||||
}
|
||||
fn screen_y(&self) -> i32 {
|
||||
self.0.screen_y()
|
||||
}
|
||||
fn shift_key(&self) -> bool {
|
||||
self.0.shift_key()
|
||||
}
|
||||
|
||||
// yikes
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool {
|
||||
self.0.get_modifier_state(key_code)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysPointerEvent(pub web_sys::PointerEvent);
|
||||
impl PointerEventInner for WebsysPointerEvent {
|
||||
fn alt_key(&self) -> bool {
|
||||
self.0.alt_key()
|
||||
}
|
||||
fn button(&self) -> i16 {
|
||||
self.0.button()
|
||||
}
|
||||
fn buttons(&self) -> u16 {
|
||||
self.0.buttons()
|
||||
}
|
||||
fn client_x(&self) -> i32 {
|
||||
self.0.client_x()
|
||||
}
|
||||
fn client_y(&self) -> i32 {
|
||||
self.0.client_y()
|
||||
}
|
||||
fn ctrl_key(&self) -> bool {
|
||||
self.0.ctrl_key()
|
||||
}
|
||||
fn meta_key(&self) -> bool {
|
||||
self.0.meta_key()
|
||||
}
|
||||
fn page_x(&self) -> i32 {
|
||||
self.0.page_x()
|
||||
}
|
||||
fn page_y(&self) -> i32 {
|
||||
self.0.page_y()
|
||||
}
|
||||
fn screen_x(&self) -> i32 {
|
||||
self.0.screen_x()
|
||||
}
|
||||
fn screen_y(&self) -> i32 {
|
||||
self.0.screen_y()
|
||||
}
|
||||
fn shift_key(&self) -> bool {
|
||||
self.0.shift_key()
|
||||
}
|
||||
|
||||
// yikes
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool {
|
||||
self.0.get_modifier_state(key_code)
|
||||
}
|
||||
|
||||
fn pointer_id(&self) -> i32 {
|
||||
self.0.pointer_id()
|
||||
}
|
||||
|
||||
fn width(&self) -> i32 {
|
||||
self.0.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
self.0.height()
|
||||
}
|
||||
|
||||
fn pressure(&self) -> f32 {
|
||||
self.0.pressure()
|
||||
}
|
||||
|
||||
fn tangential_pressure(&self) -> f32 {
|
||||
self.0.tangential_pressure()
|
||||
}
|
||||
|
||||
fn tilt_x(&self) -> i32 {
|
||||
self.0.tilt_x()
|
||||
}
|
||||
|
||||
fn tilt_y(&self) -> i32 {
|
||||
self.0.tilt_y()
|
||||
}
|
||||
|
||||
fn twist(&self) -> i32 {
|
||||
self.0.twist()
|
||||
}
|
||||
|
||||
fn pointer_type(&self) -> String {
|
||||
self.0.pointer_type()
|
||||
}
|
||||
|
||||
fn is_primary(&self) -> bool {
|
||||
self.0.is_primary()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysWheelEvent(pub web_sys::WheelEvent);
|
||||
impl WheelEventInner for WebsysWheelEvent {
|
||||
fn delta_mode(&self) -> u32 {
|
||||
self.0.delta_mode()
|
||||
}
|
||||
|
||||
fn delta_x(&self) -> f64 {
|
||||
self.0.delta_x()
|
||||
}
|
||||
|
||||
fn delta_y(&self) -> f64 {
|
||||
self.0.delta_y()
|
||||
}
|
||||
|
||||
fn delta_z(&self) -> f64 {
|
||||
self.0.delta_z()
|
||||
}
|
||||
}
|
||||
pub struct WebsysAnimationEvent(pub web_sys::AnimationEvent);
|
||||
impl AnimationEventInner for WebsysAnimationEvent {
|
||||
fn animation_name(&self) -> String {
|
||||
self.0.animation_name()
|
||||
}
|
||||
|
||||
fn pseudo_element(&self) -> String {
|
||||
self.0.pseudo_element()
|
||||
}
|
||||
|
||||
fn elapsed_time(&self) -> f32 {
|
||||
self.0.elapsed_time()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysTransitionEvent(pub web_sys::TransitionEvent);
|
||||
impl TransitionEventInner for WebsysTransitionEvent {
|
||||
fn property_name(&self) -> String {
|
||||
self.0.property_name()
|
||||
}
|
||||
|
||||
fn pseudo_element(&self) -> String {
|
||||
self.0.pseudo_element()
|
||||
}
|
||||
|
||||
fn elapsed_time(&self) -> f32 {
|
||||
self.0.elapsed_time()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysTouchEvent(pub web_sys::TouchEvent);
|
||||
impl TouchEventInner for WebsysTouchEvent {
|
||||
fn alt_key(&self) -> bool {
|
||||
self.0.alt_key()
|
||||
}
|
||||
|
||||
fn ctrl_key(&self) -> bool {
|
||||
self.0.ctrl_key()
|
||||
}
|
||||
|
||||
fn meta_key(&self) -> bool {
|
||||
self.0.meta_key()
|
||||
}
|
||||
|
||||
fn shift_key(&self) -> bool {
|
||||
self.0.shift_key()
|
||||
}
|
||||
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool {
|
||||
if cfg!(debug_assertions) {
|
||||
todo!("get_modifier_state is not currently supported for touch events");
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebsysMediaEvent(pub web_sys::UiEvent);
|
||||
impl MediaEventInner for WebsysMediaEvent {}
|
||||
|
||||
pub struct WebsysToggleEvent(pub web_sys::UiEvent);
|
||||
impl ToggleEventInner for WebsysToggleEvent {}
|
||||
|
|
|
@ -116,7 +116,7 @@ pub fn launch(root_component: FC<()>, configuration: impl FnOnce(WebConfig) -> W
|
|||
/// ```
|
||||
pub fn launch_with_props<T, F>(root_component: FC<T>, root_properties: T, configuration_builder: F)
|
||||
where
|
||||
T: Properties + 'static,
|
||||
T: Send + 'static,
|
||||
F: FnOnce(WebConfig) -> WebConfig,
|
||||
{
|
||||
let config = configuration_builder(WebConfig::default());
|
||||
|
@ -135,7 +135,7 @@ where
|
|||
/// wasm_bindgen_futures::spawn_local(app_fut);
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) {
|
||||
pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg: WebConfig) {
|
||||
let mut dom = VirtualDom::new_with_props(root, root_props);
|
||||
|
||||
intern_cached_strings();
|
||||
|
|
8
packages/webview-client/Cargo.toml
Normal file
8
packages/webview-client/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "dioxus-webview-client"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
8
packages/webview-client/README.md
Normal file
8
packages/webview-client/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# the client part of the vdom
|
||||
|
||||
|
||||
this crate is designed to be the "receiving end" of the dioxus virtualdom, using wasm_bindgen to expose an API for the receiving end.
|
||||
|
||||
|
||||
|
||||
|
3
packages/webview-client/src/main.rs
Normal file
3
packages/webview-client/src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
Loading…
Reference in a new issue