mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Unify pointer & touch move grab, add view offset to it
This commit is contained in:
@@ -40,7 +40,6 @@ use tracing::field::Empty;
|
|||||||
|
|
||||||
use crate::input::move_grab::MoveGrab;
|
use crate::input::move_grab::MoveGrab;
|
||||||
use crate::input::resize_grab::ResizeGrab;
|
use crate::input::resize_grab::ResizeGrab;
|
||||||
use crate::input::touch_move_grab::TouchMoveGrab;
|
|
||||||
use crate::input::touch_resize_grab::TouchResizeGrab;
|
use crate::input::touch_resize_grab::TouchResizeGrab;
|
||||||
use crate::input::{PointerOrTouchStartData, DOUBLE_CLICK_TIME};
|
use crate::input::{PointerOrTouchStartData, DOUBLE_CLICK_TIME};
|
||||||
use crate::layout::ActivateWindow;
|
use crate::layout::ActivateWindow;
|
||||||
@@ -133,33 +132,17 @@ impl XdgShellHandler for State {
|
|||||||
let window = mapped.window.clone();
|
let window = mapped.window.clone();
|
||||||
let output = output.clone();
|
let output = output.clone();
|
||||||
|
|
||||||
let output_pos = self
|
match &start_data {
|
||||||
.niri
|
PointerOrTouchStartData::Pointer(_) => {
|
||||||
.global_space
|
if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true) {
|
||||||
.output_geometry(&output)
|
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||||
.unwrap()
|
}
|
||||||
.loc
|
|
||||||
.to_f64();
|
|
||||||
|
|
||||||
let pos_within_output = start_data.location() - output_pos;
|
|
||||||
|
|
||||||
if !self
|
|
||||||
.niri
|
|
||||||
.layout
|
|
||||||
.interactive_move_begin(window.clone(), &output, pos_within_output)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match start_data {
|
|
||||||
PointerOrTouchStartData::Pointer(start_data) => {
|
|
||||||
let grab = MoveGrab::new(start_data, window, false);
|
|
||||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
|
||||||
}
|
}
|
||||||
PointerOrTouchStartData::Touch(start_data) => {
|
PointerOrTouchStartData::Touch(_) => {
|
||||||
let touch = self.niri.seat.get_touch().unwrap();
|
let touch = self.niri.seat.get_touch().unwrap();
|
||||||
let grab = TouchMoveGrab::new(start_data, window);
|
if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true) {
|
||||||
touch.set_grab(self, grab, serial);
|
touch.set_grab(self, grab, serial);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+44
-37
@@ -36,7 +36,6 @@ use smithay::wayland::keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitor;
|
|||||||
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
|
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
|
||||||
use smithay::wayland::selection::data_device::DnDGrab;
|
use smithay::wayland::selection::data_device::DnDGrab;
|
||||||
use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait};
|
use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait};
|
||||||
use touch_move_grab::TouchMoveGrab;
|
|
||||||
use touch_overview_grab::TouchOverviewGrab;
|
use touch_overview_grab::TouchOverviewGrab;
|
||||||
|
|
||||||
use self::move_grab::MoveGrab;
|
use self::move_grab::MoveGrab;
|
||||||
@@ -59,7 +58,6 @@ pub mod scroll_swipe_gesture;
|
|||||||
pub mod scroll_tracker;
|
pub mod scroll_tracker;
|
||||||
pub mod spatial_movement_grab;
|
pub mod spatial_movement_grab;
|
||||||
pub mod swipe_tracker;
|
pub mod swipe_tracker;
|
||||||
pub mod touch_move_grab;
|
|
||||||
pub mod touch_overview_grab;
|
pub mod touch_overview_grab;
|
||||||
pub mod touch_resize_grab;
|
pub mod touch_resize_grab;
|
||||||
|
|
||||||
@@ -84,6 +82,28 @@ impl<D: SeatHandler> PointerOrTouchStartData<D> {
|
|||||||
PointerOrTouchStartData::Touch(x) => x.location,
|
PointerOrTouchStartData::Touch(x) => x.location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_pointer(&self) -> &PointerGrabStartData<D> {
|
||||||
|
match self {
|
||||||
|
PointerOrTouchStartData::Pointer(x) => x,
|
||||||
|
PointerOrTouchStartData::Touch(_) => panic!("start_data is not Pointer"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_touch(&self) -> &TouchGrabStartData<D> {
|
||||||
|
match self {
|
||||||
|
PointerOrTouchStartData::Pointer(_) => panic!("start_data is not Touch"),
|
||||||
|
PointerOrTouchStartData::Touch(x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_pointer(&self) -> bool {
|
||||||
|
matches!(self, Self::Pointer(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_touch(&self) -> bool {
|
||||||
|
matches!(self, Self::Touch(_))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@@ -2773,31 +2793,19 @@ impl State {
|
|||||||
let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());
|
let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());
|
||||||
if is_overview_open || mod_down {
|
if is_overview_open || mod_down {
|
||||||
let location = pointer.current_location();
|
let location = pointer.current_location();
|
||||||
let (output, pos_within_output) = self.niri.output_under(location).unwrap();
|
|
||||||
let output = output.clone();
|
|
||||||
|
|
||||||
if !is_overview_open {
|
if !is_overview_open {
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.niri.layout.interactive_move_begin(
|
let start_data = PointerGrabStartData {
|
||||||
window.clone(),
|
focus: None,
|
||||||
&output,
|
button: button_code,
|
||||||
pos_within_output,
|
location,
|
||||||
) {
|
};
|
||||||
let start_data = PointerGrabStartData {
|
let start_data = PointerOrTouchStartData::Pointer(start_data);
|
||||||
focus: None,
|
if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), false) {
|
||||||
button: button_code,
|
|
||||||
location,
|
|
||||||
};
|
|
||||||
let grab = MoveGrab::new(start_data, window.clone(), is_overview_open);
|
|
||||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||||
|
|
||||||
if !is_overview_open {
|
|
||||||
self.niri
|
|
||||||
.cursor_manager
|
|
||||||
.set_cursor_image(CursorImageStatus::Named(CursorIcon::Move));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4037,22 +4045,15 @@ impl State {
|
|||||||
} else if let Some((window, _)) = under.window {
|
} else if let Some((window, _)) = under.window {
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
|
|
||||||
// Check if we need to start an interactive move.
|
// Check if we need to start a touch move grab.
|
||||||
if mod_down {
|
if mod_down {
|
||||||
let (output, pos_within_output) = self.niri.output_under(pos).unwrap();
|
let start_data = TouchGrabStartData {
|
||||||
let output = output.clone();
|
focus: None,
|
||||||
|
slot,
|
||||||
if self.niri.layout.interactive_move_begin(
|
location: pos,
|
||||||
window.clone(),
|
};
|
||||||
&output,
|
let start_data = PointerOrTouchStartData::Touch(start_data);
|
||||||
pos_within_output,
|
if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true) {
|
||||||
) {
|
|
||||||
let start_data = TouchGrabStartData {
|
|
||||||
focus: None,
|
|
||||||
slot,
|
|
||||||
location: pos,
|
|
||||||
};
|
|
||||||
let grab = TouchMoveGrab::new(start_data, window.clone());
|
|
||||||
handle.set_grab(self, grab, serial);
|
handle.set_grab(self, grab, serial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4908,13 +4909,19 @@ fn grab_allows_hot_corner(grab: &(dyn PointerGrab<State> + 'static)) -> bool {
|
|||||||
//
|
//
|
||||||
// Some notable grabs not mentioned here:
|
// Some notable grabs not mentioned here:
|
||||||
// - DnDGrab allows hot corner to DnD across workspaces.
|
// - DnDGrab allows hot corner to DnD across workspaces.
|
||||||
// - MoveGrab allows hot corner to DnD across workspaces.
|
|
||||||
// - ClickGrab keeps pointer focus on the window, so the hot corner doesn't trigger.
|
// - ClickGrab keeps pointer focus on the window, so the hot corner doesn't trigger.
|
||||||
// - Touch grabs: touch doesn't trigger the hot corner.
|
// - Touch grabs: touch doesn't trigger the hot corner.
|
||||||
if grab.is::<ResizeGrab>() || grab.is::<SpatialMovementGrab>() {
|
if grab.is::<ResizeGrab>() || grab.is::<SpatialMovementGrab>() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(grab) = grab.downcast_ref::<MoveGrab>() {
|
||||||
|
// Window move allows hot corner to DnD across workspaces.
|
||||||
|
if !grab.is_move() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+341
-67
@@ -1,3 +1,5 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use smithay::backend::input::ButtonState;
|
use smithay::backend::input::ButtonState;
|
||||||
use smithay::desktop::Window;
|
use smithay::desktop::Window;
|
||||||
use smithay::input::pointer::{
|
use smithay::input::pointer::{
|
||||||
@@ -7,52 +9,260 @@ use smithay::input::pointer::{
|
|||||||
GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle,
|
GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle,
|
||||||
RelativeMotionEvent,
|
RelativeMotionEvent,
|
||||||
};
|
};
|
||||||
|
use smithay::input::touch::{
|
||||||
|
self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle,
|
||||||
|
};
|
||||||
use smithay::input::SeatHandler;
|
use smithay::input::SeatHandler;
|
||||||
use smithay::utils::{IsAlive, Logical, Point};
|
use smithay::output::Output;
|
||||||
|
use smithay::utils::{IsAlive, Logical, Point, Serial};
|
||||||
|
|
||||||
|
use crate::input::PointerOrTouchStartData;
|
||||||
use crate::niri::State;
|
use crate::niri::State;
|
||||||
|
|
||||||
pub struct MoveGrab {
|
pub struct MoveGrab {
|
||||||
start_data: PointerGrabStartData<State>,
|
start_data: PointerOrTouchStartData<State>,
|
||||||
|
start_output: Output,
|
||||||
|
start_pos_within_output: Point<f64, Logical>,
|
||||||
last_location: Point<f64, Logical>,
|
last_location: Point<f64, Logical>,
|
||||||
window: Window,
|
window: Window,
|
||||||
gesture: GestureState,
|
gesture: GestureState,
|
||||||
|
enable_view_offset: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum GestureState {
|
enum GestureState {
|
||||||
Recognizing,
|
Recognizing,
|
||||||
Move,
|
Move,
|
||||||
|
ViewOffset,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MoveGrab {
|
impl MoveGrab {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
start_data: PointerGrabStartData<State>,
|
state: &mut State,
|
||||||
|
start_data: PointerOrTouchStartData<State>,
|
||||||
window: Window,
|
window: Window,
|
||||||
use_threshold: bool,
|
enable_view_offset: bool,
|
||||||
) -> Self {
|
) -> Option<Self> {
|
||||||
let gesture = if use_threshold {
|
let (output, pos_within_output) = state.niri.output_under(start_data.location())?;
|
||||||
GestureState::Recognizing
|
|
||||||
} else {
|
|
||||||
GestureState::Move
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Some(Self {
|
||||||
last_location: start_data.location,
|
last_location: start_data.location(),
|
||||||
start_data,
|
start_data,
|
||||||
|
start_output: output.clone(),
|
||||||
|
start_pos_within_output: pos_within_output,
|
||||||
window,
|
window,
|
||||||
gesture,
|
gesture: GestureState::Recognizing,
|
||||||
}
|
enable_view_offset,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_ungrab(&mut self, state: &mut State) {
|
pub fn is_move(&self) -> bool {
|
||||||
state.niri.layout.interactive_move_end(&self.window);
|
self.gesture == GestureState::Move
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_ungrab(&mut self, data: &mut State) {
|
||||||
|
let layout = &mut data.niri.layout;
|
||||||
|
match self.gesture {
|
||||||
|
GestureState::Recognizing => {
|
||||||
|
// Activate the window on release. This is most prominent in the overview where
|
||||||
|
// windows are not activated on click. In the overview, we also try to do a nice
|
||||||
|
// synchronized workspace animation.
|
||||||
|
if layout.is_overview_open() {
|
||||||
|
let res = layout.workspaces().find_map(|(mon, ws_idx, ws)| {
|
||||||
|
ws.windows()
|
||||||
|
.any(|w| w.window == self.window)
|
||||||
|
.then(|| (mon.map(|mon| mon.output().clone()), ws_idx))
|
||||||
|
});
|
||||||
|
if let Some((Some(output), ws_idx)) = res {
|
||||||
|
layout.focus_output(&output);
|
||||||
|
layout.toggle_overview_to_workspace(ws_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.activate_window(&self.window);
|
||||||
|
}
|
||||||
|
GestureState::Move => layout.interactive_move_end(&self.window),
|
||||||
|
GestureState::ViewOffset => {
|
||||||
|
layout.view_offset_gesture_end(Some(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.start_data.is_pointer() {
|
||||||
|
data.niri
|
||||||
|
.cursor_manager
|
||||||
|
.set_cursor_image(CursorImageStatus::default_named());
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: only redraw the window output.
|
// FIXME: only redraw the window output.
|
||||||
state.niri.queue_redraw_all();
|
data.niri.queue_redraw_all();
|
||||||
state
|
}
|
||||||
.niri
|
|
||||||
.cursor_manager
|
fn begin_move(&mut self, data: &mut State) -> bool {
|
||||||
.set_cursor_image(CursorImageStatus::default_named());
|
if !data.niri.layout.interactive_move_begin(
|
||||||
|
self.window.clone(),
|
||||||
|
&self.start_output,
|
||||||
|
self.start_pos_within_output,
|
||||||
|
) {
|
||||||
|
// Can no longer start the move.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.gesture = GestureState::Move;
|
||||||
|
|
||||||
|
if self.start_data.is_pointer() {
|
||||||
|
data.niri
|
||||||
|
.cursor_manager
|
||||||
|
.set_cursor_image(CursorImageStatus::Named(CursorIcon::Move));
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn begin_view_offset(&mut self, data: &mut State) -> bool {
|
||||||
|
let layout = &mut data.niri.layout;
|
||||||
|
let Some((output, ws_idx)) = layout.workspaces().find_map(|(mon, ws_idx, ws)| {
|
||||||
|
let ws_idx = ws
|
||||||
|
.windows()
|
||||||
|
.any(|w| w.window == self.window)
|
||||||
|
.then_some(ws_idx)?;
|
||||||
|
let output = mon?.output().clone();
|
||||||
|
Some((output, ws_idx))
|
||||||
|
}) else {
|
||||||
|
// Can no longer start the gesture.
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout.view_offset_gesture_begin(&output, Some(ws_idx), false);
|
||||||
|
|
||||||
|
self.gesture = GestureState::ViewOffset;
|
||||||
|
|
||||||
|
if self.start_data.is_pointer() {
|
||||||
|
data.niri
|
||||||
|
.cursor_manager
|
||||||
|
.set_cursor_image(CursorImageStatus::Named(CursorIcon::AllScroll));
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_motion(
|
||||||
|
&mut self,
|
||||||
|
data: &mut State,
|
||||||
|
location: Point<f64, Logical>,
|
||||||
|
timestamp: Duration,
|
||||||
|
) -> bool {
|
||||||
|
let mut delta = location - self.last_location;
|
||||||
|
self.last_location = location;
|
||||||
|
|
||||||
|
// Try to recognize the gesture.
|
||||||
|
if self.gesture == GestureState::Recognizing {
|
||||||
|
// Check if the window has closed.
|
||||||
|
if !self.window.alive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the gesture moved far enough to decide.
|
||||||
|
let c = location - self.start_data.location();
|
||||||
|
if c.x * c.x + c.y * c.y >= 8. * 8. {
|
||||||
|
let is_floating = data
|
||||||
|
.niri
|
||||||
|
.layout
|
||||||
|
.workspaces()
|
||||||
|
.find_map(|(_, _, ws)| {
|
||||||
|
ws.windows()
|
||||||
|
.any(|w| w.window == self.window)
|
||||||
|
.then(|| ws.is_floating(&self.window))
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let is_view_offset =
|
||||||
|
self.enable_view_offset && !is_floating && c.x.abs() > c.y.abs();
|
||||||
|
|
||||||
|
let started = if is_view_offset {
|
||||||
|
self.begin_view_offset(data)
|
||||||
|
} else {
|
||||||
|
self.begin_move(data)
|
||||||
|
};
|
||||||
|
if !started {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the whole delta that accumulated during recognizing.
|
||||||
|
delta = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.gesture {
|
||||||
|
GestureState::Recognizing => return true,
|
||||||
|
GestureState::Move => {
|
||||||
|
let Some((output, pos_within_output)) = data.niri.output_under(self.last_location)
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
let output = output.clone();
|
||||||
|
|
||||||
|
let ongoing = data.niri.layout.interactive_move_update(
|
||||||
|
&self.window,
|
||||||
|
delta,
|
||||||
|
output,
|
||||||
|
pos_within_output,
|
||||||
|
);
|
||||||
|
if ongoing {
|
||||||
|
// FIXME: only redraw the previous and the new output.
|
||||||
|
data.niri.queue_redraw_all();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GestureState::ViewOffset => {
|
||||||
|
let res = data
|
||||||
|
.niri
|
||||||
|
.layout
|
||||||
|
.view_offset_gesture_update(-delta.x, timestamp, false);
|
||||||
|
if let Some(output) = res {
|
||||||
|
if let Some(output) = output {
|
||||||
|
data.niri.queue_redraw(&output);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_toggle_floating(&mut self, data: &mut State) -> bool {
|
||||||
|
if self.gesture == GestureState::ViewOffset {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start move if still recognizing.
|
||||||
|
if self.gesture == GestureState::Recognizing {
|
||||||
|
let Some((output, pos_within_output)) = data.niri.output_under(self.last_location)
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let output = output.clone();
|
||||||
|
|
||||||
|
if !self.begin_move(data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the delta accumulated during recognizing.
|
||||||
|
let ongoing = data.niri.layout.interactive_move_update(
|
||||||
|
&self.window,
|
||||||
|
self.last_location - self.start_data.location(),
|
||||||
|
output,
|
||||||
|
pos_within_output,
|
||||||
|
);
|
||||||
|
if !ongoing {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.niri.layout.toggle_window_floating(Some(&self.window));
|
||||||
|
data.niri.queue_redraw_all();
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,47 +277,11 @@ impl PointerGrab<State> for MoveGrab {
|
|||||||
// While the grab is active, no client has pointer focus.
|
// While the grab is active, no client has pointer focus.
|
||||||
handle.motion(data, None, event);
|
handle.motion(data, None, event);
|
||||||
|
|
||||||
if self.window.alive() {
|
let timestamp = Duration::from_millis(u64::from(event.time));
|
||||||
if let Some((output, pos_within_output)) = data.niri.output_under(event.location) {
|
if !self.on_motion(data, event.location, timestamp) {
|
||||||
let output = output.clone();
|
// The gesture is no longer ongoing.
|
||||||
let event_delta = event.location - self.last_location;
|
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||||
self.last_location = event.location;
|
|
||||||
|
|
||||||
if self.gesture == GestureState::Recognizing {
|
|
||||||
let c = event.location - self.start_data.location;
|
|
||||||
|
|
||||||
// Check if the gesture moved far enough to decide.
|
|
||||||
if c.x * c.x + c.y * c.y >= 8. * 8. {
|
|
||||||
self.gesture = GestureState::Move;
|
|
||||||
|
|
||||||
data.niri
|
|
||||||
.cursor_manager
|
|
||||||
.set_cursor_image(CursorImageStatus::Named(CursorIcon::Move));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.gesture != GestureState::Move {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ongoing = data.niri.layout.interactive_move_update(
|
|
||||||
&self.window,
|
|
||||||
event_delta,
|
|
||||||
output,
|
|
||||||
pos_within_output,
|
|
||||||
);
|
|
||||||
if ongoing {
|
|
||||||
// FIXME: only redraw the previous and the new output.
|
|
||||||
data.niri.queue_redraw_all();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The move is no longer ongoing.
|
|
||||||
handle.unset_grab(self, data, event.serial, event.time, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn relative_motion(
|
fn relative_motion(
|
||||||
@@ -129,18 +303,25 @@ impl PointerGrab<State> for MoveGrab {
|
|||||||
) {
|
) {
|
||||||
handle.button(data, event);
|
handle.button(data, event);
|
||||||
|
|
||||||
|
let start_data = self.start_data.unwrap_pointer();
|
||||||
|
|
||||||
|
if !handle.current_pressed().contains(&start_data.button) {
|
||||||
|
// The button that initiated the grab was released.
|
||||||
|
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// When moving with the left button, right toggles floating, and vice versa.
|
// When moving with the left button, right toggles floating, and vice versa.
|
||||||
let toggle_floating_button = if self.start_data.button == 0x110 {
|
let toggle_floating_button = if start_data.button == 0x110 {
|
||||||
0x111
|
0x111
|
||||||
} else {
|
} else {
|
||||||
0x110
|
0x110
|
||||||
};
|
};
|
||||||
if event.button == toggle_floating_button && event.state == ButtonState::Pressed {
|
if event.state != ButtonState::Pressed || event.button != toggle_floating_button {
|
||||||
data.niri.layout.toggle_window_floating(Some(&self.window));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !handle.current_pressed().contains(&self.start_data.button) {
|
if !self.on_toggle_floating(data) {
|
||||||
// The button that initiated the grab was released.
|
|
||||||
handle.unset_grab(self, data, event.serial, event.time, true);
|
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,7 +412,100 @@ impl PointerGrab<State> for MoveGrab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_data(&self) -> &PointerGrabStartData<State> {
|
fn start_data(&self) -> &PointerGrabStartData<State> {
|
||||||
&self.start_data
|
self.start_data.unwrap_pointer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset(&mut self, data: &mut State) {
|
||||||
|
self.on_ungrab(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchGrab<State> for MoveGrab {
|
||||||
|
fn down(
|
||||||
|
&mut self,
|
||||||
|
data: &mut State,
|
||||||
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
|
_focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
||||||
|
event: &touch::DownEvent,
|
||||||
|
seq: Serial,
|
||||||
|
) {
|
||||||
|
handle.down(data, None, event, seq);
|
||||||
|
|
||||||
|
if event.slot == self.start_data.unwrap_touch().slot {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.on_toggle_floating(data) {
|
||||||
|
handle.unset_grab(self, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn up(
|
||||||
|
&mut self,
|
||||||
|
data: &mut State,
|
||||||
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
|
event: &touch::UpEvent,
|
||||||
|
seq: Serial,
|
||||||
|
) {
|
||||||
|
handle.up(data, event, seq);
|
||||||
|
|
||||||
|
if event.slot == self.start_data.unwrap_touch().slot {
|
||||||
|
handle.unset_grab(self, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn motion(
|
||||||
|
&mut self,
|
||||||
|
data: &mut State,
|
||||||
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
|
_focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
||||||
|
event: &touch::MotionEvent,
|
||||||
|
seq: Serial,
|
||||||
|
) {
|
||||||
|
handle.motion(data, None, event, seq);
|
||||||
|
|
||||||
|
if event.slot != self.start_data.unwrap_touch().slot {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let timestamp = Duration::from_millis(u64::from(event.time));
|
||||||
|
if !self.on_motion(data, event.location, timestamp) {
|
||||||
|
// The gesture is no longer ongoing.
|
||||||
|
handle.unset_grab(self, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||||
|
handle.frame(data, seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||||
|
handle.cancel(data, seq);
|
||||||
|
handle.unset_grab(self, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shape(
|
||||||
|
&mut self,
|
||||||
|
data: &mut State,
|
||||||
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
|
event: &touch::ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
|
) {
|
||||||
|
handle.shape(data, event, seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn orientation(
|
||||||
|
&mut self,
|
||||||
|
data: &mut State,
|
||||||
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
|
event: &touch::OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
|
) {
|
||||||
|
handle.orientation(data, event, seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||||
|
self.start_data.unwrap_touch()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unset(&mut self, data: &mut State) {
|
fn unset(&mut self, data: &mut State) {
|
||||||
|
|||||||
@@ -1,136 +0,0 @@
|
|||||||
use smithay::desktop::Window;
|
|
||||||
use smithay::input::touch::{
|
|
||||||
DownEvent, GrabStartData as TouchGrabStartData, MotionEvent, OrientationEvent, ShapeEvent,
|
|
||||||
TouchGrab, TouchInnerHandle, UpEvent,
|
|
||||||
};
|
|
||||||
use smithay::input::SeatHandler;
|
|
||||||
use smithay::utils::{IsAlive, Logical, Point, Serial};
|
|
||||||
|
|
||||||
use crate::niri::State;
|
|
||||||
|
|
||||||
pub struct TouchMoveGrab {
|
|
||||||
start_data: TouchGrabStartData<State>,
|
|
||||||
last_location: Point<f64, Logical>,
|
|
||||||
window: Window,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TouchMoveGrab {
|
|
||||||
pub fn new(start_data: TouchGrabStartData<State>, window: Window) -> Self {
|
|
||||||
Self {
|
|
||||||
last_location: start_data.location,
|
|
||||||
start_data,
|
|
||||||
window,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_ungrab(&mut self, state: &mut State) {
|
|
||||||
state.niri.layout.interactive_move_end(&self.window);
|
|
||||||
// FIXME: only redraw the window output.
|
|
||||||
state.niri.queue_redraw_all();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TouchGrab<State> for TouchMoveGrab {
|
|
||||||
fn down(
|
|
||||||
&mut self,
|
|
||||||
data: &mut State,
|
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
|
||||||
_focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
|
||||||
event: &DownEvent,
|
|
||||||
seq: Serial,
|
|
||||||
) {
|
|
||||||
handle.down(data, None, event, seq);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn up(
|
|
||||||
&mut self,
|
|
||||||
data: &mut State,
|
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
|
||||||
event: &UpEvent,
|
|
||||||
seq: Serial,
|
|
||||||
) {
|
|
||||||
handle.up(data, event, seq);
|
|
||||||
|
|
||||||
if event.slot != self.start_data.slot {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.unset_grab(self, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn motion(
|
|
||||||
&mut self,
|
|
||||||
data: &mut State,
|
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
|
||||||
_focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
|
||||||
event: &MotionEvent,
|
|
||||||
seq: Serial,
|
|
||||||
) {
|
|
||||||
handle.motion(data, None, event, seq);
|
|
||||||
|
|
||||||
if event.slot != self.start_data.slot {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.window.alive() {
|
|
||||||
if let Some((output, pos_within_output)) = data.niri.output_under(event.location) {
|
|
||||||
let output = output.clone();
|
|
||||||
let event_delta = event.location - self.last_location;
|
|
||||||
self.last_location = event.location;
|
|
||||||
let ongoing = data.niri.layout.interactive_move_update(
|
|
||||||
&self.window,
|
|
||||||
event_delta,
|
|
||||||
output,
|
|
||||||
pos_within_output,
|
|
||||||
);
|
|
||||||
if ongoing {
|
|
||||||
// FIXME: only redraw the previous and the new output.
|
|
||||||
data.niri.queue_redraw_all();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The move is no longer ongoing.
|
|
||||||
handle.unset_grab(self, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
|
||||||
handle.frame(data, seq);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
|
||||||
handle.cancel(data, seq);
|
|
||||||
handle.unset_grab(self, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shape(
|
|
||||||
&mut self,
|
|
||||||
data: &mut State,
|
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
|
||||||
event: &ShapeEvent,
|
|
||||||
seq: Serial,
|
|
||||||
) {
|
|
||||||
handle.shape(data, event, seq);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn orientation(
|
|
||||||
&mut self,
|
|
||||||
data: &mut State,
|
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
|
||||||
event: &OrientationEvent,
|
|
||||||
seq: Serial,
|
|
||||||
) {
|
|
||||||
handle.orientation(data, event, seq);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
|
||||||
&self.start_data
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unset(&mut self, data: &mut State) {
|
|
||||||
self.on_ungrab(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4040,16 +4040,12 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
mon.dnd_scroll_gesture_end();
|
mon.dnd_scroll_gesture_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ws_id = None;
|
|
||||||
for ws in self.workspaces_mut() {
|
for ws in self.workspaces_mut() {
|
||||||
let id = ws.id();
|
|
||||||
if let Some(tile) = ws.tiles_mut().find(|tile| *tile.window().id() == window_id)
|
if let Some(tile) = ws.tiles_mut().find(|tile| *tile.window().id() == window_id)
|
||||||
{
|
{
|
||||||
let offset = tile.interactive_move_offset;
|
let offset = tile.interactive_move_offset;
|
||||||
tile.interactive_move_offset = Point::from((0., 0.));
|
tile.interactive_move_offset = Point::from((0., 0.));
|
||||||
tile.animate_move_from(offset);
|
tile.animate_move_from(offset);
|
||||||
|
|
||||||
ws_id = Some(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock the view on the workspaces, but if the moved window was active,
|
// Unlock the view on the workspaces, but if the moved window was active,
|
||||||
@@ -4064,32 +4060,6 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the overview, we want to click on a window to focus it, and also to
|
|
||||||
// click-and-drag to move the window. The way we handle this is by always starting
|
|
||||||
// the interactive move (to get frozen view), then, when in the overview, *not*
|
|
||||||
// calling interactive_move_update() until the cursor moves far enough. This means
|
|
||||||
// that if we "just click" then we end up in this branch with state == Starting.
|
|
||||||
// Close the overview in this case.
|
|
||||||
if self.overview_open {
|
|
||||||
let ws_id = ws_id.unwrap();
|
|
||||||
if let MonitorSet::Normal { monitors, .. } = &mut self.monitor_set {
|
|
||||||
for mon in monitors {
|
|
||||||
if let Some(ws_idx) =
|
|
||||||
mon.workspaces.iter().position(|ws| ws.id() == ws_id)
|
|
||||||
{
|
|
||||||
mon.activate_workspace_with_anim_config(
|
|
||||||
ws_idx,
|
|
||||||
Some(self.options.animations.overview_open_close.0),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.activate_window(&window_id);
|
|
||||||
self.close_overview();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InteractiveMoveState::Moving(move_) => move_,
|
InteractiveMoveState::Moving(move_) => move_,
|
||||||
|
|||||||
Reference in New Issue
Block a user