From 299ece963223de547cbcd6890aacb6e7a45f8a84 Mon Sep 17 00:00:00 2001 From: Justin Flannery Date: Sat, 22 Jul 2023 19:28:31 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20vim=20keybindings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #18 --- browsr/_base.py | 40 +++++++++++++++++++++++++++++- browsr/browsr.py | 26 +++++++++++++------ browsr/universal_directory_tree.py | 7 ++++++ 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/browsr/_base.py b/browsr/_base.py index f47c2d0..6f32cd5 100644 --- a/browsr/_base.py +++ b/browsr/_base.py @@ -19,7 +19,8 @@ from rich.text import Text from textual import on from textual.app import App, ComposeResult -from textual.containers import Container +from textual.binding import Binding +from textual.containers import Container, VerticalScroll from textual.reactive import reactive, var from textual.widget import Widget from textual.widgets import Button, DataTable, Static @@ -224,3 +225,40 @@ def handle_download_selection(self, message: Button.Pressed) -> None: if message.button.variant == "success": self.app.download_selected_file() # type: ignore[attr-defined] self.app.table_view.display = self.app.hidden_table_view # type: ignore[attr-defined] + + +vim_scroll_bindings = [ + Binding(key="k", action="scroll_up", description="Scroll Up", show=False), + Binding(key="j", action="scroll_down", description="Scroll Down", show=False), + Binding(key="h", action="scroll_left", description="Scroll Left", show=False), + Binding(key="l", action="scroll_right", description="Scroll Right", show=False), +] + +vim_cursor_bindings = [ + Binding(key="k", action="cursor_up", description="Cursor Up", show=False), + Binding(key="j", action="cursor_down", description="Cursor Down", show=False), + Binding(key="h", action="cursor_left", description="Cursor Left", show=False), + Binding(key="l", action="cursor_right", description="Cursor Right", show=False), +] + + +class VimScroll(VerticalScroll): + """ + A VerticalScroll with Vim Keybindings + """ + + BINDINGS = [ + *VerticalScroll.BINDINGS, + *vim_scroll_bindings, + ] + + +class VimDataTable(DataTable[str]): + """ + A DataTable with Vim Keybindings + """ + + BINDINGS = [ + *DataTable.BINDINGS, + *vim_cursor_bindings, + ] diff --git a/browsr/browsr.py b/browsr/browsr.py index 2b6a731..58b094d 100644 --- a/browsr/browsr.py +++ b/browsr/browsr.py @@ -21,7 +21,7 @@ from rich_pixels import Pixels from textual import on from textual.binding import Binding -from textual.containers import Container, Horizontal, VerticalScroll +from textual.containers import Container, Horizontal from textual.events import Mount from textual.reactive import var from textual.widget import Widget @@ -34,6 +34,8 @@ CurrentFileInfoBar, FileSizeError, TextualAppContext, + VimDataTable, + VimScroll, ) from browsr._config import favorite_themes, image_file_extensions from browsr._utils import ( @@ -58,11 +60,11 @@ class Browsr(BrowsrTextualApp): TITLE = __application__ CSS_PATH = "browsr.css" BINDINGS = [ - Binding("q", "quit", "Quit"), - Binding("f", "toggle_files", "Toggle Files"), - Binding("t", "theme", "Toggle Theme"), - Binding("n", "linenos", "Toggle Line Numbers"), - Binding("d", "toggle_dark", "Toggle Dark Mode"), + Binding(key="q", action="quit", description="Quit"), + Binding(key="f", action="toggle_files", description="Toggle Files"), + Binding(key="t", action="theme", description="Toggle Theme"), + Binding(key="n", action="linenos", description="Toggle Line Numbers"), + Binding(key="d", action="toggle_dark", description="Toggle Dark Mode"), ] show_tree = var(True) @@ -98,8 +100,8 @@ def compose(self) -> Iterable[Widget]: self.header = Header() yield self.header self.directory_tree = BrowsrDirectoryTree(str(file_path), id="tree-view") - self.code_view = VerticalScroll(Static(id="code", expand=True), id="code-view") - self.table_view: DataTable[str] = DataTable( + self.code_view = VimScroll(Static(id="code", expand=True), id="code-view") + self.table_view: DataTable[str] = VimDataTable( zebra_stripes=True, show_header=True, show_cursor=True, id="table-view" ) self.table_view.display = False @@ -264,11 +266,15 @@ def render_code_page( if isinstance(element, DataTable): self.code_view.display = False self.table_view.display = True + if self.code_view.has_focus: + self.table_view.focus() if scroll_home is True: self.query_one(DataTable).scroll_home(animate=False) elif element is not None: self.table_view.display = False self.code_view.display = True + if self.table_view.has_focus: + self.code_view.focus() code_view.update(element) if scroll_home is True: self.query_one("#code-view").scroll_home(animate=False) @@ -282,6 +288,10 @@ def start_up_app(self) -> None: if self.selected_file_path is not None: self.show_tree = self.force_show_tree self.render_code_page(file_path=self.selected_file_path) + if self.show_tree is False and self.code_view.display is True: + self.code_view.focus() + elif self.show_tree is False and self.table_view.display is True: + self.table_view.focus() else: self.show_tree = True self.render_code_page( diff --git a/browsr/universal_directory_tree.py b/browsr/universal_directory_tree.py index f4bb29a..741a594 100644 --- a/browsr/universal_directory_tree.py +++ b/browsr/universal_directory_tree.py @@ -11,12 +11,19 @@ from textual_universal_directorytree import UniversalDirectoryTree from upath import UPath as Path +from browsr._base import vim_cursor_bindings + class BrowsrDirectoryTree(UniversalDirectoryTree): """ A DirectoryTree that can handle any filesystem. """ + BINDINGS = [ + *UniversalDirectoryTree.BINDINGS, + *vim_cursor_bindings, + ] + @classmethod def _handle_top_level_bucket(cls, dir_path: Path) -> Optional[Iterable[Path]]: """