feat: add scrolling with configurable scrolloff
This commit is contained in:
parent
a144262f8c
commit
87c9a6f641
2 changed files with 43 additions and 5 deletions
32
src/IO/UI.py
32
src/IO/UI.py
|
|
@ -12,6 +12,8 @@ if TYPE_CHECKING:
|
|||
class UI:
|
||||
def __init__(self, app: App):
|
||||
self._app = app
|
||||
self._scroll_top = 0
|
||||
self._scroll_path = None
|
||||
|
||||
HEADER_ROWS = 1
|
||||
|
||||
|
|
@ -31,14 +33,38 @@ class UI:
|
|||
|
||||
items = navigator.items
|
||||
available = max(0, height - self.HEADER_ROWS)
|
||||
for index in range(min(available, len(items))):
|
||||
item = items[index]
|
||||
self._render_item(stdscr, item, index, index + self.HEADER_ROWS, width)
|
||||
top = self._update_scroll(navigator, available)
|
||||
for offset, item in enumerate(items[top:top + available]):
|
||||
index = top + offset
|
||||
self._render_item(stdscr, item, index, offset + self.HEADER_ROWS, width)
|
||||
|
||||
prompt = self.app.prompt
|
||||
if prompt:
|
||||
self._render_modal(stdscr, prompt, height, width)
|
||||
|
||||
def _update_scroll(self, navigator, available: int) -> int:
|
||||
current_path = navigator.history[-1]
|
||||
if current_path != self._scroll_path:
|
||||
self._scroll_path = current_path
|
||||
self._scroll_top = 0
|
||||
|
||||
total = len(navigator.items)
|
||||
if available <= 0 or total <= available:
|
||||
self._scroll_top = 0
|
||||
return self._scroll_top
|
||||
|
||||
offset = min(self.app.config.scroll_offset, (available - 1) // 2)
|
||||
pos = navigator.cursor.pos
|
||||
max_top = total - available
|
||||
|
||||
if pos < self._scroll_top + offset:
|
||||
self._scroll_top = pos - offset
|
||||
elif pos > self._scroll_top + available - 1 - offset:
|
||||
self._scroll_top = pos - available + 1 + offset
|
||||
|
||||
self._scroll_top = max(0, min(self._scroll_top, max_top))
|
||||
return self._scroll_top
|
||||
|
||||
MODAL_MAX_WIDTH = 60
|
||||
MODAL_PADDING = 2
|
||||
|
||||
|
|
|
|||
|
|
@ -20,15 +20,17 @@ DEFAULT_KEYBINDS: dict[str, list[str]] = {
|
|||
}
|
||||
|
||||
DEFAULT_CURSOR_RESET = "top"
|
||||
DEFAULT_SCROLL_OFFSET = 4
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, keybinds: dict[str, list[str]], cursor_reset: str,
|
||||
sort_mode: str, theme: Theme) -> None:
|
||||
sort_mode: str, theme: Theme, scroll_offset: int) -> None:
|
||||
self._keybinds = keybinds
|
||||
self._cursor_reset = cursor_reset
|
||||
self._sort_mode = sort_mode
|
||||
self._theme = theme
|
||||
self._scroll_offset = scroll_offset
|
||||
self._key_to_action: dict[str, str] = {
|
||||
key: action
|
||||
for action, keys in keybinds.items()
|
||||
|
|
@ -57,7 +59,13 @@ class Config:
|
|||
f"falling back to {Sort.DEFAULT_MODE!r}", file=sys.stderr)
|
||||
sort_mode = Sort.DEFAULT_MODE
|
||||
theme = Theme.load(data)
|
||||
return cls(keybinds, cursor_reset, sort_mode, theme)
|
||||
scroll_offset = data.get("scroll", {}).get("offset",
|
||||
DEFAULT_SCROLL_OFFSET)
|
||||
if not isinstance(scroll_offset, int) or scroll_offset < 0:
|
||||
print(f"pyexplorer: invalid scroll offset {scroll_offset!r}, "
|
||||
f"falling back to {DEFAULT_SCROLL_OFFSET}", file=sys.stderr)
|
||||
scroll_offset = DEFAULT_SCROLL_OFFSET
|
||||
return cls(keybinds, cursor_reset, sort_mode, theme, scroll_offset)
|
||||
|
||||
@staticmethod
|
||||
def _default_path() -> Path:
|
||||
|
|
@ -81,3 +89,7 @@ class Config:
|
|||
@property
|
||||
def theme(self) -> Theme:
|
||||
return self._theme
|
||||
|
||||
@property
|
||||
def scroll_offset(self) -> int:
|
||||
return self._scroll_offset
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue