mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
border improvement
This commit is contained in:
21
offset.css
Normal file
21
offset.css
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Screen {
|
||||||
|
layout: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#parent {
|
||||||
|
width: 32;
|
||||||
|
height: 8;
|
||||||
|
background: $panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tag {
|
||||||
|
color: $text;
|
||||||
|
background: $success;
|
||||||
|
padding: 1 2;
|
||||||
|
width: auto;
|
||||||
|
/* offset: -8 -4; */
|
||||||
|
}
|
||||||
|
|
||||||
|
#child {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
17
offset.py
Normal file
17
offset.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from textual import layout
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
|
||||||
|
class OffsetExample(App):
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield layout.Vertical(
|
||||||
|
Static("Child", id="child"),
|
||||||
|
id="parent"
|
||||||
|
)
|
||||||
|
yield Static("Tag", id="tag")
|
||||||
|
|
||||||
|
|
||||||
|
app = OffsetExample(css_path="offset.css")
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
21
sandbox/will/offset.css
Normal file
21
sandbox/will/offset.css
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Screen {
|
||||||
|
layout: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#parent {
|
||||||
|
width: 32;
|
||||||
|
height: 8;
|
||||||
|
background: $panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tag {
|
||||||
|
color: $text;
|
||||||
|
background: $success;
|
||||||
|
padding: 2 4;
|
||||||
|
width: auto;
|
||||||
|
offset: -8 -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#child {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
17
sandbox/will/offset.py
Normal file
17
sandbox/will/offset.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from textual import layout
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
|
||||||
|
class OffsetExample(App):
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield layout.Vertical(
|
||||||
|
Static("Child", id="child"),
|
||||||
|
id="parent"
|
||||||
|
)
|
||||||
|
yield Static("Tag", id="tag")
|
||||||
|
|
||||||
|
|
||||||
|
app = OffsetExample(css_path="offset.css")
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
@@ -39,8 +39,8 @@ BORDER_CHARS: dict[EdgeType, tuple[str, str, str]] = {
|
|||||||
"outer": ("▛▀▜", "▌ ▐", "▙▄▟"),
|
"outer": ("▛▀▜", "▌ ▐", "▙▄▟"),
|
||||||
"hkey": ("▔▔▔", " ", "▁▁▁"),
|
"hkey": ("▔▔▔", " ", "▁▁▁"),
|
||||||
"vkey": ("▏ ▕", "▏ ▕", "▏ ▕"),
|
"vkey": ("▏ ▕", "▏ ▕", "▏ ▕"),
|
||||||
"tall": ("▕▔▏", "▕ ▏", "▕▁▏"),
|
"tall": ("▊▔▎", "▊ ▎", "▊▁▎"),
|
||||||
"wide": ("▁▁▁", "▏ ▕", "▔▔▔"),
|
"wide": ("▁▁▁", "▎ ▋", "▔▔▔"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Some of the borders are on the widget background and some are on the background of the parent
|
# Some of the borders are on the widget background and some are on the background of the parent
|
||||||
@@ -62,8 +62,8 @@ BORDER_LOCATIONS: dict[
|
|||||||
"outer": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
"outer": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
||||||
"hkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
"hkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
||||||
"vkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
"vkey": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
|
||||||
"tall": ((1, 0, 1), (1, 0, 1), (1, 0, 1)),
|
"tall": ((2, 0, 1), (2, 0, 1), (2, 0, 1)),
|
||||||
"wide": ((1, 1, 1), (0, 1, 0), (1, 1, 1)),
|
"wide": ((1, 1, 1), (3, 1, 4), (1, 1, 1)),
|
||||||
}
|
}
|
||||||
|
|
||||||
INVISIBLE_EDGE_TYPES = cast("frozenset[EdgeType]", frozenset(("", "none", "hidden")))
|
INVISIBLE_EDGE_TYPES = cast("frozenset[EdgeType]", frozenset(("", "none", "hidden")))
|
||||||
@@ -81,7 +81,10 @@ Borders: TypeAlias = Tuple[EdgeStyle, EdgeStyle, EdgeStyle, EdgeStyle]
|
|||||||
|
|
||||||
@lru_cache(maxsize=1024)
|
@lru_cache(maxsize=1024)
|
||||||
def get_box(
|
def get_box(
|
||||||
name: EdgeType, inner_style: Style, outer_style: Style, style: Style
|
name: EdgeType,
|
||||||
|
inner_style: Style,
|
||||||
|
outer_style: Style,
|
||||||
|
style: Style,
|
||||||
) -> BoxSegments:
|
) -> BoxSegments:
|
||||||
"""Get segments used to render a box.
|
"""Get segments used to render a box.
|
||||||
|
|
||||||
@@ -107,23 +110,29 @@ def get_box(
|
|||||||
(lbottom1, lbottom2, lbottom3),
|
(lbottom1, lbottom2, lbottom3),
|
||||||
) = BORDER_LOCATIONS[name]
|
) = BORDER_LOCATIONS[name]
|
||||||
|
|
||||||
styles = (inner_style, outer_style)
|
styles: tuple[Style, ...] = (inner_style + style, outer_style + style)
|
||||||
|
|
||||||
|
styles += (
|
||||||
|
Style.from_color(styles[1].bgcolor, styles[0].color),
|
||||||
|
Style.from_color(styles[0].color, styles[0].bgcolor),
|
||||||
|
Style.from_color(styles[0].bgcolor, styles[1].color),
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
_Segment(top1, styles[ltop1] + style),
|
_Segment(top1, styles[ltop1]),
|
||||||
_Segment(top2, styles[ltop2] + style),
|
_Segment(top2, styles[ltop2]),
|
||||||
_Segment(top3, styles[ltop3] + style),
|
_Segment(top3, styles[ltop3]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
_Segment(mid1, styles[lmid1] + style),
|
_Segment(mid1, styles[lmid1]),
|
||||||
_Segment(mid2, styles[lmid2] + style),
|
_Segment(mid2, styles[lmid2]),
|
||||||
_Segment(mid3, styles[lmid3] + style),
|
_Segment(mid3, styles[lmid3]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
_Segment(bottom1, styles[lbottom1] + style),
|
_Segment(bottom1, styles[lbottom1]),
|
||||||
_Segment(bottom2, styles[lbottom2] + style),
|
_Segment(bottom2, styles[lbottom2]),
|
||||||
_Segment(bottom3, styles[lbottom3] + style),
|
_Segment(bottom3, styles[lbottom3]),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ without having to render the entire screen.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from operator import attrgetter, itemgetter
|
from operator import itemgetter
|
||||||
import sys
|
import sys
|
||||||
from typing import Callable, cast, Iterator, Iterable, NamedTuple, TYPE_CHECKING
|
from typing import Callable, cast, Iterator, Iterable, NamedTuple, TYPE_CHECKING
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ from rich.segment import Segment
|
|||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
from . import errors
|
from . import errors
|
||||||
from .geometry import Region, Offset, Size, Spacing
|
from .geometry import Region, Offset, Size
|
||||||
|
|
||||||
from ._cells import cell_len
|
from ._cells import cell_len
|
||||||
from ._profile import timer
|
from ._profile import timer
|
||||||
@@ -55,7 +55,7 @@ class MapGeometry(NamedTuple):
|
|||||||
"""Defines the absolute location of a Widget."""
|
"""Defines the absolute location of a Widget."""
|
||||||
|
|
||||||
region: Region # The (screen) region occupied by the widget
|
region: Region # The (screen) region occupied by the widget
|
||||||
order: tuple[int, ...] # A tuple of ints defining the painting order
|
order: tuple[tuple[int, ...], ...] # A tuple of ints defining the painting order
|
||||||
clip: Region # A region to clip the widget by (if a Widget is within a container)
|
clip: Region # A region to clip the widget by (if a Widget is within a container)
|
||||||
virtual_size: Size # The virtual size (scrollable region) of a widget if it is a container
|
virtual_size: Size # The virtual size (scrollable region) of a widget if it is a container
|
||||||
container_size: Size # The container size (area not occupied by scrollbars)
|
container_size: Size # The container size (area not occupied by scrollbars)
|
||||||
@@ -344,12 +344,14 @@ class Compositor:
|
|||||||
|
|
||||||
map: CompositorMap = {}
|
map: CompositorMap = {}
|
||||||
widgets: set[Widget] = set()
|
widgets: set[Widget] = set()
|
||||||
|
layer_order: int = 0
|
||||||
|
|
||||||
def add_widget(
|
def add_widget(
|
||||||
widget: Widget,
|
widget: Widget,
|
||||||
virtual_region: Region,
|
virtual_region: Region,
|
||||||
region: Region,
|
region: Region,
|
||||||
order: tuple[int, ...],
|
order: tuple[tuple[int, ...], ...],
|
||||||
|
layer_order: int,
|
||||||
clip: Region,
|
clip: Region,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Called recursively to place a widget and its children in the map.
|
"""Called recursively to place a widget and its children in the map.
|
||||||
@@ -413,15 +415,19 @@ class Compositor:
|
|||||||
)
|
)
|
||||||
widget_region = sub_region + placement_scroll_offset
|
widget_region = sub_region + placement_scroll_offset
|
||||||
|
|
||||||
widget_order = order + (get_layer_index(sub_widget.layer, 0), z)
|
widget_order = order + (
|
||||||
|
(get_layer_index(sub_widget.layer, 0), z, layer_order),
|
||||||
|
)
|
||||||
|
|
||||||
add_widget(
|
add_widget(
|
||||||
sub_widget,
|
sub_widget,
|
||||||
sub_region,
|
sub_region,
|
||||||
widget_region,
|
widget_region,
|
||||||
widget_order,
|
widget_order,
|
||||||
|
layer_order,
|
||||||
sub_clip,
|
sub_clip,
|
||||||
)
|
)
|
||||||
|
layer_order -= 1
|
||||||
|
|
||||||
# Add any scrollbars
|
# Add any scrollbars
|
||||||
for chrome_widget, chrome_region in widget._arrange_scrollbars(
|
for chrome_widget, chrome_region in widget._arrange_scrollbars(
|
||||||
@@ -457,7 +463,7 @@ class Compositor:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Add top level (root) widget
|
# Add top level (root) widget
|
||||||
add_widget(root, size.region, size.region, (0,), size.region)
|
add_widget(root, size.region, size.region, ((0,),), layer_order, size.region)
|
||||||
return map, widgets
|
return map, widgets
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -124,13 +124,13 @@ class ColorSystem:
|
|||||||
background = self.background or Color.parse(DEFAULT_LIGHT_BACKGROUND)
|
background = self.background or Color.parse(DEFAULT_LIGHT_BACKGROUND)
|
||||||
surface = self.surface or Color.parse(DEFAULT_LIGHT_SURFACE)
|
surface = self.surface or Color.parse(DEFAULT_LIGHT_SURFACE)
|
||||||
|
|
||||||
|
boost = self.boost or background.get_contrast_text(1.0).with_alpha(0.07)
|
||||||
|
|
||||||
if self.panel is None:
|
if self.panel is None:
|
||||||
panel = surface.blend(primary, luminosity_spread)
|
panel = surface.blend(primary, luminosity_spread) + boost
|
||||||
else:
|
else:
|
||||||
panel = self.panel
|
panel = self.panel
|
||||||
|
|
||||||
boost = self.boost or background.get_contrast_text(1.0).with_alpha(0.07)
|
|
||||||
|
|
||||||
colors: dict[str, str] = {}
|
colors: dict[str, str] = {}
|
||||||
|
|
||||||
def luminosity_range(spread) -> Iterable[tuple[str, float]]:
|
def luminosity_range(spread) -> Iterable[tuple[str, float]]:
|
||||||
|
|||||||
Reference in New Issue
Block a user