Skip to content

Commit 245033e

Browse files
committed
Layout refactor
1 parent 4394d82 commit 245033e

File tree

2 files changed

+48
-135
lines changed

2 files changed

+48
-135
lines changed

src/textual/layout.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def __rich_console__(
5757

5858

5959
class Layout(ABC):
60-
"""Responsible for rendering a layout."""
60+
"""Responsible for arranging Widgets in a view."""
6161

6262
def __init__(self) -> None:
6363
self._layout_map: dict[Widget, MapRegion] = {}
@@ -70,8 +70,25 @@ def reset(self) -> None:
7070
self.renders.clear()
7171
self._cuts = None
7272

73-
@abstractmethod
7473
def reflow(self, width: int, height: int) -> None:
74+
self.reset()
75+
76+
map = self.generate_map(width, height)
77+
self._layout_map = map
78+
self.width = width
79+
self.height = height
80+
81+
new_renders: dict[Widget, tuple[Region, Lines]] = {}
82+
for widget, (region, order) in map.items():
83+
if widget in self.renders and self.renders[widget][0].size == region.size:
84+
new_renders[widget] = (region, self.renders[widget][1])
85+
86+
self.renders = new_renders
87+
88+
@abstractmethod
89+
def generate_map(
90+
self, width: int, height: int, offset: Point = Point(0, 0)
91+
) -> dict[Widget, MapRegion]:
7592
...
7693

7794
@property

src/textual/layouts/dock.py

Lines changed: 29 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
from rich._ratio import ratio_resolve
1010

11-
from ..widget import WidgetID
1211
from ..geometry import Region, Point
1312
from ..layout import Layout, MapRegion
1413
from .._types import Lines
@@ -51,23 +50,23 @@ def __init__(self, docks: list[Dock] = None) -> None:
5150
self.docks: list[Dock] = docks or []
5251
super().__init__()
5352

54-
def reflow(self, width: int, height: int, offset: Point = Point(0, 0)) -> None:
55-
self.reset()
56-
self.width = width
57-
self.height = height
53+
def generate_map(
54+
self, width: int, height: int, offset: Point = Point(0, 0)
55+
) -> dict[Widget, MapRegion]:
56+
from ..view import View
57+
5858
map: dict[Widget, MapRegion] = {}
5959

60-
layers: dict[int, Region] = defaultdict(
61-
lambda: Region(0, 0, self.width, self.height)
62-
)
60+
layout_region = Region(0, 0, width, height)
61+
layers: dict[int, Region] = defaultdict(lambda: layout_region)
6362

6463
def add_widget(widget: Widget, region: Region, order: tuple[int, int]):
6564
region = region + offset
66-
if hasattr(widget, "layout"):
67-
widget.layout.reflow(
68-
region.width, region.height, offset=region.origin + offset
65+
if isinstance(widget, View):
66+
sub_map = widget.layout.generate_map(
67+
region.width, region.height, offset=region.origin
6968
)
70-
map.update(widget.layout.map)
69+
map.update(sub_map)
7170
else:
7271
map[widget] = MapRegion(region + widget.layout_offset, order)
7372

@@ -80,9 +79,9 @@ def add_widget(widget: Widget, region: Region, order: tuple[int, int]):
8079
)
8180
for widget in dock.widgets
8281
]
83-
8482
region = layers[dock.z]
8583
if not region:
84+
# No space left
8685
continue
8786

8887
order = (dock.z, index)
@@ -100,10 +99,11 @@ def add_widget(widget: Widget, region: Region, order: tuple[int, int]):
10099
if not size:
101100
break
102101
total += size
103-
render_region = (
104-
Region(x, render_y, width, size) + widget.layout_offset
102+
add_widget(
103+
widget,
104+
Region(x, render_y, width, size) + widget.layout_offset,
105+
order,
105106
)
106-
add_widget(widget, render_region, order)
107107
render_y += size
108108
remaining = max(0, remaining - size)
109109
region = Region(x, y + total, width, height - total)
@@ -120,10 +120,11 @@ def add_widget(widget: Widget, region: Region, order: tuple[int, int]):
120120
if not size:
121121
break
122122
total += size
123-
render_region = (
124-
Region(x, render_y - size, width, size) + widget.layout_offset
123+
add_widget(
124+
widget,
125+
Region(x, render_y - size, width, size) + widget.layout_offset,
126+
order,
125127
)
126-
add_widget(widget, render_region, order)
127128
render_y -= size
128129
remaining = max(0, remaining - size)
129130
region = Region(x, y, width, height - total)
@@ -140,10 +141,11 @@ def add_widget(widget: Widget, region: Region, order: tuple[int, int]):
140141
if not size:
141142
break
142143
total += size
143-
render_region = (
144-
Region(render_x, y, size, height) + widget.layout_offset
144+
add_widget(
145+
widget,
146+
Region(render_x, y, size, height) + widget.layout_offset,
147+
order,
145148
)
146-
add_widget(widget, render_region, order)
147149
render_x += size
148150
remaining = max(0, remaining - size)
149151
region = Region(x + total, y, width - total, height)
@@ -160,121 +162,15 @@ def add_widget(widget: Widget, region: Region, order: tuple[int, int]):
160162
if not size:
161163
break
162164
total += size
163-
render_region = (
164-
Region(render_x - size, y, size, height) + widget.layout_offset
165+
add_widget(
166+
widget,
167+
Region(render_x - size, y, size, height) + widget.layout_offset,
168+
order,
165169
)
166-
add_widget(widget, render_region, order)
167170
render_x -= size
168171
remaining = max(0, remaining - size)
169172
region = Region(x, y, width - total, height)
170173

171174
layers[dock.z] = region
172175

173-
new_renders: dict[Widget, tuple[Region, Lines]] = {}
174-
for widget, (region, order) in map.items():
175-
if widget in self.renders and self.renders[widget][0].size == region.size:
176-
new_renders[widget] = (region, self.renders[widget][1])
177-
178-
self.renders = new_renders
179-
log.debug("DOCK")
180-
self._layout_map = map
181-
182-
183-
if __name__ == "__main__":
184-
185-
from ..widgets.placeholder import Placeholder
186-
187-
import os.path
188-
189-
readme_path = os.path.join(
190-
os.path.dirname(os.path.abspath(__file__)), "../richreadme.md"
191-
)
192-
from rich.markdown import Markdown
193-
from ..widget import StaticWidget
194-
195-
with open(readme_path) as f:
196-
markdown = Markdown(f.read())
197-
198-
widget1 = StaticWidget(markdown)
199-
widget1.layout_size = 3
200-
201-
widget2 = StaticWidget(markdown)
202-
widget3 = Placeholder()
203-
204-
widget3.style = "on dark_blue"
205-
widget3.layout_size = 40
206-
widget3.layout_offset_x = +10
207-
widget3.layout_offset_y = +5
208-
# widget3.dock_offset = Point(-20, +1)
209-
210-
widget4 = Placeholder()
211-
widget4.layout_size = 1
212-
213-
widget5 = StaticWidget(markdown)
214-
widget5.layout_fraction = 1
215-
216-
widget6 = StaticWidget(markdown)
217-
widget6.layout_fraction = 1
218-
219-
widgets: dict[WidgetID, Widget] = {
220-
widget1.id: widget1,
221-
widget2.id: widget2,
222-
widget3.id: widget3,
223-
widget4.id: widget4,
224-
widget5.id: widget5,
225-
widget6.id: widget6,
226-
}
227-
228-
docks = [
229-
Dock(
230-
"top",
231-
[
232-
widget1.id,
233-
],
234-
),
235-
Dock(
236-
"left",
237-
[
238-
widget3.id,
239-
],
240-
z=1,
241-
),
242-
Dock(
243-
"bottom",
244-
[
245-
widget4.id,
246-
],
247-
),
248-
Dock(
249-
"left",
250-
[
251-
widget5.id,
252-
widget6.id,
253-
],
254-
),
255-
]
256-
layout = DockLayout(docks)
257-
258-
# fmt: on
259-
from rich import print
260-
from rich.console import Console
261-
from rich.segment import Segments, SegmentLines
262-
263-
console = Console()
264-
265-
width, height = console.size
266-
267-
layout.reflow(widgets, width, height)
268-
269-
print(layout.render(widgets, console))
270-
271-
widget3.style = "on red"
272-
# print(
273-
# layout.render(
274-
# widgets,
275-
# console,
276-
# )
277-
# )
278-
console.clear()
279-
280-
print(layout.update_widget(widgets, console, widget3))
176+
return map

0 commit comments

Comments
 (0)