From a444efd0ebee8e203877daedc7bc65584f72daba Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Wed, 5 Feb 2025 17:18:21 +0300 Subject: Add focus-window-in-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/monitor.rs | 4 ++++ src/layout/scrolling.rs | 12 ++++++++++++ src/layout/tests.rs | 2 ++ src/layout/workspace.rs | 7 +++++++ 8 files changed, 49 insertions(+) diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index bb1f626c..8238e37d 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -1323,6 +1323,7 @@ pub enum Action { FullscreenWindowById(u64), #[knuffel(skip)] FocusWindow(u64), + FocusWindowInColumn(#[knuffel(argument)] u8), FocusWindowPrevious, FocusColumnLeft, FocusColumnRight, @@ -1506,6 +1507,7 @@ impl From for Action { niri_ipc::Action::FullscreenWindow { id: None } => Self::FullscreenWindow, niri_ipc::Action::FullscreenWindow { id: Some(id) } => Self::FullscreenWindowById(id), niri_ipc::Action::FocusWindow { id } => Self::FocusWindow(id), + niri_ipc::Action::FocusWindowInColumn { index } => Self::FocusWindowInColumn(index), niri_ipc::Action::FocusWindowPrevious {} => Self::FocusWindowPrevious, niri_ipc::Action::FocusColumnLeft {} => Self::FocusColumnLeft, niri_ipc::Action::FocusColumnRight {} => Self::FocusColumnRight, diff --git a/niri-ipc/src/lib.rs b/niri-ipc/src/lib.rs index 791b09a4..c5551cc3 100644 --- a/niri-ipc/src/lib.rs +++ b/niri-ipc/src/lib.rs @@ -215,6 +215,14 @@ pub enum Action { #[cfg_attr(feature = "clap", arg(long))] id: u64, }, + /// Focus a window in the focused column by index. + FocusWindowInColumn { + /// Index of the window in the column. + /// + /// The index starts from 1 for the topmost window. + #[cfg_attr(feature = "clap", arg())] + index: u8, + }, /// Focus the previously focused window. FocusWindowPrevious {}, /// Focus the column to the left. diff --git a/src/input/mod.rs b/src/input/mod.rs index 9f5f363c..5c3df990 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -681,6 +681,13 @@ impl State { self.focus_window(&window); } } + Action::FocusWindowInColumn(index) => { + self.niri.layout.focus_window_in_column(index); + self.maybe_warp_cursor_to_focus(); + self.niri.layer_shell_on_demand_focus = None; + // FIXME: granular + self.niri.queue_redraw_all(); + } Action::FocusWindowPrevious => { if let Some(window) = self.niri.previously_focused_window.clone() { self.focus_window(&window); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 45c5ffce..419f3fd3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1893,6 +1893,13 @@ impl Layout { true } + pub fn focus_window_in_column(&mut self, index: u8) { + let Some(monitor) = self.active_monitor() else { + return; + }; + monitor.focus_window_in_column(index); + } + pub fn focus_down(&mut self) { let Some(monitor) = self.active_monitor() else { return; diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index 37f5fb94..ebafad17 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -457,6 +457,10 @@ impl Monitor { self.active_workspace().focus_column_left_or_last(); } + pub fn focus_window_in_column(&mut self, index: u8) { + self.active_workspace().focus_window_in_column(index); + } + pub fn focus_down(&mut self) -> bool { self.active_workspace().focus_down() } diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index 07890284..da193e0d 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -1362,6 +1362,14 @@ impl ScrollingSpace { self.activate_column(self.columns.len() - 1); } + pub fn focus_window_in_column(&mut self, index: u8) { + if self.columns.is_empty() { + return; + } + + self.columns[self.active_column_idx].focus_index(index); + } + pub fn focus_down(&mut self) -> bool { if self.columns.is_empty() { return false; @@ -3614,6 +3622,10 @@ impl Column { .unwrap() } + fn focus_index(&mut self, index: u8) { + self.active_tile_idx = min(usize::from(index.saturating_sub(1)), self.tiles.len() - 1); + } + fn focus_up(&mut self) -> bool { if self.active_tile_idx == 0 { return false; diff --git a/src/layout/tests.rs b/src/layout/tests.rs index b08b1de0..05178f01 100644 --- a/src/layout/tests.rs +++ b/src/layout/tests.rs @@ -380,6 +380,7 @@ enum Op { FocusWindowOrWorkspaceDown, FocusWindowOrWorkspaceUp, FocusWindow(#[proptest(strategy = "1..=5usize")] usize), + FocusWindowInColumn(#[proptest(strategy = "1..=5u8")] u8), FocusWindowTop, FocusWindowBottom, FocusWindowDownOrTop, @@ -928,6 +929,7 @@ impl Op { Op::FocusWindowOrWorkspaceDown => layout.focus_window_or_workspace_down(), Op::FocusWindowOrWorkspaceUp => layout.focus_window_or_workspace_up(), Op::FocusWindow(id) => layout.activate_window(&id), + Op::FocusWindowInColumn(index) => layout.focus_window_in_column(index), Op::FocusWindowTop => layout.focus_window_top(), Op::FocusWindowBottom => layout.focus_window_bottom(), Op::FocusWindowDownOrTop => layout.focus_window_down_or_top(), diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 680e5e4c..519a9c61 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -848,6 +848,13 @@ impl Workspace { } } + pub fn focus_window_in_column(&mut self, index: u8) { + if self.floating_is_active.get() { + return; + } + self.scrolling.focus_window_in_column(index); + } + pub fn focus_down(&mut self) -> bool { if self.floating_is_active.get() { self.floating.focus_down() -- cgit