mini-arcade-core 1.1.1__py3-none-any.whl → 1.2.1__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.
- mini_arcade_core/__init__.py +14 -42
- mini_arcade_core/backend/__init__.py +1 -2
- mini_arcade_core/backend/backend.py +182 -184
- mini_arcade_core/backend/types.py +5 -1
- mini_arcade_core/engine/commands.py +8 -8
- mini_arcade_core/engine/game.py +54 -354
- mini_arcade_core/engine/game_config.py +40 -0
- mini_arcade_core/engine/gameplay_settings.py +24 -0
- mini_arcade_core/engine/loop/config.py +20 -0
- mini_arcade_core/engine/loop/hooks.py +77 -0
- mini_arcade_core/engine/loop/runner.py +272 -0
- mini_arcade_core/engine/loop/state.py +32 -0
- mini_arcade_core/engine/managers.py +24 -0
- mini_arcade_core/engine/render/context.py +0 -2
- mini_arcade_core/engine/render/effects/base.py +2 -2
- mini_arcade_core/engine/render/effects/crt.py +4 -4
- mini_arcade_core/engine/render/effects/registry.py +1 -1
- mini_arcade_core/engine/render/effects/vignette.py +8 -8
- mini_arcade_core/engine/render/passes/begin_frame.py +1 -1
- mini_arcade_core/engine/render/passes/end_frame.py +1 -1
- mini_arcade_core/engine/render/passes/postfx.py +1 -1
- mini_arcade_core/engine/render/passes/ui.py +1 -1
- mini_arcade_core/engine/render/passes/world.py +6 -6
- mini_arcade_core/engine/render/pipeline.py +7 -6
- mini_arcade_core/engine/render/viewport.py +10 -4
- mini_arcade_core/engine/scenes/models.py +54 -0
- mini_arcade_core/engine/scenes/scene_manager.py +213 -0
- mini_arcade_core/runtime/audio/audio_adapter.py +4 -3
- mini_arcade_core/runtime/audio/audio_port.py +0 -4
- mini_arcade_core/runtime/capture/capture_adapter.py +53 -31
- mini_arcade_core/runtime/capture/capture_port.py +0 -4
- mini_arcade_core/runtime/capture/capture_worker.py +174 -0
- mini_arcade_core/runtime/context.py +8 -6
- mini_arcade_core/runtime/scene/scene_query_adapter.py +31 -0
- mini_arcade_core/runtime/scene/scene_query_port.py +38 -0
- mini_arcade_core/runtime/services.py +3 -2
- mini_arcade_core/runtime/window/window_adapter.py +43 -41
- mini_arcade_core/runtime/window/window_port.py +3 -17
- mini_arcade_core/scenes/debug_overlay.py +5 -4
- mini_arcade_core/scenes/registry.py +11 -1
- mini_arcade_core/scenes/sim_scene.py +14 -14
- mini_arcade_core/ui/menu.py +54 -16
- mini_arcade_core/utils/__init__.py +2 -1
- mini_arcade_core/utils/logging.py +47 -18
- mini_arcade_core/utils/profiler.py +283 -0
- {mini_arcade_core-1.1.1.dist-info → mini_arcade_core-1.2.1.dist-info}/METADATA +1 -1
- mini_arcade_core-1.2.1.dist-info/RECORD +93 -0
- {mini_arcade_core-1.1.1.dist-info → mini_arcade_core-1.2.1.dist-info}/WHEEL +1 -1
- mini_arcade_core/managers/inputs.py +0 -284
- mini_arcade_core/runtime/scene/scene_adapter.py +0 -125
- mini_arcade_core/runtime/scene/scene_port.py +0 -170
- mini_arcade_core/sim/protocols.py +0 -41
- mini_arcade_core/sim/runner.py +0 -222
- mini_arcade_core-1.1.1.dist-info/RECORD +0 -85
- /mini_arcade_core/{managers → engine}/cheats.py +0 -0
- /mini_arcade_core/{managers → engine/loop}/__init__.py +0 -0
- /mini_arcade_core/{sim → engine/scenes}/__init__.py +0 -0
- {mini_arcade_core-1.1.1.dist-info → mini_arcade_core-1.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Game core module defining the Game class and configuration.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import enum
|
|
8
|
+
import logging
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from time import perf_counter
|
|
11
|
+
from typing import Dict, Iterable, Mapping
|
|
12
|
+
|
|
13
|
+
perf_logger = logging.getLogger("mini-arcade-core.perf")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Ansi(enum.Enum):
|
|
17
|
+
"""
|
|
18
|
+
ANSI escape codes for terminal text formatting.
|
|
19
|
+
|
|
20
|
+
cvar RESET (str): Reset all formatting.
|
|
21
|
+
cvar BOLD (str): Bold text.
|
|
22
|
+
cvar DIM (str): Dim text.
|
|
23
|
+
cvar RED (str): Red text.
|
|
24
|
+
cvar GREEN (str): Green text.
|
|
25
|
+
cvar YELLOW (str): Yellow text.
|
|
26
|
+
cvar CYAN (str): Cyan text.
|
|
27
|
+
cvar MAGENTA (str): Magenta text.
|
|
28
|
+
cvar WHITE (str): White text.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
RESET = "\033[0m"
|
|
32
|
+
BOLD = "\033[1m"
|
|
33
|
+
DIM = "\033[2m"
|
|
34
|
+
|
|
35
|
+
RED = "\033[91m"
|
|
36
|
+
GREEN = "\033[92m"
|
|
37
|
+
YELLOW = "\033[93m"
|
|
38
|
+
CYAN = "\033[96m"
|
|
39
|
+
MAGENTA = "\033[95m"
|
|
40
|
+
WHITE = "\033[97m"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _c(text: str, *codes: str) -> str:
|
|
44
|
+
"""Convenience function to wrap text with ANSI codes."""
|
|
45
|
+
return "".join(codes) + text + Ansi.RESET.value
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class FrameTimingReport:
|
|
50
|
+
"""
|
|
51
|
+
Report of frame timing data.
|
|
52
|
+
|
|
53
|
+
:ivar frame_index (int): Index of the frame.
|
|
54
|
+
:ivar diffs_ms (Dict[str, float]): Dictionary of time differences in milliseconds.
|
|
55
|
+
:ivar total_ms (float): Total time in milliseconds.
|
|
56
|
+
:ivar budget_ms (float): Frame budget in milliseconds.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
frame_index: int
|
|
60
|
+
diffs_ms: Dict[str, float]
|
|
61
|
+
total_ms: float
|
|
62
|
+
budget_ms: float
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class FrameTimingFormatter:
|
|
67
|
+
"""
|
|
68
|
+
Formats a FrameTimingReport into a colored, multi-line table string.
|
|
69
|
+
Keeps FrameTimer lean and avoids pylint complexity in the timer itself.
|
|
70
|
+
|
|
71
|
+
:ivar target_fps (int): Target frames per second for budget calculation.
|
|
72
|
+
:ivar top_n (int): Number of top time-consuming segments to display.
|
|
73
|
+
:ivar min_ms (float): Minimum time in milliseconds to include in the top list.
|
|
74
|
+
:ivar phases (tuple[tuple[str, str], ...]): Tuples of (display name, mark key)
|
|
75
|
+
for table columns.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
target_fps: int = 60
|
|
79
|
+
top_n: int = 6
|
|
80
|
+
min_ms: float = 0.05
|
|
81
|
+
|
|
82
|
+
# These are the “headline” segments you want as columns.
|
|
83
|
+
phases: tuple[tuple[str, str], ...] = (
|
|
84
|
+
("events", "frame_start->events_polled"),
|
|
85
|
+
("input", "events_polled->input_built"),
|
|
86
|
+
("tick", "tick_start->tick_end"),
|
|
87
|
+
("render", "render_start->render_done"),
|
|
88
|
+
("sleep", "sleep_start->sleep_end"),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def make_report(
|
|
92
|
+
self, frame_index: int, diffs_ms: Dict[str, float]
|
|
93
|
+
) -> FrameTimingReport:
|
|
94
|
+
"""
|
|
95
|
+
Create a FrameTimingReport from the given diffs.
|
|
96
|
+
|
|
97
|
+
:param frame_index: Index of the frame.
|
|
98
|
+
:type frame_index: int
|
|
99
|
+
:param diffs_ms: Dictionary of time differences in milliseconds.
|
|
100
|
+
:type diffs_ms: Dict[str, float]
|
|
101
|
+
:return: FrameTimingReport instance.
|
|
102
|
+
:rtype: FrameTimingReport
|
|
103
|
+
"""
|
|
104
|
+
total = sum(diffs_ms.values()) if diffs_ms else 0.0
|
|
105
|
+
budget = (1000.0 / self.target_fps) if self.target_fps > 0 else 0.0
|
|
106
|
+
return FrameTimingReport(
|
|
107
|
+
frame_index=frame_index,
|
|
108
|
+
diffs_ms=diffs_ms,
|
|
109
|
+
total_ms=total,
|
|
110
|
+
budget_ms=budget,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def format(self, report: FrameTimingReport) -> str:
|
|
114
|
+
"""
|
|
115
|
+
Format the FrameTimingReport into a colored string.
|
|
116
|
+
|
|
117
|
+
:param report: FrameTimingReport instance.
|
|
118
|
+
:type report: FrameTimingReport
|
|
119
|
+
:return: Formatted string.
|
|
120
|
+
:rtype: str
|
|
121
|
+
"""
|
|
122
|
+
header = self._format_header(report)
|
|
123
|
+
table = self._format_table(report.diffs_ms)
|
|
124
|
+
top = self._format_top(report.diffs_ms)
|
|
125
|
+
return f"{header}\n{table}\n{top}\n"
|
|
126
|
+
|
|
127
|
+
def _format_header(self, report: FrameTimingReport) -> str:
|
|
128
|
+
over = report.budget_ms > 0 and report.total_ms > report.budget_ms
|
|
129
|
+
status = (
|
|
130
|
+
_c("OVER", Ansi.BOLD.value, Ansi.RED.value)
|
|
131
|
+
if over
|
|
132
|
+
else _c("OK", Ansi.BOLD.value, Ansi.GREEN.value)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
frame = _c(
|
|
136
|
+
f"[Frame {report.frame_index}]", Ansi.BOLD.value, Ansi.WHITE.value
|
|
137
|
+
)
|
|
138
|
+
total = _c(
|
|
139
|
+
f"{report.total_ms:.2f}ms", Ansi.BOLD.value, Ansi.WHITE.value
|
|
140
|
+
)
|
|
141
|
+
budget = _c(f"{report.budget_ms:.2f}ms", Ansi.DIM.value)
|
|
142
|
+
dim = Ansi.DIM.value
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
f"{frame} {_c('total', dim)}={total} "
|
|
146
|
+
f"{_c('budget', dim)}={budget} {_c('status', dim)}={status}"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def _format_table(self, diffs: Mapping[str, float]) -> str:
|
|
150
|
+
# Header line
|
|
151
|
+
headers = [name for name, _ in self.phases]
|
|
152
|
+
line_h = self._pipe_row((_c(h, Ansi.DIM.value) for h in headers))
|
|
153
|
+
|
|
154
|
+
# Values line
|
|
155
|
+
values = [diffs.get(key, 0.0) for _, key in self.phases]
|
|
156
|
+
line_v = self._pipe_row(self._color_values(values))
|
|
157
|
+
|
|
158
|
+
return f"{line_h}\n{line_v}"
|
|
159
|
+
|
|
160
|
+
def _color_values(self, values: Iterable[float]) -> list[str]:
|
|
161
|
+
# Keep coloring policy centralized and easy to tweak.
|
|
162
|
+
# events/input: cyan, tick: yellow, render: magenta, sleep: green
|
|
163
|
+
colors = [
|
|
164
|
+
Ansi.CYAN.value,
|
|
165
|
+
Ansi.CYAN.value,
|
|
166
|
+
Ansi.YELLOW.value,
|
|
167
|
+
Ansi.MAGENTA.value,
|
|
168
|
+
Ansi.GREEN.value,
|
|
169
|
+
]
|
|
170
|
+
out: list[str] = []
|
|
171
|
+
for v, col in zip(values, colors):
|
|
172
|
+
out.append(_c(f"{v:6.2f}", col))
|
|
173
|
+
return out
|
|
174
|
+
|
|
175
|
+
def _format_top(self, diffs: Mapping[str, float]) -> str:
|
|
176
|
+
dim = Ansi.DIM.value
|
|
177
|
+
items = [
|
|
178
|
+
(k, float(v)) for k, v in diffs.items() if float(v) >= self.min_ms
|
|
179
|
+
]
|
|
180
|
+
items.sort(key=lambda kv: kv[1], reverse=True)
|
|
181
|
+
items = items[: self.top_n]
|
|
182
|
+
|
|
183
|
+
if not items:
|
|
184
|
+
return f"{_c('top:', dim)} (none >= {self.min_ms:.2f}ms)"
|
|
185
|
+
|
|
186
|
+
top_str = ", ".join(f"{k}:{v:.2f}ms" for k, v in items)
|
|
187
|
+
return f"{_c('top:', dim)} {top_str}"
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def _pipe_row(cells: Iterable[str]) -> str:
|
|
191
|
+
# Keeps lines short and avoids long f-strings.
|
|
192
|
+
return " | ".join(cells)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dataclass
|
|
196
|
+
class FrameTimerConfig:
|
|
197
|
+
"""
|
|
198
|
+
Configuration for FrameTimer.
|
|
199
|
+
|
|
200
|
+
:ivar enabled (bool): Whether timing is enabled.
|
|
201
|
+
:ivar report_every (int): Number of frames between reports.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
enabled: bool = False
|
|
205
|
+
report_every: int = 60
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@dataclass
|
|
209
|
+
class FrameTimer:
|
|
210
|
+
"""
|
|
211
|
+
Simple frame timer for marking and reporting time intervals.
|
|
212
|
+
|
|
213
|
+
:ivar config (FrameTimerConfig): Configuration for the timer.
|
|
214
|
+
:ivar formatter (FrameTimingFormatter): Formatter for timing reports.
|
|
215
|
+
:ivar marks (Dict[str, float]): Recorded time marks.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
config: FrameTimerConfig = field(default_factory=FrameTimerConfig)
|
|
219
|
+
formatter: FrameTimingFormatter = field(
|
|
220
|
+
default_factory=FrameTimingFormatter
|
|
221
|
+
)
|
|
222
|
+
marks: Dict[str, float] = field(default_factory=dict)
|
|
223
|
+
|
|
224
|
+
def clear(self):
|
|
225
|
+
"""Clear all recorded marks."""
|
|
226
|
+
if not self.config.enabled:
|
|
227
|
+
return
|
|
228
|
+
self.marks.clear()
|
|
229
|
+
|
|
230
|
+
def mark(self, name: str):
|
|
231
|
+
"""
|
|
232
|
+
Record a time mark with the given name.
|
|
233
|
+
|
|
234
|
+
:param name: Name of the mark.
|
|
235
|
+
:type name: str
|
|
236
|
+
"""
|
|
237
|
+
if not self.config.enabled:
|
|
238
|
+
return
|
|
239
|
+
self.marks[name] = perf_counter()
|
|
240
|
+
|
|
241
|
+
def report_ms(self) -> Dict[str, float]:
|
|
242
|
+
"""
|
|
243
|
+
Returns diffs between consecutive marks in insertion order.
|
|
244
|
+
|
|
245
|
+
:return: Dictionary mapping "start->end" to time difference in milliseconds.
|
|
246
|
+
:rtype: Dict[str, float]
|
|
247
|
+
"""
|
|
248
|
+
if not self.config.enabled:
|
|
249
|
+
return {}
|
|
250
|
+
keys = list(self.marks.keys())
|
|
251
|
+
out: Dict[str, float] = {}
|
|
252
|
+
for a, b in zip(keys, keys[1:]):
|
|
253
|
+
out[f"{a}->{b}"] = (self.marks[b] - self.marks[a]) * 1000.0
|
|
254
|
+
return out
|
|
255
|
+
|
|
256
|
+
def should_report(self, frame_index: int) -> bool:
|
|
257
|
+
"""
|
|
258
|
+
Determine if a report should be emitted for the given frame index.
|
|
259
|
+
|
|
260
|
+
:param frame_index: Current frame index.
|
|
261
|
+
:type frame_index: int
|
|
262
|
+
:return: True if a report should be emitted, False otherwise.
|
|
263
|
+
:rtype: bool
|
|
264
|
+
"""
|
|
265
|
+
return (
|
|
266
|
+
self.config.enabled
|
|
267
|
+
and self.config.report_every > 0
|
|
268
|
+
and frame_index > 0
|
|
269
|
+
and (frame_index % self.config.report_every == 0)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def emit(self, frame_index: int):
|
|
273
|
+
"""
|
|
274
|
+
Emit a timing report to the performance logger.
|
|
275
|
+
|
|
276
|
+
:param frame_index: Current frame index.
|
|
277
|
+
:type frame_index: int
|
|
278
|
+
"""
|
|
279
|
+
if not self.config.enabled:
|
|
280
|
+
return
|
|
281
|
+
diffs = self.report_ms()
|
|
282
|
+
report = self.formatter.make_report(frame_index, diffs)
|
|
283
|
+
perf_logger.info(self.formatter.format(report))
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
mini_arcade_core/__init__.py,sha256=AdhR8GzhJnPYBkwIdNag1cnB8e4JqCHNZxsZfrw5vCQ,2564
|
|
2
|
+
mini_arcade_core/backend/__init__.py,sha256=J1wZBHX-aqmBP3zh_ey9PK2b_gnWp72zxMfKcs3iwSw,274
|
|
3
|
+
mini_arcade_core/backend/backend.py,sha256=tJYjHqjWZst8P3uikStndEHIVWLvF0ypmibVtCErbag,8699
|
|
4
|
+
mini_arcade_core/backend/events.py,sha256=5Ohve3CQ6n2CztiOhbCoz6yFDY4z0j4v4R9FBKRDRjc,2929
|
|
5
|
+
mini_arcade_core/backend/keys.py,sha256=LTg20SwLBI3kpPIiTNpq2yBft_QUGj-iNFSNm9M-Fus,3010
|
|
6
|
+
mini_arcade_core/backend/sdl_map.py,sha256=_yBRtvaFUcQKy1kcoIf-SPhbbKEW7dzvzBcI6TLmKjc,2060
|
|
7
|
+
mini_arcade_core/backend/types.py,sha256=EW0bW4MvsEZKot0Z1h_5LuFSzoYGiJBphTquBz4oXf4,244
|
|
8
|
+
mini_arcade_core/bus.py,sha256=2Etpoa-UWhk33xJjqDlY5YslPDJEjxNoIEVtF3C73vs,1558
|
|
9
|
+
mini_arcade_core/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
mini_arcade_core/engine/cheats.py,sha256=jMx2a8YnaNCkCG5MPmIzz4uHuS7-_aYf0J45cv2-3v0,5569
|
|
11
|
+
mini_arcade_core/engine/commands.py,sha256=Cw1tAwVRO5U2--hFX1Jq00LH_84oe1Oqw-Ngc0RnkGI,5383
|
|
12
|
+
mini_arcade_core/engine/game.py,sha256=DDdvlv0cN4sjBKtoIuP19W3YQCF1LwZbC_jYCz-1E4U,5767
|
|
13
|
+
mini_arcade_core/engine/game_config.py,sha256=4AP8n0Uk1HKEdPLOrV1xsySzBljAh8VhZASNrxPIMMc,1034
|
|
14
|
+
mini_arcade_core/engine/gameplay_settings.py,sha256=W8WBwfAvGZftkL4aMnOTx6SsGxwG-9Ou1Ey0AeWPCxs,549
|
|
15
|
+
mini_arcade_core/engine/loop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
mini_arcade_core/engine/loop/config.py,sha256=Sj1LrdnD_aACmHUuRQuKB7bDbDZy60WVe8sJHRmzmIU,461
|
|
17
|
+
mini_arcade_core/engine/loop/hooks.py,sha256=nmZi-35iMsZoPedTdZzsIKJP6O_iFUeKjB8xc4-XTHU,2417
|
|
18
|
+
mini_arcade_core/engine/loop/runner.py,sha256=uB4onDMO6lWFt40YNJsovkVHXiyaVdEuEFj8kYqWn6k,8858
|
|
19
|
+
mini_arcade_core/engine/loop/state.py,sha256=fzXQ9GP05PVNXEBTgIwA4qjMujxdUae3CXM6uRQz92Y,858
|
|
20
|
+
mini_arcade_core/engine/managers.py,sha256=eQJYe-xYtRha-FWxzJ3DcpwlcHwiT5sGt4oCD9ZPxEE,664
|
|
21
|
+
mini_arcade_core/engine/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
mini_arcade_core/engine/render/context.py,sha256=igoBmsasv3AJtrIIe2IGkjHXEm5ouEobvdN7Gmm0vxY,1327
|
|
23
|
+
mini_arcade_core/engine/render/effects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
mini_arcade_core/engine/render/effects/base.py,sha256=uix-kfzvN5j3dx655h_Yxe-a3rN1WTtN4tj5zeqFi3c,2357
|
|
25
|
+
mini_arcade_core/engine/render/effects/crt.py,sha256=75ejQVNhftN_GwZV9CYkHv2D672V84zDygmO9h_fo1A,2154
|
|
26
|
+
mini_arcade_core/engine/render/effects/registry.py,sha256=ZBbyyhF4K2gxb4f6hV35uh6RuSj7kOhQgcxwKocqJpY,1212
|
|
27
|
+
mini_arcade_core/engine/render/effects/vignette.py,sha256=K87CjIWpjlWN_tJVrnY3tW2njOvLvnuRezCwk05OgVw,2610
|
|
28
|
+
mini_arcade_core/engine/render/frame_packet.py,sha256=nYHvR7CHlIZa6ZazmPO2dU2P91vEkBjBzUVQGrOkaYc,624
|
|
29
|
+
mini_arcade_core/engine/render/packet.py,sha256=OiAPwGoVHo04OcUWMAoA_N1AFPUMyf8yxNgJthGj4-c,1440
|
|
30
|
+
mini_arcade_core/engine/render/passes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
mini_arcade_core/engine/render/passes/base.py,sha256=LWgWhfafbCvRKIFbt3koW-ibjYxMfKOIXyLNazCakcM,864
|
|
32
|
+
mini_arcade_core/engine/render/passes/begin_frame.py,sha256=RowMYnkSSQVlMCDVRHXEiy4Hb5Ru-VPBqxsmMzzkXuU,706
|
|
33
|
+
mini_arcade_core/engine/render/passes/end_frame.py,sha256=BBNyVhD8s7B9c_s373g9DDH_eVNGGVnMl5n0GhKIgiw,767
|
|
34
|
+
mini_arcade_core/engine/render/passes/lighting.py,sha256=ugmHHNNZWArL_Xs6-1SQIxLptUTQwe4M2sSPEk8X-7s,677
|
|
35
|
+
mini_arcade_core/engine/render/passes/postfx.py,sha256=YLLcJb1qwNwxg40f_6Nh1xXeGN1tsCnGd8_FdKsfRAM,1424
|
|
36
|
+
mini_arcade_core/engine/render/passes/ui.py,sha256=cct8v0Jjqv6w77IlKz2medPkQjPvG8cCifRv943qyFM,1129
|
|
37
|
+
mini_arcade_core/engine/render/passes/world.py,sha256=OhvDB8aPTkFGlHiROlL9A13pdjWAYGbo8NadkWCUK-c,1522
|
|
38
|
+
mini_arcade_core/engine/render/pipeline.py,sha256=A-Pxw0t0RbW_pswVtBEGNIgC-1dSNqwaK8p7JoXiw8k,3288
|
|
39
|
+
mini_arcade_core/engine/render/render_service.py,sha256=1ueir8MZ6Six5gAHt5StoICPAbyppX4DqzWb8HEuS9g,531
|
|
40
|
+
mini_arcade_core/engine/render/viewport.py,sha256=Fi7O04KRC6d3s01sP0cfchdwegt6s_vOdwomck9yryc,5972
|
|
41
|
+
mini_arcade_core/engine/scenes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
mini_arcade_core/engine/scenes/models.py,sha256=UKKGUWyFf_XFC09hICTFQ6_szDXytoI5KauCmr9ugoM,1217
|
|
43
|
+
mini_arcade_core/engine/scenes/scene_manager.py,sha256=xGzMH7tBsHExN9aYLZsx462aY9r5j-cDNqQvwnK3cI8,6455
|
|
44
|
+
mini_arcade_core/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
mini_arcade_core/runtime/audio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
|
+
mini_arcade_core/runtime/audio/audio_adapter.py,sha256=lnP35txPzSKX1_il0nXcK7RMF5Qp9Qhi9YMh_7LTdPM,588
|
|
47
|
+
mini_arcade_core/runtime/audio/audio_port.py,sha256=jBd9WabN41uK3MHjg_1n4AOw83NivJlGE2m430WZTnk,831
|
|
48
|
+
mini_arcade_core/runtime/capture/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
|
+
mini_arcade_core/runtime/capture/capture_adapter.py,sha256=T3Ca7qCB4ZtGDLE5iKxSWwStMen4RTWdQEn8Zs48tqk,5447
|
|
50
|
+
mini_arcade_core/runtime/capture/capture_port.py,sha256=niHi0pAo10mC9p73FxFkYBIGLOLRN0PiOvxE4Zgo5fM,1162
|
|
51
|
+
mini_arcade_core/runtime/capture/capture_worker.py,sha256=XAzL28jD8jnGoG3bvsd9yQW5S4Nv7licNJn94QTIzp4,5393
|
|
52
|
+
mini_arcade_core/runtime/context.py,sha256=ONKQryO3KEOOqHaByxCUola07kdjrnvr4WfXwgwTobk,1777
|
|
53
|
+
mini_arcade_core/runtime/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
+
mini_arcade_core/runtime/file/file_adapter.py,sha256=09q7G9Qijml9d4AAjo6HLC1yuoVTjE_7xaT8apT4mk0,523
|
|
55
|
+
mini_arcade_core/runtime/file/file_port.py,sha256=p1MouCSHXZw--rWNMw3aYBLU-of8mXaT_suopczPtM8,608
|
|
56
|
+
mini_arcade_core/runtime/input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
+
mini_arcade_core/runtime/input/input_adapter.py,sha256=vExQiwFIWTI3zYD8lmnD9TvoQPZvJfI6IINPJUqAdQ0,1467
|
|
58
|
+
mini_arcade_core/runtime/input/input_port.py,sha256=d4ptftwf92_LJdyaUMFxIsLHXBINzQyJACHn4laNyxQ,746
|
|
59
|
+
mini_arcade_core/runtime/input_frame.py,sha256=34-RAfOD-YScVLyRQrarpm7byFTHjsWM77lIH0JsmT8,2384
|
|
60
|
+
mini_arcade_core/runtime/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
+
mini_arcade_core/runtime/render/render_port.py,sha256=Sqp-JBh-iRzzGtgnO_nU1KiJEqyrTYPRDQbg04HdR0A,507
|
|
62
|
+
mini_arcade_core/runtime/scene/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
|
+
mini_arcade_core/runtime/scene/scene_query_adapter.py,sha256=FNkqXgwxfugX_xqqFlZl0ELXsrW_gco5Au0tJhMGLgQ,909
|
|
64
|
+
mini_arcade_core/runtime/scene/scene_query_port.py,sha256=qTikQVxOkJCdoMoH_lbe_ctJj7SWeJnnqDo6Ee0N_pQ,1019
|
|
65
|
+
mini_arcade_core/runtime/services.py,sha256=iYcXt2CTapgDzSb54DsPasYZ4jTN7tA_B0lV1Sl5b1g,1243
|
|
66
|
+
mini_arcade_core/runtime/window/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
|
+
mini_arcade_core/runtime/window/window_adapter.py,sha256=VLZGYBVl7sGMmnk5mVowDleTyciAfE-Tc2woNFvRrgE,2890
|
|
68
|
+
mini_arcade_core/runtime/window/window_port.py,sha256=HBy2OjsZzlxbBDQiTqlKEbIaejpN1zDp5whgvKxZxaY,2322
|
|
69
|
+
mini_arcade_core/scenes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
70
|
+
mini_arcade_core/scenes/autoreg.py,sha256=wsuY7YUSZFmDyToKHFriAG78OU48-7J4BfL_X6T5GBg,1037
|
|
71
|
+
mini_arcade_core/scenes/debug_overlay.py,sha256=t7zWeTxosCUWj3gBDkYF2448EBM5zwLCOEmWVHXghMk,2495
|
|
72
|
+
mini_arcade_core/scenes/registry.py,sha256=DHliUGGiSLWugtRU9R6JeH5gK3GUDICmS-3iie6GtH8,3631
|
|
73
|
+
mini_arcade_core/scenes/sim_scene.py,sha256=32GVR9XHMak-afyyH9M6_UkCPAjRz8XiUKj2lcpAGAE,1061
|
|
74
|
+
mini_arcade_core/scenes/systems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
|
+
mini_arcade_core/scenes/systems/base_system.py,sha256=GfMrXsO8ynW3xOxWeav7Ug5XUbRnbF0vo8VzmG7gpec,1075
|
|
76
|
+
mini_arcade_core/scenes/systems/system_pipeline.py,sha256=Cy9y1DclbMLZZ-yx7OKYe34ORoGLNa6dReQfOdiO8SY,1642
|
|
77
|
+
mini_arcade_core/spaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
|
+
mini_arcade_core/spaces/d2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
+
mini_arcade_core/spaces/d2/boundaries2d.py,sha256=xeTnd0pW5DKfqaKsfSBXnufeb45aXNIspgHRyLXWejo,2804
|
|
80
|
+
mini_arcade_core/spaces/d2/collision2d.py,sha256=5IvgLnyVb8i0uzzZuum1noWsNhoxcvHOLaHkmrTMTxQ,1710
|
|
81
|
+
mini_arcade_core/spaces/d2/geometry2d.py,sha256=FuYzef-XdOyb1aeGLJbxINxr0WJHnqFFBgtbPi1WonY,1716
|
|
82
|
+
mini_arcade_core/spaces/d2/kinematics2d.py,sha256=AJ3DhPXNgm6wZYwCljMIE4_2BYx3E2rPcwhXTgQALkU,2030
|
|
83
|
+
mini_arcade_core/spaces/d2/physics2d.py,sha256=OQT7r-zMtmoKD2aWCSNmRAdI0OGIpxGX-pLR8LcAMbQ,1854
|
|
84
|
+
mini_arcade_core/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
|
+
mini_arcade_core/ui/menu.py,sha256=t2YpwiKqy4KZHfU9U7c6CUE3SNamBSyipWnd9Y4BVxA,25738
|
|
86
|
+
mini_arcade_core/utils/__init__.py,sha256=id1C0au8r1oIzGha42xXwnI9ojcU1hxPgto6QSh9H8c,236
|
|
87
|
+
mini_arcade_core/utils/deprecated_decorator.py,sha256=yrrW2ZqPskK-4MUTyIrMb465Wc54X2poV53ZQutZWqc,1140
|
|
88
|
+
mini_arcade_core/utils/logging.py,sha256=ygKpey6nikp30PrNDP_yRs8pxPPRbsQ0ivR6LUuEn3Q,6413
|
|
89
|
+
mini_arcade_core/utils/profiler.py,sha256=vLzrxDfAplgKGxpuzk4eFJx4t5DU5M3DQAn6sfS5D_4,8733
|
|
90
|
+
mini_arcade_core-1.2.1.dist-info/METADATA,sha256=me0QR43UEolQONEt9HpCqPM-R9Khq6nq9YNa2GcePtw,8188
|
|
91
|
+
mini_arcade_core-1.2.1.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
|
|
92
|
+
mini_arcade_core-1.2.1.dist-info/licenses/LICENSE,sha256=3lHAuV0584cVS5vAqi2uC6GcsVgxUijvwvtZckyvaZ4,1096
|
|
93
|
+
mini_arcade_core-1.2.1.dist-info/RECORD,,
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Input manager for handling input bindings and commands.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
# TODO: Implement this manager into the new input system
|
|
6
|
-
# Justification: These module will be used later.
|
|
7
|
-
# pylint: disable=no-name-in-module,import-error,used-before-assignment
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import logging
|
|
12
|
-
from dataclasses import dataclass
|
|
13
|
-
from typing import TYPE_CHECKING, Callable, Dict, Optional
|
|
14
|
-
|
|
15
|
-
from mini_arcade_core.backend import Event, EventType
|
|
16
|
-
from mini_arcade_core.keymaps import Key
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from mini_arcade_core.engine.commands import BaseCommand, BaseSceneCommand
|
|
20
|
-
from mini_arcade_core.scenes.scene import Scene
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Predicate = Callable[["Event"], bool]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@dataclass(frozen=True)
|
|
29
|
-
class InputBinding:
|
|
30
|
-
"""
|
|
31
|
-
Defines an input binding.
|
|
32
|
-
|
|
33
|
-
:ivar action (str): The action name.
|
|
34
|
-
:ivar command (BaseCommand): The command to execute.
|
|
35
|
-
:ivar predicate (Predicate): Predicate to match events.
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
action: str
|
|
39
|
-
command: BaseCommand
|
|
40
|
-
predicate: Predicate # decides whether this binding matches an event
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class InputManager:
|
|
44
|
-
"""
|
|
45
|
-
Manager for handling input bindings and commands.
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
def __init__(self):
|
|
49
|
-
# event_type -> key -> action -> command
|
|
50
|
-
self._bindings: Dict[EventType, Dict[Key, Dict[str, BaseCommand]]] = {}
|
|
51
|
-
|
|
52
|
-
# Justification: The method needs multiple optional parameters for flexibility.
|
|
53
|
-
# pylint: disable=too-many-arguments
|
|
54
|
-
def bind(
|
|
55
|
-
self,
|
|
56
|
-
event_type: EventType,
|
|
57
|
-
action: str,
|
|
58
|
-
command: BaseCommand,
|
|
59
|
-
*,
|
|
60
|
-
key: Optional[Key] = None,
|
|
61
|
-
button: Optional[int] = None,
|
|
62
|
-
predicate: Optional[Predicate] = None,
|
|
63
|
-
):
|
|
64
|
-
"""
|
|
65
|
-
Generic binding.
|
|
66
|
-
|
|
67
|
-
You can filter by:
|
|
68
|
-
- key: for KEYDOWN/KEYUP
|
|
69
|
-
- button: for MOUSEBUTTONDOWN/MOUSEBUTTONUP (if your Event exposes it)
|
|
70
|
-
- predicate: custom matcher (for anything)
|
|
71
|
-
|
|
72
|
-
:param event_type: The type of event to bind to.
|
|
73
|
-
:type event_type: EventType
|
|
74
|
-
|
|
75
|
-
:param action: The action name for the binding.
|
|
76
|
-
:type action: str
|
|
77
|
-
|
|
78
|
-
:param command: The command to execute when the binding is triggered.
|
|
79
|
-
:type command: BaseCommand
|
|
80
|
-
|
|
81
|
-
:param key: Optional key to filter KEYDOWN/KEYUP events.
|
|
82
|
-
:type key: Key | None
|
|
83
|
-
|
|
84
|
-
:param button: Optional button to filter MOUSEBUTTONDOWN/MOUSEBUTTONUP events.
|
|
85
|
-
:type button: int | None
|
|
86
|
-
|
|
87
|
-
:param predicate: Optional custom predicate to match events.
|
|
88
|
-
:type predicate: Predicate | None
|
|
89
|
-
"""
|
|
90
|
-
logger.debug(
|
|
91
|
-
f"Binding {action} to {event_type} with key={key}, button={button}"
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
def default_predicate(ev: Event) -> bool:
|
|
95
|
-
if key is not None and getattr(ev, "key", None) != key:
|
|
96
|
-
return False
|
|
97
|
-
if button is not None and getattr(ev, "button", None) != button:
|
|
98
|
-
return False
|
|
99
|
-
return True
|
|
100
|
-
|
|
101
|
-
pred = predicate or default_predicate
|
|
102
|
-
self._bindings.setdefault(event_type, []).append(
|
|
103
|
-
InputBinding(action=action, command=command, predicate=pred)
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# pylint: enable=too-many-arguments
|
|
107
|
-
|
|
108
|
-
def unbind(self, event_type: EventType, action: str):
|
|
109
|
-
"""
|
|
110
|
-
Remove bindings by action for an event type.
|
|
111
|
-
|
|
112
|
-
:param event_type: The type of event to unbind from.
|
|
113
|
-
:type event_type: EventType
|
|
114
|
-
|
|
115
|
-
:param action: The action name of the binding to remove.
|
|
116
|
-
:type action: str
|
|
117
|
-
"""
|
|
118
|
-
lst = self._bindings.get(event_type, [])
|
|
119
|
-
self._bindings[event_type] = [b for b in lst if b.action != action]
|
|
120
|
-
|
|
121
|
-
def clear(self):
|
|
122
|
-
"""Clear all input bindings."""
|
|
123
|
-
self._bindings.clear()
|
|
124
|
-
|
|
125
|
-
def handle_event(self, event: Event, scene: Scene):
|
|
126
|
-
"""
|
|
127
|
-
Handle an incoming event, executing any matching commands.
|
|
128
|
-
|
|
129
|
-
:param event: The event to handle.
|
|
130
|
-
:type event: Event
|
|
131
|
-
|
|
132
|
-
:param scene: The current scene context.
|
|
133
|
-
:type scene: Scene
|
|
134
|
-
"""
|
|
135
|
-
et = event.type
|
|
136
|
-
|
|
137
|
-
for binding in self._bindings.get(et, []):
|
|
138
|
-
if binding.predicate(event):
|
|
139
|
-
to_inject = (
|
|
140
|
-
scene.model
|
|
141
|
-
if isinstance(binding.command, BaseSceneCommand)
|
|
142
|
-
else scene.game
|
|
143
|
-
)
|
|
144
|
-
binding.command.execute(to_inject)
|
|
145
|
-
|
|
146
|
-
def on_quit(self, command: BaseCommand, action: str = "quit"):
|
|
147
|
-
"""
|
|
148
|
-
Bind a command to the QUIT event.
|
|
149
|
-
|
|
150
|
-
:param command: The command to execute on quit.
|
|
151
|
-
:type command: BaseCommand
|
|
152
|
-
|
|
153
|
-
:param action: The action name for the binding.
|
|
154
|
-
:type action: str
|
|
155
|
-
"""
|
|
156
|
-
self.bind(EventType.QUIT, action=action, command=command)
|
|
157
|
-
|
|
158
|
-
def on_key_down(self, key: Key, command: BaseCommand, action: str):
|
|
159
|
-
"""
|
|
160
|
-
Bind a command to a key down event.
|
|
161
|
-
|
|
162
|
-
:param key: The key to bind to.
|
|
163
|
-
:type key: Key
|
|
164
|
-
|
|
165
|
-
:param command: The command to execute on key down.
|
|
166
|
-
:type command: BaseCommand
|
|
167
|
-
|
|
168
|
-
:param action: The action name for the binding.
|
|
169
|
-
:type action: str
|
|
170
|
-
"""
|
|
171
|
-
self.bind(EventType.KEYDOWN, key=key, action=action, command=command)
|
|
172
|
-
|
|
173
|
-
def on_key_up(self, key: Key, command: BaseCommand, action: str):
|
|
174
|
-
"""
|
|
175
|
-
Bind a command to a key up event.
|
|
176
|
-
|
|
177
|
-
:param key: The key to bind to.
|
|
178
|
-
:type key: Key
|
|
179
|
-
|
|
180
|
-
:param command: The command to execute on key up.
|
|
181
|
-
:type command: BaseCommand
|
|
182
|
-
|
|
183
|
-
:param action: The action name for the binding.
|
|
184
|
-
:type action: str
|
|
185
|
-
"""
|
|
186
|
-
self.bind(EventType.KEYUP, key=key, action=action, command=command)
|
|
187
|
-
|
|
188
|
-
def on_mouse_button_down(
|
|
189
|
-
self, button: int, command: BaseCommand, action: str
|
|
190
|
-
):
|
|
191
|
-
"""
|
|
192
|
-
Bind a command to a mouse button down event.
|
|
193
|
-
|
|
194
|
-
:param button: The mouse button to bind to.
|
|
195
|
-
:type button: int
|
|
196
|
-
|
|
197
|
-
:param command: The command to execute on mouse button down.
|
|
198
|
-
:type command: BaseCommand
|
|
199
|
-
|
|
200
|
-
:param action: The action name for the binding.
|
|
201
|
-
:type action: str
|
|
202
|
-
"""
|
|
203
|
-
self.bind(
|
|
204
|
-
EventType.MOUSEBUTTONDOWN,
|
|
205
|
-
button=button,
|
|
206
|
-
action=action,
|
|
207
|
-
command=command,
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
def on_mouse_button_up(
|
|
211
|
-
self, button: int, command: BaseCommand, action: str
|
|
212
|
-
):
|
|
213
|
-
"""
|
|
214
|
-
Bind a command to a mouse button up event.
|
|
215
|
-
|
|
216
|
-
:param button: The mouse button to bind to.
|
|
217
|
-
:type button: int
|
|
218
|
-
|
|
219
|
-
:param command: The command to execute on mouse button up.
|
|
220
|
-
:type command: BaseCommand
|
|
221
|
-
|
|
222
|
-
:param action: The action name for the binding.
|
|
223
|
-
:type action: str
|
|
224
|
-
"""
|
|
225
|
-
self.bind(
|
|
226
|
-
EventType.MOUSEBUTTONUP,
|
|
227
|
-
button=button,
|
|
228
|
-
action=action,
|
|
229
|
-
command=command,
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
def on_mouse_motion(
|
|
233
|
-
self, command: BaseCommand, action: str = "mouse_motion"
|
|
234
|
-
):
|
|
235
|
-
"""
|
|
236
|
-
Bind a command to mouse motion events.
|
|
237
|
-
|
|
238
|
-
:param command: The command to execute on mouse motion.
|
|
239
|
-
:type command: BaseCommand
|
|
240
|
-
|
|
241
|
-
:param action: The action name for the binding.
|
|
242
|
-
:type action: str
|
|
243
|
-
"""
|
|
244
|
-
self.bind(EventType.MOUSEMOTION, action=action, command=command)
|
|
245
|
-
|
|
246
|
-
def on_mouse_wheel(
|
|
247
|
-
self, command: BaseCommand, action: str = "mouse_wheel"
|
|
248
|
-
):
|
|
249
|
-
"""
|
|
250
|
-
Bind a command to mouse wheel events.
|
|
251
|
-
|
|
252
|
-
:param command: The command to execute on mouse wheel.
|
|
253
|
-
:type command: BaseCommand
|
|
254
|
-
|
|
255
|
-
:param action: The action name for the binding.
|
|
256
|
-
:type action: str
|
|
257
|
-
"""
|
|
258
|
-
self.bind(EventType.MOUSEWHEEL, action=action, command=command)
|
|
259
|
-
|
|
260
|
-
def on_window_resized(
|
|
261
|
-
self, command: BaseCommand, action: str = "window_resized"
|
|
262
|
-
):
|
|
263
|
-
"""
|
|
264
|
-
Bind a command to window resized events.
|
|
265
|
-
|
|
266
|
-
:param command: The command to execute on window resize.
|
|
267
|
-
:type command: BaseCommand
|
|
268
|
-
|
|
269
|
-
:param action: The action name for the binding.
|
|
270
|
-
:type action: str
|
|
271
|
-
"""
|
|
272
|
-
self.bind(EventType.WINDOWRESIZED, action=action, command=command)
|
|
273
|
-
|
|
274
|
-
def on_text_input(self, command: BaseCommand, action: str = "text_input"):
|
|
275
|
-
"""
|
|
276
|
-
Bind a command to text input events.
|
|
277
|
-
|
|
278
|
-
:param command: The command to execute on text input.
|
|
279
|
-
:type command: BaseCommand
|
|
280
|
-
|
|
281
|
-
:param action: The action name for the binding.
|
|
282
|
-
:type action: str
|
|
283
|
-
"""
|
|
284
|
-
self.bind(EventType.TEXTINPUT, action=action, command=command)
|