add file property to drag data

This commit is contained in:
Evan Almloff 2023-04-27 10:21:05 -05:00
parent c904411190
commit ada246c12e
11 changed files with 133 additions and 77 deletions

View file

@ -2,10 +2,10 @@
use dioxus::prelude::*;
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);
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 {
for file in files_uploaded.read().iter() {

View file

@ -87,7 +87,7 @@ impl Config {
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(
mut self,
handler: impl Fn(&Window, FileDropEvent) -> bool + 'static,

View file

@ -49,14 +49,12 @@ pub fn build(
.with_custom_protocol(String::from("dioxus"), move |r| {
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);
if let Some(handler) = file_handler {
webview = webview.with_file_drop_handler(handler)
}
#[cfg(windows)]
{
// Windows has a platform specific settings to disable the browser shortcut keys

View file

@ -41,6 +41,7 @@ features = [
"FocusEvent",
"CompositionEvent",
"ClipboardEvent",
"DragEvent"
]
[dev-dependencies]

View file

@ -1,7 +1,9 @@
use dioxus_core::Event;
use crate::FileEngine;
use crate::MouseData;
use dioxus_core::Event;
use std::fmt::Debug;
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
@ -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
/// application-specific way.
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Clone)]
pub struct DragData {
/// Inherit mouse data
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! {
DragData;

View file

@ -48,7 +48,7 @@ impl FileEngine for SerializedFileEngine {
}
#[cfg(feature = "serialize")]
fn deserialize_file_engine<'de, D>(
pub(crate) fn deserialize_file_engine<'de, D>(
deserializer: D,
) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error>
where

View file

@ -123,6 +123,7 @@ impl From<&MouseEvent> for DragData {
fn from(value: &MouseEvent) -> Self {
Self {
mouse: MouseData::from(value),
files: None,
}
}
}

View file

@ -351,7 +351,11 @@ class Interpreter {
let handler = (event) => {
let target = event.target;
if (target != null) {
let realId = target.getAttribute(`data-dioxus-id`);
const realId = find_real_id(target);
if (realId === null) {
return;
}
let shouldPreventDefault = target.getAttribute(
`dioxus-prevent-default`
);
@ -379,22 +383,6 @@ class Interpreter {
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}`) {
event.preventDefault();
@ -404,41 +392,42 @@ class Interpreter {
event.preventDefault();
}
if (
target.tagName === "FORM" &&
(event.type === "submit" || event.type === "input")
) {
for (let x = 0; x < target.elements.length; x++) {
let element = target.elements[x];
let name = element.getAttribute("name");
if (name != null) {
if (element.getAttribute("type") === "checkbox") {
// @ts-ignore
contents.values[name] = element.checked ? "true" : "false";
} else if (element.getAttribute("type") === "radio") {
if (element.checked) {
contents.values[name] = element.value;
serialize_event(event).then((contents) => {
if (
target.tagName === "FORM" &&
(event.type === "submit" || event.type === "input")
) {
for (let x = 0; x < target.elements.length; x++) {
let element = target.elements[x];
let name = element.getAttribute("name");
if (name != null) {
if (element.getAttribute("type") === "checkbox") {
// @ts-ignore
contents.values[name] = element.checked
? "true"
: "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) {
return;
}
window.ipc.postMessage(
serializeIpcMessage("user_event", {
name: edit.name,
element: parseInt(realId),
data: contents,
bubbles,
})
);
window.ipc.postMessage(
serializeIpcMessage("user_event", {
name: edit.name,
element: parseInt(realId),
data: contents,
bubbles,
})
);
});
}
};
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) {
case "copy":
case "cut":
@ -661,7 +650,11 @@ function serialize_event(event) {
case "dragover":
case "dragstart":
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 "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 = {}) {
return JSON.stringify({ method, params });
}

View file

@ -45,19 +45,7 @@ static INTERPRETER_JS: Lazy<String> = Lazy::new(|| {
const type = target.getAttribute("type");
if (type === "file") {
async function read_files() {
const files = 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,
};
const file_engine=await serializeFileList( target.files);
contents.files = file_engine;
if (realId === null) {

View file

@ -79,7 +79,8 @@ features = [
"console",
"FileList",
"File",
"FileReader"
"FileReader",
"DataTransfer"
]
[features]

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"
| "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);
Rc::new(DragData { mouse })
Rc::new(DragData { mouse, files })
}
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"