Merge pull request #95 from DioxusLabs/jk/filedragindrop
File Drag and Drop support
This commit is contained in:
5 files changed
+87
-73
No files matched your search
@@ -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" }
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -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
@@ -1,19 +1,29 @@
|
|||||||
use dioxus_core::DomEdit;
|
|
||||||
use wry::{
|
use wry::{
|
||||||
application::{event_loop::EventLoop, window::WindowBuilder},
|
application::{
|
||||||
webview::WebView,
|
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(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);
|
||||||
|
|
||||||
pub struct DesktopConfig<'a> {
|
pub struct DesktopConfig {
|
||||||
pub window: WindowBuilder,
|
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) pre_rendered: Option<String>,
|
||||||
pub(crate) event_handler: Option<Box<DynEventHandlerFn>>,
|
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.
|
/// Initializes a new `WindowBuilder` with default values.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -21,16 +31,12 @@ impl<'a> DesktopConfig<'a> {
|
|||||||
Self {
|
Self {
|
||||||
event_handler: None,
|
event_handler: None,
|
||||||
window,
|
window,
|
||||||
|
protocos: Vec::new(),
|
||||||
|
file_drop_handler: None,
|
||||||
pre_rendered: 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 {
|
pub fn with_prerendered(&mut self, content: String) -> &mut Self {
|
||||||
self.pre_rendered = Some(content);
|
self.pre_rendered = Some(content);
|
||||||
self
|
self
|
||||||
@@ -56,9 +62,25 @@ impl<'a> DesktopConfig<'a> {
|
|||||||
self.event_handler = Some(Box::new(handler));
|
self.event_handler = Some(Box::new(handler));
|
||||||
self
|
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 {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,4 +15,4 @@
|
|||||||
<script type="text/javascript" src="index.js">
|
<script type="text/javascript" src="index.js">
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
+33
-20
@@ -116,7 +116,7 @@ pub fn launch(root: Component) {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn launch_cfg(
|
pub fn launch_cfg(
|
||||||
root: Component,
|
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)
|
launch_with_props(root, (), config_builder)
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ pub fn launch_cfg(
|
|||||||
pub fn launch_with_props<P: 'static + Send>(
|
pub fn launch_with_props<P: 'static + Send>(
|
||||||
root: Component<P>,
|
root: Component<P>,
|
||||||
props: 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();
|
let mut cfg = DesktopConfig::new();
|
||||||
builder(&mut cfg);
|
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 (is_ready, sender) = (desktop.is_ready.clone(), desktop.sender.clone());
|
||||||
|
|
||||||
let proxy = proxy.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()
|
.unwrap()
|
||||||
.with_url("wry://index.html/")
|
.with_url("dioxus://index.html/")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_rpc_handler(move |_window: &Window, req: RpcRequest| {
|
.with_rpc_handler(move |_window: &Window, req: RpcRequest| {
|
||||||
match req.method.as_str() {
|
match req.method.as_str() {
|
||||||
@@ -189,26 +191,37 @@ pub fn launch_with_props<P: 'static + Send>(
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
.with_custom_protocol("wry".into(), move |request| {
|
.with_custom_protocol("dioxus".into(), move |request| {
|
||||||
// Any content that that uses the `wry://` scheme will be shuttled through this handler as a "special case"
|
// 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.
|
// 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 path = request.uri().replace("dioxus://", "");
|
||||||
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)
|
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()
|
.with_file_drop_handler(move |window, evet| {
|
||||||
.unwrap();
|
if let Some(handler) = file_handler.as_ref() {
|
||||||
|
return handler(window, evet);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
desktop.webviews.insert(window_id, webview);
|
for (name, handler) in cfg.protocos.drain(..) {
|
||||||
|
webview = webview.with_custom_protocol(name, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
desktop.webviews.insert(window_id, webview.build().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::WindowEvent {
|
Event::WindowEvent {
|
||||||
|
|||||||
Reference in new issue
Block a user