feat: wire up event delegator for webview
This commit is contained in:
1 parent
0a907b35c6
commit
7dfe89c958
21 files changed
+265
-272
No files matched your search
+3
-4
@@ -5,10 +5,9 @@ fn main() {
|
|||||||
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||||
__cx.element(
|
__cx.element(
|
||||||
dioxus_elements::button,
|
dioxus_elements::button,
|
||||||
__cx.bump()
|
[dioxus::events::on::onclick(__cx, move |_| {})],
|
||||||
.alloc([dioxus::events::on::onclick(__cx, move |_| {})]),
|
[],
|
||||||
__cx.bump().alloc([]),
|
[],
|
||||||
__cx.bump().alloc([]),
|
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// use dioxus::prelude::*;
|
// use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
dioxus::desktop::launch(App, |cfg| cfg);
|
dioxus::desktop::launch(App, |cfg| cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ const App: FC<()> = |cx| {
|
|||||||
let operator = use_state(cx, || None as Option<Operator>);
|
let operator = use_state(cx, || None as Option<Operator>);
|
||||||
let display_value = use_state(cx, || "".to_string());
|
let display_value = use_state(cx, || "".to_string());
|
||||||
|
|
||||||
let clear_display = display_value.eq("0");
|
let clear_display = display_value == "0";
|
||||||
let clear_text = if clear_display { "C" } else { "AC" };
|
let clear_text = if clear_display { "C" } else { "AC" };
|
||||||
|
|
||||||
let input_digit = move |num: u8| display_value.get_mut().push_str(num.to_string().as_str());
|
let input_digit = move |num: u8| display_value.get_mut().push_str(num.to_string().as_str());
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use dioxus::prelude::*;
|
|||||||
|
|
||||||
const STYLE: &str = include_str!("./assets/calculator.css");
|
const STYLE: &str = include_str!("./assets/calculator.css");
|
||||||
fn main() {
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
dioxus::desktop::launch(App, |cfg| cfg.with_title("Calculator Demo"))
|
dioxus::desktop::launch(App, |cfg| cfg.with_title("Calculator Demo"))
|
||||||
.expect("failed to launch dioxus app");
|
.expect("failed to launch dioxus app");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
dioxus::desktop::launch(App, |c| c);
|
dioxus::desktop::launch(App, |c| c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::util::is_valid_svg_tag;
|
|
||||||
|
|
||||||
use {
|
use {
|
||||||
proc_macro::TokenStream,
|
proc_macro::TokenStream,
|
||||||
proc_macro2::{Span, TokenStream as TokenStream2},
|
proc_macro2::{Span, TokenStream as TokenStream2},
|
||||||
@@ -163,11 +161,11 @@ impl ToTokens for ToToksCtx<&Element> {
|
|||||||
self.recurse(attr).to_tokens(tokens);
|
self.recurse(attr).to_tokens(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_valid_svg_tag(&name.to_string()) {
|
// if is_valid_svg_tag(&name.to_string()) {
|
||||||
tokens.append_all(quote! {
|
// tokens.append_all(quote! {
|
||||||
.namespace(Some("http://www.w3.org/2000/svg"))
|
// .namespace(Some("http://www.w3.org/2000/svg"))
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
match &self.inner.children {
|
match &self.inner.children {
|
||||||
MaybeExpr::Expr(expr) => tokens.append_all(quote! {
|
MaybeExpr::Expr(expr) => tokens.append_all(quote! {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ pub(crate) mod htm;
|
|||||||
pub(crate) mod ifmt;
|
pub(crate) mod ifmt;
|
||||||
pub(crate) mod props;
|
pub(crate) mod props;
|
||||||
pub(crate) mod rsx;
|
pub(crate) mod rsx;
|
||||||
pub(crate) mod util;
|
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn format_args_f(input: TokenStream) -> TokenStream {
|
pub fn format_args_f(input: TokenStream) -> TokenStream {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::util::is_valid_tag;
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{
|
use syn::{
|
||||||
|
|||||||
@@ -273,8 +273,7 @@ fn parse_rsx_element_field(
|
|||||||
|
|
||||||
let ty: AttrType = match name_str.as_str() {
|
let ty: AttrType = match name_str.as_str() {
|
||||||
// short circuit early if style is using the special syntax
|
// short circuit early if style is using the special syntax
|
||||||
"style" if stream.peek(Token![:]) => {
|
"style" if stream.peek(token::Brace) => {
|
||||||
stream.parse::<Token![:]>().unwrap();
|
|
||||||
let inner;
|
let inner;
|
||||||
syn::braced!(inner in stream);
|
syn::braced!(inner in stream);
|
||||||
|
|
||||||
|
|||||||
@@ -1,179 +0,0 @@
|
|||||||
// use lazy_static::lazy_static;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::collections::hash_set::HashSet;
|
|
||||||
use syn::{parse::ParseBuffer, Expr};
|
|
||||||
|
|
||||||
pub fn try_parse_bracketed(stream: &ParseBuffer) -> syn::Result<Expr> {
|
|
||||||
let content;
|
|
||||||
syn::braced!(content in stream);
|
|
||||||
content.parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// rsx! and html! macros support the html namespace as well as svg namespace
|
|
||||||
static HTML_TAGS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
|
||||||
[
|
|
||||||
"a",
|
|
||||||
"abbr",
|
|
||||||
"address",
|
|
||||||
"area",
|
|
||||||
"article",
|
|
||||||
"aside",
|
|
||||||
"audio",
|
|
||||||
"b",
|
|
||||||
"base",
|
|
||||||
"bdi",
|
|
||||||
"bdo",
|
|
||||||
"big",
|
|
||||||
"blockquote",
|
|
||||||
"body",
|
|
||||||
"br",
|
|
||||||
"button",
|
|
||||||
"canvas",
|
|
||||||
"caption",
|
|
||||||
"cite",
|
|
||||||
"code",
|
|
||||||
"col",
|
|
||||||
"colgroup",
|
|
||||||
"command",
|
|
||||||
"data",
|
|
||||||
"datalist",
|
|
||||||
"dd",
|
|
||||||
"del",
|
|
||||||
"details",
|
|
||||||
"dfn",
|
|
||||||
"dialog",
|
|
||||||
"div",
|
|
||||||
"dl",
|
|
||||||
"dt",
|
|
||||||
"em",
|
|
||||||
"embed",
|
|
||||||
"fieldset",
|
|
||||||
"figcaption",
|
|
||||||
"figure",
|
|
||||||
"footer",
|
|
||||||
"form",
|
|
||||||
"h1",
|
|
||||||
"h2",
|
|
||||||
"h3",
|
|
||||||
"h4",
|
|
||||||
"h5",
|
|
||||||
"h6",
|
|
||||||
"head",
|
|
||||||
"header",
|
|
||||||
"hr",
|
|
||||||
"html",
|
|
||||||
"i",
|
|
||||||
"iframe",
|
|
||||||
"img",
|
|
||||||
"input",
|
|
||||||
"ins",
|
|
||||||
"kbd",
|
|
||||||
"keygen",
|
|
||||||
"label",
|
|
||||||
"legend",
|
|
||||||
"li",
|
|
||||||
"link",
|
|
||||||
"main",
|
|
||||||
"map",
|
|
||||||
"mark",
|
|
||||||
"menu",
|
|
||||||
"menuitem",
|
|
||||||
"meta",
|
|
||||||
"meter",
|
|
||||||
"nav",
|
|
||||||
"noscript",
|
|
||||||
"object",
|
|
||||||
"ol",
|
|
||||||
"optgroup",
|
|
||||||
"option",
|
|
||||||
"output",
|
|
||||||
"p",
|
|
||||||
"param",
|
|
||||||
"picture",
|
|
||||||
"pre",
|
|
||||||
"progress",
|
|
||||||
"q",
|
|
||||||
"rp",
|
|
||||||
"rt",
|
|
||||||
"ruby",
|
|
||||||
"s",
|
|
||||||
"samp",
|
|
||||||
"script",
|
|
||||||
"section",
|
|
||||||
"select",
|
|
||||||
"small",
|
|
||||||
"source",
|
|
||||||
"span",
|
|
||||||
"strong",
|
|
||||||
"style",
|
|
||||||
"sub",
|
|
||||||
"summary",
|
|
||||||
"sup",
|
|
||||||
"table",
|
|
||||||
"tbody",
|
|
||||||
"td",
|
|
||||||
"textarea",
|
|
||||||
"tfoot",
|
|
||||||
"th",
|
|
||||||
"thead",
|
|
||||||
"time",
|
|
||||||
"title",
|
|
||||||
"tr",
|
|
||||||
"track",
|
|
||||||
"u",
|
|
||||||
"ul",
|
|
||||||
"var",
|
|
||||||
"video",
|
|
||||||
"wbr",
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
static SVG_TAGS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
|
||||||
[
|
|
||||||
// SVTG
|
|
||||||
"svg", "path", "g", "text",
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
// these tags are reserved by dioxus for any reason
|
|
||||||
// They might not all be used
|
|
||||||
static RESERVED_TAGS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
|
||||||
[
|
|
||||||
// a fragment
|
|
||||||
"fragment",
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Whether or not this tag is valid
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use html_validation::is_valid_tag;
|
|
||||||
///
|
|
||||||
/// assert_eq!(is_valid_tag("br"), true);
|
|
||||||
///
|
|
||||||
/// assert_eq!(is_valid_tag("random"), false);
|
|
||||||
/// ```
|
|
||||||
pub fn is_valid_tag(tag: &str) -> bool {
|
|
||||||
is_valid_html_tag(tag) || is_valid_svg_tag(tag) || is_valid_reserved_tag(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_valid_html_tag(tag: &str) -> bool {
|
|
||||||
HTML_TAGS.contains(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_valid_svg_tag(tag: &str) -> bool {
|
|
||||||
SVG_TAGS.contains(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_valid_reserved_tag(tag: &str) -> bool {
|
|
||||||
RESERVED_TAGS.contains(tag)
|
|
||||||
}
|
|
||||||
@@ -20,8 +20,7 @@ pub struct ElementId(pub usize);
|
|||||||
|
|
||||||
impl ElementId {
|
impl ElementId {
|
||||||
pub fn as_u64(self) -> u64 {
|
pub fn as_u64(self) -> u64 {
|
||||||
todo!()
|
self.0 as u64
|
||||||
// self.0.as_ffi()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ impl<'r, 'b> DiffMachine<'r, 'b> {
|
|||||||
pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&'b mut Scope> {
|
pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&'b mut Scope> {
|
||||||
// ensure we haven't seen this scope before
|
// ensure we haven't seen this scope before
|
||||||
// if we have, then we're trying to alias it, which is not allowed
|
// if we have, then we're trying to alias it, which is not allowed
|
||||||
debug_assert!(!self.seen_nodes.contains(id));
|
// debug_assert!(!self.seen_nodes.contains(id));
|
||||||
unsafe { self.vdom.get_scope_mut(*id) }
|
unsafe { self.vdom.get_scope_mut(*id) }
|
||||||
}
|
}
|
||||||
pub fn get_scope(&mut self, id: &ScopeId) -> Option<&'b Scope> {
|
pub fn get_scope(&mut self, id: &ScopeId) -> Option<&'b Scope> {
|
||||||
@@ -120,10 +120,11 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
|
|||||||
//
|
//
|
||||||
// each function call assumes the stack is fresh (empty).
|
// each function call assumes the stack is fresh (empty).
|
||||||
pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
|
pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
|
||||||
let root = old_node
|
// currently busted for components - need to fid
|
||||||
.dom_id
|
let root = old_node.dom_id.get().expect(&format!(
|
||||||
.get()
|
"Should not be diffing old nodes that were never assigned, {:#?}",
|
||||||
.expect("Should not be diffing old nodes that were never assigned");
|
old_node
|
||||||
|
));
|
||||||
|
|
||||||
match (&old_node.kind, &new_node.kind) {
|
match (&old_node.kind, &new_node.kind) {
|
||||||
// Handle the "sane" cases first.
|
// Handle the "sane" cases first.
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ pub enum VirtualEvent {
|
|||||||
hook_idx: usize,
|
hook_idx: usize,
|
||||||
domnode: Rc<Cell<Option<ElementId>>>,
|
domnode: Rc<Cell<Option<ElementId>>>,
|
||||||
},
|
},
|
||||||
|
// TOOD: make garbage collection its own dedicated event
|
||||||
|
// GarbageCollection {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod on {
|
pub mod on {
|
||||||
@@ -183,6 +185,7 @@ pub mod on {
|
|||||||
where F: FnMut($wrapper) + 'a
|
where F: FnMut($wrapper) + 'a
|
||||||
{
|
{
|
||||||
let bump = &c.bump();
|
let bump = &c.bump();
|
||||||
|
|
||||||
let cb: &mut dyn FnMut(VirtualEvent) = bump.alloc(move |evt: VirtualEvent| match evt {
|
let cb: &mut dyn FnMut(VirtualEvent) = bump.alloc(move |evt: VirtualEvent| match evt {
|
||||||
VirtualEvent::$wrapper(event) => callback(event),
|
VirtualEvent::$wrapper(event) => callback(event),
|
||||||
_ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
|
_ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
|
||||||
@@ -190,8 +193,10 @@ pub mod on {
|
|||||||
|
|
||||||
let callback: BumpBox<dyn FnMut(VirtualEvent) + 'a> = unsafe { BumpBox::from_raw(cb) };
|
let callback: BumpBox<dyn FnMut(VirtualEvent) + 'a> = unsafe { BumpBox::from_raw(cb) };
|
||||||
|
|
||||||
|
let event_name = stringify!($name);
|
||||||
|
let shortname: &'static str = &event_name[2..];
|
||||||
Listener {
|
Listener {
|
||||||
event: stringify!($name),
|
event: shortname,
|
||||||
mounted_node: Cell::new(None),
|
mounted_node: Cell::new(None),
|
||||||
scope: c.scope.our_arena_idx,
|
scope: c.scope.our_arena_idx,
|
||||||
callback: RefCell::new(callback),
|
callback: RefCell::new(callback),
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
//!
|
//!
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventTrigger,
|
format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority,
|
||||||
LazyNodes, NodeFactory, Properties, RealDom, ScopeId, VNode, VNodeKind, VirtualDom,
|
EventTrigger, LazyNodes, NodeFactory, Properties, RealDom, ScopeId, VNode, VNodeKind,
|
||||||
VirtualEvent, FC,
|
VirtualDom, VirtualEvent, FC,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|||||||
@@ -249,13 +249,12 @@ impl<'a> NodeFactory<'a> {
|
|||||||
|
|
||||||
// We take the references directly from the bump arena
|
// We take the references directly from the bump arena
|
||||||
//
|
//
|
||||||
//
|
|
||||||
// TODO: this code shouldn't necessarily be here of all places
|
// TODO: this code shouldn't necessarily be here of all places
|
||||||
// It would make more sense to do this in diffing
|
// It would make more sense to do this in diffing
|
||||||
|
|
||||||
let mut queue = self.scope.listeners.borrow_mut();
|
let mut queue = self.scope.listeners.borrow_mut();
|
||||||
for listener in listeners.iter() {
|
for listener in listeners.iter() {
|
||||||
let long_listener: &Listener<'static> = unsafe { std::mem::transmute(listener) };
|
let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
|
||||||
queue.push(long_listener as *const _)
|
queue.push(long_listener as *const _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ impl Scope {
|
|||||||
let next_frame = self.frames.prev_frame_mut();
|
let next_frame = self.frames.prev_frame_mut();
|
||||||
next_frame.bump.reset();
|
next_frame.bump.reset();
|
||||||
|
|
||||||
|
// make sure we call the drop implementation on all the listeners
|
||||||
|
// this is important to not leak memory
|
||||||
self.listeners.borrow_mut().clear();
|
self.listeners.borrow_mut().clear();
|
||||||
|
|
||||||
unsafe { self.hooks.reset() };
|
unsafe { self.hooks.reset() };
|
||||||
|
|||||||
@@ -258,10 +258,10 @@ impl VirtualDom {
|
|||||||
// but the guarantees provide a safe, fast, and efficient abstraction for the VirtualDOM updating framework.
|
// but the guarantees provide a safe, fast, and efficient abstraction for the VirtualDOM updating framework.
|
||||||
//
|
//
|
||||||
// A good project would be to remove all unsafe from this crate and move the unsafety into safer abstractions.
|
// A good project would be to remove all unsafe from this crate and move the unsafety into safer abstractions.
|
||||||
pub async fn progress_with_event<'s>(
|
pub async fn progress_with_event<'a, 's>(
|
||||||
&'s mut self,
|
&'s mut self,
|
||||||
realdom: &'s mut dyn RealDom<'s>,
|
realdom: &'a mut dyn RealDom<'s>,
|
||||||
edits: &mut Vec<DomEdit<'s>>,
|
edits: &'a mut Vec<DomEdit<'s>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let trigger = self.triggers.borrow_mut().pop().expect("failed");
|
let trigger = self.triggers.borrow_mut().pop().expect("failed");
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ log = "0.4.13"
|
|||||||
fern = { version = "0.6.0", features = ["colored"] }
|
fern = { version = "0.6.0", features = ["colored"] }
|
||||||
html-escape = "0.2.9"
|
html-escape = "0.2.9"
|
||||||
wry = "0.11.0"
|
wry = "0.11.0"
|
||||||
|
async-std = { version = "1.9.0", features = ["attributes"] }
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dioxus-html = { path = "../html" }
|
dioxus-html = { path = "../html" }
|
||||||
tide = "0.15.0"
|
tide = "0.15.0"
|
||||||
tide-websockets = "0.3.0"
|
tide-websockets = "0.3.0"
|
||||||
async-std = { version = "1.9.0", features = ["attributes"] }
|
|
||||||
|
|
||||||
# thiserror = "1.0.23"
|
# thiserror = "1.0.23"
|
||||||
# log = "0.4.13"
|
# log = "0.4.13"
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
//! Convert a serialized event to an event Trigger
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use dioxus_core::{
|
||||||
|
events::{
|
||||||
|
on::{MouseEvent, MouseEventInner},
|
||||||
|
VirtualEvent,
|
||||||
|
},
|
||||||
|
ElementId, EventPriority, EventTrigger, ScopeId,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
struct ImEvent {
|
||||||
|
event: String,
|
||||||
|
mounted_dom_id: u64,
|
||||||
|
scope: u64,
|
||||||
|
}
|
||||||
|
pub fn trigger_from_serialized(val: serde_json::Value) -> EventTrigger {
|
||||||
|
let mut data: Vec<ImEvent> = serde_json::from_value(val).unwrap();
|
||||||
|
let data = data.drain(..).next().unwrap();
|
||||||
|
|
||||||
|
let event = VirtualEvent::MouseEvent(MouseEvent(Rc::new(WebviewMouseEvent)));
|
||||||
|
let scope = ScopeId(data.scope as usize);
|
||||||
|
let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize));
|
||||||
|
let priority = EventPriority::High;
|
||||||
|
EventTrigger::new(event, scope, mounted_dom_id, priority)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct WebviewMouseEvent;
|
||||||
|
impl MouseEventInner for WebviewMouseEvent {
|
||||||
|
fn alt_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn button(&self) -> i16 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buttons(&self) -> u16 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_x(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_y(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ctrl_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn meta_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_x(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_y(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn screen_x(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn screen_y(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shift_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_modifier_state(&self, key_code: &str) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
+108
-45
@@ -5,10 +5,13 @@
|
|||||||
<script>
|
<script>
|
||||||
class Interpreter {
|
class Interpreter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
this.stack = [root];
|
this.stack = [root];
|
||||||
this.listeners = {};
|
this.listeners = {
|
||||||
|
"onclick": {}
|
||||||
|
};
|
||||||
this.lastNodeWasText = false;
|
this.lastNodeWasText = false;
|
||||||
this.nodes = [];
|
this.nodes = [root, root, root, root];
|
||||||
}
|
}
|
||||||
|
|
||||||
top() {
|
top() {
|
||||||
@@ -18,66 +21,125 @@
|
|||||||
pop() {
|
pop() {
|
||||||
return this.stack.pop();
|
return this.stack.pop();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class OPTABLE {
|
PushRoot(edit) {
|
||||||
PushRoot(self, edit) {
|
const id = edit.id;
|
||||||
const id = edit.root;
|
const node = this.nodes[id];
|
||||||
const node = self.nodes[id];
|
console.log("pushing root ", node, "with id", id);
|
||||||
self.stack.push(node);
|
this.stack.push(node);
|
||||||
}
|
}
|
||||||
AppendChildren(self, edit) {
|
|
||||||
let root = self.stack[self.stack.length - (edit.many + 1)];
|
AppendChildren(edit) {
|
||||||
|
let root = this.stack[this.stack.length - (edit.many + 1)];
|
||||||
for (let i = 0; i < edit.many; i++) {
|
for (let i = 0; i < edit.many; i++) {
|
||||||
console.log("popping ", i, edit.many);
|
console.log("popping ", i, edit.many);
|
||||||
let node = self.pop();
|
let node = this.pop();
|
||||||
root.appendChild(node);
|
root.appendChild(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReplaceWith(self, edit) {
|
|
||||||
|
|
||||||
let root = self.stack[self.stack.length - (edit.many + 1)];
|
ReplaceWith(edit) {
|
||||||
|
|
||||||
|
let root = this.stack[this.stack.length - (edit.many + 1)];
|
||||||
let els = [];
|
let els = [];
|
||||||
|
|
||||||
for (let i = 0; i < edit.many; i++) {
|
for (let i = 0; i < edit.many; i++) {
|
||||||
els.push(self.pop());
|
els.push(this.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
root.replaceWith(...els);
|
root.replaceWith(...els);
|
||||||
}
|
}
|
||||||
Remove(self, edit) {
|
|
||||||
const node = self.stack.pop();
|
Remove(edit) {
|
||||||
|
const node = this.stack.pop();
|
||||||
node.remove();
|
node.remove();
|
||||||
}
|
}
|
||||||
RemoveAllChildren(self, edit) {}
|
|
||||||
CreateTextNode(self, edit) {
|
RemoveAllChildren(edit) {}
|
||||||
self.stack.push(document.createTextNode(edit.text));
|
|
||||||
|
CreateTextNode(edit) {
|
||||||
|
const node = document.createTextNode(edit.text);
|
||||||
|
this.nodes[edit.id] = node;
|
||||||
|
this.stack.push(node);
|
||||||
}
|
}
|
||||||
CreateElement(self, edit) {
|
|
||||||
|
CreateElement(edit) {
|
||||||
const tagName = edit.tag;
|
const tagName = edit.tag;
|
||||||
|
const el = document.createElement(tagName);
|
||||||
|
this.nodes[edit.id] = el;
|
||||||
console.log(`creating element: `, edit);
|
console.log(`creating element: `, edit);
|
||||||
self.stack.push(document.createElement(tagName));
|
this.stack.push(el);
|
||||||
}
|
}
|
||||||
CreateElementNs(self, edit) {
|
|
||||||
|
CreateElementNs(edit) {
|
||||||
const tagName = edit.tag;
|
const tagName = edit.tag;
|
||||||
console.log(`creating namespaced element: `, edit);
|
console.log(`creating namespaced element: `, edit);
|
||||||
self.stack.push(document.createElementNS(edit.ns, edit.tag));
|
this.stack.push(document.createElementNS(edit.ns, edit.tag));
|
||||||
}
|
}
|
||||||
CreatePlaceholder(self, edit) {
|
|
||||||
const a = `self.stack.push(document.createElement("pre"))`;
|
CreatePlaceholder(edit) {
|
||||||
self.stack.push(document.createComment("vroot"));
|
const a = `this.stack.push(document.createElement(" pre"))`;
|
||||||
|
this.stack.push(document.createComment("vroot"));
|
||||||
}
|
}
|
||||||
NewEventListener(self, edit) {}
|
|
||||||
RemoveEventListener(self, edit) {}
|
NewEventListener(edit) {
|
||||||
SetText(self, edit) {
|
const element_id = edit.element_id;
|
||||||
self.top().textContent = edit.text;
|
const event_name = edit.event_name;
|
||||||
|
const mounted_node_id = edit.mounted_node_id;
|
||||||
|
const scope = edit.scope;
|
||||||
|
|
||||||
|
const element = this.top();
|
||||||
|
element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
|
||||||
|
|
||||||
|
console.log("listener map is", this.listeners);
|
||||||
|
if (this.listeners[event_name] === undefined) {
|
||||||
|
console.log("adding listener!");
|
||||||
|
this.listeners[event_name] = "bla";
|
||||||
|
this.root.addEventListener(event_name, (event) => {
|
||||||
|
const target = event.target;
|
||||||
|
const type = event.type;
|
||||||
|
const val = target.getAttribute(`dioxus-event-${event_name}`);
|
||||||
|
const fields = val.split(".");
|
||||||
|
const scope_id = parseInt(fields[0]);
|
||||||
|
const real_id = parseInt(fields[1]);
|
||||||
|
|
||||||
|
console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
|
||||||
|
|
||||||
|
rpc.call('user_event', {
|
||||||
|
event: event_name,
|
||||||
|
scope: scope_id,
|
||||||
|
mounted_dom_id: real_id,
|
||||||
|
}).then((reply) => {
|
||||||
|
console.log(reply);
|
||||||
|
this.stack.push(this.root);
|
||||||
|
|
||||||
|
for (let x = 0; x < reply.length; x++) {
|
||||||
|
let edit = reply[x];
|
||||||
|
console.log(edit);
|
||||||
|
|
||||||
|
let f = this[edit.type];
|
||||||
|
f.call(this, edit);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("initiated");
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log("failed to initiate", err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SetAttribute(self, edit) {
|
|
||||||
|
RemoveEventListener(edit) {}
|
||||||
|
|
||||||
|
SetText(edit) {
|
||||||
|
this.top().textContent = edit.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetAttribute(edit) {
|
||||||
const name = edit.field;
|
const name = edit.field;
|
||||||
const value = edit.value;
|
const value = edit.value;
|
||||||
const ns = edit.ns;
|
const ns = edit.ns;
|
||||||
|
const node = this.top(this.stack);
|
||||||
const node = self.top(self.stack);
|
|
||||||
if (ns == "style") {
|
if (ns == "style") {
|
||||||
node.style[name] = value;
|
node.style[name] = value;
|
||||||
} else if (ns !== undefined) {
|
} else if (ns !== undefined) {
|
||||||
@@ -85,36 +147,35 @@
|
|||||||
} else {
|
} else {
|
||||||
node.setAttribute(name, value);
|
node.setAttribute(name, value);
|
||||||
}
|
}
|
||||||
|
if (name === "value") {
|
||||||
if ((name === "value", self)) {
|
|
||||||
node.value = value;
|
node.value = value;
|
||||||
}
|
}
|
||||||
if ((name === "checked", self)) {
|
if (name === "checked") {
|
||||||
node.checked = true;
|
node.checked = true;
|
||||||
}
|
}
|
||||||
if ((name === "selected", self)) {
|
if (name === "selected") {
|
||||||
node.selected = true;
|
node.selected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RemoveAttribute(self, edit) {
|
RemoveAttribute(edit) {
|
||||||
const name = edit.field;
|
const name = edit.field;
|
||||||
const node = self.top(self.stack);
|
const node = this.top(this.stack);
|
||||||
|
|
||||||
node.removeAttribute(name);
|
node.removeAttribute(name);
|
||||||
|
|
||||||
if ((name === "value", self)) {
|
if (name === "value") {
|
||||||
node.value = null;
|
node.value = null;
|
||||||
}
|
}
|
||||||
if ((name === "checked", self)) {
|
if (name === "checked") {
|
||||||
node.checked = false;
|
node.checked = false;
|
||||||
}
|
}
|
||||||
if ((name === "selected", self)) {
|
if (name === "selected") {
|
||||||
node.selected = false;
|
node.selected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const op_table = new OPTABLE();
|
|
||||||
|
|
||||||
|
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
const reply = await rpc.call('initiate');
|
const reply = await rpc.call('initiate');
|
||||||
@@ -124,7 +185,9 @@
|
|||||||
for (let x = 0; x < reply.length; x++) {
|
for (let x = 0; x < reply.length; x++) {
|
||||||
let edit = reply[x];
|
let edit = reply[x];
|
||||||
console.log(edit);
|
console.log(edit);
|
||||||
op_table[edit.type](interpreter, edit);
|
|
||||||
|
let f = interpreter[edit.type];
|
||||||
|
f.call(interpreter, edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("stack completed: ", interpreter.stack);
|
console.log("stack completed: ", interpreter.stack);
|
||||||
|
|||||||
+34
-13
@@ -17,6 +17,8 @@ use wry::{
|
|||||||
|
|
||||||
mod dom;
|
mod dom;
|
||||||
mod escape;
|
mod escape;
|
||||||
|
mod events;
|
||||||
|
use events::*;
|
||||||
|
|
||||||
static HTML_CONTENT: &'static str = include_str!("./index.html");
|
static HTML_CONTENT: &'static str = include_str!("./index.html");
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
|
|||||||
user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||||
redits: Option<Vec<DomEdit<'static>>>,
|
redits: Option<Vec<DomEdit<'static>>>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
log::info!("hello edits");
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
|
|
||||||
let window = user_builder(WindowBuilder::new()).build(&event_loop)?;
|
let window = user_builder(WindowBuilder::new()).build(&event_loop)?;
|
||||||
@@ -101,24 +104,42 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
|
|||||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||||
}
|
}
|
||||||
"user_event" => {
|
"user_event" => {
|
||||||
let mut lock = vdom.write().unwrap();
|
log::debug!("User event received");
|
||||||
let mut reg_lock = registry.write().unwrap();
|
|
||||||
|
|
||||||
// Create the thin wrapper around the registry to collect the edits into
|
let registry = registry.clone();
|
||||||
let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
|
let vdom = vdom.clone();
|
||||||
|
let response = async_std::task::block_on(async move {
|
||||||
|
let mut lock = vdom.write().unwrap();
|
||||||
|
let mut reg_lock = registry.write().unwrap();
|
||||||
|
|
||||||
// Serialize the edit stream
|
// a deserialized event
|
||||||
let edits = {
|
let data = req.params.unwrap();
|
||||||
|
log::debug!("Data: {:#?}", data);
|
||||||
|
let event = trigger_from_serialized(data);
|
||||||
|
|
||||||
|
lock.queue_event(event);
|
||||||
|
|
||||||
|
// Create the thin wrapper around the registry to collect the edits into
|
||||||
|
let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
|
||||||
|
|
||||||
|
// Serialize the edit stream
|
||||||
|
//
|
||||||
let mut edits = Vec::new();
|
let mut edits = Vec::new();
|
||||||
lock.rebuild(&mut real, &mut edits).unwrap();
|
lock.progress_with_event(&mut real, &mut edits)
|
||||||
serde_json::to_value(edits).unwrap()
|
.await
|
||||||
};
|
.expect("failed to progress");
|
||||||
|
let edits = serde_json::to_value(edits).unwrap();
|
||||||
|
|
||||||
// Give back the registry into its slot
|
// Give back the registry into its slot
|
||||||
*reg_lock = Some(real.consume());
|
*reg_lock = Some(real.consume());
|
||||||
|
|
||||||
// Return the edits into the webview runtime
|
// Return the edits into the webview runtime
|
||||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||||
|
});
|
||||||
|
|
||||||
|
response
|
||||||
|
|
||||||
|
// spawn a task to clean up the garbage
|
||||||
}
|
}
|
||||||
_ => todo!("this message failed"),
|
_ => todo!("this message failed"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,8 +212,8 @@ impl<'a, T: Copy + Div<T, Output = T>> DivAssign<T> for UseState<'a, T> {
|
|||||||
self.set(self.inner.current_val.div(rhs));
|
self.set(self.inner.current_val.div(rhs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: PartialEq<T>> PartialEq<T> for UseState<'a, T> {
|
impl<'a, V, T: PartialEq<V>> PartialEq<V> for UseState<'a, T> {
|
||||||
fn eq(&self, other: &T) -> bool {
|
fn eq(&self, other: &V) -> bool {
|
||||||
self.get() == other
|
self.get() == other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user