@@ -66,6 +66,11 @@ def __init__(
66
66
id: The ID of the canvas widget in the DOM.
67
67
classes: The CSS classes of the canvas widget.
68
68
disabled: Whether the canvas widget is disabled or not.
69
+
70
+ If `canvas_color` is omitted, the widget's `background` styling will
71
+ be used.
72
+
73
+ If `pen_color` is omitted, the widget's `color` styling will be used.
69
74
"""
70
75
super ().__init__ (name = name , id = id , classes = classes , disabled = disabled )
71
76
self ._width = width
@@ -76,25 +81,16 @@ def __init__(
76
81
"""The background colour of the canvas itself."""
77
82
self ._pen_colour = pen_color
78
83
"""The default pen colour, used when drawing pixels."""
79
- self ._the_void : list [Color ] = []
80
- """The final empty line if the last row isn't part of the canvas."""
81
- self ._canvas : list [list [Color ]] = []
84
+ self ._canvas : list [list [Color | None ]] = self ._blank_canvas
82
85
"""The canvas itself."""
83
86
self .virtual_size = Size (width , ceil (height / 2 ))
84
87
85
88
@property
86
- def _blank_canvas (self ) -> list [list [Color ]]:
89
+ def _blank_canvas (self ) -> list [list [Color | None ]]:
87
90
"""A blank canvas."""
88
- canvas_colour = self ._canvas_colour or self .styles .background
89
- return [[canvas_colour for _ in range (self .width )] for _ in range (self .height )]
90
-
91
- def on_mount (self ) -> None :
92
- """Initialise the widget once the DOM is mounted."""
93
- # Now that we know the background colour, because CSS will have been
94
- # applied, we can create the void line.
95
- self ._the_void = [self .styles .background for _ in range (self ._width )]
96
- # For the same reason, it's now safe to actually create the canvas.
97
- self ._canvas = self ._blank_canvas
91
+ return [
92
+ [self ._canvas_colour for _ in range (self .width )] for _ in range (self .height )
93
+ ]
98
94
99
95
@property
100
96
def width (self ) -> int :
@@ -106,6 +102,10 @@ def height(self) -> int:
106
102
"""The height of the canvas in 'pixels'."""
107
103
return self ._height
108
104
105
+ def notify_style_update (self ) -> None :
106
+ self .refresh ()
107
+ return super ().notify_style_update ()
108
+
109
109
def _outwith_the_canvas (self , x : int , y : int ) -> bool :
110
110
"""Is the location outwith the canvas?
111
111
@@ -145,12 +145,13 @@ def clear(self, color: Color | None = None) -> Self:
145
145
making the canvas is used, this in turn becomes the new default
146
146
color (and will then be used for subsequent clears, unless
147
147
another color is provided).
148
+
149
+ Explicitly setting the colour to [`None`][None] will set the
150
+ canvas colour to whatever the widget's `background` colour is.
148
151
"""
149
- if color is not None :
150
- self ._canvas_colour = color
152
+ self ._canvas_colour = color or self ._canvas_colour
151
153
self ._canvas = self ._blank_canvas
152
- self .refresh ()
153
- return self
154
+ return self .refresh ()
154
155
155
156
def set_pen (self , color : Color | None ) -> Self :
156
157
"""Set the default pen colour.
@@ -209,12 +210,7 @@ def clear_pixels(self, locations: Iterable[tuple[int, int]]) -> Self:
209
210
Note:
210
211
The origin of the canvas is the top left corner.
211
212
"""
212
- color = self ._canvas_colour or self .styles .background
213
- for x , y in locations :
214
- self ._pixel_check (x , y )
215
- self ._canvas [y ][x ] = color
216
- self .refresh ()
217
- return self
213
+ return self .set_pixels (locations , self ._canvas_colour )
218
214
219
215
def set_pixel (self , x : int , y : int , color : Color | None = None ) -> Self :
220
216
"""Set the colour of a specific pixel on the canvas.
@@ -264,7 +260,7 @@ def get_pixel(self, x: int, y: int) -> Color:
264
260
The origin of the canvas is the top left corner.
265
261
"""
266
262
self ._pixel_check (x , y )
267
- return self ._canvas [y ][x ]
263
+ return self ._canvas [y ][x ] or self . styles . background
268
264
269
265
def draw_line (
270
266
self , x0 : int , y0 : int , x1 : int , y1 : int , color : Color | None = None
@@ -431,7 +427,7 @@ def render_line(self, y: int) -> Strip:
431
427
y: The line to render.
432
428
433
429
Returns:
434
- A `Strip` that is the line to render.
430
+ A [ `Strip`][textual.strip.Strip] that is the line to render.
435
431
"""
436
432
437
433
# Get where we're scrolled to.
@@ -446,24 +442,42 @@ def render_line(self, y: int) -> Strip:
446
442
# Yup. Don't bother drawing anything.
447
443
return Strip ([])
448
444
445
+ # Set up the two main background colours we need.
446
+ background_colour = self .styles .background
447
+ canvas_colour = self ._canvas_colour or background_colour
448
+
449
+ # Reduce some attribute lookups.
450
+ height = self ._height
451
+ width = self ._width
452
+ canvas = self ._canvas
453
+
449
454
# Now, the bottom line is easy enough to work out.
450
455
bottom_line = top_line + 1
451
456
452
457
# Get the pixel values for the top line.
453
- top_pixels = self . _canvas [top_line ]
458
+ top_pixels = canvas [top_line ]
454
459
455
- # It's possible that the bottom line might be in the void, so...
460
+ # It's possible that the bottom line might be outwith the canvas
461
+ # itself; so here we set the bottom line to the widget's background
462
+ # colour if it is, otherwise we use the line form the canvas.
456
463
bottom_pixels = (
457
- self ._the_void if bottom_line >= self .height else self ._canvas [bottom_line ]
464
+ [background_colour for _ in range (width )]
465
+ if bottom_line >= height
466
+ else canvas [bottom_line ]
458
467
)
459
468
460
469
# At this point we know what colours we're going to be mashing
461
470
# together into the terminal line we're drawing. So let's get to it.
471
+ # Note that in every case, if the colour we have is `None` that
472
+ # means we're using the canvas colour.
462
473
return (
463
474
Strip (
464
475
[
465
- self ._segment_of (top_pixels [pixel ], bottom_pixels [pixel ])
466
- for pixel in range (self .width )
476
+ self ._segment_of (
477
+ top_pixels [pixel ] or canvas_colour ,
478
+ bottom_pixels [pixel ] or canvas_colour ,
479
+ )
480
+ for pixel in range (width )
467
481
]
468
482
)
469
483
.crop (scroll_x , scroll_x + self .scrollable_content_region .width )
0 commit comments