add file property to drag data

This commit is contained in:
Evan Almloff committed 2023-04-27 10:21:05 -05:00
1 parent c904411190
commit ada246c12e
11 files changed
+133 -77

No files matched your search

+25 -2
View File
@@ -2,10 +2,10 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() { fn main() {
dioxus_desktop::launch(App); dioxus_desktop::launch(app);
} }
fn App(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let files_uploaded: &UseRef<Vec<String>> = use_ref(cx, Vec::new); let files_uploaded: &UseRef<Vec<String>> = use_ref(cx, Vec::new);
cx.render(rsx! { cx.render(rsx! {
@@ -27,6 +27,29 @@ fn App(cx: Scope) -> Element {
} }
}, },
} }
div {
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() {
+1 -1
View File
@@ -87,7 +87,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(&Window, FileDropEvent) -> bool + 'static, handler: impl Fn(&Window, FileDropEvent) -> bool + 'static,
+4 -6
View File
@@ -49,14 +49,12 @@ pub fn build(
.with_custom_protocol(String::from("dioxus"), move |r| { .with_custom_protocol(String::from("dioxus"), move |r| {
protocol::desktop_handler(r, custom_head.clone(), index_file.clone(), &root_name) protocol::desktop_handler(r, custom_head.clone(), index_file.clone(), &root_name)
}) })
.with_file_drop_handler(move |window, evet| {
file_handler
.as_ref()
.map(|handler| handler(window, evet))
.unwrap_or_default()
})
.with_web_context(&mut web_context); .with_web_context(&mut web_context);
if let Some(handler) = file_handler {
webview = webview.with_file_drop_handler(handler)
}
#[cfg(windows)] #[cfg(windows)]
{ {
// Windows has a platform specific settings to disable the browser shortcut keys // Windows has a platform specific settings to disable the browser shortcut keys
+1
View File
@@ -41,6 +41,7 @@ features = [
"FocusEvent", "FocusEvent",
"CompositionEvent", "CompositionEvent",
"ClipboardEvent", "ClipboardEvent",
"DragEvent"
] ]
[dev-dependencies] [dev-dependencies]
+31 -3
View File
@@ -1,7 +1,9 @@
use dioxus_core::Event; use crate::FileEngine;
use crate::MouseData; use crate::MouseData;
use dioxus_core::Event;
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
@@ -9,12 +11,38 @@ pub type DragEvent = Event<DragData>;
/// (such as another DOM element). Applications are free to interpret a drag and drop interaction in an /// (such as another DOM element). Applications are free to interpret a drag and drop interaction in an
/// application-specific way. /// application-specific way.
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Clone)]
pub struct DragData { pub struct DragData {
/// Inherit mouse data /// Inherit mouse data
pub mouse: MouseData, pub mouse: MouseData,
#[cfg_attr(
feature = "serialize",
serde(
default,
skip_serializing,
deserialize_with = "crate::events::deserialize_file_engine"
)
)]
pub files: Option<std::sync::Arc<dyn FileEngine>>,
} }
impl Debug for DragData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DragData")
.field("mouse", &self.mouse)
.finish()
}
}
impl PartialEq for DragData {
fn eq(&self, other: &Self) -> bool {
self.mouse == other.mouse
}
}
impl Eq for DragData {}
impl_event! { impl_event! {
DragData; DragData;
+1 -1
View File
@@ -48,7 +48,7 @@ impl FileEngine for SerializedFileEngine {
} }
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
fn deserialize_file_engine<'de, D>( pub(crate) fn deserialize_file_engine<'de, D>(
deserializer: D, deserializer: D,
) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error> ) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error>
where where
+1
View File
@@ -123,6 +123,7 @@ impl From<&MouseEvent> for DragData {
fn from(value: &MouseEvent) -> Self { fn from(value: &MouseEvent) -> Self {
Self { Self {
mouse: MouseData::from(value), mouse: MouseData::from(value),
files: None,
} }
} }
} }
+56 -49
View File
@@ -351,7 +351,11 @@ class Interpreter {
let handler = (event) => { let handler = (event) => {
let target = event.target; let target = event.target;
if (target != null) { if (target != null) {
let realId = target.getAttribute(`data-dioxus-id`); const realId = find_real_id(target);
if (realId === null) {
return;
}
let shouldPreventDefault = target.getAttribute( let shouldPreventDefault = target.getAttribute(
`dioxus-prevent-default` `dioxus-prevent-default`
); );
@@ -379,22 +383,6 @@ class Interpreter {
event.preventDefault(); event.preventDefault();
} }
} }
// walk the tree to find the real element
while (realId == null) {
// we've reached the root we don't want to send an event
if (target.parentElement === null) {
return;
}
target = target.parentElement;
realId = target.getAttribute(`data-dioxus-id`);
}
shouldPreventDefault = target.getAttribute(
`dioxus-prevent-default`
);
let contents = serialize_event(event);
if (shouldPreventDefault === `on${event.type}`) { if (shouldPreventDefault === `on${event.type}`) {
event.preventDefault(); event.preventDefault();
@@ -404,41 +392,42 @@ class Interpreter {
event.preventDefault(); event.preventDefault();
} }
if ( serialize_event(event).then((contents) => {
target.tagName === "FORM" && if (
(event.type === "submit" || event.type === "input") target.tagName === "FORM" &&
) { (event.type === "submit" || event.type === "input")
for (let x = 0; x < target.elements.length; x++) { ) {
let element = target.elements[x]; for (let x = 0; x < target.elements.length; x++) {
let name = element.getAttribute("name"); let element = target.elements[x];
if (name != null) { let name = element.getAttribute("name");
if (element.getAttribute("type") === "checkbox") { if (name != null) {
// @ts-ignore if (element.getAttribute("type") === "checkbox") {
contents.values[name] = element.checked ? "true" : "false"; // @ts-ignore
} else if (element.getAttribute("type") === "radio") { contents.values[name] = element.checked
if (element.checked) { ? "true"
contents.values[name] = element.value; : "false";
} else if (element.getAttribute("type") === "radio") {
if (element.checked) {
contents.values[name] = element.value;
}
} else {
// @ts-ignore
contents.values[name] =
element.value ?? element.textContent;
} }
} else {
// @ts-ignore
contents.values[name] =
element.value ?? element.textContent;
} }
} }
} }
}
if (realId === null) { window.ipc.postMessage(
return; serializeIpcMessage("user_event", {
} name: edit.name,
window.ipc.postMessage( element: parseInt(realId),
serializeIpcMessage("user_event", { data: contents,
name: edit.name, bubbles,
element: parseInt(realId), })
data: contents, );
bubbles, });
})
);
} }
}; };
this.NewEventListener(edit.name, edit.id, bubbles, handler); this.NewEventListener(edit.name, edit.id, bubbles, handler);
@@ -575,7 +564,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":
@@ -661,7 +650,11 @@ 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":
@@ -816,6 +809,20 @@ function serialize_event(event) {
} }
} }
} }
async function serializeFileList(fileList) {
const file_contents = {};
for (let i = 0; i < fileList.length; i++) {
const file = fileList[i];
file_contents[file.name] = Array.from(
new Uint8Array(await file.arrayBuffer())
);
}
return {
files: file_contents,
};
}
function serializeIpcMessage(method, params = {}) { function serializeIpcMessage(method, params = {}) {
return JSON.stringify({ method, params }); return JSON.stringify({ method, params });
} }
+1 -13
View File
@@ -45,19 +45,7 @@ static INTERPRETER_JS: Lazy<String> = Lazy::new(|| {
const type = target.getAttribute("type"); const type = target.getAttribute("type");
if (type === "file") { if (type === "file") {
async function read_files() { async function read_files() {
const files = target.files; const file_engine=await serializeFileList( target.files);
const file_contents = {};
for (let i = 0; i < files.length; i++) {
const file = files[i];
file_contents[file.name] = Array.from(
new Uint8Array(await file.arrayBuffer())
);
}
let file_engine = {
files: file_contents,
};
contents.files = file_engine; contents.files = file_engine;
if (realId === null) { if (realId === null) {
+2 -1
View File
@@ -79,7 +79,8 @@ features = [
"console", "console",
"FileList", "FileList",
"File", "File",
"FileReader" "FileReader",
"DataTransfer"
] ]
[features] [features]
+10 -1
View File
@@ -244,8 +244,17 @@ pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -
} }
"drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
| "drop" => { | "drop" => {
let mut files = None;
if let Some(event) = event.dyn_ref::<web_sys::DragEvent>() {
if let Some(data) = event.data_transfer() {
if let Some(file_list) = data.files() {
files = WebFileEngine::new(file_list)
.map(|f| Arc::new(f) as Arc<dyn FileEngine>);
}
}
}
let mouse = MouseData::from(event); let mouse = MouseData::from(event);
Rc::new(DragData { mouse }) Rc::new(DragData { mouse, files })
} }
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture" "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"