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