From d2e394b957162b7849c7698bb4db954bc49f7bec Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 20 Dec 2023 20:19:42 +0000 Subject: [PATCH] menu: Allow toggling sticky state --- resources/i18n/en/cosmic_comp.ftl | 3 +- src/shell/element/mod.rs | 4 + src/shell/element/stack.rs | 22 ++++- src/shell/element/window.rs | 22 ++++- src/shell/focus/mod.rs | 4 + src/shell/grabs/menu/default.rs | 28 ++++--- src/shell/mod.rs | 128 ++++++++++++++++++++++++++++-- 7 files changed, 187 insertions(+), 24 deletions(-) diff --git a/resources/i18n/en/cosmic_comp.ftl b/resources/i18n/en/cosmic_comp.ftl index d0a02a91..aade9afe 100644 --- a/resources/i18n/en/cosmic_comp.ftl +++ b/resources/i18n/en/cosmic_comp.ftl @@ -14,8 +14,7 @@ window-menu-move-next-workspace = Move to next workspace window-menu-stack = Create window stack window-menu-unstack-all = Unstack windows window-menu-unstack = Unstack window -window-menu-always-on-top = Always on top -window-menu-always-on-visible-ws = Always on visible workspace +window-menu-sticky = Sticky window window-menu-close = Close window-menu-close-all = Close all windows window-menu-resize-edge-top = Top diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 77d0a4cc..3be54355 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -108,6 +108,8 @@ pub struct CosmicMapped { pub last_geometry: Arc>>>, pub moved_since_mapped: Arc, pub floating_tiled: Arc>>, + //sticky + pub previous_layer: Arc>>, #[cfg(feature = "debug")] debug: Arc>>, @@ -1118,6 +1120,7 @@ impl From for CosmicMapped { last_geometry: Arc::new(Mutex::new(None)), moved_since_mapped: Arc::new(AtomicBool::new(false)), floating_tiled: Arc::new(Mutex::new(None)), + previous_layer: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } @@ -1135,6 +1138,7 @@ impl From for CosmicMapped { last_geometry: Arc::new(Mutex::new(None)), moved_since_mapped: Arc::new(AtomicBool::new(false)), floating_tiled: Arc::new(Mutex::new(None)), + previous_layer: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index c67a352d..8fdb43ba 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -692,12 +692,27 @@ impl Program for CosmicStackInternal { if let Some(mapped) = state.common.shell.element_for_wl_surface(&surface).cloned() { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let position = workspace + let position = if let Some((output, set)) = + state.common.shell.workspaces.sets.iter().find(|(_, set)| { + set.sticky_layer.mapped().any(|m| m == &mapped) + }) { + set.sticky_layer .element_geometry(&mapped) .unwrap() .loc - .to_global(&workspace.output); + .to_global(output) + } else if let Some(workspace) = + state.common.shell.space_for_mut(&mapped) + { + workspace + .element_geometry(&mapped) + .unwrap() + .loc + .to_global(&workspace.output) + } else { + return; + }; + let mut cursor = seat .get_pointer() .unwrap() @@ -712,7 +727,6 @@ impl Program for CosmicStackInternal { cursor - position.as_logical(), true, ); - } } }); } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index b4919173..1ed7d9a2 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -292,12 +292,27 @@ impl Program for CosmicWindowInternal { if let Some(mapped) = state.common.shell.element_for_wl_surface(&surface).cloned() { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let position = workspace + let position = if let Some((output, set)) = + state.common.shell.workspaces.sets.iter().find(|(_, set)| { + set.sticky_layer.mapped().any(|m| m == &mapped) + }) { + set.sticky_layer .element_geometry(&mapped) .unwrap() .loc - .to_global(&workspace.output); + .to_global(output) + } else if let Some(workspace) = + state.common.shell.space_for_mut(&mapped) + { + workspace + .element_geometry(&mapped) + .unwrap() + .loc + .to_global(&workspace.output) + } else { + return; + }; + let mut cursor = seat .get_pointer() .unwrap() @@ -312,7 +327,6 @@ impl Program for CosmicWindowInternal { cursor - position.as_logical(), false, ); - } } }); } diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 446a0101..16331c0e 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -58,6 +58,10 @@ impl<'a> FocusStackMut<'a> { self.0.insert(window.clone()); } + pub fn remove(&mut self, window: &CosmicMapped) { + self.0.retain(|w| w != window); + } + pub fn last(&self) -> Option<&CosmicMapped> { self.0.iter().rev().find(|w| w.alive()) } diff --git a/src/shell/grabs/menu/default.rs b/src/shell/grabs/menu/default.rs index 0d312e3e..bc7d0c56 100644 --- a/src/shell/grabs/menu/default.rs +++ b/src/shell/grabs/menu/default.rs @@ -167,13 +167,11 @@ pub fn window_items( window: &CosmicMapped, is_tiled: bool, is_stacked: bool, + is_sticky: bool, tiling_enabled: bool, possible_resizes: ResizeEdge, config: &StaticConfig, ) -> impl Iterator { - //let is_always_on_top = false; // TODO check window (potentially shell?) - //let is_always_on_visible_ws = false; // TODO check window (potentially shell?) - let maximize_clone = window.clone(); let tile_clone = window.clone(); let move_prev_clone = window.clone(); @@ -186,6 +184,7 @@ pub fn window_items( let unstack_clone = window.clone(); let screenshot_clone = window.clone(); let stack_clone = window.clone(); + let sticky_clone = window.clone(); let close_clone = window.clone(); vec![ @@ -275,14 +274,14 @@ pub fn window_items( .disabled(!possible_resizes.contains(ResizeEdge::BOTTOM)), ], )), - Some( + (!is_sticky).then_some( Item::new(fl!("window-menu-move-prev-workspace"), move |handle| { let mapped = move_prev_clone.clone(); let _ = handle.insert_idle(move |state| move_prev_workspace(state, &mapped)); }) .shortcut(config.get_shortcut_for_action(&Action::MoveToPreviousWorkspace)), ), - Some( + (!is_sticky).then_some( Item::new(fl!("window-menu-move-next-workspace"), move |handle| { let mapped = move_next_clone.clone(); let _ = handle.insert_idle(move |state| move_next_workspace(state, &mapped)); @@ -297,10 +296,21 @@ pub fn window_items( .shortcut(config.get_shortcut_for_action(&Action::ToggleStacking)), ), Some(Item::Separator), - //Some(Item::new(fl!("window-menu-always-on-top"), |handle| {}).toggled(is_always_on_top)), - //Some(Item::new(fl!("window-menu-always-on-visible-ws"), |handle| {}) - // .toggled(is_always_on_visible_ws)), - //Some(Item::Separator), + Some( + Item::new(fl!("window-menu-sticky"), move |handle| { + let mapped = sticky_clone.clone(); + let _ = handle.insert_idle(move |state| { + let seat = state.common.last_active_seat().clone(); + let seats = state.common.seats().cloned().collect::>(); + state + .common + .shell + .toggle_sticky(seats.iter(), &seat, &mapped); + }); + }) + .toggled(is_sticky), + ), + Some(Item::Separator), if is_stacked { Some(Item::new(fl!("window-menu-close-all"), move |_handle| { for (window, _) in close_clone.windows() { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 8a5f692e..e892b3dd 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1970,13 +1970,37 @@ impl Shell { check_grab_preconditions(&seat, surface, serial, ReleaseMode::NoMouseButtons) { if let Some(mapped) = state.common.shell.element_for_wl_surface(surface).cloned() { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let output = seat.active_output(); let (_, relative_loc) = mapped .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) .unwrap(); + let (global_position, edge, is_tiled, is_stacked, is_sticky, tiling_enabled) = + if let Some(set) = state + .common + .shell + .workspaces + .sets + .values_mut() + .find(|set| set.sticky_layer.mapped().any(|m| m == &mapped)) + { + let output = set.output.clone(); + let global_position = + (set.sticky_layer.element_geometry(&mapped).unwrap().loc + + relative_loc.as_local() + + location.as_local()) + .to_global(&output); + ( + global_position, + ResizeEdge::all(), + false, + mapped.is_stack(), + true, + false, + ) + } else if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + let output = seat.active_output(); + let global_position = (workspace.element_geometry(&mapped).unwrap().loc + relative_loc.as_local() + location.as_local()) @@ -1998,8 +2022,19 @@ impl Shell { } else { ResizeEdge::all() }; - let is_stacked = mapped.is_stack(); - let tiling_enabled = workspace.tiling_enabled; + + ( + global_position, + edge, + is_tiled, + mapped.is_stack(), + false, + workspace.tiling_enabled, + ) + } else { + return; + }; + let grab = MenuGrab::new( start_data, seat, @@ -2008,6 +2043,7 @@ impl Shell { &mapped, is_tiled, is_stacked, + is_sticky, tiling_enabled, edge, &state.common.config.static_conf, @@ -2034,7 +2070,6 @@ impl Shell { serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), Focus::Keep, ); - } } } } @@ -2242,6 +2277,89 @@ impl Shell { } } + pub fn toggle_sticky<'a>( + &mut self, + seats: impl Iterator>, + seat: &Seat, + mapped: &CosmicMapped, + ) { + // clean from focus-stacks + let seats = seats.collect::>(); + for workspace in self.workspaces.spaces_mut() { + for seat in seats.iter() { + let mut stack = workspace.focus_stack.get_mut(seat); + stack.remove(mapped); + } + } + + if let Some(workspace) = self.space_for_mut(mapped) { + if workspace.is_fullscreen(mapped) { + // we are making it sticky, we don't need to move it to it's previous workspace + let _ = workspace.remove_fullscreen(); + } + let previous_layer = if workspace.is_tiled(mapped) { + workspace.toggle_floating_window(seat, mapped); + ManagedLayer::Tiling + } else { + ManagedLayer::Floating + }; + let geometry = workspace.element_geometry(mapped).unwrap(); + workspace.unmap(mapped); + + *mapped.previous_layer.lock().unwrap() = Some(previous_layer); + let output = workspace.output().clone(); + let handle = workspace.handle; + + for (window, _) in mapped.windows() { + self.toplevel_info_state + .toplevel_leave_workspace(&window, &handle); + } + + self.workspaces + .sets + .get_mut(&output) + .unwrap() + .sticky_layer + .map(mapped.clone(), geometry.loc); + } else if let Some(set) = self + .workspaces + .sets + .values_mut() + .find(|set| set.sticky_layer.mapped().any(|m| m == mapped)) + { + let geometry = set.sticky_layer.element_geometry(mapped).unwrap(); + set.sticky_layer.unmap(mapped); + + let workspace = &mut set.workspaces[set.active]; + for (window, _) in mapped.windows() { + self.toplevel_info_state + .toplevel_enter_workspace(&window, &workspace.handle); + } + + match mapped + .previous_layer + .lock() + .unwrap() + .take() + .unwrap_or(ManagedLayer::Floating) + { + ManagedLayer::Floating => { + workspace.floating_layer.map(mapped.clone(), geometry.loc) + } + ManagedLayer::Tiling => { + let focus_stack = workspace.focus_stack.get(seat); + workspace.tiling_layer.map( + mapped.clone(), + Some(focus_stack.iter()), + None, + false, + ); + } + ManagedLayer::Sticky => unreachable!(), + } + } + } + pub(crate) fn set_theme(&mut self, theme: cosmic::Theme) { self.theme = theme.clone(); self.refresh();