1use crate::{
2 div, point, Div, Element, FocusHandle, IntoElement, Keystroke, Modifiers, Pixels, Point,
3 Render, ViewContext,
4};
5use smallvec::SmallVec;
6use std::{any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct KeyDownEvent {
10 pub keystroke: Keystroke,
11 pub is_held: bool,
12}
13
14#[derive(Clone, Debug)]
15pub struct KeyUpEvent {
16 pub keystroke: Keystroke,
17}
18
19#[derive(Clone, Debug, Default)]
20pub struct ModifiersChangedEvent {
21 pub modifiers: Modifiers,
22}
23
24impl Deref for ModifiersChangedEvent {
25 type Target = Modifiers;
26
27 fn deref(&self) -> &Self::Target {
28 &self.modifiers
29 }
30}
31
32/// The phase of a touch motion event.
33/// Based on the winit enum of the same name.
34#[derive(Clone, Copy, Debug)]
35pub enum TouchPhase {
36 Started,
37 Moved,
38 Ended,
39}
40
41#[derive(Clone, Debug, Default)]
42pub struct MouseDownEvent {
43 pub button: MouseButton,
44 pub position: Point<Pixels>,
45 pub modifiers: Modifiers,
46 pub click_count: usize,
47}
48
49#[derive(Clone, Debug, Default)]
50pub struct MouseUpEvent {
51 pub button: MouseButton,
52 pub position: Point<Pixels>,
53 pub modifiers: Modifiers,
54 pub click_count: usize,
55}
56
57#[derive(Clone, Debug, Default)]
58pub struct ClickEvent {
59 pub down: MouseDownEvent,
60 pub up: MouseUpEvent,
61}
62
63pub struct Drag<S, R, V, E>
64where
65 R: Fn(&mut V, &mut ViewContext<V>) -> E,
66 V: 'static,
67 E: IntoElement,
68{
69 pub state: S,
70 pub render_drag_handle: R,
71 view_element_types: PhantomData<(V, E)>,
72}
73
74impl<S, R, V, E> Drag<S, R, V, E>
75where
76 R: Fn(&mut V, &mut ViewContext<V>) -> E,
77 V: 'static,
78 E: Element,
79{
80 pub fn new(state: S, render_drag_handle: R) -> Self {
81 Drag {
82 state,
83 render_drag_handle,
84 view_element_types: Default::default(),
85 }
86 }
87}
88
89#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
90pub enum MouseButton {
91 Left,
92 Right,
93 Middle,
94 Navigate(NavigationDirection),
95}
96
97impl MouseButton {
98 pub fn all() -> Vec<Self> {
99 vec![
100 MouseButton::Left,
101 MouseButton::Right,
102 MouseButton::Middle,
103 MouseButton::Navigate(NavigationDirection::Back),
104 MouseButton::Navigate(NavigationDirection::Forward),
105 ]
106 }
107}
108
109impl Default for MouseButton {
110 fn default() -> Self {
111 Self::Left
112 }
113}
114
115#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
116pub enum NavigationDirection {
117 Back,
118 Forward,
119}
120
121impl Default for NavigationDirection {
122 fn default() -> Self {
123 Self::Back
124 }
125}
126
127#[derive(Clone, Debug, Default)]
128pub struct MouseMoveEvent {
129 pub position: Point<Pixels>,
130 pub pressed_button: Option<MouseButton>,
131 pub modifiers: Modifiers,
132}
133
134impl MouseMoveEvent {
135 pub fn dragging(&self) -> bool {
136 self.pressed_button == Some(MouseButton::Left)
137 }
138}
139
140#[derive(Clone, Debug)]
141pub struct ScrollWheelEvent {
142 pub position: Point<Pixels>,
143 pub delta: ScrollDelta,
144 pub modifiers: Modifiers,
145 pub touch_phase: TouchPhase,
146}
147
148impl Deref for ScrollWheelEvent {
149 type Target = Modifiers;
150
151 fn deref(&self) -> &Self::Target {
152 &self.modifiers
153 }
154}
155
156#[derive(Clone, Copy, Debug)]
157pub enum ScrollDelta {
158 Pixels(Point<Pixels>),
159 Lines(Point<f32>),
160}
161
162impl Default for ScrollDelta {
163 fn default() -> Self {
164 Self::Lines(Default::default())
165 }
166}
167
168impl ScrollDelta {
169 pub fn precise(&self) -> bool {
170 match self {
171 ScrollDelta::Pixels(_) => true,
172 ScrollDelta::Lines(_) => false,
173 }
174 }
175
176 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
177 match self {
178 ScrollDelta::Pixels(delta) => *delta,
179 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
180 }
181 }
182}
183
184#[derive(Clone, Debug, Default)]
185pub struct MouseExitEvent {
186 pub position: Point<Pixels>,
187 pub pressed_button: Option<MouseButton>,
188 pub modifiers: Modifiers,
189}
190
191impl Deref for MouseExitEvent {
192 type Target = Modifiers;
193
194 fn deref(&self) -> &Self::Target {
195 &self.modifiers
196 }
197}
198
199#[derive(Debug, Clone, Default)]
200pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
201
202impl ExternalPaths {
203 pub fn paths(&self) -> &[PathBuf] {
204 &self.0
205 }
206}
207
208impl Render for ExternalPaths {
209 type Element = Div;
210
211 fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
212 div() // Intentionally left empty because the platform will render icons for the dragged files
213 }
214}
215
216#[derive(Debug, Clone)]
217pub enum FileDropEvent {
218 Entered {
219 position: Point<Pixels>,
220 files: ExternalPaths,
221 },
222 Pending {
223 position: Point<Pixels>,
224 },
225 Submit {
226 position: Point<Pixels>,
227 },
228 Exited,
229}
230
231#[derive(Clone, Debug)]
232pub enum InputEvent {
233 KeyDown(KeyDownEvent),
234 KeyUp(KeyUpEvent),
235 ModifiersChanged(ModifiersChangedEvent),
236 MouseDown(MouseDownEvent),
237 MouseUp(MouseUpEvent),
238 MouseMove(MouseMoveEvent),
239 MouseExited(MouseExitEvent),
240 ScrollWheel(ScrollWheelEvent),
241 FileDrop(FileDropEvent),
242}
243
244impl InputEvent {
245 pub fn position(&self) -> Option<Point<Pixels>> {
246 match self {
247 InputEvent::KeyDown { .. } => None,
248 InputEvent::KeyUp { .. } => None,
249 InputEvent::ModifiersChanged { .. } => None,
250 InputEvent::MouseDown(event) => Some(event.position),
251 InputEvent::MouseUp(event) => Some(event.position),
252 InputEvent::MouseMove(event) => Some(event.position),
253 InputEvent::MouseExited(event) => Some(event.position),
254 InputEvent::ScrollWheel(event) => Some(event.position),
255 InputEvent::FileDrop(FileDropEvent::Exited) => None,
256 InputEvent::FileDrop(
257 FileDropEvent::Entered { position, .. }
258 | FileDropEvent::Pending { position, .. }
259 | FileDropEvent::Submit { position, .. },
260 ) => Some(*position),
261 }
262 }
263
264 pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
265 match self {
266 InputEvent::KeyDown { .. } => None,
267 InputEvent::KeyUp { .. } => None,
268 InputEvent::ModifiersChanged { .. } => None,
269 InputEvent::MouseDown(event) => Some(event),
270 InputEvent::MouseUp(event) => Some(event),
271 InputEvent::MouseMove(event) => Some(event),
272 InputEvent::MouseExited(event) => Some(event),
273 InputEvent::ScrollWheel(event) => Some(event),
274 InputEvent::FileDrop(event) => Some(event),
275 }
276 }
277
278 pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
279 match self {
280 InputEvent::KeyDown(event) => Some(event),
281 InputEvent::KeyUp(event) => Some(event),
282 InputEvent::ModifiersChanged(event) => Some(event),
283 InputEvent::MouseDown(_) => None,
284 InputEvent::MouseUp(_) => None,
285 InputEvent::MouseMove(_) => None,
286 InputEvent::MouseExited(_) => None,
287 InputEvent::ScrollWheel(_) => None,
288 InputEvent::FileDrop(_) => None,
289 }
290 }
291}
292
293pub struct FocusEvent {
294 pub blurred: Option<FocusHandle>,
295 pub focused: Option<FocusHandle>,
296}
297
298#[cfg(test)]
299mod test {
300 use crate::{
301 self as gpui, div, Div, FocusHandle, InteractiveElement, IntoElement, KeyBinding,
302 Keystroke, ParentElement, Render, Stateful, TestAppContext, VisualContext,
303 };
304
305 struct TestView {
306 saw_key_down: bool,
307 saw_action: bool,
308 focus_handle: FocusHandle,
309 }
310
311 actions!(test, [TestAction]);
312
313 impl Render for TestView {
314 type Element = Stateful<Div>;
315
316 fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
317 div().id("testview").child(
318 div()
319 .key_context("parent")
320 .on_key_down(cx.listener(|this, _, _| this.saw_key_down = true))
321 .on_action(
322 cx.listener(|this: &mut TestView, _: &TestAction, _| {
323 this.saw_action = true
324 }),
325 )
326 .child(
327 div()
328 .key_context("nested")
329 .track_focus(&self.focus_handle)
330 .into_element(),
331 ),
332 )
333 }
334 }
335
336 #[gpui::test]
337 fn test_on_events(cx: &mut TestAppContext) {
338 let window = cx.update(|cx| {
339 cx.open_window(Default::default(), |cx| {
340 cx.build_view(|cx| TestView {
341 saw_key_down: false,
342 saw_action: false,
343 focus_handle: cx.focus_handle(),
344 })
345 })
346 });
347
348 cx.update(|cx| {
349 cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]);
350 });
351
352 window
353 .update(cx, |test_view, cx| cx.focus(&test_view.focus_handle))
354 .unwrap();
355
356 cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap(), false);
357
358 window
359 .update(cx, |test_view, _| {
360 assert!(test_view.saw_key_down || test_view.saw_action);
361 assert!(test_view.saw_key_down);
362 assert!(test_view.saw_action);
363 })
364 .unwrap();
365 }
366}