euporie 2.6.2__py3-none-any.whl → 2.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. euporie/console/tabs/console.py +52 -38
  2. euporie/core/__init__.py +5 -2
  3. euporie/core/app.py +74 -57
  4. euporie/core/comm/ipywidgets.py +7 -3
  5. euporie/core/config.py +51 -27
  6. euporie/core/convert/__init__.py +2 -0
  7. euporie/core/convert/datum.py +82 -45
  8. euporie/core/convert/formats/ansi.py +1 -2
  9. euporie/core/convert/formats/common.py +7 -11
  10. euporie/core/convert/formats/ft.py +10 -7
  11. euporie/core/convert/formats/png.py +7 -6
  12. euporie/core/convert/formats/sixel.py +1 -1
  13. euporie/core/convert/formats/svg.py +28 -0
  14. euporie/core/convert/mime.py +4 -7
  15. euporie/core/data_structures.py +24 -22
  16. euporie/core/filters.py +16 -2
  17. euporie/core/format.py +30 -4
  18. euporie/core/ft/ansi.py +2 -1
  19. euporie/core/ft/html.py +155 -42
  20. euporie/core/{widgets/graphics.py → graphics.py} +225 -227
  21. euporie/core/io.py +8 -0
  22. euporie/core/key_binding/bindings/__init__.py +8 -2
  23. euporie/core/key_binding/bindings/basic.py +9 -14
  24. euporie/core/key_binding/bindings/micro.py +0 -12
  25. euporie/core/key_binding/bindings/mouse.py +107 -80
  26. euporie/core/key_binding/bindings/page_navigation.py +129 -0
  27. euporie/core/key_binding/key_processor.py +9 -1
  28. euporie/core/layout/__init__.py +1 -0
  29. euporie/core/layout/containers.py +1011 -0
  30. euporie/core/layout/decor.py +381 -0
  31. euporie/core/layout/print.py +130 -0
  32. euporie/core/layout/screen.py +75 -0
  33. euporie/core/{widgets/page.py → layout/scroll.py} +166 -111
  34. euporie/core/log.py +1 -1
  35. euporie/core/margins.py +11 -5
  36. euporie/core/path.py +43 -176
  37. euporie/core/renderer.py +31 -8
  38. euporie/core/style.py +2 -0
  39. euporie/core/tabs/base.py +2 -1
  40. euporie/core/terminal.py +19 -21
  41. euporie/core/widgets/cell.py +2 -4
  42. euporie/core/widgets/cell_outputs.py +2 -2
  43. euporie/core/widgets/decor.py +3 -359
  44. euporie/core/widgets/dialog.py +5 -5
  45. euporie/core/widgets/display.py +32 -12
  46. euporie/core/widgets/file_browser.py +3 -4
  47. euporie/core/widgets/forms.py +36 -14
  48. euporie/core/widgets/inputs.py +171 -99
  49. euporie/core/widgets/layout.py +80 -5
  50. euporie/core/widgets/menu.py +1 -3
  51. euporie/core/widgets/pager.py +3 -3
  52. euporie/core/widgets/palette.py +3 -2
  53. euporie/core/widgets/status_bar.py +2 -6
  54. euporie/core/widgets/tree.py +3 -6
  55. euporie/notebook/app.py +8 -8
  56. euporie/notebook/tabs/notebook.py +2 -2
  57. euporie/notebook/widgets/side_bar.py +1 -1
  58. euporie/preview/tabs/notebook.py +2 -2
  59. euporie/web/tabs/web.py +6 -1
  60. euporie/web/widgets/webview.py +52 -32
  61. {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/METADATA +9 -11
  62. {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/RECORD +67 -60
  63. {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/WHEEL +1 -1
  64. {euporie-2.6.2.data → euporie-2.7.0.data}/data/share/applications/euporie-console.desktop +0 -0
  65. {euporie-2.6.2.data → euporie-2.7.0.data}/data/share/applications/euporie-notebook.desktop +0 -0
  66. {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/entry_points.txt +0 -0
  67. {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,381 @@
1
+ """Decorative widgets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from prompt_toolkit.cache import FastDictCache
9
+ from prompt_toolkit.filters import has_focus
10
+ from prompt_toolkit.layout.containers import (
11
+ Container,
12
+ to_container,
13
+ )
14
+ from prompt_toolkit.layout.dimension import Dimension
15
+ from prompt_toolkit.layout.screen import Char, Screen, WritePosition
16
+ from prompt_toolkit.mouse_events import MouseEventType
17
+
18
+ from euporie.core.border import ThinLine
19
+ from euporie.core.current import get_app
20
+ from euporie.core.style import ColorPaletteColor
21
+
22
+ if TYPE_CHECKING:
23
+ from typing import Callable
24
+
25
+ from prompt_toolkit.key_binding.key_bindings import (
26
+ NotImplementedOrNone,
27
+ )
28
+ from prompt_toolkit.layout.containers import AnyContainer
29
+ from prompt_toolkit.layout.mouse_handlers import MouseHandlers
30
+ from prompt_toolkit.mouse_events import MouseEvent
31
+
32
+ from euporie.core.style import ColorPalette
33
+
34
+ MouseHandler = Callable[[MouseEvent], object]
35
+
36
+ log = logging.getLogger(__name__)
37
+
38
+
39
+ class Line(Container):
40
+ """Draw a horizontal or vertical line."""
41
+
42
+ def __init__(
43
+ self,
44
+ char: str | None = None,
45
+ width: int | None = None,
46
+ height: int | None = None,
47
+ collapse: bool = False,
48
+ style: str = "class:grid-line",
49
+ ) -> None:
50
+ """Initialize a grid line.
51
+
52
+ Args:
53
+ char: The character to draw. If unset, the relevant character from
54
+ :py:class:`euporie.core.box.grid` is used
55
+ width: The length of the line. If specified, the line will be horizontal
56
+ height: The height of the line. If specified, the line will be vertical
57
+ collapse: Whether to hide the line when there is not enough space
58
+ style: Style to apply to the line
59
+
60
+ Raises:
61
+ ValueError: If both width and height are specified. A line must only have a
62
+ single dimension.
63
+
64
+ """
65
+ if width and height:
66
+ raise ValueError("Only one of `width` or `height` must be set")
67
+ self.width = width
68
+ self.height = height
69
+ if char is None:
70
+ char = ThinLine.grid.VERTICAL if width else ThinLine.grid.HORIZONTAL
71
+ self.char = Char(char, style)
72
+ self.collapse = collapse
73
+
74
+ def reset(self) -> None:
75
+ """Reet the state of the line. Does nothing."""
76
+
77
+ def preferred_width(self, max_available_width: int) -> Dimension:
78
+ """Return the preferred width of the line."""
79
+ return Dimension(min=int(not self.collapse), max=self.width)
80
+
81
+ def preferred_height(self, width: int, max_available_height: int) -> Dimension:
82
+ """Return the preferred height of the line."""
83
+ return Dimension(min=int(not self.collapse), max=self.height)
84
+
85
+ def write_to_screen(
86
+ self,
87
+ screen: Screen,
88
+ mouse_handlers: MouseHandlers,
89
+ write_position: WritePosition,
90
+ parent_style: str,
91
+ erase_bg: bool,
92
+ z_index: int | None,
93
+ ) -> None:
94
+ """Draw a continuous line in the ``write_position`` area.
95
+
96
+ Args:
97
+ screen: The :class:`~prompt_toolkit.layout.screen.Screen` class to which
98
+ the output has to be written.
99
+ mouse_handlers: :class:`prompt_toolkit.layout.mouse_handlers.MouseHandlers`.
100
+ write_position: A :class:`prompt_toolkit.layout.screen.WritePosition` object
101
+ defining where this container should be drawn.
102
+ erase_bg: If true, the background will be erased prior to drawing.
103
+ parent_style: Style string to pass to the :class:`.Window` object. This will
104
+ be applied to all content of the windows. :class:`.VSplit` and
105
+ :class:`prompt_toolkit.layout.containers.HSplit` can use it to pass
106
+ their style down to the windows that they contain.
107
+ z_index: Used for propagating z_index from parent to child.
108
+
109
+ """
110
+ ypos = write_position.ypos
111
+ xpos = write_position.xpos
112
+
113
+ for y in range(ypos, ypos + write_position.height):
114
+ row = screen.data_buffer[y]
115
+ for x in range(xpos, xpos + write_position.width):
116
+ row[x] = self.char
117
+
118
+ def get_children(self) -> list:
119
+ """Return an empty list of the container's children."""
120
+ return []
121
+
122
+
123
+ class Pattern(Container):
124
+ """Fill an area with a repeating background pattern."""
125
+
126
+ def __init__(
127
+ self,
128
+ char: str | Callable[[], str],
129
+ pattern: int | Callable[[], int] = 1,
130
+ ) -> None:
131
+ """Initialize the :class:`Pattern`."""
132
+ self.bg = Char(" ", "class:pattern")
133
+ self.char = char
134
+ self.pattern = pattern
135
+
136
+ def reset(self) -> None:
137
+ """Reet the pattern. Does nothing."""
138
+
139
+ def preferred_width(self, max_available_width: int) -> Dimension:
140
+ """Return an empty dimension (expand to available width)."""
141
+ return Dimension()
142
+
143
+ def preferred_height(self, width: int, max_available_height: int) -> Dimension:
144
+ """Return an empty dimension (expand to available height)."""
145
+ return Dimension()
146
+
147
+ def write_to_screen(
148
+ self,
149
+ screen: Screen,
150
+ mouse_handlers: MouseHandlers,
151
+ write_position: WritePosition,
152
+ parent_style: str,
153
+ erase_bg: bool,
154
+ z_index: int | None,
155
+ ) -> None:
156
+ """Fill the whole area of write_position with a pattern.
157
+
158
+ Args:
159
+ screen: The :class:`~prompt_toolkit.layout.screen.Screen` class to which
160
+ the output has to be written.
161
+ mouse_handlers: :class:`prompt_toolkit.layout.mouse_handlers.MouseHandlers`.
162
+ write_position: A :class:`prompt_toolkit.layout.screen.WritePosition` object
163
+ defining where this container should be drawn.
164
+ erase_bg: If true, the background will be erased prior to drawing.
165
+ parent_style: Style string to pass to the :class:`.Window` object. This will
166
+ be applied to all content of the windows. :class:`.VSplit` and
167
+ :class:`prompt_toolkit.layout.containers.HSplit` can use it to pass
168
+ their style down to the windows that they contain.
169
+ z_index: Used for propagating z_index from parent to child.
170
+
171
+ """
172
+ bg = self.bg
173
+ pattern = self.pattern() if callable(self.pattern) else self.pattern
174
+ if callable(self.char):
175
+ char = Char(self.char(), "class:pattern")
176
+ else:
177
+ char = Char(self.char, "class:pattern")
178
+
179
+ ypos = write_position.ypos
180
+ xpos = write_position.xpos
181
+
182
+ for y in range(ypos, ypos + write_position.height):
183
+ row = screen.data_buffer[y]
184
+ for x in range(xpos, xpos + write_position.width):
185
+ if (
186
+ (pattern == 1)
187
+ or (pattern == 2 and (x + y) % 2 == 0)
188
+ or (pattern == 3 and (x + 2 * y) % 4 == 0)
189
+ or (pattern == 4 and (x + y) % 3 == 0)
190
+ or (pattern == 5 and ((x + y % 2 * 3) % 6) % 4 == 0)
191
+ ):
192
+ row[x] = char
193
+ else:
194
+ row[x] = bg
195
+
196
+ def get_children(self) -> list:
197
+ """Return an empty list of the container's children."""
198
+ return []
199
+
200
+
201
+ class FocusedStyle(Container):
202
+ """Apply a style to child containers when focused or hovered."""
203
+
204
+ def __init__(
205
+ self,
206
+ body: AnyContainer,
207
+ style_focus: str | Callable[[], str] = "class:focused",
208
+ style_hover: str | Callable[[], str] = "",
209
+ ) -> None:
210
+ """Create a new instance of the widget.
211
+
212
+ Args:
213
+ body: The container to act on
214
+ style_focus: The style to apply when the body has focus
215
+ style_hover: The style to apply when the body is hovered
216
+ """
217
+ self.body = body
218
+ self.style_focus = style_focus
219
+ self.style_hover = style_hover
220
+ self.hover = False
221
+ self.has_focus = has_focus(self.body)
222
+
223
+ def reset(self) -> None:
224
+ """Reet the wrapped container."""
225
+ to_container(self.body).reset()
226
+
227
+ def preferred_width(self, max_available_width: int) -> Dimension:
228
+ """Return the wrapped container's preferred width."""
229
+ return to_container(self.body).preferred_width(max_available_width)
230
+
231
+ def preferred_height(self, width: int, max_available_height: int) -> Dimension:
232
+ """Return the wrapped container's preferred height."""
233
+ return to_container(self.body).preferred_height(width, max_available_height)
234
+
235
+ def write_to_screen(
236
+ self,
237
+ screen: Screen,
238
+ mouse_handlers: MouseHandlers,
239
+ write_position: WritePosition,
240
+ parent_style: str,
241
+ erase_bg: bool,
242
+ z_index: int | None,
243
+ ) -> None:
244
+ """Draw the wrapped container with the additional style."""
245
+ to_container(self.body).write_to_screen(
246
+ screen,
247
+ mouse_handlers,
248
+ write_position,
249
+ f"{parent_style} {self.get_style()}",
250
+ erase_bg,
251
+ z_index,
252
+ )
253
+
254
+ if self.style_hover:
255
+ x_min = write_position.xpos
256
+ x_max = x_min + write_position.width
257
+ y_min = write_position.ypos
258
+ y_max = y_min + write_position.height
259
+
260
+ # Wrap mouse handlers to add "hover" class on hover
261
+ def _wrap_mouse_handler(handler: Callable) -> MouseHandler:
262
+ def wrapped_mouse_handler(
263
+ mouse_event: MouseEvent,
264
+ ) -> NotImplementedOrNone:
265
+ result = handler(mouse_event)
266
+
267
+ if mouse_event.event_type == MouseEventType.MOUSE_MOVE:
268
+ app = get_app()
269
+ if (
270
+ mouse_event.position.x,
271
+ mouse_event.position.y,
272
+ ) == app.mouse_position:
273
+ app.mouse_limits = write_position
274
+ self.hover = True
275
+ else:
276
+ app.mouse_limits = None
277
+ self.hover = False
278
+ result = None
279
+ return result
280
+
281
+ return wrapped_mouse_handler
282
+
283
+ mouse_handler_wrappers: FastDictCache[
284
+ tuple[Callable], MouseHandler
285
+ ] = FastDictCache(get_value=_wrap_mouse_handler)
286
+ for y in range(y_min, y_max):
287
+ row = mouse_handlers.mouse_handlers[y]
288
+ for x in range(x_min, x_max):
289
+ row[x] = mouse_handler_wrappers[(row[x],)]
290
+
291
+ def get_style(self) -> str:
292
+ """Determine the style to apply depending on the focus status."""
293
+ style = ""
294
+ if self.has_focus():
295
+ style += (
296
+ self.style_focus() if callable(self.style_focus) else self.style_focus
297
+ )
298
+ if self.hover:
299
+ style += " " + (
300
+ self.style_hover() if callable(self.style_hover) else self.style_hover
301
+ )
302
+ return style
303
+
304
+ def get_children(self) -> list[Container]:
305
+ """Return the list of child :class:`.Container` objects."""
306
+ return [to_container(self.body)]
307
+
308
+
309
+ class DropShadow(Container):
310
+ """A transparent container which makes the background darker."""
311
+
312
+ def __init__(self, amount: float = 0.5) -> None:
313
+ """Create a new instance."""
314
+ self.amount = amount
315
+ self.renderer = get_app().renderer
316
+
317
+ @property
318
+ def cp(self) -> ColorPalette:
319
+ """Get the current app's current color palette."""
320
+ return get_app().color_palette
321
+
322
+ def reset(self) -> None:
323
+ """Reet the wrapped container - here, do nothing."""
324
+
325
+ def preferred_width(self, max_available_width: int) -> Dimension:
326
+ """Return the wrapped container's preferred width."""
327
+ return Dimension(weight=1)
328
+
329
+ def preferred_height(self, width: int, max_available_height: int) -> Dimension:
330
+ """Return the wrapped container's preferred height."""
331
+ return Dimension(weight=1)
332
+
333
+ def write_to_screen(
334
+ self,
335
+ screen: Screen,
336
+ mouse_handlers: MouseHandlers,
337
+ write_position: WritePosition,
338
+ parent_style: str,
339
+ erase_bg: bool,
340
+ z_index: int | None,
341
+ ) -> None:
342
+ """Draw the wrapped container with the additional style."""
343
+ attr_cache = self.renderer._attrs_for_style
344
+ if attr_cache is not None:
345
+ ypos = write_position.ypos
346
+ xpos = write_position.xpos
347
+ amount = self.amount
348
+ for y in range(ypos, ypos + write_position.height):
349
+ row = screen.data_buffer[y]
350
+ for x in range(xpos, xpos + write_position.width):
351
+ char = row[x]
352
+ style = char.style
353
+ attrs = attr_cache[style]
354
+
355
+ if not (fg := attrs.color) or fg == "default":
356
+ color = self.cp.fg
357
+ style += f" fg:{color.darker(amount)}"
358
+ else:
359
+ try:
360
+ color = ColorPaletteColor(fg)
361
+ except ValueError:
362
+ pass
363
+ else:
364
+ style += f" fg:{color.darker(amount)}"
365
+
366
+ if not (bg := attrs.bgcolor) or bg == "default":
367
+ color = self.cp.bg
368
+ style += f" bg:{color.darker(amount)}"
369
+ else:
370
+ try:
371
+ color = ColorPaletteColor(bg)
372
+ except ValueError:
373
+ pass
374
+ else:
375
+ style += f" bg:{color.darker(amount)}"
376
+
377
+ row[x] = Char(char=char.char, style=style)
378
+
379
+ def get_children(self) -> list[Container]:
380
+ """Return an empty list of child :class:`.Container` objects."""
381
+ return []
@@ -0,0 +1,130 @@
1
+ """Defines a container which displays all children at full height vertially stacked."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from prompt_toolkit.layout.containers import (
9
+ Container,
10
+ Window,
11
+ to_container,
12
+ )
13
+ from prompt_toolkit.layout.dimension import Dimension, to_dimension
14
+
15
+ from euporie.core.layout.screen import BoundedWritePosition
16
+
17
+ if TYPE_CHECKING:
18
+ from typing import Callable, Sequence
19
+
20
+ from prompt_toolkit.key_binding.key_bindings import (
21
+ KeyBindingsBase,
22
+ )
23
+ from prompt_toolkit.layout.containers import AnyContainer
24
+ from prompt_toolkit.layout.dimension import AnyDimension
25
+ from prompt_toolkit.layout.mouse_handlers import MouseHandlers
26
+ from prompt_toolkit.layout.screen import Screen, WritePosition
27
+ from prompt_toolkit.mouse_events import MouseEvent
28
+
29
+ MouseHandler = Callable[[MouseEvent], object]
30
+
31
+ log = logging.getLogger(__name__)
32
+
33
+
34
+ class PrintingContainer(Container):
35
+ """A container which displays all it's children in a vertical list."""
36
+
37
+ def __init__(
38
+ self,
39
+ children: Callable | Sequence[AnyContainer],
40
+ width: AnyDimension = None,
41
+ key_bindings: KeyBindingsBase | None = None,
42
+ ) -> None:
43
+ """Initiate the container."""
44
+ self.width = width
45
+ self.rendered = False
46
+ self._children = children
47
+ self.key_bindings = key_bindings
48
+
49
+ def get_key_bindings(self) -> KeyBindingsBase | None:
50
+ """Return the container's key bindings."""
51
+ return self.key_bindings
52
+
53
+ @property
54
+ def children(self) -> Sequence[AnyContainer]:
55
+ """Return the container's children."""
56
+ children = self._children() if callable(self._children) else self._children
57
+ return children or [Window()]
58
+
59
+ def get_children(self) -> list[Container]:
60
+ """Return a list of all child containers."""
61
+ return list(map(to_container, self.children))
62
+
63
+ def write_to_screen(
64
+ self,
65
+ screen: Screen,
66
+ mouse_handlers: MouseHandlers,
67
+ write_position: WritePosition,
68
+ parent_style: str,
69
+ erase_bg: bool,
70
+ z_index: int | None,
71
+ ) -> None:
72
+ """Render the container to a `Screen` instance.
73
+
74
+ All children are rendered vertically in sequence.
75
+
76
+ Args:
77
+ screen: The :class:`~prompt_toolkit.layout.screen.Screen` class to which
78
+ the output has to be written.
79
+ mouse_handlers: :class:`prompt_toolkit.layout.mouse_handlers.MouseHandlers`.
80
+ write_position: A :class:`prompt_toolkit.layout.screen.WritePosition` object
81
+ defining where this container should be drawn.
82
+ erase_bg: If true, the background will be erased prior to drawing.
83
+ parent_style: Style string to pass to the :class:`.Window` object. This will
84
+ be applied to all content of the windows. :class:`.VSplit` and
85
+ :class:`prompt_toolkit.layout.containers.HSplit` can use it to pass
86
+ their style down to the windows that they contain.
87
+ z_index: Used for propagating z_index from parent to child.
88
+
89
+ """
90
+ xpos = write_position.xpos
91
+ ypos = write_position.ypos
92
+
93
+ children = self.get_children()
94
+ for child in children:
95
+ height = child.preferred_height(write_position.width, 999999).preferred
96
+ child.write_to_screen(
97
+ screen,
98
+ mouse_handlers,
99
+ BoundedWritePosition(xpos, ypos, write_position.width, height),
100
+ parent_style,
101
+ erase_bg,
102
+ z_index,
103
+ )
104
+ ypos += height
105
+
106
+ def preferred_height(self, width: int, max_available_height: int) -> Dimension:
107
+ """Return the preferred height, equal to the sum of the child heights."""
108
+ return Dimension(
109
+ min=1,
110
+ preferred=sum(
111
+ [
112
+ c.preferred_height(width, max_available_height).preferred
113
+ for c in self.get_children()
114
+ ]
115
+ ),
116
+ )
117
+
118
+ def preferred_width(self, max_available_width: int) -> Dimension:
119
+ """Calculate and returns the desired width for this container."""
120
+ if self.width is not None:
121
+ dim = to_dimension(self.width).preferred
122
+ return Dimension(max=dim, preferred=dim)
123
+ else:
124
+ return Dimension(max_available_width)
125
+
126
+ def reset(self) -> None:
127
+ """Reet the state of this container and all the children.
128
+
129
+ Does nothing as this container is used for dumping output.
130
+ """
@@ -0,0 +1,75 @@
1
+ """Overrides for PTK containers which only render visible lines."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from prompt_toolkit.layout import screen
8
+
9
+ from euporie.core.data_structures import DiInt
10
+
11
+ log = logging.getLogger(__name__)
12
+
13
+
14
+ class BoundedWritePosition(screen.WritePosition):
15
+ """A write position which also hold bounding box information."""
16
+
17
+ def __init__(
18
+ self,
19
+ xpos: int,
20
+ ypos: int,
21
+ width: int,
22
+ height: int,
23
+ bbox: DiInt | None = None,
24
+ ) -> None:
25
+ """Create a new instance of the write position."""
26
+ super().__init__(xpos, ypos, width, height)
27
+ self.bbox = bbox or DiInt(0, 0, 0, 0)
28
+
29
+ def __repr__(self) -> str:
30
+ """Return a string representation of the write position."""
31
+ return (
32
+ f"{self.__class__.__name__}("
33
+ f"x={self.xpos}, y={self.ypos}, "
34
+ f"w={self.width}, h={self.height}, "
35
+ f"bbox={self.bbox})"
36
+ )
37
+
38
+
39
+ class Screen(screen.Screen):
40
+ """Screen class which uses :py:`BoundedWritePosition`s."""
41
+
42
+ def fill_area(
43
+ self, write_position: screen.WritePosition, style: str = "", after: bool = False
44
+ ) -> None:
45
+ """Fill the content of this area, using the given `style`."""
46
+ if not style.strip():
47
+ return
48
+
49
+ if isinstance(write_position, BoundedWritePosition):
50
+ bbox = write_position.bbox
51
+ else:
52
+ bbox = DiInt(0, 0, 0, 0)
53
+
54
+ xmin = write_position.xpos + bbox.left
55
+ xmax = write_position.xpos + write_position.width - bbox.right
56
+ char_cache = screen._CHAR_CACHE
57
+ data_buffer = self.data_buffer
58
+
59
+ if after:
60
+ append_style = " " + style
61
+ prepend_style = ""
62
+ else:
63
+ append_style = ""
64
+ prepend_style = style + " "
65
+
66
+ for y in range(
67
+ write_position.ypos + bbox.top,
68
+ write_position.ypos + write_position.height - bbox.bottom,
69
+ ):
70
+ row = data_buffer[y]
71
+ for x in range(xmin, xmax):
72
+ cell = row[x]
73
+ row[x] = char_cache[
74
+ cell.char, prepend_style + cell.style + append_style
75
+ ]