mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Add support for HSL and HSLA
This commit is contained in:
committed by
Will McGugan
parent
566eb837b7
commit
a97a2c6bfd
11
sandbox/hsl.py
Normal file
11
sandbox/hsl.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
class HSLApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static(classes="box")
|
||||
|
||||
|
||||
app = HSLApp(css_path="hsl.scss", watch_css=True)
|
||||
app.run()
|
||||
5
sandbox/hsl.scss
Normal file
5
sandbox/hsl.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
.box {
|
||||
height: 1fr;
|
||||
/*background: rgb(180,50, 50);*/
|
||||
background: hsl(180,50%, 50%);
|
||||
}
|
||||
@@ -23,6 +23,8 @@ from rich.color import Color as RichColor
|
||||
from rich.style import Style
|
||||
from rich.text import Text
|
||||
|
||||
from textual.css.scalar import percentage_string_to_float
|
||||
from textual.css.tokenize import COMMA, OPEN_BRACE, CLOSE_BRACE, DECIMAL, PERCENT
|
||||
from textual.suggestions import get_suggestion
|
||||
from ._color_constants import COLOR_NAME_TO_RGB
|
||||
from .geometry import clamp
|
||||
@@ -53,13 +55,15 @@ class Lab(NamedTuple):
|
||||
|
||||
|
||||
RE_COLOR = re.compile(
|
||||
r"""^
|
||||
\#([0-9a-fA-F]{3})$|
|
||||
\#([0-9a-fA-F]{4})$|
|
||||
\#([0-9a-fA-F]{6})$|
|
||||
\#([0-9a-fA-F]{8})$|
|
||||
rgb\((\-?\d+\.?\d*,\-?\d+\.?\d*,\-?\d+\.?\d*)\)$|
|
||||
rgba\((\-?\d+\.?\d*,\-?\d+\.?\d*,\-?\d+\.?\d*,\-?\d+\.?\d*)\)$
|
||||
rf"""^
|
||||
\#([0-9a-fA-F]{{3}})$|
|
||||
\#([0-9a-fA-F]{{4}})$|
|
||||
\#([0-9a-fA-F]{{6}})$|
|
||||
\#([0-9a-fA-F]{{8}})$|
|
||||
rgb{OPEN_BRACE}({DECIMAL}{COMMA}{DECIMAL}{COMMA}{DECIMAL}){CLOSE_BRACE}$|
|
||||
rgba{OPEN_BRACE}({DECIMAL}{COMMA}{DECIMAL}{COMMA}{DECIMAL}{COMMA}{DECIMAL}){CLOSE_BRACE}$|
|
||||
hsl{OPEN_BRACE}({DECIMAL}{COMMA}{PERCENT}{COMMA}{PERCENT}){CLOSE_BRACE}$|
|
||||
hsla{OPEN_BRACE}({DECIMAL}{COMMA}{PERCENT}{COMMA}{PERCENT}{COMMA}{DECIMAL}){CLOSE_BRACE}$|
|
||||
""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
@@ -123,7 +127,9 @@ class Color(NamedTuple):
|
||||
Returns:
|
||||
Color: A new color.
|
||||
"""
|
||||
print("A")
|
||||
r, g, b = hls_to_rgb(h, l, s)
|
||||
print("B")
|
||||
return cls(int(r * 255 + 0.5), int(g * 255 + 0.5), int(b * 255 + 0.5))
|
||||
|
||||
def __rich__(self) -> Text:
|
||||
@@ -296,6 +302,8 @@ class Color(NamedTuple):
|
||||
rgba_hex,
|
||||
rgb,
|
||||
rgba,
|
||||
hsl,
|
||||
hsla,
|
||||
) = color_match.groups()
|
||||
|
||||
if rgb_hex_triple is not None:
|
||||
@@ -328,6 +336,19 @@ class Color(NamedTuple):
|
||||
clamp(int(float_b), 0, 255),
|
||||
clamp(float_a, 0.0, 1.0),
|
||||
)
|
||||
elif hsl is not None:
|
||||
h, s, l = [value.strip() for value in hsl.split(",")]
|
||||
h = clamp(int(h), 0, 360) / 360
|
||||
s = percentage_string_to_float(s)
|
||||
l = percentage_string_to_float(l)
|
||||
color = Color.from_hls(h, l, s)
|
||||
elif hsla is not None:
|
||||
h, s, l, a = [value.strip() for value in hsl.split(",")]
|
||||
h = clamp(h, 0, 360)
|
||||
s = percentage_string_to_float(s)
|
||||
l = percentage_string_to_float(l)
|
||||
a = clamp(a, 0.0, 1.0)
|
||||
color = Color.from_hls(h, l, s).with_alpha(a)
|
||||
else:
|
||||
raise AssertionError("Can't get here if RE_COLOR matches")
|
||||
return color
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from typing import cast, Iterable, NoReturn, Sequence
|
||||
|
||||
@@ -40,7 +42,14 @@ from .constants import (
|
||||
)
|
||||
from .errors import DeclarationError, StyleValueError
|
||||
from .model import Declaration
|
||||
from .scalar import Scalar, ScalarOffset, Unit, ScalarError, ScalarParseError
|
||||
from .scalar import (
|
||||
Scalar,
|
||||
ScalarOffset,
|
||||
Unit,
|
||||
ScalarError,
|
||||
ScalarParseError,
|
||||
percentage_string_to_float,
|
||||
)
|
||||
from .styles import DockGroup, Styles
|
||||
from .tokenize import Token
|
||||
from .transition import Transition
|
||||
@@ -333,9 +342,8 @@ class StylesBuilder:
|
||||
token_name = token.name
|
||||
value = token.value
|
||||
if token_name == "scalar" and value.endswith("%"):
|
||||
percentage = value[:-1]
|
||||
try:
|
||||
opacity = clamp(float(percentage) / 100, 0, 1)
|
||||
opacity = percentage_string_to_float(value)
|
||||
self.styles.set_rule(name, opacity)
|
||||
except ValueError:
|
||||
error = True
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Iterable, NamedTuple
|
||||
|
||||
import rich.repr
|
||||
|
||||
from ..geometry import Offset, Size
|
||||
from ..geometry import Offset, Size, clamp
|
||||
|
||||
|
||||
class ScalarError(Exception):
|
||||
@@ -334,6 +334,17 @@ class ScalarOffset(NamedTuple):
|
||||
|
||||
NULL_SCALAR = ScalarOffset(Scalar.from_number(0), Scalar.from_number(0))
|
||||
|
||||
|
||||
def percentage_string_to_float(string: str) -> float:
|
||||
string = string.strip()
|
||||
if string.endswith("%"):
|
||||
percentage = string[:-1]
|
||||
float_percentage = clamp(float(percentage) / 100, 0, 1)
|
||||
else:
|
||||
float_percentage = float(string)
|
||||
return float_percentage
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(Scalar.parse("3.14fr"))
|
||||
s = Scalar.parse("23")
|
||||
|
||||
@@ -6,11 +6,21 @@ from typing import Iterable
|
||||
|
||||
from textual.css.tokenizer import Expect, Tokenizer, Token
|
||||
|
||||
PERCENT = r"-?\d+\.?\d*%"
|
||||
DECIMAL = r"-?\d+\.?\d*"
|
||||
COMMA = r",\s*"
|
||||
OPEN_BRACE = r"\(\s*"
|
||||
CLOSE_BRACE = r"\s*\)"
|
||||
|
||||
HEX_COLOR = r"\#[0-9a-fA-F]{8}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{4}|\#[0-9a-fA-F]{3}"
|
||||
RGB_COLOR = rf"rgb{OPEN_BRACE}{DECIMAL}{COMMA}{DECIMAL}{COMMA}{DECIMAL}{CLOSE_BRACE}|rgba{OPEN_BRACE}{DECIMAL}{COMMA}{DECIMAL}{COMMA}{DECIMAL}{COMMA}{DECIMAL}{CLOSE_BRACE}"
|
||||
HSL_COLOR = rf"hsl{OPEN_BRACE}{DECIMAL}{COMMA}{PERCENT}{COMMA}{PERCENT}{CLOSE_BRACE}|hsla{OPEN_BRACE}{DECIMAL}{COMMA}{PERCENT}{COMMA}{PERCENT}{COMMA}{DECIMAL}{CLOSE_BRACE}"
|
||||
|
||||
COMMENT_START = r"\/\*"
|
||||
SCALAR = r"\-?\d+\.?\d*(?:fr|%|w|h|vw|vh)"
|
||||
SCALAR = rf"{DECIMAL}(?:fr|%|w|h|vw|vh)"
|
||||
DURATION = r"\d+\.?\d*(?:ms|s)"
|
||||
NUMBER = r"\-?\d+\.?\d*"
|
||||
COLOR = r"\#[0-9a-fA-F]{8}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{4}|\#[0-9a-fA-F]{3}|rgb\(\-?\d+\.?\d*,\-?\d+\.?\d*,\-?\d+\.?\d*\)|rgba\(\-?\d+\.?\d*,\-?\d+\.?\d*,\-?\d+\.?\d*,\-?\d+\.?\d*\)"
|
||||
COLOR = f"{HEX_COLOR}|{RGB_COLOR}|{HSL_COLOR}"
|
||||
KEY_VALUE = r"[a-zA-Z_-][a-zA-Z0-9_-]*=[0-9a-zA-Z_\-\/]+"
|
||||
TOKEN = "[a-zA-Z][a-zA-Z0-9_-]*"
|
||||
STRING = r"\".*?\""
|
||||
|
||||
Reference in New Issue
Block a user