file_upload.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #![allow(unused)]
  2. use dioxus_html::{
  3. geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint},
  4. input_data::{MouseButton, MouseButtonSet},
  5. native_bind::NativeFileEngine,
  6. point_interaction::{
  7. InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
  8. },
  9. prelude::{SerializedMouseData, SerializedPointInteraction},
  10. FileEngine, HasDragData, HasFileData, HasFormData, HasMouseData,
  11. };
  12. use muda::accelerator::Modifiers;
  13. use serde::Deserialize;
  14. use std::{cell::Cell, path::PathBuf, rc::Rc, str::FromStr, sync::Arc};
  15. use wry::FileDropEvent;
  16. #[derive(Debug, Deserialize)]
  17. pub(crate) struct FileDialogRequest {
  18. #[serde(default)]
  19. accept: Option<String>,
  20. multiple: bool,
  21. directory: bool,
  22. pub event: String,
  23. pub target: usize,
  24. pub bubbles: bool,
  25. }
  26. #[allow(unused)]
  27. impl FileDialogRequest {
  28. #[cfg(not(any(
  29. target_os = "windows",
  30. target_os = "macos",
  31. target_os = "linux",
  32. target_os = "dragonfly",
  33. target_os = "freebsd",
  34. target_os = "netbsd",
  35. target_os = "openbsd"
  36. )))]
  37. pub(crate) fn get_file_event(&self) -> Vec<PathBuf> {
  38. vec![]
  39. }
  40. #[cfg(any(
  41. target_os = "windows",
  42. target_os = "macos",
  43. target_os = "linux",
  44. target_os = "dragonfly",
  45. target_os = "freebsd",
  46. target_os = "netbsd",
  47. target_os = "openbsd"
  48. ))]
  49. pub(crate) fn get_file_event(&self) -> Vec<PathBuf> {
  50. fn get_file_event_for_folder(
  51. request: &FileDialogRequest,
  52. dialog: rfd::FileDialog,
  53. ) -> Vec<PathBuf> {
  54. if request.multiple {
  55. dialog.pick_folders().into_iter().flatten().collect()
  56. } else {
  57. dialog.pick_folder().into_iter().collect()
  58. }
  59. }
  60. fn get_file_event_for_file(
  61. request: &FileDialogRequest,
  62. mut dialog: rfd::FileDialog,
  63. ) -> Vec<PathBuf> {
  64. let filters: Vec<_> = request
  65. .accept
  66. .as_deref()
  67. .unwrap_or_default()
  68. .split(',')
  69. .filter_map(|s| Filters::from_str(s).ok())
  70. .collect();
  71. let file_extensions: Vec<_> = filters
  72. .iter()
  73. .flat_map(|f| f.as_extensions().into_iter())
  74. .collect();
  75. dialog = dialog.add_filter("name", file_extensions.as_slice());
  76. let files: Vec<_> = if request.multiple {
  77. dialog.pick_files().into_iter().flatten().collect()
  78. } else {
  79. dialog.pick_file().into_iter().collect()
  80. };
  81. files
  82. }
  83. let dialog = rfd::FileDialog::new();
  84. if self.directory {
  85. get_file_event_for_folder(self, dialog)
  86. } else {
  87. get_file_event_for_file(self, dialog)
  88. }
  89. }
  90. }
  91. enum Filters {
  92. Extension(String),
  93. Mime(String),
  94. Audio,
  95. Video,
  96. Image,
  97. }
  98. impl Filters {
  99. fn as_extensions(&self) -> Vec<&str> {
  100. match self {
  101. Filters::Extension(extension) => vec![extension.as_str()],
  102. Filters::Mime(_) => vec![],
  103. Filters::Audio => vec!["mp3", "wav", "ogg"],
  104. Filters::Video => vec!["mp4", "webm"],
  105. Filters::Image => vec!["png", "jpg", "jpeg", "gif", "webp"],
  106. }
  107. }
  108. }
  109. impl FromStr for Filters {
  110. type Err = String;
  111. fn from_str(s: &str) -> Result<Self, Self::Err> {
  112. if let Some(extension) = s.strip_prefix('.') {
  113. Ok(Filters::Extension(extension.to_string()))
  114. } else {
  115. match s {
  116. "audio/*" => Ok(Filters::Audio),
  117. "video/*" => Ok(Filters::Video),
  118. "image/*" => Ok(Filters::Image),
  119. _ => Ok(Filters::Mime(s.to_string())),
  120. }
  121. }
  122. }
  123. }
  124. #[derive(Clone)]
  125. pub(crate) struct DesktopFileUploadForm {
  126. pub files: Arc<NativeFileEngine>,
  127. }
  128. impl HasFileData for DesktopFileUploadForm {
  129. fn files(&self) -> Option<Arc<dyn FileEngine>> {
  130. Some(self.files.clone())
  131. }
  132. }
  133. impl HasFormData for DesktopFileUploadForm {
  134. fn as_any(&self) -> &dyn std::any::Any {
  135. self
  136. }
  137. }
  138. #[derive(Default, Clone)]
  139. pub struct NativeFileHover {
  140. event: Rc<Cell<Option<FileDropEvent>>>,
  141. }
  142. impl NativeFileHover {
  143. pub fn set(&self, event: FileDropEvent) {
  144. self.event.set(Some(event));
  145. }
  146. pub fn current(&self) -> Option<FileDropEvent> {
  147. self.event.as_ref().clone().take()
  148. }
  149. }
  150. #[derive(Clone)]
  151. pub(crate) struct DesktopFileDragEvent {
  152. pub mouse: SerializedPointInteraction,
  153. pub files: Arc<NativeFileEngine>,
  154. }
  155. impl HasFileData for DesktopFileDragEvent {
  156. fn files(&self) -> Option<Arc<dyn FileEngine>> {
  157. Some(self.files.clone())
  158. }
  159. }
  160. impl HasDragData for DesktopFileDragEvent {
  161. fn as_any(&self) -> &dyn std::any::Any {
  162. self
  163. }
  164. }
  165. impl HasMouseData for DesktopFileDragEvent {
  166. fn as_any(&self) -> &dyn std::any::Any {
  167. self
  168. }
  169. }
  170. impl InteractionLocation for DesktopFileDragEvent {
  171. fn client_coordinates(&self) -> ClientPoint {
  172. self.mouse.client_coordinates()
  173. }
  174. fn page_coordinates(&self) -> PagePoint {
  175. self.mouse.page_coordinates()
  176. }
  177. fn screen_coordinates(&self) -> ScreenPoint {
  178. self.mouse.screen_coordinates()
  179. }
  180. }
  181. impl InteractionElementOffset for DesktopFileDragEvent {
  182. fn element_coordinates(&self) -> ElementPoint {
  183. self.mouse.element_coordinates()
  184. }
  185. fn coordinates(&self) -> Coordinates {
  186. self.mouse.coordinates()
  187. }
  188. }
  189. impl ModifiersInteraction for DesktopFileDragEvent {
  190. fn modifiers(&self) -> Modifiers {
  191. self.mouse.modifiers()
  192. }
  193. }
  194. impl PointerInteraction for DesktopFileDragEvent {
  195. fn held_buttons(&self) -> MouseButtonSet {
  196. self.mouse.held_buttons()
  197. }
  198. fn trigger_button(&self) -> Option<MouseButton> {
  199. self.mouse.trigger_button()
  200. }
  201. }