compositor nesting fix

This commit is contained in:
Will McGugan
2022-04-05 15:47:08 +01:00
parent ca77f7e24d
commit e44877c2af
11 changed files with 122 additions and 39 deletions

View File

@@ -3,8 +3,8 @@
App > Screen {
layout: dock;
docks: side=left/1;
background: $background;
color: $on-background;
background: $surface;
color: $on-surface;
}
#sidebar {
@@ -32,6 +32,7 @@ App > Screen {
height: 8;
background: $secondary-darken1;
color: $on-secondary-darken1;
border-right: outer $secondary-darken3;
}
@@ -45,14 +46,38 @@ App > Screen {
color: $on-primary;
background: $primary;
height: 3;
border-right: outer $secondary-darken3;
border: hkey $secondary-darken3;
}
#content {
color: $on-background;
background: $background;
color: $on-surface;
background: $surface;
layout: vertical;
}
Tweet {
height: 7;
max-width: 80;
margin: 1 2;
background: $background;
color: $on-background;
layout: vertical
}
TweetHeader {
height:1
background: $secondary
color: $on-secondary
}
TweetBody {
background: $background
color: $on-background
}
#footer {
color: $on-accent1;
background: $accent1;

View File

@@ -1,7 +1,29 @@
from rich.console import RenderableType
from rich.text import Text
from textual.app import App
from textual.widget import Widget
lorem = Text.from_markup(
"""Lorem ipsum dolor sit amet, consectetur adipiscing elit. In velit libero, volutpat nec hendrerit at, faucibus in odio. Aliquam hendrerit nibh sed quam volutpat maximus. Nullam suscipit convallis lorem quis sodales. In tristique lobortis ante et dictum. Ut at finibus ipsum. In urna dolor, placerat et mi facilisis, congue sollicitudin massa. Phasellus felis turpis, cursus eu lectus et, porttitor malesuada augue. Sed feugiat volutpat velit, sollicitudin fringilla velit bibendum faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In velit libero, volutpat nec hendrerit at, faucibus in odio. Aliquam hendrerit nibh sed quam volutpat maximus. Nullam suscipit convallis lorem quis sodales. In tristique lobortis ante et dictum. Ut at finibus ipsum. In urna dolor, placerat et mi facilisis, congue sollicitudin massa. Phasellus felis turpis, cursus eu lectus et, porttitor malesuada augue. Sed feugiat volutpat velit, sollicitudin fringilla velit bibendum faucibus. """,
)
class TweetHeader(Widget):
def render(self) -> RenderableType:
return Text("Lorem Impsum", justify="center")
class TweetBody(Widget):
def render(self) -> Text:
return lorem
class Tweet(Widget):
pass
class BasicApp(App):
"""A basic app demonstrating CSS"""
@@ -13,7 +35,16 @@ class BasicApp(App):
"""Build layout here."""
self.mount(
header=Widget(),
content=Widget(),
content=Widget(
Tweet(TweetHeader(), TweetBody()),
Tweet(TweetHeader(), TweetBody()),
Tweet(TweetHeader(), TweetBody())
# Tweet(TweetHeader(), TweetBody()),
# Tweet(TweetHeader(), TweetBody()),
# Tweet(TweetHeader(), TweetBody()),
# Tweet(TweetHeader(), TweetBody()),
# Tweet(TweetHeader(), TweetBody()),
),
footer=Widget(),
sidebar=Widget(
Widget(classes={"title"}),

View File

@@ -17,6 +17,7 @@ OUTER = 2
BORDER_CHARS: dict[EdgeType, tuple[str, str, str]] = {
"": (" ", " ", " "),
"none": (" ", " ", " "),
"hidden": (" ", " ", " "),
"round": ("╭─╮", "│ │", "╰─╯"),
"solid": ("┌─┐", "│ │", "└─┘"),
"double": ("╔═╗", "║ ║", "╚═╝"),
@@ -37,6 +38,7 @@ BORDER_LOCATIONS: dict[
] = {
"": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"none": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"hidden": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"round": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"solid": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"double": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
@@ -118,9 +120,9 @@ class Border:
self,
renderable: RenderableType,
edge_styles: tuple[EdgeStyle, EdgeStyle, EdgeStyle, EdgeStyle],
inner_color: Color,
outer_color: Color,
outline: bool = False,
inner_color: Color | None = None,
outer_color: Color | None = None,
):
self.renderable = renderable
self.edge_styles = edge_styles
@@ -141,8 +143,8 @@ class Border:
from_color(bottom_color.rich_color),
from_color(left_color.rich_color),
)
self.inner_style = from_color(bgcolor=inner_color)
self.outer_style = from_color(bgcolor=outer_color)
self.inner_style = from_color(bgcolor=inner_color.rich_color)
self.outer_style = from_color(bgcolor=outer_color.rich_color)
def __rich_repr__(self) -> rich.repr.Result:
yield self.renderable
@@ -250,7 +252,7 @@ class Border:
yield new_line
if has_bottom:
box1, box2, box3 = get_box(top, style, outer_style, bottom_style)[2]
box1, box2, box3 = get_box(bottom, style, outer_style, bottom_style)[2]
if has_left:
yield box1 if bottom == left else _Segment(" ", box1.style)
yield _Segment(box2.text * width, box2.style)
@@ -274,10 +276,10 @@ if __name__ == "__main__":
border = Border(
Padding(text, 1, style="on #303F9F"),
(
("none", Color.parse("#C5CAE9")),
("none", Color.parse("#C5CAE9")),
("wide", Color.parse("#C5CAE9")),
("wide", Color.parse("#C5CAE9")),
("wide", Color.parse("#C5CAE9")),
("wide", Color.parse("#C5CAE9")),
("none", Color.parse("#C5CAE9")),
),
inner_color=inner,
outer_color=outer,

View File

@@ -178,6 +178,7 @@ class Compositor:
and the "virtual size" (scrollable region)
"""
indent = 0
ORIGIN = Offset(0, 0)
size = root.size
map: RenderRegionMap = {}
@@ -197,6 +198,8 @@ class Compositor:
order (tuple[int, ...]): A tuple of ints to define the order.
clip (Region): The clipping region (i.e. the viewport which contains it).
"""
nonlocal indent
indent += 1
widgets.add(widget)
styles_offset = widget.styles.offset
layout_offset = (
@@ -205,6 +208,8 @@ class Compositor:
else ORIGIN
)
log(" " * indent, widget, region)
# region += layout_offset
# Container region is minus border
@@ -228,6 +233,10 @@ class Compositor:
widgets.update(arranged_widgets)
placements = sorted(placements, key=attrgetter("order"))
for sub_region, sub_widget, z in placements:
if sub_widget:
log(" " * indent, sub_region)
# Add all the widgets
for sub_region, sub_widget, z in placements:
# Combine regions with children to calculate the "virtual size"
@@ -235,15 +244,24 @@ class Compositor:
if sub_widget is not None:
add_widget(
sub_widget,
(
sub_region
+ child_region.origin
- scroll_offset
+ layout_offset
),
sub_region
+ container_region.origin
+ layout_offset
- scroll_offset,
(z,) + sub_widget.z,
sub_clip,
)
# add_widget(
# sub_widget,
# (
# sub_region
# + child_region.origin
# - scroll_offset
# + layout_offset
# ),
# (z,) + sub_widget.z,
# sub_clip,
# )
# Add any scrollbars
for chrome_widget, chrome_region in widget._arrange_scrollbars(
@@ -275,6 +293,7 @@ class Compositor:
region.size,
container_region.size,
)
indent -= 1
# Add top level (root) widget
add_widget(root, size.region, (), size.region)

View File

@@ -316,7 +316,7 @@ class StyleProperty:
Returns:
A ``Style`` object.
"""
style = ColorPair(obj.color, obj.background).style
style = ColorPair(obj.color, obj.background).style + obj.text_style
return style

View File

@@ -11,6 +11,7 @@ VALID_VISIBILITY: Final = {"visible", "hidden"}
VALID_DISPLAY: Final = {"block", "none"}
VALID_BORDER: Final = {
"none",
"hidden",
"round",
"solid",
"double",

View File

@@ -140,7 +140,6 @@ class StylesBase(ABC):
visibility = StringEnumProperty(VALID_VISIBILITY, "visible")
layout = LayoutProperty()
text = StyleProperty()
color = ColorProperty(Color(255, 255, 255))
background = ColorProperty(Color(0, 0, 0))
text_style = StyleFlagsProperty()
@@ -181,6 +180,8 @@ class StylesBase(ABC):
layers = NameListProperty()
transitions = TransitionsProperty()
rich_style = StyleProperty()
def __eq__(self, styles: object) -> bool:
"""Check that Styles containts the same rules."""
if not isinstance(styles, StylesBase):
@@ -371,7 +372,7 @@ class StylesBase(ABC):
if self.box_sizing == "content-box":
if has_rule("padding"):
size += self.padding
size += self.padding.totals
if has_rule("border"):
size += self.border.spacing.totals
if has_rule("margin"):
@@ -379,7 +380,7 @@ class StylesBase(ABC):
else: # border-box
if has_rule("padding"):
size -= self.padding
size -= self.padding.totals
if has_rule("border"):
size -= self.border.spacing.totals
if has_rule("margin"):

View File

@@ -15,6 +15,7 @@ Edge = Literal["top", "right", "bottom", "left"]
EdgeType = Literal[
"",
"none",
"hidden",
"round",
"solid",
"double",

View File

@@ -245,16 +245,16 @@ class DOMNode(MessagePump):
return tuple(reversed(indexes))
@property
def text_style(self) -> Style:
def rich_text_style(self) -> Style:
"""Get the text style (added to parent style).
Returns:
Style: Rich Style object.
"""
return (
self.parent.text_style + self.styles.text
self.parent.rich_text_style + self.styles.rich_style
if self.has_parent
else self.styles.text
else self.styles.rich_style
)
@property

View File

@@ -438,14 +438,14 @@ class Region(NamedTuple):
Returns:
Region: The new, smaller region.
"""
_clamp = clamp
top, right, bottom, left = margin
x, y, width, height = self
return Region(
x=_clamp(x + left, 0, width),
y=_clamp(y + top, 0, height),
width=_clamp(width - left - right, 0, width),
height=_clamp(height - top - bottom, 0, height),
x=x + left,
y=y + top,
width=max(0, width - left - right),
height=max(0, height - top - bottom),
)
def intersection(self, region: Region) -> Region:

View File

@@ -26,6 +26,7 @@ from . import events
from ._animator import BoundAnimator
from ._border import Border
from ._callback import invoke
from .color import Color
from ._context import active_app
from ._types import Lines
from .dom import DOMNode
@@ -372,10 +373,11 @@ class Widget(DOMNode):
renderable = self.render()
styles = self.styles
parent_styles = self.parent.styles
parent_text_style = self.parent.text_style
parent_text_style = self.parent.rich_text_style
text_style = styles.rich_style
text_style = styles.text
renderable_text_style = parent_text_style + text_style
if renderable_text_style:
renderable = Styled(renderable, renderable_text_style)
@@ -389,17 +391,17 @@ class Widget(DOMNode):
renderable = Border(
renderable,
styles.border,
inner_color=renderable_text_style.bgcolor,
outer_color=parent_text_style.bgcolor,
inner_color=styles.background,
outer_color=Color.from_rich_color(parent_text_style.bgcolor),
)
if styles.outline:
renderable = Border(
renderable,
styles.outline,
inner_color=styles.background,
outer_color=parent_styles.background,
outline=True,
inner_color=renderable_text_style.bgcolor,
outer_color=parent_text_style.bgcolor,
)
if styles.opacity != 1.0:
@@ -437,7 +439,8 @@ class Widget(DOMNode):
Returns:
bool: ``True`` if there is background color, otherwise ``False``.
"""
return self.layout is not None and self.styles.text.bgcolor is None
return False
return self.layout is not None
@property
def console(self) -> Console: