diff options
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/floating.rs | 63 | ||||
| -rw-r--r-- | src/layout/mod.rs | 72 | ||||
| -rw-r--r-- | src/layout/scrolling.rs | 139 | ||||
| -rw-r--r-- | src/layout/tests.rs | 65 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 8 |
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, |
