From ecc5d24b5419b952d0137f883e923e2ec1047af9 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 28 Jul 2021 21:04:25 +0100 Subject: [PATCH] fix cropping on scroll --- examples/grid.py | 20 ++++++++------------ src/textual/layout.py | 20 ++++++++++++++------ src/textual/layout_map.py | 4 +++- src/textual/layouts/dock.py | 12 ++++++------ src/textual/layouts/grid.py | 3 ++- src/textual/layouts/vertical.py | 4 ++-- src/textual/view.py | 6 ++++-- 7 files changed, 39 insertions(+), 30 deletions(-) diff --git a/examples/grid.py b/examples/grid.py index 4bb48929f..c31a26f42 100644 --- a/examples/grid.py +++ b/examples/grid.py @@ -10,7 +10,9 @@ class GridTest(App): async def on_mount(self, event: events.Mount) -> None: grid = await self.view.dock_grid(edge="left", size=70, name="left") - left = self.view["left"] + + self.view["left"].scroll_y = 5 + self.view["left"].scroll_x = 5 grid.add_column(fraction=1, name="left", min_size=20) grid.add_column(size=30, name="center") @@ -27,18 +29,12 @@ class GridTest(App): area4="right,top-start|middle-end", ) - def make_placeholder(name: str) -> Placeholder: - p = Placeholder(name=name) - p.layout_offset_x = 10 - p.layout_offset_y = 0 - return p - grid.place( - area1=make_placeholder(name="area1"), - area2=make_placeholder(name="area2"), - area3=make_placeholder(name="area3"), - area4=make_placeholder(name="area4"), + area1=Placeholder(name="area1"), + area2=Placeholder(name="area2"), + area3=Placeholder(name="area3"), + area4=Placeholder(name="area4"), ) -GridTest.run(title="Grid Test") +GridTest.run(title="Grid Test", log="textual.log") diff --git a/src/textual/layout.py b/src/textual/layout.py index 76e59eef9..81859124c 100644 --- a/src/textual/layout.py +++ b/src/textual/layout.py @@ -18,6 +18,7 @@ from rich.style import Style from . import log from ._loop import loop_last from .layout_map import LayoutMap +from ._lines import crop_lines from ._types import Lines from .geometry import clamp, Region, Offset, Dimensions @@ -95,11 +96,16 @@ class Layout(ABC): self.renders.clear() self._layout_map = None - def reflow(self, console: Console, width: int, height: int) -> ReflowResult: + def reflow( + self, console: Console, width: int, height: int, scroll: Offset + ) -> ReflowResult: self.reset() map = self.generate_map( - console, Dimensions(width, height), Region(0, 0, width, height) + console, + Dimensions(width, height), + Region(0, 0, width, height), + scroll, ) self._require_update = False @@ -157,7 +163,7 @@ class Layout(ABC): @abstractmethod def generate_map( - self, console: Console, size: Dimensions, viewport: Region + self, console: Console, size: Dimensions, viewport: Region, scroll: Offset ) -> LayoutMap: """Generate a layout map that defines where on the screen the widgets will be drawn. @@ -360,7 +366,6 @@ class Layout(ABC): for region, clip, lines in chain( renders, [(screen, screen, background_render)] ): - # region = region.intersection(clip) for y, line in enumerate(lines, region.y): if clip_y > y > clip_y2: continue @@ -396,6 +401,9 @@ class Layout(ABC): ) self.renders[widget] = (region, clip, new_lines) + update_lines = self.render(console, region.intersection(clip)).lines - update_lines = self.render(console, region).lines - return LayoutUpdate(update_lines, region.x, region.y) + clipped_region = region.intersection(clip) + update = LayoutUpdate(update_lines, clipped_region.x, clipped_region.y) + + return update diff --git a/src/textual/layout_map.py b/src/textual/layout_map.py index 8f07c2a9c..44a171e14 100644 --- a/src/textual/layout_map.py +++ b/src/textual/layout_map.py @@ -54,7 +54,9 @@ class LayoutMap: self.region = self.region.union(region.intersection(clip)) if isinstance(widget, View): - sub_map = widget.layout.generate_map(console, region.size, region) + sub_map = widget.layout.generate_map( + console, region.size, region, widget.scroll + ) for widget, (sub_region, sub_order, sub_clip) in sub_map.items(): sub_region += region.origin sub_clip = sub_clip.intersection(clip) diff --git a/src/textual/layouts/dock.py b/src/textual/layouts/dock.py index 3cc2e6dcd..9bbba9cce 100644 --- a/src/textual/layouts/dock.py +++ b/src/textual/layouts/dock.py @@ -8,7 +8,7 @@ from typing import Iterable, TYPE_CHECKING, Sequence from rich.console import Console from .._layout_resolve import layout_resolve -from ..geometry import Region, Dimensions +from ..geometry import Offset, Region, Dimensions from ..layout import Layout from ..layout_map import LayoutMap @@ -49,7 +49,7 @@ class DockLayout(Layout): yield from dock.widgets def generate_map( - self, console: Console, size: Dimensions, viewport: Region + self, console: Console, size: Dimensions, viewport: Region, scroll: Offset ) -> LayoutMap: map: LayoutMap = LayoutMap(size) @@ -90,7 +90,7 @@ class DockLayout(Layout): add_widget(widget, Region(x, render_y, width, layout_size), order) render_y += layout_size remaining = max(0, remaining - layout_size) - region = Region(x, y + total, width, height - total) + region = Region(x, y + total, width, height - total) - scroll elif dock.edge == "bottom": sizes = layout_resolve(height, dock_options) @@ -111,7 +111,7 @@ class DockLayout(Layout): ) render_y -= layout_size remaining = max(0, remaining - layout_size) - region = Region(x, y, width, height - total) + region = Region(x, y, width, height - total) - scroll elif dock.edge == "left": sizes = layout_resolve(width, dock_options) @@ -128,7 +128,7 @@ class DockLayout(Layout): add_widget(widget, Region(render_x, y, layout_size, height), order) render_x += layout_size remaining = max(0, remaining - layout_size) - region = Region(x + total, y, width - total, height) + region = Region(x + total, y, width - total, height) - scroll elif dock.edge == "right": sizes = layout_resolve(width, dock_options) @@ -149,7 +149,7 @@ class DockLayout(Layout): ) render_x -= layout_size remaining = max(0, remaining - layout_size) - region = Region(x, y, width - total, height) + region = Region(x, y, width - total, height) - scroll layers[dock.z] = region diff --git a/src/textual/layouts/grid.py b/src/textual/layouts/grid.py index b3ce81b44..5a1d7c31e 100644 --- a/src/textual/layouts/grid.py +++ b/src/textual/layouts/grid.py @@ -264,7 +264,7 @@ class GridLayout(Layout): return self.widgets.keys() def generate_map( - self, console: Console, size: Dimensions, viewport: Region + self, console: Console, size: Dimensions, viewport: Region, scroll: Offset ) -> LayoutMap: """Generate a map that associates widgets with their location on screen. @@ -328,6 +328,7 @@ class GridLayout(Layout): return names, tracks, len(spans), max_size def add_widget(widget: Widget, region: Region, order: tuple[int, int]): + region -= scroll map.add_widget(console, widget, region, order, viewport) # region = region + widget.layout_offset # map[widget] = RenderRegion(region, order, offset) diff --git a/src/textual/layouts/vertical.py b/src/textual/layouts/vertical.py index 3677fa80b..b14db585f 100644 --- a/src/textual/layouts/vertical.py +++ b/src/textual/layouts/vertical.py @@ -28,7 +28,7 @@ class VerticalLayout(Layout): return self._widgets def generate_map( - self, console: Console, size: Dimensions, viewport: Region + self, console: Console, size: Dimensions, viewport: Region, scroll: Offset ) -> LayoutMap: index = 0 width, height = size @@ -54,7 +54,7 @@ class VerticalLayout(Layout): else: add_widget( widget, - Region(x, y, region.width, region.height) - scroll_offset, + Region(x, y, region.width, region.height) - scroll, clip, ) y += region.height + gutter_height diff --git a/src/textual/view.py b/src/textual/view.py index b57466186..ce5d494f4 100644 --- a/src/textual/view.py +++ b/src/textual/view.py @@ -53,7 +53,7 @@ class View(Widget): scroll_y: Reactive[int] = Reactive(0) @property - def scroll_offset(self) -> Offset: + def scroll(self) -> Offset: return Offset(self.scroll_x, self.scroll_y) @property @@ -154,7 +154,9 @@ class View(Widget): width, height = self.console.size # virtual_width, virtual_height = self.virtual_size - hidden, shown, resized = self.layout.reflow(self.console, width, height) + hidden, shown, resized = self.layout.reflow( + self.console, width, height, self.scroll + ) self.app.refresh() for widget in hidden: