wip: more work on web
this commit adds complete event support for web-sys but with a few hole
This commit is contained in:
1 parent
59219b9ef2
commit
3bf19d8106
13 files changed
+709
-355
No files matched your search
+10
-5
@@ -19,7 +19,7 @@ dioxus-mobile = { path = "./packages/mobile", optional = true }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
# core
|
# core
|
||||||
default = ["core", "ssr"]
|
default = ["core", "ssr", "web"]
|
||||||
core = ["macro", "hooks", "html"]
|
core = ["macro", "hooks", "html"]
|
||||||
macro = ["dioxus-core-macro"]
|
macro = ["dioxus-core-macro"]
|
||||||
hooks = ["dioxus-hooks"]
|
hooks = ["dioxus-hooks"]
|
||||||
@@ -56,12 +56,17 @@ gloo-timers = "0.2.1"
|
|||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||||
gloo-timers = "0.2.1"
|
gloo-timers = "0.2.1"
|
||||||
surf = { version = "2.2.0", default-features = false, features = [
|
wasm-logger = "0.2.0"
|
||||||
"wasm-client",
|
console_error_panic_hook = "0.1.6"
|
||||||
], git = "https://github.com/jkelleyrtp/surf/", branch = "jk/fix-the-wasm" }
|
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||||
|
wasm-bindgen = { version = "0.2.71", features = ["enable-interning"] }
|
||||||
|
|
||||||
|
# surf = { version = "2.2.0", default-features = false, features = [
|
||||||
|
# "wasm-client",
|
||||||
|
# ], git = "https://github.com/jkelleyrtp/surf/", branch = "jk/fix-the-wasm" }
|
||||||
|
|
||||||
|
|
||||||
[dependencies.getrandom]
|
[dev-dependencies.getrandom]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
features = ["js"]
|
features = ["js"]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#![allow(non_upper_case_globals, non_snake_case)]
|
||||||
|
//! Example: Webview Renderer
|
||||||
|
//! -------------------------
|
||||||
|
//!
|
||||||
|
//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
|
||||||
|
//!
|
||||||
|
//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuit application running
|
||||||
|
//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
|
||||||
|
//! into the native VDom instance.
|
||||||
|
//!
|
||||||
|
//! Currently, NodeRefs won't work properly, but all other event functionality will.
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Setup logging
|
||||||
|
// wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||||
|
// console_error_panic_hook::set_once();
|
||||||
|
for adj in ADJECTIVES {
|
||||||
|
wasm_bindgen::intern(adj);
|
||||||
|
}
|
||||||
|
for col in COLOURS {
|
||||||
|
wasm_bindgen::intern(col);
|
||||||
|
}
|
||||||
|
for no in NOUNS {
|
||||||
|
wasm_bindgen::intern(no);
|
||||||
|
}
|
||||||
|
wasm_bindgen::intern("col-md-1");
|
||||||
|
wasm_bindgen::intern("col-md-6");
|
||||||
|
wasm_bindgen::intern("glyphicon glyphicon-remove remove");
|
||||||
|
wasm_bindgen::intern("remove");
|
||||||
|
wasm_bindgen::intern("dioxus");
|
||||||
|
wasm_bindgen::intern("lbl");
|
||||||
|
wasm_bindgen::intern("true");
|
||||||
|
|
||||||
|
dioxus::web::launch(App, |c| c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static App: FC<()> = |cx| {
|
||||||
|
// let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
|
let mut rng = SmallRng::from_entropy();
|
||||||
|
let rows = (0..1_000).map(|f| {
|
||||||
|
let label = Label::new(&mut rng);
|
||||||
|
rsx! {
|
||||||
|
Row {
|
||||||
|
row_id: f,
|
||||||
|
label: label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cx.render(rsx! {
|
||||||
|
table {
|
||||||
|
tbody {
|
||||||
|
{rows}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// cx.render(rsx! {
|
||||||
|
// div {
|
||||||
|
// // h1 { "Hifive counter: {count}" }
|
||||||
|
// // {cx.children()}
|
||||||
|
// // button { onclick: move |_| count += 1, "Up high!" }
|
||||||
|
// // button { onclick: move |_| count -= 1, "Down low!" }
|
||||||
|
// {(0..1000).map(|i| rsx!{ div { "Count: {count}" } })}
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props)]
|
||||||
|
struct RowProps {
|
||||||
|
row_id: usize,
|
||||||
|
label: Label,
|
||||||
|
}
|
||||||
|
fn Row<'a>(cx: Context<'a, RowProps>) -> DomTree {
|
||||||
|
let [adj, col, noun] = cx.label.0;
|
||||||
|
cx.render(rsx! {
|
||||||
|
tr {
|
||||||
|
td { class:"col-md-1", "{cx.row_id}" }
|
||||||
|
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
||||||
|
a { class: "lbl", "{adj}" "{col}" "{noun}" }
|
||||||
|
}
|
||||||
|
td { class: "col-md-1"
|
||||||
|
a { class: "remove", onclick: move |_| {/* remove */}
|
||||||
|
span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td { class: "col-md-6" }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct Label([&'static str; 3]);
|
||||||
|
|
||||||
|
impl Label {
|
||||||
|
fn new(rng: &mut SmallRng) -> Self {
|
||||||
|
Label([
|
||||||
|
ADJECTIVES.choose(rng).unwrap(),
|
||||||
|
COLOURS.choose(rng).unwrap(),
|
||||||
|
NOUNS.choose(rng).unwrap(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ADJECTIVES: &[&str] = &[
|
||||||
|
"pretty",
|
||||||
|
"large",
|
||||||
|
"big",
|
||||||
|
"small",
|
||||||
|
"tall",
|
||||||
|
"short",
|
||||||
|
"long",
|
||||||
|
"handsome",
|
||||||
|
"plain",
|
||||||
|
"quaint",
|
||||||
|
"clean",
|
||||||
|
"elegant",
|
||||||
|
"easy",
|
||||||
|
"angry",
|
||||||
|
"crazy",
|
||||||
|
"helpful",
|
||||||
|
"mushy",
|
||||||
|
"odd",
|
||||||
|
"unsightly",
|
||||||
|
"adorable",
|
||||||
|
"important",
|
||||||
|
"inexpensive",
|
||||||
|
"cheap",
|
||||||
|
"expensive",
|
||||||
|
"fancy",
|
||||||
|
];
|
||||||
|
|
||||||
|
static COLOURS: &[&str] = &[
|
||||||
|
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
|
||||||
|
"orange",
|
||||||
|
];
|
||||||
|
|
||||||
|
static NOUNS: &[&str] = &[
|
||||||
|
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
|
||||||
|
"pizza", "mouse", "keyboard",
|
||||||
|
];
|
||||||
@@ -26,7 +26,7 @@ fxhash = "0.2.1"
|
|||||||
longest-increasing-subsequence = "0.1.0"
|
longest-increasing-subsequence = "0.1.0"
|
||||||
|
|
||||||
# internall used
|
# internall used
|
||||||
log = "0.4"
|
log = { verison = "0.4", features = ["release_max_level_off"] }
|
||||||
|
|
||||||
futures-util = "0.3.15"
|
futures-util = "0.3.15"
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -90,17 +90,17 @@ impl SharedResources {
|
|||||||
let comps = unsafe { &*components.get() };
|
let comps = unsafe { &*components.get() };
|
||||||
|
|
||||||
if let Some(scope) = comps.get(idx.0) {
|
if let Some(scope) = comps.get(idx.0) {
|
||||||
todo!("implement immediates again")
|
// todo!("implement immediates again")
|
||||||
|
//
|
||||||
|
|
||||||
// queue
|
// queue
|
||||||
// .unbounded_send(EventTrigger::new(
|
// .unbounded_send(EventTrigger::new(
|
||||||
// VirtualEvent::ScheduledUpdate {
|
// V
|
||||||
// height: scope.height,
|
// idx,
|
||||||
// },
|
// None,
|
||||||
// idx,
|
// EventPriority::High,
|
||||||
// None,
|
// ))
|
||||||
// EventPriority::High,
|
// .expect("The event queu receiver should *never* be dropped");
|
||||||
// ))
|
|
||||||
// .expect("The event queu receiver should *never* be dropped");
|
|
||||||
}
|
}
|
||||||
}) as Rc<dyn Fn(ScopeId)>
|
}) as Rc<dyn Fn(ScopeId)>
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -138,14 +138,12 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
pub async fn diff_scope(&mut self, id: ScopeId) -> Result<()> {
|
pub async fn diff_scope(&mut self, id: ScopeId) {
|
||||||
let component = self
|
if let Some(component) = self.vdom.get_scope_mut(id) {
|
||||||
.vdom
|
let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
|
||||||
.get_scope_mut(id)
|
self.stack.push(DiffInstruction::DiffNode { new, old });
|
||||||
.ok_or_else(|| Error::NotMounted)?;
|
self.work().await;
|
||||||
let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
|
}
|
||||||
self.diff_node(old, new);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Progress the diffing for this "fiber"
|
/// Progress the diffing for this "fiber"
|
||||||
|
|||||||
+25
-18
@@ -153,6 +153,7 @@ pub enum VirtualEvent {
|
|||||||
MouseEvent(on::MouseEvent),
|
MouseEvent(on::MouseEvent),
|
||||||
PointerEvent(on::PointerEvent),
|
PointerEvent(on::PointerEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VirtualEvent {
|
impl VirtualEvent {
|
||||||
pub fn is_input_event(&self) -> bool {
|
pub fn is_input_event(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@@ -215,7 +216,7 @@ pub mod on {
|
|||||||
|
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
use bumpalo::boxed::Box as BumpBox;
|
use bumpalo::boxed::Box as BumpBox;
|
||||||
use std::{cell::RefCell, fmt::Debug, ops::Deref, rc::Rc};
|
use std::{any::Any, cell::RefCell, fmt::Debug, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
innerlude::NodeFactory,
|
innerlude::NodeFactory,
|
||||||
@@ -563,6 +564,8 @@ pub mod on {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait 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
|
/// Returns whether or not a specific event is a bubbling event
|
||||||
fn bubbles(&self) -> bool;
|
fn bubbles(&self) -> bool;
|
||||||
/// Sets or returns whether the event should propagate up the hierarchy or not
|
/// Sets or returns whether the event should propagate up the hierarchy or not
|
||||||
@@ -571,14 +574,18 @@ pub mod on {
|
|||||||
fn cancelable(&self) -> bool;
|
fn cancelable(&self) -> bool;
|
||||||
/// Returns whether the event is composed or not
|
/// Returns whether the event is composed or not
|
||||||
fn composed(&self) -> bool;
|
fn composed(&self) -> bool;
|
||||||
/// Returns the event's path
|
|
||||||
fn composed_path(&self) -> String;
|
// 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
|
/// Returns the element whose event listeners triggered the event
|
||||||
fn current_target(&self);
|
fn current_target(&self);
|
||||||
/// Returns whether or not the preventDefault method was called for the event
|
/// Returns whether or not the preventDefault method was called for the event
|
||||||
fn default_prevented(&self) -> bool;
|
fn default_prevented(&self) -> bool;
|
||||||
/// Returns which phase of the event flow is currently being evaluated
|
/// Returns which phase of the event flow is currently being evaluated
|
||||||
fn event_phase(&self) -> usize;
|
fn event_phase(&self) -> u16;
|
||||||
/// Returns whether or not an event is trusted
|
/// Returns whether or not an event is trusted
|
||||||
fn is_trusted(&self) -> bool;
|
fn is_trusted(&self) -> bool;
|
||||||
/// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
|
/// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
|
||||||
@@ -590,7 +597,7 @@ pub mod on {
|
|||||||
/// Returns the element that triggered the event
|
/// Returns the element that triggered the event
|
||||||
fn target(&self);
|
fn target(&self);
|
||||||
/// Returns the time (in milliseconds relative to the epoch) at which the event was created
|
/// Returns the time (in milliseconds relative to the epoch) at which the event was created
|
||||||
fn time_stamp(&self) -> usize;
|
fn time_stamp(&self) -> f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ClipboardEventInner {
|
pub trait ClipboardEventInner {
|
||||||
@@ -688,8 +695,8 @@ pub mod on {
|
|||||||
pub trait PointerEventInner {
|
pub trait PointerEventInner {
|
||||||
// Mouse only
|
// Mouse only
|
||||||
fn alt_key(&self) -> bool;
|
fn alt_key(&self) -> bool;
|
||||||
fn button(&self) -> usize;
|
fn button(&self) -> i16;
|
||||||
fn buttons(&self) -> usize;
|
fn buttons(&self) -> u16;
|
||||||
fn client_x(&self) -> i32;
|
fn client_x(&self) -> i32;
|
||||||
fn client_y(&self) -> i32;
|
fn client_y(&self) -> i32;
|
||||||
fn ctrl_key(&self) -> bool;
|
fn ctrl_key(&self) -> bool;
|
||||||
@@ -699,12 +706,12 @@ pub mod on {
|
|||||||
fn screen_x(&self) -> i32;
|
fn screen_x(&self) -> i32;
|
||||||
fn screen_y(&self) -> i32;
|
fn screen_y(&self) -> i32;
|
||||||
fn shift_key(&self) -> bool;
|
fn shift_key(&self) -> bool;
|
||||||
fn get_modifier_state(&self, key_code: usize) -> bool;
|
fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||||
fn pointer_id(&self) -> usize;
|
fn pointer_id(&self) -> i32;
|
||||||
fn width(&self) -> usize;
|
fn width(&self) -> i32;
|
||||||
fn height(&self) -> usize;
|
fn height(&self) -> i32;
|
||||||
fn pressure(&self) -> usize;
|
fn pressure(&self) -> f32;
|
||||||
fn tangential_pressure(&self) -> usize;
|
fn tangential_pressure(&self) -> f32;
|
||||||
fn tilt_x(&self) -> i32;
|
fn tilt_x(&self) -> i32;
|
||||||
fn tilt_y(&self) -> i32;
|
fn tilt_y(&self) -> i32;
|
||||||
fn twist(&self) -> i32;
|
fn twist(&self) -> i32;
|
||||||
@@ -719,7 +726,7 @@ pub mod on {
|
|||||||
fn ctrl_key(&self) -> bool;
|
fn ctrl_key(&self) -> bool;
|
||||||
fn meta_key(&self) -> bool;
|
fn meta_key(&self) -> bool;
|
||||||
fn shift_key(&self) -> bool;
|
fn shift_key(&self) -> bool;
|
||||||
fn get_modifier_state(&self, key_code: usize) -> bool;
|
fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||||
// changedTouches: DOMTouchList,
|
// changedTouches: DOMTouchList,
|
||||||
// targetTouches: DOMTouchList,
|
// targetTouches: DOMTouchList,
|
||||||
// touches: DOMTouchList,
|
// touches: DOMTouchList,
|
||||||
@@ -731,10 +738,10 @@ pub mod on {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait WheelEventInner {
|
pub trait WheelEventInner {
|
||||||
fn delta_mode(&self) -> i32;
|
fn delta_mode(&self) -> u32;
|
||||||
fn delta_x(&self) -> i32;
|
fn delta_x(&self) -> f64;
|
||||||
fn delta_y(&self) -> i32;
|
fn delta_y(&self) -> f64;
|
||||||
fn delta_z(&self) -> i32;
|
fn delta_z(&self) -> f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MediaEventInner {}
|
pub trait MediaEventInner {}
|
||||||
|
|||||||
@@ -50,15 +50,13 @@ pub struct Scheduler {
|
|||||||
|
|
||||||
shared: SharedResources,
|
shared: SharedResources,
|
||||||
|
|
||||||
waypoints: VecDeque<Waypoint>,
|
|
||||||
|
|
||||||
high_priorty: PriortySystem,
|
high_priorty: PriortySystem,
|
||||||
medium_priority: PriortySystem,
|
medium_priority: PriortySystem,
|
||||||
low_priority: PriortySystem,
|
low_priority: PriortySystem,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FiberResult<'a> {
|
pub enum FiberResult<'a> {
|
||||||
Done(&'a mut Mutations<'a>),
|
Done(Mutations<'a>),
|
||||||
Interrupted,
|
Interrupted,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +73,6 @@ impl Scheduler {
|
|||||||
garbage_scopes: HashSet::new(),
|
garbage_scopes: HashSet::new(),
|
||||||
|
|
||||||
current_priority: EventPriority::Low,
|
current_priority: EventPriority::Low,
|
||||||
waypoints: VecDeque::new(),
|
|
||||||
|
|
||||||
high_priorty: PriortySystem::new(),
|
high_priorty: PriortySystem::new(),
|
||||||
medium_priority: PriortySystem::new(),
|
medium_priority: PriortySystem::new(),
|
||||||
@@ -202,7 +199,9 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_work(&self) -> bool {
|
pub fn has_work(&self) -> bool {
|
||||||
self.waypoints.len() > 0
|
self.high_priorty.has_work()
|
||||||
|
|| self.medium_priority.has_work()
|
||||||
|
|| self.low_priority.has_work()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_pending_garbage(&self) -> bool {
|
pub fn has_pending_garbage(&self) -> bool {
|
||||||
@@ -219,10 +218,10 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// If a the fiber finishes its works (IE needs to be committed) the scheduler will drop the dirty scope
|
/// If a the fiber finishes its works (IE needs to be committed) the scheduler will drop the dirty scope
|
||||||
pub fn work_with_deadline(
|
pub async fn work_with_deadline<'a>(
|
||||||
&mut self,
|
&'a mut self,
|
||||||
mut deadline: &mut Pin<Box<impl FusedFuture<Output = ()>>>,
|
deadline: &mut Pin<Box<impl FusedFuture<Output = ()>>>,
|
||||||
) -> FiberResult {
|
) -> FiberResult<'a> {
|
||||||
// check if we need to elevate priority
|
// check if we need to elevate priority
|
||||||
self.current_priority = match (
|
self.current_priority = match (
|
||||||
self.high_priorty.has_work(),
|
self.high_priorty.has_work(),
|
||||||
@@ -234,11 +233,46 @@ impl Scheduler {
|
|||||||
(false, false, _) => EventPriority::Low,
|
(false, false, _) => EventPriority::Low,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut is_ready = || -> bool { (&mut deadline).now_or_never().is_some() };
|
let mut machine = DiffMachine::new_headless(&self.shared);
|
||||||
|
|
||||||
// TODO: remove this unwrap - proprogate errors out
|
let dirty_root = {
|
||||||
// self.get_current_fiber().work(is_ready).unwrap()
|
let dirty_roots = match self.current_priority {
|
||||||
todo!()
|
EventPriority::High => &self.high_priorty.dirty_scopes,
|
||||||
|
EventPriority::Medium => &self.medium_priority.dirty_scopes,
|
||||||
|
EventPriority::Low => &self.low_priority.dirty_scopes,
|
||||||
|
};
|
||||||
|
let mut height = 0;
|
||||||
|
let mut dirty_root = {
|
||||||
|
let root = dirty_roots.iter().next();
|
||||||
|
if root.is_none() {
|
||||||
|
return FiberResult::Done(machine.mutations);
|
||||||
|
}
|
||||||
|
root.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
for root in dirty_roots {
|
||||||
|
if let Some(scope) = self.shared.get_scope(*root) {
|
||||||
|
if scope.height < height {
|
||||||
|
height = scope.height;
|
||||||
|
dirty_root = root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dirty_root
|
||||||
|
};
|
||||||
|
|
||||||
|
match {
|
||||||
|
let fut = machine.diff_scope(*dirty_root).fuse();
|
||||||
|
pin_mut!(fut);
|
||||||
|
|
||||||
|
match futures_util::future::select(deadline, fut).await {
|
||||||
|
futures_util::future::Either::Left((deadline, work_fut)) => true,
|
||||||
|
futures_util::future::Either::Right((_, deadline_fut)) => false,
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
true => FiberResult::Done(machine.mutations),
|
||||||
|
false => FiberResult::Interrupted,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// waits for a trigger, canceling early if the deadline is reached
|
// waits for a trigger, canceling early if the deadline is reached
|
||||||
@@ -356,32 +390,6 @@ pub struct DirtyScope {
|
|||||||
start_tick: u32,
|
start_tick: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
A "waypoint" represents a frozen unit in time for the DiffingMachine to resume from. Whenever the deadline runs out
|
|
||||||
while diffing, the diffing algorithm generates a Waypoint in order to easily resume from where it left off. Waypoints are
|
|
||||||
fairly expensive to create, especially for big trees, so it's a good idea to pre-allocate them.
|
|
||||||
|
|
||||||
Waypoints are created pessimisticly, and are only generated when an "Error" state is bubbled out of the diffing machine.
|
|
||||||
This saves us from wasting cycles book-keeping waypoints for 99% of edits where the deadline is not reached.
|
|
||||||
*/
|
|
||||||
pub struct Waypoint {
|
|
||||||
// the progenitor of this waypoint
|
|
||||||
root: ScopeId,
|
|
||||||
|
|
||||||
edits: Vec<DomEdit<'static>>,
|
|
||||||
|
|
||||||
// a saved position in the tree
|
|
||||||
// these indicies continue to map through the tree into children nodes.
|
|
||||||
// A sequence of usizes is all that is needed to represent the path to a node.
|
|
||||||
tree_position: SmallVec<[usize; 10]>,
|
|
||||||
|
|
||||||
seen_scopes: HashSet<ScopeId>,
|
|
||||||
|
|
||||||
invalidate_scopes: HashSet<ScopeId>,
|
|
||||||
|
|
||||||
priority_level: EventPriority,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PriortySystem {
|
pub struct PriortySystem {
|
||||||
pub pending_scopes: Vec<ScopeId>,
|
pub pending_scopes: Vec<ScopeId>,
|
||||||
pub dirty_scopes: HashSet<ScopeId>,
|
pub dirty_scopes: HashSet<ScopeId>,
|
||||||
|
|||||||
@@ -298,11 +298,11 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create work from the pending event queue
|
// Create work from the pending event queue
|
||||||
self.scheduler.consume_pending_events();
|
self.scheduler.consume_pending_events().unwrap();
|
||||||
|
|
||||||
// Work through the current subtree, and commit the results when it finishes
|
// Work through the current subtree, and commit the results when it finishes
|
||||||
// When the deadline expires, give back the work
|
// When the deadline expires, give back the work
|
||||||
match self.scheduler.work_with_deadline(&mut deadline) {
|
match self.scheduler.work_with_deadline(&mut deadline).await {
|
||||||
FiberResult::Done(mut mutations) => {
|
FiberResult::Done(mut mutations) => {
|
||||||
committed_mutations.extend(&mut mutations);
|
committed_mutations.extend(&mut mutations);
|
||||||
|
|
||||||
@@ -332,7 +332,10 @@ impl VirtualDom {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_any_work(&self) {}
|
pub async fn wait_for_any_work(&mut self) {
|
||||||
|
let mut timeout = Box::pin(futures_util::future::pending().fuse());
|
||||||
|
self.scheduler.wait_for_any_trigger(&mut timeout).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO!
|
// TODO!
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ js-sys = "0.3"
|
|||||||
wasm-bindgen = { version = "0.2.71", features = ["enable-interning"] }
|
wasm-bindgen = { version = "0.2.71", features = ["enable-interning"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
wasm-bindgen-futures = "0.4.20"
|
wasm-bindgen-futures = "0.4.20"
|
||||||
wasm-logger = "0.2.0"
|
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
|
wasm-logger = "0.2.0"
|
||||||
console_error_panic_hook = "0.1.6"
|
console_error_panic_hook = "0.1.6"
|
||||||
generational-arena = "0.2.8"
|
generational-arena = "0.2.8"
|
||||||
wasm-bindgen-test = "0.3.21"
|
wasm-bindgen-test = "0.3.21"
|
||||||
|
|||||||
+31
-233
@@ -1,7 +1,7 @@
|
|||||||
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
events::{EventTrigger, VirtualEvent},
|
events::{on::GenericEventInner, EventTrigger, VirtualEvent},
|
||||||
mutations::NodeRefMutation,
|
mutations::NodeRefMutation,
|
||||||
DomEdit, ElementId, ScopeId,
|
DomEdit, ElementId, ScopeId,
|
||||||
};
|
};
|
||||||
@@ -9,7 +9,7 @@ use fxhash::FxHashMap;
|
|||||||
use wasm_bindgen::{closure::Closure, JsCast};
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
window, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
window, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
||||||
HtmlOptionElement, Node, NodeList,
|
HtmlOptionElement, Node, NodeList, UiEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{nodeslab::NodeSlab, WebConfig};
|
use crate::{nodeslab::NodeSlab, WebConfig};
|
||||||
@@ -436,284 +436,82 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
|
fn virtual_event_from_websys_event(event: web_sys::Event) -> VirtualEvent {
|
||||||
|
use crate::events::*;
|
||||||
use dioxus_core::events::on::*;
|
use dioxus_core::events::on::*;
|
||||||
match event.type_().as_str() {
|
match event.type_().as_str() {
|
||||||
"copy" | "cut" | "paste" => {
|
"copy" | "cut" | "paste" => {
|
||||||
struct WebsysClipboardEvent();
|
VirtualEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent(event))))
|
||||||
impl ClipboardEventInner for WebsysClipboardEvent {}
|
|
||||||
VirtualEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent())))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||||
let evt: web_sys::CompositionEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::CompositionEvent = event.clone().dyn_into().unwrap();
|
||||||
struct WebsysCompositionEvent(web_sys::CompositionEvent);
|
|
||||||
impl CompositionEventInner for WebsysCompositionEvent {
|
|
||||||
fn data(&self) -> String {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VirtualEvent::CompositionEvent(CompositionEvent(Rc::new(WebsysCompositionEvent(evt))))
|
VirtualEvent::CompositionEvent(CompositionEvent(Rc::new(WebsysCompositionEvent(evt))))
|
||||||
}
|
}
|
||||||
|
|
||||||
"keydown" | "keypress" | "keyup" => {
|
"keydown" | "keypress" | "keyup" => {
|
||||||
struct Event(web_sys::KeyboardEvent);
|
let evt: web_sys::KeyboardEvent = event.dyn_into().unwrap();
|
||||||
impl KeyboardEventInner for Event {
|
VirtualEvent::KeyboardEvent(KeyboardEvent(Rc::new(WebsysKeyboardEvent(evt))))
|
||||||
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 {
|
|
||||||
todo!("Locale is currently not supported. :(")
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let evt: web_sys::KeyboardEvent = event.clone().dyn_into().unwrap();
|
|
||||||
VirtualEvent::KeyboardEvent(KeyboardEvent(Rc::new(Event(evt))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"focus" | "blur" => {
|
"focus" | "blur" => {
|
||||||
struct Event(web_sys::FocusEvent);
|
let evt: web_sys::FocusEvent = event.dyn_into().unwrap();
|
||||||
impl FocusEventInner for Event {}
|
VirtualEvent::FocusEvent(FocusEvent(Rc::new(WebsysFocusEvent(evt))))
|
||||||
let evt: web_sys::FocusEvent = event.clone().dyn_into().unwrap();
|
|
||||||
VirtualEvent::FocusEvent(FocusEvent(Rc::new(Event(evt))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"change" => {
|
"change" => {
|
||||||
// struct Event(web_sys::Event);
|
let evt = event.dyn_into().unwrap();
|
||||||
// impl GenericEventInner for Event {
|
VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
||||||
// fn bubbles(&self) -> bool {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn cancel_bubble(&self) {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn cancelable(&self) -> bool {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn composed(&self) -> bool {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn composed_path(&self) -> String {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn current_target(&self) {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn default_prevented(&self) -> bool {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn event_phase(&self) -> usize {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn is_trusted(&self) -> bool {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn prevent_default(&self) {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn stop_immediate_propagation(&self) {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn stop_propagation(&self) {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn target(&self) {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn time_stamp(&self) -> usize {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// let evt: web_sys::Event = event.clone().dyn_into().expect("wrong error typ");
|
|
||||||
// VirtualEvent::Event(GenericEvent(Rc::new(Event(evt))))
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"input" | "invalid" | "reset" | "submit" => {
|
"input" | "invalid" | "reset" | "submit" => {
|
||||||
// is a special react events
|
let evt: web_sys::InputEvent = event.clone().dyn_into().unwrap();
|
||||||
let evt: web_sys::InputEvent = event.clone().dyn_into().expect("wrong event type");
|
VirtualEvent::FormEvent(FormEvent(Rc::new(WebsysFormEvent(evt))))
|
||||||
let this: web_sys::EventTarget = evt.target().unwrap();
|
|
||||||
|
|
||||||
let value = (&this)
|
|
||||||
.dyn_ref()
|
|
||||||
.map(|input: &web_sys::HtmlInputElement| input.value())
|
|
||||||
.or_else(|| {
|
|
||||||
(&this)
|
|
||||||
.dyn_ref()
|
|
||||||
.map(|input: &web_sys::HtmlTextAreaElement| 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");
|
|
||||||
|
|
||||||
todo!()
|
|
||||||
// VirtualEvent::FormEvent(FormEvent { value })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||||
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||||
let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
|
||||||
|
VirtualEvent::MouseEvent(MouseEvent(Rc::new(WebsysMouseEvent(evt))))
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CustomMouseEvent(web_sys::MouseEvent);
|
|
||||||
impl dioxus_core::events::on::MouseEventInner for CustomMouseEvent {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VirtualEvent::MouseEvent(MouseEvent(Rc::new(CustomMouseEvent(evt))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||||
let evt: web_sys::PointerEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::PointerEvent = event.clone().dyn_into().unwrap();
|
||||||
todo!()
|
VirtualEvent::PointerEvent(PointerEvent(Rc::new(WebsysPointerEvent(evt))))
|
||||||
}
|
}
|
||||||
|
|
||||||
"select" => {
|
"select" => {
|
||||||
// let evt: web_sys::SelectionEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
||||||
// not required to construct anything special beyond standard event stuff
|
VirtualEvent::SelectionEvent(SelectionEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
||||||
let evt: web_sys::TouchEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::TouchEvent = event.clone().dyn_into().unwrap();
|
||||||
todo!()
|
VirtualEvent::TouchEvent(TouchEvent(Rc::new(WebsysTouchEvent(evt))))
|
||||||
}
|
}
|
||||||
|
|
||||||
"scroll" => {
|
"scroll" => {
|
||||||
// let evt: web_sys::UIEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
||||||
todo!()
|
VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
||||||
}
|
}
|
||||||
|
|
||||||
"wheel" => {
|
"wheel" => {
|
||||||
let evt: web_sys::WheelEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::WheelEvent = event.clone().dyn_into().unwrap();
|
||||||
todo!()
|
VirtualEvent::WheelEvent(WheelEvent(Rc::new(WebsysWheelEvent(evt))))
|
||||||
}
|
}
|
||||||
"animationstart" | "animationend" | "animationiteration" => {
|
"animationstart" | "animationend" | "animationiteration" => {
|
||||||
let evt: web_sys::AnimationEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::AnimationEvent = event.clone().dyn_into().unwrap();
|
||||||
todo!()
|
VirtualEvent::AnimationEvent(AnimationEvent(Rc::new(WebsysAnimationEvent(evt))))
|
||||||
}
|
}
|
||||||
|
|
||||||
"transitionend" => {
|
"transitionend" => {
|
||||||
let evt: web_sys::TransitionEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::TransitionEvent = event.clone().dyn_into().unwrap();
|
||||||
todo!()
|
VirtualEvent::TransitionEvent(TransitionEvent(Rc::new(WebsysTransitionEvent(evt))))
|
||||||
}
|
}
|
||||||
|
|
||||||
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||||
| "timeupdate" | "volumechange" | "waiting" => {
|
| "timeupdate" | "volumechange" | "waiting" => {
|
||||||
// not required to construct anything special beyond standard event stuff
|
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
||||||
|
VirtualEvent::MediaEvent(MediaEvent(Rc::new(WebsysMediaEvent(evt))))
|
||||||
// let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
|
|
||||||
// let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"toggle" => {
|
"toggle" => {
|
||||||
// not required to construct anything special beyond standard event stuff (target)
|
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
||||||
|
VirtualEvent::ToggleEvent(ToggleEvent(Rc::new(WebsysToggleEvent(evt))))
|
||||||
// let evt: web_sys::ToggleEvent = event.clone().dyn_into().unwrap();
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
todo!()
|
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
||||||
|
VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -763,7 +561,7 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
|||||||
// let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into();
|
// let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into();
|
||||||
log::debug!("Triggered scope is {:#?}", triggered_scope);
|
log::debug!("Triggered scope is {:#?}", triggered_scope);
|
||||||
Ok(EventTrigger::new(
|
Ok(EventTrigger::new(
|
||||||
virtual_event_from_websys_event(event),
|
virtual_event_from_websys_event(event.clone()),
|
||||||
ScopeId(triggered_scope as usize),
|
ScopeId(triggered_scope as usize),
|
||||||
Some(ElementId(real_id as usize)),
|
Some(ElementId(real_id as usize)),
|
||||||
dioxus_core::events::EventPriority::High,
|
dioxus_core::events::EventPriority::High,
|
||||||
|
|||||||
@@ -0,0 +1,418 @@
|
|||||||
|
//! Ported events into Dioxus Synthetic Event system
|
||||||
|
//!
|
||||||
|
//! event porting is pretty boring, sorry.
|
||||||
|
|
||||||
|
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 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::InputEvent);
|
||||||
|
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())
|
||||||
|
})
|
||||||
|
.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 {}
|
||||||
@@ -56,6 +56,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
pub use crate::cfg::WebConfig;
|
pub use crate::cfg::WebConfig;
|
||||||
use crate::dom::load_document;
|
use crate::dom::load_document;
|
||||||
|
use cache::intern_cache;
|
||||||
use dioxus::prelude::Properties;
|
use dioxus::prelude::Properties;
|
||||||
use dioxus::virtual_dom::VirtualDom;
|
use dioxus::virtual_dom::VirtualDom;
|
||||||
pub use dioxus_core as dioxus;
|
pub use dioxus_core as dioxus;
|
||||||
@@ -64,6 +65,7 @@ use dioxus_core::prelude::FC;
|
|||||||
mod cache;
|
mod cache;
|
||||||
mod cfg;
|
mod cfg;
|
||||||
mod dom;
|
mod dom;
|
||||||
|
mod events;
|
||||||
mod nodeslab;
|
mod nodeslab;
|
||||||
mod ric_raf;
|
mod ric_raf;
|
||||||
|
|
||||||
@@ -114,6 +116,8 @@ where
|
|||||||
pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) {
|
pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) {
|
||||||
let mut dom = VirtualDom::new_with_props(root, root_props);
|
let mut dom = VirtualDom::new_with_props(root, root_props);
|
||||||
|
|
||||||
|
intern_cache();
|
||||||
|
|
||||||
let hydrating = cfg.hydrate;
|
let hydrating = cfg.hydrate;
|
||||||
|
|
||||||
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
|
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
//! RequestAnimationFrame and RequestIdleCallback port and polyfill.
|
//! RequestAnimationFrame and RequestIdleCallback port and polyfill.
|
||||||
|
|
||||||
use std::{cell::RefCell, fmt, rc::Rc};
|
|
||||||
|
|
||||||
use gloo_timers::future::TimeoutFuture;
|
use gloo_timers::future::TimeoutFuture;
|
||||||
use js_sys::Function;
|
use js_sys::Function;
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen::{prelude::Closure, JsValue};
|
use wasm_bindgen::{prelude::Closure, JsValue};
|
||||||
use web_sys::Window;
|
use web_sys::Window;
|
||||||
@@ -21,13 +18,15 @@ impl RafLoop {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let (raf_sender, raf_receiver) = async_channel::unbounded();
|
let (raf_sender, raf_receiver) = async_channel::unbounded();
|
||||||
|
|
||||||
let raf_closure: Closure<dyn Fn(JsValue)> =
|
let raf_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
|
||||||
Closure::wrap(Box::new(move |v: JsValue| raf_sender.try_send(()).unwrap()));
|
raf_sender.try_send(()).unwrap()
|
||||||
|
}));
|
||||||
|
|
||||||
let (ric_sender, ric_receiver) = async_channel::unbounded();
|
let (ric_sender, ric_receiver) = async_channel::unbounded();
|
||||||
|
|
||||||
let ric_closure: Closure<dyn Fn(JsValue)> =
|
let ric_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
|
||||||
Closure::wrap(Box::new(move |v: JsValue| ric_sender.try_send(()).unwrap()));
|
ric_sender.try_send(()).unwrap()
|
||||||
|
}));
|
||||||
|
|
||||||
// execute the polyfill for safari
|
// execute the polyfill for safari
|
||||||
Function::new_no_args(include_str!("./ricpolyfill.js"))
|
Function::new_no_args(include_str!("./ricpolyfill.js"))
|
||||||
@@ -44,45 +43,16 @@ impl RafLoop {
|
|||||||
}
|
}
|
||||||
/// waits for some idle time and returns a timeout future that expires after the idle time has passed
|
/// waits for some idle time and returns a timeout future that expires after the idle time has passed
|
||||||
pub async fn wait_for_idle_time(&self) -> TimeoutFuture {
|
pub async fn wait_for_idle_time(&self) -> TimeoutFuture {
|
||||||
// comes with its own safari polyfill :)
|
|
||||||
|
|
||||||
let ric_fn = self.ric_closure.as_ref().dyn_ref::<Function>().unwrap();
|
let ric_fn = self.ric_closure.as_ref().dyn_ref::<Function>().unwrap();
|
||||||
let deadline: u32 = self.window.request_idle_callback(ric_fn).unwrap();
|
let deadline: u32 = self.window.request_idle_callback(ric_fn).unwrap();
|
||||||
|
|
||||||
self.ric_receiver.recv().await.unwrap();
|
self.ric_receiver.recv().await.unwrap();
|
||||||
|
|
||||||
let deadline = TimeoutFuture::new(deadline);
|
let deadline = TimeoutFuture::new(deadline);
|
||||||
deadline
|
deadline
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_raf(&self) {
|
pub async fn wait_for_raf(&self) {
|
||||||
let raf_fn = self.raf_closure.as_ref().dyn_ref::<Function>().unwrap();
|
let raf_fn = self.raf_closure.as_ref().dyn_ref::<Function>().unwrap();
|
||||||
let id: i32 = self.window.request_animation_frame(raf_fn).unwrap();
|
let _id: i32 = self.window.request_animation_frame(raf_fn).unwrap();
|
||||||
self.raf_receiver.recv().await.unwrap();
|
self.raf_receiver.recv().await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AnimationFrame {
|
|
||||||
render_id: i32,
|
|
||||||
closure: Closure<dyn Fn(JsValue)>,
|
|
||||||
callback_wrapper: Rc<RefCell<Option<CallbackWrapper>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CallbackWrapper(Box<dyn FnOnce(f64) + 'static>);
|
|
||||||
impl fmt::Debug for CallbackWrapper {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("CallbackWrapper")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for AnimationFrame {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.callback_wrapper.borrow_mut().is_some() {
|
|
||||||
web_sys::window()
|
|
||||||
.unwrap_throw()
|
|
||||||
.cancel_animation_frame(self.render_id)
|
|
||||||
.unwrap_throw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user