|
8 | 8 | //! The goal here is to remove as much as possible from the specific instances into separate systems |
9 | 9 | //! and aggregates that are event driven. |
10 | 10 |
|
11 | | -use std::cmp::max; |
12 | | -use std::collections::HashSet; |
13 | | - |
14 | 11 | use anyhow::{Result, anyhow, bail}; |
15 | 12 | use derive_more::{Debug, From}; |
16 | 13 | use log::warn; |
| 14 | +use std::cmp::max; |
| 15 | +use std::collections::HashSet; |
| 16 | +use std::time::Duration; |
17 | 17 | use winit::event::ElementState; |
18 | 18 | use winit::keyboard::{Key, NamedKey}; |
19 | 19 |
|
@@ -49,7 +49,8 @@ use crate::send_transition::{SendTransition, convert_to_send_transitions}; |
49 | 49 | use crate::{DesktopEnvironment, DirectionBias, EventRouter, Map, OrderedHierarchy, navigation}; |
50 | 50 |
|
51 | 51 | const SECTION_SPACING: u32 = 20; |
52 | | - |
| 52 | +const POINTER_FEEDBACK_REENABLE_MIN_DISTANCE_PX: f64 = 24.0; |
| 53 | +const POINTER_FEEDBACK_REENABLE_MAX_DURATION: Duration = Duration::from_millis(200); |
53 | 54 | /// This enum specifies a unique target inside the navigation and layout history. |
54 | 55 | #[derive(Debug, Clone, PartialEq, Eq, Hash, From)] |
55 | 56 | pub enum DesktopTarget { |
@@ -114,6 +115,7 @@ pub struct DesktopSystem { |
114 | 115 |
|
115 | 116 | event_router: EventRouter<DesktopTarget>, |
116 | 117 | camera: Animated<PixelCamera>, |
| 118 | + pointer_feedback_enabled: bool, |
117 | 119 |
|
118 | 120 | #[debug(skip)] |
119 | 121 | layouter: IncrementalLayouter<DesktopTarget, 2>, |
@@ -178,6 +180,7 @@ impl DesktopSystem { |
178 | 180 |
|
179 | 181 | event_router, |
180 | 182 | camera: scene.animated(PixelCamera::default()), |
| 183 | + pointer_feedback_enabled: true, |
181 | 184 | layouter, |
182 | 185 |
|
183 | 186 | aggregates: Aggregates::new(OrderedHierarchy::default(), project_presenter), |
@@ -452,41 +455,78 @@ impl DesktopSystem { |
452 | 455 | self.camera.value() |
453 | 456 | } |
454 | 457 |
|
| 458 | + pub fn cursor_visible(&self) -> bool { |
| 459 | + self.pointer_feedback_enabled |
| 460 | + } |
| 461 | + |
455 | 462 | pub fn process_input_event( |
456 | 463 | &mut self, |
457 | 464 | event: &Event<ViewEvent>, |
458 | 465 | instance_manager: &InstanceManager, |
459 | 466 | render_geometry: &RenderGeometry, |
460 | 467 | ) -> Result<Cmd> { |
461 | 468 | let keyboard_cmd = self.preprocess_keyboard_input(event)?; |
462 | | - if !keyboard_cmd.is_none() { |
463 | | - return Ok(keyboard_cmd); |
464 | | - } |
465 | 469 |
|
466 | | - let hit_tester = AggregateHitTester::new( |
467 | | - &self.aggregates.hierarchy, |
468 | | - &self.layouter, |
469 | | - &self.aggregates.launchers, |
470 | | - &self.aggregates.instances, |
471 | | - render_geometry, |
472 | | - ); |
| 470 | + let cmd = if !keyboard_cmd.is_none() { |
| 471 | + keyboard_cmd |
| 472 | + } else { |
| 473 | + let hit_tester = AggregateHitTester::new( |
| 474 | + &self.aggregates.hierarchy, |
| 475 | + &self.layouter, |
| 476 | + &self.aggregates.launchers, |
| 477 | + &self.aggregates.instances, |
| 478 | + render_geometry, |
| 479 | + ); |
473 | 480 |
|
474 | | - // Capture focus before routing the event so we can detect focus transitions afterwards. |
475 | | - let focused_before = self.event_router.focused().cloned(); |
476 | | - let transitions = self.event_router.process(event, &hit_tester)?; |
477 | | - // Read focus after routing and immediately recompute launcher layouts when it changed. |
478 | | - let focused_after = self.event_router.focused().cloned(); |
479 | | - self.apply_launcher_layout_for_focus_change(focused_before, focused_after, true); |
| 481 | + let transitions = self.event_router.process(event, &hit_tester)?; |
| 482 | + if let Some((from, to)) = transitions.keyboard_focus_change() { |
| 483 | + self.apply_launcher_layout_for_focus_change(from.cloned(), to.cloned(), true); |
| 484 | + } |
480 | 485 |
|
481 | | - self.forward_event_transitions(transitions, instance_manager) |
| 486 | + self.forward_event_transitions(transitions, instance_manager)? |
| 487 | + }; |
| 488 | + |
| 489 | + self.update_pointer_feedback(event); |
| 490 | + |
| 491 | + Ok(cmd) |
| 492 | + } |
| 493 | + |
| 494 | + fn update_pointer_feedback(&mut self, event: &Event<ViewEvent>) { |
| 495 | + match (self.pointer_feedback_enabled, event.event()) { |
| 496 | + ( |
| 497 | + true, |
| 498 | + ViewEvent::KeyboardInput { |
| 499 | + event: key_event, .. |
| 500 | + }, |
| 501 | + ) if key_event.state == ElementState::Pressed && !key_event.repeat => { |
| 502 | + self.pointer_feedback_enabled = false; |
| 503 | + self.aggregates.project_presenter.set_hover_rect(None); |
| 504 | + } |
| 505 | + (false, ViewEvent::MouseInput { .. } | ViewEvent::MouseWheel { .. }) => { |
| 506 | + self.pointer_feedback_enabled = true; |
| 507 | + let pointer_focus = self.event_router.pointer_focus().cloned(); |
| 508 | + self.sync_hover_rect_to_pointer_path(pointer_focus.as_ref()); |
| 509 | + } |
| 510 | + (false, ViewEvent::CursorMoved { .. }) |
| 511 | + if event.cursor_has_velocity( |
| 512 | + POINTER_FEEDBACK_REENABLE_MIN_DISTANCE_PX, |
| 513 | + POINTER_FEEDBACK_REENABLE_MAX_DURATION, |
| 514 | + ) => |
| 515 | + { |
| 516 | + self.pointer_feedback_enabled = true; |
| 517 | + let pointer_focus = self.event_router.pointer_focus().cloned(); |
| 518 | + self.sync_hover_rect_to_pointer_path(pointer_focus.as_ref()); |
| 519 | + } |
| 520 | + _ => {} |
| 521 | + } |
482 | 522 | } |
483 | 523 |
|
484 | 524 | fn focus(&mut self, target: &DesktopTarget, instance_manager: &InstanceManager) -> Result<Cmd> { |
485 | 525 | // Focus changes can alter launcher layout targets. |
486 | | - let focused_before = self.event_router.focused().cloned(); |
487 | 526 | let transitions = self.event_router.focus(target); |
488 | | - let focused_after = self.event_router.focused().cloned(); |
489 | | - self.apply_launcher_layout_for_focus_change(focused_before, focused_after, true); |
| 527 | + if let Some((from, to)) = transitions.keyboard_focus_change() { |
| 528 | + self.apply_launcher_layout_for_focus_change(from.cloned(), to.cloned(), true); |
| 529 | + } |
490 | 530 | self.forward_event_transitions(transitions, instance_manager) |
491 | 531 | } |
492 | 532 |
|
@@ -923,7 +963,9 @@ impl DesktopSystem { |
923 | 963 | transitions: EventTransitions<DesktopTarget>, |
924 | 964 | instance_manager: &InstanceManager, |
925 | 965 | ) -> Result<Cmd> { |
926 | | - if let Some(pointer_focus) = transitions.pointer_focus_target() { |
| 966 | + if self.pointer_feedback_enabled |
| 967 | + && let Some(pointer_focus) = transitions.pointer_focus_target() |
| 968 | + { |
927 | 969 | self.sync_hover_rect_to_pointer_path(pointer_focus); |
928 | 970 | } |
929 | 971 |
|
|
0 commit comments