make desktop compile

This commit is contained in:
Evan Almloff committed 2024-01-05 14:39:45 -06:00
1 parent 21b71e992f
commit 3865f44081
27 files changed
+357 -422

No files matched your search

+15 -19
View File
@@ -7,11 +7,13 @@ use crate::shortcut::{HotKey, ShortcutId, ShortcutRegistry, ShortcutRegistryErro
use crate::AssetHandler;
use crate::Config;
use crate::WebviewHandler;
use dioxus_core::ScopeState;
use dioxus_core::once;
use dioxus_core::ScopeId;
use dioxus_core::VirtualDom;
#[cfg(all(feature = "hot-reload", debug_assertions))]
use dioxus_hot_reload::HotReloadMsg;
use dioxus_interpreter_js::binary_protocol::Channel;
use dioxus_interpreter_js::MutationState;
use rustc_hash::FxHashMap;
use slab::Slab;
use std::cell::RefCell;
@@ -45,10 +47,8 @@ pub fn window() -> DesktopContext {
/// Get an imperative handle to the current window
#[deprecated = "Prefer the using the `window` function directly for cleaner code"]
pub fn use_window(cx: &ScopeState) -> &DesktopContext {
cx.use_hook(|| cx.consume_context::<DesktopContext>())
.as_ref()
.unwrap()
pub fn use_window() -> DesktopContext {
once(window)
}
/// 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.
@@ -123,10 +123,8 @@ pub struct DesktopService {
pub(crate) shortcut_manager: ShortcutRegistry,
pub(crate) edit_queue: EditQueue,
pub(crate) templates: RefCell<FxHashMap<String, u16>>,
pub(crate) max_template_count: AtomicU16,
pub(crate) mutation_state: RefCell<MutationState>,
pub(crate) channel: RefCell<Channel>,
pub(crate) asset_handlers: AssetHandlerRegistry,
#[cfg(target_os = "ios")]
@@ -165,9 +163,7 @@ impl DesktopService {
event_handlers,
shortcut_manager,
edit_queue,
templates: Default::default(),
max_template_count: Default::default(),
channel: Default::default(),
mutation_state: Default::default(),
asset_handlers,
#[cfg(target_os = "ios")]
views: Default::default(),
@@ -192,11 +188,11 @@ impl DesktopService {
self.shortcut_manager.clone(),
);
let desktop_context = window
.dom
.base_scope()
.consume_context::<Rc<DesktopService>>()
.unwrap();
let desktop_context = window.dom.in_runtime(|| {
ScopeId::ROOT
.consume_context::<Rc<DesktopService>>()
.unwrap()
});
let id = window.desktop_context.webview.window().id();
@@ -463,10 +459,9 @@ impl WryWindowEventHandlerInner {
/// 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 || {
) -> WryEventHandler {
once(move || {
let desktop = window();
let id = desktop.create_wry_event_handler(handler);
@@ -481,6 +476,7 @@ pub fn use_wry_event_handler(
/// A wry event handler that is scoped to the current component and window. The event handler will only receive events for the window it was created for and global events.
///
/// This will automatically be removed when the component is unmounted.
#[derive(Clone)]
pub struct WryEventHandler {
handlers: WindowEventHandlers,
/// The unique identifier of the event handler.
+5 -4
View File
@@ -1,16 +1,17 @@
#![allow(clippy::await_holding_refcell_ref)]
use async_trait::async_trait;
use dioxus_core::ScopeState;
use dioxus_core::prelude::consume_context;
use dioxus_core::prelude::provide_context;
use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator};
use std::{cell::RefCell, rc::Rc};
use crate::{query::Query, DesktopContext};
/// Provides the DesktopEvalProvider through [`cx.provide_context`].
pub fn init_eval(cx: &ScopeState) {
let desktop_ctx = cx.consume_context::<DesktopContext>().unwrap();
pub fn init_eval() {
let desktop_ctx = consume_context::<DesktopContext>().unwrap();
let provider: Rc<dyn EvalProvider> = Rc::new(DesktopEvalProvider { desktop_ctx });
cx.provide_context(provider);
provide_context(provider);
}
/// Reprents the desktop-target's provider of evaluators.
+38 -162
View File
@@ -126,7 +126,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: Clone + 'static>(root: Component<P>, props: P, cfg: Config) {
let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
let proxy = event_loop.create_proxy();
@@ -301,13 +301,13 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
// check for a mounted event placeholder and replace it with a desktop specific element
let as_any = if let dioxus_html::EventData::Mounted = &data {
let query = view
.dom
.base_scope()
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone();
let query = view.dom.in_runtime(|| {
ScopeId::ROOT
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone()
});
let element =
DesktopElement::new(element, view.desktop_context.clone(), query);
@@ -319,7 +319,9 @@ 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);
view.dom
.render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut());
send_edits(&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,13 +330,13 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
if let Ok(result) = serde_json::from_value::<QueryResult>(params) {
let view = webviews.get(&event.1).unwrap();
let query = view
.dom
.base_scope()
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone();
let query = view.dom.in_runtime(|| {
ScopeId::ROOT
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone()
});
query.send(result);
}
@@ -342,7 +344,9 @@ 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);
view.dom
.rebuild(&mut *view.desktop_context.mutation_state.borrow_mut());
send_edits(&view.desktop_context);
view.desktop_context
.webview
.window()
@@ -384,7 +388,10 @@ 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);
view.dom.render_immediate(
&mut *view.desktop_context.mutation_state.borrow_mut(),
);
send_edits(&view.desktop_context);
}
}
@@ -417,11 +424,12 @@ fn create_new_window(
asset_handlers,
));
let cx = dom.base_scope();
cx.provide_context(desktop_context.clone());
// Init eval
init_eval(cx);
let query = dom.in_runtime(|| {
let query = ScopeId::ROOT.provide_context(desktop_context.clone());
// Init eval
init_eval();
query
});
WebviewHandler {
// We want to poll the virtualdom and the event loop at the same time, so the waker will be connected to both
@@ -461,149 +469,17 @@ fn poll_vdom(view: &mut WebviewHandler) {
}
}
send_edits(view.dom.render_immediate(), &view.desktop_context);
view.dom
.render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut());
send_edits(&view.desktop_context);
}
}
/// Send a list of mutations to the webview
fn send_edits(edits: Mutations, desktop_context: &DesktopContext) {
let mut channel = desktop_context.channel.borrow_mut();
let mut templates = desktop_context.templates.borrow_mut();
if let Some(bytes) = apply_edits(
edits,
&mut channel,
&mut templates,
&desktop_context.max_template_count,
) {
desktop_context.edit_queue.add_edits(bytes)
}
}
fn apply_edits(
mutations: Mutations,
channel: &mut Channel,
templates: &mut FxHashMap<String, u16>,
max_template_count: &AtomicU16,
) -> 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, 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: &AtomicU16,
) {
let current_max_template_count = max_template_count.load(std::sync::atomic::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, std::sync::atomic::Ordering::Relaxed);
}
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(),
}
fn send_edits(desktop_context: &DesktopContext) {
let mut mutations = desktop_context.mutation_state.borrow_mut();
let serialized_edits = mutations.export_memory();
desktop_context.edit_queue.add_edits(serialized_edits);
}
/// Different hide implementations per platform
+5 -7
View File
@@ -1,5 +1,5 @@
use crate::{window, DesktopContext};
use dioxus_core::ScopeState;
use dioxus_core::prelude::{once, spawn};
use dioxus_interpreter_js::INTERPRETER_JS;
use slab::Slab;
use std::{
@@ -159,6 +159,7 @@ impl AssetHandlerRegistry {
}
/// A handle to a registered asset handler.
#[derive(Clone)]
pub struct AssetHandlerHandle {
desktop: DesktopContext,
handler_id: Rc<OnceCell<usize>>,
@@ -192,16 +193,13 @@ impl Drop for AssetHandlerHandle {
///
/// 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<F: AssetFuture>(
cx: &ScopeState,
handler: impl AssetHandler<F>,
) -> &AssetHandlerHandle {
cx.use_hook(|| {
pub fn use_asset_handler<F: AssetFuture>(handler: impl AssetHandler<F>) -> AssetHandlerHandle {
once(|| {
let desktop = window();
let handler_id = Rc::new(OnceCell::new());
let handler_id_ref = Rc::clone(&handler_id);
let desktop_ref = Rc::clone(&desktop);
cx.push_future(async move {
spawn(async move {
let id = desktop.asset_handlers.register_handler(handler).await;
handler_id.set(id).unwrap();
});
+8 -9
View File
@@ -1,6 +1,5 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr};
use dioxus_core::ScopeState;
use dioxus_html::input_data::keyboard_types::Modifiers;
use slab::Slab;
use wry::application::keyboard::ModifiersState;
@@ -100,7 +99,7 @@ impl ShortcutRegistry {
Err(HotkeyError::HotKeyParseError(shortcut)) => {
return Err(ShortcutRegistryError::InvalidShortcut(shortcut))
}
Err(err) => return Err(ShortcutRegistryError::Other(Box::new(err))),
Err(err) => return Err(ShortcutRegistryError::Other(Rc::new(err))),
}
},
)
@@ -125,24 +124,25 @@ impl ShortcutRegistry {
}
}
#[non_exhaustive]
#[derive(Debug)]
/// An error that can occur when registering a shortcut.
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum ShortcutRegistryError {
/// The shortcut is invalid.
InvalidShortcut(String),
/// An unknown error occurred.
Other(Box<dyn std::error::Error>),
Other(Rc<dyn std::error::Error>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
/// An global id for a shortcut.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ShortcutId {
id: u32,
number: usize,
}
/// A global shortcut. This will be automatically removed when it is dropped.
#[derive(Clone)]
pub struct ShortcutHandle {
desktop: DesktopContext,
/// The id of the shortcut
@@ -179,11 +179,10 @@ impl IntoAccelerator for &str {
/// 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 || {
) -> Result<ShortcutHandle, ShortcutRegistryError> {
dioxus_core::once(move || {
let desktop = window();
let id = desktop.create_shortcut(accelerator.accelerator(), handler);