mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
virtual size
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
|
||||
/* height: 8; */
|
||||
margin: 1 2;
|
||||
text: on dark_blue;
|
||||
text-background: dark_blue;
|
||||
}
|
||||
|
||||
#child1 {
|
||||
|
||||
@@ -8,6 +8,9 @@ from textual.widget import Widget
|
||||
class BasicApp(App):
|
||||
"""Sandbox application used for testing/development by Textual developers"""
|
||||
|
||||
def on_load(self):
|
||||
self.bind("q", "quit", "Quit")
|
||||
|
||||
def on_mount(self):
|
||||
"""Build layout here."""
|
||||
|
||||
@@ -34,5 +37,8 @@ class BasicApp(App):
|
||||
async def on_key(self, event: events.Key) -> None:
|
||||
await self.dispatch_key(event)
|
||||
|
||||
def action_quit(self):
|
||||
self.panic(self.screen.tree)
|
||||
|
||||
|
||||
BasicApp.run(css_file="uber.css", log="textual.log")
|
||||
|
||||
@@ -43,6 +43,7 @@ class RenderRegion(NamedTuple):
|
||||
region: Region
|
||||
order: tuple[int, ...]
|
||||
clip: Region
|
||||
virtual_size: Size
|
||||
|
||||
|
||||
RenderRegionMap: TypeAlias = dict[Widget, RenderRegion]
|
||||
@@ -133,7 +134,10 @@ class Compositor:
|
||||
self.width = size.width
|
||||
self.height = size.height
|
||||
|
||||
map, virtual_size, widgets = self._arrange_root(parent)
|
||||
# TODO: Handle virtual size
|
||||
map, widgets = self._arrange_root(parent)
|
||||
|
||||
log(map)
|
||||
|
||||
self._require_update = False
|
||||
|
||||
@@ -149,7 +153,7 @@ class Compositor:
|
||||
|
||||
# Copy renders if the size hasn't changed
|
||||
new_renders = {
|
||||
widget: (region, clip) for widget, (region, _order, clip) in map.items()
|
||||
widget: (region, clip) for widget, (region, _order, clip, _) in map.items()
|
||||
}
|
||||
self.regions = new_renders
|
||||
|
||||
@@ -160,14 +164,13 @@ class Compositor:
|
||||
if widget in old_widgets and widget.size != region.size
|
||||
}
|
||||
|
||||
parent.virtual_size = virtual_size
|
||||
self.widgets.clear()
|
||||
self.widgets.update(widgets)
|
||||
return ReflowResult(
|
||||
hidden=hidden_widgets, shown=shown_widgets, resized=resized_widgets
|
||||
)
|
||||
|
||||
def _arrange_root(self, root: Widget) -> tuple[RenderRegionMap, Size, set[Widget]]:
|
||||
def _arrange_root(self, root: Widget) -> tuple[RenderRegionMap, set[Widget]]:
|
||||
"""Arrange a widgets children based on its layout attribute.
|
||||
|
||||
Args:
|
||||
@@ -177,9 +180,9 @@ class Compositor:
|
||||
map[dict[Widget, RenderRegion], Size]: A mapping of widget on to render region
|
||||
and the "virtual size" (scrollable reason)
|
||||
"""
|
||||
size = Size(self.width, self.height)
|
||||
ORIGIN = Offset(0, 0)
|
||||
|
||||
ORIGIN = Offset(0, 0)
|
||||
size = root.size
|
||||
map: dict[Widget, RenderRegion] = {}
|
||||
widgets: set[Widget] = set()
|
||||
|
||||
@@ -188,7 +191,7 @@ class Compositor:
|
||||
region: Region,
|
||||
order: tuple[int, ...],
|
||||
clip: Region,
|
||||
):
|
||||
) -> None:
|
||||
widgets.add(widget)
|
||||
styles_offset = widget.styles.offset
|
||||
total_region = region
|
||||
@@ -197,7 +200,6 @@ class Compositor:
|
||||
if styles_offset
|
||||
else ORIGIN
|
||||
)
|
||||
map[widget] = RenderRegion(region + layout_offset, order, clip)
|
||||
|
||||
if widget.layout is not None:
|
||||
scroll = widget.scroll
|
||||
@@ -220,17 +222,21 @@ class Compositor:
|
||||
sub_widget.z + (z,),
|
||||
sub_clip,
|
||||
)
|
||||
return total_region.size
|
||||
|
||||
virtual_size = add_widget(root, size.region, (), size.region)
|
||||
return map, virtual_size, widgets
|
||||
map[widget] = RenderRegion(
|
||||
region + layout_offset, order, clip, total_region.size
|
||||
)
|
||||
|
||||
add_widget(root, size.region, (), size.region)
|
||||
|
||||
return map, widgets
|
||||
|
||||
async def mount_all(self, screen: Screen) -> None:
|
||||
screen.app.mount(*self.widgets)
|
||||
|
||||
def __iter__(self) -> Iterator[tuple[Widget, Region, Region]]:
|
||||
layers = sorted(self.map.items(), key=lambda item: item[1].order, reverse=True)
|
||||
for widget, (region, order, clip) in layers:
|
||||
for widget, (region, _order, clip, _) in layers:
|
||||
yield widget, region.intersection(clip), region
|
||||
|
||||
def get_offset(self, widget: Widget) -> Offset:
|
||||
@@ -312,7 +318,7 @@ class Compositor:
|
||||
screen_region = Region(0, 0, width, height)
|
||||
cuts = [[0, width] for _ in range(height)]
|
||||
|
||||
for region, order, clip in self.map.values():
|
||||
for region, order, clip, _ in self.map.values():
|
||||
region = region.intersection(clip)
|
||||
if region and (region in screen_region):
|
||||
region_cuts = (region.x, region.x + region.width)
|
||||
@@ -337,7 +343,7 @@ class Compositor:
|
||||
widget_regions = sorted(
|
||||
[
|
||||
(widget, region, order, clip)
|
||||
for widget, (region, order, clip) in self.map.items()
|
||||
for widget, (region, order, clip, _) in self.map.items()
|
||||
if widget.is_visual and widget.visible
|
||||
],
|
||||
key=itemgetter(2),
|
||||
@@ -347,12 +353,11 @@ class Compositor:
|
||||
widget_regions = []
|
||||
|
||||
for widget, region, _order, clip in widget_regions:
|
||||
|
||||
lines = widget._get_lines()
|
||||
|
||||
if region in clip:
|
||||
lines = widget._get_lines()
|
||||
yield region, clip, lines
|
||||
elif clip.overlaps(region):
|
||||
lines = widget._get_lines()
|
||||
new_region = region.intersection(clip)
|
||||
delta_x = new_region.x - region.x
|
||||
delta_y = new_region.y - region.y
|
||||
|
||||
@@ -265,6 +265,7 @@ class DOMNode(MessagePump):
|
||||
Tree: A Rich object which may be printed.
|
||||
"""
|
||||
from rich.columns import Columns
|
||||
from rich.console import Group
|
||||
from rich.panel import Panel
|
||||
|
||||
highlighter = ReprHighlighter()
|
||||
@@ -272,21 +273,27 @@ class DOMNode(MessagePump):
|
||||
|
||||
def add_children(tree, node):
|
||||
for child in node.node_list:
|
||||
cols = [
|
||||
Pretty(child),
|
||||
Text(f"{child.size.width} X {child.size.height}", style="dim"),
|
||||
]
|
||||
info = Columns(
|
||||
[
|
||||
Pretty(child),
|
||||
highlighter(f"region={child.region!r}"),
|
||||
highlighter(
|
||||
f"virtual_size={child.virtual_size!r}",
|
||||
),
|
||||
]
|
||||
)
|
||||
css = child.styles.css
|
||||
if css:
|
||||
cols.append(
|
||||
Panel(
|
||||
info = Group(
|
||||
info,
|
||||
Panel.fit(
|
||||
Text(child.styles.css),
|
||||
border_style="dim",
|
||||
title="css",
|
||||
title_align="left",
|
||||
)
|
||||
),
|
||||
branch = tree.add(Columns(cols))
|
||||
),
|
||||
)
|
||||
branch = tree.add(info)
|
||||
if tree.children:
|
||||
add_children(branch, child)
|
||||
|
||||
|
||||
@@ -111,11 +111,6 @@ class Size(NamedTuple):
|
||||
"""A Size is Falsy if it has area 0."""
|
||||
return self.width * self.height != 0
|
||||
|
||||
@property
|
||||
def clamped(self) -> Size:
|
||||
width, height = self
|
||||
return Size(max(0, width), max(0, height))
|
||||
|
||||
@property
|
||||
def area(self) -> int:
|
||||
"""Get the area of the size.
|
||||
|
||||
@@ -29,7 +29,7 @@ from ._callback import invoke
|
||||
from ._context import active_app
|
||||
from ._types import Lines
|
||||
from .dom import DOMNode
|
||||
from .geometry import Offset, Size
|
||||
from .geometry import Offset, Region, Size
|
||||
from .message import Message
|
||||
from . import messages
|
||||
from .layout import Layout
|
||||
@@ -97,7 +97,7 @@ class Widget(DOMNode):
|
||||
if self.name:
|
||||
yield "name", self.name
|
||||
if self.classes:
|
||||
yield "classes", self.classes
|
||||
yield "classes", set(self.classes)
|
||||
pseudo_classes = self.pseudo_classes
|
||||
if pseudo_classes:
|
||||
yield "pseudo_classes", pseudo_classes
|
||||
@@ -159,6 +159,10 @@ class Widget(DOMNode):
|
||||
def size(self) -> Size:
|
||||
return self._size
|
||||
|
||||
@property
|
||||
def region(self) -> Region:
|
||||
return self.screen._compositor.get_widget_region(self)
|
||||
|
||||
@property
|
||||
def scroll(self) -> Offset:
|
||||
return Offset(self.scroll_x, self.scroll_y)
|
||||
@@ -265,7 +269,7 @@ class Widget(DOMNode):
|
||||
|
||||
# Default displays a pretty repr in the center of the screen
|
||||
|
||||
label = f"{self.css_identifier_styled} {self.size}"
|
||||
label = f"{self.css_identifier_styled} {self.size} {self.virtual_size}"
|
||||
|
||||
return Align.center(label, vertical="middle")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user