grid layout

This commit is contained in:
Will McGugan
2021-07-10 20:49:22 +01:00
parent b7b129b6ef
commit 2257cbe97a
3 changed files with 87 additions and 30 deletions

View File

@@ -1,9 +1,10 @@
from rich.align import Align
from rich.text import Text
from textual.app import App
from textual import events
from textual.view import View
from textual.widgets import Button, Placeholder
from textual.widgets import Button, Placeholder, Static
from textual.layouts.grid import GridLayout
try:
@@ -20,32 +21,45 @@ class GridTest(App):
async def on_startup(self, event: events.Startup) -> None:
layout = GridLayout(gap=1, gutter=1, align=("center", "center"))
layout = GridLayout(gap=(2, 1), gutter=1, align=("center", "center"))
await self.push_view(View(layout=layout))
layout.add_column("col", max_size=20, repeat=4)
layout.add_row("numbers", min_size=3, max_size=10)
layout.add_row("row", max_size=10, repeat=4)
layout.add_row("numbers", min_size=3, max_size=6)
layout.add_row("row", max_size=10, repeat=5)
layout.add_areas(
numbers="col1-start|col4-end,numbers",
zero="col1-start|col2-end,row4",
zero="col1-start|col2-end,row5",
dot="col3,row4",
equals="col4,row4",
)
def make_text(text: str) -> Text:
return Text(font.renderText(text).rstrip("\n"), style="bold")
def make_button(text: str) -> Button:
label = Text(font.renderText(text).rstrip("\n"), style="bold")
return Button(label)
label = make_text(text)
return Button(label, style="white on rgb(51,51,51)")
buttons = {
name: make_button(name)
for name in "AC,+/-,%,/,7,8,9,X,4,5,6,-,1,2,3,+,.,=".split(",")
}
buttons["AC"].style = "#000000 on rgb(165,165,165)"
buttons["+/-"].style = "#000000 on rgb(165,165,165)"
buttons["%"].style = "#000000 on rgb(165,165,165)"
buttons["/"].style = "#ffffff on rgb(255,159,7)"
buttons["X"].style = "#ffffff on rgb(255,159,7)"
buttons["-"].style = "#ffffff on rgb(255,159,7)"
buttons["+"].style = "#ffffff on rgb(255,159,7)"
buttons["="].style = "#ffffff on rgb(255,159,7)"
zero_text = make_text("0")
layout.place(
*buttons.values(),
numbers=Placeholder(name="numbers"),
numbers=Static(Align.right(zero_text)),
zero=make_button("0"),
)

View File

@@ -31,10 +31,15 @@ class NoWidget(Exception):
pass
@rich.repr.auto
class OrderedRegion(NamedTuple):
region: Region
order: tuple[int, int]
def __rich_repr__(self) -> rich.repr.RichReprResult:
yield "region", self.region
yield "order", self.order
class ReflowResult(NamedTuple):
"""The result of a reflow operation. Describes the chances to widgets."""
@@ -284,7 +289,7 @@ class Layout(ABC):
clip_x, clip_y, clip_x2, clip_y2 = clip.corners
divide = Segment.divide
back = Style.parse("on blue")
back = Style.parse("black")
# Maps each cut on to a list of segments
cuts = self.cuts

View File

@@ -251,26 +251,33 @@ class GridLayout(Layout):
def resolve_tracks(
grid: list[GridOptions], size: int, gap: int, repeat: bool
) -> tuple[dict[str, list[tuple[int, int]]], int]:
spans = (
) -> tuple[list[str], dict[str, tuple[int, int]], int, int]:
spans = [
(options.name, span)
for options, span in zip(cycle(grid), resolve(size, grid, gap, repeat))
)
]
names = [name for name, _span in spans]
max_size = 0
tracks: dict[str, list[tuple[int, int]]] = defaultdict(list)
tracks: dict[str, tuple[int, int]] = {}
counts = defaultdict(int)
for index, (name, (start, end)) in enumerate(spans):
max_size = max(max_size, end)
tracks[f"{name}-start"].append((index, start))
tracks[f"{name}-end"].append((index, end))
return tracks, max_size
counts[name] += 1
count = counts[name]
tracks[f"{name}-start"] = (index, start)
tracks[f"{name}-end"] = (index, end)
if repeat:
tracks[f"{name}-{count}-start"] = (index, start)
tracks[f"{name}-{count}-end"] = (index, end)
return names, tracks, len(spans), max_size
container = Dimensions(
width - self.column_gutter * 2, height - self.row_gutter * 2
)
column_tracks, column_size = resolve_tracks(
column_names, column_tracks, column_count, column_size = resolve_tracks(
self.columns, container.width, self.column_gap, self.column_repeat
)
row_tracks, row_size = resolve_tracks(
row_names, row_tracks, row_count, row_size = resolve_tracks(
self.rows, container.height, self.row_gap, self.row_repeat
)
grid_size = Dimensions(column_size, row_size)
@@ -282,8 +289,7 @@ class GridLayout(Layout):
)
free_slots = {
(col, row)
for col, row in product(range(len(self.columns)), range(len(self.rows)))
(col, row) for col, row in product(range(column_count), range(row_count))
}
map = {}
@@ -293,15 +299,16 @@ class GridLayout(Layout):
for widget, area in widget_areas:
column_start, column_end, row_start, row_end = self.areas[area]
try:
col1, x1 = column_tracks[column_start][0]
col2, x2 = column_tracks[column_end][0]
row1, y1 = row_tracks[row_start][0]
row2, y2 = row_tracks[row_end][0]
col1, x1 = column_tracks[column_start]
col2, x2 = column_tracks[column_end]
row1, y1 = row_tracks[row_start]
row2, y2 = row_tracks[row_end]
except (KeyError, IndexError):
continue
free_slots -= {
(col, row) for col, row in product(range(col1, col2), range(row1, row2))
(col, row)
for col, row in product(range(col1, col2 + 1), range(row1, row2 + 1))
}
region = self._align(
@@ -317,10 +324,36 @@ class GridLayout(Layout):
# Widgets with no area assigned.
auto_widgets = [widget for widget, area in self.widgets.items() if area is None]
# for (widget, area) in widget_areas:
# if widget in map:
# col1, col2, row1, row2 = area
# for col in range()
grid_refs = sorted(
product(range(column_count), range(row_count)), key=(lambda i: (i[1], i[0]))
)
iter_grid = iter(grid_refs)
for widget in auto_widgets:
try:
while True:
col, row = next(iter_grid)
if (col, row) in free_slots:
break
except StopIteration:
break
col_name = column_names[col]
row_name = row_names[row]
col1, x1 = column_tracks[f"{col_name}-start"]
col2, x2 = column_tracks[f"{col_name}-end"]
row1, y1 = row_tracks[f"{row_name}-start"]
row2, y2 = row_tracks[f"{row_name}-end"]
region = self._align(
from_corners(x1, y1, x2, y2),
grid_size,
container,
self.column_align,
self.row_align,
)
map[widget] = OrderedRegion(region + gutter, (0, order))
order += 1
return map
@@ -334,7 +367,12 @@ if __name__ == "__main__":
layout.add_row(fraction=1, name="top")
layout.add_row(fraction=2, name="bottom")
layout.add_area("center", "middle", "top")
layout.add_areas(center="a-start|b-end,top")
# layout.set_repeat(True)
from ..widgets import Placeholder
layout.place(center=Placeholder())
from rich import print