Merge pull request #95 from DioxusLabs/jk/filedragindrop

File Drag and Drop support
This commit is contained in:
Jonathan Kelley 2022-01-10 23:19:36 -05:00 committed by GitHub
commit ca0dd4aa71
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 73 deletions

18
examples/filedragdrop.rs Normal file
View file

@ -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" }
}
))
}

View file

@ -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));
}

View file

@ -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()
}

View file

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

View file

@ -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"),
};
let path = request.uri().replace("dioxus://", "");
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()
.unwrap();
.with_file_drop_handler(move |window, evet| {
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 {