aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-02-05 17:18:21 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-02-05 17:25:57 +0300
commita444efd0ebee8e203877daedc7bc65584f72daba (patch)
tree2ca010493711213ee3faced62b63a62048f37bad
parentc41f93a468c4d04e16f8c89736cc48cd4ab4c623 (diff)
downloadniri-a444efd0ebee8e203877daedc7bc65584f72daba.tar.gz
niri-a444efd0ebee8e203877daedc7bc65584f72daba.tar.bz2
niri-a444efd0ebee8e203877daedc7bc65584f72daba.zip
Add focus-window-in-column (by index) action
-rw-r--r--niri-config/src/lib.rs2
-rw-r--r--niri-ipc/src/lib.rs8
-rw-r--r--src/input/mod.rs7
-rw-r--r--src/layout/mod.rs7
-rw-r--r--src/layout/monitor.rs4
-rw-r--r--src/layout/scrolling.rs12
-rw-r--r--src/layout/tests.rs2
-rw-r--r--src/layout/workspace.rs7
8 files changed, 49 insertions, 0 deletions
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<niri_ipc::Action> 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<W: LayoutElement> Layout<W> {
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<W: LayoutElement> Monitor<W> {
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<W: LayoutElement> ScrollingSpace<W> {
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<W: LayoutElement> Column<W> {
.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<W: LayoutElement> Workspace<W> {
}
}
+ 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()