From 012340c5f4553e79d995693812d581a5e68d84f2 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Thu, 24 Oct 2024 09:12:12 +0300 Subject: [PATCH] Freeze view when pointer or touch is grabbed --- src/input/mod.rs | 4 ++++ src/layout/mod.rs | 13 ++++++++++++ src/layout/workspace.rs | 44 +++++++++++++++++++++++++++++++++++++---- src/niri.rs | 10 ++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 07c77e8c..f9212a4c 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1735,6 +1735,8 @@ impl State { }, ); pointer.frame(self); + + self.niri.refresh_layout_is_grabbed(); } fn on_pointer_axis(&mut self, event: I::PointerAxisEvent) { @@ -2370,6 +2372,8 @@ impl State { // We're using touch, hide the pointer. self.niri.pointer_hidden = true; + + self.niri.refresh_layout_is_grabbed(); } fn on_touch_up(&mut self, evt: I::TouchUpEvent) { let Some(handle) = self.niri.seat.get_touch() else { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index b404da16..ee0ab8a3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1928,6 +1928,12 @@ impl Layout { mon.resize_edges_under(pos_within_output) } + pub fn set_pointer_grabbed(&mut self, is_grabbed: bool) { + for ws in self.workspaces_mut() { + ws.set_pointer_grabbed(is_grabbed); + } + } + #[cfg(test)] fn verify_invariants(&self) { use std::collections::HashSet; @@ -2912,6 +2918,13 @@ impl Layout { .unwrap(); tile_pos = Some(ws_offset + tile_offset); + + // The view offset anim is paused because a grab is active. Since we + // just dragged a window out, cancel the animation, because when we put + // the window back, we will animate to it afresh. + let ws_id = ws.id(); + let ws = self.workspaces_mut().find(|ws| ws.id() == ws_id).unwrap(); + ws.cancel_view_offset_anim(); } } } diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 0f378abc..a41ff656 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -114,6 +114,11 @@ pub struct Workspace { /// Buffer for the insert hint. insert_hint_buffer: SolidColorBuffer, + /// Whether a pointer grab is active. + /// + /// This means that view offset animations should be temporarily frozen. + is_pointer_grabbed: bool, + /// Configurable properties of the layout as received from the parent monitor. pub(super) base_options: Rc, @@ -441,6 +446,7 @@ impl Workspace { closing_windows: vec![], insert_hint: None, insert_hint_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]), + is_pointer_grabbed: false, base_options, options, name: config.map(|c| c.name.0), @@ -481,6 +487,7 @@ impl Workspace { closing_windows: vec![], insert_hint: None, insert_hint_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]), + is_pointer_grabbed: false, base_options, options, name: config.map(|c| c.name.0), @@ -515,10 +522,12 @@ impl Workspace { pub fn advance_animations(&mut self, current_time: Duration) { if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj { - anim.set_current_time(current_time); - self.view_offset = anim.value(); - if anim.is_done() { - self.view_offset_adj = None; + if !self.is_pointer_grabbed { + anim.set_current_time(current_time); + self.view_offset = anim.value(); + if anim.is_done() { + self.view_offset_adj = None; + } } } else if let Some(ViewOffsetAdjustment::Gesture(gesture)) = &self.view_offset_adj { self.view_offset = gesture.current_view_offset; @@ -1699,6 +1708,33 @@ impl Workspace { } } + pub fn set_pointer_grabbed(&mut self, is_grabbed: bool) { + if self.is_pointer_grabbed == is_grabbed { + return; + } + + self.is_pointer_grabbed = is_grabbed; + + // After ungrabbing, restart the view offset animation. + if !is_grabbed { + if let Some(ViewOffsetAdjustment::Animation(anim)) = &self.view_offset_adj { + // FIXME: preserve velocity. + let anim = anim.restarted(anim.value(), anim.to(), 0.); + self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(anim)); + } + } + } + + /// Stops a view offset animation if one is in progress. + /// + /// The view will stop in place. When calling this, make sure that you will move the view to + /// the active window when necessary. + pub fn cancel_view_offset_anim(&mut self) { + if let Some(ViewOffsetAdjustment::Animation(_)) = &self.view_offset_adj { + self.view_offset_adj = None; + } + } + #[cfg(test)] pub fn verify_invariants(&self, move_win_id: Option<&W::Id>) { use approx::assert_abs_diff_eq; diff --git a/src/niri.rs b/src/niri.rs index 851bb25a..625f6496 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -2840,7 +2840,17 @@ impl Niri { } } + pub fn refresh_layout_is_grabbed(&mut self) { + let pointer = self.seat.get_pointer().unwrap(); + let touch = self.seat.get_touch(); + let touch_is_grabbed = touch.map_or(false, |touch| touch.is_grabbed()); + self.layout + .set_pointer_grabbed(pointer.is_grabbed() || touch_is_grabbed); + } + pub fn refresh_layout(&mut self) { + self.refresh_layout_is_grabbed(); + let layout_is_active = match &self.keyboard_focus { KeyboardFocus::Layout { .. } => true, KeyboardFocus::LayerShell { .. } => false,