diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-12-25 16:43:15 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-12-30 20:12:37 +0300 |
| commit | 1710bb78df51ae763e926297c02acf9f9036cf74 (patch) | |
| tree | ea5aabf8fef702a4446b290ff50f5b5ac35437e6 | |
| parent | 3e13fc3e705f5a9c14cc993d4841ee1346e883f2 (diff) | |
| download | niri-1710bb78df51ae763e926297c02acf9f9036cf74.tar.gz niri-1710bb78df51ae763e926297c02acf9f9036cf74.tar.bz2 niri-1710bb78df51ae763e926297c02acf9f9036cf74.zip | |
floating: Implement toggle-width/height actions
| -rw-r--r-- | src/layout/floating.rs | 104 | ||||
| -rw-r--r-- | src/layout/scrolling.rs | 10 | ||||
| -rw-r--r-- | src/layout/tile.rs | 44 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 12 |
4 files changed, 163 insertions, 7 deletions
diff --git a/src/layout/floating.rs b/src/layout/floating.rs index 4c56288b..6262f085 100644 --- a/src/layout/floating.rs +++ b/src/layout/floating.rs @@ -2,6 +2,7 @@ use std::cmp::max; use std::iter::zip; use std::rc::Rc; +use niri_config::PresetSize; use niri_ipc::SizeChange; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size}; @@ -9,7 +10,7 @@ use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size}; use super::closing_window::{ClosingWindow, ClosingWindowRenderElement}; use super::scrolling::ColumnWidth; use super::tile::{Tile, TileRenderElement, TileRenderSnapshot}; -use super::workspace::InteractiveResize; +use super::workspace::{InteractiveResize, ResolvedSize}; use super::{ ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile, SizeFrac, }; @@ -597,6 +598,89 @@ impl<W: LayoutElement> FloatingSpace<W> { } } + pub fn toggle_window_width(&mut self, id: Option<&W::Id>) { + let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else { + return; + }; + let idx = self.idx_of(&id).unwrap(); + + let view_size = self.working_area.size.w; + + let tile = &mut self.tiles[idx]; + let preset_idx = if let Some(idx) = tile.floating_preset_width_idx { + (idx + 1) % self.options.preset_column_widths.len() + } else { + let current_window = tile.window_expected_or_current_size().w; + let current_tile = tile.tile_expected_or_current_size().w; + + self.options + .preset_column_widths + .iter() + .position(|preset| { + let resolved = preset.resolve_no_gaps(&self.options, view_size); + match resolved { + // Some allowance for fractional scaling purposes. + ResolvedSize::Tile(resolved) => current_tile + 1. < resolved, + ResolvedSize::Window(resolved) => current_window + 1. < resolved, + } + }) + .unwrap_or(0) + }; + + let preset = self.options.preset_column_widths[preset_idx]; + let window_width = match preset.resolve_no_gaps(&self.options, view_size) { + ResolvedSize::Tile(w) => tile.window_width_for_tile_width(w), + ResolvedSize::Window(w) => w, + }; + + let window_width = window_width.round().clamp(1., 100000.) as i32; + self.set_window_width(Some(&id), SizeChange::SetFixed(window_width), true); + + self.tiles[idx].floating_preset_width_idx = Some(preset_idx); + } + + pub fn toggle_window_height(&mut self, id: Option<&W::Id>) { + let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else { + return; + }; + let idx = self.idx_of(&id).unwrap(); + + let view_size = self.working_area.size.h; + + let tile = &mut self.tiles[idx]; + let preset_idx = if let Some(idx) = tile.floating_preset_height_idx { + (idx + 1) % self.options.preset_window_heights.len() + } else { + let current_window = tile.window_expected_or_current_size().h; + let current_tile = tile.tile_expected_or_current_size().h; + + self.options + .preset_window_heights + .iter() + .position(|preset| { + let resolved = resolve_preset_size(*preset, view_size); + match resolved { + // Some allowance for fractional scaling purposes. + ResolvedSize::Tile(resolved) => current_tile + 1. < resolved, + ResolvedSize::Window(resolved) => current_window + 1. < resolved, + } + }) + .unwrap_or(0) + }; + + let preset = self.options.preset_window_heights[preset_idx]; + let window_height = match resolve_preset_size(preset, view_size) { + ResolvedSize::Tile(h) => tile.window_height_for_tile_height(h), + ResolvedSize::Window(h) => h, + }; + + let window_height = window_height.round().clamp(1., 100000.) as i32; + self.set_window_height(Some(&id), SizeChange::SetFixed(window_height), true); + + let tile = &mut self.tiles[idx]; + tile.floating_preset_height_idx = Some(preset_idx); + } + pub fn set_window_width(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) { let Some(id) = id.or(self.active_window_id.as_ref()) else { return; @@ -609,6 +693,8 @@ impl<W: LayoutElement> FloatingSpace<W> { }; let tile = &mut self.tiles[idx]; + tile.floating_preset_width_idx = None; + let win = tile.window_mut(); let min_size = win.min_size(); let max_size = win.max_size(); @@ -635,6 +721,8 @@ impl<W: LayoutElement> FloatingSpace<W> { }; let tile = &mut self.tiles[idx]; + tile.floating_preset_height_idx = None; + let win = tile.window_mut(); let min_size = win.min_size(); let max_size = win.max_size(); @@ -1004,6 +1092,13 @@ impl<W: LayoutElement> FloatingSpace<W> { assert_eq!(self.scale, tile.scale()); tile.verify_invariants(); + if let Some(idx) = tile.floating_preset_width_idx { + assert!(idx < self.options.preset_column_widths.len()); + } + if let Some(idx) = tile.floating_preset_height_idx { + assert!(idx < self.options.preset_window_heights.len()); + } + assert!( !tile.window().is_pending_fullscreen(), "floating windows cannot be fullscreen" @@ -1055,3 +1150,10 @@ fn compute_toplevel_bounds( )) .to_i32_floor() } + +fn resolve_preset_size(preset: PresetSize, view_size: f64) -> ResolvedSize { + match preset { + PresetSize::Proportion(proportion) => ResolvedSize::Tile(view_size * proportion), + PresetSize::Fixed(width) => ResolvedSize::Window(f64::from(width)), + } +} diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index 1fe8cc34..7cb01218 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -2832,6 +2832,16 @@ impl ColumnWidth { 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 { diff --git a/src/layout/tile.rs b/src/layout/tile.rs index 6551e88c..fac2c5c4 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -67,6 +67,12 @@ pub struct Tile<W: LayoutElement> { /// the window starts out in the tiling layout. pub(super) floating_pos: Option<Point<f64, SizeFrac>>, + /// Currently selected preset width index when this tile is floating. + pub(super) floating_preset_width_idx: Option<usize>, + + /// Currently selected preset height index when this tile is floating. + pub(super) floating_preset_height_idx: Option<usize>, + /// The animation upon opening a window. open_animation: Option<OpenAnimation>, @@ -143,6 +149,8 @@ impl<W: LayoutElement> Tile<W> { unfullscreen_to_floating: false, floating_window_size: None, floating_pos: None, + floating_preset_width_idx: None, + floating_preset_height_idx: None, open_animation: None, resize_animation: None, move_x_animation: None, @@ -157,6 +165,14 @@ impl<W: LayoutElement> Tile<W> { } pub fn update_config(&mut self, scale: f64, options: Rc<Options>) { + // If preset widths or heights changed, clear our stored preset index. + if self.options.preset_column_widths != options.preset_column_widths { + self.floating_preset_width_idx = None; + } + if self.options.preset_window_heights != options.preset_window_heights { + self.floating_preset_height_idx = None; + } + self.scale = scale; self.options = options; @@ -475,6 +491,25 @@ impl<W: LayoutElement> Tile<W> { size } + pub fn tile_expected_or_current_size(&self) -> Size<f64, Logical> { + let mut size = self.window_expected_or_current_size(); + + if self.is_fullscreen { + // Normally we'd just return the fullscreen size here, but this makes things a bit + // nicer if a fullscreen window is bigger than the fullscreen size for some reason. + size.w = f64::max(size.w, self.fullscreen_size.w); + size.h = f64::max(size.h, self.fullscreen_size.h); + return size; + } + + if let Some(width) = self.effective_border_width() { + size.w += width * 2.; + size.h += width * 2.; + } + + size + } + pub fn window_size(&self) -> Size<f64, Logical> { let mut size = self.window.size().to_f64(); size = size @@ -483,6 +518,15 @@ impl<W: LayoutElement> Tile<W> { size } + pub fn window_expected_or_current_size(&self) -> Size<f64, Logical> { + let size = self.window.expected_size(); + let mut size = size.unwrap_or_else(|| self.window.size()).to_f64(); + size = size + .to_physical_precise_round(self.scale) + .to_logical(self.scale); + size + } + fn animated_window_size(&self) -> Size<f64, Logical> { let mut size = self.window_size(); diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 747b6a13..e3757ff5 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -952,11 +952,11 @@ impl<W: LayoutElement> Workspace<W> { } pub fn toggle_width(&mut self) { - // TODO if self.floating_is_active.get() { - return; + self.floating.toggle_window_width(None); + } else { + self.scrolling.toggle_width(); } - self.scrolling.toggle_width(); } pub fn toggle_full_width(&mut self) { @@ -998,10 +998,10 @@ impl<W: LayoutElement> Workspace<W> { if window.map_or(self.floating_is_active.get(), |id| { self.floating.has_window(id) }) { - // TODO - return; + self.floating.toggle_window_height(window); + } else { + self.scrolling.toggle_window_height(window); } - self.scrolling.toggle_window_height(window); } pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) { |
