Merge pull request #763 from Textualize/auto-color

auto color
This commit is contained in:
Will McGugan
2022-09-13 13:21:27 +01:00
committed by GitHub
28 changed files with 327 additions and 171 deletions

View File

@@ -14,30 +14,45 @@
App > Screen {
background: $surface;
color: $text-surface;
layers: sidebar;
color: $text-background;
background: $background;
color: $text;
layers: base sidebar;
layout: vertical;
overflow: hidden;
}
#tree-container {
overflow-y: auto;
height: 20;
margin: 1 2;
background: $surface;
padding: 1 2;
}
DirectoryTree {
padding: 0 1;
height: auto;
}
DataTable {
/*border:heavy red;*/
/* tint: 10% green; */
/* text-opacity: 50%; */
padding: 1;
margin: 1 2;
height: 12;
height: 24;
}
#sidebar {
color: $text-panel;
#sidebar {
background: $panel;
color: $text;
dock: left;
width: 30;
margin-bottom: 1;
offset-x: -100%;
transition: offset 500ms in_out_cubic;
@@ -51,7 +66,7 @@ DataTable {
#sidebar .title {
height: 1;
background: $primary-background-darken-1;
color: $text-primary-background-darken-1;
color: $text;
border-right: wide $background;
content-align: center middle;
}
@@ -59,35 +74,29 @@ DataTable {
#sidebar .user {
height: 8;
background: $panel-darken-1;
color: $text-panel-darken-1;
color: $text;
border-right: wide $background;
content-align: center middle;
}
#sidebar .content {
background: $panel-darken-2;
color: $text-surface;
color: $text;
border-right: wide $background;
content-align: center middle;
}
#header {
color: $text-secondary-background;
background: $secondary-background;
height: 1;
content-align: center middle;
dock: top;
}
Tweet {
height:12;
width: 100%;
margin: 0 2;
margin:0 2;
background: $panel;
color: $text-panel;
color: $text;
layout: vertical;
/* border: outer $primary; */
padding: 1;
@@ -96,14 +105,15 @@ Tweet {
/* scrollbar-gutter: stable; */
align-horizontal: center;
box-sizing: border-box;
}
.scrollable {
overflow-x: auto;
overflow-y: scroll;
margin: 1 2;
height: 20;
height: 24;
align-horizontal: center;
layout: vertical;
}
@@ -117,13 +127,13 @@ Tweet {
TweetHeader {
height:1;
background: $accent;
color: $text-accent
color: $text;
}
TweetBody {
width: 100%;
background: $panel;
color: $text-panel;
color: $text;
height: auto;
padding: 0 1 0 0;
}
@@ -134,7 +144,7 @@ Tweet.scroll-horizontal TweetBody {
.button {
background: $accent;
color: $text-accent;
color: $text;
width:20;
height: 3;
/* border-top: hidden $accent-darken-3; */
@@ -150,7 +160,7 @@ Tweet.scroll-horizontal TweetBody {
.button:hover {
background: $accent-lighten-1;
color: $text-accent-lighten-1;
color: $text;
width: 20;
height: 3;
border: tall $accent-darken-1;
@@ -162,7 +172,7 @@ Tweet.scroll-horizontal TweetBody {
}
#footer {
color: $text-accent;
color: $text;
background: $accent;
height: 1;
@@ -185,7 +195,7 @@ OptionItem {
OptionItem:hover {
height: 3;
color: $text-primary;
color: $text;
background: $primary-darken-1;
/* border-top: hkey $accent2-darken-3;
border-bottom: hkey $accent2-darken-3; */
@@ -197,7 +207,7 @@ Error {
width: 100%;
height:3;
background: $error;
color: $text-error;
color: $text;
border-top: tall $error-darken-2;
border-bottom: tall $error-darken-2;
@@ -210,7 +220,7 @@ Warning {
width: 100%;
height:3;
background: $warning;
color: $text-warning-fade-1;
color: $text;
border-top: tall $warning-darken-2;
border-bottom: tall $warning-darken-2;
@@ -224,7 +234,7 @@ Success {
height:auto;
box-sizing: border-box;
background: $success;
color: $text-success-fade-1;
color: $text;
border-top: hkey $success-darken-2;
border-bottom: hkey $success-darken-2;

View File

@@ -6,42 +6,55 @@ from rich.text import Text
from textual.app import App, ComposeResult
from textual.reactive import Reactive
from textual.widget import Widget
from textual.widgets import Static, DataTable
from textual.widgets import Static, DataTable, DirectoryTree, Header, Footer
from textual.layout import Container
CODE = '''
class Offset(NamedTuple):
"""A point defined by x and y coordinates."""
from __future__ import annotations
x: int = 0
y: int = 0
from typing import Iterable, TypeVar
@property
def is_origin(self) -> bool:
"""Check if the point is at the origin (0, 0)"""
return self == (0, 0)
T = TypeVar("T")
def __bool__(self) -> bool:
return self != (0, 0)
def __add__(self, other: object) -> Offset:
if isinstance(other, tuple):
_x, _y = self
x, y = other
return Offset(_x + x, _y + y)
return NotImplemented
def loop_first(values: Iterable[T]) -> Iterable[tuple[bool, T]]:
"""Iterate and generate a tuple with a flag for first value."""
iter_values = iter(values)
try:
value = next(iter_values)
except StopIteration:
return
yield True, value
for value in iter_values:
yield False, value
def __sub__(self, other: object) -> Offset:
if isinstance(other, tuple):
_x, _y = self
x, y = other
return Offset(_x - x, _y - y)
return NotImplemented
def __mul__(self, other: object) -> Offset:
if isinstance(other, (float, int)):
x, y = self
return Offset(int(x * other), int(y * other))
return NotImplemented
def loop_last(values: Iterable[T]) -> Iterable[tuple[bool, T]]:
"""Iterate and generate a tuple with a flag for last value."""
iter_values = iter(values)
try:
previous_value = next(iter_values)
except StopIteration:
return
for value in iter_values:
yield False, previous_value
previous_value = value
yield True, previous_value
def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]:
"""Iterate and generate a tuple with a flag for first and last value."""
iter_values = iter(values)
try:
previous_value = next(iter_values)
except StopIteration:
return
first = True
for value in iter_values:
yield first, False, previous_value
first = False
previous_value = value
yield first, True, previous_value
'''
@@ -96,25 +109,28 @@ class BasicApp(App, css_path="basic.css"):
def on_load(self):
"""Bind keys here."""
self.bind("s", "toggle_class('#sidebar', '-active')")
self.bind("s", "toggle_class('#sidebar', '-active')", description="Sidebar")
self.bind("d", "toggle_dark", description="Dark mode")
self.bind("q", "quit", description="Quit")
self.bind("f", "query_test", description="Query test")
def compose(self):
yield Header()
def compose(self) -> ComposeResult:
table = DataTable()
self.scroll_to_target = Tweet(TweetBody())
yield Static(
Text.from_markup(
"[b]This is a [u]Textual[/u] app, running in the terminal"
),
id="header",
)
yield from (
yield Container(
Tweet(TweetBody()),
Widget(
Static(Syntax(CODE, "python"), classes="code"),
Static(
Syntax(CODE, "python", line_numbers=True, indent_guides=True),
classes="code",
),
classes="scrollable",
),
table,
Widget(DirectoryTree("~/"), id="tree-container"),
Error(),
Tweet(TweetBody(), classes="scrollbar-size-custom"),
Warning(),
@@ -126,7 +142,6 @@ class BasicApp(App, css_path="basic.css"):
Tweet(TweetBody(), classes="scroll-horizontal"),
Tweet(TweetBody(), classes="scroll-horizontal"),
)
yield Widget(id="footer")
yield Widget(
Widget(classes="title"),
Widget(classes="user"),
@@ -136,6 +151,7 @@ class BasicApp(App, css_path="basic.css"):
Widget(classes="content"),
id="sidebar",
)
yield Footer()
table.add_column("Foo", width=20)
table.add_column("Bar", width=20)
@@ -147,12 +163,32 @@ class BasicApp(App, css_path="basic.css"):
for n in range(100):
table.add_row(*[f"Cell ([b]{n}[/b], {col})" for col in range(6)])
def on_mount(self):
self.sub_title = "Widget demo"
async def on_key(self, event) -> None:
await self.dispatch_key(event)
def key_d(self):
def action_toggle_dark(self):
self.dark = not self.dark
def action_query_test(self):
query = self.query("Tweet")
self.log(query)
self.log(query.nodes)
self.log(query)
self.log(query.nodes)
query.set_styles("outline: outer red;")
query = query.exclude(".scroll-horizontal")
self.log(query)
self.log(query.nodes)
# query = query.filter(".rubbish")
# self.log(query)
# self.log(query.first())
async def key_q(self):
await self.shutdown()

View File

@@ -24,7 +24,7 @@ Button {
padding: 0 1;
height: 100%;
background: $primary-lighten-2;
color: $text-primary-lighten-2;
color: $text;
}
#number-0 {

View File

@@ -1,3 +1,7 @@
Screen {
background: $surface-darken-1;
}
#tree-view {
display: none;
scrollbar-gutter: stable;
@@ -9,7 +13,7 @@ CodeBrowser.-show-tree #tree-view {
dock: left;
height: 100%;
max-width: 50%;
background: $panel;
background: $surface;
}
CodeBrowser{
@@ -17,6 +21,7 @@ CodeBrowser{
}
DirectoryTree {
padding-right: 1;
padding-right: 1;
}

View File

@@ -42,10 +42,10 @@ class CodeBrowser(App):
line_numbers=True,
word_wrap=True,
indent_guides=True,
theme="monokai",
theme="github-dark",
)
except Exception:
code_view.update(Traceback(theme="monokai", width=None))
code_view.update(Traceback(theme="github-dark", width=None))
self.sub_title = "ERROR"
else:
code_view.update(syntax)

View File

@@ -14,16 +14,11 @@
App > Screen {
background: $surface;
color: $text-surface;
layers: base sidebar;
color: $text-background;
background: $background;
color: $text;
layers: base sidebar;
layout: vertical;
overflow: hidden;
}
#tree-container {
@@ -52,9 +47,9 @@ DataTable {
height: 24;
}
#sidebar {
color: $text-panel;
#sidebar {
background: $panel;
color: $text;
dock: left;
width: 30;
margin-bottom: 1;
@@ -71,7 +66,7 @@ DataTable {
#sidebar .title {
height: 1;
background: $primary-background-darken-1;
color: $text-primary-background-darken-1;
color: $text;
border-right: wide $background;
content-align: center middle;
}
@@ -79,14 +74,14 @@ DataTable {
#sidebar .user {
height: 8;
background: $panel-darken-1;
color: $text-panel-darken-1;
color: $text;
border-right: wide $background;
content-align: center middle;
}
#sidebar .content {
background: $panel-darken-2;
color: $text-surface;
color: $text;
border-right: wide $background;
content-align: center middle;
}
@@ -101,7 +96,7 @@ Tweet {
margin:0 2;
background: $panel;
color: $text-panel;
color: $text;
layout: vertical;
/* border: outer $primary; */
padding: 1;
@@ -132,13 +127,13 @@ Tweet {
TweetHeader {
height:1;
background: $accent;
color: $text-accent
color: $text;
}
TweetBody {
width: 100%;
background: $panel;
color: $text-panel;
color: $text;
height: auto;
padding: 0 1 0 0;
}
@@ -149,7 +144,7 @@ Tweet.scroll-horizontal TweetBody {
.button {
background: $accent;
color: $text-accent;
color: $text;
width:20;
height: 3;
/* border-top: hidden $accent-darken-3; */
@@ -165,7 +160,7 @@ Tweet.scroll-horizontal TweetBody {
.button:hover {
background: $accent-lighten-1;
color: $text-accent-lighten-1;
color: $text;
width: 20;
height: 3;
border: tall $accent-darken-1;
@@ -177,7 +172,7 @@ Tweet.scroll-horizontal TweetBody {
}
#footer {
color: $text-accent;
color: $text;
background: $accent;
height: 1;
@@ -200,7 +195,7 @@ OptionItem {
OptionItem:hover {
height: 3;
color: $text-primary;
color: $text;
background: $primary-darken-1;
/* border-top: hkey $accent2-darken-3;
border-bottom: hkey $accent2-darken-3; */
@@ -212,7 +207,7 @@ Error {
width: 100%;
height:3;
background: $error;
color: $text-error;
color: $text;
border-top: tall $error-darken-2;
border-bottom: tall $error-darken-2;
@@ -225,7 +220,7 @@ Warning {
width: 100%;
height:3;
background: $warning;
color: $text-warning-fade-1;
color: $text;
border-top: tall $warning-darken-2;
border-bottom: tall $warning-darken-2;
@@ -239,7 +234,7 @@ Success {
height:auto;
box-sizing: border-box;
background: $success;
color: $text-success-fade-1;
color: $text;
border-top: hkey $success-darken-2;
border-bottom: hkey $success-darken-2;

View File

@@ -24,7 +24,7 @@ Button {
padding: 0 1;
height: 100%;
background: $primary-lighten-2;
color: $text-primary-lighten-2;
color: $text;
}
#number-0 {

View File

@@ -33,7 +33,7 @@ class CenterApp(App):
Static {
background: $panel;
color: $text-panel;
color: $text;
content-align: center middle;
}

23
sandbox/will/design.css Normal file
View File

@@ -0,0 +1,23 @@
Screen {
background: $surface;
}
Container {
height: auto;
background: $boost;
}
Panel {
height: auto;
background: $boost;
margin: 1 2;
}
Content {
background: $boost;
padding: 1 2;
margin: 1 2;
color: auto 95%;
}

35
sandbox/will/design.py Normal file
View File

@@ -0,0 +1,35 @@
from textual.app import App
from textual.layout import Container
from textual.widgets import Header, Footer, Static
class Content(Static):
pass
class Panel(Container):
pass
class Panel2(Container):
pass
class DesignApp(App):
BINDINGS = [("d", "toggle_dark", "Toggle dark mode")]
def compose(self):
yield Header()
yield Footer()
yield Container(
Content("content"),
Panel(
Content("more content"),
Content("more content"),
),
)
app = DesignApp(css_path="design.css")
if __name__ == "__main__":
app.run()

View File

@@ -250,7 +250,7 @@ class StylesCache:
line: Iterable[Segment]
# Draw top or bottom borders (A)
if (border_top and y == 0) or (border_bottom and y == height - 1):
border_color = background + (
border_color = base_background + (
border_top_color if y == 0 else border_bottom_color
)
box_segments = get_box(
@@ -271,9 +271,9 @@ class StylesCache:
pad_bottom and y >= height - gutter.bottom
):
background_style = from_color(bgcolor=background.rich_color)
left_style = from_color(color=border_left_color.rich_color)
left_style = from_color(color=(background + 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)
right_style = from_color(color=(background + border_right_color).rich_color)
right = get_box(border_right, inner, outer, right_style)[1][2]
if border_left and border_right:
line = [left, Segment(" " * (width - 2), background_style), right]
@@ -296,9 +296,13 @@ class StylesCache:
if border_left or border_right:
# Add left / right border
left_style = from_color((background + border_left_color).rich_color)
left_style = from_color(
(base_background + border_left_color).rich_color
)
left = get_box(border_left, inner, outer, left_style)[1][0]
right_style = from_color((background + border_right_color).rich_color)
right_style = from_color(
(base_background + border_right_color).rich_color
)
right = get_box(border_right, inner, outer, right_style)[1][2]
if border_left and border_right:
@@ -327,9 +331,9 @@ class StylesCache:
elif outline_left or outline_right:
# Lines in side outline
left_style = from_color((background + outline_left_color).rich_color)
left_style = from_color((base_background + outline_left_color).rich_color)
left = get_box(outline_left, inner, outer, left_style)[1][0]
right_style = from_color((background + outline_right_color).rich_color)
right_style = from_color((base_background + outline_right_color).rich_color)
right = get_box(outline_right, inner, outer, right_style)[1][2]
line = line_trim(list(line), outline_left != "", outline_right != "")
if outline_left and outline_right:

View File

@@ -146,7 +146,7 @@ class App(Generic[ReturnType], DOMNode):
DEFAULT_CSS = """
App {
background: $background;
color: $text-background;
color: $text;
}
"""
@@ -541,6 +541,10 @@ class App(Generic[ReturnType], DOMNode):
except Exception as error:
self._handle_exception(error)
def action_toggle_dark(self) -> None:
"""Action to toggle dark mode."""
self.dark = not self.dark
def action_screenshot(self, filename: str | None, path: str = "~/") -> None:
"""Action to save a screenshot."""
self.bell()

View File

@@ -41,7 +41,7 @@ class BorderApp(App):
border: solid $secondary;
height: auto;
background: $panel;
color: $text-panel;
color: $text;
}
"""

View File

@@ -13,12 +13,20 @@ EasingButtons {
#duration-input {
width: 30;
background: $boost;
padding: 0 1;
border: tall transparent;
}
#duration-input:focus {
border: tall $accent;
}
#inputs {
padding: 1;
height: auto;
dock: top;
background: $boost;
}
Bar {
@@ -36,6 +44,11 @@ Bar {
#opacity-widget {
padding: 1;
background: $warning;
color: $text-warning;
color: $text;
border: wide $background;
}
#label {
width: auto;
padding: 1;
}

View File

@@ -79,7 +79,9 @@ class EasingApp(App):
yield EasingButtons()
yield layout.Vertical(
layout.Vertical(Static("Animation Duration:"), duration_input, id="inputs"),
layout.Horizontal(
Static("Animation Duration:", id="label"), duration_input, id="inputs"
),
layout.Horizontal(
self.animated_bar,
layout.Container(self.opacity_widget, id="other"),

View File

@@ -249,6 +249,17 @@ class Color(NamedTuple):
else f"#{r:02X}{g:02X}{b:02X}{int(a*255):02X}"
)
@property
def hex6(self) -> str:
"""The color in CSS hex form, with 6 digits for RGB. Alpha is ignored.
Returns:
str: A CSS hex-style color, e.g. "#46b3de"
"""
r, g, b, a = self.clamped
return f"#{r:02X}{g:02X}{b:02X}"
@property
def css(self) -> str:
"""The color in CSS rgb or rgba form.
@@ -279,12 +290,13 @@ class Color(NamedTuple):
r, g, b, _ = self
return Color(r, g, b, alpha)
def blend(self, destination: Color, factor: float) -> Color:
def blend(self, destination: Color, factor: float, alpha: float = 1) -> Color:
"""Generate a new color between two colors.
Args:
destination (Color): Another color.
factor (float): A blend factor, 0 -> 1
factor (float): A blend factor, 0 -> 1.
alpha (float | None): New alpha for result. Defaults to 1.
Returns:
Color: A new color.
@@ -299,6 +311,7 @@ class Color(NamedTuple):
int(r1 + (r2 - r1) * factor),
int(g1 + (g2 - g1) * factor),
int(b1 + (b2 - b1) * factor),
alpha,
)
def __add__(self, other: object) -> Color:
@@ -396,29 +409,31 @@ class Color(NamedTuple):
return color
@lru_cache(maxsize=1024)
def darken(self, amount: float) -> Color:
def darken(self, amount: float, alpha: float | None = None) -> Color:
"""Darken the color by a given amount.
Args:
amount (float): Value between 0-1 to reduce luminance by.
alpha (float | None, optional): Alpha component for new color or None to copy alpha. Defaults to None.
Returns:
Color: New color.
"""
l, a, b = rgb_to_lab(self)
l -= amount * 100
return lab_to_rgb(Lab(l, a, b)).clamped
return lab_to_rgb(Lab(l, a, b), self.a if alpha is None else alpha).clamped
def lighten(self, amount: float) -> Color:
def lighten(self, amount: float, alpha: float | None = None) -> Color:
"""Lighten the color by a given amount.
Args:
amount (float): Value between 0-1 to increase luminance by.
alpha (float | None, optional): Alpha component for new color or None to copy alpha. Defaults to None.
Returns:
Color: New color.
"""
return self.darken(-amount)
return self.darken(-amount, alpha)
@lru_cache(maxsize=1024)
def get_contrast_text(self, alpha=0.95) -> Color:
@@ -508,7 +523,7 @@ def rgb_to_lab(rgb: Color) -> Lab:
return Lab(116 * y - 16, 500 * (x - y), 200 * (y - z))
def lab_to_rgb(lab: Lab) -> Color:
def lab_to_rgb(lab: Lab, alpha: float = 1.0) -> Color:
"""Convert a CIE-L*ab color to RGB.
Uses the standard RGB color space with a D65/2⁰ standard illuminant.
@@ -533,7 +548,7 @@ def lab_to_rgb(lab: Lab) -> Color:
g = 1.055 * pow(g, 1 / 2.4) - 0.055 if g > 0.0031308 else 12.92 * g
b = 1.055 * pow(b, 1 / 2.4) - 0.055 if b > 0.0031308 else 12.92 * b
return Color(int(r * 255), int(g * 255), int(b * 255))
return Color(int(r * 255), int(g * 255), int(b * 255), alpha)
if __name__ == "__main__":

View File

@@ -102,6 +102,13 @@ class IntegerProperty(GenericProperty[int, int]):
raise StyleValueError(f"Expected a number here, got f{value}")
class BooleanProperty(GenericProperty[bool, bool]):
"""A property that requires a True or False value."""
def validate_value(self, value: object) -> bool:
return bool(value)
class ScalarProperty:
"""Descriptor for getting and setting scalar properties. Scalars are numeric values with a unit, e.g. "50vh"."""

View File

@@ -580,7 +580,9 @@ class StylesBuilder:
alpha: float | None = None
for token in tokens:
if token.name == "scalar":
if name == "color" and token.name == "token" and token.value == "auto":
self.styles._rules["auto_color"] = True
elif token.name == "scalar":
alpha_scalar = Scalar.parse(token.value)
if alpha_scalar.unit != Unit.PERCENT:
self.error(name, token, "alpha must be given as a percentage.")
@@ -598,9 +600,10 @@ class StylesBuilder:
else:
self.error(name, token, color_property_help_text(name, context="css"))
if color is not None:
if color is not None or alpha is not None:
if alpha is not None:
color = color.with_alpha(alpha)
color = (color or Color(255, 255, 255)).with_alpha(alpha)
self.styles._rules[name] = color
process_tint = process_color

View File

@@ -16,6 +16,7 @@ from ..color import Color
from ..geometry import Offset, Spacing
from ._style_properties import (
AlignProperty,
BooleanProperty,
BorderProperty,
BoxProperty,
ColorProperty,
@@ -83,6 +84,7 @@ class RulesMap(TypedDict, total=False):
visibility: Visibility
layout: "Layout"
auto_color: bool
color: Color
background: Color
text_style: Style
@@ -183,6 +185,7 @@ class StylesBase(ABC):
"min_height",
"max_width",
"max_height",
"auto_color",
"color",
"background",
"opacity",
@@ -202,6 +205,7 @@ class StylesBase(ABC):
visibility = StringEnumProperty(VALID_VISIBILITY, "visible")
layout = LayoutProperty()
auto_color = BooleanProperty(default=False)
color = ColorProperty(Color(255, 255, 255))
background = ColorProperty(Color(0, 0, 0, 0), background=True)
text_style = StyleFlagsProperty()

View File

@@ -15,7 +15,7 @@ NUMBER_OF_SHADES = 3
# Where no content exists
DEFAULT_DARK_BACKGROUND = "#000000"
# What text usually goes on top off
DEFAULT_DARK_SURFACE = "#292929"
DEFAULT_DARK_SURFACE = "#121212"
DEFAULT_LIGHT_SURFACE = "#f5f5f5"
DEFAULT_LIGHT_BACKGROUND = "#efefef"
@@ -38,6 +38,7 @@ class ColorSystem:
"secondary-background",
"surface",
"panel",
"boost",
"warning",
"error",
"success",
@@ -55,6 +56,7 @@ class ColorSystem:
background: str | None = None,
surface: str | None = None,
panel: str | None = None,
boost: str | None = None,
dark: bool = False,
luminosity_spread: float = 0.15,
text_alpha: float = 0.95,
@@ -73,6 +75,7 @@ class ColorSystem:
self.background = parse(background)
self.surface = parse(surface)
self.panel = parse(panel)
self.boost = parse(boost)
self._dark = dark
self._luminosity_spread = luminosity_spread
self._text_alpha = text_alpha
@@ -126,6 +129,8 @@ class ColorSystem:
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]]:
@@ -153,6 +158,7 @@ class ColorSystem:
("secondary-background", secondary),
("background", background),
("panel", panel),
("boost", boost),
("surface", surface),
("warning", warning),
("error", error),
@@ -178,14 +184,10 @@ class ColorSystem:
else:
shade_color = color.lighten(luminosity_delta)
colors[f"{name}{shade_name}"] = shade_color.hex
for fade in range(3):
text_color = shade_color.get_contrast_text(text_alpha)
if fade > 0:
text_color = text_color.blend(shade_color, fade * 0.1 + 0.15)
on_name = f"text-{name}{shade_name}-fade-{fade}"
else:
on_name = f"text-{name}{shade_name}"
colors[on_name] = text_color.hex
colors["text"] = "auto 95%"
colors["text-muted"] = "auto 80%"
colors["text-disabled"] = "auto 60%"
return colors
@@ -206,16 +208,12 @@ def show_design(light: ColorSystem, dark: ColorSystem) -> Table:
def make_shades(system: ColorSystem):
colors = system.generate()
for name in system.shades:
background = colors[name]
foreground = colors[f"text-{name}"]
text = Text(f"{background} ", style=f"{foreground} on {background}")
for fade in range(3):
foreground = colors[
f"text-{name}-fade-{fade}" if fade else f"text-{name}"
]
text.append(f"{name} ", style=f"{foreground} on {background}")
background = Color.parse(colors[name]).with_alpha(1.0)
foreground = background + background.get_contrast_text(0.9)
yield Padding(text, 1, style=f"{foreground} on {background}")
text = Text(name)
yield Padding(text, 1, style=f"{foreground.hex6} on {background.hex6}")
table = Table(box=None, expand=True)
table.add_column("Light", justify="center")

View File

@@ -497,6 +497,8 @@ class DOMNode(MessagePump):
if styles.has_rule("color"):
color = styles.color
style += styles.text_style
if styles.has_rule("auto_color") and styles.auto_color:
color = background.get_contrast_text(color.a)
style += Style.from_color(
(background + color).rich_color, background.rich_color
)
@@ -534,7 +536,11 @@ class DOMNode(MessagePump):
background += styles.background
if styles.has_rule("color"):
base_color = color
color = styles.color
if styles.auto_color:
color = background.get_contrast_text(color.a)
else:
color = styles.color
return (base_background, base_color, background, color)
@property

View File

@@ -33,6 +33,7 @@ class Screen(Widget):
Screen {
layout: vertical;
overflow-y: auto;
background: $surface;
}
"""

View File

@@ -36,7 +36,7 @@ class Button(Widget, can_focus=True):
width: auto;
height: 3;
background: $panel;
color: $text-panel;
color: auto;
border: none;
border-top: tall $panel-lighten-2;
border-bottom: tall $panel-darken-3;
@@ -56,7 +56,7 @@ class Button(Widget, can_focus=True):
Button:hover {
border-top: tall $panel-lighten-1;
background: $panel-darken-2;
color: $text-panel-darken-2;
color: $text;
}
Button.-active {
@@ -69,7 +69,7 @@ class Button(Widget, can_focus=True):
/* Primary variant */
Button.-primary {
background: $primary;
color: $text-primary;
color: $text;
border-top: tall $primary-lighten-3;
border-bottom: tall $primary-darken-3;
@@ -77,8 +77,8 @@ class Button(Widget, can_focus=True):
Button.-primary:hover {
background: $primary-darken-2;
color: $text-primary-darken-2;
color: $text;
border-top: tall $primary-lighten-2;
}
Button.-primary.-active {
@@ -91,14 +91,14 @@ class Button(Widget, can_focus=True):
/* Success variant */
Button.-success {
background: $success;
color: $text-success;
color: $text;
border-top: tall $success-lighten-2;
border-bottom: tall $success-darken-3;
}
Button.-success:hover {
background: $success-darken-2;
color: $text-success-darken-2;
color: $text;
}
Button.-success.-active {
@@ -111,14 +111,14 @@ class Button(Widget, can_focus=True):
/* Warning variant */
Button.-warning {
background: $warning;
color: $text-warning;
color: $text;
border-top: tall $warning-lighten-2;
border-bottom: tall $warning-darken-3;
}
Button.-warning:hover {
background: $warning-darken-2;
color: $text-warning-darken-1;
color: $text;
}
@@ -132,7 +132,7 @@ class Button(Widget, can_focus=True):
/* Error variant */
Button.-error {
background: $error;
color: $text-error;
color: $text;
border-top: tall $error-lighten-2;
border-bottom: tall $error-darken-3;
@@ -140,7 +140,7 @@ class Button(Widget, can_focus=True):
Button.-error:hover {
background: $error-darken-1;
color: $text-error-darken-2;
color: $text;
}

View File

@@ -109,17 +109,17 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
DEFAULT_CSS = """
DataTable {
background: $surface;
color: $text-surface;
color: $text;
}
DataTable > .datatable--header {
text-style: bold;
background: $primary;
color: $text-primary;
color: $text;
}
DataTable > .datatable--fixed {
text-style: bold;
background: $primary;
color: $text-primary;
color: $text;
}
DataTable > .datatable--odd-row {
@@ -132,7 +132,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
DataTable > .datatable--cursor {
background: $secondary;
color: $text-secondary;
color: $text;
}
.-dark-mode DataTable > .datatable--even-row {

View File

@@ -74,7 +74,6 @@ class DirectoryTree(TreeControl[DirEntry]):
label.stylize("bold")
icon = "📂" if expanded else "📁"
else:
icon = "📄"
label.highlight_regex(r"\..*$", "italic")

View File

@@ -16,25 +16,22 @@ class Footer(Widget):
DEFAULT_CSS = """
Footer {
background: $accent;
color: $text-accent;
color: $text;
dock: bottom;
height: 1;
}
Footer > .footer--highlight {
background: $accent-darken-1;
color: $text-accent-darken-1;
background: $accent-darken-1;
}
Footer > .footer--highlight-key {
background: $secondary;
color: $text-secondary;
background: $secondary;
text-style: bold;
}
Footer > .footer--key {
text-style: bold;
background: $accent-darken-2;
color: $text-accent-darken-2;
background: $accent-darken-2;
}
"""

View File

@@ -34,7 +34,7 @@ class HeaderClock(Widget):
width: 10;
padding: 0 1;
background: $secondary-background-lighten-1;
color: $text-secondary-background;
color: $text;
text-opacity: 85%;
content-align: center middle;
}
@@ -76,7 +76,7 @@ class Header(Widget):
dock: top;
width: 100%;
background: $secondary-background;
color: $text-secondary-background;
color: $text;
height: 1;
}
Header.tall {

View File

@@ -164,8 +164,8 @@ class TreeNode(Generic[NodeDataType]):
class TreeControl(Generic[NodeDataType], Static, can_focus=True):
DEFAULT_CSS = """
TreeControl {
background: $panel;
color: $text-panel;
background: $surface;
color: $text;
height: auto;
width: 100%;
}
@@ -176,8 +176,7 @@ class TreeControl(Generic[NodeDataType], Static, can_focus=True):
TreeControl > .tree--guides-highlight {
color: $success;
text-style: uu;
text-style: uu;
}
TreeControl > .tree--guides-cursor {
@@ -186,12 +185,12 @@ class TreeControl(Generic[NodeDataType], Static, can_focus=True):
}
TreeControl > .tree--labels {
color: $text-panel;
color: $text;
}
TreeControl > .tree--cursor {
background: $secondary;
color: $text-secondary;
color: $text;
}
"""