euporie 2.8.1__py3-none-any.whl → 2.8.5__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/_commands.py +143 -0
- euporie/console/_settings.py +58 -0
- euporie/console/app.py +25 -71
- euporie/console/tabs/console.py +267 -147
- euporie/core/__init__.py +1 -9
- euporie/core/__main__.py +31 -5
- euporie/core/_settings.py +104 -0
- euporie/core/app/__init__.py +3 -0
- euporie/core/app/_commands.py +70 -0
- euporie/core/app/_settings.py +427 -0
- euporie/core/{app.py → app/app.py} +214 -572
- euporie/core/app/base.py +51 -0
- euporie/core/{current.py → app/current.py} +13 -4
- euporie/core/app/cursor.py +35 -0
- euporie/core/app/dummy.py +12 -0
- euporie/core/app/launch.py +28 -0
- euporie/core/bars/__init__.py +11 -0
- euporie/core/bars/command.py +182 -0
- euporie/core/bars/menu.py +258 -0
- euporie/core/{widgets → bars}/search.py +154 -57
- euporie/core/{widgets → bars}/status.py +9 -26
- euporie/core/clipboard.py +19 -80
- euporie/core/comm/base.py +8 -6
- euporie/core/comm/ipywidgets.py +21 -12
- euporie/core/comm/registry.py +2 -1
- euporie/core/commands.py +11 -5
- euporie/core/completion.py +3 -2
- euporie/core/config.py +368 -341
- euporie/core/convert/__init__.py +0 -30
- euporie/core/convert/datum.py +131 -60
- euporie/core/convert/formats/__init__.py +31 -0
- euporie/core/convert/formats/ansi.py +46 -30
- euporie/core/convert/formats/common.py +11 -23
- euporie/core/convert/formats/html.py +45 -40
- euporie/core/convert/formats/pil.py +1 -1
- euporie/core/convert/formats/png.py +3 -5
- euporie/core/convert/formats/sixel.py +3 -3
- euporie/core/convert/registry.py +11 -8
- euporie/core/convert/utils.py +50 -23
- euporie/core/diagnostics.py +2 -2
- euporie/core/filters.py +72 -82
- euporie/core/format.py +13 -2
- euporie/core/ft/ansi.py +1 -1
- euporie/core/ft/html.py +36 -36
- euporie/core/ft/table.py +1 -3
- euporie/core/ft/utils.py +4 -1
- euporie/core/graphics.py +216 -124
- euporie/core/history.py +2 -2
- euporie/core/inspection.py +3 -2
- euporie/core/io.py +207 -28
- euporie/core/kernel/__init__.py +1 -0
- euporie/core/{kernel.py → kernel/client.py} +100 -139
- euporie/core/kernel/manager.py +114 -0
- euporie/core/key_binding/bindings/__init__.py +2 -8
- euporie/core/key_binding/bindings/basic.py +47 -7
- euporie/core/key_binding/bindings/completion.py +3 -8
- euporie/core/key_binding/bindings/micro.py +5 -7
- euporie/core/key_binding/bindings/mouse.py +26 -24
- euporie/core/key_binding/bindings/terminal.py +193 -0
- euporie/core/key_binding/bindings/vi.py +46 -0
- euporie/core/key_binding/key_processor.py +43 -2
- euporie/core/key_binding/registry.py +2 -0
- euporie/core/key_binding/utils.py +22 -2
- euporie/core/keys.py +7156 -93
- euporie/core/layout/cache.py +35 -25
- euporie/core/layout/containers.py +280 -74
- euporie/core/layout/decor.py +5 -5
- euporie/core/layout/mouse.py +1 -1
- euporie/core/layout/print.py +16 -3
- euporie/core/layout/scroll.py +26 -28
- euporie/core/log.py +75 -60
- euporie/core/lsp.py +118 -24
- euporie/core/margins.py +60 -31
- euporie/core/path.py +2 -1
- euporie/core/renderer.py +58 -17
- euporie/core/style.py +60 -40
- euporie/core/suggest.py +103 -85
- euporie/core/tabs/__init__.py +34 -0
- euporie/core/tabs/_settings.py +113 -0
- euporie/core/tabs/base.py +11 -435
- euporie/core/tabs/kernel.py +420 -0
- euporie/core/tabs/notebook.py +20 -54
- euporie/core/utils.py +98 -6
- euporie/core/validation.py +1 -1
- euporie/core/widgets/_settings.py +188 -0
- euporie/core/widgets/cell.py +90 -158
- euporie/core/widgets/cell_outputs.py +25 -36
- euporie/core/widgets/decor.py +11 -41
- euporie/core/widgets/dialog.py +55 -44
- euporie/core/widgets/display.py +27 -24
- euporie/core/widgets/file_browser.py +5 -26
- euporie/core/widgets/forms.py +16 -12
- euporie/core/widgets/inputs.py +37 -81
- euporie/core/widgets/layout.py +7 -6
- euporie/core/widgets/logo.py +49 -0
- euporie/core/widgets/menu.py +13 -11
- euporie/core/widgets/pager.py +8 -11
- euporie/core/widgets/palette.py +6 -6
- euporie/hub/app.py +52 -31
- euporie/notebook/_commands.py +24 -0
- euporie/notebook/_settings.py +107 -0
- euporie/notebook/app.py +109 -210
- euporie/notebook/filters.py +1 -1
- euporie/notebook/tabs/__init__.py +46 -7
- euporie/notebook/tabs/_commands.py +714 -0
- euporie/notebook/tabs/_settings.py +32 -0
- euporie/notebook/tabs/display.py +2 -2
- euporie/notebook/tabs/edit.py +12 -7
- euporie/notebook/tabs/json.py +3 -3
- euporie/notebook/tabs/log.py +1 -18
- euporie/notebook/tabs/notebook.py +21 -674
- euporie/notebook/widgets/_commands.py +11 -0
- euporie/notebook/widgets/_settings.py +19 -0
- euporie/notebook/widgets/side_bar.py +14 -34
- euporie/preview/_settings.py +104 -0
- euporie/preview/app.py +8 -30
- euporie/preview/tabs/notebook.py +15 -86
- euporie/web/tabs/web.py +4 -6
- euporie/web/widgets/webview.py +5 -12
- {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/METADATA +11 -15
- euporie-2.8.5.dist-info/RECORD +172 -0
- {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/WHEEL +1 -1
- {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/entry_points.txt +2 -2
- {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/licenses/LICENSE +1 -1
- euporie/core/launch.py +0 -59
- euporie/core/terminal.py +0 -527
- euporie-2.8.1.dist-info/RECORD +0 -146
- {euporie-2.8.1.data → euporie-2.8.5.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.1.data → euporie-2.8.5.data}/data/share/applications/euporie-notebook.desktop +0 -0
euporie/core/layout/cache.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from functools import
|
6
|
+
from functools import cache
|
7
7
|
from typing import TYPE_CHECKING
|
8
8
|
|
9
9
|
from prompt_toolkit.data_structures import Point
|
@@ -17,7 +17,7 @@ from prompt_toolkit.layout.layout import walk
|
|
17
17
|
from prompt_toolkit.layout.mouse_handlers import MouseHandlers
|
18
18
|
from prompt_toolkit.mouse_events import MouseEvent
|
19
19
|
|
20
|
-
from euporie.core.current import get_app
|
20
|
+
from euporie.core.app.current import get_app
|
21
21
|
from euporie.core.data_structures import DiInt
|
22
22
|
from euporie.core.layout.screen import BoundedWritePosition, Screen
|
23
23
|
|
@@ -256,7 +256,10 @@ class CachedContainer(Container):
|
|
256
256
|
cols: The columns to copy
|
257
257
|
rows: The rows to copy
|
258
258
|
.
|
259
|
-
"""
|
259
|
+
"""
|
260
|
+
# Copy write positions
|
261
|
+
new_wps = {}
|
262
|
+
|
260
263
|
for win, wp in self.screen.visible_windows_to_write_positions.items():
|
261
264
|
new_wp = BoundedWritePosition(
|
262
265
|
xpos=wp.xpos + left,
|
@@ -270,10 +273,9 @@ class CachedContainer(Container):
|
|
270
273
|
left=max(0, wp.width - (cols.stop - wp.xpos)),
|
271
274
|
),
|
272
275
|
)
|
273
|
-
|
274
|
-
screen.height = max(screen.height, self.screen.height)
|
276
|
+
new_wps[win] = new_wp
|
275
277
|
|
276
|
-
#
|
278
|
+
# Modify render info
|
277
279
|
info = win.render_info
|
278
280
|
if info is not None:
|
279
281
|
visible_line_to_row_col = {
|
@@ -308,7 +310,10 @@ class CachedContainer(Container):
|
|
308
310
|
win.render_info, "horizontal_scroll", horizontal_scroll
|
309
311
|
)
|
310
312
|
|
311
|
-
|
313
|
+
screen.visible_windows_to_write_positions.update(new_wps)
|
314
|
+
screen.height = max(screen.height, self.screen.height)
|
315
|
+
|
316
|
+
@cache
|
312
317
|
def _wrap_mouse_handler(handler: Callable) -> MouseHandler:
|
313
318
|
def _wrapped(mouse_event: MouseEvent) -> NotImplementedOrNone:
|
314
319
|
# Modify mouse events to reflect position of content
|
@@ -334,14 +339,17 @@ class CachedContainer(Container):
|
|
334
339
|
output_db = screen.data_buffer
|
335
340
|
output_zwes = screen.zero_width_escapes
|
336
341
|
output_mhs = mouse_handlers.mouse_handlers
|
337
|
-
|
342
|
+
|
343
|
+
rows_range = range(max(0, rows.start), rows.stop)
|
344
|
+
cols_range = range(max(0, cols.start), cols.stop)
|
345
|
+
for y in rows_range:
|
338
346
|
input_db_row = input_db[y]
|
339
347
|
input_zwes_row = input_zwes[y]
|
340
348
|
input_mhs_row = input_mhs[y]
|
341
349
|
output_dbs_row = output_db[top + y]
|
342
350
|
output_zwes_row = output_zwes[top + y]
|
343
351
|
output_mhs_row = output_mhs[top + y]
|
344
|
-
for x in
|
352
|
+
for x in cols_range:
|
345
353
|
# Data
|
346
354
|
output_dbs_row[left + x] = input_db_row[x]
|
347
355
|
# Escape sequences
|
@@ -353,24 +361,26 @@ class CachedContainer(Container):
|
|
353
361
|
layout = get_app().layout
|
354
362
|
if self.screen.show_cursor:
|
355
363
|
for window, point in self.screen.cursor_positions.items():
|
356
|
-
if
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
)
|
369
|
-
screen.show_cursor = True
|
364
|
+
if (
|
365
|
+
layout.current_control == window.content
|
366
|
+
and window.render_info is not None
|
367
|
+
and window.render_info.ui_content.show_cursor
|
368
|
+
and not window.always_hide_cursor()
|
369
|
+
and cols.start <= point.x < cols.stop
|
370
|
+
and rows.start <= point.y < rows.stop
|
371
|
+
):
|
372
|
+
screen.cursor_positions[window] = Point(
|
373
|
+
x=left + point.x, y=top + point.y
|
374
|
+
)
|
375
|
+
screen.show_cursor = True
|
370
376
|
|
371
377
|
# Copy menu positions
|
372
|
-
|
373
|
-
|
378
|
+
screen.menu_positions.update(
|
379
|
+
{
|
380
|
+
window: Point(x=left + point.x, y=top + point.y)
|
381
|
+
for window, point in self.screen.menu_positions.items()
|
382
|
+
}
|
383
|
+
)
|
374
384
|
|
375
385
|
def get_children(self) -> list[Container]:
|
376
386
|
"""Return a list of all child containers."""
|
@@ -7,31 +7,47 @@ from functools import lru_cache, partial
|
|
7
7
|
from typing import TYPE_CHECKING
|
8
8
|
|
9
9
|
from prompt_toolkit.application.current import get_app
|
10
|
+
from prompt_toolkit.cache import FastDictCache
|
10
11
|
from prompt_toolkit.data_structures import Point
|
11
|
-
from prompt_toolkit.layout import containers
|
12
|
-
from prompt_toolkit.layout.containers import
|
12
|
+
from prompt_toolkit.layout import containers as ptk_containers
|
13
|
+
from prompt_toolkit.layout.containers import (
|
14
|
+
Container,
|
15
|
+
HorizontalAlign,
|
16
|
+
VerticalAlign,
|
17
|
+
WindowAlign,
|
18
|
+
WindowRenderInfo,
|
19
|
+
)
|
20
|
+
from prompt_toolkit.layout.controls import DummyControl as PtkDummyControl
|
13
21
|
from prompt_toolkit.layout.controls import (
|
14
22
|
FormattedTextControl,
|
15
23
|
UIContent,
|
16
24
|
fragment_list_width,
|
17
25
|
to_formatted_text,
|
18
26
|
)
|
19
|
-
from prompt_toolkit.layout.dimension import
|
27
|
+
from prompt_toolkit.layout.dimension import Dimension
|
20
28
|
from prompt_toolkit.layout.screen import _CHAR_CACHE
|
21
29
|
from prompt_toolkit.layout.utils import explode_text_fragments
|
22
|
-
from prompt_toolkit.mouse_events import MouseEvent
|
23
|
-
from prompt_toolkit.utils import get_cwidth,
|
30
|
+
from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
|
31
|
+
from prompt_toolkit.utils import get_cwidth, to_str
|
24
32
|
|
25
33
|
from euporie.core.data_structures import DiInt
|
34
|
+
from euporie.core.layout.controls import DummyControl
|
26
35
|
from euporie.core.layout.screen import BoundedWritePosition
|
27
36
|
|
28
37
|
if TYPE_CHECKING:
|
29
|
-
from
|
38
|
+
from collections.abc import Sequence
|
39
|
+
from typing import Any, Callable
|
30
40
|
|
31
41
|
from prompt_toolkit.formatted_text import AnyFormattedText, StyleAndTextTuples
|
32
|
-
from prompt_toolkit.key_binding.key_bindings import
|
33
|
-
|
34
|
-
|
42
|
+
from prompt_toolkit.key_binding.key_bindings import (
|
43
|
+
KeyBindingsBase,
|
44
|
+
NotImplementedOrNone,
|
45
|
+
)
|
46
|
+
from prompt_toolkit.layout.containers import (
|
47
|
+
AnyContainer,
|
48
|
+
AnyDimension,
|
49
|
+
Float,
|
50
|
+
)
|
35
51
|
from prompt_toolkit.layout.margins import Margin
|
36
52
|
from prompt_toolkit.layout.mouse_handlers import MouseHandlers
|
37
53
|
from prompt_toolkit.layout.screen import Screen, WritePosition
|
@@ -40,9 +56,87 @@ if TYPE_CHECKING:
|
|
40
56
|
log = logging.getLogger(__name__)
|
41
57
|
|
42
58
|
|
43
|
-
|
59
|
+
@lru_cache(maxsize=None)
|
60
|
+
class DummyContainer(Container):
|
61
|
+
"""Base class for user interface layout."""
|
62
|
+
|
63
|
+
def __init__(self, width: int = 0, height: int = 0) -> None:
|
64
|
+
"""Define width and height if any."""
|
65
|
+
self.width = width
|
66
|
+
self.height = height
|
67
|
+
|
68
|
+
def reset(self) -> None:
|
69
|
+
"""Reset the state of this container (does nothing)."""
|
70
|
+
|
71
|
+
def preferred_width(self, max_available_width: int) -> Dimension:
|
72
|
+
"""Return a zero-width dimension."""
|
73
|
+
return Dimension.exact(self.width)
|
74
|
+
|
75
|
+
def preferred_height(self, width: int, max_available_height: int) -> Dimension:
|
76
|
+
"""Return a zero-height dimension."""
|
77
|
+
return Dimension.exact(self.height)
|
78
|
+
|
79
|
+
def write_to_screen(
|
80
|
+
self,
|
81
|
+
screen: Screen,
|
82
|
+
mouse_handlers: MouseHandlers,
|
83
|
+
write_position: WritePosition,
|
84
|
+
parent_style: str,
|
85
|
+
erase_bg: bool,
|
86
|
+
z_index: int | None,
|
87
|
+
) -> None:
|
88
|
+
"""Write the actual content to the screen. Does nothing."""
|
89
|
+
|
90
|
+
def get_children(self) -> list[Container]:
|
91
|
+
"""Return an empty list of child :class:`.Container` objects."""
|
92
|
+
return []
|
93
|
+
|
94
|
+
|
95
|
+
class HSplit(ptk_containers.HSplit):
|
44
96
|
"""Several layouts, one stacked above/under the other."""
|
45
97
|
|
98
|
+
_pad_window: Window
|
99
|
+
|
100
|
+
def __init__(
|
101
|
+
self,
|
102
|
+
children: Sequence[AnyContainer],
|
103
|
+
window_too_small: Container | None = None,
|
104
|
+
align: VerticalAlign = VerticalAlign.JUSTIFY,
|
105
|
+
padding: AnyDimension = 0,
|
106
|
+
padding_char: str | None = None,
|
107
|
+
padding_style: str = "",
|
108
|
+
width: AnyDimension = None,
|
109
|
+
height: AnyDimension = None,
|
110
|
+
z_index: int | None = None,
|
111
|
+
modal: bool = False,
|
112
|
+
key_bindings: KeyBindingsBase | None = None,
|
113
|
+
style: str | Callable[[], str] = "",
|
114
|
+
) -> None:
|
115
|
+
"""Initialize the HSplit with a cache."""
|
116
|
+
if window_too_small is None:
|
117
|
+
window_too_small = DummyContainer()
|
118
|
+
super().__init__(
|
119
|
+
children=children,
|
120
|
+
window_too_small=window_too_small,
|
121
|
+
align=align,
|
122
|
+
padding=padding,
|
123
|
+
padding_char=padding_char,
|
124
|
+
padding_style=padding_style,
|
125
|
+
width=width,
|
126
|
+
height=height,
|
127
|
+
z_index=z_index,
|
128
|
+
modal=modal,
|
129
|
+
key_bindings=key_bindings,
|
130
|
+
style=style,
|
131
|
+
)
|
132
|
+
_split_cache_getter = super()._divide_heights
|
133
|
+
self._split_cache: FastDictCache[
|
134
|
+
tuple[int, WritePosition], list[int] | None
|
135
|
+
] = FastDictCache(lambda rc, wp: _split_cache_getter(wp), size=100)
|
136
|
+
|
137
|
+
def _divide_heights(self, write_position: WritePosition) -> list[int] | None:
|
138
|
+
return self._split_cache[get_app().render_counter, write_position]
|
139
|
+
|
46
140
|
def write_to_screen(
|
47
141
|
self,
|
48
142
|
screen: Screen,
|
@@ -132,68 +226,87 @@ class HSplit(containers.HSplit):
|
|
132
226
|
z_index,
|
133
227
|
)
|
134
228
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
def
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
child_generator = take_using_weights(
|
168
|
-
items=list(range(len(dimensions))), weights=[d.weight for d in dimensions]
|
169
|
-
)
|
170
|
-
|
171
|
-
i = next(child_generator)
|
172
|
-
|
173
|
-
# Increase until we meet at least the 'preferred' size.
|
174
|
-
preferred_stop = min(height, sum_dimensions.preferred)
|
175
|
-
preferred_dimensions = [d.preferred for d in dimensions]
|
229
|
+
@property
|
230
|
+
def pad_window(self) -> Window:
|
231
|
+
"""Create a single instance of the padding window."""
|
232
|
+
try:
|
233
|
+
return self._pad_window
|
234
|
+
except AttributeError:
|
235
|
+
self._pad_window = Window(
|
236
|
+
height=self.padding,
|
237
|
+
char=self.padding_char,
|
238
|
+
style=self.padding_style,
|
239
|
+
)
|
240
|
+
return self._pad_window
|
241
|
+
|
242
|
+
@property
|
243
|
+
def _all_children(self) -> list[Container]:
|
244
|
+
"""List of child objects, including padding."""
|
245
|
+
|
246
|
+
def get() -> list[Container]:
|
247
|
+
result: list[Container] = []
|
248
|
+
# Padding Top.
|
249
|
+
if self.align in (VerticalAlign.CENTER, VerticalAlign.BOTTOM):
|
250
|
+
result.append(Window(width=Dimension(preferred=0)))
|
251
|
+
# The children with padding.
|
252
|
+
for child in self.children:
|
253
|
+
result.append(child)
|
254
|
+
result.append(self.pad_window)
|
255
|
+
if result:
|
256
|
+
result.pop()
|
257
|
+
# Padding right.
|
258
|
+
if self.align in (VerticalAlign.CENTER, VerticalAlign.TOP):
|
259
|
+
result.append(Window(width=Dimension(preferred=0)))
|
260
|
+
return result
|
176
261
|
|
177
|
-
|
178
|
-
if sizes[i] < preferred_dimensions[i]:
|
179
|
-
sizes[i] += 1
|
180
|
-
i = next(child_generator)
|
262
|
+
return self._children_cache.get(tuple(self.children), get)
|
181
263
|
|
182
|
-
# Increase until we use all the available space. (or until "max")
|
183
|
-
if not get_app().is_done:
|
184
|
-
max_stop = min(height, sum_dimensions.max)
|
185
|
-
max_dimensions = [d.max for d in dimensions]
|
186
264
|
|
187
|
-
|
188
|
-
|
189
|
-
sizes[i] += 1
|
190
|
-
i = next(child_generator)
|
265
|
+
class VSplit(ptk_containers.VSplit):
|
266
|
+
"""Several layouts, one stacked left/right of the other."""
|
191
267
|
|
192
|
-
|
268
|
+
_pad_window: Window
|
193
269
|
|
270
|
+
def __init__(
|
271
|
+
self,
|
272
|
+
children: Sequence[AnyContainer],
|
273
|
+
window_too_small: Container | None = None,
|
274
|
+
align: HorizontalAlign = HorizontalAlign.JUSTIFY,
|
275
|
+
padding: AnyDimension = 0,
|
276
|
+
padding_char: str | None = None,
|
277
|
+
padding_style: str = "",
|
278
|
+
width: AnyDimension = None,
|
279
|
+
height: AnyDimension = None,
|
280
|
+
z_index: int | None = None,
|
281
|
+
modal: bool = False,
|
282
|
+
key_bindings: KeyBindingsBase | None = None,
|
283
|
+
style: str | Callable[[], str] = "",
|
284
|
+
) -> None:
|
285
|
+
"""Initialize the VSplit with a cache."""
|
286
|
+
if window_too_small is None:
|
287
|
+
window_too_small = DummyContainer()
|
288
|
+
super().__init__(
|
289
|
+
children=children,
|
290
|
+
window_too_small=window_too_small,
|
291
|
+
align=align,
|
292
|
+
padding=padding,
|
293
|
+
padding_char=padding_char,
|
294
|
+
padding_style=padding_style,
|
295
|
+
width=width,
|
296
|
+
height=height,
|
297
|
+
z_index=z_index,
|
298
|
+
modal=modal,
|
299
|
+
key_bindings=key_bindings,
|
300
|
+
style=style,
|
301
|
+
)
|
302
|
+
_split_cache_getter = super()._divide_widths
|
303
|
+
self._split_cache: FastDictCache[tuple[int, int], list[int] | None] = (
|
304
|
+
FastDictCache(lambda rc, w: _split_cache_getter(w), size=100)
|
305
|
+
)
|
194
306
|
|
195
|
-
|
196
|
-
|
307
|
+
def _divide_widths(self, width: int) -> list[int] | None:
|
308
|
+
"""Calculate and cache widths for all columns."""
|
309
|
+
return self._split_cache[get_app().render_counter, width]
|
197
310
|
|
198
311
|
def write_to_screen(
|
199
312
|
self,
|
@@ -294,10 +407,53 @@ class VSplit(containers.VSplit):
|
|
294
407
|
z_index,
|
295
408
|
)
|
296
409
|
|
410
|
+
@property
|
411
|
+
def pad_window(self) -> Window:
|
412
|
+
"""Create a single instance of the padding window."""
|
413
|
+
try:
|
414
|
+
return self._pad_window
|
415
|
+
except AttributeError:
|
416
|
+
self._pad_window = Window(
|
417
|
+
width=self.padding,
|
418
|
+
char=self.padding_char,
|
419
|
+
style=self.padding_style,
|
420
|
+
)
|
421
|
+
return self._pad_window
|
422
|
+
|
423
|
+
@property
|
424
|
+
def _all_children(self) -> list[Container]:
|
425
|
+
"""List of child objects, including padding."""
|
426
|
+
|
427
|
+
def get() -> list[Container]:
|
428
|
+
result: list[Container] = []
|
429
|
+
|
430
|
+
# Padding left.
|
431
|
+
if self.align in (HorizontalAlign.CENTER, HorizontalAlign.RIGHT):
|
432
|
+
result.append(Window(width=Dimension(preferred=0)))
|
433
|
+
# The children with padding.
|
434
|
+
for child in self.children:
|
435
|
+
result.append(child)
|
436
|
+
result.append(self.pad_window)
|
437
|
+
if result:
|
438
|
+
result.pop()
|
439
|
+
# Padding right.
|
440
|
+
if self.align in (HorizontalAlign.CENTER, HorizontalAlign.LEFT):
|
441
|
+
result.append(Window(width=Dimension(preferred=0)))
|
442
|
+
|
443
|
+
return result
|
444
|
+
|
445
|
+
return self._children_cache.get(tuple(self.children), get)
|
446
|
+
|
297
447
|
|
298
|
-
class Window(
|
448
|
+
class Window(ptk_containers.Window):
|
299
449
|
"""Container that holds a control."""
|
300
450
|
|
451
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
452
|
+
"""Initialize `Windows`, updating the default control for empty windows."""
|
453
|
+
super().__init__(*args, **kwargs)
|
454
|
+
if isinstance(self.content, PtkDummyControl):
|
455
|
+
self.content = DummyControl()
|
456
|
+
|
301
457
|
def write_to_screen(
|
302
458
|
self,
|
303
459
|
screen: Screen,
|
@@ -308,17 +464,20 @@ class Window(containers.Window):
|
|
308
464
|
z_index: int | None,
|
309
465
|
) -> None:
|
310
466
|
"""Write window to screen."""
|
311
|
-
assert isinstance(write_position, BoundedWritePosition)
|
312
467
|
# If dont_extend_width/height was given, then reduce width/height in
|
313
468
|
# WritePosition, if the parent wanted us to paint in a bigger area.
|
314
469
|
# (This happens if this window is bundled with another window in a
|
315
470
|
# HSplit/VSplit, but with different size requirements.)
|
471
|
+
if isinstance(write_position, BoundedWritePosition):
|
472
|
+
bbox = write_position.bbox
|
473
|
+
else:
|
474
|
+
bbox = DiInt(0, 0, 0, 0)
|
316
475
|
write_position = BoundedWritePosition(
|
317
476
|
xpos=write_position.xpos,
|
318
477
|
ypos=write_position.ypos,
|
319
478
|
width=write_position.width,
|
320
479
|
height=write_position.height,
|
321
|
-
bbox=
|
480
|
+
bbox=bbox,
|
322
481
|
)
|
323
482
|
|
324
483
|
if self.dont_extend_width():
|
@@ -856,8 +1015,55 @@ class Window(containers.Window):
|
|
856
1015
|
else:
|
857
1016
|
new_screen.fill_area(write_position, "class:last-line", after=True)
|
858
1017
|
|
1018
|
+
def _mouse_handler(self, mouse_event: MouseEvent) -> NotImplementedOrNone:
|
1019
|
+
"""Mouse handler. Called when the UI control doesn't handle this particular event.
|
1020
|
+
|
1021
|
+
Return `NotImplemented` if nothing was done as a consequence of this
|
1022
|
+
key binding (no UI invalidate required in that case).
|
1023
|
+
"""
|
1024
|
+
if mouse_event.event_type == MouseEventType.SCROLL_DOWN:
|
1025
|
+
return self._scroll_down()
|
1026
|
+
elif mouse_event.event_type == MouseEventType.SCROLL_UP:
|
1027
|
+
return self._scroll_up()
|
1028
|
+
|
1029
|
+
return NotImplemented
|
1030
|
+
|
1031
|
+
def _scroll_down(self) -> NotImplementedOrNone: # type: ignore [override]
|
1032
|
+
"""Scroll window down."""
|
1033
|
+
info = self.render_info
|
1034
|
+
|
1035
|
+
if info is None:
|
1036
|
+
return NotImplemented
|
1037
|
+
|
1038
|
+
if self.vertical_scroll < info.content_height - info.window_height:
|
1039
|
+
if info.cursor_position.y <= info.configured_scroll_offsets.top:
|
1040
|
+
self.content.move_cursor_down()
|
1041
|
+
self.vertical_scroll += 1
|
1042
|
+
return None
|
1043
|
+
|
1044
|
+
return NotImplemented
|
1045
|
+
|
1046
|
+
def _scroll_up(self) -> NotImplementedOrNone: # type: ignore [override]
|
1047
|
+
"""Scroll window up."""
|
1048
|
+
info = self.render_info
|
1049
|
+
|
1050
|
+
if info is None:
|
1051
|
+
return NotImplemented
|
1052
|
+
|
1053
|
+
if info.vertical_scroll > 0:
|
1054
|
+
# TODO: not entirely correct yet in case of line wrapping and long lines.
|
1055
|
+
if (
|
1056
|
+
info.cursor_position.y
|
1057
|
+
>= info.window_height - 1 - info.configured_scroll_offsets.bottom
|
1058
|
+
):
|
1059
|
+
self.content.move_cursor_up()
|
1060
|
+
self.vertical_scroll -= 1
|
1061
|
+
return None
|
1062
|
+
|
1063
|
+
return NotImplemented
|
1064
|
+
|
859
1065
|
|
860
|
-
class FloatContainer(
|
1066
|
+
class FloatContainer(ptk_containers.FloatContainer):
|
861
1067
|
"""A `FloatContainer` which uses :py`BoundedWritePosition`s."""
|
862
1068
|
|
863
1069
|
def _draw_float(
|
@@ -1005,7 +1211,7 @@ class FloatContainer(containers.FloatContainer):
|
|
1005
1211
|
)
|
1006
1212
|
|
1007
1213
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1214
|
+
ptk_containers.HSplit = HSplit # type: ignore[misc]
|
1215
|
+
ptk_containers.VSplit = VSplit # type: ignore[misc]
|
1216
|
+
ptk_containers.Window = Window # type: ignore[misc]
|
1217
|
+
ptk_containers.FloatContainer = FloatContainer # type: ignore[misc]
|
euporie/core/layout/decor.py
CHANGED
@@ -15,8 +15,8 @@ from prompt_toolkit.layout.dimension import Dimension
|
|
15
15
|
from prompt_toolkit.layout.screen import Char, Screen, WritePosition
|
16
16
|
from prompt_toolkit.mouse_events import MouseEventType
|
17
17
|
|
18
|
+
from euporie.core.app.current import get_app
|
18
19
|
from euporie.core.border import ThinLine
|
19
|
-
from euporie.core.current import get_app
|
20
20
|
from euporie.core.style import ColorPaletteColor
|
21
21
|
|
22
22
|
if TYPE_CHECKING:
|
@@ -280,9 +280,9 @@ class FocusedStyle(Container):
|
|
280
280
|
|
281
281
|
return wrapped_mouse_handler
|
282
282
|
|
283
|
-
mouse_handler_wrappers: FastDictCache[
|
284
|
-
|
285
|
-
|
283
|
+
mouse_handler_wrappers: FastDictCache[tuple[Callable], MouseHandler] = (
|
284
|
+
FastDictCache(get_value=_wrap_mouse_handler)
|
285
|
+
)
|
286
286
|
for y in range(y_min, y_max):
|
287
287
|
row = mouse_handlers.mouse_handlers[y]
|
288
288
|
for x in range(x_min, x_max):
|
@@ -341,7 +341,7 @@ class DropShadow(Container):
|
|
341
341
|
) -> None:
|
342
342
|
"""Draw the wrapped container with the additional style."""
|
343
343
|
attr_cache = self.renderer._attrs_for_style
|
344
|
-
if attr_cache is not None:
|
344
|
+
if attr_cache is not None and self.amount:
|
345
345
|
ypos = write_position.ypos
|
346
346
|
xpos = write_position.xpos
|
347
347
|
amount = self.amount
|
euporie/core/layout/mouse.py
CHANGED
euporie/core/layout/print.py
CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
5
5
|
import logging
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
+
from prompt_toolkit.application.current import get_app
|
8
9
|
from prompt_toolkit.layout.containers import (
|
9
10
|
Container,
|
10
11
|
Window,
|
@@ -15,7 +16,8 @@ from prompt_toolkit.layout.dimension import Dimension, to_dimension
|
|
15
16
|
from euporie.core.layout.screen import BoundedWritePosition
|
16
17
|
|
17
18
|
if TYPE_CHECKING:
|
18
|
-
from
|
19
|
+
from collections.abc import Sequence
|
20
|
+
from typing import Callable
|
19
21
|
|
20
22
|
from prompt_toolkit.key_binding.key_bindings import (
|
21
23
|
KeyBindingsBase,
|
@@ -42,8 +44,9 @@ class PrintingContainer(Container):
|
|
42
44
|
) -> None:
|
43
45
|
"""Initiate the container."""
|
44
46
|
self.width = width
|
45
|
-
self.rendered = False
|
46
47
|
self._children = children
|
48
|
+
self._render_count = -1
|
49
|
+
self._cached_children: Sequence[AnyContainer] | None = None
|
47
50
|
self.key_bindings = key_bindings
|
48
51
|
|
49
52
|
def get_key_bindings(self) -> KeyBindingsBase | None:
|
@@ -53,7 +56,17 @@ class PrintingContainer(Container):
|
|
53
56
|
@property
|
54
57
|
def children(self) -> Sequence[AnyContainer]:
|
55
58
|
"""Return the container's children."""
|
56
|
-
children
|
59
|
+
# Only load the children from a callable once per render cycle
|
60
|
+
if callable(self._children):
|
61
|
+
if (
|
62
|
+
self._cached_children is None
|
63
|
+
or self._render_count != get_app().render_counter
|
64
|
+
):
|
65
|
+
self._cached_children = self._children()
|
66
|
+
self._render_count = get_app().render_counter
|
67
|
+
children = self._cached_children
|
68
|
+
else:
|
69
|
+
children = self._children
|
57
70
|
return children or [Window()]
|
58
71
|
|
59
72
|
def get_children(self) -> list[Container]:
|