Skip to content

Commit

Permalink
test fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Nov 8, 2024
1 parent a317c04 commit 783b085
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 56 deletions.
6 changes: 2 additions & 4 deletions src/textual/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -1021,11 +1021,9 @@ async def _gather_commands(self, search_value: str) -> None:
# Turn the command into something for display, and add it to the
# list of commands that have been gathered so far.

prompt = hit.prompt

content = Content(prompt)
prompt = Content(hit.prompt, no_wrap=True, ellipsis=True)
if hit.help:
prompt = content.append("\n").append(
prompt = prompt.append("\n").append(
Content.styled(hit.help, help_style)
)

Expand Down
51 changes: 34 additions & 17 deletions src/textual/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import re
from operator import itemgetter
from typing import Callable, Iterable, NamedTuple, Sequence
from typing import TYPE_CHECKING, Callable, Iterable, NamedTuple, Sequence

import rich.repr
from rich._wrap import divide_line
Expand All @@ -23,10 +23,13 @@
from textual._cells import cell_len
from textual._loop import loop_last
from textual.color import Color
from textual.css.styles import Styles
from textual.css.types import TextAlign
from textual.strip import Strip
from textual.visual import Style, Visual

if TYPE_CHECKING:
from textual.widget import Widget

_re_whitespace = re.compile(r"\s+$")


Expand Down Expand Up @@ -136,19 +139,38 @@ def __init__(
text: str,
spans: list[Span] | None = None,
cell_length: int | None = None,
justify: TextAlign = "left",
no_wrap: bool = False,
ellipsis: bool = False,
) -> None:
self._text: str = text
self._spans: list[Span] = [] if spans is None else spans
self._cell_length = cell_length
self._justify = justify
self._no_wrap = no_wrap
self._ellipsis = ellipsis

@classmethod
def styled(
cls, text: str, style: Style | str = "", cell_length: int | None = None
cls,
text: str,
style: Style | str = "",
cell_length: int | None = None,
justify: TextAlign = "left",
no_wrap: bool = False,
ellipsis: bool = False,
) -> Content:
if not text:
return Content("")
span_length = cell_len(text) if cell_length is None else cell_length
new_content = cls(text, [Span(0, span_length, style)], span_length)
new_content = cls(
text,
[Span(0, span_length, style)],
span_length,
justify=justify,
no_wrap=no_wrap,
ellipsis=ellipsis,
)
return new_content

def get_optimal_width(self, tab_size: int = 8) -> int:
Expand All @@ -164,27 +186,24 @@ def textualize(self) -> Content:

def render_strips(
self,
widget: Widget,
width: int,
height: int | None,
base_style: Style,
styles: Styles,
style: Style,
) -> list[Strip]:
horizontal_align = styles.text_align
justify = self._NORMALIZE_TEXT_ALIGN.get(horizontal_align, horizontal_align)
lines = self.wrap(
width,
justify=justify, # type: ignore[arg-type]
overflow="fold",
justify=self._justify,
overflow=(
("ellipsis" if self._ellipsis else "crop") if self._no_wrap else "fold"
),
no_wrap=False,
tab_size=8,
)
if height is not None:
lines = lines[:height]

base_style += Style.from_styles(styles)
return [
Strip(line.render_segments(base_style), line.cell_length) for line in lines
]
return [Strip(line.render_segments(style), line.cell_length) for line in lines]

def get_height(self, width: int) -> int:
lines = self.wrap(width)
Expand Down Expand Up @@ -371,9 +390,7 @@ def get_style_at_offset(self, offset: int) -> Style:
style = Style()
for start, end, span_style in self._spans:
if end > offset >= start:
print(style, span_style)
style += span_style
print("---", style)
return style

def truncate(
Expand Down Expand Up @@ -800,7 +817,7 @@ def expand_tabs(self, tab_size: int = 8) -> Content:
def wrap(
self,
width: int,
justify: JustifyMethod = "left",
justify: TextAlign = "left",
overflow: OverflowMethod = "fold",
no_wrap: bool = False,
tab_size: int = 8,
Expand Down
60 changes: 27 additions & 33 deletions src/textual/visual.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import sys
from abc import ABC, abstractmethod
from dataclasses import dataclass
from functools import cached_property, lru_cache
Expand All @@ -22,18 +21,17 @@
from textual.geometry import Spacing
from textual.strip import Strip

if sys.version_info >= (3, 8):
pass
else:
pass


if TYPE_CHECKING:
from textual.widget import Widget

_NULL_RICH_STYLE = RichStyle()


def is_visual(obj: object) -> bool:
"""Check if the given object is a Visual or supports the Visual protocol."""
return isinstance(obj, Visual) or hasattr(obj, "textualize")


class SupportsTextualize(Protocol):
"""An object that supports the textualize protocol."""

Expand All @@ -44,7 +42,7 @@ class VisualError(Exception):
"""An error with the visual protocol."""


VisualType = RenderableType | SupportsTextualize
VisualType = RenderableType | SupportsTextualize | "Visual"


def visualize(widget: Widget, obj: object) -> Visual:
Expand Down Expand Up @@ -189,11 +187,7 @@ class Visual(ABC):

@abstractmethod
def render_strips(
self,
width: int,
height: int | None,
base_style: Style,
styles: Styles,
self, widget: Widget, width: int, height: int | None, style: Style
) -> list[Strip]:
"""Render the visual in to an iterable of strips.
Expand Down Expand Up @@ -246,16 +240,12 @@ def to_strips(
widget: Widget,
component_classes: list[str] | None = None,
) -> list[Strip]:
styles: Styles
if component_classes:
rules = widget.styles.get_rules()
rules |= widget.get_component_styles(*component_classes).get_rules()
styles = Styles(widget, rules)
else:
styles = widget.styles

strips = visual.render_strips(width, height, widget.visual_style, styles)

visual_style = (
widget.get_visual_style(*component_classes)
if component_classes
else widget.get_visual_style()
)
strips = visual.render_strips(widget, width, height, visual_style)
return strips


Expand Down Expand Up @@ -307,23 +297,27 @@ def get_height(self, width: int) -> int:

def render_strips(
self,
widget: Widget,
width: int,
height: int | None,
base_style: Style,
styles: Styles,
style: Style,
) -> list[Strip]:
console = active_app.get().console
options = console.options.update(
highlight=False,
width=width,
height=height,
)
renderable = self._widget.post_render(self._renderable)
renderable = widget.post_render(self._renderable)

segments = console.render(renderable, options)
rich_style = style.rich_style
if rich_style:
segments = Segment.apply_style(segments, style=rich_style)

strips = [
Strip(segments)
for segments in islice(
Strip(line)
for line in islice(
Segment.split_and_crop_lines(
segments, width, include_new_lines=False, pad=False
),
Expand All @@ -350,21 +344,21 @@ def get_height(self, width: int) -> int:

def render_strips(
self,
widget: Widget,
width: int,
height: int | None,
base_style: Style,
styles: Styles,
style: Style,
) -> list[Strip]:
padding = self._spacing
top, right, bottom, left = self._spacing
render_width = width - (left + right)
strips = self._visual.render_strips(
widget,
render_width,
None if height is None else height - padding.height,
base_style,
styles,
style,
)
rich_style = base_style.rich_style
rich_style = style.rich_style
if padding:
top_padding = [Strip.blank(width, rich_style)] * top if top else []
bottom_padding = [Strip.blank(width, rich_style)] * bottom if bottom else []
Expand Down
51 changes: 50 additions & 1 deletion src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
from rich.text import Text
from typing_extensions import Self

from textual.css.styles import StylesBase

if TYPE_CHECKING:
from textual.app import RenderResult

Expand Down Expand Up @@ -83,7 +85,7 @@
from textual.rlock import RLock
from textual.strip import Strip
from textual.visual import Style as VisualStyle
from textual.visual import Visual, visualize
from textual.visual import Visual, is_visual, visualize

if TYPE_CHECKING:
from textual.app import App, ComposeResult
Expand Down Expand Up @@ -1013,6 +1015,49 @@ def get_component_rich_style(self, *names: str, partial: bool = False) -> Style:

return partial_style if partial else style

def get_visual_style(self, *names: str) -> VisualStyle:
background = Color(0, 0, 0, 0)
color = Color(255, 255, 255, 0)

style = Style()
opacity = 1.0

def iter_styles() -> Iterable[StylesBase]:
for node in reversed(self.ancestors_with_self):
yield node.styles
for name in names:
yield node.get_component_styles(name)

for styles in iter_styles():
has_rule = styles.has_rule
opacity *= styles.opacity
if has_rule("background"):
text_background = background + styles.background.tint(
styles.background_tint
)
background += (
styles.background.tint(styles.background_tint)
).multiply_alpha(opacity)
else:
text_background = background
if has_rule("color"):
color = styles.color
style += styles.text_style
if has_rule("auto_color") and styles.auto_color:
color = text_background.get_contrast_text(color.a)

visual_style = VisualStyle(
background,
color,
bold=style.bold,
dim=style.dim,
italic=style.italic,
underline=style.underline,
strike=style.strike,
)

return visual_style

def render_str(self, text_content: str | Text) -> Text:
"""Convert str in to a Text object.
Expand Down Expand Up @@ -3500,6 +3545,7 @@ def _pseudo_classes_cache_key(self) -> tuple[int, ...]:
def _get_justify_method(self) -> JustifyMethod | None:
"""Get the justify method that may be passed to a Rich renderable."""
text_justify: JustifyMethod | None = None

if self.styles.has_rule("text_align"):
text_align: JustifyMethod = cast(JustifyMethod, self.styles.text_align)
text_justify = _JUSTIFY_MAP.get(text_align, text_align)
Expand Down Expand Up @@ -3923,6 +3969,9 @@ def _render(self) -> Visual:
A renderable.
"""
renderable = self.render()
if not is_visual(renderable):
renderable = self.post_render(renderable)

visual = visualize(self, renderable)
return visual

Expand Down
1 change: 0 additions & 1 deletion src/textual/widgets/_option_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,6 @@ def _render_option_content(
visual = visualize(self, content)
padding = self.get_component_styles("option-list--option").padding
if padding:
self.notify(str(padding))
visual = Padding(visual, padding)

strips = Visual.to_strips(
Expand Down

0 comments on commit 783b085

Please sign in to comment.