diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/layout/scrolling.rs | 125 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 6 |
2 files changed, 93 insertions, 38 deletions
diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index 20e4c388..48773110 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -166,8 +166,15 @@ pub struct Column<W: LayoutElement> { /// Whether this column is full-width. is_full_width: bool, - /// Whether this column contains a single full-screened window. - is_fullscreen: bool, + /// Whether this column is going to be fullscreen. + /// + /// This is the compositor-side fullscreen state, so it changes immediately upon + /// set_fullscreen(). The actual tiles will take some time to respond to the fullscreen request + /// and become fullscreen. + /// + /// Similarly, unsetting fullscreen will change this value to false immediately, and tiles will + /// take some time to catch up and actually unfullscreen. + is_pending_fullscreen: bool, /// How this column displays and arranges windows. display_mode: ColumnDisplay, @@ -426,13 +433,13 @@ impl<W: LayoutElement> ScrollingSpace<W> { Some(&mut col.tiles[col.active_tile_idx]) } - pub fn is_active_fullscreen(&self) -> bool { + pub fn is_active_pending_fullscreen(&self) -> bool { if self.columns.is_empty() { return false; } let col = &self.columns[self.active_column_idx]; - col.is_fullscreen + col.is_pending_fullscreen } pub fn new_window_toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> { @@ -570,7 +577,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { target_x, self.column_x(idx), col.width(), - col.is_fullscreen, + col.is_fullscreen(), ) } @@ -584,7 +591,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { target_x, self.column_x(idx), col.width(), - col.is_fullscreen, + col.is_fullscreen(), ) } @@ -1052,14 +1059,14 @@ impl<W: LayoutElement> ScrollingSpace<W> { tile.animate_alpha(0., 1., movement_config); } - let was_fullscreen = column.is_any_fullscreen(); + let was_fullscreen = column.is_fullscreen(); 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_any_fullscreen() { + if column_idx == self.active_column_idx && was_fullscreen && !column.is_fullscreen() { self.view_offset_before_fullscreen = None; } @@ -1218,7 +1225,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { .enumerate() .find(|(_, col)| col.contains(window)) .unwrap(); - let was_fullscreen = column.is_any_fullscreen(); + let was_fullscreen = column.is_fullscreen(); let (tile_idx, tile) = column .tiles @@ -1317,7 +1324,7 @@ 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_any_fullscreen(); + let is_fullscreen = self.columns[col_idx].is_fullscreen(); if !was_fullscreen && is_fullscreen { self.view_offset_before_fullscreen = Some(self.view_offset.stationary()); } @@ -2450,7 +2457,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.is_fullscreen() { col.tab_indicator.extra_size(col.tiles.len(), col.scale).w } else { 0. @@ -2669,7 +2676,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { } let col = &mut self.columns[self.active_column_idx]; - if col.is_fullscreen || col.is_full_width { + if col.is_pending_fullscreen || col.is_full_width { return; } @@ -2765,7 +2772,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { .find_map(|(col_idx, col)| col.position(window).map(|tile_idx| (col_idx, tile_idx))) .unwrap(); - if is_fullscreen == self.columns[col_idx].is_fullscreen { + if is_fullscreen == self.columns[col_idx].is_pending_fullscreen { return false; } @@ -2813,7 +2820,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { return false; } - self.columns[self.active_column_idx].is_fullscreen + self.columns[self.active_column_idx].is_fullscreen() } pub fn render_elements<R: NiriRenderer>( @@ -2893,7 +2900,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.is_fullscreen() { let col_pos = view_off + col_off + col_render_off; let col_pos = col_pos.to_physical_precise_round(scale).to_logical(scale); @@ -3133,7 +3140,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { for (col_idx, col) in self.columns.iter().enumerate() { let col_w = col.width(); - let view_pos = if col.is_fullscreen { + let view_pos = if col.is_fullscreen() { col_x } else if self.working_area.size.w <= col_w { col_x - left_strut @@ -3160,7 +3167,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { // Normal columns align with the working area, but fullscreen columns align with // the view size. - if col.is_fullscreen { + if col.is_fullscreen() { let left = col_x; let right = col_x + col_w; (left, right) @@ -3287,7 +3294,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { let col_x = self.column_x(col_idx); let col_w = col.width(); - if col.is_fullscreen { + if col.is_fullscreen() { if target_snap.view_pos + self.view_size.w < col_x + col_w { break; } @@ -3309,7 +3316,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { let col_x = self.column_x(col_idx); let col_w = col.width(); - if col.is_fullscreen { + if col.is_fullscreen() { if col_x < target_snap.view_pos { break; } @@ -3382,7 +3389,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { .find(|col| col.contains(&window)) .unwrap(); - if col.is_fullscreen { + if col.is_pending_fullscreen { return false; } @@ -3630,7 +3637,7 @@ impl<W: LayoutElement> ScrollingSpace<W> { if self.view_offset_before_fullscreen.is_some() { assert!( - col.is_any_fullscreen(), + col.is_fullscreen(), "when view_offset_before_fullscreen is set, \ the active column must be fullscreen" ); @@ -3807,7 +3814,7 @@ impl<W: LayoutElement> Column<W> { width, preset_width_idx: None, is_full_width, - is_fullscreen: false, + is_pending_fullscreen: false, display_mode, tab_indicator: TabIndicator::new(options.tab_indicator), move_animation: None, @@ -3829,7 +3836,7 @@ impl<W: LayoutElement> Column<W> { // Animate the tab indicator for new columns. if display_mode == ColumnDisplay::Tabbed && !rv.options.tab_indicator.hide_when_single_tab - && !is_pending_fullscreen + && !rv.is_fullscreen() { // Usually new columns are created together with window movement actions. For new // windows, we handle that in start_open_animation(). @@ -3945,7 +3952,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.is_fullscreen(); self.tab_indicator.update_render_elements( enabled, @@ -4003,7 +4010,52 @@ impl<W: LayoutElement> Column<W> { } } - fn is_any_fullscreen(&self) -> bool { + /// Returns whether this column is currently fullscreen. + /// + /// As in, if it contains one currently-fullscreen tile, or in tabbed mode, if it contains at + /// least one currently-fullscreen tile. + /// + /// This will lag behind is_pending_fullscreen, depending on when the tiles actually respond to + /// the un/fullscreen request. But, it's possible for is_fullscreen() to flip instantly, for + /// example when consuming a fullscreen tile into a non-pending-fullscreen column. + /// + /// This controls things like: + /// + /// - whether the column draws at the top of the screen or at the start of the working area + /// - whether the column draws above the top layer-shell layer + /// - whether the tab indicator is shown + /// - restoring view_offset_before_fullscreen + /// + /// Edge cases to watch out for: + /// + /// - Consuming a fullscreen tile into a non-tabbed column will keep that tile fullscreen until + /// it responds to the unfullscreen request. This tile may be anywhere in the column, + /// including at the active position. + /// + /// - Changing a fullscreen tabbed column into normal mode is an easy way to get randomly + /// delayed unfullscreening tiles in a normal column. + /// + /// - 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 { + // Behaviors that we want: + // + // 1. The common case: single tile in a column. Assume no animations. Fullscreening the tile + // should make it jump to the top-left of the screen only when the tile finishes + // fullscreening. Similarly, unfullscreening should keep it at the top-left until the + // tile had unfullscreened. + // + // 2. Unfullscreening a tabbed column with multiple tiles should restore the view offset + // correctly. This means waiting for *all* tiles to unfullscreen, because otherwise the + // restored view offset will immediately get overwritted by the still screen-wide column + // (it uses the largest tile's width). + // + // 3. Changing a fullscreen tabbed column to normal should probably also restore the view + // offset correctly. Same problem as above, but now for normal columns (since display + // 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()) } @@ -4047,7 +4099,7 @@ impl<W: LayoutElement> Column<W> { prev_offsets.extend(self.tile_offsets().take(self.tiles.len())); if self.display_mode != ColumnDisplay::Tabbed { - self.is_fullscreen = false; + self.is_pending_fullscreen = false; } self.data @@ -4159,7 +4211,7 @@ impl<W: LayoutElement> Column<W> { } fn update_tile_sizes_with_transaction(&mut self, animate: bool, transaction: Transaction) { - if self.is_fullscreen { + if self.is_pending_fullscreen { 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; @@ -4456,7 +4508,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.is_fullscreen() { let extra_size = self.tab_indicator.extra_size(self.tiles.len(), self.scale); tiles_width += extra_size.w; } @@ -4807,7 +4859,7 @@ impl<W: LayoutElement> Column<W> { } fn set_fullscreen(&mut self, is_fullscreen: bool) { - if self.is_fullscreen == is_fullscreen { + if self.is_pending_fullscreen == is_fullscreen { return; } @@ -4815,7 +4867,7 @@ impl<W: LayoutElement> Column<W> { assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed); } - self.is_fullscreen = is_fullscreen; + self.is_pending_fullscreen = is_fullscreen; self.update_tile_sizes(false); } @@ -4880,7 +4932,7 @@ impl<W: LayoutElement> Column<W> { fn tiles_origin(&self) -> Point<f64, Logical> { let mut origin = Point::from((0., 0.)); - if self.is_fullscreen { + if self.is_fullscreen() { return origin; } @@ -5040,7 +5092,7 @@ impl<W: LayoutElement> Column<W> { // Animate the appearance of the tab indicator. if self.display_mode == ColumnDisplay::Tabbed - && !self.is_fullscreen + && !self.is_fullscreen() && self.tiles.len() == 1 && !self.tab_indicator.config().hide_when_single_tab { @@ -5063,7 +5115,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_fullscreen { + if self.is_pending_fullscreen { assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed); } @@ -5094,7 +5146,10 @@ impl<W: LayoutElement> Column<W> { assert!(Rc::ptr_eq(&self.options, &tile.options)); assert_eq!(self.clock, tile.clock); assert_eq!(self.scale, tile.scale()); - assert_eq!(self.is_fullscreen, tile.window().is_pending_fullscreen()); + assert_eq!( + self.is_pending_fullscreen, + tile.window().is_pending_fullscreen() + ); assert_eq!(self.view_size, tile.view_size()); tile.verify_invariants(); @@ -5119,7 +5174,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_fullscreen + if !self.is_pending_fullscreen && self.scale.round() == self.scale && working_size.h.round() == working_size.h && gaps.round() == gaps diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index efba6ee6..6c343858 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -444,8 +444,8 @@ impl<W: LayoutElement> Workspace<W> { } } - pub fn is_active_fullscreen(&self) -> bool { - self.scrolling.is_active_fullscreen() + pub fn is_active_pending_fullscreen(&self) -> bool { + self.scrolling.is_active_pending_fullscreen() } pub fn set_output(&mut self, output: Option<Output>) { @@ -571,7 +571,7 @@ impl<W: LayoutElement> Workspace<W> { match target { WorkspaceAddWindowTarget::Auto => { // Don't steal focus from an active fullscreen window. - let activate = activate.map_smart(|| !self.is_active_fullscreen()); + 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. |
