feat: add TOML config for keybinds and cursor reset strategy
This commit is contained in:
parent
abd3e2e98d
commit
c0ff8ae70b
5 changed files with 117 additions and 18 deletions
|
|
@ -8,23 +8,45 @@ if TYPE_CHECKING:
|
|||
class Input:
|
||||
def __init__(self, app: App) -> None:
|
||||
self._app = app
|
||||
self._actions = {
|
||||
"quit": self._quit,
|
||||
"down": self._down,
|
||||
"up": self._up,
|
||||
"enter": self._enter,
|
||||
"back": self._back,
|
||||
"parent": self._parent,
|
||||
}
|
||||
|
||||
def handle(self, key: str) -> bool:
|
||||
action = self.app.config.action_for(key)
|
||||
handler = self._actions.get(action)
|
||||
return handler() if handler else True
|
||||
|
||||
def _quit(self) -> bool:
|
||||
return False
|
||||
|
||||
def _down(self) -> bool:
|
||||
navigator = self.app.navigator
|
||||
if 'q' == key:
|
||||
return False
|
||||
elif key in ('j', 'KEY_DOWN'):
|
||||
navigator.cursor.down(len(navigator.items))
|
||||
elif key in ('k', 'KEY_UP'):
|
||||
navigator.cursor.up()
|
||||
elif key in ('l', 'KEY_RIGHT', '\n', 'KEY_ENTER'):
|
||||
items = navigator.items
|
||||
if items:
|
||||
navigator.enter(items[navigator.cursor.pos])
|
||||
elif key in ('h', 'KEY_LEFT', 'KEY_BACKSPACE', '\x7f', '\x08'):
|
||||
navigator.back()
|
||||
elif key == '-':
|
||||
navigator.up()
|
||||
navigator.cursor.down(len(navigator.items))
|
||||
return True
|
||||
|
||||
def _up(self) -> bool:
|
||||
self.app.navigator.cursor.up()
|
||||
return True
|
||||
|
||||
def _enter(self) -> bool:
|
||||
navigator = self.app.navigator
|
||||
items = navigator.items
|
||||
if items:
|
||||
navigator.enter(items[navigator.cursor.pos])
|
||||
return True
|
||||
|
||||
def _back(self) -> bool:
|
||||
self.app.navigator.back()
|
||||
return True
|
||||
|
||||
def _parent(self) -> bool:
|
||||
self.app.navigator.up()
|
||||
return True
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from pathlib import Path
|
||||
from ..IO.UI import UI
|
||||
from ..IO.Input import Input
|
||||
from .Config import Config
|
||||
from .Navigator import Navigator
|
||||
import curses
|
||||
|
||||
|
|
@ -13,7 +14,8 @@ class App():
|
|||
"""
|
||||
|
||||
def __init__(self, path: Path) -> None:
|
||||
self._navigator = Navigator(path)
|
||||
self._config = Config.load()
|
||||
self._navigator = Navigator(path, self._config)
|
||||
self._UI = UI(self)
|
||||
self._input = Input(self)
|
||||
|
||||
|
|
@ -38,6 +40,10 @@ class App():
|
|||
def run(self) -> None:
|
||||
curses.wrapper(self._run)
|
||||
|
||||
@property
|
||||
def config(self) -> Config:
|
||||
return self._config
|
||||
|
||||
@property
|
||||
def navigator(self) -> Navigator:
|
||||
return self._navigator
|
||||
|
|
|
|||
60
src/core/Config.py
Normal file
60
src/core/Config.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
from __future__ import annotations
|
||||
import os
|
||||
import sys
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
DEFAULT_KEYBINDS: dict[str, list[str]] = {
|
||||
"quit": ["q"],
|
||||
"down": ["j", "KEY_DOWN"],
|
||||
"up": ["k", "KEY_UP"],
|
||||
"enter": ["l", "KEY_RIGHT", "\n", "KEY_ENTER"],
|
||||
"back": ["h", "KEY_LEFT", "KEY_BACKSPACE", "\x7f", "\x08"],
|
||||
"parent": ["-"],
|
||||
}
|
||||
|
||||
DEFAULT_CURSOR_RESET = "top"
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, keybinds: dict[str, list[str]], cursor_reset: str) -> None:
|
||||
self._keybinds = keybinds
|
||||
self._cursor_reset = cursor_reset
|
||||
self._key_to_action: dict[str, str] = {
|
||||
key: action
|
||||
for action, keys in keybinds.items()
|
||||
for key in keys
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def load(cls, path: Path | None = None) -> Config:
|
||||
path = path or cls._default_path()
|
||||
data: dict = {}
|
||||
if path.is_file():
|
||||
try:
|
||||
with path.open("rb") as f:
|
||||
data = tomllib.load(f)
|
||||
except tomllib.TOMLDecodeError as e:
|
||||
print(f"pyexplorer: ignoring invalid config {path}: {e}",
|
||||
file=sys.stderr)
|
||||
data = {}
|
||||
|
||||
keybinds = dict(DEFAULT_KEYBINDS)
|
||||
keybinds.update(data.get("keybinds", {}))
|
||||
cursor_reset = data.get("cursor", {}).get("reset", DEFAULT_CURSOR_RESET)
|
||||
return cls(keybinds, cursor_reset)
|
||||
|
||||
@staticmethod
|
||||
def _default_path() -> Path:
|
||||
base = os.environ.get("XDG_CONFIG_HOME") or str(Path.home() / ".config")
|
||||
return Path(base) / "pyexplorer" / "config.toml"
|
||||
|
||||
def keys_for(self, action: str) -> list[str]:
|
||||
return self._keybinds.get(action, [])
|
||||
|
||||
def action_for(self, key: str) -> str | None:
|
||||
return self._key_to_action.get(key)
|
||||
|
||||
@property
|
||||
def cursor_reset(self) -> str:
|
||||
return self._cursor_reset
|
||||
|
|
@ -6,6 +6,9 @@ class Cursor:
|
|||
def resete(self) -> None:
|
||||
self._pos = 0
|
||||
|
||||
def clamp(self, count: int) -> None:
|
||||
self._pos = min(self._pos, max(count - 1, 0))
|
||||
|
||||
def up(self) -> None:
|
||||
self._pos -= 1 if self.pos >= 1 else 0
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ from pathlib import Path
|
|||
from ..items.AItems import AItem
|
||||
from ..items.Folder import Folder
|
||||
from ..items.Symlink import Symlink
|
||||
from .Config import Config
|
||||
from .Cursor import Cursor
|
||||
|
||||
|
||||
class Navigator:
|
||||
|
||||
def __init__(self, path: Path) -> None:
|
||||
def __init__(self, path: Path, config: Config) -> None:
|
||||
self._config = config
|
||||
self._history = [path]
|
||||
self._cursor = Cursor()
|
||||
self._current: Folder | None = None
|
||||
|
|
@ -29,10 +31,16 @@ class Navigator:
|
|||
def refresh(self) -> None:
|
||||
self._load()
|
||||
|
||||
def _reset_cursor(self) -> None:
|
||||
if self._config.cursor_reset == "keep":
|
||||
self._cursor.clamp(len(self._items))
|
||||
else:
|
||||
self._cursor.resete()
|
||||
|
||||
def _navigate_to(self, path: Path) -> None:
|
||||
self._history.append(path)
|
||||
self._load()
|
||||
self._cursor.resete()
|
||||
self._reset_cursor()
|
||||
|
||||
def enter(self, item: AItem) -> bool:
|
||||
if not item.is_navigable():
|
||||
|
|
@ -53,7 +61,7 @@ class Navigator:
|
|||
return False
|
||||
self._history.pop()
|
||||
self._load()
|
||||
self._cursor.resete()
|
||||
self._reset_cursor()
|
||||
return True
|
||||
|
||||
@property
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue