From 8deac25f0066d860a46beeed789349e46f6ed271 Mon Sep 17 00:00:00 2001 From: Joakim Date: Mon, 12 Oct 2026 22:07:53 +0200 Subject: [PATCH] colors, ../ och -a --- CHANGELOG.md | 5 +++++ README.md | 6 ++++++ tui/tui.py | 23 +++++++++++++++++------ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 710ac2a..e877e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,3 +23,8 @@ - Navigation is limited to the directory containing the applicable `.tui` file. - Added `ls --color`-style colors for directories, symlinks, executables, archives and common image files. - Marked paths remain visible as marked when returning to their directory. + +### v2 – cursor and block selection update +- Cursor can now move to the empty line immediately below the last object. +- `Ctrl+K K` now behaves like joe: the block end is the row immediately above the cursor. +- On the bottom empty line, `Ctrl+K K` therefore selects through the last object. diff --git a/README.md b/README.md index 778d95d..2bf745e 100644 --- a/README.md +++ b/README.md @@ -227,3 +227,9 @@ tui ``` The example configuration is in `tui/example.tui`. + +### Cursor and block selection + +There is an empty cursor position immediately below the last object in a directory. This makes range selection behave like a text editor. + +`Ctrl+K K` follows joe-style semantics: the end of the selection is the row immediately above the cursor. Thus, when the cursor is on the empty line below the final object, `Ctrl+K K` includes the final object. diff --git a/tui/tui.py b/tui/tui.py index 5c865d0..36a59bb 100755 --- a/tui/tui.py +++ b/tui/tui.py @@ -179,7 +179,7 @@ class FileBrowser: self.entries = entries # Cursor 0 is always the synthetic parent entry. - self.cursor = min(self.cursor, len(self.entries)) + self.cursor = min(self.cursor, len(self.entries) + 1) # Marks are global absolute paths, so they survive directory changes. # Drop marks only for objects that no longer exist. @@ -237,15 +237,20 @@ class FileBrowser: if not self.entries: return - if self.cursor == 0: + # Like joe's Ctrl-K K, the end of the block is the line immediately + # above the cursor. The bottom empty line therefore selects the last + # object in the directory, while standing on an object selects the + # object immediately above it. + end = self.cursor - 2 + if end < 0: return # K sets/replaces only the end endpoint. The start endpoint is kept. - self.selection_end = self.current_entry_index() + self.selection_end = end self._update_selection() def move(self, delta): - self.cursor = max(0, min(len(self.entries), self.cursor + delta)) + self.cursor = max(0, min(len(self.entries) + 1, self.cursor + delta)) def enter(self): if self.cursor == 0: @@ -421,7 +426,7 @@ def draw(stdscr, browser, status=""): for row in range(visible): index = first + row - if index > len(browser.entries): + if index > len(browser.entries) + 1: break if index == 0: @@ -429,13 +434,19 @@ def draw(stdscr, browser, status=""): text = "../" if entry is not None else "./" attrs = entry_color(entry) | curses.A_BOLD is_marked = False - else: + elif index <= len(browser.entries): entry = browser.entries[index - 1] is_marked = entry.resolve() in browser.marked attrs = entry_color(entry) if is_marked: attrs |= curses.A_REVERSE text = entry.name + ("/" if entry.is_dir() else "") + else: + # One empty line after the final object, matching the cursor range. + entry = None + is_marked = False + attrs = curses.A_NORMAL + text = "" if index == browser.cursor: attrs |= curses.A_UNDERLINE