refactor(tui): trim the folder column change to what it needs
The path splitter is a one-line stdlib call with a single caller, so it lives beside that caller as a private fn, without its own tests. The pane tests assert the two columns in one screen fragment each, the end-offset test keeps only the two cases the existing name tests do not cover, and the key-handling test that repeated one of them goes. The website mock splits on the backslash the same way its Info pane already does.
This commit is contained in:
@@ -98,8 +98,8 @@ 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.
|
||||
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
|
||||
|
||||
|
||||
@@ -1274,18 +1274,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scrolling_runs_to_the_end_of_the_folder_when_it_is_the_longer() {
|
||||
let mut tui = results_tui(3);
|
||||
tui.state.results_items[1].filename =
|
||||
format!("{}\\short.mp3", "f".repeat(20));
|
||||
tui.state.results_table_state.select(Some(1));
|
||||
press(&mut tui, KeyCode::Char('$'));
|
||||
assert_eq!(tui.state.results_name_offset, 14);
|
||||
press(&mut tui, KeyCode::Char('l'));
|
||||
assert_eq!(tui.state.results_name_offset, 14, "stops at the end");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn picking_another_search_starts_its_results_unscrolled() {
|
||||
let mut tui = results_tui(3);
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::models::FileDisplayData;
|
||||
use crate::ui::{
|
||||
BYTES_PER_MB, HIGHLIGHT_SYMBOL, body_style, dimmed_style, format_bytes,
|
||||
header_style, info_style, pane_block, pane_title, row_highlight_style,
|
||||
split_path, success_style, warning_style,
|
||||
success_style, warning_style,
|
||||
};
|
||||
use ratatui::{
|
||||
Frame,
|
||||
@@ -47,6 +47,12 @@ pub struct ResultsPaneParams<'a> {
|
||||
const NAME_COLUMN: usize = 1;
|
||||
const FOLDER_COLUMN: usize = 2;
|
||||
|
||||
/// A shared path as `(folder, name)`. Peers send backslashes, but a slash
|
||||
/// counts too.
|
||||
fn split_path(path: &str) -> (&str, &str) {
|
||||
path.rsplit_once(['\\', '/']).unwrap_or(("", path))
|
||||
}
|
||||
|
||||
/// The offset that brings the end of a result's longer column into view.
|
||||
/// One offset scrolls the name and the folder together, so it runs until
|
||||
/// both tails are showing.
|
||||
@@ -285,55 +291,30 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_path_shows_its_name_and_folder_in_separate_columns() {
|
||||
fn a_path_shows_its_name_before_its_folder() {
|
||||
let items = [file("@@abc\\Music\\Album\\01.flac"), file("cover.jpg")];
|
||||
let screen = render_rows(&items, 0);
|
||||
let header = screen.lines().nth(1).expect("header");
|
||||
let name = header.find("Filename").expect("name header");
|
||||
let folder = header.find("Folder").expect("folder header");
|
||||
assert!(name < folder, "{header}");
|
||||
|
||||
let row = screen.lines().nth(2).expect("first row");
|
||||
assert!(row.contains("01.flac "), "{row}");
|
||||
assert!(!row.contains("Album\\01.flac"), "the name alone: {row}");
|
||||
assert!(row.contains("@@abc\\M"), "{row}");
|
||||
assert!(
|
||||
row.find("01.flac") < row.find("@@abc"),
|
||||
"the name before the folder: {row}"
|
||||
);
|
||||
|
||||
let row = screen.lines().nth(3).expect("second row");
|
||||
assert!(row.contains("cover.jpg"), "{row}");
|
||||
assert!(screen.contains("01.flac @@abc\\M"), "{screen}");
|
||||
assert!(screen.contains("cover.jpg "), "{screen}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_offset_scrolls_the_name_and_the_folder_together() {
|
||||
let items = [file("@@abc\\Music\\Album\\abcdefghijklmnopqrstuvwxyz")];
|
||||
let screen = render_rows(&items, 4);
|
||||
assert!(screen.contains("…efghijklmn"), "{screen}");
|
||||
assert!(screen.contains("…c\\Musi"), "{screen}");
|
||||
assert!(screen.contains("…efghijklmn …c\\Musi"), "{screen}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_end_offset_is_the_longer_columns() {
|
||||
use super::name_end_offset;
|
||||
use ratatui::layout::Rect;
|
||||
// 88 cells wide: 11 for the name, 7 for the folder.
|
||||
let area = Rect::new(0, 0, 88, 6);
|
||||
assert_eq!(name_end_offset("short.mp3", area), 0);
|
||||
assert_eq!(name_end_offset(&"x".repeat(40), area), 30);
|
||||
let area = ratatui::layout::Rect::new(0, 0, 88, 6);
|
||||
let folder = "f".repeat(20);
|
||||
assert_eq!(super::name_end_offset(&format!("{folder}\\a"), area), 14);
|
||||
let name = "x".repeat(40);
|
||||
assert_eq!(
|
||||
name_end_offset(&format!("{}\\short.mp3", "f".repeat(20)), area),
|
||||
14,
|
||||
"the folder overflows its narrower column"
|
||||
);
|
||||
assert_eq!(
|
||||
name_end_offset(
|
||||
&format!("{}\\{}", "f".repeat(20), "x".repeat(40)),
|
||||
area
|
||||
),
|
||||
30,
|
||||
"whichever runs further"
|
||||
super::name_end_offset(&format!("{folder}\\{name}"), area),
|
||||
30
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,6 @@ import { TuiPane } from './chrome'
|
||||
import type { ResultRow, SearchRow, TransferRow, TransferStatus } from './data'
|
||||
|
||||
const RESULT_COLS = '30px 3fr 2fr 74px 92px 78px 74px 42px'
|
||||
|
||||
/** A shared path split at its last separator, as the TUI's two columns show it. */
|
||||
const splitPath = (path: string): { folder: string; name: string } => {
|
||||
const at = Math.max(path.lastIndexOf('\\'), path.lastIndexOf('/'))
|
||||
return at < 0
|
||||
? { folder: '', name: path }
|
||||
: { folder: path.slice(0, at), name: path.slice(at + 1) }
|
||||
}
|
||||
const TRANSFER_COLS = '48px 1fr 96px 168px 74px'
|
||||
|
||||
interface SearchesPaneProps {
|
||||
@@ -152,7 +144,7 @@ export const ResultsPane = ({
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{rows.map((row, i) => {
|
||||
const { glyph, tone } = resultGlyph(row)
|
||||
const { folder, name } = splitPath(row.name)
|
||||
const parts = row.name.split('\\')
|
||||
const isSelected = selected === i
|
||||
return (
|
||||
<button
|
||||
@@ -177,10 +169,10 @@ export const ResultsPane = ({
|
||||
isSelected ? 'text-[var(--color-paper)]' : 'text-[var(--color-paper-dim)]',
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
{parts.at(-1)}
|
||||
</span>
|
||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap text-[var(--color-dust)]">
|
||||
{folder}
|
||||
{parts.slice(0, -1).join('\\')}
|
||||
</span>
|
||||
<span className="text-right text-[var(--color-tape)]">{row.size}</span>
|
||||
<span className="text-[var(--color-signal)]">{row.user}</span>
|
||||
|
||||
Reference in New Issue
Block a user