From a5d58d670bde166ff8bca8a83bb6f1136c7fa67b Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 13 Mar 2025 03:05:55 +0100 Subject: Add focus-column (by index) action --- niri-config/src/lib.rs | 2 ++ niri-ipc/src/lib.rs | 8 ++++++++ src/input/mod.rs | 7 +++++++ src/layout/mod.rs | 7 +++++++ src/layout/scrolling.rs | 8 ++++++++ src/layout/tests.rs | 2 ++ src/layout/workspace.rs | 7 +++++++ 7 files changed, 41 insertions(+) diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 579db485..0b776b67 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -1486,6 +1486,7 @@ pub enum Action { FocusColumnLast, FocusColumnRightOrFirst, FocusColumnLeftOrLast, + FocusColumn(#[knuffel(argument)] usize), FocusWindowOrMonitorUp, FocusWindowOrMonitorDown, FocusColumnOrMonitorLeft, @@ -1682,6 +1683,7 @@ impl From for Action { niri_ipc::Action::FocusColumnLast {} => Self::FocusColumnLast, niri_ipc::Action::FocusColumnRightOrFirst {} => Self::FocusColumnRightOrFirst, niri_ipc::Action::FocusColumnLeftOrLast {} => Self::FocusColumnLeftOrLast, + niri_ipc::Action::FocusColumn { index } => Self::FocusColumn(index), niri_ipc::Action::FocusWindowOrMonitorUp {} => Self::FocusWindowOrMonitorUp, niri_ipc::Action::FocusWindowOrMonitorDown {} => Self::FocusWindowOrMonitorDown, niri_ipc::Action::FocusColumnOrMonitorLeft {} => Self::FocusColumnOrMonitorLeft, diff --git a/niri-ipc/src/lib.rs b/niri-ipc/src/lib.rs index d4886819..c7dd2083 100644 --- a/niri-ipc/src/lib.rs +++ b/niri-ipc/src/lib.rs @@ -249,6 +249,14 @@ pub enum Action { FocusColumnRightOrFirst {}, /// Focus the next column to the left, looping if at start. FocusColumnLeftOrLast {}, + /// Focus a column by index. + FocusColumn { + /// Index of the column to focus. + /// + /// The index starts from 1 for the first column. + #[cfg_attr(feature = "clap", arg())] + index: usize, + }, /// Focus the window or the monitor above. FocusWindowOrMonitorUp {}, /// Focus the window or the monitor below. diff --git a/src/input/mod.rs b/src/input/mod.rs index 1141d5ce..b38da2a0 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -882,6 +882,13 @@ impl State { // FIXME: granular self.niri.queue_redraw_all(); } + Action::FocusColumn(index) => { + self.niri.layout.focus_column(index); + self.maybe_warp_cursor_to_focus(); + self.niri.layer_shell_on_demand_focus = None; + // FIXME: granular + self.niri.queue_redraw_all(); + } Action::FocusWindowOrMonitorUp => { if let Some(output) = self.niri.output_up() { if self.niri.layout.focus_window_up_or_output(&output) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index dc7daa65..11469551 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1946,6 +1946,13 @@ impl Layout { workspace.focus_column_left_or_last(); } + pub fn focus_column(&mut self, index: usize) { + let Some(workspace) = self.active_workspace_mut() else { + return; + }; + workspace.focus_column(index); + } + pub fn focus_window_up_or_output(&mut self, output: &Output) -> bool { if let Some(workspace) = self.active_workspace_mut() { if workspace.focus_up() { diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index a6df4045..cad65881 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -1491,6 +1491,14 @@ impl ScrollingSpace { self.activate_column(self.columns.len() - 1); } + pub fn focus_column(&mut self, index: usize) { + if self.columns.is_empty() { + return; + } + + self.activate_column(index.saturating_sub(1).min(self.columns.len() - 1)); + } + pub fn focus_window_in_column(&mut self, index: u8) { if self.columns.is_empty() { return; diff --git a/src/layout/tests.rs b/src/layout/tests.rs index 160e4e97..cfd5d053 100644 --- a/src/layout/tests.rs +++ b/src/layout/tests.rs @@ -374,6 +374,7 @@ enum Op { FocusColumnLast, FocusColumnRightOrFirst, FocusColumnLeftOrLast, + FocusColumn(#[proptest(strategy = "1..=5usize")] usize), FocusWindowOrMonitorUp(#[proptest(strategy = "1..=2u8")] u8), FocusWindowOrMonitorDown(#[proptest(strategy = "1..=2u8")] u8), FocusColumnOrMonitorLeft(#[proptest(strategy = "1..=2u8")] u8), @@ -907,6 +908,7 @@ impl Op { Op::FocusColumnLast => layout.focus_column_last(), Op::FocusColumnRightOrFirst => layout.focus_column_right_or_first(), Op::FocusColumnLeftOrLast => layout.focus_column_left_or_last(), + Op::FocusColumn(index) => layout.focus_column(index), Op::FocusWindowOrMonitorUp(id) => { let name = format!("output{id}"); let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else { diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 1cb55233..08614308 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -850,6 +850,13 @@ impl Workspace { } } + pub fn focus_column(&mut self, index: usize) { + if self.floating_is_active.get() { + self.focus_tiling(); + } + self.scrolling.focus_column(index); + } + pub fn focus_window_in_column(&mut self, index: u8) { if self.floating_is_active.get() { return; -- cgit