add animation system

This commit is contained in:
Will McGugan
2021-06-23 21:42:57 +01:00
parent 2be8bf9e74
commit 5ed98ba750
6 changed files with 54 additions and 24 deletions

View File

@@ -25,6 +25,7 @@ typing-extensions = { version = "^3.10.0", python = "<3.8" }
[tool.poetry.dev-dependencies]
# rich = {git = "git@github.com:willmcgugan/rich", rev = "pretty-classes"}
mypy = "^0.910"
[build-system]
requires = ["poetry-core>=1.0.0"]

View File

@@ -7,6 +7,13 @@ class Point(NamedTuple):
x: int
y: int
def __add__(self, other: object) -> Point:
if isinstance(other, Point):
_x, _y = self
x, y = other
return Point(_x + x, _y + y)
raise NotImplemented
class Dimensions(NamedTuple):
width: int

View File

@@ -6,7 +6,7 @@ from rich.segment import Segment
from rich.style import StyleType
from .geometry import Dimensions, Point
from .widget import Widget
from .widget import Widget, Reactive
class PageRender:
@@ -81,6 +81,16 @@ class Page(Widget):
self._page = PageRender(renderable, style=style)
super().__init__(name=name)
x: Reactive[int] = Reactive(0)
y: Reactive[int] = Reactive(0)
def validate_y(self, value: int) -> int:
return max(0, value)
def update_y(self, old: int, new: int) -> None:
x, y = self._page.offset
self._page.offset = Point(x, new)
@property
def virtual_size(self) -> Dimensions:
return self._page.size

View File

@@ -142,6 +142,7 @@ class ScrollBar(Widget):
virtual_size: Reactive[int] = Reactive(100)
window_size: Reactive[int] = Reactive(20)
position: Reactive[int] = Reactive(0)
mouse_over: Reactive[bool] = Reactive(False)
def __rich_repr__(self) -> RichReprResult:
yield "virtual_size", self.virtual_size
@@ -156,8 +157,17 @@ class ScrollBar(Widget):
window_size=self.window_size,
position=self.position,
vertical=self.vertical,
style="bright_magenta on #555555"
if self.mouse_over
else "bright_magenta on #444444",
)
async def on_enter(self, event: events.Enter) -> None:
self.mouse_over = True
async def on_leave(self, event: events.Leave) -> None:
self.mouse_over = False
if __name__ == "__main__":
from rich.console import Console

View File

@@ -21,6 +21,7 @@ from rich.segment import Segment
from rich.style import Style
from . import events
from .animator import Animator
from ._context import active_app
from ._loop import loop_last
from ._line_cache import LineCache
@@ -41,7 +42,7 @@ class UpdateMessage(Message):
def __init__(
self,
sender: MessagePump,
widget: Widget,
widget: WidgetBase,
offset_x: int = 0,
offset_y: int = 0,
):
@@ -72,15 +73,15 @@ class Reactive(Generic[ReactiveType]):
self._default = default
self.validator = validator
def __set_name__(self, owner: "Widget", name: str) -> None:
def __set_name__(self, owner: "WidgetBase", name: str) -> None:
self.name = name
self.internal_name = f"__{name}"
setattr(owner, self.internal_name, self._default)
def __get__(self, obj: "Widget", obj_type: type[object]) -> ReactiveType:
def __get__(self, obj: "WidgetBase", obj_type: type[object]) -> ReactiveType:
return getattr(obj, self.internal_name)
def __set__(self, obj: "Widget", value: ReactiveType) -> None:
def __set__(self, obj: "WidgetBase", value: ReactiveType) -> None:
if getattr(obj, self.internal_name) != value:
log.debug("%s -> %s", self.internal_name, value)
@@ -115,6 +116,7 @@ class WidgetBase(MessagePump):
self._repaint_required = False
super().__init__()
self._animator = Animator(self)
# self.disable_messages(events.MouseMove)
def __init_subclass__(

View File

@@ -22,40 +22,39 @@ class ScrollView(LayoutView):
def __init__(
self, renderable: RenderableType, name: str | None = None, style: StyleType = ""
) -> None:
layout = Layout()
layout = Layout(name="outer")
layout.split_row(
Layout(name="main", ratio=1), Layout(name="vertical_scrollbar", size=1)
)
layout["main"].split_column(
Layout(name="content", ratio=1), Layout(name="horizontal_scrollbar", size=1)
Layout(name="content", ratio=1), Layout(name="vertical_scrollbar", size=1)
)
# layout["main"].split_column(
# Layout(name="content", ratio=1), Layout(name="horizontal_scrollbar", size=1)
# )
self._vertical_scrollbar = ScrollBar(vertical=True)
self._horizontal_Scrollbar = ScrollBar(vertical=False)
# self._horizontal_Scrollbar = ScrollBar(vertical=False)
self._page = Page(renderable, style=style)
super().__init__(layout=layout, name=name)
position_x: Reactive[int] = Reactive(0)
position_y: Reactive[int] = Reactive(0)
x: Reactive[float] = Reactive(0)
y: Reactive[float] = Reactive(0)
def validate_position_y(self, value: int) -> int:
def validate_y(self, value: float) -> int:
return max(0, value)
def update_position_y(self, old_value: int, new_value: int) -> None:
self._vertical_scrollbar.position = new_value
def update_y(self, old_value: float, new_value: float) -> None:
self._page.y = int(new_value)
self._vertical_scrollbar.position = int(new_value)
async def on_mount(self, event: events.Mount) -> None:
await self.mount_all(
content=self._page,
vertical_scrollbar=self._vertical_scrollbar,
horizontal_scrollbar=self._horizontal_Scrollbar,
# horizontal_scrollbar=self._horizontal_Scrollbar,
)
async def on_idle(self, event: events.Idle) -> None:
self._vertical_scrollbar.virtual_size = self._page.virtual_size.height
self._vertical_scrollbar.window_size = self.size.height
# self._vertical_scrollbar.position = self.position_y
log.debug("SCROLLVIEW BAR %r", self._vertical_scrollbar)
log.debug("SCROLL SIZE %r", self._page.size)
await super().on_idle(event)
async def on_mouse_scroll_up(self, event: events.MouseScrollUp) -> None:
@@ -66,9 +65,10 @@ class ScrollView(LayoutView):
async def on_key(self, event: events.Key) -> None:
if event.key == "down":
self.position_y += 1
self.y += 1
elif event.key == "up":
self.position_y -= 1
# self._vertical_scrollbar.require_repaint()
# await self._vertical_scrollbar.post_message(events.Null(self))
# self.require_repaint()
self.y -= 1
elif event.key == "pagedown":
self._animator.animate("y", self.y + self.size.height, duration=0.5)
elif event.key == "pageup":
self._animator.animate("y", self.y - self.size.height, duration=0.5)