diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-10-30 08:35:19 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-11-21 09:15:18 +0300 |
| commit | 0cd8484bdce68fa3eae493dcb220abeb440ab5cd (patch) | |
| tree | 4653f9ce21015efdde7cc9ddce7e2d239964c72f /src | |
| parent | 9d522ed51e75d1253793f9f5ec42b8faf36e47e7 (diff) | |
| download | niri-0cd8484bdce68fa3eae493dcb220abeb440ab5cd.tar.gz niri-0cd8484bdce68fa3eae493dcb220abeb440ab5cd.tar.bz2 niri-0cd8484bdce68fa3eae493dcb220abeb440ab5cd.zip | |
Unify pointer & touch move grab, add view offset to it
Diffstat (limited to 'src')
| -rw-r--r-- | src/handlers/xdg_shell.rs | 35 | ||||
| -rw-r--r-- | src/input/mod.rs | 81 | ||||
| -rw-r--r-- | src/input/move_grab.rs | 392 | ||||
| -rw-r--r-- | src/input/touch_move_grab.rs | 136 | ||||
| -rw-r--r-- | src/layout/mod.rs | 30 |
5 files changed, 386 insertions, 288 deletions
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 20f348ba..171b65fd 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -40,7 +40,6 @@ use tracing::field::Empty; use crate::input::move_grab::MoveGrab; use crate::input::resize_grab::ResizeGrab; -use crate::input::touch_move_grab::TouchMoveGrab; use crate::input::touch_resize_grab::TouchResizeGrab; use crate::input::{PointerOrTouchStartData, DOUBLE_CLICK_TIME}; use crate::layout::ActivateWindow; @@ -133,33 +132,17 @@ impl XdgShellHandler for State { let window = mapped.window.clone(); let output = output.clone(); - let output_pos = self - .niri - .global_space - .output_geometry(&output) - .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); + match &start_data { + PointerOrTouchStartData::Pointer(_) => { + if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true) { + pointer.set_grab(self, grab, serial, Focus::Clear); + } } - PointerOrTouchStartData::Touch(start_data) => { + PointerOrTouchStartData::Touch(_) => { let touch = self.niri.seat.get_touch().unwrap(); - let grab = TouchMoveGrab::new(start_data, window); - touch.set_grab(self, grab, serial); + if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true) { + touch.set_grab(self, grab, serial); + } } } diff --git a/src/input/mod.rs b/src/input/mod.rs index e8a4c5ee..d584c940 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -36,7 +36,6 @@ use smithay::wayland::keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitor; use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint}; use smithay::wayland::selection::data_device::DnDGrab; use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait}; -use touch_move_grab::TouchMoveGrab; use touch_overview_grab::TouchOverviewGrab; use self::move_grab::MoveGrab; @@ -59,7 +58,6 @@ pub mod scroll_swipe_gesture; pub mod scroll_tracker; pub mod spatial_movement_grab; pub mod swipe_tracker; -pub mod touch_move_grab; pub mod touch_overview_grab; pub mod touch_resize_grab; @@ -84,6 +82,28 @@ impl<D: SeatHandler> PointerOrTouchStartData<D> { 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 { @@ -2773,31 +2793,19 @@ impl State { let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers()); if is_overview_open || mod_down { let location = pointer.current_location(); - let (output, pos_within_output) = self.niri.output_under(location).unwrap(); - let output = output.clone(); if !is_overview_open { self.niri.layout.activate_window(&window); } - if self.niri.layout.interactive_move_begin( - window.clone(), - &output, - pos_within_output, - ) { - let start_data = PointerGrabStartData { - focus: None, - button: button_code, - location, - }; - let grab = MoveGrab::new(start_data, window.clone(), is_overview_open); + let start_data = PointerGrabStartData { + focus: None, + button: button_code, + location, + }; + let start_data = PointerOrTouchStartData::Pointer(start_data); + if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), false) { 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 { 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 { - let (output, pos_within_output) = self.niri.output_under(pos).unwrap(); - let output = output.clone(); - - if self.niri.layout.interactive_move_begin( - window.clone(), - &output, - pos_within_output, - ) { - let start_data = TouchGrabStartData { - focus: None, - slot, - location: pos, - }; - let grab = TouchMoveGrab::new(start_data, window.clone()); + let start_data = TouchGrabStartData { + focus: None, + slot, + location: pos, + }; + let start_data = PointerOrTouchStartData::Touch(start_data); + if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true) { 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: // - 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. // - Touch grabs: touch doesn't trigger the hot corner. if grab.is::<ResizeGrab>() || grab.is::<SpatialMovementGrab>() { 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 } diff --git a/src/input/move_grab.rs b/src/input/move_grab.rs index e939696b..c78b796e 100644 --- a/src/input/move_grab.rs +++ b/src/input/move_grab.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use smithay::backend::input::ButtonState; use smithay::desktop::Window; use smithay::input::pointer::{ @@ -7,107 +9,279 @@ use smithay::input::pointer::{ GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }; +use smithay::input::touch::{ + self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle, +}; 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; 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>, window: Window, gesture: GestureState, + enable_view_offset: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum GestureState { Recognizing, Move, + ViewOffset, } impl MoveGrab { pub fn new( - start_data: PointerGrabStartData<State>, + state: &mut State, + start_data: PointerOrTouchStartData<State>, window: Window, - use_threshold: bool, - ) -> Self { - let gesture = if use_threshold { - GestureState::Recognizing - } else { - GestureState::Move - }; + enable_view_offset: bool, + ) -> Option<Self> { + let (output, pos_within_output) = state.niri.output_under(start_data.location())?; - Self { - last_location: start_data.location, + Some(Self { + last_location: start_data.location(), start_data, + start_output: output.clone(), + start_pos_within_output: pos_within_output, window, - gesture, - } + gesture: GestureState::Recognizing, + enable_view_offset, + }) } - fn on_ungrab(&mut self, state: &mut State) { - state.niri.layout.interactive_move_end(&self.window); + pub fn is_move(&self) -> bool { + 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. - state.niri.queue_redraw_all(); - state - .niri - .cursor_manager - .set_cursor_image(CursorImageStatus::default_named()); + data.niri.queue_redraw_all(); } -} -impl PointerGrab<State> for MoveGrab { - fn motion( + fn begin_move(&mut self, data: &mut State) -> bool { + 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, - handle: &mut PointerInnerHandle<'_, State>, - _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>, - event: &MotionEvent, - ) { - // While the grab is active, no client has pointer focus. - handle.motion(data, None, event); + location: Point<f64, Logical>, + timestamp: Duration, + ) -> bool { + let mut delta = location - self.last_location; + self.last_location = location; - 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; + // Try to recognize the gesture. + if self.gesture == GestureState::Recognizing { + // Check if the window has closed. + if !self.window.alive() { + return false; + } - if self.gesture == GestureState::Recognizing { - let c = event.location - self.start_data.location; + // 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); - // Check if the gesture moved far enough to decide. - if c.x * c.x + c.y * c.y >= 8. * 8. { - self.gesture = GestureState::Move; + let is_view_offset = + self.enable_view_offset && !is_floating && c.x.abs() > c.y.abs(); - data.niri - .cursor_manager - .set_cursor_image(CursorImageStatus::Named(CursorIcon::Move)); - } + let started = if is_view_offset { + self.begin_view_offset(data) + } else { + self.begin_move(data) + }; + if !started { + return false; } - if self.gesture != GestureState::Move { - return; - } + // 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, - event_delta, + delta, output, pos_within_output, ); if ongoing { // FIXME: only redraw the previous and the new output. data.niri.queue_redraw_all(); - return; + return true; } - } else { - return; + } + 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; } } - // The move is no longer ongoing. - handle.unset_grab(self, data, event.serial, event.time, true); + data.niri.layout.toggle_window_floating(Some(&self.window)); + data.niri.queue_redraw_all(); + + true + } +} + +impl PointerGrab<State> for MoveGrab { + fn motion( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>, + event: &MotionEvent, + ) { + // While the grab is active, no client has pointer focus. + handle.motion(data, None, event); + + 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, event.serial, event.time, true); + } } fn relative_motion( @@ -129,18 +303,25 @@ impl PointerGrab<State> for MoveGrab { ) { 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. - let toggle_floating_button = if self.start_data.button == 0x110 { + let toggle_floating_button = if start_data.button == 0x110 { 0x111 } else { 0x110 }; - if event.button == toggle_floating_button && event.state == ButtonState::Pressed { - data.niri.layout.toggle_window_floating(Some(&self.window)); + if event.state != ButtonState::Pressed || event.button != toggle_floating_button { + return; } - if !handle.current_pressed().contains(&self.start_data.button) { - // The button that initiated the grab was released. + if !self.on_toggle_floating(data) { 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> { - &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) { diff --git a/src/input/touch_move_grab.rs b/src/input/touch_move_grab.rs deleted file mode 100644 index e69f4c6e..00000000 --- a/src/input/touch_move_grab.rs +++ /dev/null @@ -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); - } -} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 8761c0e3..9fd99a32 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -4040,16 +4040,12 @@ impl<W: LayoutElement> Layout<W> { mon.dnd_scroll_gesture_end(); } - let mut ws_id = None; for ws in self.workspaces_mut() { - let id = ws.id(); if let Some(tile) = ws.tiles_mut().find(|tile| *tile.window().id() == window_id) { let offset = tile.interactive_move_offset; tile.interactive_move_offset = Point::from((0., 0.)); tile.animate_move_from(offset); - - ws_id = Some(id); } // 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; } InteractiveMoveState::Moving(move_) => move_, |
