merge upstream
This commit is contained in:
commit
f4af297777
127 files changed
+5668
-1277
No files matched your search
@@ -0,0 +1,60 @@
|
||||
pub fn copy_assets() {
|
||||
#[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"
|
||||
)
|
||||
))]
|
||||
{
|
||||
// The CLI will copy assets to the current working directory
|
||||
if std::env::var_os("DIOXUS_ACTIVE").is_some() {
|
||||
return;
|
||||
}
|
||||
use manganis_cli_support::AssetManifest;
|
||||
use manganis_cli_support::AssetManifestExt;
|
||||
use manganis_cli_support::Config;
|
||||
use std::path::PathBuf;
|
||||
let config = Config::current();
|
||||
let asset_location = config.assets_serve_location();
|
||||
let asset_location = PathBuf::from(asset_location);
|
||||
let _ = std::fs::remove_dir_all(&asset_location);
|
||||
|
||||
println!("Finding assets... (Note: if you run a dioxus desktop application with the CLI. This process will be significantly faster.)");
|
||||
let manifest = AssetManifest::load();
|
||||
let has_assets = manifest
|
||||
.packages()
|
||||
.iter()
|
||||
.any(|package| !package.assets().is_empty());
|
||||
|
||||
if has_assets {
|
||||
println!("Copying and optimizing assets...");
|
||||
manifest.copy_static_assets_to(&asset_location).unwrap();
|
||||
println!("Copied assets to {}", asset_location.display());
|
||||
} else {
|
||||
println!("No assets found");
|
||||
}
|
||||
}
|
||||
#[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"
|
||||
)
|
||||
)))]
|
||||
{
|
||||
println!(
|
||||
"Skipping assets in release mode. You compile assets with the dioxus-cli in release mode"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use dioxus_html::{geometry::euclid::Rect, MountedResult, RenderedElementBacking}
|
||||
|
||||
use crate::{desktop_context::DesktopContext, query::QueryEngine};
|
||||
|
||||
#[derive(Clone)]
|
||||
/// A mounted element passed to onmounted events
|
||||
pub struct DesktopElement {
|
||||
id: ElementId,
|
||||
@@ -17,8 +18,8 @@ impl DesktopElement {
|
||||
}
|
||||
|
||||
impl RenderedElementBacking for DesktopElement {
|
||||
fn get_raw_element(&self) -> dioxus_html::MountedResult<&dyn std::any::Any> {
|
||||
Ok(self)
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_client_rect(
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//! Convert a serialized event to an event trigger
|
||||
|
||||
use dioxus_html::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::element::DesktopElement;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct IpcMessage {
|
||||
method: String,
|
||||
@@ -17,3 +20,147 @@ impl IpcMessage {
|
||||
self.params
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializedHtmlEventConverter;
|
||||
|
||||
impl HtmlEventConverter for SerializedHtmlEventConverter {
|
||||
fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData {
|
||||
event
|
||||
.downcast::<SerializedAnimationData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData {
|
||||
event
|
||||
.downcast::<SerializedClipboardData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData {
|
||||
event
|
||||
.downcast::<SerializedCompositionData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_drag_data(&self, event: &PlatformEventData) -> DragData {
|
||||
event
|
||||
.downcast::<SerializedDragData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData {
|
||||
event
|
||||
.downcast::<SerializedFocusData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_form_data(&self, event: &PlatformEventData) -> FormData {
|
||||
event
|
||||
.downcast::<SerializedFormData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_image_data(&self, event: &PlatformEventData) -> ImageData {
|
||||
event
|
||||
.downcast::<SerializedImageData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData {
|
||||
event
|
||||
.downcast::<SerializedKeyboardData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_media_data(&self, event: &PlatformEventData) -> MediaData {
|
||||
event
|
||||
.downcast::<SerializedMediaData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData {
|
||||
event.downcast::<DesktopElement>().cloned().unwrap().into()
|
||||
}
|
||||
|
||||
fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData {
|
||||
event
|
||||
.downcast::<SerializedMouseData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData {
|
||||
event
|
||||
.downcast::<SerializedPointerData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
|
||||
event
|
||||
.downcast::<SerializedScrollData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData {
|
||||
event
|
||||
.downcast::<SerializedSelectionData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData {
|
||||
event
|
||||
.downcast::<SerializedToggleData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData {
|
||||
event
|
||||
.downcast::<SerializedTouchData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData {
|
||||
event
|
||||
.downcast::<SerializedTransitionData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData {
|
||||
event
|
||||
.downcast::<SerializedWheelData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
#![deny(missing_docs)]
|
||||
|
||||
mod cfg;
|
||||
mod collect_assets;
|
||||
mod desktop_context;
|
||||
mod element;
|
||||
mod escape;
|
||||
@@ -28,11 +29,12 @@ pub use desktop_context::{
|
||||
};
|
||||
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
|
||||
use dioxus_core::*;
|
||||
use dioxus_html::{event_bubbles, MountedData};
|
||||
use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent};
|
||||
use dioxus_html::{event_bubbles, FileEngine, HasFormData, MountedData, PlatformEventData};
|
||||
use dioxus_html::{native_bind::NativeFileEngine, HtmlEvent};
|
||||
use dioxus_interpreter_js::binary_protocol::Channel;
|
||||
use element::DesktopElement;
|
||||
use eval::init_eval;
|
||||
use events::SerializedHtmlEventConverter;
|
||||
use futures_util::{pin_mut, FutureExt};
|
||||
pub use protocol::{use_asset_handler, AssetFuture, AssetHandler, AssetRequest, AssetResponse};
|
||||
use rustc_hash::FxHashMap;
|
||||
@@ -145,6 +147,12 @@ pub fn launch_with_props<P: Clone + 'static>(root: Component<P>, props: P, cfg:
|
||||
}
|
||||
});
|
||||
|
||||
// Copy over any assets we find
|
||||
crate::collect_assets::copy_assets();
|
||||
|
||||
// Set the event converter
|
||||
dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter));
|
||||
|
||||
// We start the tokio runtime *on this thread*
|
||||
// Any future we poll later will use this runtime to spawn tasks and for IO
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
@@ -312,7 +320,7 @@ pub fn launch_with_props<P: Clone + 'static>(root: Component<P>, props: P, cfg:
|
||||
let element =
|
||||
DesktopElement::new(element, view.desktop_context.clone(), query);
|
||||
|
||||
Rc::new(MountedData::new(element))
|
||||
Rc::new(PlatformEventData::new(Box::new(MountedData::new(element))))
|
||||
} else {
|
||||
data.into_any()
|
||||
};
|
||||
@@ -368,15 +376,28 @@ pub fn launch_with_props<P: Clone + 'static>(root: Component<P>, props: P, cfg:
|
||||
if let Ok(file_diolog) =
|
||||
serde_json::from_value::<file_upload::FileDialogRequest>(msg.params())
|
||||
{
|
||||
struct DesktopFileUploadForm {
|
||||
files: Arc<NativeFileEngine>,
|
||||
}
|
||||
|
||||
impl HasFormData for DesktopFileUploadForm {
|
||||
fn files(&self) -> Option<Arc<dyn FileEngine>> {
|
||||
Some(self.files.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
let id = ElementId(file_diolog.target);
|
||||
let event_name = &file_diolog.event;
|
||||
let event_bubbles = file_diolog.bubbles;
|
||||
let files = file_upload::get_file_event(&file_diolog);
|
||||
let data = Rc::new(FormData {
|
||||
value: Default::default(),
|
||||
values: Default::default(),
|
||||
files: Some(Arc::new(NativeFileEngine::new(files))),
|
||||
});
|
||||
let data =
|
||||
Rc::new(PlatformEventData::new(Box::new(DesktopFileUploadForm {
|
||||
files: Arc::new(NativeFileEngine::new(files)),
|
||||
})));
|
||||
|
||||
let view = webviews.get_mut(&event.1).unwrap();
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@ pub(super) async fn desktop_handler(
|
||||
request: Request<Vec<u8>>,
|
||||
custom_head: Option<String>,
|
||||
custom_index: Option<String>,
|
||||
#[allow(unused_variables)] assets_head: Option<String>,
|
||||
root_name: &str,
|
||||
asset_handlers: &AssetHandlerRegistry,
|
||||
edit_queue: &EditQueue,
|
||||
@@ -238,9 +239,46 @@ pub(super) async fn desktop_handler(
|
||||
// Otherwise, we'll serve the default index.html and apply a custom head if that's specified.
|
||||
let mut template = include_str!("./index.html").to_string();
|
||||
|
||||
if let Some(custom_head) = custom_head {
|
||||
template = template.replace("<!-- CUSTOM HEAD -->", &custom_head);
|
||||
#[allow(unused_mut)]
|
||||
let mut head = custom_head.unwrap_or_default();
|
||||
#[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"
|
||||
)
|
||||
))]
|
||||
{
|
||||
use manganis_cli_support::AssetManifestExt;
|
||||
let manifest = manganis_cli_support::AssetManifest::load();
|
||||
head += &manifest.head();
|
||||
}
|
||||
#[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"
|
||||
)
|
||||
)))]
|
||||
{
|
||||
if let Some(assets_head) = assets_head {
|
||||
head += &assets_head;
|
||||
} else {
|
||||
tracing::warn!("No assets head found. You can compile assets with the dioxus-cli in release mode");
|
||||
}
|
||||
}
|
||||
|
||||
template = template.replace("<!-- CUSTOM HEAD -->", &head);
|
||||
|
||||
template
|
||||
.replace(
|
||||
@@ -277,12 +315,10 @@ pub(super) async fn desktop_handler(
|
||||
// Else, try to serve a file from the filesystem.
|
||||
|
||||
// If the path is relative, we'll try to serve it from the assets directory.
|
||||
let mut asset = get_asset_root()
|
||||
.unwrap_or_else(|| Path::new(".").to_path_buf())
|
||||
.join(&request.path);
|
||||
let mut asset = get_asset_root_or_default().join(&request.path);
|
||||
|
||||
if !asset.exists() {
|
||||
asset = PathBuf::from("/").join(request.path);
|
||||
asset = PathBuf::from("/").join(&request.path);
|
||||
}
|
||||
|
||||
if asset.exists() {
|
||||
@@ -293,7 +329,7 @@ pub(super) async fn desktop_handler(
|
||||
return;
|
||||
}
|
||||
};
|
||||
let asset = match std::fs::read(asset) {
|
||||
let asset = match std::fs::read(&asset) {
|
||||
Ok(asset) => asset,
|
||||
Err(err) => {
|
||||
tracing::error!("error reading asset: {}", err);
|
||||
@@ -312,6 +348,12 @@ pub(super) async fn desktop_handler(
|
||||
}
|
||||
}
|
||||
|
||||
tracing::error!(
|
||||
"Failed to find {} (as path {})",
|
||||
request.uri().path(),
|
||||
asset.display()
|
||||
);
|
||||
|
||||
match Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Cow::from(String::from("Not Found").into_bytes()))
|
||||
@@ -323,6 +365,11 @@ pub(super) async fn desktop_handler(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
pub(crate) fn get_asset_root_or_default() -> PathBuf {
|
||||
get_asset_root().unwrap_or_else(|| Path::new(".").to_path_buf())
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
fn get_asset_root() -> Option<PathBuf> {
|
||||
/*
|
||||
@@ -337,7 +384,7 @@ fn get_asset_root() -> Option<PathBuf> {
|
||||
|
||||
*/
|
||||
|
||||
if std::env::var_os("CARGO").is_some() {
|
||||
if std::env::var_os("CARGO").is_some() || std::env::var_os("DIOXUS_ACTIVE").is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget};
|
||||
pub use wry;
|
||||
pub use wry::application as tao;
|
||||
use wry::application::window::Window;
|
||||
use wry::http::Response;
|
||||
use wry::webview::{WebContext, WebView, WebViewBuilder};
|
||||
|
||||
pub(crate) fn build(
|
||||
@@ -19,6 +18,46 @@ pub(crate) fn build(
|
||||
let custom_head = cfg.custom_head.clone();
|
||||
let index_file = cfg.custom_index.clone();
|
||||
let root_name = cfg.root_name.clone();
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: restore the menu bar with muda: https://github.com/tauri-apps/muda/blob/dev/examples/wry.rs
|
||||
// if cfg.enable_default_menu_bar {
|
||||
@@ -59,6 +98,7 @@ pub(crate) fn build(
|
||||
move |request, responder| {
|
||||
let custom_head = custom_head.clone();
|
||||
let index_file = index_file.clone();
|
||||
let assets_head = assets_head.clone();
|
||||
let root_name = root_name.clone();
|
||||
let asset_handlers_ref = asset_handlers_ref.clone();
|
||||
let edit_queue = edit_queue.clone();
|
||||
@@ -67,6 +107,7 @@ pub(crate) fn build(
|
||||
request,
|
||||
custom_head.clone(),
|
||||
index_file.clone(),
|
||||
assets_head.clone(),
|
||||
&root_name,
|
||||
&asset_handlers_ref,
|
||||
&edit_queue,
|
||||
|
||||
Reference in new issue
Block a user