aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/layout/scrolling.rs125
-rw-r--r--src/layout/workspace.rs6
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.