123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- #![allow(unused)]
- use std::any::Any;
- #[cfg(feature = "tokio_runtime")]
- use tokio::{fs::File, io::AsyncReadExt};
- use dioxus_html::{
- geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint},
- input_data::{MouseButton, MouseButtonSet},
- point_interaction::{
- InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
- },
- prelude::{SerializedMouseData, SerializedPointInteraction},
- FileEngine, HasDragData, HasFileData, HasFormData, HasMouseData,
- };
- use serde::Deserialize;
- use std::{
- cell::{Cell, RefCell},
- path::PathBuf,
- rc::Rc,
- str::FromStr,
- sync::Arc,
- };
- use wry::DragDropEvent;
- #[derive(Debug, Deserialize)]
- pub(crate) struct FileDialogRequest {
- #[serde(default)]
- accept: Option<String>,
- multiple: bool,
- directory: bool,
- pub event: String,
- pub target: usize,
- pub bubbles: bool,
- }
- #[allow(unused)]
- impl FileDialogRequest {
- #[cfg(not(any(
- target_os = "windows",
- target_os = "macos",
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- )))]
- pub(crate) fn get_file_event(&self) -> Vec<PathBuf> {
- vec![]
- }
- #[cfg(any(
- target_os = "windows",
- target_os = "macos",
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- ))]
- pub(crate) fn get_file_event(&self) -> Vec<PathBuf> {
- 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
- }
- let dialog = rfd::FileDialog::new();
- if self.directory {
- get_file_event_for_folder(self, dialog)
- } else {
- get_file_event_for_file(self, dialog)
- }
- }
- }
- 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())),
- }
- }
- }
- }
- #[derive(Clone)]
- pub(crate) struct DesktopFileUploadForm {
- pub files: Arc<NativeFileEngine>,
- }
- impl HasFileData for DesktopFileUploadForm {
- fn files(&self) -> Option<Arc<dyn FileEngine>> {
- Some(self.files.clone())
- }
- }
- impl HasFormData for DesktopFileUploadForm {
- fn as_any(&self) -> &dyn std::any::Any {
- self
- }
- }
- #[derive(Default, Clone)]
- pub struct NativeFileHover {
- event: Rc<RefCell<Option<DragDropEvent>>>,
- }
- impl NativeFileHover {
- pub fn set(&self, event: DragDropEvent) {
- self.event.borrow_mut().replace(event);
- }
- pub fn current(&self) -> Option<DragDropEvent> {
- self.event.borrow_mut().clone()
- }
- }
- #[derive(Clone)]
- pub(crate) struct DesktopFileDragEvent {
- pub mouse: SerializedPointInteraction,
- pub files: Arc<NativeFileEngine>,
- }
- impl HasFileData for DesktopFileDragEvent {
- fn files(&self) -> Option<Arc<dyn FileEngine>> {
- Some(self.files.clone())
- }
- }
- impl HasDragData for DesktopFileDragEvent {
- fn as_any(&self) -> &dyn std::any::Any {
- self
- }
- }
- impl HasMouseData for DesktopFileDragEvent {
- fn as_any(&self) -> &dyn std::any::Any {
- self
- }
- }
- impl InteractionLocation for DesktopFileDragEvent {
- fn client_coordinates(&self) -> ClientPoint {
- self.mouse.client_coordinates()
- }
- fn page_coordinates(&self) -> PagePoint {
- self.mouse.page_coordinates()
- }
- fn screen_coordinates(&self) -> ScreenPoint {
- self.mouse.screen_coordinates()
- }
- }
- impl InteractionElementOffset for DesktopFileDragEvent {
- fn element_coordinates(&self) -> ElementPoint {
- self.mouse.element_coordinates()
- }
- fn coordinates(&self) -> Coordinates {
- self.mouse.coordinates()
- }
- }
- impl ModifiersInteraction for DesktopFileDragEvent {
- fn modifiers(&self) -> dioxus_html::prelude::Modifiers {
- self.mouse.modifiers()
- }
- }
- impl PointerInteraction for DesktopFileDragEvent {
- fn held_buttons(&self) -> MouseButtonSet {
- self.mouse.held_buttons()
- }
- fn trigger_button(&self) -> Option<MouseButton> {
- self.mouse.trigger_button()
- }
- }
- pub struct NativeFileEngine {
- files: Vec<PathBuf>,
- }
- impl NativeFileEngine {
- pub fn new(files: Vec<PathBuf>) -> Self {
- Self { files }
- }
- }
- #[async_trait::async_trait(?Send)]
- impl FileEngine for NativeFileEngine {
- fn files(&self) -> Vec<String> {
- self.files
- .iter()
- .filter_map(|f| Some(f.to_str()?.to_string()))
- .collect()
- }
- async fn file_size(&self, file: &str) -> Option<u64> {
- #[cfg(feature = "tokio_runtime")]
- {
- let file = File::open(file).await.ok()?;
- Some(file.metadata().await.ok()?.len())
- }
- #[cfg(not(feature = "tokio_runtime"))]
- {
- None
- }
- }
- async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
- #[cfg(feature = "tokio_runtime")]
- {
- let mut file = File::open(file).await.ok()?;
- let mut contents = Vec::new();
- file.read_to_end(&mut contents).await.ok()?;
- Some(contents)
- }
- #[cfg(not(feature = "tokio_runtime"))]
- {
- None
- }
- }
- async fn read_file_to_string(&self, file: &str) -> Option<String> {
- #[cfg(feature = "tokio_runtime")]
- {
- let mut file = File::open(file).await.ok()?;
- let mut contents = String::new();
- file.read_to_string(&mut contents).await.ok()?;
- Some(contents)
- }
- #[cfg(not(feature = "tokio_runtime"))]
- {
- None
- }
- }
- async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
- #[cfg(feature = "tokio_runtime")]
- {
- let file = File::open(file).await.ok()?;
- Some(Box::new(file))
- }
- #[cfg(not(feature = "tokio_runtime"))]
- {
- None
- }
- }
- }
|