Skip to content

Log widget with a border tries to render a line -1 #5634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
gunslingerfry opened this issue Mar 10, 2025 · 3 comments · Fixed by #5641
Closed

Log widget with a border tries to render a line -1 #5634

gunslingerfry opened this issue Mar 10, 2025 · 3 comments · Fixed by #5641

Comments

@gunslingerfry
Copy link

gunslingerfry commented Mar 10, 2025

Have you checked closed issues? yes

Have you checked against the most recent version of Textual? yes

The bug

Please give a brief but clear explanation of the issue. If you can, include a complete working example that demonstrates the bug. Check it can run without modifications.

If you add a border to the log widget and click slightly above the border it attempts to run render_line with a y=-1 and crashes.

minimum example:

#!/usr/bin/env -S uv run --script --no-config
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "textual >=2.1.2",
#     "textual-dev"
# ]
# ///

from textual.app import App, ComposeResult
from textual.widgets import Log

class MyApp(App):
    def compose(self) -> ComposeResult:
        self.my_log = Log()
        self.my_log.styles.border = ("round", "white")
        yield self.my_log

if __name__ == "__main__":
    MyApp().run()

Then click above the border.

Stack Trace

╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────── Traceback (most recent call last) ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ /home/matthew/.cache/uv/environments-v2/test-95a82182e4a7c559/lib/python3.12/site-packages/textual/app.py:3748 in on_event                                                                                                                                         │
│                                                                                                                                                                                                                                                                    │
│   3745 │   │   │   │   │   │   # Shouldn't occur, since at the very least this will find the Sc ╭──────────────────────────────────────── locals ────────────────────────────────────────╮                                                                         │
│   3746 │   │   │   │   │   │   self._mouse_down_widget = None                                   │     _ = Region(x=0, y=0, width=262, height=57)                                         │                                                                         │
│   3747 │   │   │   │                                                                            │ event = MouseDown(None, x=2, y=0, pointer_x=2.0, pointer_y=0.0, button=1)              │                                                                         │
│ ❱ 3748 │   │   │   │   self.screen._forward_event(event)                                        │  self = MyApp(title='MyApp', classes={'-dark-mode'}, pseudo_classes={'dark', 'focus'}) │                                                                         │
│   3749 │   │   │   │                                                                            ╰────────────────────────────────────────────────────────────────────────────────────────╯                                                                         │
│   3750 │   │   │   │   # If a MouseUp occurs at the same widget as a MouseDown, then we should                                                                                                                                                                     │
│   3751 │   │   │   │   # consider it a click, and produce a Click event.                                                                                                                                                                                           │
│                                                                                                                                                                                                                                                                    │
│ /home/matthew/.cache/uv/environments-v2/test-95a82182e4a7c559/lib/python3.12/site-packages/textual/screen.py:1533 in _forward_event                                                                                                                                │
│                                                                                                                                                                                                                                                                    │
│   1530 │   │   │   elif isinstance(event, events.MouseDown) and not self.app.mouse_captured:    ╭───────────────────────────────── locals ──────────────────────────────────╮                                                                                      │
│   1531 │   │   │   │   self._box_select = event.shift                                           │ event = MouseDown(None, x=2, y=0, pointer_x=2.0, pointer_y=0.0, button=1) │                                                                                      │
│   1532 │   │   │   │   self._mouse_down_offset = event.screen_offset                            │  self = Screen(id='_default')                                             │                                                                                      │
│ ❱ 1533 │   │   │   │   select_widget, select_offset = self.get_widget_and_offset_at(            ╰───────────────────────────────────────────────────────────────────────────╯                                                                                      │
│   1534 │   │   │   │   │   event.screen_x, event.screen_y                                                                                                                                                                                                          │
│   1535 │   │   │   │   )                                                                                                                                                                                                                                           │
│   1536 │   │   │   │   if (                                                                                                                                                                                                                                        │
│                                                                                                                                                                                                                                                                    │
│ /home/matthew/.cache/uv/environments-v2/test-95a82182e4a7c559/lib/python3.12/site-packages/textual/screen.py:634 in get_widget_and_offset_at                                                                                                                       │
│                                                                                                                                                                                                                                                                    │
│    631 │   │   Returns:                                                                         ╭─────────── locals ───────────╮                                                                                                                                   │
│    632 │   │   │   Tuple of Widget and Offset, both of which may be None.                       │ self = Screen(id='_default') │                                                                                                                                   │
│    633 │   │   """                                                                              │    x = 2                     │                                                                                                                                   │
│ ❱  634 │   │   return self._compositor.get_widget_and_offset_at(x, y)                           │    y = 0                     │                                                                                                                                   │
│    635 │                                                                                        ╰──────────────────────────────╯                                                                                                                                   │
│    636 │   def find_widget(self, widget: Widget) -> MapGeometry:                                                                                                                                                                                                   │
│    637 │   │   """Get the screen region of a Widget.                                                                                                                                                                                                               │
│                                                                                                                                                                                                                                                                    │
│ /home/matthew/.cache/uv/environments-v2/test-95a82182e4a7c559/lib/python3.12/site-packages/textual/_compositor.py:904 in get_widget_and_offset_at                                                                                                                  │
│                                                                                                                                                                                                                                                                    │
│    901 │   │   y -= region.y + gutter_right                                                     ╭────────────────────────────────────────────── locals ──────────────────────────────────────────────╮                                                             │
│    902 │   │                                                                                    │  gutter_left = 1                                                                                   │                                                             │
│    903 │   │   visible_screen_stack.set(widget.app._background_screens)                         │ gutter_right = 1                                                                                   │                                                             │
│ ❱  904 │   │   line = widget.render_line(y)                                                     │       region = Region(x=0, y=0, width=262, height=57)                                              │                                                             │
│    905 │   │                                                                                    │         self = <Compositor size=Size(width=262, height=57) widgets={Screen(id='_default'), Log()}> │                                                             │
│    906 │   │   end = 0                                                                          │       widget = Log()                                                                               │                                                             │
│    907 │   │   start = 0                                                                        │            x = 1                                                                                   │                                                             │
│                                                                                                 │            y = -1                                                                                  │                                                             │
│                                                                                                 ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯                                                             │
│                                                                                                                                                                                                                                                                    │
│ /home/matthew/.cache/uv/environments-v2/test-95a82182e4a7c559/lib/python3.12/site-packages/textual/widgets/_log.py:286 in render_line                                                                                                                              │
│                                                                                                                                                                                                                                                                    │
│   283 │   │   │   A rendered line.                                                             ╭───── locals ─────╮                                                                                                                                                │
│   284 │   │   """                                                                              │ scroll_x = 0     │                                                                                                                                                │
│   285 │   │   scroll_x, scroll_y = self.scroll_offset                                          │ scroll_y = 0     │                                                                                                                                                │
│ ❱ 286 │   │   strip = self._render_line(scroll_y + y, scroll_x, self.size.width)               │     self = Log() │                                                                                                                                                │
│   287 │   │   return strip                                                                     │        y = -1    │                                                                                                                                                │
│   288 │                                                                                        ╰──────────────────╯                                                                                                                                                │
│   289 │   def _render_line(self, y: int, scroll_x: int, width: int) -> Strip:                                                                                                                                                                                      │
│                                                                                                                                                                                                                                                                    │
│ /home/matthew/.cache/uv/environments-v2/test-95a82182e4a7c559/lib/python3.12/site-packages/textual/widgets/_log.py:304 in _render_line                                                                                                                             │
│                                                                                                                                                                                                                                                                    │
│   301 │   │   if y >= len(self._lines):                                                                                                                                                                                                                            │
│   302 │   │   │   return Strip.blank(width, rich_style)                                                                                                                                                                                                            │
│   303 │   │                                                                                                                                                                                                                                                        │
│ ❱ 304 │   │   line = self._render_line_strip(y, rich_style)                                                                                                                                                                                                        │
│   305 │   │   assert line._cell_length is not None                                                                                                                                                                                                                 │
│   306 │   │   line = line.crop_extend(scroll_x, scroll_x + width, rich_style)                                                                                                                                                                                      │
│   307 │   │   line = line.apply_offsets(scroll_x, y)                                                                                                                                                                                                               │
│                                                                                                                                                                                                                                                                    │
│ ╭────────────────────────────────────────────────────────────────────────────────────────────────────── locals ───────────────────────────────────────────────────────────────────────────────────────────────────────╮                                            │
│ │ rich_style = Style(color=Color('#e2e2e2', ColorType.TRUECOLOR, triplet=ColorTriplet(red=226, green=226, blue=226)), bgcolor=Color('#272727', ColorType.TRUECOLOR, triplet=ColorTriplet(red=39, green=39, blue=39))) │                                            │
│ │   scroll_x = 0                                                                                                                                                                                                      │                                            │
│ │       self = Log()                                                                                                                                                                                                  │                                            │
│ │      width = 260                                                                                                                                                                                                    │                                            │
│ │          y = -1                                                                                                                                                                                                     │                                            │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯                                            │
│                                                                                                                                                                                                                                                                    │
│ /home/matthew/.cache/uv/environments-v2/test-95a82182e4a7c559/lib/python3.12/site-packages/textual/widgets/_log.py:324 in _render_line_strip                                                                                                                       │
│                                                                                                                                                                                                                                                                    │
│   321 │   │   if y in self._render_line_cache and selection is None:                                                                                                                                                                                               │
│   322 │   │   │   return self._render_line_cache[y]                                                                                                                                                                                                                │
│   323 │   │                                                                                                                                                                                                                                                        │
│ ❱ 324 │   │   _line = self._process_line(self._lines[y])                                                                                                                                                                                                           │
│   325 │   │                                                                                                                                                                                                                                                        │
│   326 │   │   line_text = Text(_line, no_wrap=True)                                                                                                                                                                                                                │
│   327 │   │   line_text.stylize(rich_style)                                                                                                                                                                                                                        │
│                                                                                                                                                                                                                                                                    │
│ ╭────────────────────────────────────────────────────────────────────────────────────────────────────── locals ───────────────────────────────────────────────────────────────────────────────────────────────────────╮                                            │
│ │ rich_style = Style(color=Color('#e2e2e2', ColorType.TRUECOLOR, triplet=ColorTriplet(red=226, green=226, blue=226)), bgcolor=Color('#272727', ColorType.TRUECOLOR, triplet=ColorTriplet(red=39, green=39, blue=39))) │                                            │
│ │  selection = None                                                                                                                                                                                                   │                                            │
│ │       self = Log()                                                                                                                                                                                                  │                                            │
│ │          y = -1                                                                                                                                                                                                     │                                            │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯                                            │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
IndexError: list index out of range
textual diagnose

Textual Diagnostics

Versions

Name Value
Textual 2.1.2
Rich 13.9.4

Python

Name Value
Version 3.12.4
Implementation CPython
Compiler GCC 14.1.1 20240522
Executable /home/matthew/test/textual-log-bug/.venv/bin/python3

Operating System

Name Value
System Linux
Release 6.12.10-arch1-1
Version #1 SMP PREEMPT_DYNAMIC Sat, 18 Jan 2025 02:26:57 +0000

Terminal

Name Value
Terminal Application Unknown (konsole)
TERM xterm-256color
COLORTERM truecolor
FORCE_COLOR Not set
NO_COLOR Not set

Rich Console options

Name Value
size width=262, height=57
legacy_windows False
min_width 1
max_width 262
is_terminal True
encoding utf-8
max_height 57
justify None
overflow None
no_wrap False
highlight None
markup None
height None
Copy link

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

Copy link

Don't forget to star the repository!

Follow @textualizeio for Textual updates.

@gunslingerfry
Copy link
Author

You rock @willmcgugan !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant