Clean up desktop even more
This commit is contained in:
6 files changed
+137
-178
No files matched your search
+53
-89
@@ -1,48 +1,25 @@
|
||||
pub use crate::assets::{AssetFuture, AssetHandler, AssetRequest, AssetResponse};
|
||||
pub use crate::cfg::{Config, WindowCloseBehaviour};
|
||||
pub use crate::desktop_context::DesktopContext;
|
||||
pub use crate::desktop_context::{window, DesktopService, WryEventHandler, WryEventHandlerId};
|
||||
use crate::edits::{EditQueue, WebviewQueue};
|
||||
use crate::element::DesktopElement;
|
||||
use crate::eval::init_eval;
|
||||
use crate::events::{IpcMessage, IpcMethod};
|
||||
use crate::file_upload;
|
||||
use crate::hooks::*;
|
||||
use crate::query::QueryResult;
|
||||
use crate::shortcut::GlobalHotKeyEvent;
|
||||
use crate::shortcut::ShortcutRegistry;
|
||||
pub use crate::shortcut::{ShortcutHandle, ShortcutId, ShortcutRegistryError};
|
||||
use crate::{
|
||||
cfg::{Config, WindowCloseBehaviour},
|
||||
desktop_context::{EventData, UserWindowEvent, WindowEventHandlers},
|
||||
edits::WebviewQueue,
|
||||
element::DesktopElement,
|
||||
events::IpcMessage,
|
||||
file_upload::FileDialogRequest,
|
||||
query::QueryResult,
|
||||
shortcut::{GlobalHotKeyEvent, ShortcutRegistry},
|
||||
webview::WebviewHandler,
|
||||
};
|
||||
use dioxus_core::*;
|
||||
use dioxus_html::{event_bubbles, MountedData};
|
||||
use dioxus_core::{Component, ElementId, VirtualDom};
|
||||
use dioxus_html::MountedData;
|
||||
use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent};
|
||||
use dioxus_interpreter_js::binary_protocol::Channel;
|
||||
use futures_util::{pin_mut, FutureExt};
|
||||
use global_hotkey::{
|
||||
hotkey::{Code, HotKey, Modifiers},
|
||||
GlobalHotKeyManager,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicU16;
|
||||
use std::task::Waker;
|
||||
use std::{borrow::Borrow, cell::Cell};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
pub use tao::dpi::{LogicalSize, PhysicalSize};
|
||||
use tao::event_loop::EventLoopBuilder;
|
||||
use tao::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
|
||||
pub use tao::window::WindowBuilder;
|
||||
use tao::window::WindowId;
|
||||
use tao::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
};
|
||||
use tao::{event_loop::EventLoopBuilder, window::Window};
|
||||
use tokio::runtime::Builder;
|
||||
pub use wry;
|
||||
use wry::WebContext;
|
||||
use tao::{event::Event, event_loop::ControlFlow};
|
||||
use wry::WebView;
|
||||
|
||||
pub struct App<P> {
|
||||
@@ -221,24 +198,14 @@ impl<P: 'static> App<P> {
|
||||
return;
|
||||
};
|
||||
|
||||
view.dom
|
||||
.base_scope()
|
||||
.consume_context::<DesktopContext>()
|
||||
.unwrap()
|
||||
.query
|
||||
.send(result);
|
||||
view.desktop_context.query.send(result);
|
||||
}
|
||||
|
||||
pub fn handle_user_event_msg(&mut self, msg: IpcMessage, id: WindowId) {
|
||||
let params = msg.params();
|
||||
let parsed_params = serde_json::from_value(msg.params())
|
||||
.map_err(|err| tracing::error!("Error parsing user_event: {:?}", err));
|
||||
|
||||
let evt = match serde_json::from_value::<HtmlEvent>(params) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
tracing::error!("Error parsing user_event: {:?}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let Ok(evt) = parsed_params else { return };
|
||||
|
||||
let HtmlEvent {
|
||||
element,
|
||||
@@ -250,20 +217,13 @@ impl<P: 'static> App<P> {
|
||||
let view = self.webviews.get_mut(&id).unwrap();
|
||||
|
||||
// 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 element = DesktopElement::new(element, view.desktop_context.clone(), query);
|
||||
|
||||
Rc::new(MountedData::new(element))
|
||||
} else {
|
||||
data.into_any()
|
||||
let as_any = match data {
|
||||
dioxus_html::EventData::Mounted => Rc::new(MountedData::new(DesktopElement::new(
|
||||
element,
|
||||
view.desktop_context.clone(),
|
||||
view.desktop_context.query.clone(),
|
||||
))),
|
||||
_ => data.into_any(),
|
||||
};
|
||||
|
||||
view.dom.handle_event(&name, as_any, element, bubbles);
|
||||
@@ -278,9 +238,7 @@ impl<P: 'static> App<P> {
|
||||
webview.dom.replace_template(template);
|
||||
}
|
||||
|
||||
let ids = self.webviews.keys().copied().collect::<Vec<_>>();
|
||||
|
||||
for id in ids {
|
||||
for id in self.webviews.keys().copied().collect::<Vec<_>>() {
|
||||
self.poll_vdom(id);
|
||||
}
|
||||
}
|
||||
@@ -291,31 +249,32 @@ impl<P: 'static> App<P> {
|
||||
}
|
||||
|
||||
pub fn handle_file_dialog_msg(&mut self, msg: IpcMessage, window: WindowId) {
|
||||
if let Ok(file_diolog) =
|
||||
serde_json::from_value::<file_upload::FileDialogRequest>(msg.params())
|
||||
{
|
||||
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 Ok(file_dialog) = serde_json::from_value::<FileDialogRequest>(msg.params()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let view = self.webviews.get_mut(&window).unwrap();
|
||||
let id = ElementId(file_dialog.target);
|
||||
let event_name = &file_dialog.event;
|
||||
let event_bubbles = file_dialog.bubbles;
|
||||
let files = file_dialog.get_file_event();
|
||||
|
||||
if event_name == "change&input" {
|
||||
view.dom
|
||||
.handle_event("input", data.clone(), id, event_bubbles);
|
||||
view.dom.handle_event("change", data, id, event_bubbles);
|
||||
} else {
|
||||
view.dom.handle_event(event_name, data, id, event_bubbles);
|
||||
}
|
||||
let data = Rc::new(FormData {
|
||||
value: Default::default(),
|
||||
values: Default::default(),
|
||||
files: Some(Arc::new(NativeFileEngine::new(files))),
|
||||
});
|
||||
|
||||
view.desktop_context.send_edits(view.dom.render_immediate());
|
||||
let view = self.webviews.get_mut(&window).unwrap();
|
||||
|
||||
if event_name == "change&input" {
|
||||
view.dom
|
||||
.handle_event("input", data.clone(), id, event_bubbles);
|
||||
view.dom.handle_event("change", data, id, event_bubbles);
|
||||
} else {
|
||||
view.dom.handle_event(event_name, data, id, event_bubbles);
|
||||
}
|
||||
|
||||
view.desktop_context.send_edits(view.dom.render_immediate());
|
||||
}
|
||||
|
||||
/// Poll the virtualdom until it's pending
|
||||
@@ -324,10 +283,15 @@ impl<P: 'static> App<P> {
|
||||
///
|
||||
/// All IO is done on the tokio runtime we started earlier
|
||||
pub fn poll_vdom(&mut self, id: WindowId) {
|
||||
let view = self.webviews.get_mut(&id).unwrap();
|
||||
let Some(view) = self.webviews.get_mut(&id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut cx = std::task::Context::from_waker(&view.waker);
|
||||
|
||||
// Continously poll the virtualdom until it's pending
|
||||
// Wait for work will return Ready when it has edits to be sent to the webview
|
||||
// It will return Pending when it needs to be polled again - nothing is ready
|
||||
loop {
|
||||
{
|
||||
let fut = view.dom.wait_for_work();
|
||||
@@ -335,7 +299,7 @@ impl<P: 'static> App<P> {
|
||||
|
||||
match fut.poll_unpin(&mut cx) {
|
||||
std::task::Poll::Ready(_) => {}
|
||||
std::task::Poll::Pending => break,
|
||||
std::task::Poll::Pending => return,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::edits::EditQueue;
|
||||
use crate::DesktopContext;
|
||||
use dioxus_core::ScopeState;
|
||||
use slab::Slab;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@@ -15,10 +13,7 @@ use tokio::{
|
||||
runtime::Handle,
|
||||
sync::{OnceCell, RwLock},
|
||||
};
|
||||
use wry::{
|
||||
http::{status::StatusCode, Request, Response},
|
||||
Result,
|
||||
};
|
||||
use wry::http::{Request, Response};
|
||||
|
||||
/// An arbitrary asset is an HTTP response containing a binary body.
|
||||
pub type AssetResponse = Response<Cow<'static, [u8]>>;
|
||||
|
||||
@@ -146,25 +146,19 @@ impl DesktopService {
|
||||
self.shortcut_manager.clone(),
|
||||
);
|
||||
|
||||
let desktop_context = window
|
||||
.dom
|
||||
.base_scope()
|
||||
.consume_context::<Rc<DesktopService>>()
|
||||
.unwrap();
|
||||
|
||||
let id = window.desktop_context.window.id();
|
||||
let cx = window.desktop_context.clone();
|
||||
|
||||
self.proxy
|
||||
.send_event(UserWindowEvent(EventData::NewWindow, id))
|
||||
.send_event(UserWindowEvent(EventData::NewWindow, cx.id()))
|
||||
.unwrap();
|
||||
|
||||
self.proxy
|
||||
.send_event(UserWindowEvent(EventData::Poll, id))
|
||||
.send_event(UserWindowEvent(EventData::Poll, cx.id()))
|
||||
.unwrap();
|
||||
|
||||
self.pending_windows.borrow_mut().push(window);
|
||||
|
||||
Rc::downgrade(&desktop_context)
|
||||
Rc::downgrade(&cx)
|
||||
}
|
||||
|
||||
/// trigger the drag-window event
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
#![allow(clippy::await_holding_refcell_ref)]
|
||||
use async_trait::async_trait;
|
||||
use dioxus_core::ScopeState;
|
||||
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();
|
||||
let provider: Rc<dyn EvalProvider> = Rc::new(DesktopEvalProvider { desktop_ctx });
|
||||
cx.provide_context(provider);
|
||||
}
|
||||
|
||||
/// Reprents the desktop-target's provider of evaluators.
|
||||
pub struct DesktopEvalProvider {
|
||||
desktop_ctx: DesktopContext,
|
||||
pub(crate) desktop_ctx: DesktopContext,
|
||||
}
|
||||
|
||||
impl DesktopEvalProvider {
|
||||
pub fn new(desktop_ctx: DesktopContext) -> Self {
|
||||
Self { desktop_ctx }
|
||||
}
|
||||
}
|
||||
|
||||
impl EvalProvider for DesktopEvalProvider {
|
||||
|
||||
@@ -14,74 +14,77 @@ pub(crate) struct FileDialogRequest {
|
||||
pub bubbles: bool,
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
)))]
|
||||
pub(crate) fn get_file_event(_request: &FileDialogRequest) -> Vec<PathBuf> {
|
||||
vec![]
|
||||
}
|
||||
#[allow(unused)]
|
||||
impl FileDialogRequest {
|
||||
#[cfg(not(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
)))]
|
||||
pub(crate) fn get_file_event(&self) -> Vec<PathBuf> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub(crate) fn get_file_event(request: &FileDialogRequest) -> Vec<PathBuf> {
|
||||
fn get_file_event_for_folder(
|
||||
request: &FileDialogRequest,
|
||||
dialog: rfd::FileDialog,
|
||||
) -> Vec<PathBuf> {
|
||||
if request.multiple {
|
||||
dialog.pick_folders().into_iter().flatten().collect()
|
||||
} else {
|
||||
dialog.pick_folder().into_iter().collect()
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub(crate) fn get_file_event(&self) -> Vec<PathBuf> {
|
||||
fn get_file_event_for_folder(
|
||||
request: &FileDialogRequest,
|
||||
dialog: rfd::FileDialog,
|
||||
) -> Vec<PathBuf> {
|
||||
if request.multiple {
|
||||
dialog.pick_folders().into_iter().flatten().collect()
|
||||
} else {
|
||||
dialog.pick_folder().into_iter().collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_file_event_for_file(
|
||||
request: &FileDialogRequest,
|
||||
mut dialog: rfd::FileDialog,
|
||||
) -> Vec<PathBuf> {
|
||||
let filters: Vec<_> = request
|
||||
.accept
|
||||
.as_deref()
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
.filter_map(|s| Filters::from_str(s).ok())
|
||||
.collect();
|
||||
fn get_file_event_for_file(
|
||||
request: &FileDialogRequest,
|
||||
mut dialog: rfd::FileDialog,
|
||||
) -> Vec<PathBuf> {
|
||||
let filters: Vec<_> = request
|
||||
.accept
|
||||
.as_deref()
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
.filter_map(|s| Filters::from_str(s).ok())
|
||||
.collect();
|
||||
|
||||
let file_extensions: Vec<_> = filters
|
||||
.iter()
|
||||
.flat_map(|f| f.as_extensions().into_iter())
|
||||
.collect();
|
||||
let file_extensions: Vec<_> = filters
|
||||
.iter()
|
||||
.flat_map(|f| f.as_extensions().into_iter())
|
||||
.collect();
|
||||
|
||||
dialog = dialog.add_filter("name", file_extensions.as_slice());
|
||||
dialog = dialog.add_filter("name", file_extensions.as_slice());
|
||||
|
||||
let files: Vec<_> = if request.multiple {
|
||||
dialog.pick_files().into_iter().flatten().collect()
|
||||
let files: Vec<_> = if request.multiple {
|
||||
dialog.pick_files().into_iter().flatten().collect()
|
||||
} else {
|
||||
dialog.pick_file().into_iter().collect()
|
||||
};
|
||||
|
||||
files
|
||||
}
|
||||
|
||||
let dialog = rfd::FileDialog::new();
|
||||
|
||||
if self.directory {
|
||||
get_file_event_for_folder(self, dialog)
|
||||
} else {
|
||||
dialog.pick_file().into_iter().collect()
|
||||
};
|
||||
|
||||
files
|
||||
}
|
||||
|
||||
let dialog = rfd::FileDialog::new();
|
||||
|
||||
if request.directory {
|
||||
get_file_event_for_folder(request, dialog)
|
||||
} else {
|
||||
get_file_event_for_file(request, dialog)
|
||||
get_file_event_for_file(self, dialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use std::{rc::Rc, task::Waker};
|
||||
|
||||
use crate::edits::{EditQueue, WebviewQueue};
|
||||
use crate::{
|
||||
assets::AssetHandlerRegistry, desktop_context::UserWindowEvent, waker::tao_waker, Config,
|
||||
DesktopContext,
|
||||
};
|
||||
use crate::{
|
||||
desktop_context::{EventData, WindowEventHandlers},
|
||||
eval::init_eval,
|
||||
shortcut::ShortcutRegistry,
|
||||
};
|
||||
use crate::{
|
||||
edits::{EditQueue, WebviewQueue},
|
||||
eval::DesktopEvalProvider,
|
||||
};
|
||||
use crate::{
|
||||
protocol::{self},
|
||||
DesktopService,
|
||||
@@ -162,9 +164,12 @@ pub fn create_new_window(
|
||||
asset_handlers,
|
||||
));
|
||||
|
||||
// Provide the desktop context to the virtualdom
|
||||
dom.base_scope().provide_context(desktop_context.clone());
|
||||
|
||||
init_eval(dom.base_scope());
|
||||
// Also set up its eval provider
|
||||
dom.base_scope()
|
||||
.provide_context(Rc::new(DesktopEvalProvider::new(desktop_context.clone())));
|
||||
|
||||
WebviewHandler {
|
||||
// We want to poll the virtualdom and the event loop at the same time, so the waker will be connected to both
|
||||
|
||||
Reference in new issue
Block a user