feat: add support for jumping to specific item

This also cleans up some type checker issues
This commit is contained in:
2026-08-21 17:08:29 +03:00
parent 454d4a9003
commit cf6e91017a
+63 -3
View File
@@ -9,13 +9,15 @@ Here's an example usage:
import pathlib
import random
from typing import cast, override
from typing import ClassVar, cast, final, override
import typed_argparse as tap
from textual import work
from textual.app import App, ComposeResult
from textual.binding import BindingType
from textual.widgets import Footer, Header, Label
from textual.containers import Grid
from textual.screen import ModalScreen
from textual.widgets import Footer, Header, Input, Label
from textual.worker import Worker, WorkerState
from random_file_picker.core import IMG_EXTENSIONS, collect_files, move_file_to_move_folder, trash_file, upload_file
@@ -38,12 +40,50 @@ class Args(tap.TypedArgs):
blacklist: list[str] | None = tap.arg("--blacklist", nargs="*", help="List of glob patterns to blacklist files")
whitelist: list[str] | None = tap.arg("--whitelist", nargs="*", help="List of glob patterns to whitelist files")
@final
class JumpModal(ModalScreen[int | None]):
"""Modal screen to prompt the user for an item index to skip to."""
# css in the terminal before gta6
DEFAULT_CSS = """
JumpModal {
align: center middle;
}
#dialog {
grid-size: 1;
padding: 1 2;
width: 40;
height: 11;
border: thick $background 80%;
background: $surface;
}
"""
@override
def compose(self) -> ComposeResult:
yield Grid(
Label("Enter item index to jump to:"),
Input(placeholder="e.g. 5", id="index-input", type="integer"),
id="dialog",
)
def on_input_submitted(self, event: Input.Submitted) -> None:
"""Handle the submission of the input field, dismissing the modal with the entered index."""
val = event.value.strip()
if val.isdigit():
_ = self.dismiss(int(val))
return
_ = self.dismiss(None)
class MenuApp(App[None]):
# This defines the keys shown in the footer
BINDINGS: list[BindingType] = [
BINDINGS: ClassVar[list[BindingType]] = [
("left", "prev_file", "Previous File"),
("right", "next_file", "Next File"),
("r", "reopen_file", "Reopen"),
("colon", "jump_to_item", "Jump to Item"),
("g", "jump_to_item", "Jump to Item"),
("u", "upload", "Upload"),
("d", "delete", "Delete"),
("m", "move_to_folder", "Move File"),
@@ -53,6 +93,7 @@ class MenuApp(App[None]):
def __init__(
self,
*,
directory: str = ".",
file_limit: int = 10,
recurse: bool = False,
@@ -60,6 +101,7 @@ class MenuApp(App[None]):
whitelist: list[str] | None = None,
use_geeqie: bool = False,
use_trash: bool = True,
no_random: bool = False,
):
super().__init__()
self.directory: pathlib.Path = pathlib.Path(directory)
@@ -69,6 +111,7 @@ class MenuApp(App[None]):
self.whitelist_patterns: list[str] = whitelist or []
self.use_geeqie: bool = use_geeqie
self.use_trash: bool = use_trash
self.no_random: bool = no_random
self.usable_files: list[pathlib.Path] = []
self.current_index: int = 0
self.current_file: pathlib.Path | None = None
@@ -82,6 +125,7 @@ class MenuApp(App[None]):
yield Footer()
def on_mount(self) -> None:
"""On application mount, start gathering files in the background."""
self.start_gathering_files()
@override
@@ -307,6 +351,22 @@ class MenuApp(App[None]):
self.open_file()
self.query_one("#status", Label).update("🔄 Reopened file")
def action_jump_to_item(self) -> None:
"""Prompt for item number and jump to it."""
if not self.usable_files:
return
def check_jump(target_1based: int | None) -> None:
if target_1based is None:
return
if 1 <= target_1based <= len(self.usable_files):
self.current_index = target_1based - 1
self.display_current_file()
else:
self.query_one("#status", Label).update(f"❌ Index out of range (1 - {len(self.usable_files)})")
_ = self.push_screen(JumpModal(), check_jump)
def main_with_tap(args: Args) -> None:
if args.use_geeqie and not GEEQIE_AVAILABLE: