dnd/
lib.rs

1use std::{
2    borrow::Cow,
3    fmt::Debug,
4    sync::{mpsc::SendError, Arc},
5};
6
7use bitflags::bitflags;
8use raw_window_handle::HasWindowHandle;
9
10#[cfg(all(
11    unix,
12    not(any(
13        target_os = "macos",
14        target_os = "ios",
15        target_os = "android",
16        target_os = "emscripten",
17        target_os = "redox"
18    ))
19))]
20#[path = "platform/linux.rs"]
21pub mod platform;
22
23bitflags! {
24    // Attributes can be applied to flags types
25    #[repr(transparent)]
26    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27    pub struct DndAction: u32 {
28        const Copy = 0b00000001;
29        const Move = 0b00000010;
30        const Ask = 0b00000100;
31    }
32}
33
34#[derive(Debug, Clone)]
35pub enum DndEvent<T> {
36    /// Dnd Offer event with the corresponding destination rectangle ID.
37    Offer(Option<u128>, OfferEvent<T>),
38    /// Dnd Source event.
39    Source(SourceEvent),
40}
41
42impl<T> PartialEq for DndEvent<T> {
43    fn eq(&self, other: &Self) -> bool {
44        match (self, other) {
45            (DndEvent::Offer(a, b), DndEvent::Offer(a2, b2)) => {
46                a == a2 && b == b2
47            }
48            (DndEvent::Source(a), DndEvent::Source(b)) => a == b,
49            _ => false,
50        }
51    }
52}
53
54#[derive(Debug, Clone)]
55pub enum SourceEvent {
56    /// DnD operation ended.
57    Finished,
58    /// DnD Cancelled.
59    Cancelled,
60    /// DnD action chosen by the compositor.
61    Action(DndAction),
62    /// Mime accepted by destination.
63    /// If [`None`], no mime types are accepted.
64    Mime(Option<String>),
65    /// DnD Dropped. The operation is still ongoing until receiving a
66    /// [`SourceEvent::Finished`] event.
67    Dropped,
68}
69
70impl PartialEq for SourceEvent {
71    fn eq(&self, other: &Self) -> bool {
72        match (self, other) {
73            (SourceEvent::Finished, SourceEvent::Finished)
74            | (SourceEvent::Cancelled, SourceEvent::Cancelled)
75            | (SourceEvent::Dropped, SourceEvent::Dropped) => true,
76            (SourceEvent::Action(a), SourceEvent::Action(b)) => a == b,
77            (SourceEvent::Mime(a), SourceEvent::Mime(b)) => a == b,
78            _ => false,
79        }
80    }
81}
82
83#[derive(Debug, Clone)]
84pub enum OfferEvent<T> {
85    Enter {
86        x: f64,
87        y: f64,
88        mime_types: Vec<String>,
89        surface: T,
90    },
91    Motion {
92        x: f64,
93        y: f64,
94    },
95    /// The offer is no longer on a DnD destination.
96    LeaveDestination,
97    /// The offer has left the surface.
98    Leave,
99    /// An offer was dropped
100    Drop,
101    /// If the selected action is ASK, the user must be presented with a
102    /// choice. [`Clipboard::set_action`] should then be called before data
103    /// can be requested and th DnD operation can be finished.
104    SelectedAction(DndAction),
105    Data {
106        data: Vec<u8>,
107        mime_type: String,
108    },
109}
110
111impl<T> PartialEq for OfferEvent<T> {
112    fn eq(&self, other: &Self) -> bool {
113        match (self, other) {
114            (
115                OfferEvent::Enter {
116                    x,
117                    y,
118                    mime_types,
119                    surface: _,
120                },
121                OfferEvent::Enter {
122                    x: x2,
123                    y: y2,
124                    mime_types: mime_types2,
125                    surface: _,
126                },
127            ) => x == x2 && y == y2 && mime_types == mime_types2,
128            (
129                OfferEvent::Motion { x, y },
130                OfferEvent::Motion { x: x2, y: y2 },
131            ) => x == x2 && y == y2,
132            (OfferEvent::LeaveDestination, OfferEvent::LeaveDestination)
133            | (OfferEvent::Leave, OfferEvent::Leave)
134            | (OfferEvent::Drop, OfferEvent::Drop) => true,
135            (OfferEvent::SelectedAction(a), OfferEvent::SelectedAction(b)) => {
136                a == b
137            }
138            (
139                OfferEvent::Data { data, mime_type },
140                OfferEvent::Data {
141                    data: data2,
142                    mime_type: mime_type2,
143                },
144            ) => data == data2 && mime_type == mime_type2,
145            _ => false,
146        }
147    }
148}
149
150/// A rectangle with a logical location and size relative to a [`DndSurface`]
151#[derive(Debug, Default, Clone)]
152pub struct Rectangle {
153    pub x: f64,
154    pub y: f64,
155    pub width: f64,
156    pub height: f64,
157}
158
159pub trait Sender<T> {
160    /// Send an event in the channel
161    fn send(&self, t: DndEvent<T>) -> Result<(), SendError<DndEvent<T>>>;
162}
163
164/// A rectangle with a logical location and size relative to a [`DndSurface`]
165#[derive(Debug, Clone)]
166pub struct DndDestinationRectangle {
167    /// A unique ID
168    pub id: u128,
169    /// The rectangle representing this destination.
170    pub rectangle: Rectangle,
171    /// Accepted mime types in this rectangle
172    pub mime_types: Vec<Cow<'static, str>>,
173    /// Accepted actions in this rectangle
174    pub actions: DndAction,
175    /// Prefered action in this rectangle
176    pub preferred: DndAction,
177}
178
179#[derive(Clone)]
180pub enum Icon {
181    Surface(DndSurface),
182    /// Xrgb8888 or Argb8888 image data with premultiplied alpha
183    Buffer {
184        data: Arc<Vec<u8>>,
185        width: u32,
186        height: u32,
187        transparent: bool,
188    },
189}
190
191#[derive(Clone)]
192pub struct DndSurface(pub Arc<dyn HasWindowHandle + 'static + Send + Sync>);
193
194impl Debug for DndSurface {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        f.debug_struct("DndSurface").finish()
197    }
198}
199
200#[derive(Clone)]
201pub struct DataWrapper<T>(pub T);