Merge pull request #1074 from kaid/directory-selection-dialog

Add directory support for file input.
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2023-07-18 18:23:23 -07:00
commit 41c4b57984
6 files changed
+66 -28

No files matched your search

+20 -5
View File
@@ -1,32 +1,47 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use dioxus::prelude::*; use dioxus::prelude::*;
use tokio::time::sleep;
fn main() { fn main() {
dioxus_desktop::launch(App); dioxus_desktop::launch(App);
} }
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
let enable_directory_upload = use_state(cx, || false);
let files_uploaded: &UseRef<Vec<String>> = use_ref(cx, Vec::new); let files_uploaded: &UseRef<Vec<String>> = use_ref(cx, Vec::new);
cx.render(rsx! { 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 { input {
r#type: "file", r#type: "file",
accept: ".txt,.rs", accept: ".txt,.rs",
multiple: true, multiple: true,
directory: **enable_directory_upload,
onchange: |evt| { onchange: |evt| {
to_owned![files_uploaded]; to_owned![files_uploaded];
async move { async move {
if let Some(file_engine) = &evt.files { if let Some(file_engine) = &evt.files {
let files = file_engine.files(); let files = file_engine.files();
for file_name in &files { for file_name in files {
if let Some(file) = file_engine.read_file_to_string(file_name).await{ sleep(std::time::Duration::from_secs(1)).await;
files_uploaded.write().push(file); files_uploaded.write().push(file_name);
}
} }
} }
} }
}, },
} },
div { "progress: {files_uploaded.read().len()}" },
ul { ul {
for file in files_uploaded.read().iter() { for file in files_uploaded.read().iter() {
+42 -22
View File
@@ -8,11 +8,48 @@ pub(crate) struct FileDialogRequest {
#[serde(default)] #[serde(default)]
accept: Option<String>, accept: Option<String>,
multiple: bool, multiple: bool,
directory: bool,
pub event: String, pub event: String,
pub target: usize, pub target: usize,
pub bubbles: bool, pub bubbles: bool,
} }
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();
let file_extensions: Vec<_> = filters
.iter()
.flat_map(|f| f.as_extensions().into_iter())
.collect();
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()
};
files
}
#[cfg(not(any( #[cfg(not(any(
target_os = "windows", target_os = "windows",
target_os = "macos", target_os = "macos",
@@ -36,30 +73,13 @@ pub(crate) fn get_file_event(_request: &FileDialogRequest) -> Vec<PathBuf> {
target_os = "openbsd" target_os = "openbsd"
))] ))]
pub(crate) fn get_file_event(request: &FileDialogRequest) -> Vec<PathBuf> { pub(crate) fn get_file_event(request: &FileDialogRequest) -> Vec<PathBuf> {
let mut dialog = rfd::FileDialog::new(); let dialog = rfd::FileDialog::new();
let filters: Vec<_> = request if request.directory {
.accept get_file_event_for_folder(request, dialog)
.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();
dialog = dialog.add_filter("name", file_extensions.as_slice());
let files: Vec<_> = if request.multiple {
dialog.pick_files().into_iter().flatten().collect()
} else { } else {
dialog.pick_file().into_iter().collect() get_file_event_for_file(request, dialog)
}; }
files
} }
enum Filters { enum Filters {
+1 -1
View File
@@ -24,7 +24,7 @@ fn module_loader(root_name: &str) -> String {
let target_id = find_real_id(target); let target_id = find_real_id(target);
if (target_id !== null) { if (target_id !== null) {
const send = (event_name) => { 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.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name });
window.ipc.postMessage(message); window.ipc.postMessage(message);
}; };
send("change&input"); send("change&input");
+1
View File
@@ -1098,6 +1098,7 @@ builder_constructors! {
autofocus: Bool DEFAULT, autofocus: Bool DEFAULT,
capture: String DEFAULT, capture: String DEFAULT,
checked: Bool DEFAULT, checked: Bool DEFAULT,
directory: Bool "webkitdirectory",
disabled: Bool DEFAULT, disabled: Bool DEFAULT,
form: Id DEFAULT, form: Id DEFAULT,
formaction: Uri DEFAULT, formaction: Uri DEFAULT,
+1
View File
@@ -25,6 +25,7 @@ const bool_attrs = {
reversed: true, reversed: true,
selected: true, selected: true,
truespeed: true, truespeed: true,
webkitdirectory: true,
}; };
export function setAttributeInner(node, field, value, ns) { export function setAttributeInner(node, field, value, ns) {
@@ -163,6 +163,7 @@ mod js {
reversed: true, reversed: true,
selected: true, selected: true,
truespeed: true, truespeed: true,
webkitdirectory: true,
}; };
function truthy(val) { function truthy(val) {
return val === "true" || val === true; return val === "true" || val === true;