diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-06-19 21:54:46 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-06-19 21:55:39 +0300 |
| commit | db89d4d3dd1440e18193d4ce98d207531c7720e0 (patch) | |
| tree | 9c4974f68ad38928d4bb0b53f219cc607cf9ea59 | |
| parent | 226273f6607ae83b17c06e0e094ace64ed7a4f7c (diff) | |
| download | niri-db89d4d3dd1440e18193d4ce98d207531c7720e0.tar.gz niri-db89d4d3dd1440e18193d4ce98d207531c7720e0.tar.bz2 niri-db89d4d3dd1440e18193d4ce98d207531c7720e0.zip | |
Implement vertical middle mouse gesture
| -rw-r--r-- | src/input/mod.rs | 16 | ||||
| -rw-r--r-- | src/input/spatial_movement_grab.rs (renamed from src/input/view_offset_grab.rs) | 65 | ||||
| -rw-r--r-- | src/layout/mod.rs | 44 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 43 |
4 files changed, 128 insertions, 40 deletions
diff --git a/src/input/mod.rs b/src/input/mod.rs index d0e1c55b..d5b0cd0e 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -29,7 +29,7 @@ use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerCons use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait}; use self::resize_grab::ResizeGrab; -use self::view_offset_grab::ViewOffsetGrab; +use self::spatial_movement_grab::SpatialMovementGrab; use crate::niri::State; use crate::ui::screenshot_ui::ScreenshotUi; use crate::utils::spawning::spawn; @@ -37,8 +37,8 @@ use crate::utils::{center, get_monotonic_time, ResizeEdge}; pub mod resize_grab; pub mod scroll_tracker; +pub mod spatial_movement_grab; pub mod swipe_tracker; -pub mod view_offset_grab; pub const DOUBLE_CLICK_TIME: Duration = Duration::from_millis(400); @@ -1252,15 +1252,13 @@ impl State { }; if mod_down { if let Some(output) = self.niri.output_under_cursor() { - self.niri.layout.view_offset_gesture_begin(&output, false); - let location = pointer.current_location(); let start_data = PointerGrabStartData { focus: None, button: event.button_code(), location, }; - let grab = ViewOffsetGrab::new(start_data); + let grab = SpatialMovementGrab::new(start_data, output); pointer.set_grab(self, grab, serial, Focus::Clear); self.niri.pointer_grab_ongoing = true; self.niri @@ -1699,7 +1697,9 @@ impl State { if cx.abs() > cy.abs() { self.niri.layout.view_offset_gesture_begin(&output, true); } else { - self.niri.layout.workspace_switch_gesture_begin(&output); + self.niri + .layout + .workspace_switch_gesture_begin(&output, true); } } } @@ -1711,7 +1711,7 @@ impl State { let res = self .niri .layout - .workspace_switch_gesture_update(delta_y, timestamp); + .workspace_switch_gesture_update(delta_y, timestamp, true); if let Some(output) = res { if let Some(output) = output { self.niri.queue_redraw(&output); @@ -1757,7 +1757,7 @@ impl State { let res = self .niri .layout - .workspace_switch_gesture_end(event.cancelled()); + .workspace_switch_gesture_end(event.cancelled(), Some(true)); if let Some(output) = res { self.niri.queue_redraw(&output); handled = true; diff --git a/src/input/view_offset_grab.rs b/src/input/spatial_movement_grab.rs index b4f9f96c..5a0e6a8a 100644 --- a/src/input/view_offset_grab.rs +++ b/src/input/spatial_movement_grab.rs @@ -7,28 +7,45 @@ use smithay::input::pointer::{ MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }; use smithay::input::SeatHandler; +use smithay::output::Output; use smithay::utils::{Logical, Point}; use crate::niri::State; -pub struct ViewOffsetGrab { +pub struct SpatialMovementGrab { start_data: PointerGrabStartData<State>, last_location: Point<f64, Logical>, + output: Output, + gesture: GestureState, } -impl ViewOffsetGrab { - pub fn new(start_data: PointerGrabStartData<State>) -> Self { +#[derive(Debug, Clone, Copy)] +enum GestureState { + Recognizing, + ViewOffset, + WorkspaceSwitch, +} + +impl SpatialMovementGrab { + pub fn new(start_data: PointerGrabStartData<State>, output: Output) -> Self { Self { last_location: start_data.location, start_data, + output, + gesture: GestureState::Recognizing, } } fn on_ungrab(&mut self, state: &mut State) { - let res = state - .niri - .layout - .view_offset_gesture_end(false, Some(false)); + let layout = &mut state.niri.layout; + let res = match self.gesture { + GestureState::Recognizing => None, + GestureState::ViewOffset => layout.view_offset_gesture_end(false, Some(false)), + GestureState::WorkspaceSwitch => { + layout.workspace_switch_gesture_end(false, Some(false)) + } + }; + if let Some(output) = res { state.niri.queue_redraw(&output); } @@ -41,7 +58,7 @@ impl ViewOffsetGrab { } } -impl PointerGrab<State> for ViewOffsetGrab { +impl PointerGrab<State> for SpatialMovementGrab { fn motion( &mut self, data: &mut State, @@ -56,10 +73,34 @@ impl PointerGrab<State> for ViewOffsetGrab { let delta = event.location - self.last_location; self.last_location = event.location; - let res = data - .niri - .layout - .view_offset_gesture_update(-delta.x, timestamp, false); + let layout = &mut data.niri.layout; + let res = match self.gesture { + GestureState::Recognizing => { + let c = event.location - self.start_data.location; + + // Check if the gesture moved far enough to decide. Threshold copied from GTK 4. + if c.x * c.x + c.y * c.y >= 8. * 8. { + if c.x.abs() > c.y.abs() { + self.gesture = GestureState::ViewOffset; + layout.view_offset_gesture_begin(&self.output, false); + layout.view_offset_gesture_update(-c.x, timestamp, false) + } else { + self.gesture = GestureState::WorkspaceSwitch; + layout.workspace_switch_gesture_begin(&self.output, false); + layout.workspace_switch_gesture_update(-c.y, timestamp, false) + } + } else { + Some(None) + } + } + GestureState::ViewOffset => { + layout.view_offset_gesture_update(-delta.x, timestamp, false) + } + GestureState::WorkspaceSwitch => { + layout.workspace_switch_gesture_update(-delta.y, timestamp, false) + } + }; + if let Some(output) = res { if let Some(output) = output { data.niri.queue_redraw(&output); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index b8b13560..b74ef729 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -2002,7 +2002,7 @@ impl<W: LayoutElement> Layout<W> { } } - pub fn workspace_switch_gesture_begin(&mut self, output: &Output) { + pub fn workspace_switch_gesture_begin(&mut self, output: &Output, is_touchpad: bool) { let monitors = match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => monitors, MonitorSet::NoOutputs { .. } => unreachable!(), @@ -2011,11 +2011,11 @@ impl<W: LayoutElement> Layout<W> { for monitor in monitors { // Cancel the gesture on other outputs. if &monitor.output != output { - monitor.workspace_switch_gesture_end(true); + monitor.workspace_switch_gesture_end(true, None); continue; } - monitor.workspace_switch_gesture_begin(); + monitor.workspace_switch_gesture_begin(is_touchpad); } } @@ -2023,6 +2023,7 @@ impl<W: LayoutElement> Layout<W> { &mut self, delta_y: f64, timestamp: Duration, + is_touchpad: bool, ) -> Option<Option<Output>> { let monitors = match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => monitors, @@ -2030,7 +2031,9 @@ impl<W: LayoutElement> Layout<W> { }; for monitor in monitors { - if let Some(refresh) = monitor.workspace_switch_gesture_update(delta_y, timestamp) { + if let Some(refresh) = + monitor.workspace_switch_gesture_update(delta_y, timestamp, is_touchpad) + { if refresh { return Some(Some(monitor.output.clone())); } else { @@ -2042,14 +2045,18 @@ impl<W: LayoutElement> Layout<W> { None } - pub fn workspace_switch_gesture_end(&mut self, cancelled: bool) -> Option<Output> { + pub fn workspace_switch_gesture_end( + &mut self, + cancelled: bool, + is_touchpad: Option<bool>, + ) -> Option<Output> { let monitors = match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => monitors, MonitorSet::NoOutputs { .. } => return None, }; for monitor in monitors { - if monitor.workspace_switch_gesture_end(cancelled) { + if monitor.workspace_switch_gesture_end(cancelled, is_touchpad) { return Some(monitor.output.clone()); } } @@ -2750,14 +2757,17 @@ mod tests { WorkspaceSwitchGestureBegin { #[proptest(strategy = "1..=5usize")] output_idx: usize, + is_touchpad: bool, }, WorkspaceSwitchGestureUpdate { #[proptest(strategy = "-400f64..400f64")] delta: f64, timestamp: Duration, + is_touchpad: bool, }, WorkspaceSwitchGestureEnd { cancelled: bool, + is_touchpad: Option<bool>, }, InteractiveResizeBegin { #[proptest(strategy = "1..=5usize")] @@ -3135,19 +3145,29 @@ mod tests { // We don't handle cancels in this gesture. layout.view_offset_gesture_end(false, is_touchpad); } - Op::WorkspaceSwitchGestureBegin { output_idx: id } => { + Op::WorkspaceSwitchGestureBegin { + output_idx: id, + is_touchpad, + } => { let name = format!("output{id}"); let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else { return; }; - layout.workspace_switch_gesture_begin(&output); + layout.workspace_switch_gesture_begin(&output, is_touchpad); } - Op::WorkspaceSwitchGestureUpdate { delta, timestamp } => { - layout.workspace_switch_gesture_update(delta, timestamp); + Op::WorkspaceSwitchGestureUpdate { + delta, + timestamp, + is_touchpad, + } => { + layout.workspace_switch_gesture_update(delta, timestamp, is_touchpad); } - Op::WorkspaceSwitchGestureEnd { cancelled } => { - layout.workspace_switch_gesture_end(cancelled); + Op::WorkspaceSwitchGestureEnd { + cancelled, + is_touchpad, + } => { + layout.workspace_switch_gesture_end(cancelled, is_touchpad); } Op::InteractiveResizeBegin { window, edges } => { layout.interactive_resize_begin(window, edges); diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index 911e0480..423b2ef9 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -54,10 +54,12 @@ pub enum WorkspaceSwitch { #[derive(Debug)] pub struct WorkspaceSwitchGesture { /// Index of the workspace where the gesture was started. - pub center_idx: usize, + center_idx: usize, /// Current, fractional workspace index. pub current_idx: f64, - pub tracker: SwipeTracker, + tracker: SwipeTracker, + /// Whether the gesture is controlled by the touchpad. + is_touchpad: bool, } pub type MonitorRenderElement<R> = @@ -973,7 +975,7 @@ impl<W: LayoutElement> Monitor<W> { } } - pub fn workspace_switch_gesture_begin(&mut self) { + pub fn workspace_switch_gesture_begin(&mut self, is_touchpad: bool) { let center_idx = self.active_workspace_idx; let current_idx = self .workspace_switch @@ -985,6 +987,7 @@ impl<W: LayoutElement> Monitor<W> { center_idx, current_idx, tracker: SwipeTracker::new(), + is_touchpad, }; self.workspace_switch = Some(WorkspaceSwitch::Gesture(gesture)); } @@ -993,14 +996,24 @@ impl<W: LayoutElement> Monitor<W> { &mut self, delta_y: f64, timestamp: Duration, + is_touchpad: bool, ) -> Option<bool> { let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else { return None; }; + if gesture.is_touchpad != is_touchpad { + return None; + } + gesture.tracker.push(delta_y, timestamp); - let pos = gesture.tracker.pos() / WORKSPACE_GESTURE_MOVEMENT; + let total_height = if gesture.is_touchpad { + WORKSPACE_GESTURE_MOVEMENT + } else { + self.workspaces[0].view_size().h + }; + let pos = gesture.tracker.pos() / total_height; let min = gesture.center_idx.saturating_sub(1) as f64; let max = (gesture.center_idx + 1).min(self.workspaces.len() - 1) as f64; @@ -1015,20 +1028,34 @@ impl<W: LayoutElement> Monitor<W> { Some(true) } - pub fn workspace_switch_gesture_end(&mut self, cancelled: bool) -> bool { + pub fn workspace_switch_gesture_end( + &mut self, + cancelled: bool, + is_touchpad: Option<bool>, + ) -> bool { let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else { return false; }; + if is_touchpad.map_or(false, |x| gesture.is_touchpad != x) { + return false; + } + if cancelled { self.workspace_switch = None; self.clean_up_workspaces(); return true; } - let mut velocity = gesture.tracker.velocity() / WORKSPACE_GESTURE_MOVEMENT; - let current_pos = gesture.tracker.pos() / WORKSPACE_GESTURE_MOVEMENT; - let pos = gesture.tracker.projected_end_pos() / WORKSPACE_GESTURE_MOVEMENT; + let total_height = if gesture.is_touchpad { + WORKSPACE_GESTURE_MOVEMENT + } else { + self.workspaces[0].view_size().h + }; + + let mut velocity = gesture.tracker.velocity() / total_height; + let current_pos = gesture.tracker.pos() / total_height; + let pos = gesture.tracker.projected_end_pos() / total_height; let min = gesture.center_idx.saturating_sub(1) as f64; let max = (gesture.center_idx + 1).min(self.workspaces.len() - 1) as f64; |
