scalar resolve

This commit is contained in:
Will McGugan
2021-11-26 13:24:40 +00:00
parent ad8f7e893a
commit 6706746548
4 changed files with 73 additions and 13 deletions

View File

@@ -18,8 +18,11 @@ if TYPE_CHECKING:
class ScalarProperty:
def __init__(self, units: set[Unit] | None = None) -> None:
def __init__(
self, units: set[Unit] | None = None, percent_unit: Unit = Unit.WIDTH
) -> None:
self.units: set[Unit] = units or {*UNIT_SYMBOL}
self.percent_unit = percent_unit
super().__init__()
def __set_name__(self, owner: Styles, name: str) -> None:
@@ -53,6 +56,8 @@ class ScalarProperty:
raise StyleValueError(
f"{self.name} units must be one of {friendly_list(get_symbols(self.units))}"
)
if new_value is not None and new_value.is_percent:
new_value = Scalar(new_value.value, self.percent_unit)
setattr(obj, self.internal_name, new_value)
return value

View File

@@ -94,7 +94,7 @@ class StylesBuilder:
def process_visibility(self, name: str, tokens: list[Token]) -> None:
for token in tokens:
_, _, location, name, value = token
name, value, _, _, location = token
if name == "token":
value = value.lower()
if value in VALID_VISIBILITY:
@@ -114,12 +114,15 @@ class StylesBuilder:
for token in tokens:
(token_name, value, _, _, location) = token
if token_name == "scalar":
append(int(value))
try:
append(int(value))
except ValueError:
self.error(name, token, f"expected a number here; found {value!r}")
else:
self.error(name, token, f"unexpected token {value!r} in declaration")
if len(space) not in (1, 2, 4):
self.error(
name, tokens[0], f"1, 2, or 4 values expected (received {len(space)})"
name, tokens[0], f"1, 2, or 4 values expected; received {len(space)}"
)
setattr(
self.styles,

View File

@@ -4,6 +4,18 @@ from enum import Enum, unique
import re
from typing import Iterable, NamedTuple
import rich.repr
from ..geometry import Offset
class ScalarResolveError(Exception):
pass
class ScalarParseError(Exception):
pass
@unique
class Unit(Enum):
@@ -31,6 +43,15 @@ SYMBOL_UNIT = {v: k for k, v in UNIT_SYMBOL.items()}
_MATCH_SCALAR = re.compile(r"^(\d+\.?\d*)(fr|%|w|h|vw|vh)?$").match
RESOLVE_MAP = {
Unit.CELLS: lambda value, size, viewport: value,
Unit.WIDTH: lambda value, size, viewport: size[0],
Unit.HEIGHT: lambda value, size, viewport: size[1],
Unit.VIEW_WIDTH: lambda value, size, viewport: viewport[0],
Unit.VIEW_HEIGHT: lambda value, size, viewport: viewport[1],
}
def get_symbols(units: Iterable[Unit]) -> list[str]:
"""Get symbols for an iterable of units.
@@ -43,10 +64,6 @@ def get_symbols(units: Iterable[Unit]) -> list[str]:
return [UNIT_SYMBOL[unit] for unit in units]
class ScalarParseError(Exception):
pass
class Scalar(NamedTuple):
"""A numeric value and a unit."""
@@ -57,6 +74,10 @@ class Scalar(NamedTuple):
value, _unit = self
return f"{int(value) if value.is_integer() else value}{self.symbol}"
@property
def is_percent(self) -> bool:
return self.unit == Unit.PERCENT
@property
def cells(self) -> int | None:
value, unit = self
@@ -91,6 +112,37 @@ class Scalar(NamedTuple):
scalar = cls(float(value), SYMBOL_UNIT[unit_name or ""])
return scalar
def resolve(
self,
size: tuple[int, int],
viewport: tuple[int, int],
percent_unit: Unit = Unit.WIDTH,
) -> float:
value, unit = self
if unit == Unit.PERCENT:
unit = percent_unit
try:
return RESOLVE_MAP[unit](value, size, viewport)
except KeyError:
raise ScalarResolveError("unable to resolve {self!r}")
@rich.repr.auto(angular=True)
class ScalarOffset(NamedTuple):
x: Scalar
y: Scalar
def __rich_repr__(self) -> rich.repr.Result:
yield str(self.x)
yield str(self.y)
def resolve(self, size: tuple[int, int], viewport: tuple[int, int]) -> Offset:
x, y = self
return Offset(
round(x.resolve(size, viewport, percent_unit=Unit.WIDTH)),
round(y.resolve(size, viewport, percent_unit=Unit.HEIGHT)),
)
if __name__ == "__main__":

View File

@@ -18,7 +18,7 @@ from .constants import (
NULL_SPACING,
)
from ..geometry import NULL_OFFSET, Offset, Spacing
from .scalar import Scalar
from .scalar import Scalar, Unit
from ._style_properties import (
BorderProperty,
BoxProperty,
@@ -115,10 +115,10 @@ class Styles:
outline_bottom = BoxProperty()
outline_left = BoxProperty()
width = ScalarProperty()
height = ScalarProperty()
min_width = ScalarProperty()
min_height = ScalarProperty()
width = ScalarProperty(percent_unit=Unit.WIDTH)
height = ScalarProperty(percent_unit=Unit.HEIGHT)
min_width = ScalarProperty(percent_unit=Unit.WIDTH)
min_height = ScalarProperty(percent_unit=Unit.HEIGHT)
dock_group = DockGroupProperty()
docks = DocksProperty()