Clean up desktop even more

This commit is contained in:
Jonathan Kelley committed 2024-01-04 18:19:28 -08:00
1 parent 7444796a85
commit 060490892a
6 files changed
+137 -178

No files matched your search

+53 -89
View File
@@ -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
View File
@@ -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]>>;
+4 -10
View File
@@ -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
+7 -9
View File
@@ -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 {
+64 -61
View File
@@ -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)
}
}
}
+8 -3
View File
@@ -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