mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
fix basic
This commit is contained in:
@@ -632,30 +632,6 @@ class StylesBuilder:
|
||||
dock = tokens[0].value
|
||||
self.styles._rules["dock"] = dock
|
||||
|
||||
def process_docks(self, name: str, tokens: list[Token]) -> None:
|
||||
def docks_error(name, token):
|
||||
self.error(name, token, docks_property_help_text(name, context="css"))
|
||||
|
||||
docks: list[DockGroup] = []
|
||||
for token in tokens:
|
||||
if token.name == "key_value":
|
||||
key, edge_name = token.value.split("=")
|
||||
edge_name = edge_name.strip().lower()
|
||||
edge_name, _, number = edge_name.partition("/")
|
||||
z = 0
|
||||
if number:
|
||||
if not number.isdigit():
|
||||
docks_error(name, token)
|
||||
z = int(number)
|
||||
if edge_name not in VALID_EDGE:
|
||||
docks_error(name, token)
|
||||
docks.append(DockGroup(key.strip(), cast(Edge, edge_name), z))
|
||||
elif token.name == "bar":
|
||||
pass
|
||||
else:
|
||||
docks_error(name, token)
|
||||
self.styles._rules["docks"] = tuple(docks + [DockGroup("_default", "top", 0)])
|
||||
|
||||
def process_layer(self, name: str, tokens: list[Token]) -> None:
|
||||
if len(tokens) > 1:
|
||||
self.error(name, tokens[1], f"unexpected tokens in dock-edge declaration")
|
||||
|
||||
@@ -32,7 +32,7 @@ VALID_BORDER: Final[set[EdgeType]] = {
|
||||
"wide",
|
||||
}
|
||||
VALID_EDGE: Final = {"top", "right", "bottom", "left"}
|
||||
VALID_LAYOUT: Final = {"dock", "vertical", "horizontal"}
|
||||
VALID_LAYOUT: Final = {"vertical", "horizontal"}
|
||||
|
||||
VALID_BOX_SIZING: Final = {"border-box", "content-box"}
|
||||
VALID_OVERFLOW: Final = {"scroll", "hidden", "auto"}
|
||||
|
||||
@@ -304,8 +304,6 @@ class Stylesheet:
|
||||
animate (bool, optional): Animate changed rules. Defaults to ``False``.
|
||||
"""
|
||||
|
||||
print(node)
|
||||
|
||||
# Dictionary of rule attribute names e.g. "text_background" to list of tuples.
|
||||
# The tuples contain the rule specificity, and the value for that rule.
|
||||
# We can use this to determine, for a given rule, whether we should apply it
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from operator import attrgetter
|
||||
from typing import TYPE_CHECKING, NamedTuple, Sequence
|
||||
|
||||
from .._layout_resolve import layout_resolve
|
||||
from ..css.types import Edge
|
||||
from ..geometry import Region, Size
|
||||
from .._layout import ArrangeResult, Layout, WidgetPlacement
|
||||
from ..widget import Widget
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..screen import Screen
|
||||
|
||||
DockEdge = Literal["top", "right", "bottom", "left"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class DockOptions:
|
||||
size: int | None = None
|
||||
fraction: int | None = 1
|
||||
min_size: int = 1
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.size is None and self.fraction is None:
|
||||
self.fraction = 1
|
||||
|
||||
|
||||
class Dock(NamedTuple):
|
||||
edge: Edge
|
||||
widgets: Sequence[Widget]
|
||||
z: int = 0
|
||||
|
||||
|
||||
class DockLayout(Layout):
|
||||
"""Dock Widgets to edge of screen."""
|
||||
|
||||
name = "dock"
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._docks: list[Dock] | None = None
|
||||
|
||||
def __repr__(self):
|
||||
return "<DockLayout>"
|
||||
|
||||
def get_docks(self, parent: Widget, children: list[Widget]) -> list[Dock]:
|
||||
groups: dict[str, list[Widget]] = defaultdict(list)
|
||||
for child in children:
|
||||
assert isinstance(child, Widget)
|
||||
groups[child.styles.dock].append(child)
|
||||
docks: list[Dock] = []
|
||||
append_dock = docks.append
|
||||
for name, edge, z in parent.styles.docks:
|
||||
append_dock(Dock(edge, groups[name], z))
|
||||
return docks
|
||||
|
||||
def arrange(
|
||||
self, parent: Widget, children: list[Widget], size: Size
|
||||
) -> ArrangeResult:
|
||||
|
||||
width, height = size
|
||||
layout_region = Region(0, 0, width, height)
|
||||
layers: dict[int, Region] = defaultdict(lambda: layout_region)
|
||||
|
||||
docks = self.get_docks(parent, children)
|
||||
|
||||
def make_dock_options(widget: Widget, edge: Edge) -> DockOptions:
|
||||
styles = widget.styles
|
||||
has_rule = styles.has_rule
|
||||
|
||||
# TODO: This was written pre resolve_dimension, we should update this to use available units
|
||||
return (
|
||||
DockOptions(
|
||||
styles.width.cells if has_rule("width") else None,
|
||||
styles.width.fraction if has_rule("width") else 1,
|
||||
styles.min_width.cells if has_rule("min_width") else 1,
|
||||
)
|
||||
if edge in ("left", "right")
|
||||
else DockOptions(
|
||||
styles.height.cells if has_rule("height") else None,
|
||||
styles.height.fraction if has_rule("height") else 1,
|
||||
styles.min_height.cells if has_rule("min_height") else 1,
|
||||
)
|
||||
)
|
||||
|
||||
placements: list[WidgetPlacement] = []
|
||||
add_placement = placements.append
|
||||
arranged_widgets: set[Widget] = set()
|
||||
|
||||
for z, (edge, widgets, _z) in enumerate(sorted(docks, key=attrgetter("z"))):
|
||||
|
||||
arranged_widgets.update(widgets)
|
||||
dock_options = [make_dock_options(widget, edge) for widget in widgets]
|
||||
region = layers[z]
|
||||
if not region.area:
|
||||
# No space left
|
||||
continue
|
||||
|
||||
x, y, width, height = region
|
||||
|
||||
if edge == "top":
|
||||
sizes = layout_resolve(height, dock_options)
|
||||
render_y = y
|
||||
remaining = region.height
|
||||
total = 0
|
||||
for widget, new_size in zip(widgets, sizes):
|
||||
new_size = min(remaining, new_size)
|
||||
if not new_size:
|
||||
break
|
||||
total += new_size
|
||||
add_placement(
|
||||
WidgetPlacement(Region(x, render_y, width, new_size), widget, z)
|
||||
)
|
||||
render_y += new_size
|
||||
remaining = max(0, remaining - new_size)
|
||||
region = Region(x, y + total, width, height - total)
|
||||
|
||||
elif edge == "bottom":
|
||||
sizes = layout_resolve(height, dock_options)
|
||||
render_y = y + height
|
||||
remaining = region.height
|
||||
total = 0
|
||||
for widget, new_size in zip(widgets, sizes):
|
||||
new_size = min(remaining, new_size)
|
||||
if not new_size:
|
||||
break
|
||||
total += new_size
|
||||
add_placement(
|
||||
WidgetPlacement(
|
||||
Region(x, render_y - new_size, width, new_size), widget, z
|
||||
)
|
||||
)
|
||||
render_y -= new_size
|
||||
remaining = max(0, remaining - new_size)
|
||||
region = Region(x, y, width, height - total)
|
||||
|
||||
elif edge == "left":
|
||||
sizes = layout_resolve(width, dock_options)
|
||||
render_x = x
|
||||
remaining = region.width
|
||||
total = 0
|
||||
for widget, new_size in zip(widgets, sizes):
|
||||
new_size = min(remaining, new_size)
|
||||
if not new_size:
|
||||
break
|
||||
total += new_size
|
||||
add_placement(
|
||||
WidgetPlacement(
|
||||
Region(render_x, y, new_size, height), widget, z
|
||||
)
|
||||
)
|
||||
render_x += new_size
|
||||
remaining = max(0, remaining - new_size)
|
||||
region = Region(x + total, y, width - total, height)
|
||||
|
||||
elif edge == "right":
|
||||
sizes = layout_resolve(width, dock_options)
|
||||
render_x = x + width
|
||||
remaining = region.width
|
||||
total = 0
|
||||
for widget, new_size in zip(widgets, sizes):
|
||||
new_size = min(remaining, new_size)
|
||||
if not new_size:
|
||||
break
|
||||
total += new_size
|
||||
add_placement(
|
||||
WidgetPlacement(
|
||||
Region(render_x - new_size, y, new_size, height), widget, z
|
||||
)
|
||||
)
|
||||
render_x -= new_size
|
||||
remaining = max(0, remaining - new_size)
|
||||
region = Region(x, y, width - total, height)
|
||||
|
||||
layers[z] = region
|
||||
|
||||
return ArrangeResult(placements, arranged_widgets)
|
||||
@@ -1,12 +1,12 @@
|
||||
import pytest
|
||||
|
||||
from textual.layouts.dock import DockLayout
|
||||
from textual.layouts.vertical import VerticalLayout
|
||||
from textual.layouts.factory import get_layout, MissingLayout
|
||||
|
||||
|
||||
def test_get_layout_valid_layout():
|
||||
layout = get_layout("dock")
|
||||
assert type(layout) is DockLayout
|
||||
layout = get_layout("vertical")
|
||||
assert type(layout) is VerticalLayout
|
||||
|
||||
|
||||
def test_get_layout_invalid_layout():
|
||||
|
||||
Reference in New Issue
Block a user