druid_shell/
mouse.rs

1// Copyright 2019 The Druid Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Common types for representing mouse events and state
16
17use crate::backend;
18use crate::kurbo::{Point, Vec2};
19use crate::piet::ImageBuf;
20use crate::Modifiers;
21
22/// Information about the mouse event.
23///
24/// Every mouse event can have a new position. There is no guarantee of
25/// receiving a move event before another mouse event.
26#[derive(Debug, Clone, PartialEq)]
27pub struct MouseEvent {
28    /// The location of the mouse in [display points] in relation to the current window.
29    ///
30    /// [display points]: crate::Scale
31    pub pos: Point,
32    /// Mouse buttons being held down during a move or after a click event.
33    /// Thus it will contain the `button` that triggered a mouse-down event,
34    /// and it will not contain the `button` that triggered a mouse-up event.
35    pub buttons: MouseButtons,
36    /// Keyboard modifiers at the time of the event.
37    pub mods: Modifiers,
38    /// The number of mouse clicks associated with this event. This will always
39    /// be `0` for a mouse-up and mouse-move events.
40    pub count: u8,
41    /// Focus is `true` on macOS when the mouse-down event (or its companion mouse-up event)
42    /// with `MouseButton::Left` was the event that caused the window to gain focus.
43    pub focus: bool,
44    /// The button that was pressed down in the case of mouse-down,
45    /// or the button that was released in the case of mouse-up.
46    /// This will always be `MouseButton::None` in the case of mouse-move.
47    pub button: MouseButton,
48    /// The wheel movement.
49    ///
50    /// The polarity is the amount to be added to the scroll position,
51    /// in other words the opposite of the direction the content should
52    /// move on scrolling. This polarity is consistent with the
53    /// deltaX and deltaY values in a web [WheelEvent].
54    ///
55    /// [WheelEvent]: https://w3c.github.io/uievents/#event-type-wheel
56    pub wheel_delta: Vec2,
57}
58
59/// An indicator of which mouse button was pressed.
60#[derive(PartialEq, Eq, Clone, Copy, Debug)]
61#[repr(u8)]
62pub enum MouseButton {
63    /// No mouse button.
64    // MUST BE FIRST (== 0)
65    None,
66    /// Left mouse button.
67    Left,
68    /// Right mouse button.
69    Right,
70    /// Middle mouse button.
71    Middle,
72    /// First X button.
73    X1,
74    /// Second X button.
75    X2,
76}
77
78impl MouseButton {
79    /// Returns `true` if this is [`MouseButton::Left`].
80    ///
81    /// [`MouseButton::Left`]: #variant.Left
82    #[inline]
83    pub fn is_left(self) -> bool {
84        self == MouseButton::Left
85    }
86
87    /// Returns `true` if this is [`MouseButton::Right`].
88    ///
89    /// [`MouseButton::Right`]: #variant.Right
90    #[inline]
91    pub fn is_right(self) -> bool {
92        self == MouseButton::Right
93    }
94
95    /// Returns `true` if this is [`MouseButton::Middle`].
96    ///
97    /// [`MouseButton::Middle`]: #variant.Middle
98    #[inline]
99    pub fn is_middle(self) -> bool {
100        self == MouseButton::Middle
101    }
102
103    /// Returns `true` if this is [`MouseButton::X1`].
104    ///
105    /// [`MouseButton::X1`]: #variant.X1
106    #[inline]
107    pub fn is_x1(self) -> bool {
108        self == MouseButton::X1
109    }
110
111    /// Returns `true` if this is [`MouseButton::X2`].
112    ///
113    /// [`MouseButton::X2`]: #variant.X2
114    #[inline]
115    pub fn is_x2(self) -> bool {
116        self == MouseButton::X2
117    }
118}
119
120/// A set of [`MouseButton`]s.
121#[derive(PartialEq, Eq, Clone, Copy, Default)]
122pub struct MouseButtons(u8);
123
124impl MouseButtons {
125    /// Create a new empty set.
126    #[inline]
127    pub fn new() -> MouseButtons {
128        MouseButtons(0)
129    }
130
131    /// Add the `button` to the set.
132    #[inline]
133    pub fn insert(&mut self, button: MouseButton) {
134        self.0 |= 1.min(button as u8) << button as u8;
135    }
136
137    /// Remove the `button` from the set.
138    #[inline]
139    pub fn remove(&mut self, button: MouseButton) {
140        self.0 &= !(1.min(button as u8) << button as u8);
141    }
142
143    /// Builder-style method for adding the `button` to the set.
144    #[inline]
145    pub fn with(mut self, button: MouseButton) -> MouseButtons {
146        self.0 |= 1.min(button as u8) << button as u8;
147        self
148    }
149
150    /// Builder-style method for removing the `button` from the set.
151    #[inline]
152    pub fn without(mut self, button: MouseButton) -> MouseButtons {
153        self.0 &= !(1.min(button as u8) << button as u8);
154        self
155    }
156
157    /// Returns `true` if the `button` is in the set.
158    #[inline]
159    pub fn contains(self, button: MouseButton) -> bool {
160        (self.0 & (1.min(button as u8) << button as u8)) != 0
161    }
162
163    /// Returns `true` if the set is empty.
164    #[inline]
165    pub fn is_empty(self) -> bool {
166        self.0 == 0
167    }
168
169    /// Returns `true` if all the `buttons` are in the set.
170    #[inline]
171    pub fn is_superset(self, buttons: MouseButtons) -> bool {
172        self.0 & buttons.0 == buttons.0
173    }
174
175    /// Returns `true` if [`MouseButton::Left`] is in the set.
176    ///
177    /// [`MouseButton::Left`]: enum.MouseButton.html#variant.Left
178    #[inline]
179    pub fn has_left(self) -> bool {
180        self.contains(MouseButton::Left)
181    }
182
183    /// Returns `true` if [`MouseButton::Right`] is in the set.
184    ///
185    /// [`MouseButton::Right`]: enum.MouseButton.html#variant.Right
186    #[inline]
187    pub fn has_right(self) -> bool {
188        self.contains(MouseButton::Right)
189    }
190
191    /// Returns `true` if [`MouseButton::Middle`] is in the set.
192    ///
193    /// [`MouseButton::Middle`]: enum.MouseButton.html#variant.Middle
194    #[inline]
195    pub fn has_middle(self) -> bool {
196        self.contains(MouseButton::Middle)
197    }
198
199    /// Returns `true` if [`MouseButton::X1`] is in the set.
200    ///
201    /// [`MouseButton::X1`]: enum.MouseButton.html#variant.X1
202    #[inline]
203    pub fn has_x1(self) -> bool {
204        self.contains(MouseButton::X1)
205    }
206
207    /// Returns `true` if [`MouseButton::X2`] is in the set.
208    ///
209    /// [`MouseButton::X2`]: enum.MouseButton.html#variant.X2
210    #[inline]
211    pub fn has_x2(self) -> bool {
212        self.contains(MouseButton::X2)
213    }
214
215    /// Adds all the `buttons` to the set.
216    pub fn extend(&mut self, buttons: MouseButtons) {
217        self.0 |= buttons.0;
218    }
219
220    /// Returns a union of the values in `self` and `other`.
221    #[inline]
222    pub fn union(mut self, other: MouseButtons) -> MouseButtons {
223        self.0 |= other.0;
224        self
225    }
226
227    /// Clear the set.
228    #[inline]
229    pub fn clear(&mut self) {
230        self.0 = 0;
231    }
232
233    /// Count the number of pressed buttons in the set.
234    #[inline]
235    pub fn count(self) -> u32 {
236        self.0.count_ones()
237    }
238}
239
240impl std::fmt::Debug for MouseButtons {
241    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
242        write!(f, "MouseButtons({:05b})", self.0 >> 1)
243    }
244}
245
246//NOTE: this currently only contains cursors that are included by default on
247//both Windows and macOS. We may want to provide polyfills for various additional cursors.
248/// Mouse cursors.
249#[derive(Clone, PartialEq, Eq)]
250pub enum Cursor {
251    /// The default arrow cursor.
252    Arrow,
253    /// A vertical I-beam, for indicating insertion points in text.
254    IBeam,
255    Pointer,
256    Crosshair,
257
258    #[doc(hidden)]
259    #[deprecated(
260        since = "0.8.0",
261        note = "This will be removed because it is not available on Windows."
262    )]
263    OpenHand,
264    NotAllowed,
265    ResizeLeftRight,
266    ResizeUpDown,
267    // The platform cursor should be small. Any image data that it uses should be shared (i.e.
268    // behind an `Arc` or using a platform API that does the sharing).
269    Custom(backend::window::CustomCursor),
270}
271
272/// A platform-independent description of a custom cursor.
273#[derive(Clone)]
274pub struct CursorDesc {
275    #[allow(dead_code)] // Not yet used on all platforms.
276    pub(crate) image: ImageBuf,
277    #[allow(dead_code)] // Not yet used on all platforms.
278    pub(crate) hot: Point,
279}
280
281impl CursorDesc {
282    /// Creates a new `CursorDesc`.
283    ///
284    /// `hot` is the "hot spot" of the cursor, measured in terms of the pixels in `image` with
285    /// `(0, 0)` at the top left. The hot spot is the logical position of the mouse cursor within
286    /// the image. For example, if the image is a picture of a arrow, the hot spot might be the
287    /// coordinates of the arrow's tip.
288    pub fn new(image: ImageBuf, hot: impl Into<Point>) -> CursorDesc {
289        CursorDesc {
290            image,
291            hot: hot.into(),
292        }
293    }
294}
295
296impl std::fmt::Debug for Cursor {
297    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
298        #[allow(deprecated)]
299        match self {
300            Cursor::Arrow => write!(f, "Cursor::Arrow"),
301            Cursor::IBeam => write!(f, "Cursor::IBeam"),
302            Cursor::Pointer => write!(f, "Cursor::Pointer"),
303            Cursor::Crosshair => write!(f, "Cursor::Crosshair"),
304            Cursor::OpenHand => write!(f, "Cursor::OpenHand"),
305            Cursor::NotAllowed => write!(f, "Cursor::NotAllowed"),
306            Cursor::ResizeLeftRight => write!(f, "Cursor::ResizeLeftRight"),
307            Cursor::ResizeUpDown => write!(f, "Cursor::ResizeUpDown"),
308            Cursor::Custom(_) => write!(f, "Cursor::Custom"),
309        }
310    }
311}