Fix wrong placement of nodes created from dropped files. (#5906)

Right now, we use the cursor position to determine the target position for dropped items. However, it seems that during dragging of files, we do not always receive mouse events, thus cannot update the cursor position. To avoid this, this PR refactors the functionality to use the location of the drop event, instead of the last known cursor position.

Fixes #5237.

https://user-images.githubusercontent.com/1428930/224735951-9cd6ff62-a749-4ff3-8437-c0bee3c0dd05.mp4
This commit is contained in:
Michael Mauderer 2023-03-17 20:52:13 +00:00 committed by GitHub
parent b46be10f63
commit 7a40ccc3ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 13 deletions

2
Cargo.lock generated
View File

@ -2707,7 +2707,9 @@ version = "0.1.0"
dependencies = [
"enso-frp",
"enso-prelude",
"enso-types",
"enso-web",
"ensogl-core",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",

View File

@ -1756,7 +1756,8 @@ impl GraphEditorModel {
let profiling_statuses = profiling::Statuses::new();
let profiling_button = component::profiling::Button::new(&app);
let add_node_button = Rc::new(component::add_node_button::AddNodeButton::new(&app));
let drop_manager = ensogl_drop_manager::Manager::new(&scene.dom.root);
let drop_manager =
ensogl_drop_manager::Manager::new(&scene.dom.root.clone_ref().into(), scene);
let styles_frp = StyleWatchFrp::new(&scene.style_sheet);
let selection_controller =
selection::Controller::new(&frp, &app.cursor, &scene.mouse.frp, &touch_state, &nodes);
@ -3752,12 +3753,14 @@ fn new_graph_editor(app: &Application) -> GraphEditor {
let default_gap = model.styles_frp.get_number_or(gap_path, 0.0);
let files_received = model.drop_manager.files_received().clone_ref();
frp::extend! { network
files_with_positions <- files_received.map3(&cursor_pos_in_scene,&default_gap,
|files,cursor_pos,default_gap| {
files_with_positions <- files_received.map2(&default_gap,
move |drop_event_data,default_gap| {
let files = &drop_event_data.files;
let drop_posititon = drop_event_data.position;
let single_offset = default_gap + node::HEIGHT;
files.iter().enumerate().map(|(index,file)| {
let offset = Vector2(0.0, single_offset * index as f32);
(file.clone_ref(),cursor_pos+offset)
(file.clone_ref(), drop_posititon + offset)
}).collect_vec()
}
);

View File

@ -5,6 +5,8 @@ authors = ["Enso Team <contact@enso.org>"]
edition = "2021"
[dependencies]
ensogl-core = { path = "../../core" }
enso-types = { path = "../../../types" }
enso-web = { path = "../../../web" }
enso-frp = { path = "../../../frp" }
enso-prelude = { path = "../../../prelude" }

View File

@ -21,6 +21,7 @@
/// Commonly used utilities.
pub mod prelude {
pub use enso_prelude::*;
pub use enso_types::*;
}
use crate::prelude::*;
@ -30,6 +31,8 @@ use enso_web as web;
use enso_web::stream::BlobExt;
use enso_web::stream::ReadableStreamDefaultReader;
use enso_web::Closure;
use ensogl_core::display::scene::Scene;
use ensogl_core::system::web::dom::WithKnownShape;
#[cfg(target_arch = "wasm32")]
use enso_web::JsCast;
@ -113,6 +116,15 @@ impl File {
type DropClosure = Closure<dyn Fn(web_sys::DragEvent)>;
type DragOverClosure = Closure<dyn Fn(web_sys::DragEvent) -> bool>;
#[derive(Clone, Debug, Default)]
/// The data emitted by the `files_received` frp endpoint.
pub struct DropEventData {
/// The position of the drop event in the scene coordinates.
pub position: Vector2,
/// The dropped files.
pub files: Vec<File>,
}
/// The Manager of dropped files.
///
/// It adds listeners for drag and drop events to the target passed during construction. It provides
@ -122,7 +134,7 @@ type DragOverClosure = Closure<dyn Fn(web_sys::DragEvent) -> bool>;
pub struct Manager {
#[allow(dead_code)]
network: frp::Network,
files_received: frp::Source<Vec<File>>,
files_received: frp::Source<DropEventData>,
#[allow(dead_code)]
drop_handle: web::EventListenerHandle,
#[allow(dead_code)]
@ -131,17 +143,18 @@ pub struct Manager {
impl Manager {
/// Constructor, adding listener to the given target.
pub fn new(target: &enso_web::EventTarget) -> Self {
pub fn new(dom: &WithKnownShape<web::EventTarget>, scene: &Scene) -> Self {
debug!("Creating.");
let network = frp::Network::new("DropFileManager");
frp::extend! { network
files_received <- source();
}
let drop: DropClosure = Closure::new(f!([files_received](event:web_sys::DragEvent) {
let target: &web::EventTarget = dom.deref();
let drop: DropClosure = Closure::new(f!([files_received,scene](event:web_sys::DragEvent) {
debug!("Dropped files.");
event.prevent_default();
Self::handle_drop_event(event,&files_received)
Self::handle_drop_event(event, &files_received, &scene);
}));
// To mark element as a valid drop target, the `dragover` event handler should return
// `false`. See
@ -156,11 +169,28 @@ impl Manager {
}
/// The frp endpoint emitting signal when a file is dropped.
pub fn files_received(&self) -> &frp::Source<Vec<File>> {
pub fn files_received(&self) -> &frp::Source<DropEventData> {
&self.files_received
}
fn handle_drop_event(event: web_sys::DragEvent, files_received: &frp::Source<Vec<File>>) {
/// Retrieve the position of the drop event in the scene coordinates.
fn event_position(scene: &Scene, event: &web_sys::DragEvent) -> Vector2 {
let dom: WithKnownShape<web::EventTarget> = scene.dom.root.clone_ref().into();
let shape = dom.shape.value();
let base = Vector2::new(0.0, shape.height);
let position = Vector2::new(event.client_x() as f32, -event.client_y() as f32);
let position = base + position;
let center = scene.frp.shape.value().center();
let position = position - center;
scene.screen_to_scene_coordinates(Vector3::new(position.x, position.y, 0.0)).xy()
}
fn handle_drop_event(
event: web_sys::DragEvent,
files_received: &frp::Source<DropEventData>,
scene: &Scene,
) {
let position = Self::event_position(scene, &event);
let opt_files = event.data_transfer().and_then(|t| t.files());
if let Some(js_files) = opt_files {
let js_files_iter = (0..js_files.length()).filter_map(|i| js_files.get(i));
@ -171,7 +201,8 @@ impl Manager {
None
}
});
files_received.emit(files_iter.collect_vec());
let data = DropEventData { position, files: files_iter.collect_vec() };
files_received.emit(data);
}
}
}

View File

@ -57,11 +57,14 @@ fn download_file(file: ensogl_drop_manager::File) {
#[allow(dead_code)]
pub fn main() {
let world = World::new().displayed_in("root");
let drop_manager = ensogl_drop_manager::Manager::new(world.default_scene.dom.root.as_ref());
let scene = world.default_scene.clone_ref();
let drop_manager =
ensogl_drop_manager::Manager::new(&scene.dom.root.clone_ref().into(), &scene);
let network = enso_frp::Network::new("Debug Scene");
enso_frp::extend! { network
let file_received = drop_manager.files_received().clone_ref();
eval file_received ([](files) for file in files { download_file(file.clone_ref())});
eval file_received ([](ensogl_drop_manager::DropEventData{ files, ..})
for file in files { download_file(file.clone_ref())});
}
let mut loader_hidden = false;