make desktop compile
This commit is contained in:
1 parent
21b71e992f
commit
3865f44081
27 files changed
+357
-422
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user