Skip to content

Commit 273cc04

Browse files
committed
link style
1 parent ccd54fd commit 273cc04

File tree

3 files changed

+55
-16
lines changed

3 files changed

+55
-16
lines changed

src/textual/_styles_cache.py

-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]:
152152
and hover_style._meta
153153
and "@click" in hover_style.meta
154154
):
155-
1 / 0
156155
link_style_hover = widget.link_style_hover
157156
if link_style_hover:
158157
strips = [

src/textual/content.py

+54-15
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ def get_optimal_width(self, container_width: int) -> int:
210210
lines = self.without_spans.split("\n")
211211
return max(line.cell_length for line in lines)
212212

213-
def wrap(
213+
def _wrap_and_format(
214214
self,
215215
width: int,
216216
align: TextAlign = "left",
@@ -219,8 +219,22 @@ def wrap(
219219
tab_size: int = 8,
220220
selection: Selection | None = None,
221221
selection_style: Style | None = None,
222-
) -> list[ContentLine]:
223-
output_lines: list[ContentLine] = []
222+
) -> list[FormattedLine]:
223+
"""Wraps the text and applies formatting.
224+
225+
Args:
226+
width: Desired width.
227+
align: Text alignment.
228+
overflow: Overflow method.
229+
no_wrap: Disabled wrapping.
230+
tab_size: Cell with of tabs.
231+
selection: Selection information or `None` if no selection.
232+
selection_style: Selection style, or `None` if no selection.
233+
234+
Returns:
235+
List of formatted lines.
236+
"""
237+
output_lines: list[FormattedLine] = []
224238

225239
if selection is not None:
226240
get_span = selection.get_span
@@ -236,7 +250,7 @@ def get_span(y: int) -> tuple[int, int] | None:
236250
end = len(line.plain)
237251
line = line.stylize(selection_style, start, end)
238252

239-
content_line = ContentLine(
253+
content_line = FormattedLine(
240254
line.expand_tabs(tab_size), width, y=y, align=align
241255
)
242256

@@ -246,7 +260,7 @@ def get_span(y: int) -> tuple[int, int] | None:
246260
offsets = divide_line(line.plain, width, fold=overflow == "fold")
247261
divided_lines = content_line.content.divide(offsets)
248262
new_lines = [
249-
ContentLine(
263+
FormattedLine(
250264
content.rstrip_end(width), width, offset, y, align=align
251265
)
252266
for content, offset in zip(divided_lines, [0, *offsets])
@@ -277,7 +291,7 @@ def render_strips(
277291
selection_style = None
278292

279293
align = self._align
280-
lines = self.wrap(
294+
lines = self._wrap_and_format(
281295
width,
282296
align=align,
283297
overflow=(
@@ -292,11 +306,11 @@ def render_strips(
292306
if height is not None:
293307
lines = lines[:height]
294308

295-
strip_lines = [line.to_strip(style) for line in lines]
309+
strip_lines = [line.to_strip(widget, style) for line in lines]
296310
return strip_lines
297311

298312
def get_height(self, width: int) -> int:
299-
lines = self.wrap(width)
313+
lines = self._wrap_and_format(width)
300314
return len(lines)
301315

302316
def __len__(self) -> int:
@@ -982,7 +996,9 @@ def highlight_regex(
982996
return Content(self._text, spans)
983997

984998

985-
class ContentLine:
999+
class FormattedLine:
1000+
"""A line of content with additional formatting information."""
1001+
9861002
def __init__(
9871003
self,
9881004
content: Content,
@@ -991,13 +1007,15 @@ def __init__(
9911007
y: int = 0,
9921008
align: TextAlign = "left",
9931009
line_end: bool = False,
1010+
link_style: Style | None = None,
9941011
) -> None:
9951012
self.content = content
9961013
self.width = width
9971014
self.x = x
9981015
self.y = y
9991016
self.align = align
10001017
self.line_end = line_end
1018+
self.link_style = link_style
10011019
self.highlight_style: Style | None = None
10021020
self.highlight_range: tuple[int | None, int | None] | None = None
10031021

@@ -1009,7 +1027,7 @@ def highlight(self, style: Style, start: int | None, end: int | None) -> None:
10091027
self.highlight_style = style
10101028
self.highlight_range = (start, end)
10111029

1012-
def to_strip(self, style: Style) -> Strip:
1030+
def to_strip(self, widget: Widget, style: Style) -> Strip:
10131031
_Segment = Segment
10141032
align = self.align
10151033
width = self.width
@@ -1024,8 +1042,6 @@ def to_strip(self, style: Style) -> Strip:
10241042

10251043
if align in ("start", "left") or (align == "justify" and self.line_end):
10261044
pass
1027-
# pad_right = width - self.content.cell_length
1028-
# pad_right = 0
10291045

10301046
elif align == "center":
10311047
excess_space = width - self.content.cell_length
@@ -1061,7 +1077,7 @@ def to_strip(self, style: Style) -> Strip:
10611077
if index < len(spaces) and (pad := spaces[index]):
10621078
add_segment(_Segment(" " * pad, (style + text_style).rich_style))
10631079

1064-
strip = Strip(segments, width)
1080+
strip = Strip(self._apply_link_style(widget, segments), width)
10651081
return strip
10661082

10671083
segments = (
@@ -1080,9 +1096,32 @@ def to_strip(self, style: Style) -> Strip:
10801096
segments.append(
10811097
_Segment(" " * pad_right, style.background_style.rich_style)
10821098
)
1083-
strip = Strip(segments, content.cell_length + pad_left + pad_right)
1099+
strip = Strip(
1100+
self._apply_link_style(widget, segments),
1101+
content.cell_length + pad_left + pad_right,
1102+
)
10841103
return strip
10851104

1105+
def _apply_link_style(
1106+
self, widget: Widget, segments: list[Segment]
1107+
) -> list[Segment]:
1108+
link_style = widget.link_style
1109+
_Segment = Segment
1110+
segments = [
1111+
_Segment(
1112+
text,
1113+
(
1114+
style
1115+
if style._meta is None
1116+
else (style + link_style if "@click" in style.meta else style)
1117+
),
1118+
control,
1119+
)
1120+
for text, style, control in segments
1121+
if style is not None
1122+
]
1123+
return segments
1124+
10861125

10871126
if __name__ == "__main__":
10881127
from rich import print
@@ -1116,7 +1155,7 @@ def to_strip(self, style: Style) -> Strip:
11161155
"will", Style(background=Color.parse("rgba(255, 255, 20, 0.3)"))
11171156
)
11181157

1119-
lines = content.wrap(40, align="full")
1158+
lines = content._wrap_and_format(40, align="full")
11201159
print(lines)
11211160
print("x" * 40)
11221161
for line in lines:

src/textual/visual.py

+1
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ def render_strips(
504504
render_width = width - (left + right)
505505
if render_width <= 0:
506506
return []
507+
507508
strips = self._visual.render_strips(
508509
widget,
509510
render_width,

0 commit comments

Comments
 (0)