mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
align
This commit is contained in:
@@ -1,19 +1,39 @@
|
||||
|
||||
|
||||
Screen {
|
||||
layout: horizontal;
|
||||
|
||||
}
|
||||
|
||||
Widget#thing {
|
||||
Widget {
|
||||
margin:1;
|
||||
}
|
||||
|
||||
#thing {
|
||||
width: 20;
|
||||
height: 10;
|
||||
align: center middle;
|
||||
|
||||
background:magenta;
|
||||
margin: 1;
|
||||
padding: 1;
|
||||
/* border: solid white; */
|
||||
align-vertical: middle;
|
||||
}
|
||||
|
||||
|
||||
Widget#thing2 {
|
||||
width: 30;
|
||||
height: 8;
|
||||
align: center middle;
|
||||
background: green;
|
||||
}
|
||||
#thing2 {
|
||||
width: 20;
|
||||
height: 10;
|
||||
|
||||
background:green;
|
||||
align-vertical: middle;
|
||||
}
|
||||
|
||||
|
||||
#thing3 {
|
||||
width: 20;
|
||||
height: 10;
|
||||
|
||||
background:blue;
|
||||
align-vertical: bottom;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class Thing(Widget):
|
||||
def render(self):
|
||||
return Text.from_markup("Hello, World. [b magenta]Lorem impsum.")
|
||||
|
||||
|
||||
class AlignApp(App):
|
||||
def on_load(self):
|
||||
self.bind("t", "log_tree")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.log("MOUNTED")
|
||||
self.mount(thing=Widget(), thing2=Widget())
|
||||
self.mount(thing=Thing(), thing2=Widget(), thing3=Widget())
|
||||
|
||||
def action_log_tree(self):
|
||||
self.log(self.screen.tree)
|
||||
|
||||
@@ -21,6 +21,7 @@ App > Screen {
|
||||
color: $text-background;
|
||||
}
|
||||
|
||||
|
||||
#sidebar {
|
||||
color: $text-primary;
|
||||
background: $primary;
|
||||
@@ -81,7 +82,8 @@ Tweet {
|
||||
/* border: outer $primary; */
|
||||
padding: 1;
|
||||
border: wide $panel-darken-2;
|
||||
overflow-y: scroll
|
||||
overflow-y: scroll;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +170,7 @@ Error {
|
||||
margin: 1 3;
|
||||
|
||||
text-style: bold;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
Warning {
|
||||
@@ -179,6 +182,7 @@ Warning {
|
||||
border-bottom: hkey $warning-darken-2;
|
||||
margin: 1 2;
|
||||
text-style: bold;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
Success {
|
||||
@@ -190,4 +194,5 @@ Success {
|
||||
border-bottom: hkey $success;
|
||||
margin: 1 2;
|
||||
text-style: bold;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ when setting and getting.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from tkinter.tix import AUTO
|
||||
|
||||
from typing import Iterable, NamedTuple, TYPE_CHECKING, cast
|
||||
|
||||
@@ -47,10 +48,14 @@ class ScalarProperty:
|
||||
"""Descriptor for getting and setting scalar properties. Scalars are numeric values with a unit, e.g. "50vh"."""
|
||||
|
||||
def __init__(
|
||||
self, units: set[Unit] | None = None, percent_unit: Unit = Unit.WIDTH
|
||||
self,
|
||||
units: set[Unit] | None = None,
|
||||
percent_unit: Unit = Unit.WIDTH,
|
||||
allow_auto: bool = True,
|
||||
) -> None:
|
||||
self.units: set[Unit] = units or {*UNIT_SYMBOL}
|
||||
self.percent_unit = percent_unit
|
||||
self.allow_auto = allow_auto
|
||||
super().__init__()
|
||||
|
||||
def __set_name__(self, owner: Styles, name: str) -> None:
|
||||
@@ -90,7 +95,7 @@ class ScalarProperty:
|
||||
if value is None:
|
||||
obj.clear_rule(self.name)
|
||||
return
|
||||
if isinstance(value, float):
|
||||
if isinstance(value, (int, float)):
|
||||
new_value = Scalar(float(value), Unit.CELLS, Unit.WIDTH)
|
||||
elif isinstance(value, Scalar):
|
||||
new_value = value
|
||||
@@ -101,12 +106,23 @@ class ScalarProperty:
|
||||
raise StyleValueError("unable to parse scalar from {value!r}")
|
||||
else:
|
||||
raise StyleValueError("expected float, Scalar, or None")
|
||||
if new_value is not None and new_value.unit not in self.units:
|
||||
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(float(new_value.value), self.percent_unit, Unit.WIDTH)
|
||||
|
||||
if (
|
||||
new_value is not None
|
||||
and new_value.unit == Unit.AUTO
|
||||
and not self.allow_auto
|
||||
):
|
||||
raise StyleValueError("'auto' not allowed here")
|
||||
|
||||
if new_value.unit != Unit.AUTO:
|
||||
if new_value is not None and new_value.unit not in self.units:
|
||||
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(
|
||||
float(new_value.value), self.percent_unit, Unit.WIDTH
|
||||
)
|
||||
if obj.set_rule(self.name, new_value):
|
||||
obj.refresh()
|
||||
|
||||
|
||||
@@ -625,7 +625,9 @@ class StylesBuilder:
|
||||
self.styles._rules["align_vertical"] = token_vertical.value
|
||||
|
||||
def process_align_horizontal(self, name: str, tokens: list[Token]) -> None:
|
||||
self._process_enum(name, tokens, VALID_ALIGN_HORIZONTAL)
|
||||
value = self._process_enum(name, tokens, VALID_ALIGN_HORIZONTAL)
|
||||
self.styles._rules["align_horizontal"] = value
|
||||
|
||||
def process_align_vertical(self, name: str, tokens: list[Token]) -> None:
|
||||
self._process_enum(name, tokens, VALID_ALIGN_VERTICAL)
|
||||
value = self._process_enum(name, tokens, VALID_ALIGN_VERTICAL)
|
||||
self.styles._rules["align_vertical"] = value
|
||||
|
||||
@@ -152,6 +152,7 @@ class Scalar(NamedTuple):
|
||||
float: _description_
|
||||
"""
|
||||
value, unit, percent_unit = self
|
||||
|
||||
if unit == Unit.PERCENT:
|
||||
unit = percent_unit
|
||||
try:
|
||||
|
||||
@@ -418,8 +418,8 @@ class StylesBase(ABC):
|
||||
margin = self.margin
|
||||
|
||||
else: # border-box
|
||||
if has_rule("padding"):
|
||||
size -= self.padding.totals
|
||||
# if has_rule("padding"):
|
||||
# size -= self.padding.totals
|
||||
if has_rule("border"):
|
||||
size -= self.border.spacing.totals
|
||||
if has_rule("margin"):
|
||||
@@ -458,7 +458,7 @@ class StylesBase(ABC):
|
||||
"""
|
||||
offset_y = 0
|
||||
align_vertical = self.align_vertical
|
||||
if align_vertical != "left":
|
||||
if align_vertical != "top":
|
||||
if align_vertical == "middle":
|
||||
offset_y = (parent_height - height) // 2
|
||||
else:
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from operator import attrgetter
|
||||
from typing import Iterable, TYPE_CHECKING, NamedTuple, Sequence
|
||||
|
||||
from .._layout_resolve import layout_resolve
|
||||
@@ -91,7 +92,7 @@ class DockLayout(Layout):
|
||||
add_placement = placements.append
|
||||
arranged_widgets: set[Widget] = set()
|
||||
|
||||
for edge, widgets, z in docks:
|
||||
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]
|
||||
|
||||
@@ -29,7 +29,9 @@ class HorizontalLayout(Layout):
|
||||
(content_width, content_height), margin = widget.styles.get_box_model(
|
||||
size, parent_size
|
||||
)
|
||||
offset_y = styles.align_height(content_height, parent_size.height)
|
||||
offset_y = styles.align_height(
|
||||
content_height + margin.height, parent_size.height
|
||||
)
|
||||
region = Region(
|
||||
margin.left + x, margin.top + offset_y, content_width, content_height
|
||||
)
|
||||
|
||||
@@ -31,7 +31,9 @@ class VerticalLayout(Layout):
|
||||
(content_width, content_height), margin = styles.get_box_model(
|
||||
size, parent_size
|
||||
)
|
||||
offset_x = styles.align_width(content_width, parent_size.width)
|
||||
offset_x = styles.align_width(
|
||||
content_width + margin.width, parent_size.width
|
||||
)
|
||||
|
||||
region = Region(
|
||||
margin.left + offset_x, y + margin.top, content_width, content_height
|
||||
|
||||
@@ -6,7 +6,6 @@ from typing import (
|
||||
Awaitable,
|
||||
TYPE_CHECKING,
|
||||
Callable,
|
||||
ClassVar,
|
||||
Iterable,
|
||||
NamedTuple,
|
||||
cast,
|
||||
@@ -16,10 +15,9 @@ import rich.repr
|
||||
from rich.align import Align
|
||||
from rich.console import Console, RenderableType
|
||||
from rich.padding import Padding
|
||||
from rich.pretty import Pretty
|
||||
from rich.style import Style
|
||||
from rich.styled import Styled
|
||||
from rich.text import Text
|
||||
|
||||
|
||||
from . import errors, log
|
||||
from . import events
|
||||
@@ -394,10 +392,7 @@ class Widget(DOMNode):
|
||||
if renderable_text_style:
|
||||
renderable = Styled(renderable, renderable_text_style)
|
||||
|
||||
if styles.padding:
|
||||
renderable = Padding(
|
||||
renderable, styles.padding, style=renderable_text_style
|
||||
)
|
||||
renderable = Padding(renderable, styles.padding, style=renderable_text_style)
|
||||
|
||||
if styles.border:
|
||||
renderable = Border(
|
||||
|
||||
Reference in New Issue
Block a user