aboutsummaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/floating.rs63
-rw-r--r--src/layout/mod.rs72
-rw-r--r--src/layout/scrolling.rs139
-rw-r--r--src/layout/tests.rs65
-rw-r--r--src/layout/workspace.rs8
5 files changed, 188 insertions, 159 deletions
diff --git a/src/layout/floating.rs b/src/layout/floating.rs
index 3ac995f0..b908d920 100644
--- a/src/layout/floating.rs
+++ b/src/layout/floating.rs
@@ -623,7 +623,7 @@ impl<W: LayoutElement> FloatingSpace<W> {
.preset_column_widths
.iter()
.position(|preset| {
- let resolved = preset.resolve_no_gaps(&self.options, available_size);
+ let resolved = resolve_preset_size(*preset, available_size);
match resolved {
// Some allowance for fractional scaling purposes.
ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,
@@ -634,13 +634,7 @@ impl<W: LayoutElement> FloatingSpace<W> {
};
let preset = self.options.preset_column_widths[preset_idx];
- let change = match preset {
- ColumnWidth::Proportion(prop) => SizeChange::SetProportion(prop * 100.),
- ColumnWidth::Fixed(fixed) => SizeChange::SetFixed(fixed.round() as i32),
- _ => unreachable!(),
- };
-
- self.set_window_width(Some(&id), change, true);
+ self.set_window_width(Some(&id), SizeChange::from(preset), true);
self.tiles[idx].floating_preset_width_idx = Some(preset_idx);
@@ -1138,53 +1132,34 @@ impl<W: LayoutElement> FloatingSpace<W> {
}
}
- pub fn resolve_width(&self, width: ColumnWidth) -> ResolvedSize {
- width.resolve_no_gaps(&self.options, self.working_area.size.w)
- }
-
- pub fn resolve_height(&self, height: PresetSize) -> ResolvedSize {
- resolve_preset_size(height, self.working_area.size.h)
- }
-
pub fn new_window_size(
&self,
- width: Option<ColumnWidth>,
+ width: Option<PresetSize>,
height: Option<PresetSize>,
rules: &ResolvedWindowRules,
) -> Size<i32, Logical> {
let border = rules.border.resolve_against(self.options.border);
- let width = if let Some(width) = width {
- let width = match self.resolve_width(width) {
- ResolvedSize::Tile(mut size) => {
- if !border.off {
- size -= border.width.0 * 2.;
+ let resolve = |size: Option<PresetSize>, working_area_size: f64| {
+ if let Some(size) = size {
+ let size = match resolve_preset_size(size, working_area_size) {
+ ResolvedSize::Tile(mut size) => {
+ if !border.off {
+ size -= border.width.0 * 2.;
+ }
+ size
}
- size
- }
- ResolvedSize::Window(size) => size,
- };
+ ResolvedSize::Window(size) => size,
+ };
- max(1, width.floor() as i32)
- } else {
- 0
+ max(1, size.floor() as i32)
+ } else {
+ 0
+ }
};
- let height = if let Some(height) = height {
- let height = match self.resolve_height(height) {
- ResolvedSize::Tile(mut size) => {
- if !border.off {
- size -= border.width.0 * 2.;
- }
- size
- }
- ResolvedSize::Window(size) => size,
- };
-
- max(1, height.floor() as i32)
- } else {
- 0
- };
+ let width = resolve(width, self.working_area.size.w);
+ let height = resolve(height, self.working_area.size.h);
Size::from((width, height))
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index ff355615..5351ccf1 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -316,10 +316,10 @@ pub struct Options {
pub center_focused_column: CenterFocusedColumn,
pub always_center_single_column: bool,
pub empty_workspace_above_first: bool,
- /// Column widths that `toggle_width()` switches between.
- pub preset_column_widths: Vec<ColumnWidth>,
+ /// Column or window widths that `toggle_width()` switches between.
+ pub preset_column_widths: Vec<PresetSize>,
/// Initial width for new columns.
- pub default_column_width: Option<ColumnWidth>,
+ pub default_column_width: Option<PresetSize>,
/// Window height that `toggle_window_height()` switches between.
pub preset_window_heights: Vec<PresetSize>,
pub animations: niri_config::Animations,
@@ -341,9 +341,9 @@ impl Default for Options {
always_center_single_column: false,
empty_workspace_above_first: false,
preset_column_widths: vec![
- ColumnWidth::Proportion(1. / 3.),
- ColumnWidth::Proportion(0.5),
- ColumnWidth::Proportion(2. / 3.),
+ PresetSize::Proportion(1. / 3.),
+ PresetSize::Proportion(0.5),
+ PresetSize::Proportion(2. / 3.),
],
default_column_width: None,
animations: Default::default(),
@@ -492,12 +492,7 @@ impl Options {
let preset_column_widths = if layout.preset_column_widths.is_empty() {
Options::default().preset_column_widths
} else {
- layout
- .preset_column_widths
- .iter()
- .copied()
- .map(ColumnWidth::from)
- .collect()
+ layout.preset_column_widths.clone()
};
let preset_window_heights = if layout.preset_window_heights.is_empty() {
Options::default().preset_window_heights
@@ -505,13 +500,13 @@ impl Options {
layout.preset_window_heights.clone()
};
- // Missing default_column_width maps to Some(ColumnWidth::Proportion(0.5)),
+ // Missing default_column_width maps to Some(PresetSize::Proportion(0.5)),
// while present, but empty, maps to None.
let default_column_width = layout
.default_column_width
.as_ref()
- .map(|w| w.0.map(ColumnWidth::from))
- .unwrap_or(Some(ColumnWidth::Proportion(0.5)));
+ .map(|w| w.0)
+ .unwrap_or(Some(PresetSize::Proportion(0.5)));
Self {
gaps: layout.gaps.0,
@@ -855,14 +850,14 @@ impl<W: LayoutElement> Layout<W> {
&mut self,
window: W,
target: AddWindowTarget<W>,
- width: Option<ColumnWidth>,
+ width: Option<PresetSize>,
height: Option<PresetSize>,
is_full_width: bool,
is_floating: bool,
activate: ActivateWindow,
) -> Option<&Output> {
- let resolved_width = self.resolve_default_width(&window, width, is_floating);
- let resolved_height = height.map(SizeChange::from);
+ let scrolling_width = self.resolve_scrolling_width(&window, width);
+ let scrolling_height = height.map(SizeChange::from);
let id = window.id().clone();
match &mut self.monitor_set {
@@ -933,7 +928,7 @@ impl<W: LayoutElement> Layout<W> {
window,
target,
activate,
- resolved_width,
+ scrolling_width,
is_full_width,
is_floating,
);
@@ -944,7 +939,7 @@ impl<W: LayoutElement> Layout<W> {
// Set the default height for scrolling windows.
if !is_floating {
- if let Some(change) = resolved_height {
+ if let Some(change) = scrolling_height {
let ws = mon
.workspaces
.iter_mut()
@@ -1005,14 +1000,14 @@ impl<W: LayoutElement> Layout<W> {
tile,
target,
activate,
- resolved_width,
+ scrolling_width,
is_full_width,
is_floating,
);
// Set the default height for scrolling windows.
if !is_floating {
- if let Some(change) = resolved_height {
+ if let Some(change) = scrolling_height {
ws.set_window_height(Some(&id), change);
}
}
@@ -4283,28 +4278,23 @@ impl<W: LayoutElement> Layout<W> {
self.windows().any(|(_, win)| win.id() == window)
}
- fn resolve_default_width(
- &self,
- window: &W,
- width: Option<ColumnWidth>,
- is_floating: bool,
- ) -> ColumnWidth {
- let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(f64::from(window.size().w)));
- if is_floating {
- return width;
- }
+ fn resolve_scrolling_width(&self, window: &W, width: Option<PresetSize>) -> ColumnWidth {
+ let width = width.unwrap_or_else(|| PresetSize::Fixed(window.size().w));
+ match width {
+ PresetSize::Fixed(fixed) => {
+ let mut fixed = f64::from(fixed);
- // Add border width to account for the issue that the scrolling layout currently doesn't
- // take borders into account for fixed sizes.
- if let ColumnWidth::Fixed(w) = &mut width {
- let rules = window.rules();
- let border_config = rules.border.resolve_against(self.options.border);
- if !border_config.off {
- *w += border_config.width.0 * 2.;
+ // Add border width since ColumnWidth includes borders.
+ let rules = window.rules();
+ let border = rules.border.resolve_against(self.options.border);
+ if !border.off {
+ fixed += border.width.0 * 2.;
+ }
+
+ ColumnWidth::Fixed(fixed)
}
+ PresetSize::Proportion(prop) => ColumnWidth::Proportion(prop),
}
-
- width
}
}
diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs
index 460f7528..8649da27 100644
--- a/src/layout/scrolling.rs
+++ b/src/layout/scrolling.rs
@@ -162,6 +162,9 @@ pub struct Column<W: LayoutElement> {
/// upon unfullscreening and untoggling full-width.
width: ColumnWidth,
+ /// Currently selected preset width index.
+ preset_width_idx: Option<usize>,
+
/// Whether this column is full-width.
is_full_width: bool,
@@ -209,8 +212,6 @@ pub enum ColumnWidth {
Proportion(f64),
/// Fixed width in logical pixels.
Fixed(f64),
- /// One of the preset widths.
- Preset(usize),
}
/// Height of a window in a column.
@@ -417,22 +418,24 @@ impl<W: LayoutElement> ScrollingSpace<W> {
pub fn new_window_size(
&self,
- width: Option<ColumnWidth>,
+ width: Option<PresetSize>,
height: Option<PresetSize>,
rules: &ResolvedWindowRules,
) -> Size<i32, Logical> {
let border = rules.border.resolve_against(self.options.border);
- let width = if let Some(width) = width {
- let is_fixed = matches!(width, ColumnWidth::Fixed(_));
-
- let mut width = width.resolve(&self.options, self.working_area.size.w);
-
- if !is_fixed && !border.off {
- width -= border.width.0 * 2.;
- }
+ let width = if let Some(size) = width {
+ let size = match resolve_preset_size(size, &self.options, self.working_area.size.w) {
+ ResolvedSize::Tile(mut size) => {
+ if !border.off {
+ size -= border.width.0 * 2.;
+ }
+ size
+ }
+ ResolvedSize::Window(size) => size,
+ };
- max(1, width.floor() as i32)
+ max(1, size.floor() as i32)
} else {
0
};
@@ -2173,7 +2176,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
let col = &mut self.columns[self.active_column_idx];
- col.toggle_width();
+ col.toggle_width(None);
cancel_resize_for_column(&mut self.interactive_resize, col);
}
@@ -2266,18 +2269,21 @@ impl<W: LayoutElement> ScrollingSpace<W> {
return;
}
- let col = if let Some(window) = window {
+ let (col, tile_idx) = if let Some(window) = window {
self.columns
.iter_mut()
- .find(|col| col.contains(window))
+ .find_map(|col| {
+ col.tiles
+ .iter()
+ .position(|tile| tile.window().id() == window)
+ .map(|tile_idx| (col, Some(tile_idx)))
+ })
.unwrap()
} else {
- &mut self.columns[self.active_column_idx]
+ (&mut self.columns[self.active_column_idx], None)
};
- // FIXME: when we fix preset fixed width to be per-window, this should also be made
- // window-specific to resolve the correct window width.
- col.toggle_width();
+ col.toggle_width(tile_idx);
cancel_resize_for_column(&mut self.interactive_resize, col);
}
@@ -3103,22 +3109,9 @@ impl ColumnWidth {
ColumnWidth::Proportion(proportion) => {
(view_width - options.gaps) * proportion - options.gaps
}
- ColumnWidth::Preset(idx) => {
- options.preset_column_widths[idx].resolve(options, view_width)
- }
ColumnWidth::Fixed(width) => width,
}
}
-
- pub fn resolve_no_gaps(self, options: &Options, view_width: f64) -> ResolvedSize {
- match self {
- ColumnWidth::Proportion(proportion) => ResolvedSize::Tile(view_width * proportion),
- ColumnWidth::Preset(idx) => {
- options.preset_column_widths[idx].resolve_no_gaps(options, view_width)
- }
- ColumnWidth::Fixed(width) => ResolvedSize::Window(width),
- }
- }
}
impl From<PresetSize> for ColumnWidth {
@@ -3152,6 +3145,7 @@ impl<W: LayoutElement> Column<W> {
data: vec![],
active_tile_idx: 0,
width,
+ preset_width_idx: None,
is_full_width,
is_fullscreen: false,
move_animation: None,
@@ -3186,11 +3180,9 @@ impl<W: LayoutElement> Column<W> {
update_sizes = true;
}
- // If preset widths changed, make our width non-preset.
+ // If preset widths changed, clear our stored preset index.
if self.options.preset_column_widths != options.preset_column_widths {
- if let ColumnWidth::Preset(idx) = self.width {
- self.width = self.options.preset_column_widths[idx];
- }
+ self.preset_width_idx = None;
}
// If preset heights changed, make our heights non-preset.
@@ -3224,12 +3216,6 @@ impl<W: LayoutElement> Column<W> {
}
}
- fn set_width(&mut self, width: ColumnWidth, animate: bool) {
- self.width = width;
- self.is_full_width = false;
- self.update_tile_sizes(animate);
- }
-
pub fn advance_animations(&mut self) {
if let Some(anim) = &mut self.move_animation {
if anim.is_done() {
@@ -3673,30 +3659,42 @@ impl<W: LayoutElement> Column<W> {
true
}
- fn toggle_width(&mut self) {
- let width = if self.is_full_width {
- ColumnWidth::Proportion(1.)
+ fn toggle_width(&mut self, tile_idx: Option<usize>) {
+ let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);
+
+ let preset_idx = if self.is_full_width {
+ None
} else {
- self.width
+ self.preset_width_idx
};
- let idx = match width {
- ColumnWidth::Preset(idx) => (idx + 1) % self.options.preset_column_widths.len(),
- _ => {
- let current = self.width();
- self.options
- .preset_column_widths
- .iter()
- .position(|prop| {
- let resolved = prop.resolve(&self.options, self.working_area.size.w);
+ let preset_idx = if let Some(idx) = preset_idx {
+ (idx + 1) % self.options.preset_column_widths.len()
+ } else {
+ let tile = &self.tiles[tile_idx];
+ let current_window = tile.window_expected_or_current_size().w;
+ let current_tile = tile.tile_expected_or_current_size().w;
+
+ let available_size = self.working_area.size.w;
+
+ self.options
+ .preset_column_widths
+ .iter()
+ .position(|prop| {
+ let resolved = resolve_preset_size(*prop, &self.options, available_size);
+ match resolved {
// Some allowance for fractional scaling purposes.
- current + 1. < resolved
- })
- .unwrap_or(0)
- }
+ ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,
+ ResolvedSize::Window(resolved) => current_window + 1. < resolved,
+ }
+ })
+ .unwrap_or(0)
};
- let width = ColumnWidth::Preset(idx);
- self.set_width(width, true);
+
+ let preset = self.options.preset_column_widths[preset_idx];
+ self.set_column_width(SizeChange::from(preset), Some(tile_idx), true);
+
+ self.preset_width_idx = Some(preset_idx);
}
fn toggle_full_width(&mut self) {
@@ -3705,18 +3703,13 @@ impl<W: LayoutElement> Column<W> {
}
fn set_column_width(&mut self, change: SizeChange, tile_idx: Option<usize>, animate: bool) {
- let width = if self.is_full_width {
+ let current = if self.is_full_width {
ColumnWidth::Proportion(1.)
} else {
self.width
};
- let current_px = width.resolve(&self.options, self.working_area.size.w);
-
- let current = match width {
- ColumnWidth::Preset(idx) => self.options.preset_column_widths[idx],
- current => current,
- };
+ let current_px = current.resolve(&self.options, self.working_area.size.w);
// FIXME: fix overflows then remove limits.
const MAX_PX: f64 = 100000.;
@@ -3755,10 +3748,12 @@ impl<W: LayoutElement> Column<W> {
let proportion = (current + delta / 100.).clamp(0., MAX_F);
ColumnWidth::Proportion(proportion)
}
- (ColumnWidth::Preset(_), _) => unreachable!(),
};
- self.set_width(width, animate);
+ self.width = width;
+ self.preset_width_idx = None;
+ self.is_full_width = false;
+ self.update_tile_sizes(animate);
}
fn set_window_height(&mut self, change: SizeChange, tile_idx: Option<usize>, animate: bool) {
@@ -4043,6 +4038,10 @@ impl<W: LayoutElement> Column<W> {
assert_eq!(self.tiles.len(), 1);
}
+ if let Some(idx) = self.preset_width_idx {
+ assert!(idx < self.options.preset_column_widths.len());
+ }
+
let tile_count = self.tiles.len();
if tile_count == 1 {
if let WindowHeight::Auto { weight } = self.data[0].height {
diff --git a/src/layout/tests.rs b/src/layout/tests.rs
index 47484817..e39b3ca5 100644
--- a/src/layout/tests.rs
+++ b/src/layout/tests.rs
@@ -3002,6 +3002,71 @@ fn move_workspace_to_same_monitor_doesnt_reorder() {
assert_eq!(counts, &[1, 2, 0]);
}
+#[test]
+fn preset_column_width_fixed_correct_with_border() {
+ let ops = [
+ Op::AddOutput(0),
+ Op::AddWindow {
+ params: TestWindowParams::new(0),
+ },
+ Op::SwitchPresetColumnWidth,
+ ];
+
+ let options = Options {
+ preset_column_widths: vec![PresetSize::Fixed(500)],
+ ..Default::default()
+ };
+ let mut layout = check_ops_with_options(options, &ops);
+
+ let win = layout.windows().next().unwrap().1;
+ assert_eq!(win.requested_size().unwrap().w, 500);
+
+ // Add border.
+ let options = Options {
+ preset_column_widths: vec![PresetSize::Fixed(500)],
+ border: niri_config::Border {
+ off: false,
+ width: FloatOrInt(5.),
+ ..Default::default()
+ },
+ ..Default::default()
+ };
+ layout.update_options(options);
+
+ // With border, the window gets less size.
+ let win = layout.windows().next().unwrap().1;
+ assert_eq!(win.requested_size().unwrap().w, 490);
+
+ // However, preset fixed width will still work correctly.
+ layout.toggle_width();
+ let win = layout.windows().next().unwrap().1;
+ assert_eq!(win.requested_size().unwrap().w, 500);
+}
+
+#[test]
+fn preset_column_width_reset_after_set_width() {
+ let ops = [
+ Op::AddOutput(0),
+ Op::AddWindow {
+ params: TestWindowParams::new(0),
+ },
+ Op::SwitchPresetColumnWidth,
+ Op::SetWindowWidth {
+ id: None,
+ change: SizeChange::AdjustFixed(-10),
+ },
+ Op::SwitchPresetColumnWidth,
+ ];
+
+ let options = Options {
+ preset_column_widths: vec![PresetSize::Fixed(500), PresetSize::Fixed(1000)],
+ ..Default::default()
+ };
+ let layout = check_ops_with_options(options, &ops);
+ let win = layout.windows().next().unwrap().1;
+ assert_eq!(win.requested_size().unwrap().w, 500);
+}
+
fn parent_id_causes_loop(layout: &Layout<TestWindow>, id: usize, mut parent_id: usize) -> bool {
if parent_id == id {
return true;
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 4426c264..c4cad666 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -713,9 +713,9 @@ impl<W: LayoutElement> Workspace<W> {
pub fn resolve_default_width(
&self,
- default_width: Option<Option<ColumnWidth>>,
+ default_width: Option<Option<PresetSize>>,
is_floating: bool,
- ) -> Option<ColumnWidth> {
+ ) -> Option<PresetSize> {
match default_width {
Some(Some(width)) => Some(width),
Some(None) => None,
@@ -740,7 +740,7 @@ impl<W: LayoutElement> Workspace<W> {
pub fn new_window_size(
&self,
- width: Option<ColumnWidth>,
+ width: Option<PresetSize>,
height: Option<PresetSize>,
is_floating: bool,
rules: &ResolvedWindowRules,
@@ -772,7 +772,7 @@ impl<W: LayoutElement> Workspace<W> {
pub fn configure_new_window(
&self,
window: &Window,
- width: Option<ColumnWidth>,
+ width: Option<PresetSize>,
height: Option<PresetSize>,
is_floating: bool,
rules: &ResolvedWindowRules,