pygkit 1.0.1__tar.gz → 1.0.2__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.
Files changed (41) hide show
  1. {pygkit-1.0.1 → pygkit-1.0.2}/PKG-INFO +1 -1
  2. {pygkit-1.0.1 → pygkit-1.0.2}/pyproject.toml +1 -1
  3. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/__init__.py +5 -0
  4. pygkit-1.0.2/src/pygkit/inventory/__init__.py +86 -0
  5. pygkit-1.0.2/src/pygkit/inventory/assets.py +307 -0
  6. pygkit-1.0.2/src/pygkit/inventory/core.py +599 -0
  7. pygkit-1.0.2/src/pygkit/inventory/data.py +273 -0
  8. pygkit-1.0.2/src/pygkit/inventory/ui.py +694 -0
  9. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/ui/__init__.py +6 -0
  10. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/ui/base.py +1 -0
  11. pygkit-1.0.2/src/pygkit/ui/dialog.py +491 -0
  12. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit.egg-info/PKG-INFO +1 -1
  13. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit.egg-info/SOURCES.txt +8 -0
  14. pygkit-1.0.2/tests/test_dialog.py +313 -0
  15. pygkit-1.0.2/tests/test_inventory.py +730 -0
  16. {pygkit-1.0.1 → pygkit-1.0.2}/LICENSE +0 -0
  17. {pygkit-1.0.1 → pygkit-1.0.2}/README.md +0 -0
  18. {pygkit-1.0.1 → pygkit-1.0.2}/setup.cfg +0 -0
  19. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/audio/__init__.py +0 -0
  20. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/audio/soundmanager.py +0 -0
  21. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/protocols.py +0 -0
  22. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/transitions/__init__.py +0 -0
  23. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/transitions/base.py +0 -0
  24. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/transitions/effects.py +0 -0
  25. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/transitions/fades.py +0 -0
  26. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/transitions/wipes.py +0 -0
  27. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/ui/container.py +0 -0
  28. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/ui/cooldown.py +0 -0
  29. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/ui/progressbar.py +0 -0
  30. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/utils/__init__.py +0 -0
  31. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/utils/interpolation.py +0 -0
  32. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/utils/text.py +0 -0
  33. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit/utils/timer.py +0 -0
  34. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit.egg-info/dependency_links.txt +0 -0
  35. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit.egg-info/requires.txt +0 -0
  36. {pygkit-1.0.1 → pygkit-1.0.2}/src/pygkit.egg-info/top_level.txt +0 -0
  37. {pygkit-1.0.1 → pygkit-1.0.2}/tests/test_box_model.py +0 -0
  38. {pygkit-1.0.1 → pygkit-1.0.2}/tests/test_interpolation.py +0 -0
  39. {pygkit-1.0.1 → pygkit-1.0.2}/tests/test_text.py +0 -0
  40. {pygkit-1.0.1 → pygkit-1.0.2}/tests/test_timer.py +0 -0
  41. {pygkit-1.0.1 → pygkit-1.0.2}/tests/test_transitions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygkit
3
- Version: 1.0.1
3
+ Version: 1.0.2
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pygkit"
7
- version = "1.0.1"
7
+ version = "1.0.2"
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"
@@ -25,6 +25,11 @@ __all__ = [
25
25
  "BoxModel",
26
26
  "BoxModelResult",
27
27
  "generate_box_model",
28
+ "DialogBox",
29
+ "DialogConfig",
30
+ "DialogColors",
31
+ "DialogLine",
32
+ "TypewriterSpeed",
28
33
  "SimpleInterpolation",
29
34
  "Timer",
30
35
  "lerp",
@@ -0,0 +1,86 @@
1
+ """
2
+ Pygkit Inventory System - Production-grade inventory management for Pygame.
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
18
+
19
+ Usage:
20
+ from pygkit.inventory import Inventory, InventoryWidget, ItemData
21
+
22
+
23
+ config = {"rows": 4, "cols": 6, "slot_size": 48}
24
+ inventory = Inventory("player_backpack", config)
25
+
26
+
27
+ item: ItemData = {
28
+ "id": "health_potion",
29
+ "name": "Health Potion",
30
+ "description": "Restores 50 HP",
31
+ "stack_size": 99,
32
+ "icon_color": (200, 50, 50, 255),
33
+ "icon_shape": "circle",
34
+ "value": 25,
35
+ "weight": 0.5,
36
+ }
37
+ inventory.add_item(item, quantity=5)
38
+
39
+
40
+ widget = InventoryWidget(inventory, position=(100, 100))
41
+
42
+
43
+ widget.handle_events(pygame.event.get())
44
+ widget.update(dt)
45
+ widget.render(screen)
46
+ """
47
+
48
+ from .assets import (
49
+ AssetCache,
50
+ generate_procedural_icon,
51
+ get_cache,
52
+ render_quantity_text,
53
+ )
54
+ from .core import Inventory
55
+ from .data import (
56
+ DEFAULT_INVENTORY_CONFIG,
57
+ RARITY_COLORS,
58
+ InventoryConfig,
59
+ InventoryEvent,
60
+ ItemData,
61
+ ItemStack,
62
+ )
63
+ from .ui import (
64
+ DragDropState,
65
+ InventoryManager,
66
+ InventoryWidget,
67
+ Tooltip,
68
+ )
69
+
70
+ __all__ = [
71
+ "ItemData",
72
+ "InventoryConfig",
73
+ "ItemStack",
74
+ "InventoryEvent",
75
+ "RARITY_COLORS",
76
+ "DEFAULT_INVENTORY_CONFIG",
77
+ "Inventory",
78
+ "AssetCache",
79
+ "get_cache",
80
+ "generate_procedural_icon",
81
+ "render_quantity_text",
82
+ "Tooltip",
83
+ "DragDropState",
84
+ "InventoryWidget",
85
+ "InventoryManager",
86
+ ]
@@ -0,0 +1,307 @@
1
+ """
2
+ Asset caching and procedural generation for the Inventory System.
3
+
4
+ Provides optimized caching of static UI elements and procedural
5
+ generation of item icons to avoid runtime overhead.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Optional
11
+
12
+ import pygame
13
+ from pygame import SRCALPHA, Surface
14
+ from pygame.typing import ColorLike
15
+
16
+
17
+ class AssetCache:
18
+ """
19
+ Singleton-style cache for static UI assets.
20
+
21
+ Caches procedurally generated surfaces to avoid redundant creation.
22
+ Automatically handles different sizes and colors.
23
+ """
24
+
25
+ _instance: Optional[AssetCache] = None
26
+
27
+ def __new__(cls) -> AssetCache:
28
+ if cls._instance is None:
29
+ cls._instance = super().__new__(cls)
30
+ cls._instance._initialized = False
31
+ return cls._instance
32
+
33
+ def __init__(self) -> None:
34
+ if self._initialized:
35
+ return
36
+ self._initialized = True
37
+
38
+ self._slot_surfaces: dict[tuple[int, int, str], Surface] = {}
39
+ self._icon_surfaces: dict[tuple[str, tuple[int, int], ColorLike], Surface] = {}
40
+ self._background_surfaces: dict[tuple[int, int, int, ColorLike], Surface] = {}
41
+
42
+ def clear(self) -> None:
43
+ """Clear all cached surfaces."""
44
+ self._slot_surfaces.clear()
45
+ self._icon_surfaces.clear()
46
+ self._background_surfaces.clear()
47
+
48
+ def get_slot_surface(
49
+ self,
50
+ size: int | tuple[int, int],
51
+ border_radius: int = 4,
52
+ style: str = "default",
53
+ ) -> Surface:
54
+ """
55
+ Get or create a cached slot background surface.
56
+
57
+ Args:
58
+ size: Slot size (width, height) or single int for square
59
+ border_radius: Corner radius for rounded slots
60
+ style: Style variant ("default", "highlighted", "invalid")
61
+
62
+ Returns:
63
+ Cached surface for the slot
64
+ """
65
+ if isinstance(size, int):
66
+ size = (size, size)
67
+
68
+ key = (*size, style)
69
+
70
+ if key not in self._slot_surfaces:
71
+ self._slot_surfaces[key] = self._create_slot_surface(size, border_radius, style)
72
+
73
+ return self._slot_surfaces[key].copy()
74
+
75
+ def _create_slot_surface(
76
+ self,
77
+ size: tuple[int, int],
78
+ border_radius: int,
79
+ style: str,
80
+ ) -> Surface:
81
+ """Create a new slot surface (internal use)."""
82
+ surf = Surface(size, SRCALPHA)
83
+ surf.fill((0, 0, 0, 0))
84
+
85
+ if style == "default":
86
+ color = (60, 60, 70, 200)
87
+ elif style == "highlighted":
88
+ color = (100, 150, 200, 180)
89
+ elif style == "invalid":
90
+ color = (200, 80, 80, 180)
91
+ else:
92
+ color = (60, 60, 70, 200)
93
+
94
+ w, h = int(size[0]), int(size[1])
95
+ pygame.draw.rect(surf, color, (0, 0, w, h), border_radius=border_radius)
96
+
97
+ inner_rect = pygame.Rect(2, 2, size[0] - 4, size[1] - 4)
98
+ pygame.draw.rect(surf, (80, 80, 90, 150), inner_rect, width=1, border_radius=max(0, border_radius - 1))
99
+
100
+ return surf
101
+
102
+ def get_icon_surface(
103
+ self,
104
+ shape: str,
105
+ size: int | tuple[int, int],
106
+ color: ColorLike,
107
+ ) -> Surface:
108
+ """
109
+ Get or create a cached item icon surface.
110
+
111
+ Args:
112
+ shape: Icon shape ("circle", "square", "diamond", "cross", "star")
113
+ size: Icon size (width, height) or single int for square
114
+ color: RGBA color for the icon
115
+
116
+ Returns:
117
+ Cached surface with the icon rendered
118
+ """
119
+ if isinstance(size, int):
120
+ size = (size, size)
121
+
122
+ color_tuple = tuple(color) if not isinstance(color, tuple) else color
123
+
124
+ key = (shape, size, color_tuple)
125
+
126
+ if key not in self._icon_surfaces:
127
+ self._icon_surfaces[key] = self._create_icon_surface(shape, size, color)
128
+
129
+ return self._icon_surfaces[key].copy()
130
+
131
+ def _create_icon_surface(
132
+ self,
133
+ shape: str,
134
+ size: tuple[int, int],
135
+ color: ColorLike,
136
+ ) -> Surface:
137
+ """Create a new icon surface (internal use)."""
138
+ surf = Surface(size, SRCALPHA)
139
+ surf.fill((0, 0, 0, 0))
140
+
141
+ center_x = size[0] // 2
142
+ center_y = size[1] // 2
143
+ radius = min(size[0], size[1]) // 2 - 4
144
+
145
+ if shape == "circle":
146
+ pygame.draw.circle(surf, color, (center_x, center_y), radius)
147
+
148
+ elif shape == "square":
149
+ rect = pygame.Rect(
150
+ center_x - radius,
151
+ center_y - radius,
152
+ radius * 2,
153
+ radius * 2,
154
+ )
155
+ pygame.draw.rect(surf, color, rect, border_radius=4)
156
+
157
+ elif shape == "diamond":
158
+ points = [
159
+ (center_x, center_y - radius),
160
+ (center_x + radius, center_y),
161
+ (center_x, center_y + radius),
162
+ (center_x - radius, center_y),
163
+ ]
164
+ pygame.draw.polygon(surf, color, points)
165
+
166
+ elif shape == "cross":
167
+ arm_width = radius // 3
168
+
169
+ pygame.draw.rect(surf, color, (center_x - arm_width, center_y - radius, arm_width * 2, radius * 2))
170
+
171
+ pygame.draw.rect(surf, color, (center_x - radius, center_y - arm_width, radius * 2, arm_width * 2))
172
+
173
+ elif shape == "star":
174
+ outer_r = radius
175
+ inner_r = radius // 2
176
+ points = []
177
+ import math
178
+
179
+ for i in range(8):
180
+ r = outer_r if i % 2 == 0 else inner_r
181
+ rad = math.radians(i * 45)
182
+ x = center_x + r * math.cos(rad)
183
+ y = center_y + r * math.sin(rad)
184
+ points.append((x, y))
185
+ pygame.draw.polygon(surf, color, points)
186
+
187
+ else:
188
+ pygame.draw.circle(surf, color, (center_x, center_y), radius)
189
+
190
+ return surf
191
+
192
+ def get_background_surface(
193
+ self,
194
+ width: int,
195
+ height: int,
196
+ border_radius: int,
197
+ color: ColorLike,
198
+ ) -> Surface:
199
+ """
200
+ Get or create a cached background panel surface.
201
+
202
+ Args:
203
+ width: Panel width
204
+ height: Panel height
205
+ border_radius: Corner radius
206
+ color: Background color (RGBA)
207
+
208
+ Returns:
209
+ Cached background surface
210
+ """
211
+ color_tuple = tuple(color) if not isinstance(color, tuple) else color
212
+ key = (width, height, border_radius, color_tuple)
213
+
214
+ if key not in self._background_surfaces:
215
+ self._background_surfaces[key] = self._create_background_surface(width, height, border_radius, color)
216
+
217
+ return self._background_surfaces[key].copy()
218
+
219
+ def _create_background_surface(
220
+ self,
221
+ width: int,
222
+ height: int,
223
+ border_radius: int,
224
+ color: ColorLike,
225
+ ) -> Surface:
226
+ """Create a new background surface (internal use)."""
227
+ surf = Surface((width, height), SRCALPHA)
228
+ surf.fill((0, 0, 0, 0))
229
+
230
+ pygame.draw.rect(surf, color, (0, 0, width, height), border_radius=border_radius)
231
+
232
+ return surf
233
+
234
+
235
+ _cache: Optional[AssetCache] = None
236
+
237
+
238
+ def get_cache() -> AssetCache:
239
+ """Get the global asset cache instance."""
240
+ global _cache
241
+ if _cache is None:
242
+ _cache = AssetCache()
243
+ return _cache
244
+
245
+
246
+ def generate_procedural_icon(
247
+ shape: str,
248
+ size: int = 32,
249
+ color: ColorLike = (200, 200, 200, 255),
250
+ ) -> Surface:
251
+ """
252
+ Generate a procedural item icon.
253
+
254
+ Convenience function that uses the global cache.
255
+
256
+ Args:
257
+ shape: Icon shape ("circle", "square", "diamond", "cross", "star")
258
+ size: Icon size in pixels
259
+ color: RGBA color
260
+
261
+ Returns:
262
+ Surface with the rendered icon
263
+ """
264
+ return get_cache().get_icon_surface(shape, size, color)
265
+
266
+
267
+ def render_quantity_text(
268
+ quantity: int,
269
+ font: pygame.font.Font,
270
+ color: ColorLike = (255, 255, 255, 255),
271
+ outline_color: ColorLike = (0, 0, 0, 255),
272
+ ) -> Surface:
273
+ """
274
+ Render quantity text with outline for readability.
275
+
276
+ Args:
277
+ quantity: Number to display
278
+ font: Font to use
279
+ color: Text color
280
+ outline_color: Outline color
281
+
282
+ Returns:
283
+ Surface with rendered text
284
+ """
285
+ if quantity <= 1:
286
+ return Surface((1, 1), SRCALPHA)
287
+
288
+ text = str(quantity)
289
+
290
+ base_surf = font.render(text, True, color)
291
+
292
+ thickness = 1
293
+ w = base_surf.get_width() + thickness * 2
294
+ h = base_surf.get_height() + thickness * 2
295
+ result = Surface((w, h), SRCALPHA)
296
+ result.fill((0, 0, 0, 0))
297
+
298
+ for dx in range(-thickness, thickness + 1):
299
+ for dy in range(-thickness, thickness + 1):
300
+ if dx == 0 and dy == 0:
301
+ continue
302
+ outline = font.render(text, True, outline_color)
303
+ result.blit(outline, (thickness + dx, thickness + dy))
304
+
305
+ result.blit(base_surf, (thickness, thickness))
306
+
307
+ return result