pygkit 1.0.7__tar.gz → 1.2.1__tar.gz
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.
- {pygkit-1.0.7 → pygkit-1.2.1}/PKG-INFO +17 -3
- {pygkit-1.0.7 → pygkit-1.2.1}/README.md +8 -2
- {pygkit-1.0.7 → pygkit-1.2.1}/pyproject.toml +16 -1
- pygkit-1.2.1/src/pygkit/__init__.py +223 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/animation/sheet.py +72 -15
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/inventory/__init__.py +1 -16
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/inventory/assets.py +1 -4
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/inventory/core.py +1 -2
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/inventory/data.py +1 -2
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/lighting/__init__.py +4 -4
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/lighting/lightmap.py +1 -1
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/lighting/lights.py +3 -3
- pygkit-1.2.1/src/pygkit/protocols.py +49 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/ui/__init__.py +12 -0
- pygkit-1.2.1/src/pygkit/ui/anchors.py +80 -0
- pygkit-1.2.1/src/pygkit/ui/button.py +134 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/ui/dialog.py +1 -26
- pygkit-1.2.1/src/pygkit/ui/menubar.py +117 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit.egg-info/PKG-INFO +17 -3
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit.egg-info/SOURCES.txt +7 -0
- pygkit-1.2.1/src/pygkit.egg-info/requires.txt +16 -0
- pygkit-1.2.1/tests/test_anchors.py +39 -0
- pygkit-1.2.1/tests/test_button.py +88 -0
- pygkit-1.2.1/tests/test_menubar.py +132 -0
- pygkit-1.2.1/tests/test_packaging.py +106 -0
- pygkit-1.0.7/src/pygkit/__init__.py +0 -95
- pygkit-1.0.7/src/pygkit/protocols.py +0 -29
- pygkit-1.0.7/src/pygkit.egg-info/requires.txt +0 -1
- {pygkit-1.0.7 → pygkit-1.2.1}/LICENSE +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/setup.cfg +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/animation/__init__.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/animation/player.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/audio/__init__.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/audio/soundmanager.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/inventory/ui.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/lighting/blend.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/lighting/sprites.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/signals/__init__.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/signals/core.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/transitions/__init__.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/transitions/base.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/transitions/effects.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/transitions/fades.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/transitions/wipes.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/ui/base.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/ui/container.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/ui/cooldown.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/ui/progressbar.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/utils/__init__.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/utils/interpolation.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/utils/text.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit/utils/timer.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit.egg-info/dependency_links.txt +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/src/pygkit.egg-info/top_level.txt +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_animation.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_box_model.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_dialog.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_interpolation.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_inventory.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_lighting.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_signals.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_text.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_timer.py +0 -0
- {pygkit-1.0.7 → pygkit-1.2.1}/tests/test_transitions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygkit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: General-purpose Pygame CE runtime kit: UI widgets, audio, timers, interpolation, and more.
|
|
5
5
|
Author: pygkit contributors
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -694,6 +694,14 @@ Requires-Python: >=3.10
|
|
|
694
694
|
Description-Content-Type: text/markdown
|
|
695
695
|
License-File: LICENSE
|
|
696
696
|
Requires-Dist: pygame-ce>=2.5
|
|
697
|
+
Provides-Extra: ui
|
|
698
|
+
Provides-Extra: audio
|
|
699
|
+
Provides-Extra: animation
|
|
700
|
+
Provides-Extra: lighting
|
|
701
|
+
Provides-Extra: transitions
|
|
702
|
+
Provides-Extra: inventory
|
|
703
|
+
Provides-Extra: all
|
|
704
|
+
Requires-Dist: pygkit[animation,audio,inventory,lighting,transitions,ui]; extra == "all"
|
|
697
705
|
Dynamic: license-file
|
|
698
706
|
|
|
699
707
|
# pygkit
|
|
@@ -708,7 +716,7 @@ import pygkit
|
|
|
708
716
|
|
|
709
717
|
## Features
|
|
710
718
|
|
|
711
|
-
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers, dialog system
|
|
719
|
+
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers, dialog system, buttons, menubar container, anchor/row/grid placement helpers
|
|
712
720
|
- **Animation**: Multi-state `AnimationPlayer` where every state owns its own spritesheet — mixed image sizes and grids (e.g. `idle_spritesheet.png` vs `run_spritesheet.png`) in one player; pure image API, no JSON
|
|
713
721
|
- **Dialog**: Typewriter effect with customizable speeds, speaker names, and progress indicators
|
|
714
722
|
- **Inventory**: Grid-based item management with stacking, weight tracking, events, and serialization
|
|
@@ -722,5 +730,11 @@ import pygkit
|
|
|
722
730
|
## Installation
|
|
723
731
|
|
|
724
732
|
```bash
|
|
725
|
-
pip install pygkit
|
|
733
|
+
pip install pygkit # core: signals, utils, protocols
|
|
734
|
+
pip install "pygkit[ui]" # widgets, dialog, cooldowns
|
|
735
|
+
pip install "pygkit[all]" # everything
|
|
726
736
|
```
|
|
737
|
+
|
|
738
|
+
Available extras: `ui`, `audio`, `animation`, `lighting`,
|
|
739
|
+
`transitions`, `inventory`, `all`. `import pygkit` alone loads
|
|
740
|
+
core only; subsystems load lazily on first use.
|
|
@@ -10,7 +10,7 @@ import pygkit
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers, dialog system
|
|
13
|
+
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers, dialog system, buttons, menubar container, anchor/row/grid placement helpers
|
|
14
14
|
- **Animation**: Multi-state `AnimationPlayer` where every state owns its own spritesheet — mixed image sizes and grids (e.g. `idle_spritesheet.png` vs `run_spritesheet.png`) in one player; pure image API, no JSON
|
|
15
15
|
- **Dialog**: Typewriter effect with customizable speeds, speaker names, and progress indicators
|
|
16
16
|
- **Inventory**: Grid-based item management with stacking, weight tracking, events, and serialization
|
|
@@ -24,5 +24,11 @@ import pygkit
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
pip install pygkit
|
|
27
|
+
pip install pygkit # core: signals, utils, protocols
|
|
28
|
+
pip install "pygkit[ui]" # widgets, dialog, cooldowns
|
|
29
|
+
pip install "pygkit[all]" # everything
|
|
28
30
|
```
|
|
31
|
+
|
|
32
|
+
Available extras: `ui`, `audio`, `animation`, `lighting`,
|
|
33
|
+
`transitions`, `inventory`, `all`. `import pygkit` alone loads
|
|
34
|
+
core only; subsystems load lazily on first use.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pygkit"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.1"
|
|
8
8
|
description = "General-purpose Pygame CE runtime kit: UI widgets, audio, timers, interpolation, and more."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -26,6 +26,21 @@ classifiers = [
|
|
|
26
26
|
"Topic :: Software Development :: Libraries",
|
|
27
27
|
]
|
|
28
28
|
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
# One extra per subsystem. Empty for now: every subsystem is pure
|
|
31
|
+
# pygame-ce + stdlib. Slots are reserved for future speedups
|
|
32
|
+
# (lighting color math -> numpy, sprite pack -> pillow) without
|
|
33
|
+
# taxing core installs.
|
|
34
|
+
ui = []
|
|
35
|
+
audio = []
|
|
36
|
+
animation = []
|
|
37
|
+
lighting = []
|
|
38
|
+
transitions = []
|
|
39
|
+
inventory = []
|
|
40
|
+
all = [
|
|
41
|
+
"pygkit[ui, audio, animation, lighting, transitions, inventory]",
|
|
42
|
+
]
|
|
43
|
+
|
|
29
44
|
[project.urls]
|
|
30
45
|
Homepage = "https://github.com/FluffyBrudy/pygkit"
|
|
31
46
|
Repository = "https://github.com/FluffyBrudy/pygkit"
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""pygkit - Pygame CE runtime kit.
|
|
2
|
+
|
|
3
|
+
Core (signals, utils, protocols) imports eagerly. Subsystems load
|
|
4
|
+
lazily on first attribute use, so ``import pygkit`` stays cheap::
|
|
5
|
+
|
|
6
|
+
pip install pygkit # core only
|
|
7
|
+
pip install "pygkit[ui]" # + widgets, dialog, cooldowns
|
|
8
|
+
pip install "pygkit[all]" # everything
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import importlib
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from .protocols import Clickable, Drawable, Renderable, UIElement, Updateable, Widget
|
|
17
|
+
from .signals import Signal, SignalBus, signal
|
|
18
|
+
from .utils import (
|
|
19
|
+
SimpleInterpolation,
|
|
20
|
+
Timer,
|
|
21
|
+
ease_in,
|
|
22
|
+
ease_in_out,
|
|
23
|
+
ease_out,
|
|
24
|
+
fit,
|
|
25
|
+
lerp,
|
|
26
|
+
load_font,
|
|
27
|
+
outline,
|
|
28
|
+
render_multiline,
|
|
29
|
+
shadow,
|
|
30
|
+
smoothstep,
|
|
31
|
+
wrap,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_LAZY_ATTRS: dict[str, str] = {
|
|
35
|
+
"SoundManager": "audio",
|
|
36
|
+
"SFX_CHANNELS": "audio",
|
|
37
|
+
"AnimationPlayer": "animation",
|
|
38
|
+
"AnimationSheet": "animation",
|
|
39
|
+
"PackOptions": "animation",
|
|
40
|
+
"pack_spritesheet": "animation",
|
|
41
|
+
"LightMap": "lighting",
|
|
42
|
+
"PointLight": "lighting",
|
|
43
|
+
"Spotlight": "lighting",
|
|
44
|
+
"DynamicLight": "lighting",
|
|
45
|
+
"glow_sprite": "lighting",
|
|
46
|
+
"spotlight_sprite": "lighting",
|
|
47
|
+
"dynamic_glow_sprite": "lighting",
|
|
48
|
+
"clear_sprite_cache": "lighting",
|
|
49
|
+
"RGB_ADD": "lighting",
|
|
50
|
+
"RGB_SUB": "lighting",
|
|
51
|
+
"RGB_MULT": "lighting",
|
|
52
|
+
"RGB_MIN": "lighting",
|
|
53
|
+
"RGB_MAX": "lighting",
|
|
54
|
+
"RGBA_ADD": "lighting",
|
|
55
|
+
"RGBA_SUB": "lighting",
|
|
56
|
+
"RGBA_MULT": "lighting",
|
|
57
|
+
"RGBA_MIN": "lighting",
|
|
58
|
+
"RGBA_MAX": "lighting",
|
|
59
|
+
"LIGHT_ADD": "lighting",
|
|
60
|
+
"SCENE_MULT": "lighting",
|
|
61
|
+
"TransitionRunner": "transitions",
|
|
62
|
+
"TransitionState": "transitions",
|
|
63
|
+
"FadeToBlack": "transitions",
|
|
64
|
+
"FadeFromBlack": "transitions",
|
|
65
|
+
"Crossfade": "transitions",
|
|
66
|
+
"Flash": "transitions",
|
|
67
|
+
"IrisIn": "transitions",
|
|
68
|
+
"IrisOut": "transitions",
|
|
69
|
+
"Slide": "transitions",
|
|
70
|
+
"PixelDissolve": "transitions",
|
|
71
|
+
"Shake": "transitions",
|
|
72
|
+
"Container": "ui",
|
|
73
|
+
"CooldownOverlay": "ui",
|
|
74
|
+
"ProgressBarUI": "ui",
|
|
75
|
+
"UIBase": "ui",
|
|
76
|
+
"UIOptions": "ui",
|
|
77
|
+
"BoxModel": "ui",
|
|
78
|
+
"BoxModelResult": "ui",
|
|
79
|
+
"generate_box_model": "ui",
|
|
80
|
+
"DialogBox": "ui",
|
|
81
|
+
"DialogConfig": "ui",
|
|
82
|
+
"DialogColors": "ui",
|
|
83
|
+
"DialogLine": "ui",
|
|
84
|
+
"TypewriterSpeed": "ui",
|
|
85
|
+
"Button": "ui",
|
|
86
|
+
"ButtonStyle": "ui",
|
|
87
|
+
"Menubar": "ui",
|
|
88
|
+
"ANCHORS": "ui",
|
|
89
|
+
"anchor_point": "ui",
|
|
90
|
+
"place": "ui",
|
|
91
|
+
"row": "ui",
|
|
92
|
+
"column": "ui",
|
|
93
|
+
"grid": "ui",
|
|
94
|
+
"ItemData": "inventory",
|
|
95
|
+
"InventoryConfig": "inventory",
|
|
96
|
+
"ItemStack": "inventory",
|
|
97
|
+
"InventoryEvent": "inventory",
|
|
98
|
+
"RARITY_COLORS": "inventory",
|
|
99
|
+
"DEFAULT_INVENTORY_CONFIG": "inventory",
|
|
100
|
+
"Inventory": "inventory",
|
|
101
|
+
"AssetCache": "inventory",
|
|
102
|
+
"get_cache": "inventory",
|
|
103
|
+
"generate_procedural_icon": "inventory",
|
|
104
|
+
"render_quantity_text": "inventory",
|
|
105
|
+
"Tooltip": "inventory",
|
|
106
|
+
"DragDropState": "inventory",
|
|
107
|
+
"InventoryWidget": "inventory",
|
|
108
|
+
"InventoryManager": "inventory",
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
__all__ = [
|
|
112
|
+
"SoundManager",
|
|
113
|
+
"SFX_CHANNELS",
|
|
114
|
+
"AnimationPlayer",
|
|
115
|
+
"AnimationSheet",
|
|
116
|
+
"PackOptions",
|
|
117
|
+
"pack_spritesheet",
|
|
118
|
+
"LightMap",
|
|
119
|
+
"PointLight",
|
|
120
|
+
"Spotlight",
|
|
121
|
+
"DynamicLight",
|
|
122
|
+
"glow_sprite",
|
|
123
|
+
"spotlight_sprite",
|
|
124
|
+
"dynamic_glow_sprite",
|
|
125
|
+
"clear_sprite_cache",
|
|
126
|
+
"RGB_ADD",
|
|
127
|
+
"RGB_SUB",
|
|
128
|
+
"RGB_MULT",
|
|
129
|
+
"RGB_MIN",
|
|
130
|
+
"RGB_MAX",
|
|
131
|
+
"RGBA_ADD",
|
|
132
|
+
"RGBA_SUB",
|
|
133
|
+
"RGBA_MULT",
|
|
134
|
+
"RGBA_MIN",
|
|
135
|
+
"RGBA_MAX",
|
|
136
|
+
"LIGHT_ADD",
|
|
137
|
+
"SCENE_MULT",
|
|
138
|
+
"TransitionRunner",
|
|
139
|
+
"TransitionState",
|
|
140
|
+
"FadeToBlack",
|
|
141
|
+
"FadeFromBlack",
|
|
142
|
+
"Crossfade",
|
|
143
|
+
"Flash",
|
|
144
|
+
"IrisIn",
|
|
145
|
+
"IrisOut",
|
|
146
|
+
"Slide",
|
|
147
|
+
"PixelDissolve",
|
|
148
|
+
"Shake",
|
|
149
|
+
"Container",
|
|
150
|
+
"CooldownOverlay",
|
|
151
|
+
"ProgressBarUI",
|
|
152
|
+
"UIBase",
|
|
153
|
+
"UIOptions",
|
|
154
|
+
"BoxModel",
|
|
155
|
+
"BoxModelResult",
|
|
156
|
+
"generate_box_model",
|
|
157
|
+
"DialogBox",
|
|
158
|
+
"DialogConfig",
|
|
159
|
+
"DialogColors",
|
|
160
|
+
"DialogLine",
|
|
161
|
+
"TypewriterSpeed",
|
|
162
|
+
"Button",
|
|
163
|
+
"ButtonStyle",
|
|
164
|
+
"Menubar",
|
|
165
|
+
"ANCHORS",
|
|
166
|
+
"anchor_point",
|
|
167
|
+
"place",
|
|
168
|
+
"row",
|
|
169
|
+
"column",
|
|
170
|
+
"grid",
|
|
171
|
+
"ItemData",
|
|
172
|
+
"InventoryConfig",
|
|
173
|
+
"ItemStack",
|
|
174
|
+
"InventoryEvent",
|
|
175
|
+
"RARITY_COLORS",
|
|
176
|
+
"DEFAULT_INVENTORY_CONFIG",
|
|
177
|
+
"Inventory",
|
|
178
|
+
"AssetCache",
|
|
179
|
+
"get_cache",
|
|
180
|
+
"generate_procedural_icon",
|
|
181
|
+
"render_quantity_text",
|
|
182
|
+
"Tooltip",
|
|
183
|
+
"DragDropState",
|
|
184
|
+
"InventoryWidget",
|
|
185
|
+
"InventoryManager",
|
|
186
|
+
"SimpleInterpolation",
|
|
187
|
+
"Timer",
|
|
188
|
+
"lerp",
|
|
189
|
+
"smoothstep",
|
|
190
|
+
"ease_in_out",
|
|
191
|
+
"ease_out",
|
|
192
|
+
"ease_in",
|
|
193
|
+
"load_font",
|
|
194
|
+
"outline",
|
|
195
|
+
"shadow",
|
|
196
|
+
"wrap",
|
|
197
|
+
"render_multiline",
|
|
198
|
+
"fit",
|
|
199
|
+
"Signal",
|
|
200
|
+
"SignalBus",
|
|
201
|
+
"signal",
|
|
202
|
+
"Renderable",
|
|
203
|
+
"UIElement",
|
|
204
|
+
"Drawable",
|
|
205
|
+
"Updateable",
|
|
206
|
+
"Clickable",
|
|
207
|
+
"Widget",
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def __getattr__(name: str) -> Any:
|
|
212
|
+
try:
|
|
213
|
+
sub = _LAZY_ATTRS[name]
|
|
214
|
+
except KeyError:
|
|
215
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
|
|
216
|
+
module = importlib.import_module(f"{__name__}.{sub}")
|
|
217
|
+
value = getattr(module, name)
|
|
218
|
+
globals()[name] = value
|
|
219
|
+
return value
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def __dir__() -> list[str]:
|
|
223
|
+
return sorted(__all__)
|
|
@@ -196,7 +196,7 @@ class AnimationSheet:
|
|
|
196
196
|
``2.0`` doubles dimensions, ``0.5`` halves them.
|
|
197
197
|
"""
|
|
198
198
|
|
|
199
|
-
__slots__ = ("_surface", "_rows", "_cols", "_cell_w", "_cell_h", "_cache", "_frames", "_scale", "_orig_cell_w", "_orig_cell_h")
|
|
199
|
+
__slots__ = ("_surface", "_rows", "_cols", "_cell_w", "_cell_h", "_cache", "_frames", "_scale", "_orig_cell_w", "_orig_cell_h", "_start_row", "_start_col", "_sheet_rows", "_sheet_cols")
|
|
200
200
|
|
|
201
201
|
def __init__(
|
|
202
202
|
self,
|
|
@@ -205,11 +205,35 @@ class AnimationSheet:
|
|
|
205
205
|
cols: int = 1,
|
|
206
206
|
pattern: str | None = None,
|
|
207
207
|
scale: float = 1.0,
|
|
208
|
+
start_row: int = 0,
|
|
209
|
+
start_col: int = 0,
|
|
210
|
+
sheet_rows: int | None = None,
|
|
211
|
+
sheet_cols: int | None = None,
|
|
208
212
|
) -> None:
|
|
209
213
|
if not isinstance(rows, int) or not isinstance(cols, int) or isinstance(rows, bool) or isinstance(cols, bool):
|
|
210
214
|
raise TypeError("rows and cols must be ints")
|
|
211
215
|
if rows < 1 or cols < 1:
|
|
212
216
|
raise ValueError(f"rows and cols must be >= 1, got rows={rows}, cols={cols}")
|
|
217
|
+
if not isinstance(start_row, int) or not isinstance(start_col, int) or isinstance(start_row, bool) or isinstance(start_col, bool):
|
|
218
|
+
raise TypeError("start_row and start_col must be ints")
|
|
219
|
+
if start_row < 0 or start_col < 0:
|
|
220
|
+
raise ValueError(f"start_row and start_col must be >= 0, got start_row={start_row}, start_col={start_col}")
|
|
221
|
+
if sheet_rows is not None:
|
|
222
|
+
if not isinstance(sheet_rows, int) or isinstance(sheet_rows, bool):
|
|
223
|
+
raise TypeError("sheet_rows must be int or None")
|
|
224
|
+
if sheet_rows < 1:
|
|
225
|
+
raise ValueError(f"sheet_rows must be >= 1, got {sheet_rows}")
|
|
226
|
+
if sheet_cols is not None:
|
|
227
|
+
if not isinstance(sheet_cols, int) or isinstance(sheet_cols, bool):
|
|
228
|
+
raise TypeError("sheet_cols must be int or None")
|
|
229
|
+
if sheet_cols < 1:
|
|
230
|
+
raise ValueError(f"sheet_cols must be >= 1, got {sheet_cols}")
|
|
231
|
+
full_rows = sheet_rows if sheet_rows is not None else rows
|
|
232
|
+
full_cols = sheet_cols if sheet_cols is not None else cols
|
|
233
|
+
if start_row + rows > full_rows or start_col + cols > full_cols:
|
|
234
|
+
raise ValueError(
|
|
235
|
+
f"window rows={rows} cols={cols} at start_row={start_row} start_col={start_col} exceeds sheet {full_rows}x{full_cols}"
|
|
236
|
+
)
|
|
213
237
|
if pattern is not None and not isinstance(pattern, str):
|
|
214
238
|
raise TypeError("pattern must be str or None")
|
|
215
239
|
if not isinstance(scale, (int, float)) or isinstance(scale, bool):
|
|
@@ -223,9 +247,9 @@ class AnimationSheet:
|
|
|
223
247
|
raise ValueError("pattern is only valid when image is a directory path")
|
|
224
248
|
surface = image
|
|
225
249
|
width, height = surface.get_size()
|
|
226
|
-
cell_w, cell_h = width //
|
|
250
|
+
cell_w, cell_h = width // full_cols, height // full_rows
|
|
227
251
|
if cell_w < 1 or cell_h < 1:
|
|
228
|
-
raise ValueError(f"Sheet {width}x{height} cannot fit a {
|
|
252
|
+
raise ValueError(f"Sheet {width}x{height} cannot fit a {full_rows}x{full_cols} grid")
|
|
229
253
|
self._surface: Surface | None = surface
|
|
230
254
|
self._rows = rows
|
|
231
255
|
self._cols = cols
|
|
@@ -243,6 +267,10 @@ class AnimationSheet:
|
|
|
243
267
|
self._cell_w, self._cell_h = scaled_dummy.get_size()
|
|
244
268
|
self._cache: dict[int, Surface] = {}
|
|
245
269
|
self._frames: list[Surface] | None = None
|
|
270
|
+
self._start_row = start_row
|
|
271
|
+
self._start_col = start_col
|
|
272
|
+
self._sheet_rows = full_rows
|
|
273
|
+
self._sheet_cols = full_cols
|
|
246
274
|
return
|
|
247
275
|
|
|
248
276
|
path = Path(image)
|
|
@@ -250,8 +278,14 @@ class AnimationSheet:
|
|
|
250
278
|
if path.is_dir():
|
|
251
279
|
if rows != 1 or cols != 1:
|
|
252
280
|
raise ValueError(f"rows and cols are ignored in directory mode (got rows={rows}, cols={cols}); use pattern/scale instead")
|
|
281
|
+
if start_row != 0 or start_col != 0 or sheet_rows is not None or sheet_cols is not None:
|
|
282
|
+
raise ValueError("start_row/start_col/sheet_rows/sheet_cols are ignored in directory mode")
|
|
253
283
|
self._scale = scale_f
|
|
254
284
|
self._init_from_directory(path, pattern)
|
|
285
|
+
self._start_row = 0
|
|
286
|
+
self._start_col = 0
|
|
287
|
+
self._sheet_rows = 1
|
|
288
|
+
self._sheet_cols = self._cols
|
|
255
289
|
return
|
|
256
290
|
|
|
257
291
|
if pattern is not None:
|
|
@@ -268,9 +302,9 @@ class AnimationSheet:
|
|
|
268
302
|
pass
|
|
269
303
|
|
|
270
304
|
width, height = surface.get_size()
|
|
271
|
-
cell_w, cell_h = width //
|
|
305
|
+
cell_w, cell_h = width // full_cols, height // full_rows
|
|
272
306
|
if cell_w < 1 or cell_h < 1:
|
|
273
|
-
raise ValueError(f"Sheet {width}x{height} cannot fit a {
|
|
307
|
+
raise ValueError(f"Sheet {width}x{height} cannot fit a {full_rows}x{full_cols} grid")
|
|
274
308
|
|
|
275
309
|
self._surface = surface
|
|
276
310
|
self._rows = rows
|
|
@@ -287,6 +321,10 @@ class AnimationSheet:
|
|
|
287
321
|
self._cell_w, self._cell_h = scaled_dummy.get_size()
|
|
288
322
|
self._cache: dict[int, Surface] = {}
|
|
289
323
|
self._frames = None
|
|
324
|
+
self._start_row = start_row
|
|
325
|
+
self._start_col = start_col
|
|
326
|
+
self._sheet_rows = full_rows
|
|
327
|
+
self._sheet_cols = full_cols
|
|
290
328
|
|
|
291
329
|
def _init_from_directory(self, dir_path: Path, pattern: str | None) -> None:
|
|
292
330
|
files = _collect_image_files(dir_path, pattern)
|
|
@@ -338,13 +376,18 @@ class AnimationSheet:
|
|
|
338
376
|
|
|
339
377
|
@classmethod
|
|
340
378
|
def load(
|
|
341
|
-
cls,
|
|
379
|
+
cls,
|
|
380
|
+
path: PathLike,
|
|
381
|
+
rows: int = 1,
|
|
382
|
+
cols: int = 1,
|
|
383
|
+
pattern: str | None = None,
|
|
384
|
+
scale: float = 1.0,
|
|
385
|
+
start_row: int = 0,
|
|
386
|
+
start_col: int = 0,
|
|
387
|
+
sheet_rows: int | None = None,
|
|
388
|
+
sheet_cols: int | None = None,
|
|
342
389
|
) -> "AnimationSheet":
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
See :class:`AnimationSheet` for ``pattern`` and ``scale`` semantics.
|
|
346
|
-
"""
|
|
347
|
-
return cls(path, rows, cols, pattern=pattern, scale=scale)
|
|
390
|
+
return cls(path, rows, cols, pattern=pattern, scale=scale, start_row=start_row, start_col=start_col, sheet_rows=sheet_rows, sheet_cols=sheet_cols)
|
|
348
391
|
|
|
349
392
|
@classmethod
|
|
350
393
|
def from_directory(cls, directory: PathLike, pattern: str | None = None, scale: float = 1.0) -> "AnimationSheet":
|
|
@@ -377,21 +420,35 @@ class AnimationSheet:
|
|
|
377
420
|
def scale(self) -> float:
|
|
378
421
|
return self._scale
|
|
379
422
|
|
|
423
|
+
@property
|
|
424
|
+
def start_row(self) -> int:
|
|
425
|
+
return self._start_row
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def start_col(self) -> int:
|
|
429
|
+
return self._start_col
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def sheet_rows(self) -> int:
|
|
433
|
+
return self._sheet_rows
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def sheet_cols(self) -> int:
|
|
437
|
+
return self._sheet_cols
|
|
438
|
+
|
|
380
439
|
def get_frame(self, index: int) -> Surface:
|
|
381
440
|
if index < 0 or index >= self.frame_count:
|
|
382
441
|
raise IndexError(f"Frame index {index} out of range for {self.frame_count} frames")
|
|
383
442
|
if self._frames is not None:
|
|
384
|
-
# Directory mode: frames already loaded and cached (scaled if needed).
|
|
385
443
|
return self._cache[index]
|
|
386
444
|
cached = self._cache.get(index)
|
|
387
445
|
if cached is None:
|
|
388
|
-
col = index % self._cols
|
|
389
|
-
row = index // self._cols
|
|
446
|
+
col = self._start_col + (index % self._cols)
|
|
447
|
+
row = self._start_row + (index // self._cols)
|
|
390
448
|
if self._scale == 1.0:
|
|
391
449
|
src = Rect(col * self._cell_w, row * self._cell_h, self._cell_w, self._cell_h)
|
|
392
450
|
cached = self._surface.subsurface(src).copy() # type: ignore[union-attr]
|
|
393
451
|
else:
|
|
394
|
-
# Use stored original cell size to avoid rounding drift.
|
|
395
452
|
src = Rect(col * self._orig_cell_w, row * self._orig_cell_h, self._orig_cell_w, self._orig_cell_h)
|
|
396
453
|
tmp = self._surface.subsurface(src).copy() # type: ignore[union-attr]
|
|
397
454
|
cached = _scale_surface(tmp, self._scale)
|
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
A complete, generic, and optimized inventory system with:
|
|
5
|
-
- Data-driven item definitions
|
|
6
|
-
- Grid-based slot management
|
|
7
|
-
- Drag-and-drop interactions
|
|
8
|
-
- Tooltips and visual feedback
|
|
9
|
-
- Asset caching for performance
|
|
10
|
-
- Event-driven architecture
|
|
11
|
-
- Save/load serialization support
|
|
12
|
-
|
|
13
|
-
Modules:
|
|
14
|
-
data: Type definitions and data structures
|
|
15
|
-
core: Inventory logic and state management
|
|
16
|
-
assets: Asset caching and procedural generation
|
|
17
|
-
ui: UI rendering and user interaction
|
|
2
|
+
Grid inventory: data definitions, slot logic, icon assets, widgets.
|
|
18
3
|
|
|
19
4
|
Usage:
|
|
20
5
|
from pygkit.inventory import Inventory, InventoryWidget, ItemData
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Provides optimized caching of static UI elements and procedural
|
|
5
|
-
generation of item icons to avoid runtime overhead.
|
|
2
|
+
Slot surface cache and item icon generation.
|
|
6
3
|
"""
|
|
7
4
|
|
|
8
5
|
from __future__ import annotations
|
|
@@ -19,8 +19,7 @@ from .data import (
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class Inventory:
|
|
22
|
-
"""
|
|
23
|
-
Generic inventory container with full item management capabilities.
|
|
22
|
+
"""Grid slot container with stacking, weight, events, serialization.
|
|
24
23
|
|
|
25
24
|
Features:
|
|
26
25
|
- Grid-based slot system with configurable size
|
|
@@ -14,8 +14,7 @@ from pygame.typing import ColorLike
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class ItemData(TypedDict, total=False):
|
|
17
|
-
"""
|
|
18
|
-
Flexible item definition with required core fields and optional metadata.
|
|
17
|
+
"""Item definition with required core fields and optional metadata.
|
|
19
18
|
|
|
20
19
|
Required:
|
|
21
20
|
id: Unique string identifier (e.g., "potion_health_small")
|
|
@@ -18,7 +18,7 @@ the scene) with game-agnostic lights. Straight imports, no game wiring::
|
|
|
18
18
|
lightmap.apply(scene, blend=RGBA_MULT)
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
from
|
|
21
|
+
from .blend import (
|
|
22
22
|
LIGHT_ADD,
|
|
23
23
|
RGB_ADD,
|
|
24
24
|
RGB_MAX,
|
|
@@ -32,9 +32,9 @@ from pygkit.lighting.blend import (
|
|
|
32
32
|
RGBA_SUB,
|
|
33
33
|
SCENE_MULT,
|
|
34
34
|
)
|
|
35
|
-
from
|
|
36
|
-
from
|
|
37
|
-
from
|
|
35
|
+
from .lightmap import LightMap
|
|
36
|
+
from .lights import DynamicLight, PointLight, Spotlight
|
|
37
|
+
from .sprites import (
|
|
38
38
|
clear_sprite_cache,
|
|
39
39
|
dynamic_glow_sprite,
|
|
40
40
|
glow_sprite,
|
|
@@ -17,9 +17,9 @@ import random
|
|
|
17
17
|
import pygame
|
|
18
18
|
from pygame import Surface
|
|
19
19
|
|
|
20
|
-
from
|
|
21
|
-
from
|
|
22
|
-
from
|
|
20
|
+
from .blend import LIGHT_ADD
|
|
21
|
+
from .lightmap import LightMap
|
|
22
|
+
from .sprites import dynamic_glow_sprite, glow_sprite, spotlight_sprite
|
|
23
23
|
|
|
24
24
|
__all__ = ["PointLight", "Spotlight", "DynamicLight"]
|
|
25
25
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import Any, Protocol, runtime_checkable
|
|
2
|
+
|
|
3
|
+
from pygame import Surface
|
|
4
|
+
|
|
5
|
+
from .signals import Signal
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Renderable(Protocol):
|
|
9
|
+
def render(self, screen: Surface, pos_offset: tuple[int, int] = (0, 0)): ...
|
|
10
|
+
|
|
11
|
+
def update(self): ...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UIElement(Protocol):
|
|
15
|
+
def render(self, screen: Surface): ...
|
|
16
|
+
|
|
17
|
+
def update(self): ...
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def size(self) -> tuple[int, int]: ...
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def pos(self) -> tuple[int, int]: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Drawable(Protocol):
|
|
27
|
+
def draw(self, surface: Surface): ...
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Updateable(Protocol):
|
|
31
|
+
def update(self): ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@runtime_checkable
|
|
35
|
+
class Widget(Renderable, Protocol):
|
|
36
|
+
@property
|
|
37
|
+
def size(self) -> tuple[int, int]: ...
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Clickable(Widget, Protocol):
|
|
41
|
+
# contains/press/activate take screen coords; widgets track origin at render.
|
|
42
|
+
|
|
43
|
+
on_press: Signal[Any]
|
|
44
|
+
|
|
45
|
+
def contains(self, pos: tuple[int, int]) -> bool: ...
|
|
46
|
+
|
|
47
|
+
def press(self, pos: tuple[int, int]) -> bool: ...
|
|
48
|
+
|
|
49
|
+
def activate(self, pos: tuple[int, int]) -> bool: ...
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
from .anchors import ANCHORS, anchor_point, column, grid, place, row
|
|
1
2
|
from .base import UIBase, UIOptions, BoxModel, BoxModelResult, generate_box_model
|
|
3
|
+
from .button import Button, ButtonStyle
|
|
2
4
|
from .container import Container
|
|
3
5
|
from .cooldown import CooldownOverlay
|
|
4
6
|
from .dialog import DialogBox, DialogConfig, DialogColors, DialogLine, TypewriterSpeed
|
|
7
|
+
from .menubar import Menubar
|
|
5
8
|
from .progressbar import ProgressBarUI
|
|
6
9
|
|
|
7
10
|
__all__ = [
|
|
@@ -18,4 +21,13 @@ __all__ = [
|
|
|
18
21
|
"DialogColors",
|
|
19
22
|
"DialogLine",
|
|
20
23
|
"TypewriterSpeed",
|
|
24
|
+
"Button",
|
|
25
|
+
"ButtonStyle",
|
|
26
|
+
"Menubar",
|
|
27
|
+
"ANCHORS",
|
|
28
|
+
"anchor_point",
|
|
29
|
+
"place",
|
|
30
|
+
"row",
|
|
31
|
+
"column",
|
|
32
|
+
"grid",
|
|
21
33
|
]
|