feat: get desktop working with just creation
This commit is contained in:
1 parent
8caabb97d8
commit
e8ae830cf2
16 files changed
+1301
-329
No files matched your search
@@ -47,6 +47,7 @@ impl VirtualDom {
|
|||||||
// todo: adjust dynamic nodes to be in the order of roots and then leaves (ie BFS)
|
// todo: adjust dynamic nodes to be in the order of roots and then leaves (ie BFS)
|
||||||
let mut dynamic_attrs = template.template.attr_paths.iter().enumerate().peekable();
|
let mut dynamic_attrs = template.template.attr_paths.iter().enumerate().peekable();
|
||||||
let mut dynamic_nodes = template.template.node_paths.iter().enumerate().peekable();
|
let mut dynamic_nodes = template.template.node_paths.iter().enumerate().peekable();
|
||||||
|
let cur_scope = self.scope_stack.last().copied().unwrap();
|
||||||
|
|
||||||
let mut on_stack = 0;
|
let mut on_stack = 0;
|
||||||
for (root_idx, root) in template.template.roots.iter().enumerate() {
|
for (root_idx, root) in template.template.roots.iter().enumerate() {
|
||||||
@@ -78,21 +79,25 @@ impl VirtualDom {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let attribute = &template.dynamic_attrs[attr_id];
|
let attribute = template.dynamic_attrs.get(attr_id).unwrap();
|
||||||
attribute.mounted_element.set(id);
|
attribute.mounted_element.set(id);
|
||||||
|
|
||||||
match attribute.value {
|
match &attribute.value {
|
||||||
AttributeValue::Text(value) => mutations.push(SetAttribute {
|
AttributeValue::Text(value) => mutations.push(SetAttribute {
|
||||||
name: attribute.name,
|
name: attribute.name,
|
||||||
value,
|
value: *value,
|
||||||
id,
|
id,
|
||||||
}),
|
}),
|
||||||
AttributeValue::Bool(value) => mutations.push(SetBoolAttribute {
|
AttributeValue::Bool(value) => mutations.push(SetBoolAttribute {
|
||||||
name: attribute.name,
|
name: attribute.name,
|
||||||
value,
|
value: *value,
|
||||||
|
id,
|
||||||
|
}),
|
||||||
|
AttributeValue::Listener(_) => mutations.push(NewEventListener {
|
||||||
|
event_name: attribute.name,
|
||||||
|
scope: cur_scope,
|
||||||
id,
|
id,
|
||||||
}),
|
}),
|
||||||
AttributeValue::Listener(_) => todo!("create listener attributes"),
|
|
||||||
AttributeValue::Float(_) => todo!(),
|
AttributeValue::Float(_) => todo!(),
|
||||||
AttributeValue::Int(_) => todo!(),
|
AttributeValue::Int(_) => todo!(),
|
||||||
AttributeValue::Any(_) => todo!(),
|
AttributeValue::Any(_) => todo!(),
|
||||||
@@ -138,8 +143,8 @@ impl VirtualDom {
|
|||||||
let id = self.next_element(template);
|
let id = self.next_element(template);
|
||||||
mutations.push(CreatePlaceholder { id })
|
mutations.push(CreatePlaceholder { id })
|
||||||
}
|
}
|
||||||
TemplateNode::Text(value) => mutations.push(CreateText { value }),
|
TemplateNode::Text(value) => mutations.push(CreateTextNode { value }),
|
||||||
TemplateNode::DynamicText { .. } => mutations.push(CreateText {
|
TemplateNode::DynamicText { .. } => mutations.push(CreateTextNode {
|
||||||
value: "placeholder",
|
value: "placeholder",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ impl<'b> VirtualDom {
|
|||||||
|
|
||||||
(DynamicNode::Text { id: lid, .. }, right) => {
|
(DynamicNode::Text { id: lid, .. }, right) => {
|
||||||
let m = self.create_dynamic_node(muts, right_template, right, idx);
|
let m = self.create_dynamic_node(muts, right_template, right, idx);
|
||||||
muts.push(Mutation::Replace { id: lid.get(), m });
|
muts.push(Mutation::ReplaceWith { id: lid.get(), m });
|
||||||
}
|
}
|
||||||
|
|
||||||
(DynamicNode::Placeholder(_), DynamicNode::Placeholder(_)) => todo!(),
|
(DynamicNode::Placeholder(_), DynamicNode::Placeholder(_)) => todo!(),
|
||||||
|
|||||||
@@ -6,11 +6,18 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct UiEvent<T> {
|
pub struct UiEvent<T: 'static> {
|
||||||
bubble_state: Cell<bool>,
|
pub(crate) bubble_state: Cell<bool>,
|
||||||
data: Rc<T>,
|
pub(crate) data: Rc<T>,
|
||||||
|
}
|
||||||
|
impl<T> Clone for UiEvent<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
bubble_state: self.bubble_state.clone(),
|
||||||
|
data: self.data.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> UiEvent<T> {
|
impl<T> UiEvent<T> {
|
||||||
pub fn cancel_bubble(&self) {
|
pub fn cancel_bubble(&self) {
|
||||||
self.bubble_state.set(false);
|
self.bubble_state.set(false);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::arena::ElementId;
|
use crate::{arena::ElementId, ScopeId};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Mutations<'a> {
|
pub struct Mutations<'a> {
|
||||||
@@ -35,9 +35,61 @@ impl std::ops::DerefMut for Mutations<'_> {
|
|||||||
each subtree has its own numbering scheme
|
each subtree has its own numbering scheme
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(
|
||||||
|
feature = "serialize",
|
||||||
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
|
serde(tag = "type")
|
||||||
|
)]
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Mutation<'a> {
|
pub enum Mutation<'a> {
|
||||||
|
AppendChildren {
|
||||||
|
m: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
AssignId {
|
||||||
|
path: &'static [u8],
|
||||||
|
id: ElementId,
|
||||||
|
},
|
||||||
|
|
||||||
|
CreateElement {
|
||||||
|
name: &'a str,
|
||||||
|
namespace: Option<&'a str>,
|
||||||
|
id: ElementId,
|
||||||
|
},
|
||||||
|
|
||||||
|
CreatePlaceholder {
|
||||||
|
id: ElementId,
|
||||||
|
},
|
||||||
|
|
||||||
|
CreateTextNode {
|
||||||
|
value: &'a str,
|
||||||
|
},
|
||||||
|
HydrateText {
|
||||||
|
path: &'static [u8],
|
||||||
|
value: &'a str,
|
||||||
|
id: ElementId,
|
||||||
|
},
|
||||||
|
LoadTemplate {
|
||||||
|
name: &'static str,
|
||||||
|
index: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Take the current element and replace it with the element with the given id.
|
||||||
|
ReplaceWith {
|
||||||
|
id: ElementId,
|
||||||
|
m: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
ReplacePlaceholder {
|
||||||
|
m: usize,
|
||||||
|
path: &'static [u8],
|
||||||
|
},
|
||||||
|
|
||||||
|
SaveTemplate {
|
||||||
|
name: &'static str,
|
||||||
|
m: usize,
|
||||||
|
},
|
||||||
|
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
value: &'a str,
|
value: &'a str,
|
||||||
@@ -50,20 +102,8 @@ pub enum Mutation<'a> {
|
|||||||
id: ElementId,
|
id: ElementId,
|
||||||
},
|
},
|
||||||
|
|
||||||
LoadTemplate {
|
SetInnerText {
|
||||||
name: &'static str,
|
|
||||||
index: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
SaveTemplate {
|
|
||||||
name: &'static str,
|
|
||||||
m: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
HydrateText {
|
|
||||||
path: &'static [u8],
|
|
||||||
value: &'a str,
|
value: &'a str,
|
||||||
id: ElementId,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
SetText {
|
SetText {
|
||||||
@@ -71,41 +111,24 @@ pub enum Mutation<'a> {
|
|||||||
id: ElementId,
|
id: ElementId,
|
||||||
},
|
},
|
||||||
|
|
||||||
ReplacePlaceholder {
|
/// Create a new Event Listener.
|
||||||
m: usize,
|
NewEventListener {
|
||||||
path: &'static [u8],
|
/// The name of the event to listen for.
|
||||||
},
|
event_name: &'a str,
|
||||||
|
|
||||||
AssignId {
|
/// The ID of the node to attach the listener to.
|
||||||
path: &'static [u8],
|
scope: ScopeId,
|
||||||
|
|
||||||
|
/// The ID of the node to attach the listener to.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Take the current element and replace it with the element with the given id.
|
/// Remove an existing Event Listener.
|
||||||
Replace {
|
RemoveEventListener {
|
||||||
|
/// The ID of the node to remove.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
m: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
CreateElement {
|
/// The name of the event to remove.
|
||||||
name: &'a str,
|
event: &'a str,
|
||||||
namespace: Option<&'a str>,
|
|
||||||
id: ElementId,
|
|
||||||
},
|
|
||||||
|
|
||||||
SetInnerText {
|
|
||||||
value: &'a str,
|
|
||||||
},
|
|
||||||
|
|
||||||
CreateText {
|
|
||||||
value: &'a str,
|
|
||||||
},
|
|
||||||
|
|
||||||
CreatePlaceholder {
|
|
||||||
id: ElementId,
|
|
||||||
},
|
|
||||||
|
|
||||||
AppendChildren {
|
|
||||||
m: usize,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::{any_props::AnyProps, arena::ElementId, ScopeId, ScopeState};
|
use crate::{any_props::AnyProps, arena::ElementId, ScopeId, ScopeState, UiEvent};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
hash::Hasher,
|
hash::Hasher,
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type TemplateId = &'static str;
|
pub type TemplateId = &'static str;
|
||||||
@@ -154,6 +155,27 @@ pub enum AttributeValue<'a> {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> AttributeValue<'a> {
|
||||||
|
pub fn new_listener<T: 'static>(
|
||||||
|
cx: &'a ScopeState,
|
||||||
|
mut f: impl FnMut(UiEvent<T>) + 'a,
|
||||||
|
) -> AttributeValue<'a> {
|
||||||
|
let f = cx.bump().alloc(move |a: &dyn Any| {
|
||||||
|
a.downcast_ref::<UiEvent<T>>()
|
||||||
|
.map(|a| f(a.clone()))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"Expected UiEvent<{}>, got {:?}",
|
||||||
|
std::any::type_name::<T>(),
|
||||||
|
a
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}) as &mut dyn FnMut(&dyn Any);
|
||||||
|
|
||||||
|
AttributeValue::Listener(RefCell::new(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{
|
|||||||
nodes::{Template, TemplateId},
|
nodes::{Template, TemplateId},
|
||||||
scheduler::{SuspenseBoundary, SuspenseId},
|
scheduler::{SuspenseBoundary, SuspenseId},
|
||||||
scopes::{ScopeId, ScopeState},
|
scopes::{ScopeId, ScopeState},
|
||||||
Attribute, AttributeValue, Element, EventPriority, Scope, SuspenseContext,
|
Attribute, AttributeValue, Element, EventPriority, Scope, SuspenseContext, UiEvent,
|
||||||
};
|
};
|
||||||
use futures_util::{pin_mut, FutureExt, StreamExt};
|
use futures_util::{pin_mut, FutureExt, StreamExt};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
@@ -288,10 +288,10 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub fn handle_event(
|
pub fn handle_event<T: 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
event: &dyn Any,
|
data: Rc<T>,
|
||||||
element: ElementId,
|
element: ElementId,
|
||||||
bubbles: bool,
|
bubbles: bool,
|
||||||
priority: EventPriority,
|
priority: EventPriority,
|
||||||
@@ -304,6 +304,11 @@ impl VirtualDom {
|
|||||||
- send event to virtualdom
|
- send event to virtualdom
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
let event = UiEvent {
|
||||||
|
bubble_state: std::cell::Cell::new(true),
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
|
||||||
let path = &self.elements[element.0];
|
let path = &self.elements[element.0];
|
||||||
let template = unsafe { &*path.template };
|
let template = unsafe { &*path.template };
|
||||||
let dynamic = &template.dynamic_nodes[path.element];
|
let dynamic = &template.dynamic_nodes[path.element];
|
||||||
@@ -339,7 +344,7 @@ impl VirtualDom {
|
|||||||
|
|
||||||
for listener in listeners {
|
for listener in listeners {
|
||||||
if let AttributeValue::Listener(listener) = &listener.value {
|
if let AttributeValue::Listener(listener) = &listener.value {
|
||||||
(listener.borrow_mut())(event)
|
(listener.borrow_mut())(&event.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ serde = "1.0.136"
|
|||||||
serde_json = "1.0.79"
|
serde_json = "1.0.79"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
wry = { version = "0.20.2" }
|
wry = { version = "0.22.0" }
|
||||||
futures-channel = "0.3.21"
|
futures-channel = "0.3.21"
|
||||||
tokio = { version = "1.16.1", features = [
|
tokio = { version = "1.16.1", features = [
|
||||||
"sync",
|
"sync",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>;
|
|||||||
|
|
||||||
pub(crate) type WryProtocol = (
|
pub(crate) type WryProtocol = (
|
||||||
String,
|
String,
|
||||||
Box<dyn Fn(&HttpRequest) -> WryResult<HttpResponse> + 'static>,
|
Box<dyn Fn(&HttpRequest<Vec<u8>>) -> WryResult<HttpResponse<Vec<u8>>> + 'static>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -96,7 +96,7 @@ impl Config {
|
|||||||
/// Set a custom protocol
|
/// Set a custom protocol
|
||||||
pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
|
pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&HttpRequest) -> WryResult<HttpResponse> + 'static,
|
F: Fn(&HttpRequest<Vec<u8>>) -> WryResult<HttpResponse<Vec<u8>>> + 'static,
|
||||||
{
|
{
|
||||||
self.protocols.push((name, Box::new(handler)));
|
self.protocols.push((name, Box::new(handler)));
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -40,24 +40,23 @@ impl DesktopController {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
runtime.block_on(async move {
|
runtime.block_on(async move {
|
||||||
|
println!("starting vdom");
|
||||||
|
|
||||||
let mut dom = VirtualDom::new_with_props(root, props);
|
let mut dom = VirtualDom::new_with_props(root, props);
|
||||||
|
|
||||||
let window_context = DesktopContext::new(desktop_context_proxy);
|
let window_context = DesktopContext::new(desktop_context_proxy);
|
||||||
|
|
||||||
dom.base_scope().provide_context(window_context);
|
dom.base_scope().provide_context(window_context);
|
||||||
|
|
||||||
// // allow other proccesses to send the new rsx text to the @dioxusin ipc channel and recieve erros on the @dioxusout channel
|
|
||||||
// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
||||||
// crate::hot_reload::init(&dom);
|
|
||||||
|
|
||||||
let edits = dom.rebuild();
|
let edits = dom.rebuild();
|
||||||
let mut queue = edit_queue.lock().unwrap();
|
|
||||||
|
|
||||||
queue.push(serde_json::to_string(&edits.template_mutations).unwrap());
|
{
|
||||||
queue.push(serde_json::to_string(&edits.edits).unwrap());
|
let mut queue = edit_queue.lock().unwrap();
|
||||||
|
queue.push(serde_json::to_string(&edits.template_mutations).unwrap());
|
||||||
// Make sure the window is ready for any new updates
|
queue.push(serde_json::to_string(&edits.edits).unwrap());
|
||||||
proxy.send_event(UserWindowEvent::Update).unwrap();
|
proxy.send_event(UserWindowEvent::Update).unwrap();
|
||||||
|
drop(queue);
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// todo: add the channel of the event loop in
|
// todo: add the channel of the event loop in
|
||||||
@@ -71,14 +70,18 @@ impl DesktopController {
|
|||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut queue = edit_queue.lock().unwrap();
|
{
|
||||||
|
let mut queue = edit_queue.lock().unwrap();
|
||||||
|
|
||||||
for edit in muts.template_mutations {
|
for edit in muts.template_mutations {
|
||||||
queue.push(serde_json::to_string(&edit).unwrap());
|
queue.push(serde_json::to_string(&edit).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
for edit in muts.edits {
|
for edit in muts.edits {
|
||||||
queue.push(serde_json::to_string(&edit).unwrap());
|
queue.push(serde_json::to_string(&edit).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = proxy.send_event(UserWindowEvent::Update);
|
let _ = proxy.send_event(UserWindowEvent::Update);
|
||||||
@@ -104,10 +107,16 @@ impl DesktopController {
|
|||||||
|
|
||||||
pub(super) fn try_load_ready_webviews(&mut self) {
|
pub(super) fn try_load_ready_webviews(&mut self) {
|
||||||
if self.is_ready.load(std::sync::atomic::Ordering::Relaxed) {
|
if self.is_ready.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
let mut queue = self.pending_edits.lock().unwrap();
|
let mut new_queue = Vec::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut queue = self.pending_edits.lock().unwrap();
|
||||||
|
std::mem::swap(&mut new_queue, &mut *queue);
|
||||||
|
}
|
||||||
|
|
||||||
let (_id, view) = self.webviews.iter_mut().next().unwrap();
|
let (_id, view) = self.webviews.iter_mut().next().unwrap();
|
||||||
|
|
||||||
for edit in queue.drain(..) {
|
for edit in new_queue.drain(..) {
|
||||||
view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit))
|
view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::controller::DesktopController;
|
use crate::controller::DesktopController;
|
||||||
use dioxus_core::ScopeState;
|
use dioxus_core::ScopeState;
|
||||||
use wry::application::event_loop::ControlFlow;
|
use wry::application::event_loop::ControlFlow;
|
||||||
@@ -15,6 +17,13 @@ pub fn use_window(cx: &ScopeState) -> &DesktopContext {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a closure that executes any JavaScript in the WebView context.
|
||||||
|
pub fn use_eval(cx: &ScopeState) -> &Rc<dyn Fn(String)> {
|
||||||
|
let desktop = use_window(cx).clone();
|
||||||
|
|
||||||
|
&*cx.use_hook(|| Rc::new(move |script| desktop.eval(script)) as Rc<dyn Fn(String)>)
|
||||||
|
}
|
||||||
|
|
||||||
/// An imperative interface to the current window.
|
/// An imperative interface to the current window.
|
||||||
///
|
///
|
||||||
/// To get a handle to the current window, use the [`use_window`] hook.
|
/// To get a handle to the current window, use the [`use_window`] hook.
|
||||||
@@ -176,6 +185,8 @@ pub(super) fn handler(
|
|||||||
let webview = desktop.webviews.values().next().unwrap();
|
let webview = desktop.webviews.values().next().unwrap();
|
||||||
let window = webview.window();
|
let window = webview.window();
|
||||||
|
|
||||||
|
println!("user_event: {:?}", user_event);
|
||||||
|
|
||||||
match user_event {
|
match user_event {
|
||||||
Update => desktop.try_load_ready_webviews(),
|
Update => desktop.try_load_ready_webviews(),
|
||||||
CloseWindow => *control_flow = ControlFlow::Exit,
|
CloseWindow => *control_flow = ControlFlow::Exit,
|
||||||
@@ -227,10 +238,3 @@ pub(super) fn handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a closure that executes any JavaScript in the WebView context.
|
|
||||||
pub fn use_eval<S: std::string::ToString>(cx: &ScopeState) -> &dyn Fn(S) {
|
|
||||||
let desktop = use_window(cx).clone();
|
|
||||||
|
|
||||||
cx.use_hook(|| move |script| desktop.eval(script))
|
|
||||||
}
|
|
||||||
@@ -105,19 +105,20 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
|||||||
|
|
||||||
// We assume that if the icon is None, then the user just didnt set it
|
// We assume that if the icon is None, then the user just didnt set it
|
||||||
if cfg.window.window.window_icon.is_none() {
|
if cfg.window.window.window_icon.is_none() {
|
||||||
cfg.window.window.window_icon = Some(
|
cfg.window = cfg.window.with_window_icon(Some(
|
||||||
tao::window::Icon::from_rgba(
|
tao::window::Icon::from_rgba(
|
||||||
include_bytes!("./assets/default_icon.bin").to_vec(),
|
include_bytes!("./assets/default_icon.bin").to_vec(),
|
||||||
460,
|
460,
|
||||||
460,
|
460,
|
||||||
)
|
)
|
||||||
.expect("image parse failed"),
|
.expect("image parse failed"),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
event_loop.run(move |window_event, event_loop, control_flow| {
|
event_loop.run(move |window_event, event_loop, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
|
// println!("window event: {:?}", window_event);
|
||||||
match window_event {
|
match window_event {
|
||||||
Event::NewEvents(StartCause::Init) => {
|
Event::NewEvents(StartCause::Init) => {
|
||||||
let builder = cfg.window.clone();
|
let builder = cfg.window.clone();
|
||||||
@@ -151,6 +152,7 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
|||||||
}
|
}
|
||||||
"initialize" => {
|
"initialize" => {
|
||||||
is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
|
is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
println!("initializing...");
|
||||||
let _ = proxy.send_event(UserWindowEvent::Update);
|
let _ = proxy.send_event(UserWindowEvent::Update);
|
||||||
}
|
}
|
||||||
"browser_open" => {
|
"browser_open" => {
|
||||||
@@ -179,6 +181,12 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
|||||||
index_file.clone(),
|
index_file.clone(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
// passing edits via the custom protocol is faster than using eval, maybe?
|
||||||
|
.with_custom_protocol(String::from("edits"), move |r| {
|
||||||
|
//
|
||||||
|
// Ok(Response::body())
|
||||||
|
todo!()
|
||||||
|
})
|
||||||
.with_file_drop_handler(move |window, evet| {
|
.with_file_drop_handler(move |window, evet| {
|
||||||
file_handler
|
file_handler
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use wry::{
|
use wry::{
|
||||||
http::{status::StatusCode, Request, Response, ResponseBuilder},
|
http::{status::StatusCode, Request, Response},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -13,14 +13,14 @@ const MODULE_LOADER: &str = r#"
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
pub(super) fn desktop_handler(
|
pub(super) fn desktop_handler(
|
||||||
request: &Request,
|
request: &Request<Vec<u8>>,
|
||||||
asset_root: Option<PathBuf>,
|
asset_root: Option<PathBuf>,
|
||||||
custom_head: Option<String>,
|
custom_head: Option<String>,
|
||||||
custom_index: Option<String>,
|
custom_index: Option<String>,
|
||||||
) -> Result<Response> {
|
) -> Result<Response<Vec<u8>>> {
|
||||||
// Any content that uses the `dioxus://` scheme will be shuttled through this handler as a "special case".
|
// Any content that uses the `dioxus://` scheme will be shuttled through this handler as a "special case".
|
||||||
// For now, we only serve two pieces of content which get included as bytes into the final binary.
|
// For now, we only serve two pieces of content which get included as bytes into the final binary.
|
||||||
let path = request.uri().replace("dioxus://", "");
|
let path = request.uri().to_string().replace("dioxus://", "");
|
||||||
|
|
||||||
// all assets should be called from index.html
|
// all assets should be called from index.html
|
||||||
let trimmed = path.trim_start_matches("index.html/");
|
let trimmed = path.trim_start_matches("index.html/");
|
||||||
@@ -32,7 +32,10 @@ pub(super) fn desktop_handler(
|
|||||||
let rendered = custom_index
|
let rendered = custom_index
|
||||||
.replace("</body>", &format!("{}</body>", MODULE_LOADER))
|
.replace("</body>", &format!("{}</body>", MODULE_LOADER))
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
ResponseBuilder::new().mimetype("text/html").body(rendered)
|
Response::builder()
|
||||||
|
.header("Content-Type", "text/html")
|
||||||
|
.body(rendered)
|
||||||
|
.map_err(From::from)
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we'll serve the default index.html and apply a custom head if that's specified.
|
// Otherwise, we'll serve the default index.html and apply a custom head if that's specified.
|
||||||
let mut template = include_str!("./index.html").to_string();
|
let mut template = include_str!("./index.html").to_string();
|
||||||
@@ -41,14 +44,16 @@ pub(super) fn desktop_handler(
|
|||||||
}
|
}
|
||||||
template = template.replace("<!-- MODULE LOADER -->", MODULE_LOADER);
|
template = template.replace("<!-- MODULE LOADER -->", MODULE_LOADER);
|
||||||
|
|
||||||
ResponseBuilder::new()
|
Response::builder()
|
||||||
.mimetype("text/html")
|
.header("Content-Type", "text/html")
|
||||||
.body(template.into_bytes())
|
.body(template.into_bytes())
|
||||||
|
.map_err(From::from)
|
||||||
}
|
}
|
||||||
} else if trimmed == "index.js" {
|
} else if trimmed == "index.js" {
|
||||||
ResponseBuilder::new()
|
Response::builder()
|
||||||
.mimetype("text/javascript")
|
.header("Content-Type", "text/javascript")
|
||||||
.body(dioxus_interpreter_js::INTERPRETER_JS.as_bytes().to_vec())
|
.body(dioxus_interpreter_js::INTERPRETER_JS.as_bytes().to_vec())
|
||||||
|
.map_err(From::from)
|
||||||
} else {
|
} else {
|
||||||
let asset_root = asset_root
|
let asset_root = asset_root
|
||||||
.unwrap_or_else(|| get_asset_root().unwrap_or_else(|| Path::new(".").to_path_buf()))
|
.unwrap_or_else(|| get_asset_root().unwrap_or_else(|| Path::new(".").to_path_buf()))
|
||||||
@@ -57,20 +62,23 @@ pub(super) fn desktop_handler(
|
|||||||
let asset = asset_root.join(trimmed).canonicalize()?;
|
let asset = asset_root.join(trimmed).canonicalize()?;
|
||||||
|
|
||||||
if !asset.starts_with(asset_root) {
|
if !asset.starts_with(asset_root) {
|
||||||
return ResponseBuilder::new()
|
return Response::builder()
|
||||||
.status(StatusCode::FORBIDDEN)
|
.status(StatusCode::FORBIDDEN)
|
||||||
.body(String::from("Forbidden").into_bytes());
|
.body(String::from("Forbidden").into_bytes())
|
||||||
|
.map_err(From::from);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !asset.exists() {
|
if !asset.exists() {
|
||||||
return ResponseBuilder::new()
|
return Response::builder()
|
||||||
.status(StatusCode::NOT_FOUND)
|
.status(StatusCode::NOT_FOUND)
|
||||||
.body(String::from("Not Found").into_bytes());
|
.body(String::from("Not Found").into_bytes())
|
||||||
|
.map_err(From::from);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseBuilder::new()
|
Response::builder()
|
||||||
.mimetype(get_mime_from_path(trimmed)?)
|
.header("Content-Type", get_mime_from_path(trimmed)?)
|
||||||
.body(std::fs::read(asset)?)
|
.body(std::fs::read(asset)?)
|
||||||
|
.map_err(From::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,25 +42,25 @@ fn dual_stream() {
|
|||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
CreateElement { name: "div", namespace: None, id: ElementId(4) },
|
CreateElement { name: "div", namespace: None, id: ElementId(4) },
|
||||||
CreateElement { name: "h1", namespace: None, id: ElementId(5) },
|
CreateElement { name: "h1", namespace: None, id: ElementId(5) },
|
||||||
CreateText { value: "var" },
|
CreateTextNode { value: "var" },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
CreateElement { name: "p", namespace: None, id: ElementId(6) },
|
CreateElement { name: "p", namespace: None, id: ElementId(6) },
|
||||||
CreateText { value: "you're great!" },
|
CreateTextNode { value: "you're great!" },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
CreateElement { name: "div", namespace: None, id: ElementId(7) },
|
CreateElement { name: "div", namespace: None, id: ElementId(7) },
|
||||||
SetAttribute { name: "background-color", value: "red", id: ElementId(7) },
|
SetAttribute { name: "background-color", value: "red", id: ElementId(7) },
|
||||||
CreateElement { name: "h1", namespace: None, id: ElementId(8) },
|
CreateElement { name: "h1", namespace: None, id: ElementId(8) },
|
||||||
CreateText { value: "var" },
|
CreateTextNode { value: "var" },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
CreateElement { name: "div", namespace: None, id: ElementId(9) },
|
CreateElement { name: "div", namespace: None, id: ElementId(9) },
|
||||||
CreateElement { name: "b", namespace: None, id: ElementId(10) },
|
CreateElement { name: "b", namespace: None, id: ElementId(10) },
|
||||||
CreateText { value: "asd" },
|
CreateTextNode { value: "asd" },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
CreateText { value: "not great" },
|
CreateTextNode { value: "not great" },
|
||||||
AppendChildren { m: 2 },
|
AppendChildren { m: 2 },
|
||||||
AppendChildren { m: 2 },
|
AppendChildren { m: 2 },
|
||||||
CreateElement { name: "p", namespace: None, id: ElementId(11) },
|
CreateElement { name: "p", namespace: None, id: ElementId(11) },
|
||||||
CreateText { value: "you're great!" },
|
CreateTextNode { value: "you're great!" },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
AppendChildren { m: 4 },
|
AppendChildren { m: 4 },
|
||||||
AppendChildren { m: 2 },
|
AppendChildren { m: 2 },
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ macro_rules! impl_event {
|
|||||||
$(
|
$(
|
||||||
$( #[$attr] )*
|
$( #[$attr] )*
|
||||||
pub fn $name<'a>(_cx: &'a ::dioxus_core::ScopeState, _f: impl FnMut(::dioxus_core::UiEvent<$data>) + 'a) -> ::dioxus_core::Attribute<'a> {
|
pub fn $name<'a>(_cx: &'a ::dioxus_core::ScopeState, _f: impl FnMut(::dioxus_core::UiEvent<$data>) + 'a) -> ::dioxus_core::Attribute<'a> {
|
||||||
todo!()
|
::dioxus_core::Attribute {
|
||||||
|
name: stringify!($name),
|
||||||
|
value: ::dioxus_core::AttributeValue::new_listener(_cx, _f),
|
||||||
|
namespace: None,
|
||||||
|
mounted_element: Default::default(),
|
||||||
|
volatile: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -53,160 +53,110 @@ class ListenerMap {
|
|||||||
element.removeEventListener(event_name, handler);
|
element.removeEventListener(event_name, handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeAllNonBubbling(element) {
|
||||||
|
const id = element.getAttribute("data-dioxus-id");
|
||||||
|
delete this.local[id];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Interpreter {
|
export class Interpreter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.lastNode = root;
|
this.stack = [root];
|
||||||
this.listeners = new ListenerMap(root);
|
this.listeners = new ListenerMap(root);
|
||||||
this.handlers = {};
|
this.handlers = {};
|
||||||
|
this.lastNodeWasText = false;
|
||||||
this.nodes = [root];
|
this.nodes = [root];
|
||||||
this.parents = [];
|
this.templates = {};
|
||||||
}
|
}
|
||||||
checkAppendParent() {
|
top() {
|
||||||
if (this.parents.length > 0) {
|
return this.stack[this.stack.length - 1];
|
||||||
const lastParent = this.parents[this.parents.length - 1];
|
}
|
||||||
lastParent[1]--;
|
pop() {
|
||||||
if (lastParent[1] === 0) {
|
return this.stack.pop();
|
||||||
this.parents.pop();
|
}
|
||||||
}
|
SetNode(id, node) {
|
||||||
lastParent[0].appendChild(this.lastNode);
|
this.nodes[id] = node;
|
||||||
|
}
|
||||||
|
PushRoot(root) {
|
||||||
|
const node = this.nodes[root];
|
||||||
|
this.stack.push(node);
|
||||||
|
}
|
||||||
|
PopRoot() {
|
||||||
|
this.stack.pop();
|
||||||
|
}
|
||||||
|
AppendChildren(many) {
|
||||||
|
let root = this.stack[this.stack.length - (1 + many)];
|
||||||
|
let to_add = this.stack.splice(this.stack.length - many);
|
||||||
|
for (let i = 0; i < many; i++) {
|
||||||
|
root.appendChild(to_add[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AppendChildren(root, children) {
|
ReplaceWith(root_id, m) {
|
||||||
let node;
|
let root = this.nodes[root_id];
|
||||||
if (root == null) {
|
let els = this.stack.splice(this.stack.length - m);
|
||||||
node = this.lastNode;
|
if (is_element_node(root.nodeType)) {
|
||||||
} else {
|
this.listeners.removeAllNonBubbling(root);
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
node.appendChild(this.nodes[children[i]]);
|
|
||||||
}
|
}
|
||||||
|
root.replaceWith(...els);
|
||||||
}
|
}
|
||||||
ReplaceWith(root, nodes) {
|
InsertAfter(root, n) {
|
||||||
let node;
|
let old = this.nodes[root];
|
||||||
if (root == null) {
|
let new_nodes = this.stack.splice(this.stack.length - n);
|
||||||
node = this.lastNode;
|
old.after(...new_nodes);
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
let els = [];
|
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
|
||||||
els.push(this.nodes[nodes[i]])
|
|
||||||
}
|
|
||||||
node.replaceWith(...els);
|
|
||||||
}
|
}
|
||||||
InsertAfter(root, nodes) {
|
InsertBefore(root, n) {
|
||||||
let node;
|
let old = this.nodes[root];
|
||||||
if (root == null) {
|
let new_nodes = this.stack.splice(this.stack.length - n);
|
||||||
node = this.lastNode;
|
old.before(...new_nodes);
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
let els = [];
|
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
|
||||||
els.push(this.nodes[nodes[i]])
|
|
||||||
}
|
|
||||||
node.after(...els);
|
|
||||||
}
|
|
||||||
InsertBefore(root, nodes) {
|
|
||||||
let node;
|
|
||||||
if (root == null) {
|
|
||||||
node = this.lastNode;
|
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
let els = [];
|
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
|
||||||
els.push(this.nodes[nodes[i]])
|
|
||||||
}
|
|
||||||
node.before(...els);
|
|
||||||
}
|
}
|
||||||
Remove(root) {
|
Remove(root) {
|
||||||
let node;
|
let node = this.nodes[root];
|
||||||
if (root == null) {
|
|
||||||
node = this.lastNode;
|
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
if (node !== undefined) {
|
if (node !== undefined) {
|
||||||
|
if (is_element_node(node)) {
|
||||||
|
this.listeners.removeAllNonBubbling(node);
|
||||||
|
}
|
||||||
node.remove();
|
node.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CreateTextNode(text, root) {
|
CreateTextNode(text, root) {
|
||||||
this.lastNode = document.createTextNode(text);
|
const node = document.createTextNode(text);
|
||||||
this.checkAppendParent();
|
this.nodes[root] = node;
|
||||||
if (root != null) {
|
this.stack.push(node);
|
||||||
this.nodes[root] = this.lastNode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CreateElement(tag, root, children) {
|
CreateElement(tag, root) {
|
||||||
this.lastNode = document.createElement(tag);
|
const el = document.createElement(tag);
|
||||||
this.checkAppendParent();
|
this.nodes[root] = el;
|
||||||
if (root != null) {
|
this.stack.push(el);
|
||||||
this.nodes[root] = this.lastNode;
|
|
||||||
}
|
|
||||||
if (children > 0) {
|
|
||||||
this.parents.push([this.lastNode, children]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CreateElementNs(tag, root, ns, children) {
|
CreateElementNs(tag, root, ns) {
|
||||||
this.lastNode = document.createElementNS(ns, tag);
|
let el = document.createElementNS(ns, tag);
|
||||||
this.checkAppendParent();
|
this.stack.push(el);
|
||||||
if (root != null) {
|
this.nodes[root] = el;
|
||||||
this.nodes[root] = this.lastNode;
|
|
||||||
}
|
|
||||||
if (children > 0) {
|
|
||||||
this.parents.push([this.lastNode, children]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CreatePlaceholder(root) {
|
CreatePlaceholder(root) {
|
||||||
this.lastNode = document.createElement("pre");
|
let el = document.createElement("pre");
|
||||||
this.lastNode.hidden = true;
|
el.hidden = true;
|
||||||
this.checkAppendParent();
|
this.stack.push(el);
|
||||||
if (root != null) {
|
this.nodes[root] = el;
|
||||||
this.nodes[root] = this.lastNode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NewEventListener(event_name, root, handler, bubbles) {
|
NewEventListener(event_name, root, handler, bubbles) {
|
||||||
let node;
|
const element = this.nodes[root];
|
||||||
if (root == null) {
|
element.setAttribute("data-dioxus-id", `${root}`);
|
||||||
node = this.lastNode;
|
this.listeners.create(event_name, element, handler, bubbles);
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
node.setAttribute("data-dioxus-id", `${root}`);
|
|
||||||
this.listeners.create(event_name, node, handler, bubbles);
|
|
||||||
}
|
}
|
||||||
RemoveEventListener(root, event_name, bubbles) {
|
RemoveEventListener(root, event_name, bubbles) {
|
||||||
let node;
|
const element = this.nodes[root];
|
||||||
if (root == null) {
|
element.removeAttribute(`data-dioxus-id`);
|
||||||
node = this.lastNode;
|
this.listeners.remove(element, event_name, bubbles);
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
node.removeAttribute(`data-dioxus-id`);
|
|
||||||
this.listeners.remove(node, event_name, bubbles);
|
|
||||||
}
|
}
|
||||||
SetText(root, text) {
|
SetText(root, text) {
|
||||||
let node;
|
this.nodes[root].textContent = text;
|
||||||
if (root == null) {
|
|
||||||
node = this.lastNode;
|
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
node.data = text;
|
|
||||||
}
|
}
|
||||||
SetAttribute(root, field, value, ns) {
|
SetAttribute(root, field, value, ns) {
|
||||||
const name = field;
|
const name = field;
|
||||||
let node;
|
const node = this.nodes[root];
|
||||||
if (root == null) {
|
|
||||||
node = this.lastNode;
|
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
if (ns === "style") {
|
if (ns === "style") {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
node.style[name] = value;
|
node.style[name] = value;
|
||||||
@@ -240,12 +190,7 @@ export class Interpreter {
|
|||||||
}
|
}
|
||||||
RemoveAttribute(root, field, ns) {
|
RemoveAttribute(root, field, ns) {
|
||||||
const name = field;
|
const name = field;
|
||||||
let node;
|
const node = this.nodes[root];
|
||||||
if (root == null) {
|
|
||||||
node = this.lastNode;
|
|
||||||
} else {
|
|
||||||
node = this.nodes[root];
|
|
||||||
}
|
|
||||||
if (ns == "style") {
|
if (ns == "style") {
|
||||||
node.style.removeProperty(name);
|
node.style.removeProperty(name);
|
||||||
} else if (ns !== null || ns !== undefined) {
|
} else if (ns !== null || ns !== undefined) {
|
||||||
@@ -262,87 +207,95 @@ export class Interpreter {
|
|||||||
node.removeAttribute(name);
|
node.removeAttribute(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CloneNode(old, new_id) {
|
|
||||||
let node;
|
|
||||||
if (old === null) {
|
|
||||||
node = this.lastNode;
|
|
||||||
} else {
|
|
||||||
node = this.nodes[old];
|
|
||||||
}
|
|
||||||
this.nodes[new_id] = node.cloneNode(true);
|
|
||||||
}
|
|
||||||
CloneNodeChildren(old, new_ids) {
|
|
||||||
let node;
|
|
||||||
if (old === null) {
|
|
||||||
node = this.lastNode;
|
|
||||||
} else {
|
|
||||||
node = this.nodes[old];
|
|
||||||
}
|
|
||||||
const old_node = node.cloneNode(true);
|
|
||||||
let i = 0;
|
|
||||||
for (let node = old_node.firstChild; i < new_ids.length; node = node.nextSibling) {
|
|
||||||
this.nodes[new_ids[i++]] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FirstChild() {
|
|
||||||
this.lastNode = this.lastNode.firstChild;
|
|
||||||
}
|
|
||||||
NextSibling() {
|
|
||||||
this.lastNode = this.lastNode.nextSibling;
|
|
||||||
}
|
|
||||||
ParentNode() {
|
|
||||||
this.lastNode = this.lastNode.parentNode;
|
|
||||||
}
|
|
||||||
StoreWithId(id) {
|
|
||||||
this.nodes[id] = this.lastNode;
|
|
||||||
}
|
|
||||||
SetLastNode(root) {
|
|
||||||
this.lastNode = this.nodes[root];
|
|
||||||
}
|
|
||||||
handleEdits(edits) {
|
handleEdits(edits) {
|
||||||
|
console.log("handling edits", edits);
|
||||||
|
|
||||||
for (let edit of edits) {
|
for (let edit of edits) {
|
||||||
this.handleEdit(edit);
|
this.handleEdit(edit);
|
||||||
|
console.log(this.stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(this.stack);
|
||||||
|
}
|
||||||
|
AssignId(path, id) {
|
||||||
|
this.nodes[id] = this.LoadChild(path);
|
||||||
|
}
|
||||||
|
LoadChild(path) {
|
||||||
|
// iterate through each number and get that child
|
||||||
|
let node = this.stack[this.stack.length - 1];
|
||||||
|
|
||||||
|
for (let i = 0; i < path.length; i++) {
|
||||||
|
node = node.childNodes[path[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
HydrateText(path, value, id) {
|
||||||
|
let node = this.LoadChild(path);
|
||||||
|
node.textContent = value;
|
||||||
|
this.nodes[id] = node;
|
||||||
|
}
|
||||||
|
LoadTemplate(name, index) {
|
||||||
|
console.log("loading template", name, index);
|
||||||
|
let node = this.templates[name][index].cloneNode(true);
|
||||||
|
this.stack.push(node);
|
||||||
|
}
|
||||||
|
SaveTemplate(name, m) {
|
||||||
|
this.templates[name] = this.stack.splice(-m);
|
||||||
}
|
}
|
||||||
handleEdit(edit) {
|
handleEdit(edit) {
|
||||||
|
console.log("handling edit", edit);
|
||||||
switch (edit.type) {
|
switch (edit.type) {
|
||||||
case "PushRoot":
|
|
||||||
this.PushRoot(edit.root);
|
|
||||||
break;
|
|
||||||
case "AppendChildren":
|
case "AppendChildren":
|
||||||
this.AppendChildren(edit.root, edit.children);
|
this.AppendChildren(edit.m);
|
||||||
break;
|
break;
|
||||||
case "ReplaceWith":
|
case "AssignId":
|
||||||
this.ReplaceWith(edit.root, edit.nodes);
|
this.AssignId(edit.path, edit.id);
|
||||||
break;
|
|
||||||
case "InsertAfter":
|
|
||||||
this.InsertAfter(edit.root, edit.nodes);
|
|
||||||
break;
|
|
||||||
case "InsertBefore":
|
|
||||||
this.InsertBefore(edit.root, edit.nodes);
|
|
||||||
break;
|
|
||||||
case "Remove":
|
|
||||||
this.Remove(edit.root);
|
|
||||||
break;
|
|
||||||
case "CreateTextNode":
|
|
||||||
this.CreateTextNode(edit.text, edit.root);
|
|
||||||
break;
|
break;
|
||||||
case "CreateElement":
|
case "CreateElement":
|
||||||
this.CreateElement(edit.tag, edit.root, edit.children);
|
this.CreateElement(edit.name, edit.id);
|
||||||
break;
|
|
||||||
case "CreateElementNs":
|
|
||||||
this.CreateElementNs(edit.tag, edit.root, edit.ns, edit.children);
|
|
||||||
break;
|
break;
|
||||||
case "CreatePlaceholder":
|
case "CreatePlaceholder":
|
||||||
this.CreatePlaceholder(edit.root);
|
this.CreatePlaceholder(edit.id);
|
||||||
|
break;
|
||||||
|
case "CreateTextNode":
|
||||||
|
this.CreateTextNode(edit.value);
|
||||||
|
break;
|
||||||
|
case "HydrateText":
|
||||||
|
this.HydrateText(edit.path, edit.value, edit.id);
|
||||||
|
break;
|
||||||
|
case "PushRoot":
|
||||||
|
this.PushRoot(edit.id);
|
||||||
|
break;
|
||||||
|
case "ReplaceWith":
|
||||||
|
this.ReplaceWith(edit.id, edit.m);
|
||||||
|
break;
|
||||||
|
case "InsertAfter":
|
||||||
|
this.InsertAfter(edit.id, edit.n);
|
||||||
|
break;
|
||||||
|
case "InsertBefore":
|
||||||
|
this.InsertBefore(edit.id, edit.n);
|
||||||
|
break;
|
||||||
|
case "Remove":
|
||||||
|
this.Remove(edit.id);
|
||||||
|
break;
|
||||||
|
case "LoadTemplate":
|
||||||
|
this.LoadTemplate(edit.name, edit.index);
|
||||||
|
break;
|
||||||
|
case "SaveTemplate":
|
||||||
|
this.SaveTemplate(edit.name, edit.m);
|
||||||
|
break;
|
||||||
|
case "CreateElementNs":
|
||||||
|
this.CreateElementNs(edit.tag, edit.id, edit.ns);
|
||||||
break;
|
break;
|
||||||
case "RemoveEventListener":
|
case "RemoveEventListener":
|
||||||
this.RemoveEventListener(edit.root, edit.event_name);
|
this.RemoveEventListener(edit.id, edit.event_name);
|
||||||
break;
|
break;
|
||||||
case "NewEventListener":
|
case "NewEventListener":
|
||||||
// this handler is only provided on desktop implementations since this
|
// this handler is only provided on desktop implementations since this
|
||||||
// method is not used by the web implementation
|
// method is not used by the web implementation
|
||||||
let handler = (event) => {
|
let handler = (event) => {
|
||||||
|
|
||||||
let target = event.target;
|
let target = event.target;
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
let realId = target.getAttribute(`data-dioxus-id`);
|
let realId = target.getAttribute(`data-dioxus-id`);
|
||||||
@@ -421,48 +374,26 @@ export class Interpreter {
|
|||||||
if (realId === null) {
|
if (realId === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
realId = parseInt(realId);
|
|
||||||
window.ipc.postMessage(
|
window.ipc.postMessage(
|
||||||
serializeIpcMessage("user_event", {
|
serializeIpcMessage("user_event", {
|
||||||
event: edit.event_name,
|
event: edit.event_name,
|
||||||
mounted_dom_id: realId,
|
mounted_dom_id: parseInt(realId),
|
||||||
contents: contents,
|
contents: contents,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.NewEventListener(edit.event_name, edit.root, handler, event_bubbles(edit.event_name));
|
this.NewEventListener(edit.event_name, edit.id, handler, event_bubbles(edit.event_name));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "SetText":
|
case "SetText":
|
||||||
this.SetText(edit.root, edit.text);
|
this.SetText(edit.id, edit.text);
|
||||||
break;
|
break;
|
||||||
case "SetAttribute":
|
case "SetAttribute":
|
||||||
this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
|
this.SetAttribute(edit.id, edit.field, edit.value, edit.ns);
|
||||||
break;
|
break;
|
||||||
case "RemoveAttribute":
|
case "RemoveAttribute":
|
||||||
this.RemoveAttribute(edit.root, edit.name, edit.ns);
|
this.RemoveAttribute(edit.id, edit.name, edit.ns);
|
||||||
break;
|
|
||||||
case "CloneNode":
|
|
||||||
this.CloneNode(edit.id, edit.new_id);
|
|
||||||
break;
|
|
||||||
case "CloneNodeChildren":
|
|
||||||
this.CloneNodeChildren(edit.id, edit.new_ids);
|
|
||||||
break;
|
|
||||||
case "FirstChild":
|
|
||||||
this.FirstChild();
|
|
||||||
break;
|
|
||||||
case "NextSibling":
|
|
||||||
this.NextSibling();
|
|
||||||
break;
|
|
||||||
case "ParentNode":
|
|
||||||
this.ParentNode();
|
|
||||||
break;
|
|
||||||
case "StoreWithId":
|
|
||||||
this.StoreWithId(BigInt(edit.id));
|
|
||||||
break;
|
|
||||||
case "SetLastNode":
|
|
||||||
this.SetLastNode(BigInt(edit.id));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -941,4 +872,4 @@ function event_bubbles(event) {
|
|||||||
case "toggle":
|
case "toggle":
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,944 @@
|
|||||||
|
export function main() {
|
||||||
|
let root = window.document.getElementById("main");
|
||||||
|
if (root != null) {
|
||||||
|
window.interpreter = new Interpreter(root);
|
||||||
|
window.ipc.postMessage(serializeIpcMessage("initialize"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListenerMap {
|
||||||
|
constructor(root) {
|
||||||
|
// bubbling events can listen at the root element
|
||||||
|
this.global = {};
|
||||||
|
// non bubbling events listen at the element the listener was created at
|
||||||
|
this.local = {};
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(event_name, element, handler, bubbles) {
|
||||||
|
if (bubbles) {
|
||||||
|
if (this.global[event_name] === undefined) {
|
||||||
|
this.global[event_name] = {};
|
||||||
|
this.global[event_name].active = 1;
|
||||||
|
this.global[event_name].callback = handler;
|
||||||
|
this.root.addEventListener(event_name, handler);
|
||||||
|
} else {
|
||||||
|
this.global[event_name].active++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const id = element.getAttribute("data-dioxus-id");
|
||||||
|
if (!this.local[id]) {
|
||||||
|
this.local[id] = {};
|
||||||
|
}
|
||||||
|
this.local[id][event_name] = handler;
|
||||||
|
element.addEventListener(event_name, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(element, event_name, bubbles) {
|
||||||
|
if (bubbles) {
|
||||||
|
this.global[event_name].active--;
|
||||||
|
if (this.global[event_name].active === 0) {
|
||||||
|
this.root.removeEventListener(event_name, this.global[event_name].callback);
|
||||||
|
delete this.global[event_name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const id = element.getAttribute("data-dioxus-id");
|
||||||
|
delete this.local[id][event_name];
|
||||||
|
if (this.local[id].length === 0) {
|
||||||
|
delete this.local[id];
|
||||||
|
}
|
||||||
|
element.removeEventListener(event_name, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Interpreter {
|
||||||
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
|
this.lastNode = root;
|
||||||
|
this.listeners = new ListenerMap(root);
|
||||||
|
this.handlers = {};
|
||||||
|
this.nodes = [root];
|
||||||
|
this.parents = [];
|
||||||
|
}
|
||||||
|
checkAppendParent() {
|
||||||
|
if (this.parents.length > 0) {
|
||||||
|
const lastParent = this.parents[this.parents.length - 1];
|
||||||
|
lastParent[1]--;
|
||||||
|
if (lastParent[1] === 0) {
|
||||||
|
this.parents.pop();
|
||||||
|
}
|
||||||
|
lastParent[0].appendChild(this.lastNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppendChildren(root, children) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
node.appendChild(this.nodes[children[i]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReplaceWith(root, nodes) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
let els = [];
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
els.push(this.nodes[nodes[i]])
|
||||||
|
}
|
||||||
|
node.replaceWith(...els);
|
||||||
|
}
|
||||||
|
InsertAfter(root, nodes) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
let els = [];
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
els.push(this.nodes[nodes[i]])
|
||||||
|
}
|
||||||
|
node.after(...els);
|
||||||
|
}
|
||||||
|
InsertBefore(root, nodes) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
let els = [];
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
els.push(this.nodes[nodes[i]])
|
||||||
|
}
|
||||||
|
node.before(...els);
|
||||||
|
}
|
||||||
|
Remove(root) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
if (node !== undefined) {
|
||||||
|
node.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CreateTextNode(text, root) {
|
||||||
|
this.lastNode = document.createTextNode(text);
|
||||||
|
this.checkAppendParent();
|
||||||
|
if (root != null) {
|
||||||
|
this.nodes[root] = this.lastNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CreateElement(tag, root, children) {
|
||||||
|
this.lastNode = document.createElement(tag);
|
||||||
|
this.checkAppendParent();
|
||||||
|
if (root != null) {
|
||||||
|
this.nodes[root] = this.lastNode;
|
||||||
|
}
|
||||||
|
if (children > 0) {
|
||||||
|
this.parents.push([this.lastNode, children]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CreateElementNs(tag, root, ns, children) {
|
||||||
|
this.lastNode = document.createElementNS(ns, tag);
|
||||||
|
this.checkAppendParent();
|
||||||
|
if (root != null) {
|
||||||
|
this.nodes[root] = this.lastNode;
|
||||||
|
}
|
||||||
|
if (children > 0) {
|
||||||
|
this.parents.push([this.lastNode, children]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CreatePlaceholder(root) {
|
||||||
|
this.lastNode = document.createElement("pre");
|
||||||
|
this.lastNode.hidden = true;
|
||||||
|
this.checkAppendParent();
|
||||||
|
if (root != null) {
|
||||||
|
this.nodes[root] = this.lastNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NewEventListener(event_name, root, handler, bubbles) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
node.setAttribute("data-dioxus-id", `${root}`);
|
||||||
|
this.listeners.create(event_name, node, handler, bubbles);
|
||||||
|
}
|
||||||
|
RemoveEventListener(root, event_name, bubbles) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
node.removeAttribute(`data-dioxus-id`);
|
||||||
|
this.listeners.remove(node, event_name, bubbles);
|
||||||
|
}
|
||||||
|
SetText(root, text) {
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
node.data = text;
|
||||||
|
}
|
||||||
|
SetAttribute(root, field, value, ns) {
|
||||||
|
const name = field;
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
if (ns === "style") {
|
||||||
|
// @ts-ignore
|
||||||
|
node.style[name] = value;
|
||||||
|
} else if (ns != null || ns != undefined) {
|
||||||
|
node.setAttributeNS(ns, name, value);
|
||||||
|
} else {
|
||||||
|
switch (name) {
|
||||||
|
case "value":
|
||||||
|
if (value !== node.value) {
|
||||||
|
node.value = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "checked":
|
||||||
|
node.checked = value === "true";
|
||||||
|
break;
|
||||||
|
case "selected":
|
||||||
|
node.selected = value === "true";
|
||||||
|
break;
|
||||||
|
case "dangerous_inner_html":
|
||||||
|
node.innerHTML = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
|
||||||
|
if (value === "false" && bool_attrs.hasOwnProperty(name)) {
|
||||||
|
node.removeAttribute(name);
|
||||||
|
} else {
|
||||||
|
node.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RemoveAttribute(root, field, ns) {
|
||||||
|
const name = field;
|
||||||
|
let node;
|
||||||
|
if (root == null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[root];
|
||||||
|
}
|
||||||
|
if (ns == "style") {
|
||||||
|
node.style.removeProperty(name);
|
||||||
|
} else if (ns !== null || ns !== undefined) {
|
||||||
|
node.removeAttributeNS(ns, name);
|
||||||
|
} else if (name === "value") {
|
||||||
|
node.value = "";
|
||||||
|
} else if (name === "checked") {
|
||||||
|
node.checked = false;
|
||||||
|
} else if (name === "selected") {
|
||||||
|
node.selected = false;
|
||||||
|
} else if (name === "dangerous_inner_html") {
|
||||||
|
node.innerHTML = "";
|
||||||
|
} else {
|
||||||
|
node.removeAttribute(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloneNode(old, new_id) {
|
||||||
|
let node;
|
||||||
|
if (old === null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[old];
|
||||||
|
}
|
||||||
|
this.nodes[new_id] = node.cloneNode(true);
|
||||||
|
}
|
||||||
|
CloneNodeChildren(old, new_ids) {
|
||||||
|
let node;
|
||||||
|
if (old === null) {
|
||||||
|
node = this.lastNode;
|
||||||
|
} else {
|
||||||
|
node = this.nodes[old];
|
||||||
|
}
|
||||||
|
const old_node = node.cloneNode(true);
|
||||||
|
let i = 0;
|
||||||
|
for (let node = old_node.firstChild; i < new_ids.length; node = node.nextSibling) {
|
||||||
|
this.nodes[new_ids[i++]] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FirstChild() {
|
||||||
|
this.lastNode = this.lastNode.firstChild;
|
||||||
|
}
|
||||||
|
NextSibling() {
|
||||||
|
this.lastNode = this.lastNode.nextSibling;
|
||||||
|
}
|
||||||
|
ParentNode() {
|
||||||
|
this.lastNode = this.lastNode.parentNode;
|
||||||
|
}
|
||||||
|
StoreWithId(id) {
|
||||||
|
this.nodes[id] = this.lastNode;
|
||||||
|
}
|
||||||
|
SetLastNode(root) {
|
||||||
|
this.lastNode = this.nodes[root];
|
||||||
|
}
|
||||||
|
handleEdits(edits) {
|
||||||
|
for (let edit of edits) {
|
||||||
|
this.handleEdit(edit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleEdit(edit) {
|
||||||
|
switch (edit.type) {
|
||||||
|
case "PushRoot":
|
||||||
|
this.PushRoot(edit.root);
|
||||||
|
break;
|
||||||
|
case "AppendChildren":
|
||||||
|
this.AppendChildren(edit.root, edit.children);
|
||||||
|
break;
|
||||||
|
case "ReplaceWith":
|
||||||
|
this.ReplaceWith(edit.root, edit.nodes);
|
||||||
|
break;
|
||||||
|
case "InsertAfter":
|
||||||
|
this.InsertAfter(edit.root, edit.nodes);
|
||||||
|
break;
|
||||||
|
case "InsertBefore":
|
||||||
|
this.InsertBefore(edit.root, edit.nodes);
|
||||||
|
break;
|
||||||
|
case "Remove":
|
||||||
|
this.Remove(edit.root);
|
||||||
|
break;
|
||||||
|
case "CreateTextNode":
|
||||||
|
this.CreateTextNode(edit.text, edit.root);
|
||||||
|
break;
|
||||||
|
case "CreateElement":
|
||||||
|
this.CreateElement(edit.tag, edit.root, edit.children);
|
||||||
|
break;
|
||||||
|
case "CreateElementNs":
|
||||||
|
this.CreateElementNs(edit.tag, edit.root, edit.ns, edit.children);
|
||||||
|
break;
|
||||||
|
case "CreatePlaceholder":
|
||||||
|
this.CreatePlaceholder(edit.root);
|
||||||
|
break;
|
||||||
|
case "RemoveEventListener":
|
||||||
|
this.RemoveEventListener(edit.root, edit.event_name);
|
||||||
|
break;
|
||||||
|
case "NewEventListener":
|
||||||
|
// this handler is only provided on desktop implementations since this
|
||||||
|
// method is not used by the web implementation
|
||||||
|
let handler = (event) => {
|
||||||
|
let target = event.target;
|
||||||
|
if (target != null) {
|
||||||
|
let realId = target.getAttribute(`data-dioxus-id`);
|
||||||
|
let shouldPreventDefault = target.getAttribute(
|
||||||
|
`dioxus-prevent-default`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (event.type === "click") {
|
||||||
|
// todo call prevent default if it's the right type of event
|
||||||
|
if (shouldPreventDefault !== `onclick`) {
|
||||||
|
if (target.tagName === "A") {
|
||||||
|
event.preventDefault();
|
||||||
|
const href = target.getAttribute("href");
|
||||||
|
if (href !== "" && href !== null && href !== undefined) {
|
||||||
|
window.ipc.postMessage(
|
||||||
|
serializeIpcMessage("browser_open", { href })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// also prevent buttons from submitting
|
||||||
|
if (target.tagName === "BUTTON" && event.type == "submit") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// walk the tree to find the real element
|
||||||
|
while (realId == null) {
|
||||||
|
// we've reached the root we don't want to send an event
|
||||||
|
if (target.parentElement === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target = target.parentElement;
|
||||||
|
realId = target.getAttribute(`data-dioxus-id`);
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldPreventDefault = target.getAttribute(
|
||||||
|
`dioxus-prevent-default`
|
||||||
|
);
|
||||||
|
|
||||||
|
let contents = serialize_event(event);
|
||||||
|
|
||||||
|
if (shouldPreventDefault === `on${event.type}`) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "submit") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
target.tagName === "FORM" &&
|
||||||
|
(event.type === "submit" || event.type === "input")
|
||||||
|
) {
|
||||||
|
for (let x = 0; x < target.elements.length; x++) {
|
||||||
|
let element = target.elements[x];
|
||||||
|
let name = element.getAttribute("name");
|
||||||
|
if (name != null) {
|
||||||
|
if (element.getAttribute("type") === "checkbox") {
|
||||||
|
// @ts-ignore
|
||||||
|
contents.values[name] = element.checked ? "true" : "false";
|
||||||
|
} else if (element.getAttribute("type") === "radio") {
|
||||||
|
if (element.checked) {
|
||||||
|
contents.values[name] = element.value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
contents.values[name] =
|
||||||
|
element.value ?? element.textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realId === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
realId = parseInt(realId);
|
||||||
|
window.ipc.postMessage(
|
||||||
|
serializeIpcMessage("user_event", {
|
||||||
|
event: edit.event_name,
|
||||||
|
mounted_dom_id: realId,
|
||||||
|
contents: contents,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.NewEventListener(edit.event_name, edit.root, handler, event_bubbles(edit.event_name));
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "SetText":
|
||||||
|
this.SetText(edit.root, edit.text);
|
||||||
|
break;
|
||||||
|
case "SetAttribute":
|
||||||
|
this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
|
||||||
|
break;
|
||||||
|
case "RemoveAttribute":
|
||||||
|
this.RemoveAttribute(edit.root, edit.name, edit.ns);
|
||||||
|
break;
|
||||||
|
case "CloneNode":
|
||||||
|
this.CloneNode(edit.id, edit.new_id);
|
||||||
|
break;
|
||||||
|
case "CloneNodeChildren":
|
||||||
|
this.CloneNodeChildren(edit.id, edit.new_ids);
|
||||||
|
break;
|
||||||
|
case "FirstChild":
|
||||||
|
this.FirstChild();
|
||||||
|
break;
|
||||||
|
case "NextSibling":
|
||||||
|
this.NextSibling();
|
||||||
|
break;
|
||||||
|
case "ParentNode":
|
||||||
|
this.ParentNode();
|
||||||
|
break;
|
||||||
|
case "StoreWithId":
|
||||||
|
this.StoreWithId(BigInt(edit.id));
|
||||||
|
break;
|
||||||
|
case "SetLastNode":
|
||||||
|
this.SetLastNode(BigInt(edit.id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serialize_event(event) {
|
||||||
|
switch (event.type) {
|
||||||
|
case "copy":
|
||||||
|
case "cut":
|
||||||
|
case "past": {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case "compositionend":
|
||||||
|
case "compositionstart":
|
||||||
|
case "compositionupdate": {
|
||||||
|
let { data } = event;
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "keydown":
|
||||||
|
case "keypress":
|
||||||
|
case "keyup": {
|
||||||
|
let {
|
||||||
|
charCode,
|
||||||
|
key,
|
||||||
|
altKey,
|
||||||
|
ctrlKey,
|
||||||
|
metaKey,
|
||||||
|
keyCode,
|
||||||
|
shiftKey,
|
||||||
|
location,
|
||||||
|
repeat,
|
||||||
|
which,
|
||||||
|
code,
|
||||||
|
} = event;
|
||||||
|
return {
|
||||||
|
char_code: charCode,
|
||||||
|
key: key,
|
||||||
|
alt_key: altKey,
|
||||||
|
ctrl_key: ctrlKey,
|
||||||
|
meta_key: metaKey,
|
||||||
|
key_code: keyCode,
|
||||||
|
shift_key: shiftKey,
|
||||||
|
location: location,
|
||||||
|
repeat: repeat,
|
||||||
|
which: which,
|
||||||
|
code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "focus":
|
||||||
|
case "blur": {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case "change": {
|
||||||
|
let target = event.target;
|
||||||
|
let value;
|
||||||
|
if (target.type === "checkbox" || target.type === "radio") {
|
||||||
|
value = target.checked ? "true" : "false";
|
||||||
|
} else {
|
||||||
|
value = target.value ?? target.textContent;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
value: value,
|
||||||
|
values: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "input":
|
||||||
|
case "invalid":
|
||||||
|
case "reset":
|
||||||
|
case "submit": {
|
||||||
|
let target = event.target;
|
||||||
|
let value = target.value ?? target.textContent;
|
||||||
|
|
||||||
|
if (target.type === "checkbox") {
|
||||||
|
value = target.checked ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: value,
|
||||||
|
values: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "click":
|
||||||
|
case "contextmenu":
|
||||||
|
case "doubleclick":
|
||||||
|
case "dblclick":
|
||||||
|
case "drag":
|
||||||
|
case "dragend":
|
||||||
|
case "dragenter":
|
||||||
|
case "dragexit":
|
||||||
|
case "dragleave":
|
||||||
|
case "dragover":
|
||||||
|
case "dragstart":
|
||||||
|
case "drop":
|
||||||
|
case "mousedown":
|
||||||
|
case "mouseenter":
|
||||||
|
case "mouseleave":
|
||||||
|
case "mousemove":
|
||||||
|
case "mouseout":
|
||||||
|
case "mouseover":
|
||||||
|
case "mouseup": {
|
||||||
|
const {
|
||||||
|
altKey,
|
||||||
|
button,
|
||||||
|
buttons,
|
||||||
|
clientX,
|
||||||
|
clientY,
|
||||||
|
ctrlKey,
|
||||||
|
metaKey,
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
pageX,
|
||||||
|
pageY,
|
||||||
|
screenX,
|
||||||
|
screenY,
|
||||||
|
shiftKey,
|
||||||
|
} = event;
|
||||||
|
return {
|
||||||
|
alt_key: altKey,
|
||||||
|
button: button,
|
||||||
|
buttons: buttons,
|
||||||
|
client_x: clientX,
|
||||||
|
client_y: clientY,
|
||||||
|
ctrl_key: ctrlKey,
|
||||||
|
meta_key: metaKey,
|
||||||
|
offset_x: offsetX,
|
||||||
|
offset_y: offsetY,
|
||||||
|
page_x: pageX,
|
||||||
|
page_y: pageY,
|
||||||
|
screen_x: screenX,
|
||||||
|
screen_y: screenY,
|
||||||
|
shift_key: shiftKey,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "pointerdown":
|
||||||
|
case "pointermove":
|
||||||
|
case "pointerup":
|
||||||
|
case "pointercancel":
|
||||||
|
case "gotpointercapture":
|
||||||
|
case "lostpointercapture":
|
||||||
|
case "pointerenter":
|
||||||
|
case "pointerleave":
|
||||||
|
case "pointerover":
|
||||||
|
case "pointerout": {
|
||||||
|
const {
|
||||||
|
altKey,
|
||||||
|
button,
|
||||||
|
buttons,
|
||||||
|
clientX,
|
||||||
|
clientY,
|
||||||
|
ctrlKey,
|
||||||
|
metaKey,
|
||||||
|
pageX,
|
||||||
|
pageY,
|
||||||
|
screenX,
|
||||||
|
screenY,
|
||||||
|
shiftKey,
|
||||||
|
pointerId,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
pressure,
|
||||||
|
tangentialPressure,
|
||||||
|
tiltX,
|
||||||
|
tiltY,
|
||||||
|
twist,
|
||||||
|
pointerType,
|
||||||
|
isPrimary,
|
||||||
|
} = event;
|
||||||
|
return {
|
||||||
|
alt_key: altKey,
|
||||||
|
button: button,
|
||||||
|
buttons: buttons,
|
||||||
|
client_x: clientX,
|
||||||
|
client_y: clientY,
|
||||||
|
ctrl_key: ctrlKey,
|
||||||
|
meta_key: metaKey,
|
||||||
|
page_x: pageX,
|
||||||
|
page_y: pageY,
|
||||||
|
screen_x: screenX,
|
||||||
|
screen_y: screenY,
|
||||||
|
shift_key: shiftKey,
|
||||||
|
pointer_id: pointerId,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
pressure: pressure,
|
||||||
|
tangential_pressure: tangentialPressure,
|
||||||
|
tilt_x: tiltX,
|
||||||
|
tilt_y: tiltY,
|
||||||
|
twist: twist,
|
||||||
|
pointer_type: pointerType,
|
||||||
|
is_primary: isPrimary,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "select": {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case "touchcancel":
|
||||||
|
case "touchend":
|
||||||
|
case "touchmove":
|
||||||
|
case "touchstart": {
|
||||||
|
const { altKey, ctrlKey, metaKey, shiftKey } = event;
|
||||||
|
return {
|
||||||
|
// changed_touches: event.changedTouches,
|
||||||
|
// target_touches: event.targetTouches,
|
||||||
|
// touches: event.touches,
|
||||||
|
alt_key: altKey,
|
||||||
|
ctrl_key: ctrlKey,
|
||||||
|
meta_key: metaKey,
|
||||||
|
shift_key: shiftKey,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "scroll": {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case "wheel": {
|
||||||
|
const { deltaX, deltaY, deltaZ, deltaMode } = event;
|
||||||
|
return {
|
||||||
|
delta_x: deltaX,
|
||||||
|
delta_y: deltaY,
|
||||||
|
delta_z: deltaZ,
|
||||||
|
delta_mode: deltaMode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "animationstart":
|
||||||
|
case "animationend":
|
||||||
|
case "animationiteration": {
|
||||||
|
const { animationName, elapsedTime, pseudoElement } = event;
|
||||||
|
return {
|
||||||
|
animation_name: animationName,
|
||||||
|
elapsed_time: elapsedTime,
|
||||||
|
pseudo_element: pseudoElement,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "transitionend": {
|
||||||
|
const { propertyName, elapsedTime, pseudoElement } = event;
|
||||||
|
return {
|
||||||
|
property_name: propertyName,
|
||||||
|
elapsed_time: elapsedTime,
|
||||||
|
pseudo_element: pseudoElement,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "abort":
|
||||||
|
case "canplay":
|
||||||
|
case "canplaythrough":
|
||||||
|
case "durationchange":
|
||||||
|
case "emptied":
|
||||||
|
case "encrypted":
|
||||||
|
case "ended":
|
||||||
|
case "error":
|
||||||
|
case "loadeddata":
|
||||||
|
case "loadedmetadata":
|
||||||
|
case "loadstart":
|
||||||
|
case "pause":
|
||||||
|
case "play":
|
||||||
|
case "playing":
|
||||||
|
case "progress":
|
||||||
|
case "ratechange":
|
||||||
|
case "seeked":
|
||||||
|
case "seeking":
|
||||||
|
case "stalled":
|
||||||
|
case "suspend":
|
||||||
|
case "timeupdate":
|
||||||
|
case "volumechange":
|
||||||
|
case "waiting": {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case "toggle": {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function serializeIpcMessage(method, params = {}) {
|
||||||
|
return JSON.stringify({ method, params });
|
||||||
|
}
|
||||||
|
const bool_attrs = {
|
||||||
|
allowfullscreen: true,
|
||||||
|
allowpaymentrequest: true,
|
||||||
|
async: true,
|
||||||
|
autofocus: true,
|
||||||
|
autoplay: true,
|
||||||
|
checked: true,
|
||||||
|
controls: true,
|
||||||
|
default: true,
|
||||||
|
defer: true,
|
||||||
|
disabled: true,
|
||||||
|
formnovalidate: true,
|
||||||
|
hidden: true,
|
||||||
|
ismap: true,
|
||||||
|
itemscope: true,
|
||||||
|
loop: true,
|
||||||
|
multiple: true,
|
||||||
|
muted: true,
|
||||||
|
nomodule: true,
|
||||||
|
novalidate: true,
|
||||||
|
open: true,
|
||||||
|
playsinline: true,
|
||||||
|
readonly: true,
|
||||||
|
required: true,
|
||||||
|
reversed: true,
|
||||||
|
selected: true,
|
||||||
|
truespeed: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
function is_element_node(node) {
|
||||||
|
return node.nodeType == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function event_bubbles(event) {
|
||||||
|
switch (event) {
|
||||||
|
case "copy":
|
||||||
|
return true;
|
||||||
|
case "cut":
|
||||||
|
return true;
|
||||||
|
case "paste":
|
||||||
|
return true;
|
||||||
|
case "compositionend":
|
||||||
|
return true;
|
||||||
|
case "compositionstart":
|
||||||
|
return true;
|
||||||
|
case "compositionupdate":
|
||||||
|
return true;
|
||||||
|
case "keydown":
|
||||||
|
return true;
|
||||||
|
case "keypress":
|
||||||
|
return true;
|
||||||
|
case "keyup":
|
||||||
|
return true;
|
||||||
|
case "focus":
|
||||||
|
return false;
|
||||||
|
case "focusout":
|
||||||
|
return true;
|
||||||
|
case "focusin":
|
||||||
|
return true;
|
||||||
|
case "blur":
|
||||||
|
return false;
|
||||||
|
case "change":
|
||||||
|
return true;
|
||||||
|
case "input":
|
||||||
|
return true;
|
||||||
|
case "invalid":
|
||||||
|
return true;
|
||||||
|
case "reset":
|
||||||
|
return true;
|
||||||
|
case "submit":
|
||||||
|
return true;
|
||||||
|
case "click":
|
||||||
|
return true;
|
||||||
|
case "contextmenu":
|
||||||
|
return true;
|
||||||
|
case "doubleclick":
|
||||||
|
return true;
|
||||||
|
case "dblclick":
|
||||||
|
return true;
|
||||||
|
case "drag":
|
||||||
|
return true;
|
||||||
|
case "dragend":
|
||||||
|
return true;
|
||||||
|
case "dragenter":
|
||||||
|
return false;
|
||||||
|
case "dragexit":
|
||||||
|
return false;
|
||||||
|
case "dragleave":
|
||||||
|
return true;
|
||||||
|
case "dragover":
|
||||||
|
return true;
|
||||||
|
case "dragstart":
|
||||||
|
return true;
|
||||||
|
case "drop":
|
||||||
|
return true;
|
||||||
|
case "mousedown":
|
||||||
|
return true;
|
||||||
|
case "mouseenter":
|
||||||
|
return false;
|
||||||
|
case "mouseleave":
|
||||||
|
return false;
|
||||||
|
case "mousemove":
|
||||||
|
return true;
|
||||||
|
case "mouseout":
|
||||||
|
return true;
|
||||||
|
case "scroll":
|
||||||
|
return false;
|
||||||
|
case "mouseover":
|
||||||
|
return true;
|
||||||
|
case "mouseup":
|
||||||
|
return true;
|
||||||
|
case "pointerdown":
|
||||||
|
return true;
|
||||||
|
case "pointermove":
|
||||||
|
return true;
|
||||||
|
case "pointerup":
|
||||||
|
return true;
|
||||||
|
case "pointercancel":
|
||||||
|
return true;
|
||||||
|
case "gotpointercapture":
|
||||||
|
return true;
|
||||||
|
case "lostpointercapture":
|
||||||
|
return true;
|
||||||
|
case "pointerenter":
|
||||||
|
return false;
|
||||||
|
case "pointerleave":
|
||||||
|
return false;
|
||||||
|
case "pointerover":
|
||||||
|
return true;
|
||||||
|
case "pointerout":
|
||||||
|
return true;
|
||||||
|
case "select":
|
||||||
|
return true;
|
||||||
|
case "touchcancel":
|
||||||
|
return true;
|
||||||
|
case "touchend":
|
||||||
|
return true;
|
||||||
|
case "touchmove":
|
||||||
|
return true;
|
||||||
|
case "touchstart":
|
||||||
|
return true;
|
||||||
|
case "wheel":
|
||||||
|
return true;
|
||||||
|
case "abort":
|
||||||
|
return false;
|
||||||
|
case "canplay":
|
||||||
|
return false;
|
||||||
|
case "canplaythrough":
|
||||||
|
return false;
|
||||||
|
case "durationchange":
|
||||||
|
return false;
|
||||||
|
case "emptied":
|
||||||
|
return false;
|
||||||
|
case "encrypted":
|
||||||
|
return true;
|
||||||
|
case "ended":
|
||||||
|
return false;
|
||||||
|
case "error":
|
||||||
|
return false;
|
||||||
|
case "loadeddata":
|
||||||
|
return false;
|
||||||
|
case "loadedmetadata":
|
||||||
|
return false;
|
||||||
|
case "loadstart":
|
||||||
|
return false;
|
||||||
|
case "pause":
|
||||||
|
return false;
|
||||||
|
case "play":
|
||||||
|
return false;
|
||||||
|
case "playing":
|
||||||
|
return false;
|
||||||
|
case "progress":
|
||||||
|
return false;
|
||||||
|
case "ratechange":
|
||||||
|
return false;
|
||||||
|
case "seeked":
|
||||||
|
return false;
|
||||||
|
case "seeking":
|
||||||
|
return false;
|
||||||
|
case "stalled":
|
||||||
|
return false;
|
||||||
|
case "suspend":
|
||||||
|
return false;
|
||||||
|
case "timeupdate":
|
||||||
|
return false;
|
||||||
|
case "volumechange":
|
||||||
|
return false;
|
||||||
|
case "waiting":
|
||||||
|
return false;
|
||||||
|
case "animationstart":
|
||||||
|
return true;
|
||||||
|
case "animationend":
|
||||||
|
return true;
|
||||||
|
case "animationiteration":
|
||||||
|
return true;
|
||||||
|
case "transitionend":
|
||||||
|
return true;
|
||||||
|
case "toggle":
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user