ailloli_ui_core/event/
file.rs

1//! Ordered, provider-neutral file hover and drop payloads.
2
3use crate::{Point, UploadFile};
4
5/// Platform-neutral file hover/drop event.
6///
7/// Current variants are `Entered`, `Moved`, `Left`, and `Dropped`; `Hover`,
8/// `HoverCancelled`, and `Drop` remain compatibility variants.
9///
10/// # Examples
11///
12/// ```
13/// use ailloli_ui_core::{event::FileEvent, Point, UploadFile};
14/// let event = FileEvent::dropped(Some(Point::new(2.0, 3.0)), [UploadFile::named("a.txt")]);
15/// assert_eq!(event.files()[0].name, "a.txt");
16/// ```
17#[non_exhaustive]
18#[derive(Debug, Clone, PartialEq)]
19pub enum FileEvent {
20    /// A batch of files entered the logical window.
21    Entered {
22        /// Logical pointer position when supplied by the provider.
23        pos: Option<Point>,
24        /// Files in provider order; the batch may be empty.
25        files: Vec<UploadFile>,
26    },
27    /// A hovering batch moved within the logical window.
28    Moved {
29        /// Logical pointer position when supplied by the provider.
30        pos: Option<Point>,
31        /// Current files in provider order; the batch may be empty.
32        files: Vec<UploadFile>,
33    },
34    /// The current file batch left the logical window.
35    Left,
36    /// A batch of files was dropped in the logical window.
37    Dropped {
38        /// Logical pointer position when supplied by the provider.
39        pos: Option<Point>,
40        /// Dropped files in provider order; the batch may be empty.
41        files: Vec<UploadFile>,
42    },
43    /// One or more files are hovering over the window at logical `pos`.
44    ///
45    /// New adapters should use [`FileEvent::Entered`] and [`FileEvent::Moved`].
46    Hover {
47        /// Logical pointer position reported by the legacy adapter.
48        pos: Point,
49        /// Hovering files in provider order.
50        files: Vec<UploadFile>,
51    },
52    /// File hover was cancelled by the platform.
53    ///
54    /// New adapters should use [`FileEvent::Left`].
55    HoverCancelled,
56    /// One or more files were dropped at logical `pos`.
57    ///
58    /// New adapters should use [`FileEvent::Dropped`].
59    Drop {
60        /// Logical pointer position reported by the legacy adapter.
61        pos: Point,
62        /// Dropped files in provider order.
63        files: Vec<UploadFile>,
64    },
65}
66
67impl FileEvent {
68    /// Creates an ordered batch that entered the logical window.
69    ///
70    /// `None` preserves that the provider did not supply a pointer position;
71    /// it is not replaced with an origin or stale coordinate.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use ailloli_ui_core::{event::FileEvent, UploadFile};
77    /// let event = FileEvent::entered(None, [UploadFile::named("a.txt")]);
78    /// assert_eq!(event.pos(), None);
79    /// ```
80    pub fn entered(pos: Option<Point>, files: impl IntoIterator<Item = UploadFile>) -> Self {
81        Self::Entered {
82            pos,
83            files: files.into_iter().collect(),
84        }
85    }
86
87    /// Creates an ordered hovering batch movement.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// use ailloli_ui_core::{event::FileEvent, Point, UploadFile};
93    /// let event = FileEvent::moved(Some(Point::new(1.0, 2.0)), [UploadFile::named("a.txt")]);
94    /// assert_eq!(event.pos(), Some(Point::new(1.0, 2.0)));
95    /// ```
96    pub fn moved(pos: Option<Point>, files: impl IntoIterator<Item = UploadFile>) -> Self {
97        Self::Moved {
98            pos,
99            files: files.into_iter().collect(),
100        }
101    }
102
103    /// Creates a notification that the current batch left the window.
104    ///
105    /// Leave events carry neither files nor a position.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use ailloli_ui_core::event::FileEvent;
111    /// assert!(FileEvent::left().is_left());
112    /// ```
113    pub const fn left() -> Self {
114        Self::Left
115    }
116
117    /// Creates an ordered batch dropped in the logical window.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use ailloli_ui_core::{event::FileEvent, UploadFile};
123    /// assert_eq!(FileEvent::dropped(None, [UploadFile::named("a.txt")]).files().len(), 1);
124    /// ```
125    pub fn dropped(pos: Option<Point>, files: impl IntoIterator<Item = UploadFile>) -> Self {
126        Self::Dropped {
127            pos,
128            files: files.into_iter().collect(),
129        }
130    }
131
132    /// Logical pointer position supplied by the provider, when known.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// use ailloli_ui_core::{event::FileEvent, Point, UploadFile};
138    /// let event = FileEvent::entered(Some(Point::new(3.0, 4.0)), [UploadFile::named("a")]);
139    /// assert_eq!(event.pos(), Some(Point::new(3.0, 4.0)));
140    /// ```
141    pub fn pos(&self) -> Option<Point> {
142        match self {
143            Self::Entered { pos, .. } | Self::Moved { pos, .. } | Self::Dropped { pos, .. } => *pos,
144            Self::Hover { pos, .. } | Self::Drop { pos, .. } => Some(*pos),
145            Self::Left | Self::HoverCancelled => None,
146        }
147    }
148
149    /// Ordered files carried by the event, or an empty slice for leave events.
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// use ailloli_ui_core::event::FileEvent;
155    /// assert!(FileEvent::left().files().is_empty());
156    /// ```
157    pub fn files(&self) -> &[UploadFile] {
158        match self {
159            Self::Entered { files, .. }
160            | Self::Moved { files, .. }
161            | Self::Dropped { files, .. }
162            | Self::Hover { files, .. }
163            | Self::Drop { files, .. } => files,
164            Self::Left | Self::HoverCancelled => &[],
165        }
166    }
167
168    /// Returns `true` for both current and legacy leave variants.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use ailloli_ui_core::event::FileEvent;
174    /// assert!(FileEvent::left().is_left());
175    /// ```
176    pub const fn is_left(&self) -> bool {
177        matches!(self, Self::Left | Self::HoverCancelled)
178    }
179}