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.
- euporie/console/tabs/console.py +52 -38
- euporie/core/__init__.py +5 -2
- euporie/core/app.py +74 -57
- euporie/core/comm/ipywidgets.py +7 -3
- euporie/core/config.py +51 -27
- euporie/core/convert/__init__.py +2 -0
- euporie/core/convert/datum.py +82 -45
- euporie/core/convert/formats/ansi.py +1 -2
- euporie/core/convert/formats/common.py +7 -11
- euporie/core/convert/formats/ft.py +10 -7
- euporie/core/convert/formats/png.py +7 -6
- euporie/core/convert/formats/sixel.py +1 -1
- euporie/core/convert/formats/svg.py +28 -0
- euporie/core/convert/mime.py +4 -7
- euporie/core/data_structures.py +24 -22
- euporie/core/filters.py +16 -2
- euporie/core/format.py +30 -4
- euporie/core/ft/ansi.py +2 -1
- euporie/core/ft/html.py +155 -42
- euporie/core/{widgets/graphics.py → graphics.py} +225 -227
- euporie/core/io.py +8 -0
- euporie/core/key_binding/bindings/__init__.py +8 -2
- euporie/core/key_binding/bindings/basic.py +9 -14
- euporie/core/key_binding/bindings/micro.py +0 -12
- euporie/core/key_binding/bindings/mouse.py +107 -80
- euporie/core/key_binding/bindings/page_navigation.py +129 -0
- euporie/core/key_binding/key_processor.py +9 -1
- euporie/core/layout/__init__.py +1 -0
- euporie/core/layout/containers.py +1011 -0
- euporie/core/layout/decor.py +381 -0
- euporie/core/layout/print.py +130 -0
- euporie/core/layout/screen.py +75 -0
- euporie/core/{widgets/page.py → layout/scroll.py} +166 -111
- euporie/core/log.py +1 -1
- euporie/core/margins.py +11 -5
- euporie/core/path.py +43 -176
- euporie/core/renderer.py +31 -8
- euporie/core/style.py +2 -0
- euporie/core/tabs/base.py +2 -1
- euporie/core/terminal.py +19 -21
- euporie/core/widgets/cell.py +2 -4
- euporie/core/widgets/cell_outputs.py +2 -2
- euporie/core/widgets/decor.py +3 -359
- euporie/core/widgets/dialog.py +5 -5
- euporie/core/widgets/display.py +32 -12
- euporie/core/widgets/file_browser.py +3 -4
- euporie/core/widgets/forms.py +36 -14
- euporie/core/widgets/inputs.py +171 -99
- euporie/core/widgets/layout.py +80 -5
- euporie/core/widgets/menu.py +1 -3
- euporie/core/widgets/pager.py +3 -3
- euporie/core/widgets/palette.py +3 -2
- euporie/core/widgets/status_bar.py +2 -6
- euporie/core/widgets/tree.py +3 -6
- euporie/notebook/app.py +8 -8
- euporie/notebook/tabs/notebook.py +2 -2
- euporie/notebook/widgets/side_bar.py +1 -1
- euporie/preview/tabs/notebook.py +2 -2
- euporie/web/tabs/web.py +6 -1
- euporie/web/widgets/webview.py +52 -32
- {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/METADATA +9 -11
- {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/RECORD +67 -60
- {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/WHEEL +1 -1
- {euporie-2.6.2.data → euporie-2.7.0.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.6.2.data → euporie-2.7.0.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.6.2.dist-info → euporie-2.7.0.dist-info}/entry_points.txt +0 -0
- {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
|
+
]
|