Remove native and web features from dioxus-html (#3006)

* remove native and web features from dioxus-html

* Fix downcasting web events

* Fix desktop with tokio disabled
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-10-10 13:35:59 -07:00
1 parent 677e0e7f4f
commit cda8bb24f1
54 files changed
+1768 -1512

No files matched your search

+2 -2
View File
@@ -1,14 +1,14 @@
use crate::{
config::{Config, WindowCloseBehaviour},
event_handlers::WindowEventHandlers,
file_upload::{DesktopFileUploadForm, FileDialogRequest},
file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine},
ipc::{IpcMessage, UserWindowEvent},
query::QueryResult,
shortcut::ShortcutRegistry,
webview::WebviewInstance,
};
use dioxus_core::{ElementId, VirtualDom};
use dioxus_html::{native_bind::NativeFileEngine, PlatformEventData};
use dioxus_html::PlatformEventData;
use std::{
any::Any,
cell::{Cell, RefCell},
+3
View File
@@ -3,6 +3,9 @@ use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
use crate::{query::Query, DesktopContext};
/// Code for the Dioxus channel used to communicate between the dioxus and javascript code
pub const NATIVE_EVAL_JS: &str = include_str!("./js/native_eval.js");
/// Represents the desktop-target's provider of evaluators.
pub struct DesktopDocument {
pub(crate) desktop_ctx: DesktopContext,
+81 -1
View File
@@ -1,9 +1,13 @@
#![allow(unused)]
use std::any::Any;
#[cfg(feature = "tokio_runtime")]
use tokio::{fs::File, io::AsyncReadExt};
use dioxus_html::{
geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint},
input_data::{MouseButton, MouseButtonSet},
native_bind::NativeFileEngine,
point_interaction::{
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
},
@@ -237,3 +241,79 @@ impl PointerInteraction for DesktopFileDragEvent {
self.mouse.trigger_button()
}
}
pub struct NativeFileEngine {
files: Vec<PathBuf>,
}
impl NativeFileEngine {
pub fn new(files: Vec<PathBuf>) -> Self {
Self { files }
}
}
#[async_trait::async_trait(?Send)]
impl FileEngine for NativeFileEngine {
fn files(&self) -> Vec<String> {
self.files
.iter()
.filter_map(|f| Some(f.to_str()?.to_string()))
.collect()
}
async fn file_size(&self, file: &str) -> Option<u64> {
#[cfg(feature = "tokio_runtime")]
{
let file = File::open(file).await.ok()?;
Some(file.metadata().await.ok()?.len())
}
#[cfg(not(feature = "tokio_runtime"))]
{
None
}
}
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
#[cfg(feature = "tokio_runtime")]
{
let mut file = File::open(file).await.ok()?;
let mut contents = Vec::new();
file.read_to_end(&mut contents).await.ok()?;
Some(contents)
}
#[cfg(not(feature = "tokio_runtime"))]
{
None
}
}
async fn read_file_to_string(&self, file: &str) -> Option<String> {
#[cfg(feature = "tokio_runtime")]
{
let mut file = File::open(file).await.ok()?;
let mut contents = String::new();
file.read_to_string(&mut contents).await.ok()?;
Some(contents)
}
#[cfg(not(feature = "tokio_runtime"))]
{
None
}
}
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
#[cfg(feature = "tokio_runtime")]
{
let file = File::open(file).await.ok()?;
Some(Box::new(file))
}
#[cfg(not(feature = "tokio_runtime"))]
{
None
}
}
}
+1
View File
@@ -0,0 +1 @@
[11927251734412729446]
+1
View File
@@ -0,0 +1 @@
class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class QueryParams{id;data;constructor(id,method,data){this.id=id,this.data={method,data}}}window.__msg_queues=window.__msg_queues||[];window.finalizationRegistry=window.finalizationRegistry||new FinalizationRegistry(({id})=>{window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(id,"drop")}))});window.getQuery=function(request_id){return window.__msg_queues[request_id]};window.createQuery=function(request_id){return new NativeDioxusChannel(request_id)};class NativeDioxusChannel extends DioxusChannel{rust_to_js;request_id;constructor(request_id){super();this.rust_to_js=new Channel,this.request_id=request_id,window.__msg_queues[request_id]=this.weak(),window.finalizationRegistry.register(this,{id:request_id})}async recv(){return await this.rust_to_js.recv()}send(data){window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(this.request_id,"send",data)}))}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){}}export{NativeDioxusChannel};
+11 -9
View File
@@ -63,18 +63,20 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf
/// Launches the WebView and runs the event loop, with configuration and root props.
pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! {
#[cfg(feature = "tokio_runtime")]
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(tokio::task::unconstrained(async move {
launch_virtual_dom_blocking(virtual_dom, desktop_config)
}));
{
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(tokio::task::unconstrained(async move {
launch_virtual_dom_blocking(virtual_dom, desktop_config)
}));
unreachable!("The desktop launch function will never exit")
}
#[cfg(not(feature = "tokio_runtime"))]
launch_virtual_dom_blocking(virtual_dom, desktop_config);
unreachable!("The desktop launch function will never exit")
}
/// Launches the WebView and runs the event loop, with configuration and root props.
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::document::NATIVE_EVAL_JS;
use crate::{assets::*, webview::WebviewEdits};
use dioxus_html::document::NATIVE_EVAL_JS;
use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS;
use dioxus_interpreter_js::NATIVE_JS;
use serde::Deserialize;
+90
View File
@@ -0,0 +1,90 @@
import {
Channel,
DioxusChannel,
WeakDioxusChannel,
} from "../../../html/src/ts/eval";
// In dioxus desktop, eval needs to use the window object to store global state because we evaluate separate snippets of javascript in the browser
declare global {
interface Window {
__msg_queues: WeakDioxusChannel[];
finalizationRegistry: FinalizationRegistry<{ id: number }>;
getQuery(request_id: number): WeakDioxusChannel;
createQuery(request_id: number): NativeDioxusChannel;
}
}
// A message that can be sent to the desktop renderer about a query
class QueryParams {
id: number;
data: { method: "drop" | "send"; data?: any };
constructor(id: number, method: "drop" | "send", data?: any) {
this.id = id;
this.data = { method, data };
}
}
window.__msg_queues = window.__msg_queues || [];
// In dioxus desktop, eval is copy so we cannot run a drop handler. Instead, the drop handler is run after the channel is garbage collected in the javascript side
window.finalizationRegistry =
window.finalizationRegistry ||
new FinalizationRegistry(({ id }) => {
// @ts-ignore - wry gives us this
window.ipc.postMessage(
JSON.stringify({
method: "query",
params: new QueryParams(id, "drop"),
})
);
});
// Get a query from the global state
window.getQuery = function (request_id: number): WeakDioxusChannel {
return window.__msg_queues[request_id];
};
// Create a new query (and insert it into the global state)
window.createQuery = function (request_id: number): NativeDioxusChannel {
return new NativeDioxusChannel(request_id);
};
export class NativeDioxusChannel extends DioxusChannel {
rust_to_js: Channel;
request_id: number;
constructor(request_id: number) {
super();
this.rust_to_js = new Channel();
this.request_id = request_id;
window.__msg_queues[request_id] = this.weak();
window.finalizationRegistry.register(this, { id: request_id });
}
// Receive message from Rust
async recv() {
return await this.rust_to_js.recv();
}
// Send message to rust.
send(data: any) {
// @ts-ignore - wry gives us this
window.ipc.postMessage(
JSON.stringify({
method: "query",
params: new QueryParams(this.request_id, "send", data),
})
);
}
// Send data from rust to javascript
rustSend(data: any) {
this.rust_to_js.send(data);
}
// Receive data sent from javascript in rust. This is a no-op in the native interpreter because the rust code runs remotely
async rustRecv(): Promise<any> {}
}
+9 -6
View File
@@ -3,15 +3,18 @@ use crate::element::DesktopElement;
use crate::file_upload::DesktopFileDragEvent;
use crate::menubar::DioxusMenu;
use crate::{
app::SharedContext, assets::AssetHandlerRegistry, edits::WryQueue,
file_upload::NativeFileHover, ipc::UserWindowEvent, protocol, waker::tao_waker, Config,
DesktopContext, DesktopService,
app::SharedContext,
assets::AssetHandlerRegistry,
edits::WryQueue,
file_upload::{NativeFileEngine, NativeFileHover},
ipc::UserWindowEvent,
protocol,
waker::tao_waker,
Config, DesktopContext, DesktopService,
};
use dioxus_core::{Runtime, ScopeId, VirtualDom};
use dioxus_hooks::to_owned;
use dioxus_html::{
native_bind::NativeFileEngine, prelude::Document, HasFileData, HtmlEvent, PlatformEventData,
};
use dioxus_html::{prelude::Document, HasFileData, HtmlEvent, PlatformEventData};
use futures_util::{pin_mut, FutureExt};
use std::cell::OnceCell;
use std::sync::Arc;