mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #606 from Textualize/render-enhancements
Render enhancements
This commit is contained in:
35
poetry.lock
generated
35
poetry.lock
generated
@@ -52,7 +52,7 @@ python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -208,7 +208,7 @@ dev = ["twine", "markdown", "flake8", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "griffe"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -345,7 +345,7 @@ mkdocs = ">=1.1"
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material"
|
||||
version = "8.3.8"
|
||||
version = "8.3.9"
|
||||
description = "Documentation that simply works"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -713,7 +713,7 @@ python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.2.0"
|
||||
version = "4.3.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -721,7 +721,7 @@ python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.15.0"
|
||||
version = "20.15.1"
|
||||
description = "Virtual Python Environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -869,10 +869,7 @@ asynctest = [
|
||||
{file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"},
|
||||
{file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"},
|
||||
]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
||||
]
|
||||
atomicwrites = []
|
||||
attrs = [
|
||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||
@@ -1042,10 +1039,7 @@ ghp-import = [
|
||||
{file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
|
||||
{file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
|
||||
]
|
||||
griffe = [
|
||||
{file = "griffe-0.21.0-py3-none-any.whl", hash = "sha256:e9fb5eeb7c721e1d84804452bdc742bd57b120b13aba663157668ae2d217088a"},
|
||||
{file = "griffe-0.21.0.tar.gz", hash = "sha256:61ab3bc02b09afeb489f1aef44c646a09f1837d9cdf15943ac6021903a4d3984"},
|
||||
]
|
||||
griffe = []
|
||||
identify = [
|
||||
{file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"},
|
||||
{file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"},
|
||||
@@ -1124,10 +1118,7 @@ mkdocs-autorefs = [
|
||||
{file = "mkdocs-autorefs-0.4.1.tar.gz", hash = "sha256:70748a7bd025f9ecd6d6feeba8ba63f8e891a1af55f48e366d6d6e78493aba84"},
|
||||
{file = "mkdocs_autorefs-0.4.1-py3-none-any.whl", hash = "sha256:a2248a9501b29dc0cc8ba4c09f4f47ff121945f6ce33d760f145d6f89d313f5b"},
|
||||
]
|
||||
mkdocs-material = [
|
||||
{file = "mkdocs-material-8.3.8.tar.gz", hash = "sha256:b9cd305c3c29ef758931dae06e4aea0ca9f8bcc8ac6b2d45f10f932a015d6b83"},
|
||||
{file = "mkdocs_material-8.3.8-py2.py3-none-any.whl", hash = "sha256:949c75fa934d4b9ecc7b519964e58f0c9fc29f2ceb04736c85809cdbc403dfb5"},
|
||||
]
|
||||
mkdocs-material = []
|
||||
mkdocs-material-extensions = [
|
||||
{file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"},
|
||||
{file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"},
|
||||
@@ -1468,14 +1459,8 @@ typed-ast = [
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
|
||||
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
|
||||
{file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
|
||||
]
|
||||
virtualenv = [
|
||||
{file = "virtualenv-20.15.0-py2.py3-none-any.whl", hash = "sha256:804cce4de5b8a322f099897e308eecc8f6e2951f1a8e7e2b3598dff865f01336"},
|
||||
{file = "virtualenv-20.15.0.tar.gz", hash = "sha256:4c44b1d77ca81f8368e2d7414f9b20c428ad16b343ac6d226206c5b84e2b4fcc"},
|
||||
]
|
||||
typing-extensions = []
|
||||
virtualenv = []
|
||||
watchdog = [
|
||||
{file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"},
|
||||
{file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"},
|
||||
|
||||
BIN
reference/box.monopic
Normal file
BIN
reference/box.monopic
Normal file
Binary file not shown.
@@ -1,4 +1,6 @@
|
||||
|
||||
Button {
|
||||
padding-left: 1;
|
||||
padding-right: 1;
|
||||
box-sizing: border-box;
|
||||
margin: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,6 @@
|
||||
transition: color 300ms linear, background 300ms linear;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-background: $panel-darken-2;
|
||||
scrollbar-background-hover: $panel-darken-3;
|
||||
scrollbar-color: $system;
|
||||
scrollbar-color-active: $accent-darken-1;
|
||||
scrollbar-size-horizontal: 1;
|
||||
scrollbar-size-vertical: 2;
|
||||
}
|
||||
|
||||
*:hover {
|
||||
/* tint: 30% red;
|
||||
@@ -74,8 +66,8 @@ DataTable {
|
||||
}
|
||||
|
||||
#header {
|
||||
color: $text-primary-darken-1;
|
||||
background: $primary-darken-1;
|
||||
color: $text-primary-background-darken-1;
|
||||
background: $primary-background-darken-1;
|
||||
height: 3;
|
||||
content-align: center middle;
|
||||
}
|
||||
@@ -92,7 +84,7 @@ Tweet {
|
||||
height:12;
|
||||
width: 100%;
|
||||
|
||||
margin: 1 3;
|
||||
|
||||
background: $panel;
|
||||
color: $text-panel;
|
||||
layout: vertical;
|
||||
@@ -117,7 +109,6 @@ Tweet {
|
||||
|
||||
.code {
|
||||
height: auto;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +163,7 @@ Tweet.scroll-horizontal TweetBody {
|
||||
color: $text-accent;
|
||||
background: $accent;
|
||||
height: 1;
|
||||
border-top: hkey $accent-darken-2;
|
||||
|
||||
content-align: center middle;
|
||||
}
|
||||
|
||||
@@ -191,12 +182,12 @@ OptionItem {
|
||||
|
||||
OptionItem:hover {
|
||||
height: 3;
|
||||
color: $accent;
|
||||
color: $secondary;
|
||||
background: $primary-background-darken-1;
|
||||
/* border-top: hkey $accent2-darken-3;
|
||||
border-bottom: hkey $accent2-darken-3; */
|
||||
text-style: bold;
|
||||
border-left: outer $accent-darken-2;
|
||||
border-left: outer $secondary-darken-2;
|
||||
}
|
||||
|
||||
Error {
|
||||
@@ -204,10 +195,10 @@ Error {
|
||||
height:3;
|
||||
background: $error;
|
||||
color: $text-error;
|
||||
border-top: hkey $error-darken-2;
|
||||
border-bottom: hkey $error-darken-2;
|
||||
margin: 1 3;
|
||||
|
||||
border-top: wide $error-darken-1;
|
||||
border-bottom: wide $error-darken-1;
|
||||
|
||||
padding: 0;
|
||||
text-style: bold;
|
||||
align-horizontal: center;
|
||||
}
|
||||
@@ -217,9 +208,9 @@ Warning {
|
||||
height:3;
|
||||
background: $warning;
|
||||
color: $text-warning-fade-1;
|
||||
border-top: hkey $warning-darken-2;
|
||||
border-bottom: hkey $warning-darken-2;
|
||||
margin: 1 2;
|
||||
border-top: wide $warning-darken-1;
|
||||
border-bottom: wide $warning-darken-1;
|
||||
|
||||
text-style: bold;
|
||||
align-horizontal: center;
|
||||
}
|
||||
@@ -229,13 +220,14 @@ Success {
|
||||
width:90%;
|
||||
height:auto;
|
||||
box-sizing: border-box;
|
||||
background: $success-lighten-3;
|
||||
color: $text-success-lighten-3-fade-1;
|
||||
background: $success;
|
||||
color: $text-success;
|
||||
|
||||
border-top: hkey $success;
|
||||
border-bottom: hkey $success;
|
||||
border-top: hkey $success-darken-1;
|
||||
border-bottom: hkey $success-darken-1;
|
||||
|
||||
text-style: bold underline;
|
||||
|
||||
text-style: bold;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ class Warning(Widget):
|
||||
|
||||
class Success(Widget):
|
||||
def render(self) -> Text:
|
||||
return Text("This\nis\na\nsuccess\n message", justify="center")
|
||||
return Text("This is a success message", justify="center")
|
||||
|
||||
|
||||
class BasicApp(App, css_path="basic.css"):
|
||||
|
||||
@@ -225,8 +225,7 @@ class StylesCache:
|
||||
|
||||
from_color = Style.from_color
|
||||
|
||||
rich_style = styles.rich_style
|
||||
inner = from_color(bgcolor=background.rich_color) + rich_style
|
||||
inner = from_color(bgcolor=(base_background + background).rich_color)
|
||||
outer = from_color(bgcolor=base_background.rich_color)
|
||||
|
||||
def post(segments: Iterable[Segment]) -> list[Segment]:
|
||||
@@ -265,9 +264,7 @@ class StylesCache:
|
||||
elif (pad_top and y < gutter.top) or (
|
||||
pad_bottom and y >= height - gutter.bottom
|
||||
):
|
||||
background_style = from_color(
|
||||
color=rich_style.color, bgcolor=background.rich_color
|
||||
)
|
||||
background_style = from_color(bgcolor=background.rich_color)
|
||||
left_style = from_color(color=border_left_color.rich_color)
|
||||
left = get_box(border_left, inner, outer, left_style)[1][0]
|
||||
right_style = from_color(color=border_right_color.rich_color)
|
||||
|
||||
@@ -85,12 +85,13 @@ DEFAULT_COLORS = ColorSystem(
|
||||
secondary="#ffa62b",
|
||||
warning="#ffa62b",
|
||||
error="#ba3c5b",
|
||||
success="#6d9f71",
|
||||
accent="#ffa62b",
|
||||
success="#4EBF71",
|
||||
accent="#1A75B4",
|
||||
system="#5a4599",
|
||||
dark_surface="#292929",
|
||||
)
|
||||
|
||||
|
||||
ComposeResult = Iterable[Widget]
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ def get_box_model(
|
||||
else:
|
||||
# An explicit width
|
||||
content_width = styles.width.resolve_dimension(
|
||||
sizing_container, viewport, fraction_unit
|
||||
sizing_container - styles.margin.totals, viewport, fraction_unit
|
||||
)
|
||||
if is_border_box:
|
||||
content_width -= gutter.width
|
||||
|
||||
@@ -834,10 +834,10 @@ class ColorProperty:
|
||||
_rich_traceback_omit = True
|
||||
if color is None:
|
||||
if obj.clear_rule(self.name):
|
||||
obj.refresh()
|
||||
obj.refresh(children=True)
|
||||
elif isinstance(color, Color):
|
||||
if obj.set_rule(self.name, color):
|
||||
obj.refresh()
|
||||
obj.refresh(children=True)
|
||||
elif isinstance(color, str):
|
||||
try:
|
||||
parsed_color = Color.parse(color)
|
||||
@@ -849,7 +849,7 @@ class ColorProperty:
|
||||
),
|
||||
)
|
||||
if obj.set_rule(self.name, parsed_color):
|
||||
obj.refresh()
|
||||
obj.refresh(children=True)
|
||||
else:
|
||||
raise StyleValueError(f"Invalid color value {color}")
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ VALID_STYLE_FLAGS: Final = {
|
||||
"none",
|
||||
"not",
|
||||
"bold",
|
||||
"blink",
|
||||
"italic",
|
||||
"underline",
|
||||
"overline",
|
||||
@@ -50,7 +51,9 @@ VALID_STYLE_FLAGS: Final = {
|
||||
"b",
|
||||
"i",
|
||||
"u",
|
||||
"uu",
|
||||
"o",
|
||||
"reverse",
|
||||
}
|
||||
|
||||
NULL_SPACING: Final = Spacing.all(0)
|
||||
|
||||
@@ -178,6 +178,8 @@ class StylesBase(ABC):
|
||||
"scrollbar_background_active",
|
||||
}
|
||||
|
||||
node: DOMNode | None = None
|
||||
|
||||
display = StringEnumProperty(VALID_DISPLAY, "block", layout=True)
|
||||
visibility = StringEnumProperty(VALID_VISIBILITY, "visible")
|
||||
layout = LayoutProperty()
|
||||
@@ -325,11 +327,12 @@ class StylesBase(ABC):
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def refresh(self, *, layout: bool = False) -> None:
|
||||
def refresh(self, *, layout: bool = False, children: bool = False) -> None:
|
||||
"""Mark the styles as requiring a refresh.
|
||||
|
||||
Args:
|
||||
layout (bool, optional): Also require a layout. Defaults to False.
|
||||
children (bool, opional): Also refresh children. Defaults to False.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
@@ -439,7 +442,6 @@ class StylesBase(ABC):
|
||||
class Styles(StylesBase):
|
||||
|
||||
node: DOMNode | None = None
|
||||
|
||||
_rules: RulesMap = field(default_factory=dict)
|
||||
|
||||
important: set[str] = field(default_factory=set)
|
||||
@@ -486,9 +488,12 @@ class Styles(StylesBase):
|
||||
def get_rule(self, rule: str, default: object = None) -> object:
|
||||
return self._rules.get(rule, default)
|
||||
|
||||
def refresh(self, *, layout: bool = False) -> None:
|
||||
def refresh(self, *, layout: bool = False, children: bool = False) -> None:
|
||||
if self.node is not None:
|
||||
self.node.refresh(layout=layout)
|
||||
if children:
|
||||
for child in self.node.walk_children(with_self=False):
|
||||
child.refresh(layout=layout)
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset the rules to initial state."""
|
||||
@@ -783,8 +788,8 @@ class RenderStyles(StylesBase):
|
||||
if self.has_rule(rule_name):
|
||||
yield rule_name, getattr(self, rule_name)
|
||||
|
||||
def refresh(self, *, layout: bool = False) -> None:
|
||||
self._inline_styles.refresh(layout=layout)
|
||||
def refresh(self, *, layout: bool = False, children: bool = False) -> None:
|
||||
self._inline_styles.refresh(layout=layout, children=children)
|
||||
|
||||
def merge(self, other: Styles) -> None:
|
||||
"""Merge values from another Styles.
|
||||
|
||||
@@ -143,7 +143,7 @@ class Token(NamedTuple):
|
||||
yield "name", self.name
|
||||
yield "value", self.value
|
||||
yield "path", self.path
|
||||
yield "code", self.code
|
||||
yield "code", self.code if len(self.code) < 40 else self.code[:40] + "..."
|
||||
yield "location", self.location
|
||||
yield "referenced_by", self.referenced_by, None
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ class Vertical(Widget):
|
||||
CSS = """
|
||||
Vertical {
|
||||
layout: vertical;
|
||||
overflow: auto;
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -17,5 +18,6 @@ class Horizontal(Widget):
|
||||
CSS = """
|
||||
Horizontal {
|
||||
layout: horizontal;
|
||||
overflow: auto;
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -212,18 +212,16 @@ class ScrollBar(Widget):
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
styles = self.parent.styles
|
||||
scrollbar_style = Style(
|
||||
bgcolor=(
|
||||
styles.scrollbar_background_hover.rich_color
|
||||
if self.mouse_over
|
||||
else styles.scrollbar_background.rich_color
|
||||
),
|
||||
color=(
|
||||
styles.scrollbar_color_active.rich_color
|
||||
if self.grabbed
|
||||
else styles.scrollbar_color.rich_color
|
||||
),
|
||||
background = (
|
||||
styles.scrollbar_background_hover
|
||||
if self.mouse_over
|
||||
else styles.scrollbar_background
|
||||
)
|
||||
color = (
|
||||
styles.scrollbar_color_active if self.grabbed else styles.scrollbar_color
|
||||
)
|
||||
color = background + color
|
||||
scrollbar_style = Style.from_color(color.rich_color, background.rich_color)
|
||||
return ScrollBarRender(
|
||||
virtual_size=self.window_virtual_size,
|
||||
window_size=self.window_size,
|
||||
|
||||
@@ -16,9 +16,11 @@ import rich.repr
|
||||
from rich.align import Align
|
||||
from rich.console import Console, RenderableType
|
||||
from rich.measure import Measurement
|
||||
from rich.padding import Padding
|
||||
|
||||
from rich.segment import Segment
|
||||
from rich.style import Style
|
||||
from rich.styled import Styled
|
||||
from rich.text import Text
|
||||
|
||||
from . import errors, events, messages
|
||||
from ._animator import BoundAnimator
|
||||
@@ -72,10 +74,9 @@ class Widget(DOMNode):
|
||||
scrollbar-background: $panel-darken-2;
|
||||
scrollbar-background-hover: $panel-darken-3;
|
||||
scrollbar-color: $system;
|
||||
scrollbar-color-active: $secondary-darken-1;
|
||||
scrollbar-color-active: $warning-darken-1;
|
||||
scrollbar-size-vertical: 2;
|
||||
scrollbar-size-horizontal: 1;
|
||||
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -879,54 +880,7 @@ class Widget(DOMNode):
|
||||
def watch(self, attribute_name, callback: Callable[[Any], Awaitable[None]]) -> None:
|
||||
watch(self, attribute_name, callback)
|
||||
|
||||
def _style_renderable(self, renderable: RenderableType) -> RenderableType:
|
||||
"""Applies CSS styles to a renderable by wrapping it in another renderable.
|
||||
|
||||
Args:
|
||||
renderable (RenderableType): Renderable to apply styles to.
|
||||
|
||||
Returns:
|
||||
RenderableType: An updated renderable.
|
||||
"""
|
||||
(base_background, base_color), (background, color) = self.colors
|
||||
styles = self.styles
|
||||
|
||||
content_align = (styles.content_align_horizontal, styles.content_align_vertical)
|
||||
if content_align != ("left", "top"):
|
||||
horizontal, vertical = content_align
|
||||
renderable = Align(renderable, horizontal, vertical=vertical)
|
||||
|
||||
renderable = Padding(
|
||||
renderable,
|
||||
styles.padding,
|
||||
style=Style.from_color(color.rich_color, background.rich_color),
|
||||
)
|
||||
|
||||
if styles.border:
|
||||
renderable = Border(
|
||||
renderable,
|
||||
styles.border,
|
||||
inner_color=background,
|
||||
outer_color=base_background,
|
||||
)
|
||||
|
||||
if styles.outline:
|
||||
renderable = Border(
|
||||
renderable,
|
||||
styles.outline,
|
||||
inner_color=styles.background,
|
||||
outer_color=base_background,
|
||||
outline=True,
|
||||
)
|
||||
|
||||
if styles.tint.a != 0:
|
||||
renderable = Tint(renderable, styles.tint)
|
||||
if styles.opacity != 1.0:
|
||||
renderable = Opacity(renderable, opacity=styles.opacity)
|
||||
|
||||
return renderable
|
||||
|
||||
def render_styled(self) -> RenderableType:
|
||||
def _render_styled(self) -> RenderableType:
|
||||
"""Applies style attributes to the default renderable.
|
||||
|
||||
Returns:
|
||||
@@ -934,8 +888,21 @@ class Widget(DOMNode):
|
||||
"""
|
||||
|
||||
renderable = self.render()
|
||||
|
||||
if isinstance(renderable, str):
|
||||
renderable = Text.from_markup(renderable)
|
||||
|
||||
rich_style = self.rich_style
|
||||
if isinstance(renderable, Text):
|
||||
renderable.stylize(rich_style)
|
||||
else:
|
||||
renderable = Styled(renderable, rich_style)
|
||||
|
||||
styles = self.styles
|
||||
content_align = (styles.content_align_horizontal, styles.content_align_vertical)
|
||||
content_align = (
|
||||
styles.content_align_horizontal,
|
||||
styles.content_align_vertical,
|
||||
)
|
||||
if content_align != ("left", "top"):
|
||||
horizontal, vertical = content_align
|
||||
renderable = Align(renderable, horizontal, vertical=vertical)
|
||||
@@ -977,11 +944,11 @@ class Widget(DOMNode):
|
||||
def _render_content(self) -> None:
|
||||
"""Render all lines."""
|
||||
width, height = self.size
|
||||
renderable = self.render_styled()
|
||||
renderable = self._render_styled()
|
||||
options = self.console.options.update_dimensions(width, height).update(
|
||||
highlight=False
|
||||
)
|
||||
lines = self.console.render_lines(renderable, options, style=self.rich_style)
|
||||
lines = self.console.render_lines(renderable, options)
|
||||
self._render_cache = RenderCache(self.size, lines)
|
||||
self._dirty_regions.clear()
|
||||
|
||||
|
||||
@@ -33,145 +33,96 @@ class Button(Widget, can_focus=True):
|
||||
Button {
|
||||
width: auto;
|
||||
height: 3;
|
||||
|
||||
background: $primary;
|
||||
color: $text-primary;
|
||||
border: tall $primary-lighten-3;
|
||||
color: $text-primary;
|
||||
border: none;
|
||||
border-top: tall $primary-lighten-2;
|
||||
border-bottom: tall $primary-darken-3;
|
||||
content-align: center middle;
|
||||
text-style: bold;
|
||||
}
|
||||
|
||||
content-align: center middle;
|
||||
margin: 1 0;
|
||||
align: center middle;
|
||||
text-style: bold;
|
||||
|
||||
transition: background 0.1;/* for "active" effect */
|
||||
Button:focus {
|
||||
text-style: bold underline;
|
||||
}
|
||||
|
||||
Button:hover {
|
||||
border-top: tall $primary-lighten-1;
|
||||
background: $primary-darken-2;
|
||||
color: $text-primary-darken-2;
|
||||
border: tall $primary-lighten-1;
|
||||
color: $text-primary-darken-2;
|
||||
}
|
||||
|
||||
Button.-active {
|
||||
background: $primary-lighten-1;
|
||||
background: $primary;
|
||||
border-bottom: tall $primary-lighten-2;
|
||||
border-top: tall $primary-darken-2;
|
||||
}
|
||||
|
||||
.-dark-mode Button {
|
||||
background: $background;
|
||||
color: $primary-lighten-2;
|
||||
border: tall white $primary-lighten-2;
|
||||
}
|
||||
|
||||
.-dark-mode Button:hover {
|
||||
background: $surface;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-active {
|
||||
background: $background-lighten-3;
|
||||
}
|
||||
|
||||
/* Success variant */
|
||||
Button.-success {
|
||||
background: $success;
|
||||
color: $text-success;
|
||||
border: tall $success-lighten-3;
|
||||
border-top: tall $success-lighten-2;
|
||||
border-bottom: tall $success-darken-3;
|
||||
|
||||
}
|
||||
|
||||
Button.-success:hover {
|
||||
background: $success-darken-1;
|
||||
color: $text-success-darken-1;
|
||||
border: tall $success-lighten-2;
|
||||
background: $success-darken-2;
|
||||
color: $text-success-darken-2;
|
||||
|
||||
}
|
||||
|
||||
Button.-success.-active {
|
||||
background: $success-lighten-1;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-success {
|
||||
background: $success;
|
||||
color: $text-success;
|
||||
border: tall $success-lighten-3;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-success:hover {
|
||||
background: $success-darken-1;
|
||||
color: $text-success-darken-1;
|
||||
border: tall $success-lighten-3;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-success.-active {
|
||||
background: $success-lighten-1;
|
||||
border-bottom: tall $success-lighten-2;
|
||||
border-top: tall $success-darken-2;
|
||||
}
|
||||
|
||||
|
||||
/* Warning variant */
|
||||
Button.-warning {
|
||||
background: $warning;
|
||||
color: $text-warning;
|
||||
border: tall $warning-lighten-3;
|
||||
color: $text-warning;
|
||||
border-top: tall $warning-lighten-2;
|
||||
border-bottom: tall $warning-darken-3;
|
||||
}
|
||||
|
||||
Button.-warning:hover {
|
||||
background: $warning-darken-1;
|
||||
background: $warning-darken-2;
|
||||
color: $text-warning-darken-1;
|
||||
border: tall $warning-lighten-3;
|
||||
|
||||
}
|
||||
|
||||
Button.-warning.-active {
|
||||
background: $warning;
|
||||
border-bottom: tall $warning-lighten-2;
|
||||
border-top: tall $warning-darken-2;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-warning {
|
||||
background: $warning;
|
||||
color: $text-warning;
|
||||
border: tall $warning-lighten-3;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-warning:hover {
|
||||
background: $warning-darken-1;
|
||||
color: $text-warning-darken-1;
|
||||
border: tall $warning-lighten-3;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-warning.-active {
|
||||
background: $warning-lighten-1;
|
||||
}
|
||||
|
||||
|
||||
/* Error variant */
|
||||
Button.-error {
|
||||
background: $error;
|
||||
color: $text-error;
|
||||
border: tall $error-lighten-3;
|
||||
border-top: tall $error-lighten-2;
|
||||
border-bottom: tall $error-darken-3;
|
||||
|
||||
}
|
||||
|
||||
Button.-error:hover {
|
||||
background: $error-darken-1;
|
||||
color: $text-error-darken-1;
|
||||
border: tall $error-lighten-3;
|
||||
color: $text-error-darken-2;
|
||||
|
||||
}
|
||||
|
||||
Button.-error.-active {
|
||||
background: $error;
|
||||
border-bottom: tall $error-lighten-2;
|
||||
border-top: tall $error-darken-2;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-error {
|
||||
background: $error;
|
||||
color: $text-error;
|
||||
border: tall $error-lighten-3;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-error:hover {
|
||||
background: $error-darken-1;
|
||||
color: $text-error-darken-1;
|
||||
border: tall $error-lighten-3;
|
||||
}
|
||||
|
||||
.-dark-mode Button.-error.-active {
|
||||
background: $error;
|
||||
}
|
||||
|
||||
App.-show-focus Button:focus {
|
||||
tint: $accent 20%;
|
||||
}
|
||||
"""
|
||||
|
||||
ACTIVE_EFFECT_DURATION = 0.3
|
||||
|
||||
@@ -113,8 +113,8 @@ class DataTable(ScrollView, Generic[CellType]):
|
||||
}
|
||||
DataTable > .datatable--header {
|
||||
text-style: bold;
|
||||
background: $primary;
|
||||
color: $text-primary;
|
||||
background: $primary-darken-1;
|
||||
color: $text-primary-darken-1;
|
||||
}
|
||||
DataTable > .datatable--fixed {
|
||||
text-style: bold;
|
||||
|
||||
@@ -49,7 +49,7 @@ def test_width():
|
||||
def get_auto_width(container: Size, parent: Size) -> int:
|
||||
return 10
|
||||
|
||||
def get_auto_height(container: Size, parent: Size) -> int:
|
||||
def get_auto_height(container: Size, parent: Size, width: int) -> int:
|
||||
return 10
|
||||
|
||||
box_model = get_box_model(
|
||||
@@ -88,7 +88,7 @@ def test_width():
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, get_auto_width, get_auto_height
|
||||
)
|
||||
assert box_model == BoxModel(Fraction(60), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
styles.width = "100vw"
|
||||
styles.max_width = "50%"
|
||||
@@ -107,7 +107,7 @@ def test_height():
|
||||
def get_auto_width(container: Size, parent: Size) -> int:
|
||||
return 10
|
||||
|
||||
def get_auto_height(container: Size, parent: Size) -> int:
|
||||
def get_auto_height(container: Size, parent: Size, width: int) -> int:
|
||||
return 10
|
||||
|
||||
box_model = get_box_model(
|
||||
@@ -139,6 +139,15 @@ def test_height():
|
||||
)
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(20), Spacing(1, 2, 3, 4))
|
||||
|
||||
styles.height = "auto"
|
||||
styles.margin = 2
|
||||
|
||||
box_model = get_box_model(
|
||||
styles, Size(60, 20), Size(80, 24), one, get_auto_width, get_auto_height
|
||||
)
|
||||
assert box_model == BoxModel(Fraction(56), Fraction(10), Spacing(2, 2, 2, 2))
|
||||
|
||||
styles.margin = 1, 2, 3, 4
|
||||
styles.height = "100vh"
|
||||
styles.max_height = "50%"
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from rich.segment import Segment
|
||||
from rich.style import Style
|
||||
|
||||
from textual.color import Color
|
||||
from textual.geometry import Region, Size
|
||||
@@ -41,10 +42,11 @@ def test_no_styles():
|
||||
content.__getitem__,
|
||||
content_size=Size(3, 3),
|
||||
)
|
||||
style = Style.from_color(bgcolor=Color.parse("green").rich_color)
|
||||
expected = [
|
||||
[Segment("foo", styles.rich_style)],
|
||||
[Segment("bar", styles.rich_style)],
|
||||
[Segment("baz", styles.rich_style)],
|
||||
[Segment("foo", style)],
|
||||
[Segment("bar", style)],
|
||||
[Segment("baz", style)],
|
||||
]
|
||||
assert lines == expected
|
||||
|
||||
|
||||
Reference in New Issue
Block a user