mirror of
https://github.com/rust-windowing/winit.git
synced 2026-08-29 04:40:04 -04:00
New drag and drop API (#4571)
This commit implements a new API for drag and drop, with a `DataTransfer` type which abstracts over the various clipboard/drag and drop APIs across different platforms. I built this on top of #2429 although admittedly I ended up removing pretty much all of their work while I was reworking the design. This is being built in order to help support [drag-and-drop work](https://github.com/slint-ui/slint/issues/1967) in Slint's winit backend. As part of that work, I did extensive research on how drag-and-drop and clipboard APIs are implemented across different platforms, and wrote a (still WIP) research document that can be found [here](https://gist.github.com/eira-fransham/06750cf8d25ade08d362a0ca8dfafe06). The new API is inspired by the browser's [`DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) API.
This commit is contained in:
@@ -2,8 +2,7 @@
|
||||
use std::cell::LazyCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::f64;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Mutex, Weak};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
|
||||
use dpi::{PhysicalPosition, PhysicalSize};
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -11,8 +10,9 @@ use serde::{Deserialize, Serialize};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::Instant;
|
||||
use crate::data_transfer::{DataTransferId, TypedData};
|
||||
use crate::error::RequestError;
|
||||
use crate::event_loop::AsyncRequestSerial;
|
||||
use crate::event_loop::{AsyncRequestSerial, DndAction};
|
||||
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
|
||||
#[cfg(doc)]
|
||||
use crate::window::Window;
|
||||
@@ -75,42 +75,104 @@ pub enum WindowEvent {
|
||||
/// The window has been destroyed.
|
||||
Destroyed,
|
||||
|
||||
/// A file drag operation has entered the window.
|
||||
/// A drag operation has entered the window.
|
||||
///
|
||||
/// The user can use the `id` to read information about the incoming dragged data, and report
|
||||
/// whether the operation is accepted or rejected back to the operating system (see
|
||||
/// [`crate::event_loop::ActiveEventLoop::set_valid_dnd_actions`](`crate::event_loop::ActiveEventLoop::set_valid_dnd_actions`)).
|
||||
///
|
||||
/// To read the data being dragged, see
|
||||
/// [`crate::event_loop::ActiveEventLoop::fetch_data_transfer`](`crate::event_loop::ActiveEventLoop::fetch_data_transfer`).
|
||||
DragEntered {
|
||||
/// List of paths that are being dragged onto the window.
|
||||
paths: Vec<PathBuf>,
|
||||
/// (x,y) coordinates in pixels relative to the top-left corner of the window. May be
|
||||
/// negative on some platforms if something is dragged over a window's decorations (title
|
||||
/// bar, frame, etc).
|
||||
position: PhysicalPosition<f64>,
|
||||
},
|
||||
/// A file drag operation has moved over the window.
|
||||
DragMoved {
|
||||
/// (x,y) coordinates in pixels relative to the top-left corner of the window. May be
|
||||
/// negative on some platforms if something is dragged over a window's decorations (title
|
||||
/// bar, frame, etc).
|
||||
position: PhysicalPosition<f64>,
|
||||
},
|
||||
/// The file drag operation has dropped file(s) on the window.
|
||||
DragDropped {
|
||||
/// List of paths that are being dragged onto the window.
|
||||
paths: Vec<PathBuf>,
|
||||
/// (x,y) coordinates in pixels relative to the top-left corner of the window. May be
|
||||
/// negative on some platforms if something is dragged over a window's decorations (title
|
||||
/// bar, frame, etc).
|
||||
position: PhysicalPosition<f64>,
|
||||
},
|
||||
/// The file drag operation has been cancelled or left the window.
|
||||
DragLeft {
|
||||
/// (x,y) coordinates in pixels relative to the top-left corner of the window. May be
|
||||
/// negative on some platforms if something is dragged over a window's decorations (title
|
||||
/// bar, frame, etc).
|
||||
/// ID of the data transfer object, see
|
||||
/// [`crate::event_loop::ActiveEventLoop::data_transfer`](`crate::event_loop::ActiveEventLoop::data_transfer`).
|
||||
id: DataTransferId,
|
||||
/// (x,y) coordinates in pixels relative to the top-left corner of the window.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// May be negative on some platforms if something is dragged over a window's decorations
|
||||
/// (title bar, frame, etc).
|
||||
///
|
||||
/// - **Windows:** Always emits [`None`].
|
||||
/// Some platforms will provide this on enter, others do not. If
|
||||
/// [`crate::event_loop::ActiveEventLoop::set_valid_dnd_actions`](`crate::event_loop::ActiveEventLoop::set_valid_dnd_actions`)
|
||||
/// is never called, the default state is for the drag operation to be rejected. The
|
||||
/// position is provided here when available to allow the application to accept a drag
|
||||
/// operation as soon as possible, preventing the cursor from flickering from rejected to
|
||||
/// accepted.
|
||||
position: Option<PhysicalPosition<f64>>,
|
||||
},
|
||||
/// The position of an ongoing drag operation has changed.
|
||||
DragPosition {
|
||||
/// ID of the data transfer object, see
|
||||
/// [`crate::event_loop::ActiveEventLoop::data_transfer`](`crate::event_loop::ActiveEventLoop::data_transfer`).
|
||||
id: DataTransferId,
|
||||
/// (x,y) coordinates in pixels relative to the top-left corner of the window.
|
||||
///
|
||||
/// May be negative on some platforms if something is dragged over a window's decorations
|
||||
/// (title bar, frame, etc).
|
||||
position: PhysicalPosition<f64>,
|
||||
/// The drag action proposed by the OS, based on the actions supplied in
|
||||
/// [`crate::event_loop::ActiveEventLoop::set_valid_dnd_actions`], the actions available on
|
||||
/// the source, and the held modifier keys.
|
||||
///
|
||||
/// This may be `None` if the backend has not supplied a valid action. On some platforms
|
||||
/// (in particular, X11), the application is only informed of the proposed action once
|
||||
/// the operation completes.
|
||||
proposed_action: Option<DndAction>,
|
||||
},
|
||||
/// A drag operation has dropped file(s) on the window.
|
||||
DragDropped {
|
||||
/// ID of the data transfer object, see
|
||||
/// [`crate::event_loop::ActiveEventLoop::data_transfer`].
|
||||
id: DataTransferId,
|
||||
/// The drag action proposed by the OS, based on the actions supplied in
|
||||
/// [`crate::event_loop::ActiveEventLoop::set_valid_dnd_actions`], the actions available on
|
||||
/// the source, and the held modifier keys.
|
||||
///
|
||||
/// This may be `None` if the backend has not supplied a valid action. This is different
|
||||
/// from the drag being canceled: the drag completed successfully, we just don't know
|
||||
/// what action was selected.
|
||||
proposed_action: Option<DndAction>,
|
||||
},
|
||||
/// A drag operation has been canceled or left the window.
|
||||
DragLeft {
|
||||
/// ID of the data transfer object, see
|
||||
/// [`crate::event_loop::ActiveEventLoop::data_transfer`].
|
||||
id: DataTransferId,
|
||||
},
|
||||
/// Data is available for a specific fetch request, see
|
||||
/// [`fetch_data_transfer`](crate::event_loop::ActiveEventLoop::data_transfer).
|
||||
///
|
||||
/// While winit makes a best effort to only send this event precisely once, on some platforms it
|
||||
/// may not be possible to uniquely determine the window that should receive it. In these
|
||||
/// cases, winit may dispatch the event to all windows that have access to the data
|
||||
/// transfer. If your application should only process this event once per data transfer, the
|
||||
/// `serial` field can be used to deduplicate it.
|
||||
DataTransferReceived {
|
||||
/// ID of the data transfer object, see
|
||||
/// [`crate::event_loop::ActiveEventLoop::data_transfer`].
|
||||
id: DataTransferId,
|
||||
/// Serial returned from `fetch_data_transfer`.
|
||||
serial: AsyncRequestSerial,
|
||||
/// The data for the transfer, with a specific type.
|
||||
value: Arc<dyn TypedData>,
|
||||
},
|
||||
|
||||
/// A drag operation started with `start_drag` has been dropped.
|
||||
OutgoingDragDropped {
|
||||
/// The ID returned from `start_drag`
|
||||
id: DataTransferId,
|
||||
/// The operation selected by the drop destination.
|
||||
///
|
||||
/// This may be `None` if the backend has not supplied a valid action. This is different
|
||||
/// from the drag being canceled: the drag completed successfully, we just don't know
|
||||
/// what action was selected.
|
||||
action: Option<DndAction>,
|
||||
},
|
||||
/// A drag operation started with `start_drag` has been canceled.
|
||||
OutgoingDragCanceled {
|
||||
/// The ID returned from `start_drag`
|
||||
id: DataTransferId,
|
||||
},
|
||||
|
||||
/// The window gained or lost focus.
|
||||
///
|
||||
@@ -1582,16 +1644,20 @@ mod tests {
|
||||
use crate::event::Ime::Enabled;
|
||||
use crate::event::WindowEvent::*;
|
||||
use crate::event::{PointerKind, PointerSource};
|
||||
use crate::event_loop::DndAction;
|
||||
use crate::data_transfer::DataTransferId;
|
||||
|
||||
let dnd_data = DataTransferId::from_raw(123);
|
||||
|
||||
with_window_event(CloseRequested);
|
||||
with_window_event(Destroyed);
|
||||
with_window_event(Focused(true));
|
||||
with_window_event(Moved((0, 0).into()));
|
||||
with_window_event(SurfaceResized((0, 0).into()));
|
||||
with_window_event(DragEntered { paths: vec!["x.txt".into()], position: (0, 0).into() });
|
||||
with_window_event(DragMoved { position: (0, 0).into() });
|
||||
with_window_event(DragDropped { paths: vec!["x.txt".into()], position: (0, 0).into() });
|
||||
with_window_event(DragLeft { position: Some((0, 0).into()) });
|
||||
with_window_event(DragEntered { id: dnd_data, position: None });
|
||||
with_window_event(DragPosition { id: dnd_data, position: (0, 0).into(), proposed_action: Some(DndAction::Copy) });
|
||||
with_window_event(DragDropped { id: dnd_data, proposed_action: Some(DndAction::Copy) });
|
||||
with_window_event(DragLeft { id: dnd_data });
|
||||
with_window_event(Ime(Enabled));
|
||||
with_window_event(PointerMoved {
|
||||
device_id: None,
|
||||
|
||||
Reference in New Issue
Block a user