From 1fcde0a78b5a55f3b360215323d65b7ec1db5e5e Mon Sep 17 00:00:00 2001 From: Michel de Graaf Date: Tue, 8 Sep 2026 10:30:23 +0200 Subject: [PATCH] feat(tui): W puts the pane layout back Hiding panes with w and zooming with z had no way back short of pressing each hidden pane's number and Esc. W shows every pane and leaves zoom in one go, and the shortcut bar offers it only while there is a layout to reset. --- README.md | 11 ++++++----- soulseek-rs/src/ui/main_tui/input.rs | 6 +++++- soulseek-rs/src/ui/main_tui/mod.rs | 20 ++++++++++++++++++++ soulseek-rs/src/ui/main_tui/render.rs | 15 ++++++++++----- website/src/pages/docs.tsx | 1 + 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7b3bc3c..ad6ba05 100644 --- a/README.md +++ b/README.md @@ -95,11 +95,12 @@ Everything else is a one-shot command that runs headless. Results run across the top, each file name in a column of its own with the folder it came from beside it; Searches, Downloads and Info share the row underneath. `Tab`/`Shift-Tab` or `1`–`3` move the focus between panes, `z` -zooms the focused pane to the whole window, and `w` hides it so the others -widen (its number brings it back). Every list takes `↑`/`↓`, `Home`/`End`, -`PgUp`/`PgDn` and the vim keys, and `h`/`l` or `←`/`→` scroll a long file name, -folder or query sideways, `0` and `$` to either end. `PgUp`/`PgDn` scroll a -chat log back through its history, and `?` shows every key for where you are. +zooms the focused pane to the whole window, `w` hides it so the others widen +(its number brings it back), and `W` puts the whole layout back. Every list +takes `↑`/`↓`, `Home`/`End`, `PgUp`/`PgDn` and the vim keys, and `h`/`l` or +`←`/`→` scroll a long file name, folder or query sideways, `0` and `$` to +either end. `PgUp`/`PgDn` scroll a chat log back through its history, and `?` +shows every key for where you are. ### Daemon mode diff --git a/soulseek-rs/src/ui/main_tui/input.rs b/soulseek-rs/src/ui/main_tui/input.rs index ca15dac..615fb29 100644 --- a/soulseek-rs/src/ui/main_tui/input.rs +++ b/soulseek-rs/src/ui/main_tui/input.rs @@ -1,5 +1,5 @@ use super::MainTui; -use crate::models::{CommandBarMode, FocusedPane, LogView}; +use crate::models::{CommandBarMode, FocusedPane, LogView, PaneLayout}; use crate::ui::page_of; use crate::ui::panes::{ InfoSubject, name_end_offset, query_end_offset, selected_transfer, @@ -100,6 +100,10 @@ impl MainTui { self.state.hide_focused_pane(); return; } + KeyCode::Char('W') => { + self.state.layout = PaneLayout::default(); + return; + } KeyCode::Esc if self.state.layout.zoomed => { self.state.layout.zoomed = false; return; diff --git a/soulseek-rs/src/ui/main_tui/mod.rs b/soulseek-rs/src/ui/main_tui/mod.rs index 49eba3a..9b497af 100644 --- a/soulseek-rs/src/ui/main_tui/mod.rs +++ b/soulseek-rs/src/ui/main_tui/mod.rs @@ -1818,6 +1818,26 @@ mod tests { assert!(screen.contains("[C → clear all]"), "{screen}"); } + #[test] + fn shift_w_brings_every_pane_back_and_leaves_zoom() { + let mut tui = furnished_tui(); + tui.state.focused_pane = FocusedPane::Searches; + assert!(!screen_sized(&mut tui, 300, 40).contains("[W → ")); + press(&mut tui, KeyCode::Char('w')); + press(&mut tui, KeyCode::Char('z')); + assert!(tui.state.layout.zoomed); + assert!(!tui.state.layout.is_visible(FocusedPane::Searches)); + let screen = screen_sized(&mut tui, 300, 40); + assert!(screen.contains("[W → reset layout]"), "{screen}"); + press(&mut tui, KeyCode::Char('W')); + assert!(!tui.state.layout.zoomed); + assert!( + FocusedPane::ALL + .iter() + .all(|p| tui.state.layout.is_visible(*p)) + ); + } + #[test] fn page_keys_work_in_the_transfers_and_searches_lists_too() { let mut tui = furnished_tui(); diff --git a/soulseek-rs/src/ui/main_tui/render.rs b/soulseek-rs/src/ui/main_tui/render.rs index 3f8bf96..840398e 100644 --- a/soulseek-rs/src/ui/main_tui/render.rs +++ b/soulseek-rs/src/ui/main_tui/render.rs @@ -1,5 +1,5 @@ use super::MainTui; -use crate::models::{CommandBarMode, FocusedPane, RoomsView}; +use crate::models::{CommandBarMode, FocusedPane, PaneLayout, RoomsView}; use crate::ui::panes::{ InfoSubject, ResultsPaneParams, render_browse_pane, render_chat_pane, render_download_info_pane, render_downloads_pane, render_results_pane, @@ -29,6 +29,7 @@ const HELP_LEFT: &[(&str, &[(&str, &str)])] = &[ ("z", "zoom the focused pane"), ("w", "hide the focused pane"), ("Esc", "leave zoom"), + ("W", "every pane back, zoom off"), ("click", "focus a pane"), ], ), @@ -497,7 +498,7 @@ impl MainTui { /// The keys every pane shares for moving between and resizing panes, /// ending the bar the same way wherever the focus sits. fn pane_shortcuts(&self) -> Vec<(&'static str, &'static str)> { - vec![ + let mut keys = vec![ ("Tab/1-3", "pane"), ( "z", @@ -508,9 +509,13 @@ impl MainTui { }, ), ("w", "hide"), - ("?", "keys"), - ("q", "quit"), - ] + ]; + // Only once a pane is hidden or zoomed is there a layout to reset. + if self.state.layout != PaneLayout::default() { + keys.push(("W", "reset layout")); + } + keys.extend([("?", "keys"), ("q", "quit")]); + keys } /// Which shortcuts the bar offers, which is purely a question of what is diff --git a/website/src/pages/docs.tsx b/website/src/pages/docs.tsx index 0020e7f..c7f8f89 100644 --- a/website/src/pages/docs.tsx +++ b/website/src/pages/docs.tsx @@ -34,6 +34,7 @@ const PANE_KEYS: readonly (readonly [string, string])[] = [ ['1–3', 'focus a pane, and bring it back if hidden'], ['z', 'zoom the focused pane to the whole window'], ['w', 'hide the focused pane; the others widen'], + ['W', 'every pane back on screen, zoom off'], ['↑ ↓ · j k', 'move a row in any list'], ['Home End · g G', 'first / last row'], ['PgUp PgDn · ^u ^d', 'a page, or half a page; in a chat, back through the log'],