aboutsummaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-02-17 21:22:10 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-02-17 21:30:23 +0300
commit92cc2b89f75fd13d83208626fa49724216fbbf9e (patch)
tree7591440c59e669e7f4d3e1d68a011b698a9d0cb5 /src/layout
parent078383ea8208337d3c9ea89118aa615d7fbbab17 (diff)
downloadniri-92cc2b89f75fd13d83208626fa49724216fbbf9e.tar.gz
niri-92cc2b89f75fd13d83208626fa49724216fbbf9e.tar.bz2
niri-92cc2b89f75fd13d83208626fa49724216fbbf9e.zip
Implement expand-column-to-available-width
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/mod.rs7
-rw-r--r--src/layout/monitor.rs4
-rw-r--r--src/layout/scrolling.rs83
-rw-r--r--src/layout/tests.rs2
-rw-r--r--src/layout/workspace.rs7
5 files changed, 103 insertions, 0 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 1f99f9d1..b46f5e3c 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -2977,6 +2977,13 @@ impl<W: LayoutElement> Layout<W> {
workspace.reset_window_height(window);
}
+ pub fn expand_column_to_available_width(&mut self) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.expand_column_to_available_width();
+ }
+
pub fn toggle_window_floating(&mut self, window: Option<&W::Id>) {
if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {
if window.is_none() || window == Some(move_.tile.window().id()) {
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs
index 8abaf5f6..1aae32e0 100644
--- a/src/layout/monitor.rs
+++ b/src/layout/monitor.rs
@@ -840,6 +840,10 @@ impl<W: LayoutElement> Monitor<W> {
self.active_workspace().set_column_width(change);
}
+ pub fn expand_column_to_available_width(&mut self) {
+ self.active_workspace().expand_column_to_available_width();
+ }
+
pub fn move_workspace_down(&mut self) {
let mut new_idx = min(self.active_workspace_idx + 1, self.workspaces.len() - 1);
if new_idx == self.active_workspace_idx {
diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs
index 926cfd76..417def6a 100644
--- a/src/layout/scrolling.rs
+++ b/src/layout/scrolling.rs
@@ -2524,6 +2524,89 @@ impl<W: LayoutElement> ScrollingSpace<W> {
cancel_resize_for_column(&mut self.interactive_resize, col);
}
+ pub fn expand_column_to_available_width(&mut self) {
+ if self.columns.is_empty() {
+ return;
+ }
+
+ let col = &mut self.columns[self.active_column_idx];
+ if col.is_fullscreen || col.is_full_width {
+ return;
+ }
+
+ if self.is_centering_focused_column() {
+ // Always-centered mode is different since the active window position cannot be
+ // controlled (it's always at the center). I guess you could come up with different
+ // logic here that computes the width in such a way so as to leave nearby columns fully
+ // on screen while taking into account that the active column will remain centered
+ // after resizing. But I'm not sure it's that useful? So let's do the simple thing.
+ let col = &mut self.columns[self.active_column_idx];
+ col.set_column_width(SizeChange::SetProportion(1.), None, true);
+ cancel_resize_for_column(&mut self.interactive_resize, col);
+ return;
+ }
+
+ // Consider the end of an ongoing animation because that's what compute to fit does too.
+ let view_x = self.target_view_pos();
+ let working_x = self.working_area.loc.x;
+ let working_w = self.working_area.size.w;
+
+ // Count all columns that are fully visible inside the working area.
+ let mut width_taken = 0.;
+ let mut leftmost_col_x = None;
+ let mut active_col_x = None;
+
+ let gap = self.options.gaps;
+ let col_xs = self.column_xs(self.data.iter().copied());
+ for (idx, col_x) in col_xs.take(self.columns.len()).enumerate() {
+ if col_x < view_x + working_x + gap {
+ // Column goes off-screen to the left.
+ continue;
+ }
+
+ leftmost_col_x.get_or_insert(col_x);
+
+ let width = self.data[idx].width;
+ if view_x + working_x + working_w < col_x + width + gap {
+ // Column goes off-screen to the right. We can stop here.
+ break;
+ }
+
+ if idx == self.active_column_idx {
+ active_col_x = Some(col_x);
+ }
+
+ width_taken += width + gap;
+ }
+
+ if active_col_x.is_none() {
+ // The active column wasn't fully on screen, so we can't meaningfully do anything.
+ return;
+ }
+
+ let available_width = working_w - gap - width_taken;
+ if available_width <= 0. {
+ // Nowhere to expand.
+ return;
+ }
+
+ let active_width = self.data[self.active_column_idx].width;
+
+ let col = &mut self.columns[self.active_column_idx];
+ col.width = ColumnWidth::Fixed(active_width + available_width);
+ col.preset_width_idx = None;
+ col.is_full_width = false;
+ col.update_tile_sizes(true);
+
+ cancel_resize_for_column(&mut self.interactive_resize, col);
+
+ // Put the leftmost window into the view.
+ let new_view_x = leftmost_col_x.unwrap() - gap - working_x;
+ self.animate_view_offset(self.active_column_idx, new_view_x - active_col_x.unwrap());
+ // Just in case.
+ self.animate_view_offset_to_column(None, self.active_column_idx, None);
+ }
+
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) -> bool {
let (mut col_idx, tile_idx) = self
.columns
diff --git a/src/layout/tests.rs b/src/layout/tests.rs
index 813f4765..9d3e8634 100644
--- a/src/layout/tests.rs
+++ b/src/layout/tests.rs
@@ -496,6 +496,7 @@ enum Op {
#[proptest(strategy = "proptest::option::of(1..=5usize)")]
id: Option<usize>,
},
+ ExpandColumnToAvailableWidth,
ToggleWindowFloating {
#[proptest(strategy = "proptest::option::of(1..=5usize)")]
id: Option<usize>,
@@ -1133,6 +1134,7 @@ impl Op {
let id = id.filter(|id| layout.has_window(id));
layout.reset_window_height(id.as_ref());
}
+ Op::ExpandColumnToAvailableWidth => layout.expand_column_to_available_width(),
Op::ToggleWindowFloating { id } => {
let id = id.filter(|id| layout.has_window(id));
layout.toggle_window_floating(id.as_ref());
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 54926a1f..15f6b9b0 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -1128,6 +1128,13 @@ impl<W: LayoutElement> Workspace<W> {
}
}
+ pub fn expand_column_to_available_width(&mut self) {
+ if self.floating_is_active.get() {
+ return;
+ }
+ self.scrolling.expand_column_to_available_width();
+ }
+
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {
let mut unfullscreen_to_floating = false;
if self.floating.has_window(window) {