From e67e139e15ea83bd14cff6b9e75cbf9e91be5dac Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 20 Dec 2023 20:39:51 +0000 Subject: [PATCH] focus: Allow sticky windows in focus_stacks, allow dialogs to stay on top --- src/shell/focus/mod.rs | 49 ++++++++++++++++++++++++++++------- src/shell/mod.rs | 59 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 16331c0e..c5141956 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -1,5 +1,5 @@ use crate::{ - shell::{element::CosmicMapped, Shell, Workspace}, + shell::{element::CosmicMapped, Shell}, state::Common, utils::prelude::*, wayland::handlers::xdg_shell::PopupGrabData, @@ -71,8 +71,6 @@ impl<'a> FocusStackMut<'a> { } } -impl Workspace {} - pub struct ActiveFocus(RefCell>); impl ActiveFocus { @@ -113,7 +111,16 @@ impl Shell { }; if let Some(mapped) = element { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + let workspace = state.common.shell.space_for_mut(&mapped); + let workspace = if workspace.is_none() { + state + .common + .shell + .active_space_mut(&active_seat.active_output()) + } else { + workspace.unwrap() + }; + let mut focus_stack = workspace.focus_stack.get_mut(active_seat); if Some(&mapped) != focus_stack.last() { trace!(?mapped, "Focusing window."); @@ -126,7 +133,6 @@ impl Shell { { if !popup_grab.has_ended() { popup_grab.ungrab(PopupUngrabStrategy::All); - } } } } @@ -173,7 +179,15 @@ impl Shell { .collect::>(); for output in self.outputs().cloned().collect::>().into_iter() { - // TODO: Add self.workspaces.active_workspaces() + let set = self.workspaces.sets.get_mut(&output).unwrap(); + for focused in focused_windows.iter() { + raise_with_children(&mut set.sticky_layer, focused); + } + for window in set.sticky_layer.mapped() { + window.set_activated(focused_windows.contains(&window)); + window.configure(); + } + let workspace = self.workspaces.active_mut(&output); for focused in focused_windows.iter() { raise_with_children(&mut workspace.floating_layer, focused); @@ -306,7 +320,7 @@ impl Common { } fn focus_target_is_valid( - state: &State, + state: &mut State, seat: &Seat, output: &Output, target: KeyboardFocusTarget, @@ -329,10 +343,27 @@ fn focus_target_is_valid( match target { KeyboardFocusTarget::Element(mapped) => { + let is_sticky = state + .common + .shell + .workspaces + .sets + .get(output) + .unwrap() + .sticky_layer + .mapped() + .any(|m| m == &mapped); + let workspace = state.common.shell.active_space(&output); let focus_stack = workspace.focus_stack.get(&seat); - focus_stack.last().map(|m| m == &mapped).unwrap_or(false) - && workspace.get_fullscreen().is_none() + let is_in_focus_stack = focus_stack.last().map(|m| m == &mapped).unwrap_or(false); + let has_fullscreen = workspace.get_fullscreen().is_some(); + + if is_sticky && !is_in_focus_stack { + Shell::append_focus_stack(state, Some(&KeyboardFocusTarget::Element(mapped)), seat); + } + + (is_sticky || is_in_focus_stack) && !has_fullscreen } KeyboardFocusTarget::LayerSurface(layer) => { layer_map_for_output(&output).layers().any(|l| l == &layer) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index b870d447..5dad7d26 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1569,6 +1569,27 @@ impl Shell { .unwrap(); let (window, seat, output) = state.common.shell.pending_windows.remove(pos); + let parent_is_sticky = match window.clone() { + CosmicSurface::Wayland(toplevel) => { + if let Some(parent) = toplevel.toplevel().parent() { + if let Some(elem) = state.common.shell.element_for_wl_surface(&parent) { + state + .common + .shell + .workspaces + .sets + .values() + .any(|set| set.sticky_layer.mapped().any(|m| m == elem)) + } else { + false + } + } else { + false + } + } + _ => false, + }; + let pending_activation = state .common .shell @@ -1678,7 +1699,7 @@ impl Shell { .collect::>() .into_iter() { - workspace.unmaximize_request(&mapped.active_window()); + workspace.unmaximize_request(&mapped); } let focus_stack = workspace.focus_stack.get(&seat); workspace @@ -1686,17 +1707,39 @@ impl Shell { .map(mapped.clone(), Some(focus_stack.iter()), None, true); } - if should_be_fullscreen { + if !parent_is_sticky && should_be_fullscreen { workspace.fullscreen_request(&mapped.active_window(), None); } - if workspace.output == seat.active_output() && active_handle == workspace.handle { + let was_activated = workspace_handle.is_some(); + let workspace_handle = workspace.handle; + let workspace_output = workspace.output.clone(); + + if parent_is_sticky { + let seats = state.common.seats().cloned().collect::>(); + state + .common + .shell + .toggle_sticky(seats.iter(), &seat, &mapped); + } + + if (workspace_output == seat.active_output() && active_handle == workspace_handle) + || parent_is_sticky + { // TODO: enforce focus stealing prevention by also checking the same rules as for the else case. - Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None); - } else if workspace_empty || workspace_handle.is_some() || should_be_fullscreen { - let handle = workspace.handle; - Shell::append_focus_stack(state, Some(&KeyboardFocusTarget::from(mapped)), &seat); - state.common.shell.set_urgent(&handle); + Shell::set_focus( + state, + Some(&KeyboardFocusTarget::from(mapped.clone())), + &seat, + None, + ); + } else if workspace_empty || was_activated || should_be_fullscreen { + Shell::append_focus_stack( + state, + Some(&KeyboardFocusTarget::from(mapped.clone())), + &seat, + ); + state.common.shell.set_urgent(&workspace_handle); } let active_space = state.common.shell.active_space(&output);