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:
|
class UI:
|
||||||
def __init__(self, app: App):
|
def __init__(self, app: App):
|
||||||
self._app = app
|
self._app = app
|
||||||
|
self._scroll_top = 0
|
||||||
|
self._scroll_path = None
|
||||||
|
|
||||||
HEADER_ROWS = 1
|
HEADER_ROWS = 1
|
||||||
|
|
||||||
|
|
@ -31,14 +33,38 @@ class UI:
|
||||||
|
|
||||||
items = navigator.items
|
items = navigator.items
|
||||||
available = max(0, height - self.HEADER_ROWS)
|
available = max(0, height - self.HEADER_ROWS)
|
||||||
for index in range(min(available, len(items))):
|
top = self._update_scroll(navigator, available)
|
||||||
item = items[index]
|
for offset, item in enumerate(items[top:top + available]):
|
||||||
self._render_item(stdscr, item, index, index + self.HEADER_ROWS, width)
|
index = top + offset
|
||||||
|
self._render_item(stdscr, item, index, offset + self.HEADER_ROWS, width)
|
||||||
|
|
||||||
prompt = self.app.prompt
|
prompt = self.app.prompt
|
||||||
if prompt:
|
if prompt:
|
||||||
self._render_modal(stdscr, prompt, height, width)
|
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_MAX_WIDTH = 60
|
||||||
MODAL_PADDING = 2
|
MODAL_PADDING = 2
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,17 @@ DEFAULT_KEYBINDS: dict[str, list[str]] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_CURSOR_RESET = "top"
|
DEFAULT_CURSOR_RESET = "top"
|
||||||
|
DEFAULT_SCROLL_OFFSET = 4
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, keybinds: dict[str, list[str]], cursor_reset: str,
|
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._keybinds = keybinds
|
||||||
self._cursor_reset = cursor_reset
|
self._cursor_reset = cursor_reset
|
||||||
self._sort_mode = sort_mode
|
self._sort_mode = sort_mode
|
||||||
self._theme = theme
|
self._theme = theme
|
||||||
|
self._scroll_offset = scroll_offset
|
||||||
self._key_to_action: dict[str, str] = {
|
self._key_to_action: dict[str, str] = {
|
||||||
key: action
|
key: action
|
||||||
for action, keys in keybinds.items()
|
for action, keys in keybinds.items()
|
||||||
|
|
@ -57,7 +59,13 @@ class Config:
|
||||||
f"falling back to {Sort.DEFAULT_MODE!r}", file=sys.stderr)
|
f"falling back to {Sort.DEFAULT_MODE!r}", file=sys.stderr)
|
||||||
sort_mode = Sort.DEFAULT_MODE
|
sort_mode = Sort.DEFAULT_MODE
|
||||||
theme = Theme.load(data)
|
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
|
@staticmethod
|
||||||
def _default_path() -> Path:
|
def _default_path() -> Path:
|
||||||
|
|
@ -81,3 +89,7 @@ class Config:
|
||||||
@property
|
@property
|
||||||
def theme(self) -> Theme:
|
def theme(self) -> Theme:
|
||||||
return self._theme
|
return self._theme
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scroll_offset(self) -> int:
|
||||||
|
return self._scroll_offset
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue