Implement scrolling for cursor tree
This commit is contained in:
parent
a4d33847e9
commit
dd6ad51077
2 changed files with 95 additions and 8 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
# TODO retrieve attributes of closest existing line (by y coordinate)
|
# TODO retrieve attributes of closest existing line (by y coordinate)
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
from typing import Deque, Iterator, List, Optional, Tuple
|
from typing import Any, Deque, Iterator, List, Optional, Set, Tuple
|
||||||
|
|
||||||
from .markup import AT, AttributedText, Attributes
|
from .markup import AT, AttributedText, Attributes
|
||||||
|
|
||||||
|
|
@ -213,3 +213,12 @@ class AttributedLines:
|
||||||
lines = self.render_lines(width, height,
|
lines = self.render_lines(width, height,
|
||||||
horizontal_offset=horizontal_offset)
|
horizontal_offset=horizontal_offset)
|
||||||
return AT("\n").join(lines)
|
return AT("\n").join(lines)
|
||||||
|
|
||||||
|
def all_values(self, attribute: str) -> Set[Any]:
|
||||||
|
values = set()
|
||||||
|
|
||||||
|
for attributes, _ in self._lines:
|
||||||
|
if attribute in attributes:
|
||||||
|
values.add(attributes.get(attribute))
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# TODO move meta spaces rendering to message
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Generic, Optional, Tuple, TypeVar
|
from typing import Generic, Optional, Tuple, TypeVar
|
||||||
|
|
||||||
|
|
@ -287,12 +289,15 @@ class CursorTreeRenderer(Generic[E]):
|
||||||
3. Extend upwards until the top of the screen, if necessary
|
3. Extend upwards until the top of the screen, if necessary
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
delta = 0
|
||||||
|
|
||||||
# Step 1
|
# Step 1
|
||||||
lines = self._render_cursor()
|
lines = self._render_cursor()
|
||||||
# No need to use the anchor offset since we know we're always at the
|
lines.lower_offset = self._absolute_anchor_offset
|
||||||
# bottom of the screen
|
|
||||||
|
if lines.lower_offset < self._height - 1:
|
||||||
|
delta = self._height - 1 - lines.lower_offset
|
||||||
lines.lower_offset = self._height - 1
|
lines.lower_offset = self._height - 1
|
||||||
delta = self._height - 1 - self._absolute_anchor_offset
|
|
||||||
|
|
||||||
# Step 2
|
# Step 2
|
||||||
lowest_root_id = self._supply.lowest_root_id()
|
lowest_root_id = self._supply.lowest_root_id()
|
||||||
|
|
@ -360,6 +365,14 @@ class CursorTreeRenderer(Generic[E]):
|
||||||
|
|
||||||
return self._render_lines_from_anchor(working_id)
|
return self._render_lines_from_anchor(working_id)
|
||||||
|
|
||||||
|
def _render(self) -> int:
|
||||||
|
lines, delta, hit_top = self._render_lines()
|
||||||
|
|
||||||
|
self._lines = lines
|
||||||
|
self._hit_top = hit_top
|
||||||
|
|
||||||
|
return delta
|
||||||
|
|
||||||
# Finally, another public function! :P
|
# Finally, another public function! :P
|
||||||
|
|
||||||
def render(self, width: int, height: int) -> None:
|
def render(self, width: int, height: int) -> None:
|
||||||
|
|
@ -369,10 +382,75 @@ class CursorTreeRenderer(Generic[E]):
|
||||||
self._width = width
|
self._width = width
|
||||||
self._height = height
|
self._height = height
|
||||||
|
|
||||||
lines, delta, hit_top = self._render_lines()
|
self._render()
|
||||||
|
|
||||||
self._lines = lines
|
# Scrolling
|
||||||
self._hit_top = hit_top
|
|
||||||
|
def _closest_to_middle(self) -> Tuple[Optional[Id], int]:
|
||||||
|
"""
|
||||||
|
Finds the element/cursor closest to the middle of the screen, and its
|
||||||
|
on-screen offset.
|
||||||
|
|
||||||
|
Returns None instead of an Id if the cursor is the closest.
|
||||||
|
"""
|
||||||
|
|
||||||
|
middle_index = self.get_absolute_offset(0.5, self._height)
|
||||||
|
lines = list(self.lines)
|
||||||
|
|
||||||
|
# This should never happen; there should always be at least a cursor.
|
||||||
|
# I'm just being defensive here.
|
||||||
|
if len(lines) < 1:
|
||||||
|
return 0, middle_index
|
||||||
|
|
||||||
|
if middle_index < self.lines.upper_offset:
|
||||||
|
raise Exception()
|
||||||
|
attrs, _ = lines[0]
|
||||||
|
index = self.lines.upper_offset
|
||||||
|
elif middle_index > self.lines.lower_offset:
|
||||||
|
raise Exception()
|
||||||
|
attrs, _ = lines[-1]
|
||||||
|
index = self.lines.lower_offset
|
||||||
|
else:
|
||||||
|
attrs, _ = lines[middle_index - self.lines.upper_offset]
|
||||||
|
index = middle_index
|
||||||
|
|
||||||
|
mid = attrs.get("mid")
|
||||||
|
# We know that all lines, including the cursor, have an offset.
|
||||||
|
index -= attrs.get("offset") or 0
|
||||||
|
|
||||||
|
return mid, index
|
||||||
|
|
||||||
|
def _find_cursor(self) -> Optional[int]:
|
||||||
|
for index, line in enumerate(self.lines):
|
||||||
|
attrs, _ = line
|
||||||
|
|
||||||
|
if attrs.get("cursor"):
|
||||||
|
return index
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _cursor_visible(self) -> bool:
|
||||||
|
return True in self.lines.all_values("cursor")
|
||||||
|
|
||||||
|
def scroll(self, scroll_delta: int) -> None:
|
||||||
|
self._absolute_anchor_offset += scroll_delta
|
||||||
|
|
||||||
|
delta = self._render()
|
||||||
|
if delta != 0:
|
||||||
|
self._absolute_anchor_offset += delta + scroll_delta
|
||||||
|
self._render()
|
||||||
|
|
||||||
|
cursor_index = self._find_cursor()
|
||||||
|
if cursor_index is None:
|
||||||
|
closest, offset = self._closest_to_middle()
|
||||||
|
|
||||||
|
self._anchor_id = closest
|
||||||
|
self._absolute_anchor_offset = offset
|
||||||
|
else:
|
||||||
|
self._anchor_id = None
|
||||||
|
self._absolute_anchor_offset = cursor_index
|
||||||
|
|
||||||
|
# Moving the cursor
|
||||||
|
|
||||||
class BasicCursorRenderer(CursorRenderer):
|
class BasicCursorRenderer(CursorRenderer):
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue