feat: add support for jumping to specific item
This also cleans up some type checker issues
This commit is contained in:
@@ -9,13 +9,15 @@ Here's an example usage:
|
|||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import random
|
import random
|
||||||
from typing import cast, override
|
from typing import ClassVar, cast, final, override
|
||||||
|
|
||||||
import typed_argparse as tap
|
import typed_argparse as tap
|
||||||
from textual import work
|
from textual import work
|
||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.binding import BindingType
|
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 textual.worker import Worker, WorkerState
|
||||||
|
|
||||||
from random_file_picker.core import IMG_EXTENSIONS, collect_files, move_file_to_move_folder, trash_file, upload_file
|
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")
|
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")
|
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]):
|
class MenuApp(App[None]):
|
||||||
# This defines the keys shown in the footer
|
# This defines the keys shown in the footer
|
||||||
BINDINGS: list[BindingType] = [
|
BINDINGS: ClassVar[list[BindingType]] = [
|
||||||
("left", "prev_file", "Previous File"),
|
("left", "prev_file", "Previous File"),
|
||||||
("right", "next_file", "Next File"),
|
("right", "next_file", "Next File"),
|
||||||
("r", "reopen_file", "Reopen"),
|
("r", "reopen_file", "Reopen"),
|
||||||
|
("colon", "jump_to_item", "Jump to Item"),
|
||||||
|
("g", "jump_to_item", "Jump to Item"),
|
||||||
("u", "upload", "Upload"),
|
("u", "upload", "Upload"),
|
||||||
("d", "delete", "Delete"),
|
("d", "delete", "Delete"),
|
||||||
("m", "move_to_folder", "Move File"),
|
("m", "move_to_folder", "Move File"),
|
||||||
@@ -53,6 +93,7 @@ class MenuApp(App[None]):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
*,
|
||||||
directory: str = ".",
|
directory: str = ".",
|
||||||
file_limit: int = 10,
|
file_limit: int = 10,
|
||||||
recurse: bool = False,
|
recurse: bool = False,
|
||||||
@@ -60,6 +101,7 @@ class MenuApp(App[None]):
|
|||||||
whitelist: list[str] | None = None,
|
whitelist: list[str] | None = None,
|
||||||
use_geeqie: bool = False,
|
use_geeqie: bool = False,
|
||||||
use_trash: bool = True,
|
use_trash: bool = True,
|
||||||
|
no_random: bool = False,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.directory: pathlib.Path = pathlib.Path(directory)
|
self.directory: pathlib.Path = pathlib.Path(directory)
|
||||||
@@ -69,6 +111,7 @@ class MenuApp(App[None]):
|
|||||||
self.whitelist_patterns: list[str] = whitelist or []
|
self.whitelist_patterns: list[str] = whitelist or []
|
||||||
self.use_geeqie: bool = use_geeqie
|
self.use_geeqie: bool = use_geeqie
|
||||||
self.use_trash: bool = use_trash
|
self.use_trash: bool = use_trash
|
||||||
|
self.no_random: bool = no_random
|
||||||
self.usable_files: list[pathlib.Path] = []
|
self.usable_files: list[pathlib.Path] = []
|
||||||
self.current_index: int = 0
|
self.current_index: int = 0
|
||||||
self.current_file: pathlib.Path | None = None
|
self.current_file: pathlib.Path | None = None
|
||||||
@@ -82,6 +125,7 @@ class MenuApp(App[None]):
|
|||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
|
"""On application mount, start gathering files in the background."""
|
||||||
self.start_gathering_files()
|
self.start_gathering_files()
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -307,6 +351,22 @@ class MenuApp(App[None]):
|
|||||||
self.open_file()
|
self.open_file()
|
||||||
self.query_one("#status", Label).update("🔄 Reopened 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:
|
def main_with_tap(args: Args) -> None:
|
||||||
if args.use_geeqie and not GEEQIE_AVAILABLE:
|
if args.use_geeqie and not GEEQIE_AVAILABLE:
|
||||||
|
|||||||
Reference in New Issue
Block a user