From 37a8fb539161ceeb6dc4da33976067a995460e69 Mon Sep 17 00:00:00 2001 From: Kaid Date: Mon, 5 Jun 2023 16:47:04 +0800 Subject: [PATCH 1/2] Add directory support for file input. --- examples/file_upload.rs | 13 ++++++---- packages/desktop/src/file_upload.rs | 25 +++++++++++++++---- packages/desktop/src/protocol.rs | 2 +- packages/html/src/elements.rs | 1 + .../interpreter/src/sledgehammer_bindings.rs | 1 + 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/examples/file_upload.rs b/examples/file_upload.rs index b23648b50..502d32bd8 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -1,5 +1,6 @@ #![allow(non_snake_case)] use dioxus::prelude::*; +use tokio::time::sleep; fn main() { dioxus_desktop::launch(App); @@ -13,20 +14,22 @@ fn App(cx: Scope) -> Element { r#type: "file", accept: ".txt, .rs", multiple: true, + directory: true, onchange: |evt| { to_owned![files_uploaded]; async move { if let Some(file_engine) = &evt.files { let files = file_engine.files(); - for file_name in &files { - if let Some(file) = file_engine.read_file_to_string(file_name).await{ - files_uploaded.write().push(file); - } + for file_name in files { + sleep(std::time::Duration::from_secs(1)).await; + files_uploaded.write().push(file_name); } } } }, - } + }, + + div { "progress: {files_uploaded.read().len()}" }, ul { for file in files_uploaded.read().iter() { diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index 1b24ffc55..b815064ec 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -7,14 +7,21 @@ pub(crate) struct FileDiologRequest { #[serde(default)] accept: Option, multiple: bool, + directory: bool, pub event: String, pub target: usize, pub bubbles: bool, } -pub(crate) fn get_file_event(request: &FileDiologRequest) -> Vec { - let mut dialog = rfd::FileDialog::new(); +fn get_file_event_for_folder(request: &FileDiologRequest, dialog: rfd::FileDialog) -> Vec { + if request.multiple { + dialog.pick_folders().into_iter().flatten().collect() + } else { + dialog.pick_folder().into_iter().collect() + } +} +fn get_file_event_for_file(request: &FileDiologRequest, mut dialog: rfd::FileDialog) -> Vec { let filters: Vec<_> = request .accept .as_deref() @@ -30,13 +37,21 @@ pub(crate) fn get_file_event(request: &FileDiologRequest) -> Vec { dialog = dialog.add_filter("name", file_extensions.as_slice()); - let files: Vec<_> = if request.multiple { + if request.multiple { dialog.pick_files().into_iter().flatten().collect() } else { dialog.pick_file().into_iter().collect() - }; + } +} - files +pub(crate) fn get_file_event(request: &FileDiologRequest) -> Vec { + let dialog = rfd::FileDialog::new(); + + if request.directory { + get_file_event_for_folder(request, dialog) + } else { + get_file_event_for_file(request, dialog) + } } enum Filters { diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 6be5d08b4..25e276003 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -24,7 +24,7 @@ fn module_loader(root_name: &str) -> String { let target_id = find_real_id(target); if (target_id !== null) { const send = (event_name) => { - const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); + const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.hasAttribute("directory"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); window.ipc.postMessage(message); }; send("change&input"); diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index e1e80057e..ed001a843 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -1098,6 +1098,7 @@ builder_constructors! { autofocus: Bool DEFAULT, capture: String DEFAULT, checked: Bool DEFAULT, + directory: Bool DEFAULT, disabled: Bool DEFAULT, form: Id DEFAULT, formaction: Uri DEFAULT, diff --git a/packages/interpreter/src/sledgehammer_bindings.rs b/packages/interpreter/src/sledgehammer_bindings.rs index 6c2804b59..02c2cc8dd 100644 --- a/packages/interpreter/src/sledgehammer_bindings.rs +++ b/packages/interpreter/src/sledgehammer_bindings.rs @@ -143,6 +143,7 @@ mod js { autofocus: true, autoplay: true, checked: true, + directory: true, controls: true, default: true, defer: true, From 277436f673c806f37fab4c90621e868632183f00 Mon Sep 17 00:00:00 2001 From: Kaid Date: Tue, 6 Jun 2023 08:06:27 +0800 Subject: [PATCH 2/2] Changed underlying directory attribute to `webkitdirectory`, fixed attribute parsing to properly toggle it. --- examples/file_upload.rs | 14 +++++++++++++- packages/desktop/src/file_upload.rs | 5 ++++- packages/desktop/src/protocol.rs | 2 +- packages/html/src/elements.rs | 2 +- packages/interpreter/src/sledgehammer_bindings.rs | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/examples/file_upload.rs b/examples/file_upload.rs index 502d32bd8..888459574 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -7,14 +7,26 @@ fn main() { } fn App(cx: Scope) -> Element { + let enable_directory_upload = use_state(cx, || false); let files_uploaded: &UseRef> = use_ref(cx, Vec::new); cx.render(rsx! { + label { + input { + r#type: "checkbox", + checked: "{enable_directory_upload}", + oninput: move |evt| { + enable_directory_upload.set(evt.value.parse().unwrap()); + }, + }, + "Enable directory upload" + } + input { r#type: "file", accept: ".txt, .rs", multiple: true, - directory: true, + directory: **enable_directory_upload, onchange: |evt| { to_owned![files_uploaded]; async move { diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index b815064ec..307577168 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -21,7 +21,10 @@ fn get_file_event_for_folder(request: &FileDiologRequest, dialog: rfd::FileDialo } } -fn get_file_event_for_file(request: &FileDiologRequest, mut dialog: rfd::FileDialog) -> Vec { +fn get_file_event_for_file( + request: &FileDiologRequest, + mut dialog: rfd::FileDialog, +) -> Vec { let filters: Vec<_> = request .accept .as_deref() diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 25e276003..89ce50a48 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -24,7 +24,7 @@ fn module_loader(root_name: &str) -> String { let target_id = find_real_id(target); if (target_id !== null) { const send = (event_name) => { - const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.hasAttribute("directory"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); + const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); window.ipc.postMessage(message); }; send("change&input"); diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index ed001a843..514aea2d1 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -1098,7 +1098,7 @@ builder_constructors! { autofocus: Bool DEFAULT, capture: String DEFAULT, checked: Bool DEFAULT, - directory: Bool DEFAULT, + directory: Bool "webkitdirectory", disabled: Bool DEFAULT, form: Id DEFAULT, formaction: Uri DEFAULT, diff --git a/packages/interpreter/src/sledgehammer_bindings.rs b/packages/interpreter/src/sledgehammer_bindings.rs index 02c2cc8dd..508c78a7a 100644 --- a/packages/interpreter/src/sledgehammer_bindings.rs +++ b/packages/interpreter/src/sledgehammer_bindings.rs @@ -143,10 +143,10 @@ mod js { autofocus: true, autoplay: true, checked: true, - directory: true, controls: true, default: true, defer: true, + directory: true, disabled: true, formnovalidate: true, hidden: true,