Skip to content

Commit

Permalink
Add file drag and drop in wasm
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadowMitia committed Jan 1, 2021
1 parent 38fcceb commit 3690eba
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 6 deletions.
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,29 @@ features = [
"AddEventListenerOptions",
'CssStyleDeclaration',
'BeforeUnloadEvent',
'Blob',
'Document',
'DataTransfer',
'DomRect',
'DragEvent',
'DataTransferItemList',
'DataTransferItem',
'Element',
'Event',
'EventTarget',
'FileReader',
'File',
'FocusEvent',
'HtmlCanvasElement',
'HtmlElement',
'HtmlImageElement',
'KeyboardEvent',
'MediaQueryList',
'MediaQueryListEvent',
'MouseEvent',
'Node',
'PointerEvent',
'Url',
'Window',
'WheelEvent'
]
Expand Down
61 changes: 60 additions & 1 deletion examples/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,66 @@ pub fn main() {
*control_flow = ControlFlow::Wait;

#[cfg(feature = "web-sys")]
log::debug!("{:?}", event);
{
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use web_sys::{FileReader, HtmlImageElement, Url};

log::debug!("{:?}", event);
match &event {
Event::WindowEvent {
window_id,
event: WindowEvent::DroppedFile(file),
} => {
// let reader = FileReader::new().unwrap();
// let f = Closure::wrap(Box::new(move |event: web_sys::Event| {
// let reader: FileReader = event.target().unwrap().dyn_into().unwrap();
// let file = reader.result().unwrap();
// log::debug!("dropped filed {:?}", file);

// let window = web_sys::window().unwrap();
// let document = window.document().unwrap();
// let body = document.body().unwrap();

// let img = document.create_element("img").unwrap();
// img.set_src(Url::create_object_url_with_blob(file));
// img.set_height(60);
// img.set_onload(|| {
// Url::revoke_object_url(img.src());

// });

// body.append_child(&img)
// .expect("Append canvas to HTML body");

// }) as Box<dyn FnMut(_)>);
// reader.set_onload(Some(f.as_ref().unchecked_ref()));
// reader.read_as_array_buffer(&file.slice().unwrap());
// f.forget();

let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();

let img = HtmlImageElement::new().unwrap();
img.set_src(&Url::create_object_url_with_blob(file).unwrap());
img.set_height(60);

let f = Closure::wrap(Box::new(|event: web_sys::Event| {
let img: HtmlImageElement = event.target().unwrap().dyn_into().unwrap();
Url::revoke_object_url(&img.src());
}) as Box<dyn FnMut(_)>);

img.set_onload(Some(f.as_ref().unchecked_ref()));

f.forget();

body.append_child(&img).expect("Append img to body");
}

_ => {}
}
}

#[cfg(feature = "stdweb")]
std_web::console!(log, "%s", format!("{:?}", event));
Expand Down
6 changes: 6 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,19 @@ pub enum WindowEvent<'a> {
///
/// When the user drops multiple files at once, this event will be emitted for each file
/// separately.
#[cfg(not(feature = "web-sys"))]
DroppedFile(PathBuf),
#[cfg(feature = "web-sys")]
DroppedFile(web_sys::File),

/// A file is being hovered over the window.
///
/// When the user hovers multiple files at once, this event will be emitted for each file
/// separately.
#[cfg(not(feature = "web-sys"))]
HoveredFile(PathBuf),
#[cfg(feature = "web-sys")]
HoveredFile(web_sys::File),

/// A file was hovered, but has exited the window.
///
Expand Down
28 changes: 28 additions & 0 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::window::{Theme, WindowId};
use std::cell::RefCell;
use std::clone::Clone;
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
use std::path::PathBuf;
use std::rc::Rc;

pub struct WindowTarget<T: 'static> {
Expand Down Expand Up @@ -193,6 +194,33 @@ impl<T> WindowTarget<T> {
});
});

let runner = self.runner.clone();
canvas.on_file_drag_enter(move |file| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::HoveredFile(file),
});
});

let runner = self.runner.clone();
canvas.on_file_drag_exit(move || {
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::HoveredFileCancelled,
});
});

let runner = self.runner.clone();
canvas.on_file_drop(move |file| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::DroppedFile(file),
});
});

let runner = self.runner.clone();
canvas.on_file_drag_over(move || {});

let runner = self.runner.clone();
let raw = canvas.raw().clone();

Expand Down
91 changes: 86 additions & 5 deletions src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::rc::Rc;

use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{
AddEventListenerOptions, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent,
AddEventListenerOptions, DragEvent, Event, File, FocusEvent, HtmlCanvasElement, KeyboardEvent,
MediaQueryListEvent, MouseEvent, WheelEvent,
};

Expand All @@ -27,6 +27,10 @@ pub struct Canvas {
on_received_character: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
on_mouse_wheel: Option<EventListenerHandle<dyn FnMut(WheelEvent)>>,
on_fullscreen_change: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_file_drag_enter: Option<EventListenerHandle<dyn FnMut(DragEvent)>>,
on_file_drag_exit: Option<EventListenerHandle<dyn FnMut(DragEvent)>>,
on_file_drop: Option<EventListenerHandle<dyn FnMut(DragEvent)>>,
on_file_drag_over: Option<EventListenerHandle<dyn FnMut(DragEvent)>>,
on_dark_mode: Option<MediaQueryListHandle>,
mouse_state: MouseState,
}
Expand Down Expand Up @@ -71,11 +75,13 @@ impl Canvas {
MouseState::NoPointerEvent(mouse_handler::MouseHandler::new())
};

let common = Common {
raw: canvas,
wants_fullscreen: Rc::new(RefCell::new(false)),
};

Ok(Canvas {
common: Common {
raw: canvas,
wants_fullscreen: Rc::new(RefCell::new(false)),
},
common,
on_blur: None,
on_focus: None,
on_keyboard_release: None,
Expand All @@ -84,6 +90,10 @@ impl Canvas {
on_mouse_wheel: None,
on_fullscreen_change: None,
on_dark_mode: None,
on_file_drag_enter: None,
on_file_drag_exit: None,
on_file_drop: None,
on_file_drag_over: None,
mouse_state,
})
}
Expand Down Expand Up @@ -280,6 +290,73 @@ impl Canvas {
self.on_dark_mode = MediaQueryListHandle::new("(prefers-color-scheme: dark)", closure);
}

pub fn on_file_drag_enter<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(File),
{
self.on_file_drag_enter =
Some(self.common.add_event("dragenter", move |event: DragEvent| {
event.prevent_default();
event.stop_propagation();
event.cancel_bubble();

let files = event.data_transfer().unwrap().items();
for i in 0..files.length() {

let file = files.get(i).unwrap();
if file.kind() == "file" {
handler(file.get_as_file().unwrap().unwrap());
}

}
}));
}

pub fn on_file_drag_exit<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(),
{
self.on_file_drag_exit =
Some(self.common.add_event("dragleave", move |event: DragEvent| {
event.prevent_default();
event.stop_propagation();
event.cancel_bubble();
handler();
}));
}

pub fn on_file_drop<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(File),
{
self.on_file_drop = Some(self.common.add_event("drop", move |event: DragEvent| {
event.prevent_default();
event.stop_propagation();
event.cancel_bubble();

let files = event.data_transfer().unwrap().items();

for i in 0..files.length() {
let file = files.get(i).unwrap();
if file.kind() == "file" {
handler(file.get_as_file().unwrap().unwrap());
}
}


}));
}

pub fn on_file_drag_over<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(),
{
self.on_file_drag_over =
Some(self.common.add_event("dragover", move |event: DragEvent| {
event.prevent_default();
}));
}

pub fn request_fullscreen(&self) {
self.common.request_fullscreen()
}
Expand All @@ -297,6 +374,10 @@ impl Canvas {
self.on_mouse_wheel = None;
self.on_fullscreen_change = None;
self.on_dark_mode = None;
self.on_file_drag_enter = None;
self.on_file_drag_exit = None;
self.on_file_drop = None;
self.on_file_drag_over = None;
match &mut self.mouse_state {
MouseState::HasPointerEvent(h) => h.remove_listeners(),
MouseState::NoPointerEvent(h) => h.remove_listeners(),
Expand Down

0 comments on commit 3690eba

Please sign in to comment.