PyExplorer/src/IO/Input.py

200 lines
6 KiB
Python

from __future__ import annotations
import re
from typing import TYPE_CHECKING
from ..items.Folder import Folder
from ..core.Prompt import Prompt
if TYPE_CHECKING:
from .App import App
BACKSPACE_KEYS = ("KEY_BACKSPACE", "\x7f", "\x08")
ENTER_KEYS = ("\n", "KEY_ENTER")
ESCAPE_KEY = "\x1b"
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,
"sort_direction": self._sort_direction,
"sort_grouping": self._sort_grouping,
"new_folder": self._new_folder,
"delete_folder": self._delete_folder,
"new_file": self._new_file,
"search": self._search,
"search_next": self._search_next,
"search_previous": self._search_previous,
}
def handle(self, key: str) -> bool:
if self.app.prompt is not None:
return self._handle_prompt(key)
action = self.app.config.action_for(key)
handler = self._actions.get(action)
return handler() if handler else True
def _handle_prompt(self, key: str) -> bool:
prompt = self.app.prompt
if prompt.kind == "error":
self.app.close_prompt()
prompt.on_submit(None)
return True
if prompt.kind == "confirm":
if key in ("y", "Y"):
self.app.close_prompt()
prompt.on_submit(True)
elif key in ("n", "N", ESCAPE_KEY):
self.app.close_prompt()
return True
if key in ENTER_KEYS:
self.app.close_prompt()
prompt.on_submit(prompt.buffer)
elif key == ESCAPE_KEY:
self.app.close_prompt()
elif key in BACKSPACE_KEYS:
prompt.buffer = prompt.buffer[:-1]
elif len(key) == 1 and key.isprintable():
prompt.buffer += key
return True
def _quit(self) -> bool:
return False
def _down(self) -> bool:
navigator = self.app.navigator
navigator.cursor.down(len(navigator.items))
return True
def _up(self) -> bool:
self.app.navigator.cursor.up()
return True
def _maybe_show_error(self) -> None:
error = self.app.navigator.error
if error:
self.app.open_prompt(Prompt.error(error))
def _enter(self) -> bool:
navigator = self.app.navigator
items = navigator.items
if items:
navigator.enter(items[navigator.cursor.pos])
self._maybe_show_error()
return True
def _back(self) -> bool:
self.app.navigator.back()
self._maybe_show_error()
return True
def _parent(self) -> bool:
self.app.navigator.up()
self._maybe_show_error()
return True
def _sort_direction(self) -> bool:
self.app.navigator.cycle_sort_direction()
return True
def _sort_grouping(self) -> bool:
self.app.navigator.cycle_sort_grouping()
return True
def _new_folder(self) -> bool:
navigator = self.app.navigator
def on_submit(name: str) -> None:
if not name:
return
try:
self.app.file_actions.create_folder(navigator.current.path,
name)
except OSError as e:
self.app.open_prompt(Prompt.error(str(e)))
self.app.open_prompt(Prompt("New folder name: ", "text", on_submit))
return True
def _delete_folder(self) -> bool:
navigator = self.app.navigator
items = navigator.items
if not items:
return True
item = items[navigator.cursor.pos]
if not isinstance(item, Folder):
return True
def on_confirm(confirmed: bool) -> None:
if not confirmed:
return
try:
self.app.file_actions.delete_folder(item.path)
except OSError as e:
self.app.open_prompt(Prompt.error(str(e)))
self.app.open_prompt(
Prompt(f"Delete '{item.name}'? (y/n) ", "confirm", on_confirm))
return True
def _new_file(self) -> bool:
navigator = self.app.navigator
def on_submit(name: str) -> None:
if not name:
return
try:
self.app.file_actions.create_file(navigator.current.path,
name)
except OSError as e:
self.app.open_prompt(Prompt.error(str(e)))
self.app.open_prompt(Prompt("New file name: ", "text", on_submit))
return True
def _search(self) -> bool:
navigator = self.app.navigator
def on_submit(pattern: str) -> None:
if not pattern:
return
try:
regex = re.compile(pattern)
except re.error as e:
self.app.open_prompt(Prompt.error(f"Invalid regex: {e}"))
return
if not navigator.search(regex):
self.app.open_prompt(Prompt.error(f"No match: {pattern}"))
self.app.open_prompt(Prompt("Search: ", "text", on_submit))
return True
def _search_next(self) -> bool:
navigator = self.app.navigator
if navigator.last_pattern is None:
return True
if not navigator.search_next():
self.app.open_prompt(Prompt.error(f"No match: {navigator.last_pattern}"))
return True
def _search_previous(self) -> bool:
navigator = self.app.navigator
if navigator.last_pattern is None:
return True
if not navigator.search_previous():
self.app.open_prompt(Prompt.error(f"No match: {navigator.last_pattern}"))
return True
@property
def app(self) -> App:
return self._app