aboutsummaryrefslogtreecommitdiff
path: root/src/layout/workspace.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-09-02 08:07:22 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-10-15 09:04:16 +0300
commite1fad994da9565b43c7fb139cb2fb7bf404cc320 (patch)
tree305fa0714d66ad2b4346b3aee6eb785099b29fa1 /src/layout/workspace.rs
parente5d4e7c1b1a0b61770b6711a53fe41920d56452d (diff)
downloadniri-e1fad994da9565b43c7fb139cb2fb7bf404cc320.tar.gz
niri-e1fad994da9565b43c7fb139cb2fb7bf404cc320.tar.bz2
niri-e1fad994da9565b43c7fb139cb2fb7bf404cc320.zip
Implement maximize-to-edges (true Wayland maximize)
Diffstat (limited to 'src/layout/workspace.rs')
-rw-r--r--src/layout/workspace.rs131
1 files changed, 106 insertions, 25 deletions
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 32ce3c9e..27a593ae 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -611,16 +611,16 @@ impl<W: LayoutElement> Workspace<W> {
is_floating: bool,
) {
self.enter_output_for_window(tile.window());
- tile.unfullscreen_to_floating = is_floating;
+ tile.restore_to_floating = is_floating;
match target {
WorkspaceAddWindowTarget::Auto => {
// Don't steal focus from an active fullscreen window.
let activate = activate.map_smart(|| !self.is_active_pending_fullscreen());
- // If the tile is pending fullscreen, open it in the scrolling layout where it can
- // go fullscreen.
- if is_floating && !tile.window().is_pending_fullscreen() {
+ // If the tile is pending maximized or fullscreen, open it in the scrolling layout
+ // where it can do that.
+ if is_floating && tile.window().pending_sizing_mode().is_normal() {
self.floating.add_tile(tile, activate);
if activate || self.scrolling.is_empty() {
@@ -649,7 +649,7 @@ impl<W: LayoutElement> Workspace<W> {
let floating_has_window = self.floating.has_window(next_to);
- if is_floating && !tile.window().is_pending_fullscreen() {
+ if is_floating && tile.window().pending_sizing_mode().is_normal() {
if floating_has_window {
self.floating.add_tile_above(next_to, tile, activate);
} else {
@@ -869,6 +869,8 @@ impl<W: LayoutElement> Workspace<W> {
toplevel.with_pending_state(|state| {
if state.states.contains(xdg_toplevel::State::Fullscreen) {
state.size = Some(self.view_size.to_i32_round());
+ } else if state.states.contains(xdg_toplevel::State::Maximized) {
+ state.size = Some(self.working_area.size.to_i32_round());
} else {
let size =
self.new_window_size(width, height, is_floating, rules, (min_size, max_size));
@@ -1257,10 +1259,10 @@ impl<W: LayoutElement> Workspace<W> {
}
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {
- let mut unfullscreen_to_floating = false;
+ let mut restore_to_floating = false;
if self.floating.has_window(window) {
if is_fullscreen {
- unfullscreen_to_floating = true;
+ restore_to_floating = true;
self.toggle_window_floating(Some(window));
} else {
// Floating windows are never fullscreen, so this is an unfullscreen request for an
@@ -1271,30 +1273,44 @@ impl<W: LayoutElement> Workspace<W> {
// The window is in the scrolling layout and we're requesting an unfullscreen. If it is
// indeed fullscreen (i.e. this isn't a duplicate unfullscreen request), then we may
// need to unfullscreen into floating.
- let tile = self
+ let col = self
.scrolling
- .tiles()
- .find(|tile| tile.window().id() == window)
+ .columns()
+ .find(|col| col.contains(window))
.unwrap();
- if tile.window().is_pending_fullscreen() && tile.unfullscreen_to_floating {
- // Unfullscreen and float in one call so it has a chance to notice and request a
- // (0, 0) size, rather than the scrolling column size.
- self.toggle_window_floating(Some(window));
- return;
+
+ // When going from fullscreen to maximized, don't consider restore_to_floating yet.
+ if col.is_pending_fullscreen() && !col.is_pending_maximized() {
+ let (tile, _) = col
+ .tiles()
+ .find(|(tile, _)| tile.window().id() == window)
+ .unwrap();
+ if tile.restore_to_floating {
+ // Unfullscreen and float in one call so it has a chance to notice and request a
+ // (0, 0) size, rather than the scrolling column size.
+ self.toggle_window_floating(Some(window));
+ return;
+ }
}
}
- let changed = self.scrolling.set_fullscreen(window, is_fullscreen);
+ let tile = self
+ .scrolling
+ .tiles()
+ .find(|tile| tile.window().id() == window)
+ .unwrap();
+ let was_normal = tile.window().pending_sizing_mode().is_normal();
- // When going to fullscreen, remember if we should unfullscreen to floating.
- if changed && is_fullscreen {
- let tile = self
- .scrolling
- .tiles_mut()
- .find(|tile| tile.window().id() == window)
- .unwrap();
+ self.scrolling.set_fullscreen(window, is_fullscreen);
- tile.unfullscreen_to_floating = unfullscreen_to_floating;
+ // When going from normal to fullscreen, remember if we should unfullscreen to floating.
+ let tile = self
+ .scrolling
+ .tiles_mut()
+ .find(|tile| tile.window().id() == window)
+ .unwrap();
+ if was_normal && !tile.window().pending_sizing_mode().is_normal() {
+ tile.restore_to_floating = restore_to_floating;
}
}
@@ -1303,10 +1319,75 @@ impl<W: LayoutElement> Workspace<W> {
.tiles()
.find(|tile| tile.window().id() == window)
.unwrap();
- let current = tile.window().is_pending_fullscreen();
+ let current = tile.window().pending_sizing_mode().is_fullscreen();
self.set_fullscreen(window, !current);
}
+ pub fn set_maximized(&mut self, window: &W::Id, maximize: bool) {
+ let mut restore_to_floating = false;
+ if self.floating.has_window(window) {
+ if maximize {
+ restore_to_floating = true;
+ self.toggle_window_floating(Some(window));
+ } else {
+ // Floating windows are never maximized, so this is an unmaximize request for an
+ // already unmaximized window.
+ return;
+ }
+ } else if !maximize {
+ // The window is in the scrolling layout and we're requesting to unmaximize. If it is
+ // indeed maximized (i.e. this isn't a duplicate unmaximize request), then we may
+ // need to unmaximize into floating.
+ let tile = self
+ .scrolling
+ .tiles()
+ .find(|tile| tile.window().id() == window)
+ .unwrap();
+ // The tile cannot unmaximize into fullscreen (pending_sizing_mode() will be fullscreen
+ // in that case and not maximized), so this check works.
+ if tile.window().pending_sizing_mode().is_maximized() && tile.restore_to_floating {
+ // Unmaximize and float in one call so it has a chance to notice and request a
+ // (0, 0) size, rather than the scrolling column size.
+ self.toggle_window_floating(Some(window));
+ return;
+ }
+ }
+
+ let tile = self
+ .scrolling
+ .tiles()
+ .find(|tile| tile.window().id() == window)
+ .unwrap();
+ let was_normal = tile.window().pending_sizing_mode().is_normal();
+
+ self.scrolling.set_maximized(window, maximize);
+
+ // When going from normal to maximized, remember if we should unmaximize to floating.
+ let tile = self
+ .scrolling
+ .tiles_mut()
+ .find(|tile| tile.window().id() == window)
+ .unwrap();
+ if was_normal && !tile.window().pending_sizing_mode().is_normal() {
+ tile.restore_to_floating = restore_to_floating;
+ }
+ }
+
+ pub fn toggle_maximized(&mut self, window: &W::Id) {
+ let mut current = false;
+
+ // We have to check the column property in case the window is in the scrolling layout and
+ // both maximized and fullscreen. In this case, only the column knows whether it's
+ // maximized.
+ //
+ // In the floating layout, windows cannot be maximized.
+ if let Some(col) = self.scrolling.columns().find(|col| col.contains(window)) {
+ current = col.is_pending_maximized();
+ }
+
+ self.set_maximized(window, !current);
+ }
+
pub fn toggle_window_floating(&mut self, id: Option<&W::Id>) {
let active_id = self.active_window().map(|win| win.id().clone());
let target_is_active = id.map_or(true, |id| Some(id) == active_id.as_ref());