aboutsummaryrefslogtreecommitdiff
path: root/src
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:51 +0300
commitc41f93a468c4d04e16f8c89736cc48cd4ab4c623 (patch)
tree3f6e8fb315550288b4e293e873b13dbf2d421486 /src
parent900da597e4d3d09ad4c4ddc3ba2abf58c4886ec9 (diff)
downloadniri-c41f93a468c4d04e16f8c89736cc48cd4ab4c623.tar.gz
niri-c41f93a468c4d04e16f8c89736cc48cd4ab4c623.tar.bz2
niri-c41f93a468c4d04e16f8c89736cc48cd4ab4c623.zip
Add focus-window-top/bottom/down-or-top/up-or-bottom actions
Diffstat (limited to 'src')
-rw-r--r--src/input/mod.rs28
-rw-r--r--src/layout/floating.rs20
-rw-r--r--src/layout/mod.rs28
-rw-r--r--src/layout/monitor.rs16
-rw-r--r--src/layout/scrolling.rs24
-rw-r--r--src/layout/tests.rs8
-rw-r--r--src/layout/workspace.rs28
7 files changed, 152 insertions, 0 deletions
diff --git a/src/input/mod.rs b/src/input/mod.rs
index b6eb0e3d..9f5f363c 100644
--- a/src/input/mod.rs
+++ b/src/input/mod.rs
@@ -987,6 +987,34 @@ impl State {
// FIXME: granular
self.niri.queue_redraw_all();
}
+ Action::FocusWindowTop => {
+ self.niri.layout.focus_window_top();
+ self.maybe_warp_cursor_to_focus();
+ self.niri.layer_shell_on_demand_focus = None;
+ // FIXME: granular
+ self.niri.queue_redraw_all();
+ }
+ Action::FocusWindowBottom => {
+ self.niri.layout.focus_window_bottom();
+ self.maybe_warp_cursor_to_focus();
+ self.niri.layer_shell_on_demand_focus = None;
+ // FIXME: granular
+ self.niri.queue_redraw_all();
+ }
+ Action::FocusWindowDownOrTop => {
+ self.niri.layout.focus_window_down_or_top();
+ self.maybe_warp_cursor_to_focus();
+ self.niri.layer_shell_on_demand_focus = None;
+ // FIXME: granular
+ self.niri.queue_redraw_all();
+ }
+ Action::FocusWindowUpOrBottom => {
+ self.niri.layout.focus_window_up_or_bottom();
+ self.maybe_warp_cursor_to_focus();
+ self.niri.layer_shell_on_demand_focus = None;
+ // FIXME: granular
+ self.niri.queue_redraw_all();
+ }
Action::MoveWindowToWorkspaceDown => {
self.niri.layout.move_to_workspace_down();
self.maybe_warp_cursor_to_focus();
diff --git a/src/layout/floating.rs b/src/layout/floating.rs
index b908d920..15c99e87 100644
--- a/src/layout/floating.rs
+++ b/src/layout/floating.rs
@@ -833,6 +833,26 @@ impl<W: LayoutElement> FloatingSpace<W> {
}
}
+ pub fn focus_topmost(&mut self) {
+ let result = self
+ .tiles_with_offsets()
+ .min_by(|(_, pos_a), (_, pos_b)| f64::total_cmp(&pos_a.y, &pos_b.y));
+ if let Some((tile, _)) = result {
+ let id = tile.window().id().clone();
+ self.activate_window(&id);
+ }
+ }
+
+ pub fn focus_bottommost(&mut self) {
+ let result = self
+ .tiles_with_offsets()
+ .max_by(|(_, pos_a), (_, pos_b)| f64::total_cmp(&pos_a.y, &pos_b.y));
+ if let Some((tile, _)) = result {
+ let id = tile.window().id().clone();
+ self.activate_window(&id);
+ }
+ }
+
fn move_to(&mut self, idx: usize, new_pos: Point<f64, Logical>, animate: bool) {
if animate {
self.move_and_animate(idx, new_pos);
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index f60e3192..45c5ffce 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -1949,6 +1949,34 @@ impl<W: LayoutElement> Layout<W> {
monitor.focus_window_or_workspace_up();
}
+ pub fn focus_window_top(&mut self) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.focus_window_top();
+ }
+
+ pub fn focus_window_bottom(&mut self) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.focus_window_bottom();
+ }
+
+ pub fn focus_window_down_or_top(&mut self) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.focus_window_down_or_top();
+ }
+
+ pub fn focus_window_up_or_bottom(&mut self) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.focus_window_up_or_bottom();
+ }
+
pub fn move_to_workspace_up(&mut self) {
let Some(monitor) = self.active_monitor() else {
return;
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs
index 310121b5..37f5fb94 100644
--- a/src/layout/monitor.rs
+++ b/src/layout/monitor.rs
@@ -493,6 +493,22 @@ impl<W: LayoutElement> Monitor<W> {
}
}
+ pub fn focus_window_top(&mut self) {
+ self.active_workspace().focus_window_top();
+ }
+
+ pub fn focus_window_bottom(&mut self) {
+ self.active_workspace().focus_window_bottom();
+ }
+
+ pub fn focus_window_down_or_top(&mut self) {
+ self.active_workspace().focus_window_down_or_top();
+ }
+
+ pub fn focus_window_up_or_bottom(&mut self) {
+ self.active_workspace().focus_window_up_or_bottom();
+ }
+
pub fn move_to_workspace_up(&mut self) {
let source_workspace_idx = self.active_workspace_idx;
diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs
index d90150d8..07890284 100644
--- a/src/layout/scrolling.rs
+++ b/src/layout/scrolling.rs
@@ -1422,6 +1422,22 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
}
+ pub fn focus_top(&mut self) {
+ if self.columns.is_empty() {
+ return;
+ }
+
+ self.columns[self.active_column_idx].focus_top()
+ }
+
+ pub fn focus_bottom(&mut self) {
+ if self.columns.is_empty() {
+ return;
+ }
+
+ self.columns[self.active_column_idx].focus_bottom()
+ }
+
fn move_column_to(&mut self, new_idx: usize) {
if self.active_column_idx == new_idx {
return;
@@ -3616,6 +3632,14 @@ impl<W: LayoutElement> Column<W> {
true
}
+ fn focus_top(&mut self) {
+ self.active_tile_idx = 0;
+ }
+
+ fn focus_bottom(&mut self) {
+ self.active_tile_idx = self.tiles.len() - 1;
+ }
+
fn move_up(&mut self) -> bool {
let new_idx = self.active_tile_idx.saturating_sub(1);
if self.active_tile_idx == new_idx {
diff --git a/src/layout/tests.rs b/src/layout/tests.rs
index e39b3ca5..b08b1de0 100644
--- a/src/layout/tests.rs
+++ b/src/layout/tests.rs
@@ -380,6 +380,10 @@ enum Op {
FocusWindowOrWorkspaceDown,
FocusWindowOrWorkspaceUp,
FocusWindow(#[proptest(strategy = "1..=5usize")] usize),
+ FocusWindowTop,
+ FocusWindowBottom,
+ FocusWindowDownOrTop,
+ FocusWindowUpOrBottom,
MoveColumnLeft,
MoveColumnRight,
MoveColumnToFirst,
@@ -924,6 +928,10 @@ 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::FocusWindowTop => layout.focus_window_top(),
+ Op::FocusWindowBottom => layout.focus_window_bottom(),
+ Op::FocusWindowDownOrTop => layout.focus_window_down_or_top(),
+ Op::FocusWindowUpOrBottom => layout.focus_window_up_or_bottom(),
Op::MoveColumnLeft => layout.move_left(),
Op::MoveColumnRight => layout.move_right(),
Op::MoveColumnToFirst => layout.move_column_to_first(),
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index c4cad666..680e5e4c 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -896,6 +896,34 @@ impl<W: LayoutElement> Workspace<W> {
}
}
+ pub fn focus_window_top(&mut self) {
+ if self.floating_is_active.get() {
+ self.floating.focus_topmost();
+ } else {
+ self.scrolling.focus_top();
+ }
+ }
+
+ pub fn focus_window_bottom(&mut self) {
+ if self.floating_is_active.get() {
+ self.floating.focus_bottommost();
+ } else {
+ self.scrolling.focus_bottom();
+ }
+ }
+
+ pub fn focus_window_down_or_top(&mut self) {
+ if !self.focus_down() {
+ self.focus_window_top();
+ }
+ }
+
+ pub fn focus_window_up_or_bottom(&mut self) {
+ if !self.focus_up() {
+ self.focus_window_bottom();
+ }
+ }
+
pub fn move_left(&mut self) -> bool {
if self.floating_is_active.get() {
self.floating.move_left();