Explorar o código

Merge pull request #95 from DioxusLabs/jk/filedragindrop

File Drag and Drop support
Jonathan Kelley %!s(int64=3) %!d(string=hai) anos
pai
achega
ca0dd4aa71

+ 18 - 0
examples/filedragdrop.rs

@@ -0,0 +1,18 @@
+use dioxus::prelude::*;
+
+fn main() {
+    dioxus::desktop::launch_with_props(app, (), |c| {
+        c.with_file_drop_handler(|w, e| {
+            println!("{:?}", e);
+            false
+        })
+    });
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx!(
+        div {
+            h1 { "drag an file here" }
+        }
+    ))
+}

+ 0 - 39
examples/manual_edits.rs

@@ -1,39 +0,0 @@
-/*
-Example: Manual Edits
-
-It's possible to manually provide a stream of DomEdits to a Dioxus Renderer. All renderers are designed to accept a stream
-of DomEdits that abstract over a stack machine. This allows the VirtualDOM to exist entirely separately from the RealDOM,
-though features like NodeRefs and NativeEvents might not work properly everywhere.
-*/
-
-use dioxus::core::*;
-use dioxus::prelude::*;
-
-fn main() {
-    use DomEdit::*;
-
-    let edits = vec![
-        // create a container and push it onto the stack
-        CreateElement {
-            tag: "div",
-            root: 0,
-        },
-        // create an element and push it onto the stack
-        CreateElement { tag: "h1", root: 2 },
-        // create a text node and push it onto the stack
-        CreateTextNode {
-            text: "hello world",
-            root: 3,
-        },
-        // append the text node to the h1 element
-        AppendChildren { many: 1 },
-        // append the h1 element to the container
-        AppendChildren { many: 1 },
-        // append the container to the default render element ("dioxusroot" if used with default config)
-        AppendChildren { many: 1 },
-    ];
-
-    let app: Component = |cx| cx.render(rsx!(div { "some app" }));
-
-    dioxus_desktop::launch_with_props(app, (), |c| c.with_edits(edits));
-}

+ 35 - 13
packages/desktop/src/cfg.rs

@@ -1,19 +1,29 @@
-use dioxus_core::DomEdit;
 use wry::{
-    application::{event_loop::EventLoop, window::WindowBuilder},
-    webview::WebView,
+    application::{
+        event_loop::EventLoop,
+        window::{Window, WindowBuilder},
+    },
+    http::{Request as HttpRequest, Response as HttpResponse},
+    webview::{FileDropEvent, WebView},
+    Result as WryResult,
 };
 
 pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);
 
-pub struct DesktopConfig<'a> {
+pub struct DesktopConfig {
     pub window: WindowBuilder,
-    pub(crate) manual_edits: Option<Vec<DomEdit<'a>>>,
+    pub file_drop_handler: Option<Box<dyn Fn(&Window, FileDropEvent) -> bool>>,
+    pub protocos: Vec<WryProtocl>,
     pub(crate) pre_rendered: Option<String>,
     pub(crate) event_handler: Option<Box<DynEventHandlerFn>>,
 }
 
-impl<'a> DesktopConfig<'a> {
+pub type WryProtocl = (
+    String,
+    Box<dyn Fn(&HttpRequest) -> WryResult<HttpResponse> + 'static>,
+);
+
+impl DesktopConfig {
     /// Initializes a new `WindowBuilder` with default values.
     #[inline]
     pub fn new() -> Self {
@@ -21,16 +31,12 @@ impl<'a> DesktopConfig<'a> {
         Self {
             event_handler: None,
             window,
+            protocos: Vec::new(),
+            file_drop_handler: None,
             pre_rendered: None,
-            manual_edits: None,
         }
     }
 
-    pub fn with_edits(&mut self, edits: Vec<DomEdit<'a>>) -> &mut Self {
-        self.manual_edits = Some(edits);
-        self
-    }
-
     pub fn with_prerendered(&mut self, content: String) -> &mut Self {
         self.pre_rendered = Some(content);
         self
@@ -56,9 +62,25 @@ impl<'a> DesktopConfig<'a> {
         self.event_handler = Some(Box::new(handler));
         self
     }
+
+    pub fn with_file_drop_handler(
+        &mut self,
+        handler: impl Fn(&Window, FileDropEvent) -> bool + 'static,
+    ) -> &mut Self {
+        self.file_drop_handler = Some(Box::new(handler));
+        self
+    }
+
+    pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
+    where
+        F: Fn(&HttpRequest) -> WryResult<HttpResponse> + 'static,
+    {
+        self.protocos.push((name, Box::new(handler)));
+        self
+    }
 }
 
-impl<'a> Default for DesktopConfig<'a> {
+impl Default for DesktopConfig {
     fn default() -> Self {
         Self::new()
     }

+ 1 - 1
packages/desktop/src/index.html

@@ -15,4 +15,4 @@
 <script type="text/javascript" src="index.js">
 </script>
 
-</html>
+</html>

+ 34 - 21
packages/desktop/src/lib.rs

@@ -116,7 +116,7 @@ pub fn launch(root: Component) {
 /// ```
 pub fn launch_cfg(
     root: Component,
-    config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
+    config_builder: impl FnOnce(&mut DesktopConfig) -> &mut DesktopConfig,
 ) {
     launch_with_props(root, (), config_builder)
 }
@@ -147,7 +147,7 @@ pub fn launch_cfg(
 pub fn launch_with_props<P: 'static + Send>(
     root: Component<P>,
     props: P,
-    builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
+    builder: impl FnOnce(&mut DesktopConfig) -> &mut DesktopConfig,
 ) {
     let mut cfg = DesktopConfig::new();
     builder(&mut cfg);
@@ -170,9 +170,11 @@ pub fn launch_with_props<P: 'static + Send>(
                 let (is_ready, sender) = (desktop.is_ready.clone(), desktop.sender.clone());
 
                 let proxy = proxy.clone();
-                let webview = WebViewBuilder::new(window)
+                let file_handler = cfg.file_drop_handler.take();
+
+                let mut webview = WebViewBuilder::new(window)
                     .unwrap()
-                    .with_url("wry://index.html/")
+                    .with_url("dioxus://index.html/")
                     .unwrap()
                     .with_rpc_handler(move |_window: &Window, req: RpcRequest| {
                         match req.method.as_str() {
@@ -189,26 +191,37 @@ pub fn launch_with_props<P: 'static + Send>(
                         }
                         None
                     })
-                    .with_custom_protocol("wry".into(), move |request| {
-                        // Any content that that uses the `wry://` scheme will be shuttled through this handler as a "special case"
+                    .with_custom_protocol("dioxus".into(), move |request| {
+                        // Any content that that uses the `dioxus://` scheme will be shuttled through this handler as a "special case"
                         // For now, we only serve two pieces of content which get included as bytes into the final binary.
-                        let path = request.uri().replace("wry://", "");
-                        let (data, meta) = match path.as_str() {
-                            "index.html" | "index.html/" | "/index.html" => {
-                                (include_bytes!("./index.html").to_vec(), "text/html")
-                            }
-                            "index.html/index.js" => {
-                                (include_bytes!("./index.js").to_vec(), "text/javascript")
-                            }
-                            _ => (include_bytes!("./index.html").to_vec(), "text/html"),
-                        };
-
-                        wry::http::ResponseBuilder::new().mimetype(meta).body(data)
+                        let path = request.uri().replace("dioxus://", "");
+
+                        if path.trim_end_matches('/') == "index.html" {
+                            wry::http::ResponseBuilder::new()
+                                .mimetype("text/html")
+                                .body(include_bytes!("./index.html").to_vec())
+                        } else if path.trim_end_matches('/') == "index.html/index.js" {
+                            wry::http::ResponseBuilder::new()
+                                .mimetype("text/javascript")
+                                .body(include_bytes!("./index.js").to_vec())
+                        } else {
+                            wry::http::ResponseBuilder::new()
+                                .status(wry::http::status::StatusCode::NOT_FOUND)
+                                .body(format!("Not found: {}", path).as_bytes().to_vec())
+                        }
                     })
-                    .build()
-                    .unwrap();
+                    .with_file_drop_handler(move |window, evet| {
+                        if let Some(handler) = file_handler.as_ref() {
+                            return handler(window, evet);
+                        }
+                        false
+                    });
+
+                for (name, handler) in cfg.protocos.drain(..) {
+                    webview = webview.with_custom_protocol(name, handler)
+                }
 
-                desktop.webviews.insert(window_id, webview);
+                desktop.webviews.insert(window_id, webview.build().unwrap());
             }
 
             Event::WindowEvent {