|
@@ -27,11 +27,13 @@ pub use desktop_context::{
|
|
|
};
|
|
|
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
|
|
|
use dioxus_core::*;
|
|
|
-use dioxus_html::MountedData;
|
|
|
+use dioxus_html::{event_bubbles, MountedData};
|
|
|
use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent};
|
|
|
+use dioxus_interpreter_js::Channel;
|
|
|
use element::DesktopElement;
|
|
|
use eval::init_eval;
|
|
|
use futures_util::{pin_mut, FutureExt};
|
|
|
+use rustc_hash::FxHashMap;
|
|
|
use shortcut::ShortcutRegistry;
|
|
|
pub use shortcut::{use_global_shortcut, ShortcutHandle, ShortcutId, ShortcutRegistryError};
|
|
|
use std::cell::Cell;
|
|
@@ -305,7 +307,7 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
|
|
|
|
|
|
view.dom.handle_event(&name, as_any, element, bubbles);
|
|
|
|
|
|
- send_edits(view.dom.render_immediate(), &view.desktop_context.webview);
|
|
|
+ send_edits(view.dom.render_immediate(), &view.desktop_context);
|
|
|
}
|
|
|
|
|
|
// When the webview sends a query, we need to send it to the query manager which handles dispatching the data to the correct pending query
|
|
@@ -328,7 +330,7 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
|
|
|
|
|
|
EventData::Ipc(msg) if msg.method() == "initialize" => {
|
|
|
let view = webviews.get_mut(&event.1).unwrap();
|
|
|
- send_edits(view.dom.rebuild(), &view.desktop_context.webview);
|
|
|
+ send_edits(view.dom.rebuild(), &view.desktop_context);
|
|
|
}
|
|
|
|
|
|
EventData::Ipc(msg) if msg.method() == "browser_open" => {
|
|
@@ -366,7 +368,7 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
|
|
|
view.dom.handle_event(event_name, data, id, event_bubbles);
|
|
|
}
|
|
|
|
|
|
- send_edits(view.dom.render_immediate(), &view.desktop_context.webview);
|
|
|
+ send_edits(view.dom.render_immediate(), &view.desktop_context);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -386,7 +388,7 @@ fn create_new_window(
|
|
|
event_handlers: &WindowEventHandlers,
|
|
|
shortcut_manager: ShortcutRegistry,
|
|
|
) -> WebviewHandler {
|
|
|
- let (webview, web_context) = webview::build(&mut cfg, event_loop, proxy.clone());
|
|
|
+ let (webview, web_context, edit_queue) = webview::build(&mut cfg, event_loop, proxy.clone());
|
|
|
let desktop_context = Rc::from(DesktopService::new(
|
|
|
webview,
|
|
|
proxy.clone(),
|
|
@@ -394,6 +396,7 @@ fn create_new_window(
|
|
|
queue.clone(),
|
|
|
event_handlers.clone(),
|
|
|
shortcut_manager,
|
|
|
+ edit_queue,
|
|
|
));
|
|
|
|
|
|
let cx = dom.base_scope();
|
|
@@ -440,16 +443,149 @@ fn poll_vdom(view: &mut WebviewHandler) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- send_edits(view.dom.render_immediate(), &view.desktop_context.webview);
|
|
|
+ send_edits(view.dom.render_immediate(), &view.desktop_context);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Send a list of mutations to the webview
|
|
|
-fn send_edits(edits: Mutations, webview: &WebView) {
|
|
|
- let serialized = serde_json::to_string(&edits).unwrap();
|
|
|
+fn send_edits(edits: Mutations, desktop_context: &DesktopContext) {
|
|
|
+ if edits.edits.len() > 0 || edits.templates.len() > 0 {
|
|
|
+ apply_edits(
|
|
|
+ edits,
|
|
|
+ &mut desktop_context.channel,
|
|
|
+ &mut desktop_context.templates,
|
|
|
+ &mut desktop_context.max_template_count,
|
|
|
+ );
|
|
|
+ desktop_context
|
|
|
+ .edit_queue
|
|
|
+ .add_edits(&mut desktop_context.channel)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn apply_edits(
|
|
|
+ mutations: Mutations,
|
|
|
+ channel: &mut Channel,
|
|
|
+ templates: &mut FxHashMap<String, u16>,
|
|
|
+ max_template_count: &mut u16,
|
|
|
+) -> Option<Vec<u8>> {
|
|
|
+ use dioxus_core::Mutation::*;
|
|
|
+ if mutations.templates.is_empty() && mutations.edits.is_empty() {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ for template in mutations.templates {
|
|
|
+ add_template(&template, channel, templates, max_template_count);
|
|
|
+ }
|
|
|
+ for edit in mutations.edits {
|
|
|
+ match edit {
|
|
|
+ AppendChildren { id, m } => channel.append_children(id.0 as u32, m as u16),
|
|
|
+ AssignId { path, id } => channel.assign_id(path, id.0 as u32),
|
|
|
+ CreatePlaceholder { id } => channel.create_placeholder(id.0 as u32),
|
|
|
+ CreateTextNode { value, id } => channel.create_text_node(value, id.0 as u32),
|
|
|
+ HydrateText { path, value, id } => channel.hydrate_text(path, value, id.0 as u32),
|
|
|
+ LoadTemplate { name, index, id } => {
|
|
|
+ if let Some(tmpl_id) = templates.get(name) {
|
|
|
+ channel.load_template(*tmpl_id as u16, index as u16, id.0 as u32)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ReplaceWith { id, m } => channel.replace_with(id.0 as u32, m as u16),
|
|
|
+ ReplacePlaceholder { path, m } => channel.replace_placeholder(path, m as u16),
|
|
|
+ InsertAfter { id, m } => channel.insert_after(id.0 as u32, m as u16),
|
|
|
+ InsertBefore { id, m } => channel.insert_before(id.0 as u32, m as u16),
|
|
|
+ SetAttribute {
|
|
|
+ name,
|
|
|
+ value,
|
|
|
+ id,
|
|
|
+ ns,
|
|
|
+ } => match value {
|
|
|
+ BorrowedAttributeValue::Text(txt) => {
|
|
|
+ channel.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
|
|
|
+ }
|
|
|
+ BorrowedAttributeValue::Float(f) => {
|
|
|
+ channel.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
|
|
|
+ }
|
|
|
+ BorrowedAttributeValue::Int(n) => {
|
|
|
+ channel.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
|
|
|
+ }
|
|
|
+ BorrowedAttributeValue::Bool(b) => channel.set_attribute(
|
|
|
+ id.0 as u32,
|
|
|
+ name,
|
|
|
+ if b { "true" } else { "false" },
|
|
|
+ ns.unwrap_or_default(),
|
|
|
+ ),
|
|
|
+ BorrowedAttributeValue::None => {
|
|
|
+ channel.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
|
|
|
+ }
|
|
|
+ _ => unreachable!(),
|
|
|
+ },
|
|
|
+ SetText { value, id } => channel.set_text(id.0 as u32, value),
|
|
|
+ NewEventListener { name, id, .. } => {
|
|
|
+ channel.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
|
|
|
+ }
|
|
|
+ RemoveEventListener { name, id } => {
|
|
|
+ channel.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
|
|
|
+ }
|
|
|
+ Remove { id } => channel.remove(id.0 as u32),
|
|
|
+ PushRoot { id } => channel.push_root(id.0 as u32),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let bytes: Vec<_> = channel.export_memory().collect();
|
|
|
+ channel.reset();
|
|
|
+ Some(bytes)
|
|
|
+}
|
|
|
+
|
|
|
+fn add_template(
|
|
|
+ template: &Template<'static>,
|
|
|
+ channel: &mut Channel,
|
|
|
+ templates: &mut FxHashMap<String, u16>,
|
|
|
+ max_template_count: &mut u16,
|
|
|
+) {
|
|
|
+ for (idx, root) in template.roots.iter().enumerate() {
|
|
|
+ create_template_node(channel, root);
|
|
|
+ templates.insert(template.name.to_owned(), *max_template_count);
|
|
|
+ }
|
|
|
+ channel.add_templates(*max_template_count, template.roots.len() as u16);
|
|
|
+
|
|
|
+ *max_template_count += 1
|
|
|
+}
|
|
|
|
|
|
- // todo: use SSE and binary data to send the edits with lower overhead
|
|
|
- _ = webview.evaluate_script(&format!("window.interpreter.handleEdits({serialized})"));
|
|
|
+fn create_template_node(channel: &mut Channel, v: &'static TemplateNode<'static>) {
|
|
|
+ use TemplateNode::*;
|
|
|
+ match v {
|
|
|
+ Element {
|
|
|
+ tag,
|
|
|
+ namespace,
|
|
|
+ attrs,
|
|
|
+ children,
|
|
|
+ ..
|
|
|
+ } => {
|
|
|
+ // Push the current node onto the stack
|
|
|
+ match namespace {
|
|
|
+ Some(ns) => channel.create_element_ns(tag, ns),
|
|
|
+ None => channel.create_element(tag),
|
|
|
+ }
|
|
|
+ // Set attributes on the current node
|
|
|
+ for attr in *attrs {
|
|
|
+ if let TemplateAttribute::Static {
|
|
|
+ name,
|
|
|
+ value,
|
|
|
+ namespace,
|
|
|
+ } = attr
|
|
|
+ {
|
|
|
+ channel.set_top_attribute(name, value, namespace.unwrap_or_default())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Add each child to the stack
|
|
|
+ for child in *children {
|
|
|
+ create_template_node(channel, child);
|
|
|
+ }
|
|
|
+ // Add all children to the parent
|
|
|
+ channel.append_children_to_top(children.len() as u16);
|
|
|
+ }
|
|
|
+ Text { text } => channel.create_raw_text(text),
|
|
|
+ DynamicText { .. } => channel.create_raw_text("p"),
|
|
|
+ Dynamic { .. } => channel.add_placeholder(),
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// Different hide implementations per platform
|