111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
import collections
|
|
from typing import Deque, List
|
|
|
|
from .element import Id, RenderedElement
|
|
|
|
__all__ = ["TreeList"]
|
|
|
|
class TreeList:
|
|
def __init__(self,
|
|
tree: List[RenderedElement],
|
|
anchor_id: Id,
|
|
) -> None:
|
|
self._deque: Deque = collections.deque()
|
|
|
|
# The offsets can be thought of as the index of a line relative to the
|
|
# anchor's first line.
|
|
#
|
|
# The upper offset is the index of the uppermost message's first line.
|
|
# upper_offset <= 0.
|
|
#
|
|
# The lower offset is the index of the lowermost message's LAST line.
|
|
# lower_offset >= 0.
|
|
self._upper_offset: int
|
|
self._lower_offset: int
|
|
|
|
# The upper and lower tree ids are the ids of the uppermost or
|
|
# lowermost tree added to the TreeList. They can be used to request the
|
|
# previous or next tree from an ElementSupply.
|
|
self._upper_tree_id: Id
|
|
self._lower_tree_id: Id
|
|
|
|
self._add_first_tree(tree, anchor_id)
|
|
|
|
@property
|
|
def upper_offset(self) -> int:
|
|
return self._upper_offset
|
|
|
|
@property
|
|
def lower_offset(self) -> int:
|
|
return self._lower_offset
|
|
|
|
@property
|
|
def upper_tree_id(self) -> Id:
|
|
return self._upper_tree_id
|
|
|
|
@property
|
|
def lower_tree_id(self) -> Id:
|
|
return self._lower_tree_id
|
|
|
|
def offset_by(self, offset: int) -> None:
|
|
self._upper_offset += offset
|
|
self._lower_offset += offset
|
|
|
|
def _add_first_tree(self,
|
|
tree: List[RenderedElement],
|
|
anchor_id: Id
|
|
) -> None:
|
|
if len(tree) == 0:
|
|
raise ValueError("The tree must contain at least one element")
|
|
|
|
tree_id = tree[0].element.id
|
|
self._upper_tree_id = tree_id
|
|
self._lower_tree_id = tree_id
|
|
|
|
offset = 0
|
|
found_anchor = False
|
|
|
|
for rendered in tree:
|
|
if rendered.element.id == anchor_id:
|
|
found_anchor = True
|
|
self._upper_offset = -offset
|
|
|
|
offset += rendered.height
|
|
|
|
if not found_anchor:
|
|
raise ValueError("The initial tree must contain the anchor")
|
|
|
|
# Subtracting 1 because the lower offset is the index of the lowermost
|
|
# message's last line, not the first line of a hypothetical message
|
|
# below that.
|
|
self._lower_offset = offset - 1
|
|
|
|
def add_above(self, tree: List[RenderedElement]) -> None:
|
|
if len(tree) == 0:
|
|
raise ValueError("The tree must contain at least one element")
|
|
|
|
self._upper_tree_id = tree[0].element.id
|
|
|
|
for rendered in reversed(tree):
|
|
self._deque.appendleft(rendered)
|
|
self._upper_offset -= rendered.height
|
|
|
|
# Alternative to the above for loop
|
|
#delta = sum(map(lambda r: r.height, tree))
|
|
#self._upper_offset -= delta
|
|
#self._deque.extendLeft(reversed(tree))
|
|
|
|
def add_below(self, tree: List[RenderedElement]) -> None:
|
|
if len(tree) == 0:
|
|
raise ValueError("The tree must contain at least one element")
|
|
|
|
self._lower_tree_id = tree[0].element.id
|
|
|
|
for rendered in tree:
|
|
self._deque.append(rendered)
|
|
self._lower_offset += rendered.height
|
|
|
|
# Alternative to the above for loop
|
|
#delta = sum(map(lambda r: r.height, tree))
|
|
#self._lower_offset += delta
|
|
#self._deque.extend(tree)
|