aboutsummaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/scrolling.rs149
-rw-r--r--src/layout/tab_indicator.rs33
-rw-r--r--src/layout/tests.rs2
3 files changed, 154 insertions, 30 deletions
diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs
index 42de18c8..55ddfc1b 100644
--- a/src/layout/scrolling.rs
+++ b/src/layout/scrolling.rs
@@ -421,7 +421,23 @@ impl<W: LayoutElement> ScrollingSpace<W> {
pub fn new_window_toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> {
let border_config = rules.border.resolve_against(self.options.border);
- compute_toplevel_bounds(border_config, self.working_area.size, self.options.gaps)
+
+ let display_mode = rules
+ .default_column_display
+ .unwrap_or(self.options.default_column_display);
+ let will_tab = display_mode == ColumnDisplay::Tabbed;
+ let extra_size = if will_tab {
+ TabIndicator::new(self.options.tab_indicator).extra_size(1, self.scale)
+ } else {
+ Size::from((0., 0.))
+ };
+
+ compute_toplevel_bounds(
+ border_config,
+ self.working_area.size,
+ extra_size,
+ self.options.gaps,
+ )
}
pub fn new_window_size(
@@ -432,10 +448,20 @@ impl<W: LayoutElement> ScrollingSpace<W> {
) -> Size<i32, Logical> {
let border = rules.border.resolve_against(self.options.border);
+ let display_mode = rules
+ .default_column_display
+ .unwrap_or(self.options.default_column_display);
+ let will_tab = display_mode == ColumnDisplay::Tabbed;
+ let extra = if will_tab {
+ TabIndicator::new(self.options.tab_indicator).extra_size(1, self.scale)
+ } else {
+ Size::from((0., 0.))
+ };
+
let working_size = self.working_area.size;
let width = if let Some(size) = width {
- let size = match resolve_preset_size(size, &self.options, working_size.w) {
+ let size = match resolve_preset_size(size, &self.options, working_size.w, extra.w) {
ResolvedSize::Tile(mut size) => {
if !border.off {
size -= border.width.0 * 2.;
@@ -456,7 +482,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
let height = if let Some(height) = height {
- let height = match resolve_preset_size(height, &self.options, working_size.h) {
+ let height = match resolve_preset_size(height, &self.options, working_size.h, extra.h) {
ResolvedSize::Tile(mut size) => {
if !border.off {
size -= border.width.0 * 2.;
@@ -1994,6 +2020,10 @@ impl<W: LayoutElement> ScrollingSpace<W> {
cancel_resize_for_column(&mut self.interactive_resize, col);
col.set_column_display(display);
+ // With place_within_column, the tab indicator changes the column size immediately.
+ self.data[self.active_column_idx].update(col);
+ col.update_tile_sizes(true);
+
// Disable fullscreen if needed.
if col.display_mode != ColumnDisplay::Tabbed && col.tiles.len() > 1 {
let window = col.tiles[col.active_tile_idx].window().id().clone();
@@ -2224,8 +2254,16 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
};
- let size = Size::from((self.data[column_index].width, height));
- let loc = Point::from((self.column_x(column_index), y));
+ // Adjust for place-within-column tab indicator.
+ let origin_x = col.tiles_origin().x;
+ let extra_w = if is_tabbed && !col.is_fullscreen {
+ col.tab_indicator.extra_size(col.tiles.len(), col.scale).w
+ } else {
+ 0.
+ };
+
+ let size = Size::from((self.data[column_index].width - extra_w, height));
+ let loc = Point::from((self.column_x(column_index) + origin_x, y));
Rectangle::new(loc, size)
}
InsertPosition::Floating => return None,
@@ -2472,6 +2510,9 @@ impl<W: LayoutElement> ScrollingSpace<W> {
col.set_fullscreen(is_fullscreen);
+ // With place_within_column, the tab indicator changes the column size immediately.
+ self.data[col_idx].update(col);
+
// If we quickly fullscreen and unfullscreen before any window has a chance to receive the
// request, we need to reset the offset.
if col_idx == self.active_column_idx
@@ -3016,6 +3057,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
let is_tabbed = col.display_mode == ColumnDisplay::Tabbed;
+ let extra_size = col.extra_size();
// If transactions are disabled, also disable combined throttling, for more intuitive
// behavior. In tabbed display mode, only one window is visible, so individual
@@ -3063,6 +3105,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let bounds = compute_toplevel_bounds(
border_config,
self.working_area.size,
+ extra_size,
self.options.gaps,
);
win.set_bounds(bounds);
@@ -3352,6 +3395,10 @@ impl<W: LayoutElement> Column<W> {
update_sizes = true;
}
+ if self.options.tab_indicator != options.tab_indicator {
+ update_sizes = true;
+ }
+
for (tile, data) in zip(&mut self.tiles, &mut self.data) {
tile.update_config(view_size, scale, options.clone());
data.update(tile);
@@ -3545,20 +3592,34 @@ impl<W: LayoutElement> Column<W> {
}
}
+ /// Extra size taken up by elements in the column such as the tab indicator.
+ fn extra_size(&self) -> Size<f64, Logical> {
+ if self.display_mode == ColumnDisplay::Tabbed {
+ self.tab_indicator.extra_size(self.tiles.len(), self.scale)
+ } else {
+ Size::from((0., 0.))
+ }
+ }
+
fn resolve_preset_width(&self, preset: PresetSize) -> ResolvedSize {
- resolve_preset_size(preset, &self.options, self.working_area.size.w)
+ let extra = self.extra_size();
+ resolve_preset_size(preset, &self.options, self.working_area.size.w, extra.w)
}
fn resolve_preset_height(&self, preset: PresetSize) -> ResolvedSize {
- resolve_preset_size(preset, &self.options, self.working_area.size.h)
+ let extra = self.extra_size();
+ resolve_preset_size(preset, &self.options, self.working_area.size.h, extra.h)
}
fn resolve_column_width(&self, width: ColumnWidth) -> f64 {
let working_size = self.working_area.size;
let gaps = self.options.gaps;
+ let extra = self.extra_size();
match width {
- ColumnWidth::Proportion(proportion) => (working_size.w - gaps) * proportion - gaps,
+ ColumnWidth::Proportion(proportion) => {
+ (working_size.w - gaps) * proportion - gaps - extra.w
+ }
ColumnWidth::Fixed(width) => width,
}
}
@@ -3618,10 +3679,11 @@ impl<W: LayoutElement> Column<W> {
};
let working_size = self.working_area.size;
+ let extra_size = self.extra_size();
let width = self.resolve_column_width(width);
let width = f64::max(f64::min(width, max_width), min_width);
- let max_tile_height = working_size.h - self.options.gaps * 2.;
+ let max_tile_height = working_size.h - self.options.gaps * 2. - extra_size.h;
// If there are multiple windows in a column, clamp the non-auto window's height according
// to other windows' min sizes.
@@ -3846,12 +3908,20 @@ impl<W: LayoutElement> Column<W> {
}
fn width(&self) -> f64 {
- self.data
+ let mut tiles_width = self
+ .data
.iter()
.map(|data| NotNan::new(data.size.w).unwrap())
.max()
.map(NotNan::into_inner)
- .unwrap()
+ .unwrap();
+
+ 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;
+ }
+
+ tiles_width
}
fn focus_index(&mut self, index: u8) {
@@ -4002,7 +4072,7 @@ impl<W: LayoutElement> Column<W> {
let current = if full == 0. {
1.
} else {
- (current_px + self.options.gaps) / full
+ (current_px + self.options.gaps + self.extra_size().w) / full
};
let proportion = (current + delta / 100.).clamp(0., MAX_F);
ColumnWidth::Proportion(proportion)
@@ -4037,6 +4107,7 @@ impl<W: LayoutElement> Column<W> {
let working_size = self.working_area.size.h;
let gaps = self.options.gaps;
+ let extra_size = self.extra_size().h;
let full = working_size - gaps;
let current_prop = if full == 0. {
1.
@@ -4050,13 +4121,13 @@ impl<W: LayoutElement> Column<W> {
let mut window_height = match change {
SizeChange::SetFixed(fixed) => f64::from(fixed),
SizeChange::SetProportion(proportion) => {
- let tile_height = (working_size - gaps) * (proportion / 100.) - gaps;
+ let tile_height = (working_size - gaps) * (proportion / 100.) - gaps - extra_size;
tile.window_height_for_tile_height(tile_height)
}
SizeChange::AdjustFixed(delta) => current_window_px + f64::from(delta),
SizeChange::AdjustProportion(delta) => {
let proportion = current_prop + delta / 100.;
- let tile_height = (working_size - gaps) * proportion - gaps;
+ let tile_height = (working_size - gaps) * proportion - gaps - extra_size;
tile.window_height_for_tile_height(tile_height)
}
};
@@ -4072,7 +4143,7 @@ impl<W: LayoutElement> Column<W> {
.map(|(_, tile)| f64::max(1., tile.min_size().h) + gaps)
.sum::<f64>()
};
- let height_left = working_size - gaps - min_height_taken - gaps;
+ let height_left = working_size - extra_size - gaps - min_height_taken - gaps;
let height_left = f64::max(1., tile.window_height_for_tile_height(height_left));
window_height = f64::min(height_left, window_height);
@@ -4219,6 +4290,12 @@ impl<W: LayoutElement> Column<W> {
origin.y += self.working_area.loc.y + self.options.gaps;
+ if self.display_mode == ColumnDisplay::Tabbed {
+ origin += self
+ .tab_indicator
+ .content_offset(self.tiles.len(), self.scale);
+ }
+
origin
}
@@ -4234,11 +4311,16 @@ impl<W: LayoutElement> Column<W> {
let center = self.options.center_focused_column == CenterFocusedColumn::Always;
let gaps = self.options.gaps;
let tabbed = self.display_mode == ColumnDisplay::Tabbed;
- let col_width = if self.tiles.is_empty() {
- 0.
- } else {
- self.width()
- };
+
+ // Does not include extra size from the tab indicator.
+ let tiles_width = self
+ .data
+ .iter()
+ .map(|data| NotNan::new(data.size.w).unwrap())
+ .max()
+ .map(NotNan::into_inner)
+ .unwrap_or(0.);
+
let mut origin = self.tiles_origin();
// Chain with a dummy value to be able to get one past all tiles' Y.
@@ -4253,9 +4335,9 @@ impl<W: LayoutElement> Column<W> {
let mut pos = origin;
if center {
- pos.x += (col_width - data.size.w) / 2.;
+ pos.x += (tiles_width - data.size.w) / 2.;
} else if data.interactively_resizing_by_left_edge {
- pos.x += col_width - data.size.w;
+ pos.x += tiles_width - data.size.w;
}
if !tabbed {
@@ -4354,6 +4436,7 @@ impl<W: LayoutElement> Column<W> {
}
let working_size = self.working_area.size;
+ let extra_size = self.extra_size();
let gaps = self.options.gaps;
let mut found_fixed = false;
@@ -4393,8 +4476,8 @@ impl<W: LayoutElement> Column<W> {
&& working_size.h.round() == working_size.h
&& gaps.round() == gaps
{
- let total_height = requested_tile_height + gaps * 2.;
- let total_min_height = min_tile_height + gaps * 2.;
+ let total_height = requested_tile_height + gaps * 2. + extra_size.h;
+ let total_min_height = min_tile_height + gaps * 2. + extra_size.h;
let max_height = f64::max(total_min_height, working_size.h);
assert!(
total_height <= max_height,
@@ -4413,8 +4496,8 @@ impl<W: LayoutElement> Column<W> {
&& working_size.h.round() == working_size.h
&& gaps.round() == gaps
{
- total_height += gaps * (tile_count + 1) as f64;
- total_min_height += gaps * (tile_count + 1) as f64;
+ total_height += gaps * (tile_count + 1) as f64 + extra_size.h;
+ total_min_height += gaps * (tile_count + 1) as f64 + extra_size.h;
let max_height = f64::max(total_min_height, working_size.h);
assert!(
total_height <= max_height,
@@ -4492,6 +4575,7 @@ fn compute_working_area(
fn compute_toplevel_bounds(
border_config: niri_config::Border,
working_area_size: Size<f64, Logical>,
+ extra_size: Size<f64, Logical>,
gaps: f64,
) -> Size<i32, Logical> {
let mut border = 0.;
@@ -4500,8 +4584,8 @@ fn compute_toplevel_bounds(
}
Size::from((
- f64::max(working_area_size.w - gaps * 2. - border, 1.),
- f64::max(working_area_size.h - gaps * 2. - border, 1.),
+ f64::max(working_area_size.w - gaps * 2. - extra_size.w - border, 1.),
+ f64::max(working_area_size.h - gaps * 2. - extra_size.h - border, 1.),
))
.to_i32_floor()
}
@@ -4521,10 +4605,15 @@ fn cancel_resize_for_column<W: LayoutElement>(
}
}
-fn resolve_preset_size(preset: PresetSize, options: &Options, view_size: f64) -> ResolvedSize {
+fn resolve_preset_size(
+ preset: PresetSize,
+ options: &Options,
+ view_size: f64,
+ extra_size: f64,
+) -> ResolvedSize {
match preset {
PresetSize::Proportion(proportion) => {
- ResolvedSize::Tile((view_size - options.gaps) * proportion - options.gaps)
+ ResolvedSize::Tile((view_size - options.gaps) * proportion - options.gaps - extra_size)
}
PresetSize::Fixed(width) => ResolvedSize::Window(f64::from(width)),
}
diff --git a/src/layout/tab_indicator.rs b/src/layout/tab_indicator.rs
index 295eecc6..72652369 100644
--- a/src/layout/tab_indicator.rs
+++ b/src/layout/tab_indicator.rs
@@ -174,6 +174,39 @@ impl TabIndicator {
Some(rv).into_iter().flatten()
}
+ /// Extra size occupied by the tab indicator.
+ pub fn extra_size(&self, tab_count: usize, scale: f64) -> Size<f64, Logical> {
+ if self.config.off
+ || !self.config.place_within_column
+ || (self.config.hide_when_single_tab && tab_count == 1)
+ {
+ return Size::from((0., 0.));
+ }
+
+ let round = |logical: f64| round_logical_in_physical(scale, logical);
+ let width = round(self.config.width.0);
+ let gap = round(self.config.gap.0);
+
+ // No, I am *not* falling into the rabbit hole of "what if the tab indicator is wide enough
+ // that it peeks from the other side of the window".
+ let size = f64::max(0., width + gap);
+
+ match self.config.position {
+ TabIndicatorPosition::Left | TabIndicatorPosition::Right => Size::from((size, 0.)),
+ TabIndicatorPosition::Top | TabIndicatorPosition::Bottom => Size::from((0., size)),
+ }
+ }
+
+ /// Offset of the tabbed content due to space occupied by the tab indicator.
+ pub fn content_offset(&self, tab_count: usize, scale: f64) -> Point<f64, Logical> {
+ match self.config.position {
+ TabIndicatorPosition::Left | TabIndicatorPosition::Top => {
+ self.extra_size(tab_count, scale).to_point()
+ }
+ TabIndicatorPosition::Right | TabIndicatorPosition::Bottom => Point::from((0., 0.)),
+ }
+ }
+
pub fn config(&self) -> niri_config::TabIndicator {
self.config
}
diff --git a/src/layout/tests.rs b/src/layout/tests.rs
index 83fef3bc..ecb9d683 100644
--- a/src/layout/tests.rs
+++ b/src/layout/tests.rs
@@ -3255,6 +3255,7 @@ prop_compose! {
fn arbitrary_tab_indicator()(
off in any::<bool>(),
hide_when_single_tab in any::<bool>(),
+ place_within_column in any::<bool>(),
width in arbitrary_spacing(),
gap in arbitrary_spacing_neg(),
length in (0f64..2f64),
@@ -3263,6 +3264,7 @@ prop_compose! {
niri_config::TabIndicator {
off,
hide_when_single_tab,
+ place_within_column,
width: FloatOrInt(width),
gap: FloatOrInt(gap),
length: TabIndicatorLength { total_proportion: Some(length) },