Merge pull request #765 from Textualize/layer-order

Layer order
This commit is contained in:
Will McGugan
2022-09-14 11:14:52 +01:00
committed by GitHub
6 changed files with 79 additions and 25 deletions

21
sandbox/will/offset.css Normal file
View File

@@ -0,0 +1,21 @@
Screen {
layout: center;
}
#parent {
width: 32;
height: 8;
background: $panel;
}
#tag {
color: $text;
background: $success;
padding: 2 4;
width: auto;
offset: -8 -4;
}
#child {
background: red;
}

17
sandbox/will/offset.py Normal file
View File

@@ -0,0 +1,17 @@
from textual import layout
from textual.app import App, ComposeResult
from textual.widgets import Static
class OffsetExample(App):
def compose(self) -> ComposeResult:
yield layout.Vertical(
Static("Child", id="child"),
id="parent"
)
yield Static("Tag", id="tag")
app = OffsetExample(css_path="offset.css")
if __name__ == "__main__":
app.run()

View File

@@ -39,8 +39,8 @@ BORDER_CHARS: dict[EdgeType, tuple[str, str, str]] = {
"outer": ("▛▀▜", "▌ ▐", "▙▄▟"),
"hkey": ("▔▔▔", " ", "▁▁▁"),
"vkey": ("▏ ▕", "▏ ▕", "▏ ▕"),
"tall": ("▕▔▏", "▕ ▏", "▕▁▏"),
"wide": ("▁▁▁", "▏ ▕", "▔▔▔"),
"tall": ("▊▔▎", "▊ ▎", "▊▁▎"),
"wide": ("▁▁▁", "▎ ▋", "▔▔▔"),
}
# Some of the borders are on the widget background and some are on the background of the parent
@@ -62,8 +62,8 @@ BORDER_LOCATIONS: dict[
"outer": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"hkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"vkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"tall": ((1, 0, 1), (1, 0, 1), (1, 0, 1)),
"wide": ((1, 1, 1), (0, 1, 0), (1, 1, 1)),
"tall": ((2, 0, 1), (2, 0, 1), (2, 0, 1)),
"wide": ((1, 1, 1), (0, 1, 3), (1, 1, 1)),
}
INVISIBLE_EDGE_TYPES = cast("frozenset[EdgeType]", frozenset(("", "none", "hidden")))
@@ -81,7 +81,10 @@ Borders: TypeAlias = Tuple[EdgeStyle, EdgeStyle, EdgeStyle, EdgeStyle]
@lru_cache(maxsize=1024)
def get_box(
name: EdgeType, inner_style: Style, outer_style: Style, style: Style
name: EdgeType,
inner_style: Style,
outer_style: Style,
style: Style,
) -> BoxSegments:
"""Get segments used to render a box.
@@ -107,23 +110,30 @@ def get_box(
(lbottom1, lbottom2, lbottom3),
) = BORDER_LOCATIONS[name]
styles = (inner_style, outer_style)
inner = inner_style + style
outer = outer_style + style
styles = (
inner,
outer,
Style.from_color(outer.bgcolor, inner.color),
Style.from_color(inner.bgcolor, outer.color),
)
return (
(
_Segment(top1, styles[ltop1] + style),
_Segment(top2, styles[ltop2] + style),
_Segment(top3, styles[ltop3] + style),
_Segment(top1, styles[ltop1]),
_Segment(top2, styles[ltop2]),
_Segment(top3, styles[ltop3]),
),
(
_Segment(mid1, styles[lmid1] + style),
_Segment(mid2, styles[lmid2] + style),
_Segment(mid3, styles[lmid3] + style),
_Segment(mid1, styles[lmid1]),
_Segment(mid2, styles[lmid2]),
_Segment(mid3, styles[lmid3]),
),
(
_Segment(bottom1, styles[lbottom1] + style),
_Segment(bottom2, styles[lbottom2] + style),
_Segment(bottom3, styles[lbottom3] + style),
_Segment(bottom1, styles[lbottom1]),
_Segment(bottom2, styles[lbottom2]),
_Segment(bottom3, styles[lbottom3]),
),
)

View File

@@ -14,7 +14,7 @@ without having to render the entire screen.
from __future__ import annotations
from itertools import chain
from operator import attrgetter, itemgetter
from operator import itemgetter
import sys
from typing import Callable, cast, Iterator, Iterable, NamedTuple, TYPE_CHECKING
@@ -26,7 +26,7 @@ from rich.segment import Segment
from rich.style import Style
from . import errors
from .geometry import Region, Offset, Size, Spacing
from .geometry import Region, Offset, Size
from ._cells import cell_len
from ._profile import timer
@@ -55,7 +55,7 @@ class MapGeometry(NamedTuple):
"""Defines the absolute location of a Widget."""
region: Region # The (screen) region occupied by the widget
order: tuple[int, ...] # A tuple of ints defining the painting order
order: tuple[tuple[int, ...], ...] # A tuple of ints defining the painting order
clip: Region # A region to clip the widget by (if a Widget is within a container)
virtual_size: Size # The virtual size (scrollable region) of a widget if it is a container
container_size: Size # The container size (area not occupied by scrollbars)
@@ -344,12 +344,14 @@ class Compositor:
map: CompositorMap = {}
widgets: set[Widget] = set()
layer_order: int = 0
def add_widget(
widget: Widget,
virtual_region: Region,
region: Region,
order: tuple[int, ...],
order: tuple[tuple[int, ...], ...],
layer_order: int,
clip: Region,
) -> None:
"""Called recursively to place a widget and its children in the map.
@@ -413,15 +415,19 @@ class Compositor:
)
widget_region = sub_region + placement_scroll_offset
widget_order = order + (get_layer_index(sub_widget.layer, 0), z)
widget_order = order + (
(get_layer_index(sub_widget.layer, 0), z, layer_order),
)
add_widget(
sub_widget,
sub_region,
widget_region,
widget_order,
layer_order,
sub_clip,
)
layer_order -= 1
# Add any scrollbars
for chrome_widget, chrome_region in widget._arrange_scrollbars(
@@ -457,7 +463,7 @@ class Compositor:
)
# Add top level (root) widget
add_widget(root, size.region, size.region, (0,), size.region)
add_widget(root, size.region, size.region, ((0,),), layer_order, size.region)
return map, widgets
@property

View File

@@ -124,13 +124,15 @@ class ColorSystem:
background = self.background or Color.parse(DEFAULT_LIGHT_BACKGROUND)
surface = self.surface or Color.parse(DEFAULT_LIGHT_SURFACE)
boost = self.boost or background.get_contrast_text(1.0).with_alpha(0.07)
if self.panel is None:
panel = surface.blend(primary, luminosity_spread)
if dark:
panel += boost
else:
panel = self.panel
boost = self.boost or background.get_contrast_text(1.0).with_alpha(0.07)
colors: dict[str, str] = {}
def luminosity_range(spread) -> Iterable[tuple[str, float]]:

View File

@@ -189,8 +189,6 @@ async def test_composition_of_vertical_container_with_children(
"outer",
"hkey",
"vkey",
"tall",
"wide",
]
],
),