get compiling

This commit is contained in:
Jonathan Kelley committed 2024-01-11 16:51:18 -08:00
1 parent 2ae3241cc0
commit 8c94f0bc4a
18 files changed
+325 -344

No files matched your search

+16 -4
View File
@@ -59,7 +59,7 @@ pub struct SharedContext {
pub(crate) target: EventLoopWindowTarget<UserWindowEvent>,
}
impl<P: 'static> App<P> {
impl<P: 'static + Clone> App<P> {
pub fn new(cfg: Config, props: P, root: Component<P>) -> (EventLoop<UserWindowEvent>, Self) {
let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
@@ -196,8 +196,16 @@ impl<P: 'static> App<P> {
}
pub fn handle_initialize_msg(&mut self, id: WindowId) {
// view.dom
// .rebuild(&mut *view.desktop_context.mutation_state.borrow_mut());
// send_edits(&view.desktop_context);
println!("initialize message received, sending edits to {id:?}");
let view = self.webviews.get_mut(&id).unwrap();
view.desktop_context.send_edits(view.dom.rebuild());
view.dom
.render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut());
view.desktop_context.send_edits();
view.desktop_context
.window
.set_visible(self.is_visible_before_start);
@@ -249,7 +257,9 @@ impl<P: 'static> App<P> {
};
view.dom.handle_event(&name, as_any, element, bubbles);
view.desktop_context.send_edits(view.dom.render_immediate());
view.dom
.render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut());
view.desktop_context.send_edits();
}
#[cfg(all(feature = "hot-reload", debug_assertions))]
@@ -306,7 +316,9 @@ impl<P: 'static> App<P> {
view.dom.handle_event(event_name, data, id, event_bubbles);
}
view.desktop_context.send_edits(view.dom.render_immediate());
view.dom
.render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut());
view.desktop_context.send_edits();
}
/// Poll the virtualdom until it's pending
+1 -1
View File
@@ -82,7 +82,7 @@ impl Config {
/// Launch a Dioxus app using the given component, config, and props
///
/// See the [`crate::launch::launch_with_props`] function for more details.
pub fn launch_with_props<P: 'static>(self, root: Component<P>, props: P) {
pub fn launch_with_props<P: 'static + Clone>(self, root: Component<P>, props: P) {
crate::launch::launch_with_props(root, props, self)
}
+8 -18
View File
@@ -9,8 +9,9 @@ use crate::{
AssetRequest, Config,
};
use dioxus_core::{
once,
prelude::{current_scope_id, ScopeId},
Mutations, VirtualDom,
VirtualDom,
};
use dioxus_interpreter_js::binary_protocol::Channel;
use dioxus_interpreter_js::MutationState;
@@ -64,10 +65,6 @@ pub struct DesktopService {
pub(super) query: QueryEngine,
pub(crate) edit_queue: EditQueue,
pub(crate) mutation_state: RefCell<MutationState>,
pub(crate) templates: RefCell<FxHashMap<String, u16>>,
pub(crate) max_template_count: AtomicU16,
pub(crate) channel: RefCell<Channel>,
pub(crate) asset_handlers: AssetHandlerRegistry,
#[cfg(target_os = "ios")]
@@ -99,24 +96,17 @@ impl DesktopService {
mutation_state: Default::default(),
asset_handlers,
query: Default::default(),
templates: Default::default(),
max_template_count: Default::default(),
channel: Default::default(),
#[cfg(target_os = "ios")]
views: Default::default(),
}
}
/// Send a list of mutations to the webview
pub(crate) fn send_edits(&self, edits: Mutations) {
if let Some(bytes) = crate::edits::apply_edits(
edits,
&mut self.channel.borrow_mut(),
&mut self.templates.borrow_mut(),
&self.max_template_count,
) {
self.edit_queue.add_edits(bytes)
}
pub(crate) fn send_edits(&self) {
let mut mutations = self.mutation_state.borrow_mut();
let serialized_edits = mutations.export_memory();
dbg!(serialized_edits.len());
self.edit_queue.add_edits(serialized_edits);
}
/// Create a new window using the props and window builder
@@ -402,7 +392,7 @@ pub fn use_wry_event_handler(
let id = desktop.create_wry_event_handler(handler);
WryEventHandler {
handlers: desktop.event_handlers.clone(),
handlers: desktop.shared.event_handlers.clone(),
id,
}
})
+139 -131
View File
@@ -1,4 +1,4 @@
use dioxus_core::{BorrowedAttributeValue, Mutations, Template, TemplateAttribute, TemplateNode};
use dioxus_core::{AttributeValue, Template, TemplateAttribute, TemplateNode};
use dioxus_html::event_bubbles;
use dioxus_interpreter_js::binary_protocol::Channel;
use rustc_hash::FxHashMap;
@@ -8,22 +8,30 @@ use std::{
sync::{atomic::Ordering, Mutex},
};
use std::fmt::{Debug, Formatter};
use wry::RequestAsyncResponder;
/// This handles communication between the requests that the webview makes and the interpreter. The interpreter
/// constantly makes long running requests to the webview to get any edits that should be made to the DOM almost like
/// server side events.
///
/// It will hold onto the requests until the interpreter is ready to handle them and hold onto any pending edits until
/// a new request is made.
/// This handles communication between the requests that the webview makes and the interpreter. The interpreter constantly makes long running requests to the webview to get any edits that should be made to the DOM almost like server side events.
/// It will hold onto the requests until the interpreter is ready to handle them and hold onto any pending edits until a new request is made.
#[derive(Default, Clone)]
pub(crate) struct EditQueue {
queue: Arc<Mutex<Vec<Vec<u8>>>>,
responder: Arc<Mutex<Option<RequestAsyncResponder>>>,
responder: Arc<Mutex<Option<wry::RequestAsyncResponder>>>,
}
impl Debug for EditQueue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EditQueue")
.field("queue", &self.queue)
.field("responder", {
&self.responder.lock().unwrap().as_ref().map(|_| ())
})
.finish()
}
}
impl EditQueue {
pub fn handle_request(&self, responder: RequestAsyncResponder) {
pub fn handle_request(&self, responder: wry::RequestAsyncResponder) {
let mut queue = self.queue.lock().unwrap();
if let Some(bytes) = queue.pop() {
responder.respond(wry::http::Response::new(bytes));
@@ -42,131 +50,131 @@ impl EditQueue {
}
}
pub(crate) fn apply_edits(
mutations: Mutations,
channel: &mut Channel,
templates: &mut FxHashMap<String, u16>,
max_template_count: &AtomicU16,
) -> Option<Vec<u8>> {
if mutations.templates.is_empty() && mutations.edits.is_empty() {
return None;
}
// pub(crate) fn apply_edits(
// mutations: Mutations,
// channel: &mut Channel,
// templates: &mut FxHashMap<String, u16>,
// max_template_count: &AtomicU16,
// ) -> Option<Vec<u8>> {
// 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 template in mutations.templates {
// add_template(&template, channel, templates, max_template_count);
// }
use dioxus_core::Mutation::*;
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, 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),
}
}
// use dioxus_core::Mutation::*;
// 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, 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 {
// AttributeValue::Text(txt) => {
// channel.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
// }
// AttributeValue::Float(f) => {
// channel.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
// }
// AttributeValue::Int(n) => {
// channel.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
// }
// AttributeValue::Bool(b) => channel.set_attribute(
// id.0 as u32,
// name,
// if b { "true" } else { "false" },
// ns.unwrap_or_default(),
// ),
// AttributeValue::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)
}
// let bytes: Vec<_> = channel.export_memory().collect();
// channel.reset();
// Some(bytes)
// }
pub fn add_template(
template: &Template<'static>,
channel: &mut Channel,
templates: &mut FxHashMap<String, u16>,
max_template_count: &AtomicU16,
) {
let current_max_template_count = max_template_count.load(Ordering::Relaxed);
for root in template.roots.iter() {
create_template_node(channel, root);
templates.insert(template.name.to_owned(), current_max_template_count);
}
channel.add_templates(current_max_template_count, template.roots.len() as u16);
// pub fn add_template(
// template: &Template<'static>,
// channel: &mut Channel,
// templates: &mut FxHashMap<String, u16>,
// max_template_count: &AtomicU16,
// ) {
// let current_max_template_count = max_template_count.load(Ordering::Relaxed);
// for root in template.roots.iter() {
// create_template_node(channel, root);
// templates.insert(template.name.to_owned(), current_max_template_count);
// }
// channel.add_templates(current_max_template_count, template.roots.len() as u16);
max_template_count.fetch_add(1, Ordering::Relaxed);
}
// max_template_count.fetch_add(1, Ordering::Relaxed);
// }
pub fn create_template_node(channel: &mut Channel, node: &'static TemplateNode<'static>) {
use TemplateNode::*;
match node {
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(),
}
}
// pub fn create_template_node(channel: &mut Channel, node: &'static TemplateNode<'static>) {
// use TemplateNode::*;
// match node {
// 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(),
// }
// }
+60 -61
View File
@@ -2,76 +2,75 @@ use crate::{
assets::*, ipc::UserWindowEvent, shortcut::IntoAccelerator, window, DesktopContext,
ShortcutHandle, ShortcutRegistryError, WryEventHandler,
};
use dioxus_core::ScopeState;
use tao::{event::Event, event_loop::EventLoopWindowTarget};
use wry::RequestAsyncResponder;
/// Get an imperative handle to the current window
pub fn use_window(cx: &ScopeState) -> &DesktopContext {
cx.use_hook(|| cx.consume_context::<DesktopContext>())
.as_ref()
.unwrap()
}
// /// Get an imperative handle to the current window
// pub fn use_window(cx: &ScopeState) -> &DesktopContext {
// cx.use_hook(|| cx.consume_context::<DesktopContext>())
// .as_ref()
// .unwrap()
// }
/// Get a closure that executes any JavaScript in the WebView context.
pub fn use_wry_event_handler(
cx: &ScopeState,
handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
) -> &WryEventHandler {
cx.use_hook(move || {
let desktop = window();
// /// Get a closure that executes any JavaScript in the WebView context.
// pub fn use_wry_event_handler(
// cx: &ScopeState,
// handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
// ) -> &WryEventHandler {
// cx.use_hook(move || {
// let desktop = window();
let id = desktop.create_wry_event_handler(handler);
// let id = desktop.create_wry_event_handler(handler);
WryEventHandler {
handlers: desktop.shared.event_handlers.clone(),
id,
}
})
}
// WryEventHandler {
// handlers: desktop.shared.event_handlers.clone(),
// id,
// }
// })
// }
/// Provide a callback to handle asset loading yourself.
///
/// The callback takes a path as requested by the web view, and it should return `Some(response)`
/// if you want to load the asset, and `None` if you want to fallback on the default behavior.
pub fn use_asset_handler(
cx: &ScopeState,
name: &str,
handler: impl Fn(AssetRequest, RequestAsyncResponder) + 'static,
) {
cx.use_hook(|| {
crate::window().asset_handlers.register_handler(
name.to_string(),
Box::new(handler),
cx.scope_id(),
);
// /// Provide a callback to handle asset loading yourself.
// ///
// /// The callback takes a path as requested by the web view, and it should return `Some(response)`
// /// if you want to load the asset, and `None` if you want to fallback on the default behavior.
// pub fn use_asset_handler(
// cx: &ScopeState,
// name: &str,
// handler: impl Fn(AssetRequest, RequestAsyncResponder) + 'static,
// ) {
// cx.use_hook(|| {
// crate::window().asset_handlers.register_handler(
// name.to_string(),
// Box::new(handler),
// cx.scope_id(),
// );
Handler(name.to_string())
});
// Handler(name.to_string())
// });
// todo: can we just put ondrop in core?
struct Handler(String);
impl Drop for Handler {
fn drop(&mut self) {
_ = crate::window().asset_handlers.remove_handler(&self.0);
}
}
}
// // todo: can we just put ondrop in core?
// struct Handler(String);
// impl Drop for Handler {
// fn drop(&mut self) {
// _ = crate::window().asset_handlers.remove_handler(&self.0);
// }
// }
// }
/// Get a closure that executes any JavaScript in the WebView context.
pub fn use_global_shortcut(
cx: &ScopeState,
accelerator: impl IntoAccelerator,
handler: impl FnMut() + 'static,
) -> &Result<ShortcutHandle, ShortcutRegistryError> {
cx.use_hook(move || {
let desktop = window();
// /// Get a closure that executes any JavaScript in the WebView context.
// pub fn use_global_shortcut(
// cx: &ScopeState,
// accelerator: impl IntoAccelerator,
// handler: impl FnMut() + 'static,
// ) -> &Result<ShortcutHandle, ShortcutRegistryError> {
// cx.use_hook(move || {
// let desktop = window();
let id = desktop.create_shortcut(accelerator.accelerator(), handler);
// let id = desktop.create_shortcut(accelerator.accelerator(), handler);
Ok(ShortcutHandle {
desktop,
shortcut_id: id?,
})
})
}
// Ok(ShortcutHandle {
// desktop,
// shortcut_id: id?,
// })
// })
// }
+2 -2
View File
@@ -76,7 +76,7 @@ pub fn launch_cfg(root: Component, config_builder: Config) {
/// })
/// }
/// ```
pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config) {
pub fn launch_with_props<P: 'static + Clone>(root: Component<P>, props: P, cfg: Config) {
#[cfg(feature = "tokio")]
tokio::runtime::Builder::new_multi_thread()
.enable_all()
@@ -94,7 +94,7 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
///
/// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
/// and is equivalent to calling launch_with_props with the tokio feature disabled.
pub fn launch_with_props_blocking<P: 'static>(root: Component<P>, props: P, cfg: Config) {
pub fn launch_with_props_blocking<P: 'static + Clone>(root: Component<P>, props: P, cfg: Config) {
let (event_loop, mut app) = App::new(cfg, props, root);
event_loop.run(move |window_event, _, control_flow| {
+1 -1
View File
@@ -43,6 +43,6 @@ pub use config::{Config, WindowCloseBehaviour};
pub use desktop_context::{
window, DesktopContext, DesktopService, WryEventHandler, WryEventHandlerId,
};
pub use hooks::{use_asset_handler, use_global_shortcut, use_window, use_wry_event_handler};
// pub use hooks::{use_asset_handler, use_global_shortcut, use_window, use_wry_event_handler};
pub use shortcut::{ShortcutHandle, ShortcutId, ShortcutRegistryError};
pub use wry::RequestAsyncResponder;
+42
View File
@@ -53,6 +53,47 @@ pub(super) fn index_request(
.ok()
}
// let assets_head = {
// #[cfg(all(
// debug_assertions,
// any(
// target_os = "windows",
// target_os = "macos",
// target_os = "linux",
// target_os = "dragonfly",
// target_os = "freebsd",
// target_os = "netbsd",
// target_os = "openbsd"
// )
// ))]
// {
// None
// }
// #[cfg(not(all(
// debug_assertions,
// any(
// target_os = "windows",
// target_os = "macos",
// target_os = "linux",
// target_os = "dragonfly",
// target_os = "freebsd",
// target_os = "netbsd",
// target_os = "openbsd"
// )
// )))]
// {
// let head = crate::protocol::get_asset_root_or_default();
// let head = head.join("dist/__assets_head.html");
// match std::fs::read_to_string(&head) {
// Ok(s) => Some(s),
// Err(err) => {
// tracing::error!("Failed to read {head:?}: {err}");
// None
// }
// }
// }
// };
/// Handle a request from the webview
///
/// - Tries to stream edits if they're requested.
@@ -66,6 +107,7 @@ pub(super) fn desktop_handler(
) {
// If the request is asking for edits (ie binary protocol streaming, do that)
if request.uri().path().trim_matches('/') == "edits" {
println!("Handling edits from handler");
return edit_queue.handle_request(responder);
}
+2 -2
View File
@@ -1,4 +1,4 @@
use std::{cell::RefCell, collections::HashMap, str::FromStr};
use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr};
use dioxus_html::input_data::keyboard_types::Modifiers;
use slab::Slab;
@@ -84,7 +84,7 @@ impl ShortcutRegistry {
HotkeyError::HotKeyParseError(shortcut) => {
ShortcutRegistryError::InvalidShortcut(shortcut)
}
err => ShortcutRegistryError::Other(Box::new(err)),
err => ShortcutRegistryError::Other(Rc::new(err)),
})?;
let mut shortcut = Shortcut {
+7 -3
View File
@@ -145,7 +145,7 @@ impl WebviewInstance {
));
// Provide the desktop context to the virtualdom
dom.base_scope().provide_context(desktop_context.clone());
// dom.base_scope().provide_context(desktop_context.clone());
// let query = dom.in_runtime(|| {
// let query = ScopeId::ROOT.provide_context(desktop_context.clone());
@@ -163,7 +163,7 @@ impl WebviewInstance {
// a different TypeId.
let provider: Rc<dyn EvalProvider> =
Rc::new(DesktopEvalProvider::new(desktop_context.clone()));
dom.base_scope().provide_context(provider);
// dom.base_scope().provide_context(provider);
WebviewInstance {
waker: tao_waker(shared.proxy.clone(), desktop_context.window.id()),
@@ -190,7 +190,11 @@ impl WebviewInstance {
}
}
self.desktop_context.send_edits(self.dom.render_immediate());
// self.desktop_context.send_edits(self.dom.render_immediate());
self.dom
.render_immediate(&mut *self.desktop_context.mutation_state.borrow_mut());
self.desktop_context.send_edits();
}
}
}