* remove native and web features from dioxus-html * Fix downcasting web events * Fix desktop with tokio disabled
320 lines
7.8 KiB
Rust
320 lines
7.8 KiB
Rust
#![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
|
|
}
|
|
}
|
|
}
|