implement file events on desktop
This commit is contained in:
1 parent
e487cddd1e
commit
c1e2592299
5 files changed
+123
-14
No files matched your search
@@ -11,11 +11,11 @@ fn App(cx: Scope) -> Element {
|
|||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
input {
|
input {
|
||||||
r#type: "file",
|
r#type: "file",
|
||||||
accept: "text/*",
|
accept: ".txt",
|
||||||
|
multiple: true,
|
||||||
onchange: |evt| {
|
onchange: |evt| {
|
||||||
to_owned![files_uploaded];
|
to_owned![files_uploaded];
|
||||||
async move {
|
async move {
|
||||||
println!("files uploaded: {:?}", evt);
|
|
||||||
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 {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] }
|
dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] }
|
||||||
dioxus-html = { path = "../html", features = ["serialize"], version = "^0.3.0" }
|
dioxus-html = { path = "../html", features = ["serialize", "native-bind"], version = "^0.3.0" }
|
||||||
dioxus-interpreter-js = { path = "../interpreter", version = "^0.3.0" }
|
dioxus-interpreter-js = { path = "../interpreter", version = "^0.3.0" }
|
||||||
dioxus-hot-reload = { path = "../hot-reload", optional = true }
|
dioxus-hot-reload = { path = "../hot-reload", optional = true }
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@ dunce = "1.0.2"
|
|||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
|
|
||||||
futures-util = "0.3.25"
|
futures-util = "0.3.25"
|
||||||
|
rfd = "0.11.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "ios")'.dependencies]
|
[target.'cfg(target_os = "ios")'.dependencies]
|
||||||
objc = "0.2.7"
|
objc = "0.2.7"
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
use std::{path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub(crate) struct FileDiologRequest {
|
||||||
|
accept: String,
|
||||||
|
multiple: bool,
|
||||||
|
pub event: String,
|
||||||
|
pub target: usize,
|
||||||
|
pub bubbles: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_file_event(request: &FileDiologRequest) -> Vec<PathBuf> {
|
||||||
|
let mut dialog = rfd::FileDialog::new();
|
||||||
|
|
||||||
|
let filters: Vec<_> = request
|
||||||
|
.accept
|
||||||
|
.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
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Filters {
|
||||||
|
Extension(String),
|
||||||
|
Mime(String),
|
||||||
|
Audio,
|
||||||
|
Video,
|
||||||
|
Image,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filters {
|
||||||
|
fn as_extensions(&self) -> Vec<&str> {
|
||||||
|
match self {
|
||||||
|
Filters::Extension(extension) => vec![extension.as_str()],
|
||||||
|
Filters::Mime(_) => vec![],
|
||||||
|
Filters::Audio => vec!["mp3", "wav", "ogg"],
|
||||||
|
Filters::Video => vec!["mp4", "webm"],
|
||||||
|
Filters::Image => vec!["png", "jpg", "jpeg", "gif", "webp"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Filters {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if let Some(extension) = s.strip_prefix('.') {
|
||||||
|
Ok(Filters::Extension(extension.to_string()))
|
||||||
|
} else {
|
||||||
|
match s {
|
||||||
|
"audio/*" => Ok(Filters::Audio),
|
||||||
|
"video/*" => Ok(Filters::Video),
|
||||||
|
"image/*" => Ok(Filters::Image),
|
||||||
|
_ => Ok(Filters::Mime(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ mod desktop_context;
|
|||||||
mod escape;
|
mod escape;
|
||||||
mod eval;
|
mod eval;
|
||||||
mod events;
|
mod events;
|
||||||
|
mod file_upload;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
mod shortcut;
|
mod shortcut;
|
||||||
mod waker;
|
mod waker;
|
||||||
@@ -19,14 +20,14 @@ pub use desktop_context::{
|
|||||||
};
|
};
|
||||||
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
|
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use dioxus_html::HtmlEvent;
|
use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent};
|
||||||
pub use eval::{use_eval, EvalResult};
|
pub use eval::{use_eval, EvalResult};
|
||||||
use futures_util::{pin_mut, FutureExt};
|
use futures_util::{pin_mut, FutureExt};
|
||||||
use shortcut::ShortcutRegistry;
|
use shortcut::ShortcutRegistry;
|
||||||
pub use shortcut::{use_global_shortcut, ShortcutHandle, ShortcutId, ShortcutRegistryError};
|
pub use shortcut::{use_global_shortcut, ShortcutHandle, ShortcutId, ShortcutRegistryError};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::task::Waker;
|
use std::task::Waker;
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
pub use tao::dpi::{LogicalSize, PhysicalSize};
|
pub use tao::dpi::{LogicalSize, PhysicalSize};
|
||||||
use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget};
|
use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget};
|
||||||
pub use tao::window::WindowBuilder;
|
pub use tao::window::WindowBuilder;
|
||||||
@@ -264,6 +265,29 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EventData::Ipc(msg) if msg.method() == "file_diolog" => {
|
||||||
|
if let Ok(file_diolog) =
|
||||||
|
serde_json::from_value::<file_upload::FileDiologRequest>(msg.params())
|
||||||
|
{
|
||||||
|
let id = ElementId(file_diolog.target);
|
||||||
|
let event_name = &file_diolog.event;
|
||||||
|
let event_bubbles = file_diolog.bubbles;
|
||||||
|
let files = file_upload::get_file_event(&file_diolog);
|
||||||
|
let data = FormData {
|
||||||
|
value: Default::default(),
|
||||||
|
values: Default::default(),
|
||||||
|
files: Some(Arc::new(NativeFileEngine::new(files))),
|
||||||
|
};
|
||||||
|
|
||||||
|
let view = webviews.get_mut(&event.1).unwrap();
|
||||||
|
|
||||||
|
view.dom
|
||||||
|
.handle_event(event_name, Rc::new(data), id, event_bubbles);
|
||||||
|
|
||||||
|
send_edits(view.dom.render_immediate(), &view.webview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Event::GlobalShortcutEvent(id) => shortcut_manager.call_handlers(id),
|
Event::GlobalShortcutEvent(id) => shortcut_manager.call_handlers(id),
|
||||||
|
|||||||
@@ -14,15 +14,25 @@ fn module_loader(root_name: &str) -> String {
|
|||||||
r#"// Prevent file inputs from opening the file dialog on click
|
r#"// Prevent file inputs from opening the file dialog on click
|
||||||
let inputs = document.querySelectorAll("input");
|
let inputs = document.querySelectorAll("input");
|
||||||
for (let input of inputs) {
|
for (let input of inputs) {
|
||||||
input.addEventListener("click", (event) => {
|
if (!input.getAttribute("data-dioxus-file-listener")) {
|
||||||
let target = event.target;
|
input.setAttribute("data-dioxus-file-listener", true);
|
||||||
// prevent file inputs from opening the file dialog on click
|
input.addEventListener("click", (event) => {
|
||||||
const type = target.getAttribute("type");
|
let target = event.target;
|
||||||
if (type === "file") {
|
// prevent file inputs from opening the file dialog on click
|
||||||
window.ipc.postMessage(serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple") }));
|
const type = target.getAttribute("type");
|
||||||
event.preventDefault();
|
if (type === "file") {
|
||||||
}
|
let target_id = find_real_id(target);
|
||||||
});
|
if (target_id !== null) {
|
||||||
|
const event_name = "change";
|
||||||
|
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 });
|
||||||
|
console.log(message);
|
||||||
|
console.log(event);
|
||||||
|
window.ipc.postMessage(message);
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
Reference in new issue
Block a user