Scroll view now uses grid

This commit is contained in:
Will McGugan
2021-07-11 15:47:00 +01:00
parent 022dbeec6c
commit 9d998eb982
7 changed files with 134 additions and 12 deletions

View File

@@ -74,7 +74,7 @@ class CalculatorApp(App):
layout.place(
*buttons.values(),
numbers=Static(Padding(numbers, (0, 1)), style="white on rgb(51,51,51)"),
numbers=Static(Padding(numbers, (0, 1), style="white on rgb(51,51,51)")),
zero=make_button("0"),
)

View File

@@ -25,6 +25,7 @@ log = logging.getLogger("rich")
if TYPE_CHECKING:
from .widget import Widget, WidgetID
from .view import View
class NoWidget(Exception):
@@ -137,6 +138,9 @@ class Layout(ABC):
) -> dict[Widget, OrderedRegion]:
...
async def mount_all(self, view: "View") -> None:
await view.mount(*self.get_widgets())
@property
def map(self) -> dict[Widget, OrderedRegion]:
return self._layout_map

View File

@@ -11,6 +11,7 @@ from .._layout_resolve import layout_resolve
from .._loop import loop_last
from ..geometry import Dimensions, Point, Region
from ..layout import Layout, OrderedRegion
from ..view import View
from ..widget import Widget
if sys.version_info >= (3, 8):
@@ -49,14 +50,16 @@ class GridLayout(Layout):
self.rows: list[GridOptions] = []
self.areas: dict[str, GridArea] = {}
self.widgets: dict[Widget, str | None] = {}
self.column_gap = 1
self.row_gap = 1
self.column_gap = 0
self.row_gap = 0
self.column_repeat = False
self.row_repeat = False
self.column_align: GridAlign = "start"
self.row_align: GridAlign = "start"
self.column_gutter: int = 0
self.row_gutter: int = 0
self.hidden_columns: set[str] = set()
self.hidden_rows: set[str] = set()
if gap is not None:
if isinstance(gap, tuple):
@@ -75,6 +78,18 @@ class GridLayout(Layout):
super().__init__()
def hide_row(self, row_name: str) -> None:
self.hidden_rows.add(row_name)
def show_row(self, row_name: str) -> None:
self.hidden_rows.discard(row_name)
def hide_column(self, column_name: str) -> None:
self.hidden_rows.add(column_name)
def show_column(self, column_name: str) -> None:
self.hidden_rows.discard(column_name)
def add_column(
self,
name: str,
@@ -277,14 +292,33 @@ class GridLayout(Layout):
return names, tracks, len(spans), max_size
def add_widget(widget: Widget, region: Region, order: tuple[int, int]):
region = region + offset + widget.layout_offset
map[widget] = OrderedRegion(region, order)
if isinstance(widget, View):
sub_map = widget.layout.generate_map(
region.width, region.height, offset=region.origin
)
map.update(sub_map)
container = Dimensions(
width - self.column_gutter * 2, height - self.row_gutter * 2
)
column_names, column_tracks, column_count, column_size = resolve_tracks(
self.columns, container.width, self.column_gap, self.column_repeat
[
options
for options in self.columns
if options.name not in self.hidden_columns
],
container.width,
self.column_gap,
self.column_repeat,
)
row_names, row_tracks, row_count, row_size = resolve_tracks(
self.rows, container.height, self.row_gap, self.row_repeat
[options for options in self.rows if options.name not in self.hidden_rows],
container.height,
self.row_gap,
self.row_repeat,
)
grid_size = Dimensions(column_size, row_size)
@@ -323,7 +357,8 @@ class GridLayout(Layout):
self.column_align,
self.row_align,
)
map[widget] = OrderedRegion(region + gutter, (0, order))
# map[widget] = OrderedRegion(region + gutter + offset, (0, order))
add_widget(widget, region + gutter, (0, order))
order += 1
# Widgets with no area assigned.
@@ -355,7 +390,8 @@ class GridLayout(Layout):
self.column_align,
self.row_align,
)
map[widget] = OrderedRegion(region + gutter, (0, order))
# map[widget] = OrderedRegion(region + gutter + offset, (0, order))
add_widget(widget, region + gutter, (0, order))
order += 1
return map

View File

@@ -1,5 +1,7 @@
from __future__ import annotations
from logging import getLogger
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
from rich.padding import Padding, PaddingDimensions
from rich.segment import Segment
@@ -10,6 +12,8 @@ from .geometry import Dimensions, Point
from .message import Message
from .widget import Widget, Reactive
log = getLogger("rich")
class PageUpdate(Message):
def can_batch(self, message: "Message") -> bool:

View File

@@ -135,6 +135,7 @@ class View(Widget):
region = self.get_widget_region(widget)
else:
widget, region = self.get_widget_at(event.x, event.y)
log.debug("WIDGET %r", widget)
except NoWidget:
await self.app.set_mouse_over(None)
else:

View File

@@ -0,0 +1,58 @@
from __future__ import annotations
from rich.align import Align
from rich.console import Console, ConsoleOptions, RenderResult, RenderableType
from rich.padding import Padding
from rich.panel import Panel
import rich.repr
from rich.style import StyleType
from ..reactive import Reactive
from ..widget import Widget
class Expand:
def __init__(self, renderable: RenderableType) -> None:
self.renderable = renderable
def __rich_console__(
self, console: Console, options: ConsoleOptions
) -> RenderResult:
width = options.max_width
height = options.height or 1
yield from console.render(
self.renderable, options.update_dimensions(width, height)
)
class ButtonRenderable:
def __init__(self, label: RenderableType, style: StyleType = "") -> None:
self.label = label
self.style = style
def __rich_console__(
self, console: Console, options: ConsoleOptions
) -> RenderResult:
width = options.max_width
height = options.height or 1
yield Align.center(
self.label, vertical="middle", style=self.style, width=width, height=height
)
class Button(Widget):
def __init__(
self,
label: RenderableType,
name: str | None = None,
style: StyleType = "white on dark_blue",
):
self.label = label
self.name = name or str(label)
self.style = style
super().__init__()
def render(self) -> RenderableType:
return ButtonRenderable(self.label, style=self.style)
return Align.center(self.label, vertical="middle", style=self.style)

View File

@@ -7,18 +7,19 @@ from rich.style import StyleType
from .. import events
from ..layouts.grid import GridLayout
from ..message import Message
from ..scrollbar import ScrollTo, ScrollBar
from ..geometry import clamp
from ..page import Page
from ..views import DockView
from ..view import View
from ..reactive import Reactive
log = logging.getLogger("rich")
class ScrollView(DockView):
class ScrollView(View):
def __init__(
self,
renderable: RenderableType | None = None,
@@ -29,12 +30,23 @@ class ScrollView(DockView):
) -> None:
self.fluid = fluid
self._vertical_scrollbar = ScrollBar(vertical=True)
self._horizontal_scrollbar = ScrollBar(vertical=False)
self._page = Page(renderable or "", style=style)
super().__init__(name=name)
layout = GridLayout()
layout.add_column("main")
layout.add_column("vertical", size=1)
layout.add_row("main")
layout.add_row("horizontal", size=1)
layout.add_areas(
content="main,main", vertical="vertical,main", horizontal="main,horizontal"
)
layout.hide_row("horizontal")
super().__init__(name=name, layout=layout)
x: Reactive[float] = Reactive(0)
y: Reactive[float] = Reactive(0)
target_x: Reactive[float] = Reactive(0)
target_y: Reactive[float] = Reactive(0)
def validate_y(self, value: float) -> float:
@@ -52,8 +64,15 @@ class ScrollView(DockView):
self.require_repaint()
async def on_mount(self, event: events.Mount) -> None:
await self.dock(self._vertical_scrollbar, edge="right", size=1)
await self.dock(self._page, edge="top")
assert isinstance(self.layout, GridLayout)
self.layout.place(
content=self._page,
vertical=self._vertical_scrollbar,
horizontal=self._horizontal_scrollbar,
)
await self.layout.mount_all(self)
# await self.dock(self._vertical_scrollbar, edge="right", size=1)
# await self.dock(self._page, edge="top")
def scroll_up(self) -> None:
self.target_y += 1.5