diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 1f3a7334e..582ec1b47 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1641,6 +1641,23 @@ impl FloatingLayout { elements } + pub fn snap_to_corner(&self, mapped: &CosmicMapped, corners: &TiledCorners) { + *mapped.floating_tiled.lock().unwrap() = Some(*corners); + mapped.set_tiled(true); + let snapped_geo = self.snapped_geometry(corners); + let output = self.space.outputs().next().unwrap(); + mapped.set_geometry(snapped_geo.to_global(output)); + mapped.configure(); + } + + fn snapped_geometry(&self, corners: &TiledCorners) -> Rectangle { + let output = self.space.outputs().next().unwrap().clone(); + let layers = layer_map_for_output(&output); + let non_exclusive = layers.non_exclusive_zone(); + std::mem::drop(layers); + corners.relative_geometry(non_exclusive, self.gaps()) + } + fn gaps(&self) -> (i32, i32) { let g = self.theme.cosmic().gaps; (g.0 as i32, g.1 as i32) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 1512ab492..6ba300d13 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2571,6 +2571,7 @@ impl Shell { state: FloatingRestoreData { was_maximized, + was_snapped, geometry, .. }, @@ -2594,6 +2595,8 @@ impl Shell { fullscreen_geometry, true, ); + } else if let Some(corners) = was_snapped { + workspace.floating_layer.snap_to_corner(&window, &corners); } } Some(FullscreenRestoreState::Tiling { @@ -3342,13 +3345,30 @@ impl Shell { let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above if !to_workspace.tiling_enabled { - let position = match window_state { - WorkspaceRestoreData::Floating(Some(data)) => { - Some(data.position_relative(to_workspace.output.geometry().size.as_logical())) - } - _ => None, + let (position, was_maximized, was_snapped) = match &window_state { + WorkspaceRestoreData::Floating(Some(data)) => ( + Some(data.position_relative(to_workspace.output.geometry().size.as_logical())), + data.was_maximized, + data.was_snapped, + ), + _ => (None, false, None), }; to_workspace.floating_layer.map(mapped.clone(), position); + if was_maximized { + let geometry = to_workspace + .floating_layer + .element_geometry(mapped) + .unwrap(); + *mapped.maximized_state.lock().unwrap() = Some(MaximizedState { + original_geometry: geometry, + original_layer: ManagedLayer::Floating, + }); + to_workspace + .floating_layer + .map_maximized(mapped.clone(), geometry, false); + } else if let Some(corners) = was_snapped { + to_workspace.floating_layer.snap_to_corner(mapped, &corners); + } } else { for mapped in to_workspace .mapped() @@ -4145,6 +4165,7 @@ impl Shell { geometry: geo, output_size: set.output.geometry().size.as_logical(), was_maximized: false, + was_snapped: None, }, }); } else if let Some((workspace, window)) = @@ -4658,6 +4679,11 @@ impl Shell { stack.remove_window(&surface); surface } else { + // Must be set before `map_internal`/`unmap` below, as both may call + // intermediate `configure()`, which would send a configure event without the + // fullscreen state, causing clients like Chromium to cancel the transition. + mapped.set_fullscreen(true); + if let Some(state) = mapped.maximized_state.lock().unwrap().take() { mapped.set_maximized(false); set.sticky_layer.map_internal( @@ -4689,6 +4715,7 @@ impl Shell { geometry: from, output_size: workspace.output.geometry().size.as_logical(), was_maximized, + was_snapped: None, }, }), Some(from), @@ -4699,6 +4726,11 @@ impl Shell { return None; } + // Must be set before `unmap_surface()`. + // `Workspace::unmap_surface` may call intermediate `configure()` internally, which would send + // a configure event without the fullscreen state, causing clients like Chromium to cancel the transition. + mapped.set_fullscreen(true); + let from = workspace.element_geometry(&mapped).unwrap(); let (surface, state) = workspace.unmap_surface(surface).unwrap(); window = surface; diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 1c9da011e..b03ac7766 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -8,7 +8,10 @@ use crate::{ }, shell::{ ANIMATION_DURATION, OverviewMode, SeatMoveGrabState, - layout::{floating::FloatingLayout, tiling::TilingLayout}, + layout::{ + floating::{FloatingLayout, TiledCorners}, + tiling::TilingLayout, + }, }, state::State, utils::{prelude::*, tween::EaseRectangle}, @@ -286,6 +289,7 @@ pub struct FloatingRestoreData { pub geometry: Rectangle, pub output_size: Size, pub was_maximized: bool, + pub was_snapped: Option, } impl FloatingRestoreData { @@ -637,6 +641,7 @@ impl Workspace { }))); } + let was_snapped = mapped.floating_tiled.lock().unwrap().clone(); // unmaximize_request might have triggered a `floating_layer.refresh()`, // which may have already removed a non-alive surface. if let Some(floating_geometry) = self.floating_layer.unmap(mapped, None).or(was_maximized) { @@ -644,6 +649,7 @@ impl Workspace { geometry: floating_geometry, output_size: self.output.geometry().size.as_logical(), was_maximized: was_maximized.is_some(), + was_snapped, }))); }; @@ -1066,6 +1072,7 @@ impl Workspace { mapped.set_minimized(true); mapped.configure(); + let was_snapped = mapped.floating_tiled.lock().unwrap().clone(); if let Some(geometry) = self.floating_layer.unmap(&mapped, Some(to)) { return Some(MinimizedWindow::Floating { window: mapped, @@ -1073,6 +1080,7 @@ impl Workspace { geometry: was_maximized.unwrap_or(geometry), output_size: self.output.geometry().size.as_logical(), was_maximized: was_maximized.is_some(), + was_snapped, }, }); } @@ -1133,6 +1141,8 @@ impl Workspace { }); std::mem::drop(state); self.floating_layer.map_maximized(window, geometry, true); + } else if let Some(corners) = previous.was_snapped { + self.floating_layer.snap_to_corner(&window, &corners); } None