diff options
Diffstat (limited to 'src/layout/scrolling.rs')
| -rw-r--r-- | src/layout/scrolling.rs | 363 |
1 files changed, 278 insertions, 85 deletions
diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index 02541d1b..fc163d72 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -18,6 +18,7 @@ use super::workspace::{InteractiveResize, ResolvedSize}; use super::{ConfigureIntent, HitType, InteractiveResizeData, LayoutElement, Options, RemovedTile}; use crate::animation::{Animation, Clock}; use crate::input::swipe_tracker::SwipeTracker; +use crate::layout::SizingMode; use crate::niri_render_elements; use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::RenderTarget; @@ -62,8 +63,8 @@ pub struct ScrollingSpace<W: LayoutElement> { /// The value is the view offset that the previous column had before, to restore it. activate_prev_column_on_removal: Option<f64>, - /// View offset to restore after unfullscreening. - view_offset_before_fullscreen: Option<f64>, + /// View offset to restore after unfullscreening or unmaximizing. + view_offset_to_restore: Option<f64>, /// Windows in the closing animation. closing_windows: Vec<ClosingWindow>, @@ -177,6 +178,13 @@ pub struct Column<W: LayoutElement> { /// take some time to catch up and actually unfullscreen. is_pending_fullscreen: bool, + /// Whether this column is going to be maximized. + /// + /// Can be `true` together with `is_pending_fullscreen`, which means that the column is + /// effectively pending fullscreen, but unfullscreening should go back to maximized state, + /// rather than normal. + is_pending_maximized: bool, + /// How this column displays and arranges windows. display_mode: ColumnDisplay, @@ -192,6 +200,11 @@ pub struct Column<W: LayoutElement> { /// Latest known working area for this column's workspace. working_area: Rectangle<f64, Logical>, + /// Working area for this column's workspace excluding struts. + /// + /// Used for maximize-to-edges. + parent_area: Rectangle<f64, Logical>, + /// Scale of the output the column is on (and rounds its sizes to). scale: f64, @@ -285,7 +298,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { interactive_resize: None, view_offset: ViewOffset::Static(0.), activate_prev_column_on_removal: None, - view_offset_before_fullscreen: None, + view_offset_to_restore: None, closing_windows: Vec::new(), view_size, working_area, @@ -306,7 +319,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { let working_area = compute_working_area(parent_area, scale, options.layout.struts); for (column, data) in zip(&mut self.columns, &mut self.data) { - column.update_config(view_size, working_area, scale, options.clone()); + column.update_config(view_size, working_area, parent_area, scale, options.clone()); data.update(column); } @@ -440,7 +453,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { } let col = &self.columns[self.active_column_idx]; - col.is_pending_fullscreen + col.pending_sizing_mode().is_fullscreen() } pub fn new_window_toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> { @@ -533,24 +546,25 @@ impl<W: LayoutElement> ScrollingSpace<W> { target_x: Option<f64>, col_x: f64, width: f64, - is_fullscreen: bool, + mode: SizingMode, ) -> f64 { - if is_fullscreen { + if mode.is_fullscreen() { return 0.; } + let (area, padding) = if mode.is_maximized() { + (self.parent_area, 0.) + } else { + (self.working_area, self.options.layout.gaps) + }; + let target_x = target_x.unwrap_or_else(|| self.target_view_pos()); - let new_offset = compute_new_view_offset( - target_x + self.working_area.loc.x, - self.working_area.size.w, - col_x, - width, - self.options.layout.gaps, - ); + let new_offset = + compute_new_view_offset(target_x + area.loc.x, area.size.w, col_x, width, padding); // Non-fullscreen windows are always offset at least by the working area position. - new_offset - self.working_area.loc.x + new_offset - area.loc.x } fn compute_new_view_offset_centered( @@ -558,18 +572,24 @@ impl<W: LayoutElement> ScrollingSpace<W> { target_x: Option<f64>, col_x: f64, width: f64, - is_fullscreen: bool, + mode: SizingMode, ) -> f64 { - if is_fullscreen { - return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen); + if mode.is_fullscreen() { + return self.compute_new_view_offset_fit(target_x, col_x, width, mode); } + let area = if mode.is_maximized() { + self.parent_area + } else { + self.working_area + }; + // Columns wider than the view are left-aligned (the fit code can deal with that). - if self.working_area.size.w <= width { - return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen); + if area.size.w <= width { + return self.compute_new_view_offset_fit(target_x, col_x, width, mode); } - -(self.working_area.size.w - width) / 2. - self.working_area.loc.x + -(area.size.w - width) / 2. - area.loc.x } fn compute_new_view_offset_for_column_fit(&self, target_x: Option<f64>, idx: usize) -> f64 { @@ -578,7 +598,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { target_x, self.column_x(idx), col.width(), - col.is_fullscreen(), + col.sizing_mode(), ) } @@ -592,7 +612,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { target_x, self.column_x(idx), col.width(), - col.is_fullscreen(), + col.sizing_mode(), ) } @@ -633,6 +653,8 @@ impl<W: LayoutElement> ScrollingSpace<W> { let target_col_x = self.column_x(idx); let target_col_width = self.columns[idx].width(); + // NOTE: This logic won't work entirely correctly with small fixed-size maximized + // windows (they have a different area and padding). let total_width = if source_col_x < target_col_x { // Source is left from target. target_col_x - source_col_x + target_col_width @@ -770,7 +792,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { // A different column was activated; reset the flag. self.activate_prev_column_on_removal = None; - self.view_offset_before_fullscreen = None; + self.view_offset_to_restore = None; self.interactive_resize = None; } } @@ -855,6 +877,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { tile, self.view_size, self.working_area, + self.parent_area, self.scale, width, is_full_width, @@ -956,6 +979,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { column.update_config( self.view_size, self.working_area, + self.parent_area, self.scale, self.options.clone(), ); @@ -1059,15 +1083,15 @@ impl<W: LayoutElement> ScrollingSpace<W> { tile.animate_alpha(0., 1., movement_config); } - let was_fullscreen = column.is_fullscreen(); + let was_normal = column.sizing_mode().is_normal(); let tile = column.tiles.remove(tile_idx); column.data.remove(tile_idx); // If an active column became non-fullscreen after removing the tile, clear the stored // unfullscreen offset. - if column_idx == self.active_column_idx && was_fullscreen && !column.is_fullscreen() { - self.view_offset_before_fullscreen = None; + if column_idx == self.active_column_idx && !was_normal && column.sizing_mode().is_normal() { + self.view_offset_to_restore = None; } // If one window is left, reset its weight to 1. @@ -1171,7 +1195,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { } if column_idx == self.active_column_idx { - self.view_offset_before_fullscreen = None; + self.view_offset_to_restore = None; } if self.columns.is_empty() { @@ -1225,7 +1249,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { .enumerate() .find(|(_, col)| col.contains(window)) .unwrap(); - let was_fullscreen = column.is_fullscreen(); + let was_normal = column.sizing_mode().is_normal(); let prev_origin = column.tiles_origin(); let (tile_idx, tile) = column @@ -1337,9 +1361,9 @@ impl<W: LayoutElement> ScrollingSpace<W> { } // When the active column goes fullscreen, store the view offset to restore later. - let is_fullscreen = self.columns[col_idx].is_fullscreen(); - if !was_fullscreen && is_fullscreen { - self.view_offset_before_fullscreen = Some(self.view_offset.stationary()); + let is_normal = self.columns[col_idx].sizing_mode().is_normal(); + if was_normal && !is_normal { + self.view_offset_to_restore = Some(self.view_offset.stationary()); } // Upon unfullscreening, restore the view offset. @@ -1348,11 +1372,11 @@ impl<W: LayoutElement> ScrollingSpace<W> { // will unfullscreen one by one, and the column width will shrink only when the // last tile unfullscreens. This is when we want to restore the view offset, // otherwise it will immediately reset back by the animate_view_offset below. - let unfullscreen_offset = if was_fullscreen && !is_fullscreen { + let unfullscreen_offset = if !was_normal && is_normal { // Take the value unconditionally, even if the view is currently frozen by // a view gesture. It shouldn't linger around because it's only valid for this // particular unfullscreen. - self.view_offset_before_fullscreen.take() + self.view_offset_to_restore.take() } else { None }; @@ -2165,6 +2189,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { if col.display_mode != ColumnDisplay::Tabbed && col.tiles.len() > 1 { let window = col.tiles[col.active_tile_idx].window().id().clone(); self.set_fullscreen(&window, false); + self.set_maximized(&window, false); } } @@ -2307,6 +2332,10 @@ impl<W: LayoutElement> ScrollingSpace<W> { iter::once(active_pos).chain(offsets) } + pub fn columns(&self) -> impl Iterator<Item = &Column<W>> { + self.columns.iter() + } + fn columns_mut(&mut self) -> impl Iterator<Item = (&mut Column<W>, f64)> + '_ { let offsets = self.column_xs(self.data.iter().copied()); zip(&mut self.columns, offsets) @@ -2474,7 +2503,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { // Adjust for place-within-column tab indicator. let origin_x = col.tiles_origin().x; - let extra_w = if is_tabbed && !col.is_fullscreen() { + let extra_w = if is_tabbed && col.sizing_mode().is_normal() { col.tab_indicator.extra_size(col.tiles.len(), col.scale).w } else { 0. @@ -2491,9 +2520,14 @@ impl<W: LayoutElement> ScrollingSpace<W> { // effect here. if self.columns.is_empty() { let view_offset = if self.is_centering_focused_column() { - self.compute_new_view_offset_centered(Some(0.), 0., hint_area.size.w, false) + self.compute_new_view_offset_centered( + Some(0.), + 0., + hint_area.size.w, + SizingMode::Normal, + ) } else { - self.compute_new_view_offset_fit(Some(0.), 0., hint_area.size.w, false) + self.compute_new_view_offset_fit(Some(0.), 0., hint_area.size.w, SizingMode::Normal) }; hint_area.loc.x -= view_offset; } else { @@ -2693,7 +2727,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { } let col = &mut self.columns[self.active_column_idx]; - if col.is_pending_fullscreen || col.is_full_width { + if !col.pending_sizing_mode().is_normal() || col.is_full_width { return; } @@ -2709,6 +2743,9 @@ impl<W: LayoutElement> ScrollingSpace<W> { return; } + // NOTE: This logic won't work entirely correctly with small fixed-size maximized windows + // (they have a different area and padding). + // Consider the end of an ongoing animation because that's what compute to fit does too. let view_x = self.target_view_pos(); let working_x = self.working_area.loc.x; @@ -2812,6 +2849,37 @@ impl<W: LayoutElement> ScrollingSpace<W> { true } + pub fn set_maximized(&mut self, window: &W::Id, maximize: bool) -> bool { + let mut col_idx = self + .columns + .iter() + .position(|col| col.contains(window)) + .unwrap(); + + if maximize == self.columns[col_idx].is_pending_maximized { + return false; + } + + let mut col = &mut self.columns[col_idx]; + let is_tabbed = col.display_mode == ColumnDisplay::Tabbed; + + cancel_resize_for_column(&mut self.interactive_resize, col); + + if maximize && (col.tiles.len() > 1 && !is_tabbed) { + // This wasn't the only window in its column; extract it into a separate column. + self.consume_or_expel_window_right(Some(window)); + col_idx += 1; + col = &mut self.columns[col_idx]; + } + + col.set_maximized(maximize); + + // With place_within_column, the tab indicator changes the column size immediately. + self.data[col_idx].update(col); + + true + } + pub fn render_above_top_layer(&self) -> bool { // Render above the top layer if we're on a fullscreen window and the view is stationary. if self.columns.is_empty() { @@ -2822,7 +2890,9 @@ impl<W: LayoutElement> ScrollingSpace<W> { return false; } - self.columns[self.active_column_idx].is_fullscreen() + self.columns[self.active_column_idx] + .sizing_mode() + .is_fullscreen() } pub fn render_elements<R: NiriRenderer>( @@ -2902,7 +2972,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { let col_render_off = col.render_offset(); // Hit the tab indicator. - if col.display_mode == ColumnDisplay::Tabbed && !col.is_fullscreen() { + if col.display_mode == ColumnDisplay::Tabbed && col.sizing_mode().is_normal() { let col_pos = view_off + col_off + col_render_off; let col_pos = col_pos.to_physical_precise_round(scale).to_logical(scale); @@ -3134,20 +3204,26 @@ impl<W: LayoutElement> ScrollingSpace<W> { let mut snapping_points = Vec::new(); - let left_strut = self.working_area.loc.x; - let right_strut = self.view_size.w - self.working_area.size.w - self.working_area.loc.x; - if self.is_centering_focused_column() { let mut col_x = 0.; for (col_idx, col) in self.columns.iter().enumerate() { let col_w = col.width(); + let mode = col.sizing_mode(); - let view_pos = if col.is_fullscreen() { + let area = if mode.is_maximized() { + self.parent_area + } else { + self.working_area + }; + + let left_strut = area.loc.x; + + let view_pos = if mode.is_fullscreen() { col_x - } else if self.working_area.size.w <= col_w { + } else if area.size.w <= col_w { col_x - left_strut } else { - col_x - (self.working_area.size.w - col_w) / 2. - left_strut + col_x - (area.size.w - col_w) / 2. - left_strut }; snapping_points.push(Snap { view_pos, col_idx }); @@ -3160,34 +3236,50 @@ impl<W: LayoutElement> ScrollingSpace<W> { ); let view_width = self.view_size.w; - let working_area_width = self.working_area.size.w; let gaps = self.options.layout.gaps; let snap_points = |col_x, col: &Column<W>, prev_col_w: Option<f64>, next_col_w: Option<f64>| { let col_w = col.width(); + let mode = col.sizing_mode(); + + let area = if mode.is_maximized() { + self.parent_area + } else { + self.working_area + }; + + let left_strut = area.loc.x; + let right_strut = self.view_size.w - area.size.w - area.loc.x; // Normal columns align with the working area, but fullscreen columns align with // the view size. - if col.is_fullscreen() { + if mode.is_fullscreen() { let left = col_x; - let right = col_x + col_w; + let right = left + col_w; (left, right) } else { // Logic from compute_new_view_offset. - let padding = ((working_area_width - col_w) / 2.).clamp(0., gaps); + let padding = if mode.is_maximized() { + 0. + } else { + ((area.size.w - col_w) / 2.).clamp(0., gaps) + }; - let center = if self.working_area.size.w <= col_w { + let center = if area.size.w <= col_w { col_x - left_strut } else { - col_x - (self.working_area.size.w - col_w) / 2. - left_strut + col_x - (area.size.w - col_w) / 2. - left_strut }; let is_overflowing = |adj_col_w: Option<f64>| { center_on_overflow && adj_col_w .filter(|adj_col_w| { + // NOTE: This logic won't work entirely correctly with small + // fixed-size maximized windows (they have a different area + // and padding). center_on_overflow - && adj_col_w + 3.0 * gaps + col_w > working_area_width + && adj_col_w + 3.0 * gaps + col_w > area.size.w }) .is_some() }; @@ -3211,6 +3303,14 @@ impl<W: LayoutElement> ScrollingSpace<W> { // // It's ok if leftmost_snap is > rightmost_snap (this happens if the columns on a // workspace total up to less than the workspace width). + + // The first column's left snap isn't actually guaranteed to be the *leftmost* snap. + // With weird enough left strut and perhaps a maximized small fixed-size window, you + // can make the second window's left snap be further to the left than the first + // window's. The same goes for the rightmost snap. + // + // This isn't actually a big problem because it's very much an obscure edge case. Just + // need to make sure the code doesn't panic when that happens. let leftmost_snap = snap_points( 0., &self.columns[0], @@ -3295,16 +3395,28 @@ impl<W: LayoutElement> ScrollingSpace<W> { let col = &self.columns[col_idx]; let col_x = self.column_x(col_idx); let col_w = col.width(); + let mode = col.sizing_mode(); + + let area = if mode.is_maximized() { + self.parent_area + } else { + self.working_area + }; - if col.is_fullscreen() { + let left_strut = area.loc.x; + + if mode.is_fullscreen() { if target_snap.view_pos + self.view_size.w < col_x + col_w { break; } } else { - let padding = ((self.working_area.size.w - col_w) / 2.) - .clamp(0., self.options.layout.gaps); - if target_snap.view_pos + left_strut + self.working_area.size.w - < col_x + col_w + padding + let padding = if mode.is_maximized() { + 0. + } else { + ((area.size.w - col_w) / 2.).clamp(0., self.options.layout.gaps) + }; + + if target_snap.view_pos + left_strut + area.size.w < col_x + col_w + padding { break; } @@ -3317,14 +3429,27 @@ impl<W: LayoutElement> ScrollingSpace<W> { let col = &self.columns[col_idx]; let col_x = self.column_x(col_idx); let col_w = col.width(); + let mode = col.sizing_mode(); - if col.is_fullscreen() { + let area = if mode.is_maximized() { + self.parent_area + } else { + self.working_area + }; + + let left_strut = area.loc.x; + + if mode.is_fullscreen() { if col_x < target_snap.view_pos { break; } } else { - let padding = ((self.working_area.size.w - col_w) / 2.) - .clamp(0., self.options.layout.gaps); + let padding = if mode.is_maximized() { + 0. + } else { + ((area.size.w - col_w) / 2.).clamp(0., self.options.layout.gaps) + }; + if col_x - padding < target_snap.view_pos + left_strut { break; } @@ -3339,7 +3464,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { let delta = active_col_x - new_col_x; if self.active_column_idx != new_col_idx { - self.view_offset_before_fullscreen = None; + self.view_offset_to_restore = None; } self.active_column_idx = new_col_idx; @@ -3391,7 +3516,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { .find(|col| col.contains(&window)) .unwrap(); - if col.is_pending_fullscreen { + if !col.pending_sizing_mode().is_normal() { return false; } @@ -3637,11 +3762,11 @@ impl<W: LayoutElement> ScrollingSpace<W> { let col = &self.columns[self.active_column_idx]; - if self.view_offset_before_fullscreen.is_some() { + if self.view_offset_to_restore.is_some() { assert!( - col.is_fullscreen(), - "when view_offset_before_fullscreen is set, \ - the active column must be fullscreen" + !col.sizing_mode().is_normal(), + "when view_offset_to_restore is set, \ + the active column must be fullscreen or maximized" ); } } @@ -3796,6 +3921,7 @@ impl<W: LayoutElement> Column<W> { tile: Tile<W>, view_size: Size<f64, Logical>, working_area: Rectangle<f64, Logical>, + parent_area: Rectangle<f64, Logical>, scale: f64, width: ColumnWidth, is_full_width: bool, @@ -3815,29 +3941,33 @@ impl<W: LayoutElement> Column<W> { width, preset_width_idx: None, is_full_width, + is_pending_maximized: false, is_pending_fullscreen: false, display_mode, tab_indicator: TabIndicator::new(options.layout.tab_indicator), move_animation: None, view_size, working_area, + parent_area, scale, clock: tile.clock.clone(), options, }; - let is_pending_fullscreen = tile.window().is_pending_fullscreen(); + let pending_sizing_mode = tile.window().pending_sizing_mode(); rv.add_tile_at(0, tile); - if is_pending_fullscreen { - rv.set_fullscreen(true); + match pending_sizing_mode { + SizingMode::Normal => (), + SizingMode::Maximized => rv.set_maximized(true), + SizingMode::Fullscreen => rv.set_fullscreen(true), } // Animate the tab indicator for new columns. if display_mode == ColumnDisplay::Tabbed && !rv.options.layout.tab_indicator.hide_when_single_tab - && !rv.is_fullscreen() + && rv.sizing_mode().is_normal() { // Usually new columns are created together with window movement actions. For new // windows, we handle that in start_open_animation(). @@ -3852,12 +3982,16 @@ impl<W: LayoutElement> Column<W> { &mut self, view_size: Size<f64, Logical>, working_area: Rectangle<f64, Logical>, + parent_area: Rectangle<f64, Logical>, scale: f64, options: Rc<Options>, ) { let mut update_sizes = false; - if self.view_size != view_size || self.working_area != working_area { + if self.view_size != view_size + || self.working_area != working_area + || self.parent_area != parent_area + { update_sizes = true; } @@ -3895,6 +4029,7 @@ impl<W: LayoutElement> Column<W> { .update_config(options.layout.tab_indicator); self.view_size = view_size; self.working_area = working_area; + self.parent_area = parent_area; self.scale = scale; self.options = options; @@ -3954,7 +4089,7 @@ impl<W: LayoutElement> Column<W> { // you don't want that to happen in fullscreen. Also, laying things out correctly when the // tab indicator is within the column and the column goes fullscreen, would require too // many changes to the code for too little benefit (it's mostly invisible anyway). - let enabled = self.display_mode == ColumnDisplay::Tabbed && !self.is_fullscreen(); + let enabled = self.display_mode == ColumnDisplay::Tabbed && self.sizing_mode().is_normal(); self.tab_indicator.update_render_elements( enabled, @@ -3967,6 +4102,24 @@ impl<W: LayoutElement> Column<W> { ); } + pub fn is_pending_fullscreen(&self) -> bool { + self.is_pending_fullscreen + } + + pub fn is_pending_maximized(&self) -> bool { + self.is_pending_maximized + } + + pub fn pending_sizing_mode(&self) -> SizingMode { + if self.is_pending_fullscreen { + SizingMode::Fullscreen + } else if self.is_pending_maximized { + SizingMode::Maximized + } else { + SizingMode::Normal + } + } + pub fn render_offset(&self) -> Point<f64, Logical> { let mut offset = Point::from((0., 0.)); @@ -4040,7 +4193,7 @@ impl<W: LayoutElement> Column<W> { /// - is_fullscreen() can suddenly change when consuming/expelling a fullscreen tile into/from a /// non-fullscreen column. This can influence the code that saves/restores the unfullscreen /// view offset. - fn is_fullscreen(&self) -> bool { + fn sizing_mode(&self) -> SizingMode { // Behaviors that we want: // // 1. The common case: single tile in a column. Assume no animations. Fullscreening the tile @@ -4058,7 +4211,23 @@ impl<W: LayoutElement> Column<W> { // mode change applies instantly). // // The logic that satisfies these behaviors is to check if *any* tile is fullscreen. - self.tiles.iter().any(|tile| tile.is_fullscreen()) + let mut any_fullscreen = false; + let mut any_maximized = false; + for tile in &self.tiles { + match tile.sizing_mode() { + SizingMode::Normal => (), + SizingMode::Maximized => any_maximized = true, + SizingMode::Fullscreen => any_fullscreen = true, + } + } + + if any_fullscreen { + SizingMode::Fullscreen + } else if any_maximized { + SizingMode::Maximized + } else { + SizingMode::Normal + } } pub fn contains(&self, window: &W::Id) -> bool { @@ -4102,6 +4271,7 @@ impl<W: LayoutElement> Column<W> { if self.display_mode != ColumnDisplay::Tabbed { self.is_pending_fullscreen = false; + self.is_pending_maximized = false; } self.data @@ -4213,7 +4383,8 @@ impl<W: LayoutElement> Column<W> { } fn update_tile_sizes_with_transaction(&mut self, animate: bool, transaction: Transaction) { - if self.is_pending_fullscreen { + let sizing_mode = self.pending_sizing_mode(); + if matches!(sizing_mode, SizingMode::Fullscreen | SizingMode::Maximized) { for (tile_idx, tile) in self.tiles.iter_mut().enumerate() { // In tabbed mode, only the visible window participates in the transaction. let is_active = tile_idx == self.active_tile_idx; @@ -4223,7 +4394,11 @@ impl<W: LayoutElement> Column<W> { Some(transaction.clone()) }; - tile.request_fullscreen(animate, transaction); + if matches!(sizing_mode, SizingMode::Fullscreen) { + tile.request_fullscreen(animate, transaction); + } else { + tile.request_maximized(self.parent_area.size, animate, transaction); + } } return; } @@ -4510,7 +4685,7 @@ impl<W: LayoutElement> Column<W> { .map(NotNan::into_inner) .unwrap(); - if self.display_mode == ColumnDisplay::Tabbed && !self.is_fullscreen() { + if self.display_mode == ColumnDisplay::Tabbed && self.sizing_mode().is_normal() { let extra_size = self.tab_indicator.extra_size(self.tiles.len(), self.scale); tiles_width += extra_size.w; } @@ -4875,6 +5050,19 @@ impl<W: LayoutElement> Column<W> { self.update_tile_sizes(true); } + fn set_maximized(&mut self, maximize: bool) { + if self.is_pending_maximized == maximize { + return; + } + + if maximize { + assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed); + } + + self.is_pending_maximized = maximize; + self.update_tile_sizes(true); + } + fn set_column_display(&mut self, display: ColumnDisplay) { if self.display_mode == display { return; @@ -4936,8 +5124,13 @@ impl<W: LayoutElement> Column<W> { fn tiles_origin(&self) -> Point<f64, Logical> { let mut origin = Point::from((0., 0.)); - if self.is_fullscreen() { - return origin; + match self.sizing_mode() { + SizingMode::Normal => (), + SizingMode::Maximized => { + origin.y += self.parent_area.loc.y; + return origin; + } + SizingMode::Fullscreen => return origin, } origin.y += self.working_area.loc.y + self.options.layout.gaps; @@ -5096,7 +5289,7 @@ impl<W: LayoutElement> Column<W> { // Animate the appearance of the tab indicator. if self.display_mode == ColumnDisplay::Tabbed - && !self.is_fullscreen() + && self.sizing_mode().is_normal() && self.tiles.len() == 1 && !self.tab_indicator.config().hide_when_single_tab { @@ -5119,7 +5312,7 @@ impl<W: LayoutElement> Column<W> { assert!(self.active_tile_idx < self.tiles.len()); assert_eq!(self.tiles.len(), self.data.len()); - if self.is_pending_fullscreen { + if !self.pending_sizing_mode().is_normal() { assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed); } @@ -5151,8 +5344,8 @@ impl<W: LayoutElement> Column<W> { assert_eq!(self.clock, tile.clock); assert_eq!(self.scale, tile.scale()); assert_eq!( - self.is_pending_fullscreen, - tile.window().is_pending_fullscreen() + self.pending_sizing_mode(), + tile.window().pending_sizing_mode() ); assert_eq!(self.view_size, tile.view_size()); tile.verify_invariants(); @@ -5178,7 +5371,7 @@ impl<W: LayoutElement> Column<W> { tile.tile_height_for_window_height(f64::from(requested_size.h)); let min_tile_height = f64::max(1., tile.min_size_nonfullscreen().h); - if !self.is_pending_fullscreen + if self.pending_sizing_mode().is_normal() && self.scale.round() == self.scale && working_size.h.round() == working_size.h && gaps.round() == gaps |
