Add focus-window-top/bottom/down-or-top/up-or-bottom actions

This commit is contained in:
Ivan Molodetskikh
2025-02-05 17:18:21 +03:00
parent 900da597e4
commit c41f93a468
9 changed files with 168 additions and 0 deletions
+8
View File
@@ -1342,6 +1342,10 @@ pub enum Action {
FocusWindowUpOrColumnRight,
FocusWindowOrWorkspaceDown,
FocusWindowOrWorkspaceUp,
FocusWindowTop,
FocusWindowBottom,
FocusWindowDownOrTop,
FocusWindowUpOrBottom,
MoveColumnLeft,
MoveColumnRight,
MoveColumnToFirst,
@@ -1521,6 +1525,10 @@ impl From<niri_ipc::Action> for Action {
niri_ipc::Action::FocusWindowUpOrColumnRight {} => Self::FocusWindowUpOrColumnRight,
niri_ipc::Action::FocusWindowOrWorkspaceDown {} => Self::FocusWindowOrWorkspaceDown,
niri_ipc::Action::FocusWindowOrWorkspaceUp {} => Self::FocusWindowOrWorkspaceUp,
niri_ipc::Action::FocusWindowTop {} => Self::FocusWindowTop,
niri_ipc::Action::FocusWindowBottom {} => Self::FocusWindowBottom,
niri_ipc::Action::FocusWindowDownOrTop {} => Self::FocusWindowDownOrTop,
niri_ipc::Action::FocusWindowUpOrBottom {} => Self::FocusWindowUpOrBottom,
niri_ipc::Action::MoveColumnLeft {} => Self::MoveColumnLeft,
niri_ipc::Action::MoveColumnRight {} => Self::MoveColumnRight,
niri_ipc::Action::MoveColumnToFirst {} => Self::MoveColumnToFirst,
+8
View File
@@ -253,6 +253,14 @@ pub enum Action {
FocusWindowOrWorkspaceDown {},
/// Focus the window or the workspace above.
FocusWindowOrWorkspaceUp {},
/// Focus the topmost window.
FocusWindowTop {},
/// Focus the bottommost window.
FocusWindowBottom {},
/// Focus the window below or the topmost window.
FocusWindowDownOrTop {},
/// Focus the window above or the bottommost window.
FocusWindowUpOrBottom {},
/// Move the focused column to the left.
MoveColumnLeft {},
/// Move the focused column to the right.
+28
View File
@@ -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();
+20
View File
@@ -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);
+28
View File
@@ -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;
+16
View File
@@ -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;
+24
View File
@@ -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 {
+8
View File
@@ -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(),
+28
View File
@@ -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();