Freeze view when pointer or touch is grabbed

This commit is contained in:
Ivan Molodetskikh 2024-10-24 09:12:12 +03:00
parent 6ecbf2db8a
commit 012340c5f4
4 changed files with 67 additions and 4 deletions

View file

@ -1735,6 +1735,8 @@ impl State {
},
);
pointer.frame(self);
self.niri.refresh_layout_is_grabbed();
}
fn on_pointer_axis<I: InputBackend>(&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<I: InputBackend>(&mut self, evt: I::TouchUpEvent) {
let Some(handle) = self.niri.seat.get_touch() else {

View file

@ -1928,6 +1928,12 @@ impl<W: LayoutElement> Layout<W> {
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<W: LayoutElement> Layout<W> {
.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();
}
}
}

View file

@ -114,6 +114,11 @@ pub struct Workspace<W: LayoutElement> {
/// 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<Options>,
@ -441,6 +446,7 @@ impl<W: LayoutElement> Workspace<W> {
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<W: LayoutElement> Workspace<W> {
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<W: LayoutElement> Workspace<W> {
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<W: LayoutElement> Workspace<W> {
}
}
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;

View file

@ -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,