Merge pull request #992 from ealmloff/add-file-data-drag-event
Add file property to drag data
This commit is contained in:
16 files changed
+294
-104
No files matched your search
+24
-3
@@ -39,9 +39,30 @@ fn App(cx: Scope) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
|
div {
|
||||||
div { "progress: {files_uploaded.read().len()}" },
|
width: "100px",
|
||||||
|
height: "100px",
|
||||||
|
border: "1px solid black",
|
||||||
|
prevent_default: "ondrop dragover dragenter",
|
||||||
|
ondrop: move |evt| {
|
||||||
|
to_owned![files_uploaded];
|
||||||
|
async move {
|
||||||
|
if let Some(file_engine) = &evt.files {
|
||||||
|
let files = file_engine.files();
|
||||||
|
for file_name in &files {
|
||||||
|
if let Some(file) = file_engine.read_file_to_string(file_name).await{
|
||||||
|
files_uploaded.write().push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ondragover: move |event: DragEvent| {
|
||||||
|
event.stop_propagation();
|
||||||
|
},
|
||||||
|
"Drop files here"
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
for file in files_uploaded.read().iter() {
|
for file in files_uploaded.read().iter() {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -12,7 +12,8 @@ use crate::{
|
|||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use dioxus_core::{Component, ElementId, VirtualDom};
|
use dioxus_core::{Component, ElementId, VirtualDom};
|
||||||
use dioxus_html::{
|
use dioxus_html::{
|
||||||
native_bind::NativeFileEngine, FileEngine, HasFormData, HtmlEvent, PlatformEventData,
|
native_bind::NativeFileEngine, FileEngine, HasFileData, HasFormData, HtmlEvent, MountedData,
|
||||||
|
PlatformEventData, SerializedHtmlEventConverter,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
@@ -279,11 +280,13 @@ impl<P: 'static> App<P> {
|
|||||||
files: Arc<NativeFileEngine>,
|
files: Arc<NativeFileEngine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasFormData for DesktopFileUploadForm {
|
impl HasFileData for DesktopFileUploadForm {
|
||||||
fn files(&self) -> Option<Arc<dyn FileEngine>> {
|
fn files(&self) -> Option<Arc<dyn FileEngine>> {
|
||||||
Some(self.files.clone())
|
Some(self.files.clone())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasFormData for DesktopFileUploadForm {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ impl Config {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a file drop handler
|
/// Set a file drop handler. If this is enabled, html drag events will be disabled.
|
||||||
pub fn with_file_drop_handler(
|
pub fn with_file_drop_handler(
|
||||||
mut self,
|
mut self,
|
||||||
handler: impl Fn(WindowId, FileDropEvent) -> bool + 'static,
|
handler: impl Fn(WindowId, FileDropEvent) -> bool + 'static,
|
||||||
|
|||||||
@@ -88,22 +88,18 @@ impl WebviewInstance {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let file_drop_handler = move |event| {
|
|
||||||
file_handler
|
|
||||||
.as_ref()
|
|
||||||
.map(|handler| handler(window_id, event))
|
|
||||||
.unwrap_or_default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut webview = WebViewBuilder::new(&window)
|
let mut webview = WebViewBuilder::new(&window)
|
||||||
.with_transparent(cfg.window.window.transparent)
|
.with_transparent(cfg.window.window.transparent)
|
||||||
.with_url("dioxus://index.html/")
|
.with_url("dioxus://index.html/")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_ipc_handler(ipc_handler)
|
.with_ipc_handler(ipc_handler)
|
||||||
.with_asynchronous_custom_protocol(String::from("dioxus"), request_handler)
|
.with_asynchronous_custom_protocol(String::from("dioxus"), request_handler)
|
||||||
.with_file_drop_handler(file_drop_handler)
|
|
||||||
.with_web_context(&mut web_context);
|
.with_web_context(&mut web_context);
|
||||||
|
|
||||||
|
if let Some(handler) = file_handler {
|
||||||
|
webview = webview.with_file_drop_handler(move |evt| handler(window_id, evt))
|
||||||
|
}
|
||||||
|
|
||||||
// This was removed from wry, I'm not sure what replaced it
|
// This was removed from wry, I'm not sure what replaced it
|
||||||
// #[cfg(windows)]
|
// #[cfg(windows)]
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ features = [
|
|||||||
"TouchList",
|
"TouchList",
|
||||||
"TouchEvent",
|
"TouchEvent",
|
||||||
"MouseEvent",
|
"MouseEvent",
|
||||||
|
"DragEvent",
|
||||||
"InputEvent",
|
"InputEvent",
|
||||||
"ClipboardEvent",
|
"ClipboardEvent",
|
||||||
"KeyboardEvent",
|
"KeyboardEvent",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::file_data::{FileEngine, HasFileData};
|
||||||
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
|
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
|
||||||
use crate::input_data::{MouseButton, MouseButtonSet};
|
use crate::input_data::{MouseButton, MouseButtonSet};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@@ -7,6 +8,8 @@ use keyboard_types::Modifiers;
|
|||||||
|
|
||||||
use crate::HasMouseData;
|
use crate::HasMouseData;
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub type DragEvent = Event<DragData>;
|
pub type DragEvent = Event<DragData>;
|
||||||
|
|
||||||
/// The DragEvent interface is a DOM event that represents a drag and drop interaction. The user initiates a drag by
|
/// The DragEvent interface is a DOM event that represents a drag and drop interaction. The user initiates a drag by
|
||||||
@@ -53,7 +56,13 @@ impl DragData {
|
|||||||
|
|
||||||
/// Downcast this event data to a specific type
|
/// Downcast this event data to a specific type
|
||||||
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
||||||
self.inner.as_any().downcast_ref::<T>()
|
HasDragData::as_any(&*self.inner).downcast_ref::<T>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasFileData for DragData {
|
||||||
|
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||||
|
self.inner.files()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,19 +112,34 @@ impl PointerInteraction for DragData {
|
|||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct SerializedDragData {
|
pub struct SerializedDragData {
|
||||||
mouse: crate::point_interaction::SerializedPointInteraction,
|
mouse: crate::point_interaction::SerializedPointInteraction,
|
||||||
|
files: Option<crate::file_data::SerializedFileEngine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
impl From<&DragData> for SerializedDragData {
|
impl SerializedDragData {
|
||||||
fn from(data: &DragData) -> Self {
|
fn new(drag: &DragData, files: Option<crate::file_data::SerializedFileEngine>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mouse: crate::point_interaction::SerializedPointInteraction::from(data),
|
mouse: crate::point_interaction::SerializedPointInteraction::from(drag),
|
||||||
|
files,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
impl HasDragData for SerializedDragData {}
|
impl HasDragData for SerializedDragData {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl HasFileData for SerializedDragData {
|
||||||
|
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||||
|
self.files
|
||||||
|
.as_ref()
|
||||||
|
.map(|files| std::sync::Arc::new(files.clone()) as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
impl HasMouseData for SerializedDragData {
|
impl HasMouseData for SerializedDragData {
|
||||||
@@ -171,7 +195,7 @@ impl PointerInteraction for SerializedDragData {
|
|||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
impl serde::Serialize for DragData {
|
impl serde::Serialize for DragData {
|
||||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
SerializedDragData::from(self).serialize(serializer)
|
SerializedDragData::new(self, None).serialize(serializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +210,10 @@ impl<'de> serde::Deserialize<'de> for DragData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for any object that has the data for a drag event
|
/// A trait for any object that has the data for a drag event
|
||||||
pub trait HasDragData: HasMouseData {}
|
pub trait HasDragData: HasMouseData + HasFileData {
|
||||||
|
/// return self as Any
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any;
|
||||||
|
}
|
||||||
|
|
||||||
impl_event! {
|
impl_event! {
|
||||||
DragData;
|
DragData;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use std::{any::Any, collections::HashMap, fmt::Debug};
|
use crate::file_data::FileEngine;
|
||||||
|
use crate::file_data::HasFileData;
|
||||||
|
use std::{collections::HashMap, fmt::Debug};
|
||||||
|
|
||||||
use dioxus_core::Event;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ impl FormData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An object that has all the data for a form event
|
/// An object that has all the data for a form event
|
||||||
pub trait HasFormData: std::any::Any {
|
pub trait HasFormData: HasFileData + std::any::Any {
|
||||||
fn value(&self) -> String {
|
fn value(&self) -> String {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
@@ -82,10 +84,6 @@ pub trait HasFormData: std::any::Any {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return self as Any
|
/// return self as Any
|
||||||
fn as_any(&self) -> &dyn std::any::Any;
|
fn as_any(&self) -> &dyn std::any::Any;
|
||||||
}
|
}
|
||||||
@@ -120,7 +118,7 @@ impl FormData {
|
|||||||
pub struct SerializedFormData {
|
pub struct SerializedFormData {
|
||||||
value: String,
|
value: String,
|
||||||
values: HashMap<String, FormValue>,
|
values: HashMap<String, FormValue>,
|
||||||
files: Option<SerializedFileEngine>,
|
files: Option<crate::file_data::SerializedFileEngine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
@@ -129,7 +127,7 @@ impl SerializedFormData {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
value: String,
|
value: String,
|
||||||
values: HashMap<String, FormValue>,
|
values: HashMap<String, FormValue>,
|
||||||
files: Option<SerializedFileEngine>,
|
files: Option<crate::file_data::SerializedFileEngine>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
value,
|
value,
|
||||||
@@ -152,7 +150,7 @@ impl SerializedFormData {
|
|||||||
resolved_files.insert(file, bytes.unwrap_or_default());
|
resolved_files.insert(file, bytes.unwrap_or_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(SerializedFileEngine {
|
Some(crate::file_data::SerializedFileEngine {
|
||||||
files: resolved_files,
|
files: resolved_files,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -180,15 +178,18 @@ impl HasFormData for SerializedFormData {
|
|||||||
self.values.clone()
|
self.values.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl HasFileData for SerializedFormData {
|
||||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||||
self.files
|
self.files
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|files| std::sync::Arc::new(files.clone()) as _)
|
.map(|files| std::sync::Arc::new(files.clone()) as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
@@ -208,52 +209,6 @@ impl<'de> serde::Deserialize<'de> for FormData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
/// A file engine that serializes files to bytes
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
|
||||||
pub struct SerializedFileEngine {
|
|
||||||
files: HashMap<String, Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
impl FileEngine for SerializedFileEngine {
|
|
||||||
fn files(&self) -> Vec<String> {
|
|
||||||
self.files.keys().cloned().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
|
||||||
self.files.get(file).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
|
||||||
self.read_file(file)
|
|
||||||
.await
|
|
||||||
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
|
||||||
self.read_file(file)
|
|
||||||
.await
|
|
||||||
.map(|val| Box::new(val) as Box<dyn Any>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
pub trait FileEngine {
|
|
||||||
// get a list of file names
|
|
||||||
fn files(&self) -> Vec<String>;
|
|
||||||
|
|
||||||
// read a file to bytes
|
|
||||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>>;
|
|
||||||
|
|
||||||
// read a file to string
|
|
||||||
async fn read_file_to_string(&self, file: &str) -> Option<String>;
|
|
||||||
|
|
||||||
// returns a file in platform's native representation
|
|
||||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_event! {
|
impl_event! {
|
||||||
FormData;
|
FormData;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
use std::{any::Any, collections::HashMap};
|
||||||
|
|
||||||
|
pub trait HasFileData: std::any::Any {
|
||||||
|
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
/// A file engine that serializes files to bytes
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||||
|
pub struct SerializedFileEngine {
|
||||||
|
pub files: HashMap<String, Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl FileEngine for SerializedFileEngine {
|
||||||
|
fn files(&self) -> Vec<String> {
|
||||||
|
self.files.keys().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
||||||
|
self.files.get(file).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
||||||
|
self.read_file(file)
|
||||||
|
.await
|
||||||
|
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
||||||
|
self.read_file(file)
|
||||||
|
.await
|
||||||
|
.map(|val| Box::new(val) as Box<dyn Any>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
pub trait FileEngine {
|
||||||
|
// get a list of file names
|
||||||
|
fn files(&self) -> Vec<String>;
|
||||||
|
|
||||||
|
// read a file to bytes
|
||||||
|
async fn read_file(&self, file: &str) -> Option<Vec<u8>>;
|
||||||
|
|
||||||
|
// read a file to string
|
||||||
|
async fn read_file_to_string(&self, file: &str) -> Option<String>;
|
||||||
|
|
||||||
|
// returns a file in platform's native representation
|
||||||
|
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>>;
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@ pub use elements::HtmlCtx;
|
|||||||
#[cfg(feature = "html-to-rsx")]
|
#[cfg(feature = "html-to-rsx")]
|
||||||
pub use elements::{map_html_attribute_to_rsx, map_html_element_to_rsx};
|
pub use elements::{map_html_attribute_to_rsx, map_html_element_to_rsx};
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub(crate) mod file_data;
|
||||||
|
pub use file_data::*;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
mod global_attributes;
|
mod global_attributes;
|
||||||
pub mod input_data;
|
pub mod input_data;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::FileEngine;
|
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
|
use crate::file_data::FileEngine;
|
||||||
|
|
||||||
pub struct NativeFileEngine {
|
pub struct NativeFileEngine {
|
||||||
files: Vec<PathBuf>,
|
files: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::events::{
|
|||||||
AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
|
AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
|
||||||
TransitionData, WheelData,
|
TransitionData, WheelData,
|
||||||
};
|
};
|
||||||
|
use crate::file_data::{FileEngine, HasFileData};
|
||||||
use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint};
|
use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint};
|
||||||
use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
|
use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@@ -103,8 +104,6 @@ impl ModifiersInteraction for KeyboardEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasDragData for MouseEvent {}
|
|
||||||
|
|
||||||
impl InteractionLocation for MouseEvent {
|
impl InteractionLocation for MouseEvent {
|
||||||
fn client_coordinates(&self) -> ClientPoint {
|
fn client_coordinates(&self) -> ClientPoint {
|
||||||
ClientPoint::new(self.client_x().into(), self.client_y().into())
|
ClientPoint::new(self.client_x().into(), self.client_y().into())
|
||||||
@@ -162,6 +161,14 @@ impl HasMouseData for MouseEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasFileData for MouseEvent {}
|
||||||
|
|
||||||
|
impl HasDragData for MouseEvent {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ModifiersInteraction for TouchEvent {
|
impl ModifiersInteraction for TouchEvent {
|
||||||
fn modifiers(&self) -> Modifiers {
|
fn modifiers(&self) -> Modifiers {
|
||||||
let mut modifiers = Modifiers::empty();
|
let mut modifiers = Modifiers::empty();
|
||||||
@@ -512,3 +519,23 @@ impl HasMediaData for web_sys::Event {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasFileData for web_sys::Event {
|
||||||
|
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||||
|
#[cfg(not(feature = "file_engine"))]
|
||||||
|
let files = None;
|
||||||
|
#[cfg(feature = "file_engine")]
|
||||||
|
let files = element
|
||||||
|
.dyn_ref()
|
||||||
|
.and_then(|input: &web_sys::HtmlInputElement| {
|
||||||
|
input.files().and_then(|files| {
|
||||||
|
#[allow(clippy::arc_with_non_send_sync)]
|
||||||
|
crate::file_engine::WebFileEngine::new(files).map(|f| {
|
||||||
|
std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
files
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ class InterpreterConfig {
|
|||||||
|
|
||||||
// this handler is only provided on the desktop and liveview implementations since this
|
// this handler is only provided on the desktop and liveview implementations since this
|
||||||
// method is not used by the web implementation
|
// method is not used by the web implementation
|
||||||
function handler(event, name, bubbles, config) {
|
async function handler(event, name, bubbles, config) {
|
||||||
let target = event.target;
|
let target = event.target;
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
let preventDefaultRequests = null;
|
let preventDefaultRequests = null;
|
||||||
@@ -61,7 +61,7 @@ function handler(event, name, bubbles, config) {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
let contents = serialize_event(event);
|
let contents = await serialize_event(event);
|
||||||
|
|
||||||
// TODO: this should be liveview only
|
// TODO: this should be liveview only
|
||||||
if (
|
if (
|
||||||
@@ -112,14 +112,14 @@ function handler(event, name, bubbles, config) {
|
|||||||
const fieldType = target.elements[name].type;
|
const fieldType = target.elements[name].type;
|
||||||
|
|
||||||
switch (fieldType) {
|
switch (fieldType) {
|
||||||
case "select-multiple":
|
case "select-multiple":
|
||||||
contents.values[name] = formData.getAll(name);
|
contents.values[name] = formData.getAll(name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// add cases for fieldTypes that can hold multiple values here
|
// add cases for fieldTypes that can hold multiple values here
|
||||||
default:
|
default:
|
||||||
contents.values[name] = formData.get(name);
|
contents.values[name] = formData.get(name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,7 +331,7 @@ function get_mouse_data(event) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function serialize_event(event) {
|
async function serialize_event(event) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "copy":
|
case "copy":
|
||||||
case "cut":
|
case "cut":
|
||||||
@@ -417,7 +417,12 @@ function serialize_event(event) {
|
|||||||
case "dragover":
|
case "dragover":
|
||||||
case "dragstart":
|
case "dragstart":
|
||||||
case "drop": {
|
case "drop": {
|
||||||
return { mouse: get_mouse_data(event) };
|
let files = null;
|
||||||
|
if (event.dataTransfer && event.dataTransfer.files) {
|
||||||
|
files = await serializeFileList(event.dataTransfer.files);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { mouse: get_mouse_data(event), files };
|
||||||
}
|
}
|
||||||
case "click":
|
case "click":
|
||||||
case "contextmenu":
|
case "contextmenu":
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use crossterm::event::{
|
|||||||
MouseEventKind,
|
MouseEventKind,
|
||||||
};
|
};
|
||||||
use dioxus_html::{
|
use dioxus_html::{
|
||||||
HasFormData, HasKeyboardData, HasWheelData, SerializedFocusData, SerializedKeyboardData,
|
HasFileData, HasFormData, HasKeyboardData, HasWheelData, SerializedFocusData,
|
||||||
SerializedMouseData, SerializedWheelData,
|
SerializedKeyboardData, SerializedMouseData, SerializedWheelData,
|
||||||
};
|
};
|
||||||
use dioxus_native_core::prelude::*;
|
use dioxus_native_core::prelude::*;
|
||||||
use dioxus_native_core::real_dom::NodeImmutable;
|
use dioxus_native_core::real_dom::NodeImmutable;
|
||||||
@@ -86,6 +86,8 @@ impl HasFormData for FormData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasFileData for FormData {}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Files {
|
pub struct Files {
|
||||||
files: FxHashMap<String, File>,
|
files: FxHashMap<String, File>,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ features = [
|
|||||||
"HtmlFormElement",
|
"HtmlFormElement",
|
||||||
"Text",
|
"Text",
|
||||||
"Window",
|
"Window",
|
||||||
"console",
|
"DataTransfer",
|
||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
+103
-7
@@ -1,12 +1,16 @@
|
|||||||
use std::{any::Any, collections::HashMap};
|
use std::{any::Any, collections::HashMap};
|
||||||
|
|
||||||
use dioxus_html::{
|
use dioxus_html::{
|
||||||
prelude::FormValue, FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter,
|
point_interaction::{
|
||||||
ImageData, MountedData, PlatformEventData, ScrollData,
|
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
|
||||||
|
},
|
||||||
|
prelude::FormValue,
|
||||||
|
DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData,
|
||||||
|
HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
|
||||||
};
|
};
|
||||||
use js_sys::Array;
|
use js_sys::Array;
|
||||||
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
|
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
|
||||||
use web_sys::{Document, Element, Event};
|
use web_sys::{Document, Element, Event, MouseEvent};
|
||||||
|
|
||||||
pub(crate) struct WebEventConverter;
|
pub(crate) struct WebEventConverter;
|
||||||
|
|
||||||
@@ -44,7 +48,11 @@ impl HtmlEventConverter for WebEventConverter {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
|
fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
|
||||||
downcast_event(event).raw.clone().into()
|
let event = downcast_event(event);
|
||||||
|
DragData::new(WebDragData::new(
|
||||||
|
event.element.clone(),
|
||||||
|
event.raw.clone().unchecked_into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -180,8 +188,10 @@ impl WebEventExt<web_sys::CompositionEvent> for dioxus_html::CompositionData {
|
|||||||
|
|
||||||
impl WebEventExt<web_sys::MouseEvent> for dioxus_html::DragData {
|
impl WebEventExt<web_sys::MouseEvent> for dioxus_html::DragData {
|
||||||
fn web_event(&self) -> &web_sys::MouseEvent {
|
fn web_event(&self) -> &web_sys::MouseEvent {
|
||||||
self.downcast::<web_sys::MouseEvent>()
|
&self
|
||||||
|
.downcast::<WebDragData>()
|
||||||
.expect("event should be a WebMouseEvent")
|
.expect("event should be a WebMouseEvent")
|
||||||
|
.raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +413,12 @@ impl HasFormData for WebFormData {
|
|||||||
values
|
values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
&self.raw as &dyn Any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasFileData for WebFormData {
|
||||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||||
#[cfg(not(feature = "file_engine"))]
|
#[cfg(not(feature = "file_engine"))]
|
||||||
let files = None;
|
let files = None;
|
||||||
@@ -421,9 +437,89 @@ impl HasFormData for WebFormData {
|
|||||||
|
|
||||||
files
|
files
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
struct WebDragData {
|
||||||
&self.raw as &dyn Any
|
element: Element,
|
||||||
|
raw: MouseEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebDragData {
|
||||||
|
fn new(element: Element, raw: MouseEvent) -> Self {
|
||||||
|
Self { element, raw }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasDragData for WebDragData {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
&self.raw as &dyn std::any::Any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasMouseData for WebDragData {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
&self.raw as &dyn std::any::Any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerInteraction for WebDragData {
|
||||||
|
fn trigger_button(&self) -> Option<dioxus_html::input_data::MouseButton> {
|
||||||
|
self.raw.trigger_button()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet {
|
||||||
|
self.raw.held_buttons()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModifiersInteraction for WebDragData {
|
||||||
|
fn modifiers(&self) -> dioxus_html::prelude::Modifiers {
|
||||||
|
self.raw.modifiers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractionElementOffset for WebDragData {
|
||||||
|
fn coordinates(&self) -> dioxus_html::geometry::Coordinates {
|
||||||
|
self.raw.coordinates()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint {
|
||||||
|
self.raw.element_coordinates()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractionLocation for WebDragData {
|
||||||
|
fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint {
|
||||||
|
self.raw.client_coordinates()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint {
|
||||||
|
self.raw.screen_coordinates()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint {
|
||||||
|
self.raw.page_coordinates()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasFileData for WebDragData {
|
||||||
|
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||||
|
#[cfg(not(feature = "file_engine"))]
|
||||||
|
let files = None;
|
||||||
|
#[cfg(feature = "file_engine")]
|
||||||
|
let files = self
|
||||||
|
.element
|
||||||
|
.dyn_ref()
|
||||||
|
.and_then(|input: &web_sys::HtmlInputElement| {
|
||||||
|
input.files().and_then(|files| {
|
||||||
|
#[allow(clippy::arc_with_non_send_sync)]
|
||||||
|
crate::file_engine::WebFileEngine::new(files).map(|f| {
|
||||||
|
std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user