mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Implement expand-column-to-available-width
This commit is contained in:
@@ -1593,6 +1593,7 @@ pub enum Action {
|
||||
SwitchPresetWindowHeightById(u64),
|
||||
MaximizeColumn,
|
||||
SetColumnWidth(#[knuffel(argument, str)] SizeChange),
|
||||
ExpandColumnToAvailableWidth,
|
||||
SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget),
|
||||
ShowHotkeyOverlay,
|
||||
MoveWorkspaceToMonitorLeft,
|
||||
@@ -1794,6 +1795,7 @@ impl From<niri_ipc::Action> for Action {
|
||||
}
|
||||
niri_ipc::Action::MaximizeColumn {} => Self::MaximizeColumn,
|
||||
niri_ipc::Action::SetColumnWidth { change } => Self::SetColumnWidth(change),
|
||||
niri_ipc::Action::ExpandColumnToAvailableWidth {} => Self::ExpandColumnToAvailableWidth,
|
||||
niri_ipc::Action::SwitchLayout { layout } => Self::SwitchLayout(layout),
|
||||
niri_ipc::Action::ShowHotkeyOverlay {} => Self::ShowHotkeyOverlay,
|
||||
niri_ipc::Action::MoveWorkspaceToMonitorLeft {} => Self::MoveWorkspaceToMonitorLeft,
|
||||
|
||||
@@ -539,6 +539,8 @@ pub enum Action {
|
||||
#[cfg_attr(feature = "clap", arg(allow_hyphen_values = true))]
|
||||
change: SizeChange,
|
||||
},
|
||||
/// Expand the focused column to space not taken up by other fully visible columns.
|
||||
ExpandColumnToAvailableWidth {},
|
||||
/// Switch between keyboard layouts.
|
||||
SwitchLayout {
|
||||
/// Layout to switch to.
|
||||
|
||||
@@ -504,6 +504,11 @@ binds {
|
||||
Mod+Ctrl+R { reset-window-height; }
|
||||
Mod+F { maximize-column; }
|
||||
Mod+Shift+F { fullscreen-window; }
|
||||
|
||||
// Expand the focused column to space not taken up by other fully visible columns.
|
||||
// Makes the column "fill the rest of the space".
|
||||
Mod+Ctrl+F { expand-column-to-available-width; }
|
||||
|
||||
Mod+C { center-column; }
|
||||
|
||||
// Finer width adjustments.
|
||||
|
||||
@@ -1511,6 +1511,9 @@ impl State {
|
||||
self.niri.layout.reset_window_height(Some(&window));
|
||||
}
|
||||
}
|
||||
Action::ExpandColumnToAvailableWidth => {
|
||||
self.niri.layout.expand_column_to_available_width();
|
||||
}
|
||||
Action::ShowHotkeyOverlay => {
|
||||
if self.niri.hotkey_overlay.show() {
|
||||
self.niri.queue_redraw_all();
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user