469 lines
12 KiB
Python
469 lines
12 KiB
Python
|
from __future__ import annotations
|
||
|
|
||
|
import enum
|
||
|
import typing
|
||
|
|
||
|
if typing.TYPE_CHECKING:
|
||
|
from typing_extensions import Literal
|
||
|
|
||
|
|
||
|
# define some names for these constants to avoid misspellings in the source
|
||
|
# and to document the constant strings we are using
|
||
|
|
||
|
|
||
|
class Sizing(str, enum.Enum):
|
||
|
"""Widget sizing methods."""
|
||
|
|
||
|
FLOW = "flow"
|
||
|
BOX = "box"
|
||
|
FIXED = "fixed"
|
||
|
|
||
|
|
||
|
class Align(str, enum.Enum):
|
||
|
"""Text alignment modes"""
|
||
|
|
||
|
LEFT = "left"
|
||
|
RIGHT = "right"
|
||
|
CENTER = "center"
|
||
|
|
||
|
|
||
|
class VAlign(str, enum.Enum):
|
||
|
"""Filler alignment"""
|
||
|
|
||
|
TOP = "top"
|
||
|
MIDDLE = "middle"
|
||
|
BOTTOM = "bottom"
|
||
|
|
||
|
|
||
|
class WrapMode(str, enum.Enum):
|
||
|
"""Text wrapping modes"""
|
||
|
|
||
|
SPACE = "space"
|
||
|
ANY = "any"
|
||
|
CLIP = "clip"
|
||
|
ELLIPSIS = "ellipsis"
|
||
|
|
||
|
|
||
|
class WHSettings(str, enum.Enum):
|
||
|
"""Width and Height settings"""
|
||
|
|
||
|
PACK = "pack"
|
||
|
GIVEN = "given"
|
||
|
RELATIVE = "relative"
|
||
|
WEIGHT = "weight"
|
||
|
CLIP = "clip" # Used as "given" for widgets with fixed width (with clipping part of it)
|
||
|
FLOW = "flow" # Used as pack for flow widgets
|
||
|
|
||
|
|
||
|
RELATIVE_100 = (WHSettings.RELATIVE, 100)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_align(
|
||
|
align: Literal["left", "center", "right"] | Align,
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Align, None]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_align(
|
||
|
align: tuple[Literal["relative", WHSettings.RELATIVE], int],
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
def normalize_align(
|
||
|
align: Literal["left", "center", "right"] | Align | tuple[Literal["relative", WHSettings.RELATIVE], int],
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Align, None] | tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
"""
|
||
|
Split align into (align_type, align_amount). Raise exception err
|
||
|
if align doesn't match a valid alignment.
|
||
|
"""
|
||
|
if align in (Align.LEFT, Align.CENTER, Align.RIGHT):
|
||
|
return (Align(align), None)
|
||
|
|
||
|
if isinstance(align, tuple) and len(align) == 2 and align[0] == WHSettings.RELATIVE:
|
||
|
align_type, align_amount = align
|
||
|
return (WHSettings(align_type), align_amount)
|
||
|
|
||
|
raise err(
|
||
|
f"align value {align!r} is not one of 'left', 'center', 'right', ('relative', percentage 0=left 100=right)"
|
||
|
)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_align(
|
||
|
align_type: Literal["relative", WHSettings.RELATIVE],
|
||
|
align_amount: int,
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_align(
|
||
|
align_type: Literal["relative", WHSettings.RELATIVE],
|
||
|
align_amount: None,
|
||
|
) -> typing.NoReturn:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_align(
|
||
|
align_type: Literal["left", "center", "right"] | Align,
|
||
|
align_amount: int | None,
|
||
|
) -> Align:
|
||
|
...
|
||
|
|
||
|
|
||
|
def simplify_align(
|
||
|
align_type: Literal["left", "center", "right", "relative", WHSettings.RELATIVE] | Align,
|
||
|
align_amount: int | None,
|
||
|
) -> Align | tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
"""
|
||
|
Recombine (align_type, align_amount) into an align value.
|
||
|
Inverse of normalize_align.
|
||
|
"""
|
||
|
if align_type == WHSettings.RELATIVE:
|
||
|
if not isinstance(align_amount, int):
|
||
|
raise TypeError(align_amount)
|
||
|
|
||
|
return (WHSettings(align_type), align_amount)
|
||
|
return Align(align_type)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_valign(
|
||
|
valign: Literal["top", "middle", "bottom"] | VAlign,
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[VAlign, None]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_valign(
|
||
|
valign: tuple[Literal["relative", WHSettings.RELATIVE], int],
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
def normalize_valign(
|
||
|
valign: Literal["top", "middle", "bottom"] | VAlign | tuple[Literal["relative", WHSettings.RELATIVE], int],
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[VAlign, None] | tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
"""
|
||
|
Split align into (valign_type, valign_amount). Raise exception err
|
||
|
if align doesn't match a valid alignment.
|
||
|
"""
|
||
|
if valign in (VAlign.TOP, VAlign.MIDDLE, VAlign.BOTTOM):
|
||
|
return (VAlign(valign), None)
|
||
|
|
||
|
if isinstance(valign, tuple) and len(valign) == 2 and valign[0] == WHSettings.RELATIVE:
|
||
|
valign_type, valign_amount = valign
|
||
|
return (WHSettings(valign_type), valign_amount)
|
||
|
|
||
|
raise err(
|
||
|
f"valign value {valign!r} is not one of 'top', 'middle', 'bottom', ('relative', percentage 0=left 100=right)"
|
||
|
)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_valign(
|
||
|
valign_type: Literal["top", "middle", "bottom"] | VAlign,
|
||
|
valign_amount: int | None,
|
||
|
) -> VAlign:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_valign(
|
||
|
valign_type: Literal["relative", WHSettings.RELATIVE],
|
||
|
valign_amount: int,
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_valign(
|
||
|
valign_type: Literal["relative", WHSettings.RELATIVE],
|
||
|
valign_amount: None,
|
||
|
) -> typing.NoReturn:
|
||
|
...
|
||
|
|
||
|
|
||
|
def simplify_valign(
|
||
|
valign_type: Literal["top", "middle", "bottom", "relative", WHSettings.RELATIVE] | VAlign,
|
||
|
valign_amount: int | None,
|
||
|
) -> VAlign | tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
"""
|
||
|
Recombine (valign_type, valign_amount) into an valign value.
|
||
|
Inverse of normalize_valign.
|
||
|
"""
|
||
|
if valign_type == WHSettings.RELATIVE:
|
||
|
if not isinstance(valign_amount, int):
|
||
|
raise TypeError(valign_amount)
|
||
|
return (WHSettings(valign_type), valign_amount)
|
||
|
return VAlign(valign_type)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_width(
|
||
|
width: (Literal["clip", "pack", WHSettings.CLIP, WHSettings.PACK]),
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.CLIP, WHSettings.PACK], None]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_width(
|
||
|
width: int,
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.GIVEN], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_width(
|
||
|
width: (tuple[Literal["relative", WHSettings.RELATIVE], int]),
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_width(
|
||
|
width: (tuple[Literal["weight", WHSettings.WEIGHT], int]),
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.WEIGHT], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
def normalize_width(
|
||
|
width: (
|
||
|
Literal["clip", "pack", WHSettings.CLIP, WHSettings.PACK]
|
||
|
| int
|
||
|
| tuple[Literal["relative", "weight", WHSettings.RELATIVE, WHSettings.WEIGHT], int]
|
||
|
),
|
||
|
err: type[BaseException],
|
||
|
) -> (
|
||
|
tuple[Literal[WHSettings.CLIP, WHSettings.PACK], None]
|
||
|
| tuple[Literal[WHSettings.GIVEN, WHSettings.RELATIVE, WHSettings.WEIGHT], int]
|
||
|
):
|
||
|
"""
|
||
|
Split width into (width_type, width_amount). Raise exception err
|
||
|
if width doesn't match a valid alignment.
|
||
|
"""
|
||
|
if width in (WHSettings.CLIP, WHSettings.PACK):
|
||
|
return (WHSettings(width), None)
|
||
|
|
||
|
if isinstance(width, int):
|
||
|
return (WHSettings.GIVEN, width)
|
||
|
|
||
|
if isinstance(width, tuple) and len(width) == 2 and width[0] in (WHSettings.RELATIVE, WHSettings.WEIGHT):
|
||
|
width_type, width_amount = width
|
||
|
return (WHSettings(width_type), width_amount)
|
||
|
|
||
|
raise err(
|
||
|
f"width value {width!r} is not one of"
|
||
|
f"fixed number of columns, 'pack', ('relative', percentage of total width), 'clip'"
|
||
|
)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_width(
|
||
|
width_type: Literal["clip", "pack", WHSettings.CLIP, WHSettings.PACK],
|
||
|
width_amount: int | None,
|
||
|
) -> Literal[WHSettings.CLIP, WHSettings.PACK]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_width(
|
||
|
width_type: Literal["given", WHSettings.GIVEN],
|
||
|
width_amount: int,
|
||
|
) -> int:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_width(
|
||
|
width_type: Literal["relative", WHSettings.RELATIVE],
|
||
|
width_amount: int,
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_width(
|
||
|
width_type: Literal["weight", WHSettings.WEIGHT],
|
||
|
width_amount: int,
|
||
|
) -> tuple[Literal[WHSettings.WEIGHT], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_width(
|
||
|
width_type: Literal["given", "relative", "weight", WHSettings.GIVEN, WHSettings.RELATIVE, WHSettings.WEIGHT],
|
||
|
width_amount: None,
|
||
|
) -> typing.NoReturn:
|
||
|
...
|
||
|
|
||
|
|
||
|
def simplify_width(
|
||
|
width_type: Literal["clip", "pack", "given", "relative", "weight"] | WHSettings,
|
||
|
width_amount: int | None,
|
||
|
) -> Literal[WHSettings.CLIP, WHSettings.PACK] | int | tuple[Literal[WHSettings.RELATIVE, WHSettings.WEIGHT], int]:
|
||
|
"""
|
||
|
Recombine (width_type, width_amount) into an width value.
|
||
|
Inverse of normalize_width.
|
||
|
"""
|
||
|
if width_type in (WHSettings.CLIP, WHSettings.PACK):
|
||
|
return WHSettings(width_type)
|
||
|
|
||
|
if not isinstance(width_amount, int):
|
||
|
raise TypeError(width_amount)
|
||
|
|
||
|
if width_type == WHSettings.GIVEN:
|
||
|
return width_amount
|
||
|
|
||
|
return (WHSettings(width_type), width_amount)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_height(
|
||
|
height: (int),
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.GIVEN], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_height(
|
||
|
height: (Literal["flow", "pack", Sizing.FLOW, WHSettings.PACK]),
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[Sizing.FLOW, WHSettings.PACK], None]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_height(
|
||
|
height: (tuple[Literal["relative", WHSettings.RELATIVE], int]),
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def normalize_height(
|
||
|
height: (tuple[Literal["weight", WHSettings.WEIGHT], int]),
|
||
|
err: type[BaseException],
|
||
|
) -> tuple[Literal[WHSettings.WEIGHT], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
def normalize_height(
|
||
|
height: (
|
||
|
int
|
||
|
| Literal["flow", "pack", Sizing.FLOW, WHSettings.PACK]
|
||
|
| tuple[Literal["relative", "weight", WHSettings.RELATIVE, WHSettings.WEIGHT], int]
|
||
|
),
|
||
|
err: type[BaseException],
|
||
|
) -> (
|
||
|
tuple[Literal[Sizing.FLOW, WHSettings.PACK], None]
|
||
|
| tuple[Literal[WHSettings.RELATIVE, WHSettings.GIVEN, WHSettings.WEIGHT], int]
|
||
|
):
|
||
|
"""
|
||
|
Split height into (height_type, height_amount). Raise exception err
|
||
|
if height isn't valid.
|
||
|
"""
|
||
|
if height == Sizing.FLOW:
|
||
|
return (Sizing.FLOW, None)
|
||
|
|
||
|
if height == WHSettings.PACK:
|
||
|
return (WHSettings.PACK, None)
|
||
|
|
||
|
if isinstance(height, tuple) and len(height) == 2 and height[0] in (WHSettings.RELATIVE, WHSettings.WEIGHT):
|
||
|
return (WHSettings(height[0]), height[1])
|
||
|
|
||
|
if isinstance(height, int):
|
||
|
return (WHSettings.GIVEN, height)
|
||
|
|
||
|
raise err(
|
||
|
f"height value {height!r} is not one of "
|
||
|
f"fixed number of columns, 'pack', ('relative', percentage of total height)"
|
||
|
)
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_height(
|
||
|
height_type: Literal["flow", "pack", WHSettings.FLOW, WHSettings.PACK],
|
||
|
height_amount: int | None,
|
||
|
) -> Literal[WHSettings.FLOW, WHSettings.PACK]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_height(
|
||
|
height_type: Literal["given", WHSettings.GIVEN],
|
||
|
height_amount: int,
|
||
|
) -> int:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_height(
|
||
|
height_type: Literal["relative", WHSettings.RELATIVE],
|
||
|
height_amount: int | None,
|
||
|
) -> tuple[Literal[WHSettings.RELATIVE], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_height(
|
||
|
height_type: Literal["weight", WHSettings.WEIGHT],
|
||
|
height_amount: int | None,
|
||
|
) -> tuple[Literal[WHSettings.WEIGHT], int]:
|
||
|
...
|
||
|
|
||
|
|
||
|
@typing.overload
|
||
|
def simplify_height(
|
||
|
height_type: Literal["relative", "given", "weight", WHSettings.RELATIVE, WHSettings.GIVEN, WHSettings.WEIGHT],
|
||
|
height_amount: None,
|
||
|
) -> typing.NoReturn:
|
||
|
...
|
||
|
|
||
|
|
||
|
def simplify_height(
|
||
|
height_type: Literal[
|
||
|
"flow",
|
||
|
"pack",
|
||
|
"relative",
|
||
|
"given",
|
||
|
"weight",
|
||
|
WHSettings.FLOW,
|
||
|
WHSettings.PACK,
|
||
|
WHSettings.RELATIVE,
|
||
|
WHSettings.GIVEN,
|
||
|
WHSettings.WEIGHT,
|
||
|
],
|
||
|
height_amount: int | None,
|
||
|
) -> int | Literal[WHSettings.FLOW, WHSettings.PACK] | tuple[Literal[WHSettings.RELATIVE, WHSettings.WEIGHT], int]:
|
||
|
"""
|
||
|
Recombine (height_type, height_amount) into a height value.
|
||
|
Inverse of normalize_height.
|
||
|
"""
|
||
|
if height_type in (WHSettings.FLOW, WHSettings.PACK):
|
||
|
return WHSettings(height_type)
|
||
|
|
||
|
if not isinstance(height_amount, int):
|
||
|
raise TypeError(height_amount)
|
||
|
|
||
|
if height_type == WHSettings.GIVEN:
|
||
|
return height_amount
|
||
|
|
||
|
return (WHSettings(height_type), height_amount)
|