Implement scrolling for cursor tree

This commit is contained in:
Joscha 2019-06-07 08:40:19 +00:00
parent a4d33847e9
commit dd6ad51077
2 changed files with 95 additions and 8 deletions

View file

@ -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

View file

@ -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):