From 7fd033295fdff18a730d60b7b49940c4c44ef15f Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 30 Oct 2025 13:59:26 -0700 Subject: [PATCH] focus-stack: Keep dragged surface in focus stack in `move_request()` Replacement for https://github.com/pop-os/cosmic-comp/pull/1687, that works correctly with multiple outputs. We don't want another window to show a focus indicator while a window is being dragged, so keep the window in the focus stack. If a window is being moved out of a stack, change the focus from the stack to the window. `refresh_focus_stack()` doesn't seem to be called here, but for good measure, make sure that calling that function also won't remove a `CosmicMapped` from the focus stack if it is currently part of a move grab for the seat. --- src/shell/focus/mod.rs | 4 ++-- src/shell/mod.rs | 18 +++++++++--------- src/shell/workspace.rs | 20 ++++++++++++++++++-- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 6b8830d5..34fee301 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -129,11 +129,11 @@ impl FocusStackMut<'_> { self.0.insert(target); } - pub fn remove(&mut self, target: &T) + pub fn remove(&mut self, target: &T) -> bool where T: Hash + indexmap::Equivalent, { - self.0.shift_remove(target); + self.0.shift_remove(target) } pub fn last(&self) -> Option<&FocusTarget> { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index b6e73de6..128fec17 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3415,15 +3415,6 @@ impl Shell { return None; } - if !move_out_of_stack { - for workspace in self.workspaces.spaces_mut() { - for seat in self.seats.iter() { - let mut stack = workspace.focus_stack.get_mut(seat); - stack.remove(&old_mapped); - } - } - } - let (window, _) = old_mapped .windows() .find(|(w, _)| w.wl_surface().as_deref() == Some(surface)) @@ -3441,6 +3432,15 @@ impl Shell { old_mapped.clone() }; + if move_out_of_stack { + // Update focus stack to set focus to the window being dragged out of + // the stack. + if let Some(workspace) = self.space_for_mut(&old_mapped) { + let mut stack = workspace.focus_stack.get_mut(seat); + stack.append(mapped.clone()); + } + } + let trigger = match &start_data { GrabStartData::Pointer(start_data) => Trigger::Pointer(start_data.button), GrabStartData::Touch(start_data) => Trigger::Touch(start_data.slot), diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 698f1b84..6c05f0ff 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -7,7 +7,7 @@ use crate::{ element::{AsGlowRenderer, FromGlesError}, }, shell::{ - ANIMATION_DURATION, OverviewMode, + ANIMATION_DURATION, OverviewMode, SeatMoveGrabState, layout::{floating::FloatingLayout, tiling::TilingLayout}, }, state::State, @@ -455,17 +455,33 @@ impl Workspace { } pub fn refresh_focus_stack(&mut self) { - for stack in self.focus_stack.0.values_mut() { + for (seat, stack) in self.focus_stack.0.iter_mut() { let fullscreen = self .fullscreen .as_ref() .filter(|f| f.alive()) .filter(|f| f.ended_at.is_none()) .map(|f| &f.surface); + + // Move grab is treated as focused, so don't change focus to a + // window while grab exists. + let move_grab_state = seat + .user_data() + .get::() + .unwrap() + .lock() + .unwrap(); + let move_mapped = if let Some(move_grab_state) = &*move_grab_state { + Some(move_grab_state.element()) + } else { + None + }; + let mapped = || { self.floating_layer .mapped() .chain(self.tiling_layer.mapped().map(|(w, _)| w)) + .chain(move_mapped.iter()) }; stack.retain(|w| match w { FocusTarget::Fullscreen(s) => fullscreen.is_some_and(|f| f == s),