aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-06-19 21:54:46 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2024-06-19 21:55:39 +0300
commitdb89d4d3dd1440e18193d4ce98d207531c7720e0 (patch)
tree9c4974f68ad38928d4bb0b53f219cc607cf9ea59 /src
parent226273f6607ae83b17c06e0e094ace64ed7a4f7c (diff)
downloadniri-db89d4d3dd1440e18193d4ce98d207531c7720e0.tar.gz
niri-db89d4d3dd1440e18193d4ce98d207531c7720e0.tar.bz2
niri-db89d4d3dd1440e18193d4ce98d207531c7720e0.zip
Implement vertical middle mouse gesture
Diffstat (limited to 'src')
-rw-r--r--src/input/mod.rs16
-rw-r--r--src/input/spatial_movement_grab.rs (renamed from src/input/view_offset_grab.rs)65
-rw-r--r--src/layout/mod.rs44
-rw-r--r--src/layout/monitor.rs43
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;