Browse Source

Clean up desktop even more

Jonathan Kelley 1 year ago
parent
commit
060490892a

+ 55 - 91
packages/desktop/src/app.rs

@@ -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 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);
-            }
+        let Ok(file_dialog) = serde_json::from_value::<FileDialogRequest>(msg.params()) else {
+            return;
+        };
 
-            view.desktop_context.send_edits(view.dom.render_immediate());
+        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();
+
+        let data = Rc::new(FormData {
+            value: Default::default(),
+            values: Default::default(),
+            files: Some(Arc::new(NativeFileEngine::new(files))),
+        });
+
+        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
packages/desktop/src/assets.rs

@@ -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
packages/desktop/src/desktop_context.rs

@@ -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
packages/desktop/src/eval.rs

@@ -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 {

+ 62 - 59
packages/desktop/src/file_upload.rs

@@ -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()
-        } else {
-            dialog.pick_file().into_iter().collect()
-        };
+            let files: Vec<_> = if request.multiple {
+                dialog.pick_files().into_iter().flatten().collect()
+            } else {
+                dialog.pick_file().into_iter().collect()
+            };
 
-        files
-    }
+            files
+        }
 
-    let dialog = rfd::FileDialog::new();
+        let dialog = rfd::FileDialog::new();
 
-    if request.directory {
-        get_file_event_for_folder(request, dialog)
-    } else {
-        get_file_event_for_file(request, dialog)
+        if self.directory {
+            get_file_event_for_folder(self, dialog)
+        } else {
+            get_file_event_for_file(self, dialog)
+        }
     }
 }
 

+ 8 - 3
packages/desktop/src/webview.rs

@@ -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