batframework 1.0.6__py3-none-any.whl → 1.0.8a1__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.
Files changed (59) hide show
  1. batFramework/__init__.py +23 -14
  2. batFramework/action.py +95 -106
  3. batFramework/actionContainer.py +11 -8
  4. batFramework/animatedSprite.py +60 -43
  5. batFramework/audioManager.py +52 -22
  6. batFramework/camera.py +87 -72
  7. batFramework/constants.py +19 -41
  8. batFramework/cutscene.py +12 -11
  9. batFramework/cutsceneBlocks.py +12 -14
  10. batFramework/dynamicEntity.py +5 -4
  11. batFramework/easingController.py +58 -0
  12. batFramework/entity.py +37 -130
  13. batFramework/enums.py +93 -3
  14. batFramework/fontManager.py +15 -7
  15. batFramework/gui/__init__.py +5 -1
  16. batFramework/gui/button.py +6 -144
  17. batFramework/gui/clickableWidget.py +206 -0
  18. batFramework/gui/constraints/__init__.py +1 -0
  19. batFramework/gui/constraints/constraints.py +378 -0
  20. batFramework/gui/container.py +147 -34
  21. batFramework/gui/debugger.py +39 -22
  22. batFramework/gui/dialogueBox.py +69 -43
  23. batFramework/gui/draggableWidget.py +38 -0
  24. batFramework/gui/image.py +33 -28
  25. batFramework/gui/indicator.py +30 -16
  26. batFramework/gui/interactiveWidget.py +72 -20
  27. batFramework/gui/label.py +240 -98
  28. batFramework/gui/layout.py +125 -53
  29. batFramework/gui/meter.py +76 -0
  30. batFramework/gui/radioButton.py +62 -0
  31. batFramework/gui/root.py +72 -48
  32. batFramework/gui/shape.py +257 -49
  33. batFramework/gui/slider.py +217 -2
  34. batFramework/gui/textInput.py +106 -60
  35. batFramework/gui/toggle.py +85 -42
  36. batFramework/gui/widget.py +259 -288
  37. batFramework/manager.py +30 -16
  38. batFramework/object.py +115 -0
  39. batFramework/{particles.py → particle.py} +37 -34
  40. batFramework/renderGroup.py +62 -0
  41. batFramework/resourceManager.py +36 -24
  42. batFramework/scene.py +94 -88
  43. batFramework/sceneManager.py +140 -57
  44. batFramework/scrollingSprite.py +113 -0
  45. batFramework/sprite.py +35 -23
  46. batFramework/tileset.py +34 -52
  47. batFramework/time.py +105 -56
  48. batFramework/transition.py +213 -1
  49. batFramework/utils.py +38 -18
  50. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/METADATA +1 -1
  51. batframework-1.0.8a1.dist-info/RECORD +56 -0
  52. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/WHEEL +1 -1
  53. batFramework/easing.py +0 -76
  54. batFramework/gui/constraints.py +0 -277
  55. batFramework/gui/frame.py +0 -25
  56. batFramework/transitionManager.py +0 -0
  57. batframework-1.0.6.dist-info/RECORD +0 -50
  58. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/LICENCE +0 -0
  59. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/top_level.txt +0 -0
@@ -1,73 +1,125 @@
1
1
  from .widget import Widget
2
2
  from typing import Self
3
+ from typing import TYPE_CHECKING
4
+ import pygame
5
+ from math import cos
6
+
7
+ if TYPE_CHECKING:
8
+ from .container import Container
9
+ import batFramework as bf
10
+
3
11
 
4
12
  class InteractiveWidget(Widget):
5
- def __init__(self, *args, **kwargs)->None:
6
- self.focusable = True
13
+ def __init__(self, *args, **kwargs) -> None:
7
14
  self.is_focused: bool = False
8
15
  self.is_hovered: bool = False
9
- self.is_clicked_down:bool = False
10
- super().__init__(convert_alpha=True)
16
+ self.is_clicked_down: bool = False
17
+ self.focused_index = 0
18
+ self.focusable = True
19
+ super().__init__(*args, **kwargs)
11
20
 
12
- def set_focusable(self,value:bool)->Self:
21
+ def set_focusable(self, value: bool) -> Self:
13
22
  self.focusable = value
14
23
  return self
15
24
 
25
+ def allow_focus_to_self(self) -> bool:
26
+ return True
27
+
16
28
  def get_focus(self) -> bool:
17
- if self.focusable and (r:=self.get_root()) is not None:
29
+ if self.focusable and ((r := self.get_root()) is not None):
18
30
  r.focus_on(self)
31
+ if self.parent and isinstance(self.parent, InteractiveWidget):
32
+ self.parent.set_focused_child(self)
19
33
  return True
20
34
  return False
21
35
 
22
36
  def lose_focus(self) -> bool:
23
- if self.is_focused and (r:=self.get_root()) is not None:
37
+ if self.is_focused and ((r := self.get_root()) is not None):
24
38
  r.focus_on(None)
25
39
  return True
26
40
  return False
27
41
 
28
-
29
42
  def on_get_focus(self) -> None:
30
43
  self.is_focused = True
31
44
  self.do_on_get_focus()
32
45
 
33
-
34
46
  def on_lose_focus(self) -> None:
35
47
  self.is_focused = False
36
48
  self.do_on_lose_focus()
37
49
 
50
+ def on_key_down(self,key):
51
+ if key == pygame.K_DOWN:
52
+ self.focus_next_sibling()
53
+ elif key == pygame.K_UP:
54
+ self.focus_prev_sibling()
55
+ else:
56
+ self.do_on_key_down(key)
57
+
58
+ def on_key_up(self, key):
59
+ self.do_on_key_up(key)
60
+
61
+ def do_on_key_down(self, key):
62
+ pass
63
+
64
+ def do_on_key_up(self, key):
65
+ pass
38
66
 
39
- def do_on_get_focus(self)->None:
67
+ def do_on_get_focus(self) -> None:
40
68
  pass
41
-
42
- def do_on_lose_focus(self)->None:
69
+
70
+ def do_on_lose_focus(self) -> None:
43
71
  pass
44
72
 
73
+ def focus_next_sibling(self) -> None:
74
+ if isinstance(self.parent, bf.Container):
75
+ self.parent.focus_next_child()
45
76
 
46
- def on_click_down(self,button:int)->None:
77
+ def focus_prev_sibling(self) -> None:
78
+ if isinstance(self.parent, bf.Container):
79
+ self.parent.focus_prev_child()
80
+
81
+ def on_click_down(self, button: int) -> None:
82
+ self.is_clicked_down = True
47
83
  self.do_on_click_down(button)
48
84
 
49
- def on_click_up(self,button:int)->None:
85
+ def on_click_up(self, button: int) -> None:
86
+ self.is_clicked_down = False
50
87
  self.do_on_click_up(button)
51
88
 
52
- def do_on_click_down(self,button:int)->None:
89
+ def do_on_click_down(self, button: int) -> None:
53
90
  pass
54
91
 
55
- def do_on_click_up(self,button:int)->None:
92
+ def do_on_click_up(self, button: int) -> None:
56
93
  pass
57
94
 
58
- def on_enter(self)->None:
95
+ def on_enter(self) -> None:
59
96
  self.is_hovered = True
60
97
  self.do_on_enter()
61
98
 
62
- def on_exit(self)->None:
99
+ def on_exit(self) -> None:
63
100
  self.is_hovered = False
101
+ self.is_clicked_down = False
64
102
  self.do_on_exit()
65
103
 
104
+ def do_on_enter(self) -> None:
105
+ pass
66
106
 
67
- def do_on_enter(self)->None:
107
+ def do_on_exit(self) -> None:
68
108
  pass
69
109
 
70
- def do_on_exit(self)->None:
110
+ def on_mouse_motion(self, x, y) -> None:
111
+ self.do_on_mouse_motion(x,y)
112
+
113
+ def do_on_mouse_motion(self,x,y)->None:
71
114
  pass
72
115
 
116
+ def set_focused_child(self, child: "InteractiveWidget"):
117
+ pass
73
118
 
119
+ def draw_focused(self, camera: bf.Camera) -> None:
120
+ delta = 4 + ((2*cos(pygame.time.get_ticks()/100)) //2) * 2
121
+ pygame.draw.rect(
122
+ camera.surface,
123
+ "white",
124
+ self.rect.move(-camera.rect.x,-camera.rect.y).inflate(delta,delta),1
125
+ )
batFramework/gui/label.py CHANGED
@@ -3,171 +3,313 @@ import pygame
3
3
  from .shape import Shape
4
4
  from typing import Self
5
5
 
6
+
6
7
  class Label(Shape):
7
8
  _text_cache = {}
9
+
8
10
  def __init__(self, text: str) -> None:
9
- self._text = ""
11
+ self.text = ""
10
12
 
11
- self._resized_flag: bool = False
13
+ self.resized_flag: bool = False
12
14
 
13
15
  # Enable/Disable antialiasing
14
- self._antialias: bool = True
16
+ self.antialias: bool = bf.FontManager().DEFAULT_ANTIALIAS
15
17
 
16
- self._text_size = bf.FontManager().DEFAULT_TEXT_SIZE
18
+ self.text_size = bf.FontManager().DEFAULT_TEXT_SIZE
17
19
 
18
20
  self.auto_wraplength: bool = False
19
21
 
20
- self._alignment: bf.Alignment = bf.Alignment.CENTER
22
+ self.alignment: bf.alignment = bf.alignment.CENTER
23
+
24
+ self.text_color: tuple[int, int, int] | str = "black"
25
+
26
+ self.text_outline_color: tuple[int, int, int] | str = "gray50"
27
+
28
+ self.text_outline_surface: pygame.Surface = None
29
+
30
+ self._text_outline_mask = pygame.Mask((3, 3), fill=True)
21
31
 
22
- self._text_color: tuple[int, int, int] | str = "black"
23
32
  # font name (given when loaded by utils) to use for the text
24
- self._font_name = None
33
+ self.font_name = None
25
34
  # reference to the font object
26
- self._font_object = None
35
+ self.font_object = None
27
36
  # Rect containing the text of the label
28
- self._text_rect = None
37
+ self.text_rect = pygame.FRect(0,0,0,0)
29
38
  # text surface (result of font.render)
30
- self._text_surface: pygame.Surface | None = None
39
+ self.text_surface: pygame.Surface = pygame.Surface((0, 0))
40
+ self.do_caching: bool = False
41
+
42
+ self.show_text_outline: bool = False
43
+
44
+ self.is_italic: bool = False
31
45
 
32
- self._do_caching : bool = False
33
- super().__init__(width=0, height=0)
46
+ self.is_bold: bool = False
47
+
48
+ self.is_underlined: bool = False
49
+
50
+ super().__init__((0, 0))
34
51
  self.set_padding((10, 4))
35
52
  self.set_debug_color("blue")
36
- self.set_color(bf.color.CLOUD_WHITE)
53
+ self.set_color("gray50")
37
54
  self.set_autoresize(True)
38
55
  self.set_font(force=True)
39
56
  self.set_text(text)
40
57
 
41
-
42
58
  @staticmethod
43
59
  def clear_cache():
44
60
  Label._text_cache = {}
45
-
46
- def enable_caching(self)->Self:
47
- self._do_caching = True
61
+
62
+ def enable_caching(self) -> Self:
63
+ self.do_caching = True
48
64
  return self
49
65
 
50
- def disable_caching(self)->Self:
51
- self._do_caching = False
66
+ def disable_caching(self) -> Self:
67
+ self.do_caching = False
52
68
  return self
53
69
 
54
70
  def set_text_color(self, color) -> Self:
55
- self._text_color = color
56
- self.build()
71
+ self.text_color = color
72
+ self.dirty_surface = True
73
+ return self
74
+
75
+ def set_italic(self, value: bool) -> Self:
76
+ if value == self.is_italic:
77
+ return self
78
+ self.is_italic = value
79
+ if self.autoresize_h or self.autoresize_w:
80
+ self.dirty_shape = True
81
+ else:
82
+ self.dirty_surface = True
57
83
  return self
58
84
 
59
- def to_string_id(self) -> str:
60
- return f"Label({self._text})"
85
+ def set_bold(self, value: bool) -> Self:
86
+ if value == self.is_bold:
87
+ return self
88
+ self.is_bold = value
89
+ if self.autoresize_h or self.autoresize_w:
90
+ self.dirty_shape = True
91
+ else:
92
+ self.dirty_surface = True
93
+ return self
94
+
95
+ def set_underlined(self, value: bool) -> Self:
96
+ if value == self.is_underlined:
97
+ return self
98
+ self.is_underlined = value
99
+ self.dirty_surface = True
100
+ return self
101
+
102
+ def set_text_outline_matrix(self, matrix: list[list[0 | 1]]) -> Self:
103
+ if matrix is None:
104
+ matrix = [[0 for _ in range(3)] for _ in range(3)]
105
+ for y in range(3):
106
+ for x in range(3):
107
+ self._text_outline_mask.set_at((x, y), matrix[2 - y][2 - x])
108
+ self.dirty_surface = True
109
+ return self
61
110
 
62
- def set_alignment(self, alignment: bf.Alignment) -> "Label":
63
- self._alignment = alignment
64
- self.build()
111
+ def set_text_outline_color(self, color) -> Self:
112
+ self.text_outline_color = color
113
+ self.dirty_surface = True
114
+ return self
115
+
116
+ def enable_text_outline(self) -> Self:
117
+ self.show_text_outline = True
118
+ self.dirty_surface = True
119
+ return self
120
+
121
+ def disable_text_outline(self) -> Self:
122
+ self.show_text_outline = False
123
+ self.dirty_surface = True
124
+ return self
125
+
126
+ def __str__(self) -> str:
127
+ return f"Label({self.text})"
128
+
129
+ def set_alignment(self, alignment: bf.alignment) -> "Label":
130
+ self.alignment = alignment
131
+ self.dirty_surface = True
65
132
  return self
66
133
 
67
134
  def set_auto_wraplength(self, val: bool) -> "Label":
68
135
  self.auto_wraplength = val
69
- self.build()
136
+ if self.autoresize_h or self.autoresize_w:
137
+ self.dirty_shape = True
138
+ else:
139
+ self.dirty_surface = True
70
140
  return self
71
141
 
72
- def get_bounding_box(self):
73
- yield from super().get_bounding_box()
74
- if self._text_rect:
75
- yield self._text_rect.move(*self.rect.topleft)
76
-
77
- def set_font(self, font_name: str = None, force: bool = False) -> "Label":
78
- if font_name == self._font_name and not force:
142
+ def get_debug_outlines(self):
143
+ yield from super().get_debug_outlines()
144
+ yield (self.text_rect.move(*self.rect.topleft),"purple")
145
+
146
+ def set_font(self, font_name: str = None, force: bool = False) -> Self:
147
+ if font_name == self.font_name and not force:
79
148
  return self
80
- self._font_name = font_name
81
- self._font_object = bf.FontManager().get_font(self._font_name, self._text_size)
82
- self.build()
149
+ self.font_name = font_name
150
+ self.font_object = bf.FontManager().get_font(self.font_name, self.text_size)
151
+ if self.autoresize_h or self.autoresize_w:
152
+ self.dirty_shape = True
153
+ else:
154
+ self.dirty_surface = True
83
155
  return self
84
156
 
85
- def set_text_size(self, text_size: int) -> "Label":
157
+ def set_text_size(self, text_size: int) -> Self:
86
158
  text_size = round(text_size / 2) * 2
87
- if text_size == self._text_size:
159
+ if text_size == self.text_size:
88
160
  return self
89
- self._text_size = text_size
90
- self._font_object = bf.FontManager().get_font(self._font_name, self._text_size)
91
- self.build()
161
+ self.text_size = text_size
162
+ self.font_object = bf.FontManager().get_font(self.font_name, self.text_size)
163
+ if self.autoresize_h or self.autoresize_w:
164
+ self.dirty_shape = True
92
165
  return self
93
166
 
94
167
  def get_text_size(self) -> int:
95
- return self._text_size
168
+ return self.text_size
96
169
 
97
170
  def is_antialias(self) -> bool:
98
- return self._antialias
171
+ return self.antialias
99
172
 
100
- def set_antialias(self, value: bool) -> "Label":
101
- self._antialias = value
102
- self.build()
173
+ def set_antialias(self, value: bool) -> Self:
174
+ self.antialias = value
175
+ self.dirty_surface = True
103
176
  return self
104
177
 
105
- def set_text(self, text: str) -> "Label":
106
- if text == self._text:
178
+ def set_text(self, text: str) -> Self:
179
+ if text == self.text:
107
180
  return self
108
- self._text = text
109
- self.build()
181
+ self.text = text
182
+ self.dirty_shape = True
110
183
  return self
111
184
 
185
+ def get_min_required_size(self)->tuple[float,float]:
186
+ if not (self.autoresize_w or self.autoresize_h) :
187
+ return self.rect.size
188
+ if not self.text_rect:
189
+ params = {
190
+ "font_name": self.font_object.name,
191
+ "text": self.text,
192
+ "antialias": False,
193
+ "color": "white",
194
+ "bgcolor": "black", # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
195
+ "wraplength": int(self.get_padded_width()) if self.auto_wraplength else 0,
196
+ }
197
+
198
+ self.text_rect.size = self._render_font(params).get_size()
199
+ res = self.inflate_rect_by_padding((0,0,*self.text_rect.size)).size
200
+ # return res
201
+ return res[0] if self.autoresize_w else self.rect.w, res[1] if self.autoresize_h else self.rect.h
202
+
112
203
  def get_text(self) -> str:
113
- return self._text
204
+ return self.text
205
+
206
+ def _render_font(self, params: dict) -> pygame.Surface:
207
+ key = tuple(params.values())
114
208
 
209
+ cached_value = Label._text_cache.get(key, None)
115
210
 
116
- def get_min_required_size(self)->tuple[float,float]:
117
- return (0,0) if not self._font_object else self.inflate_rect_by_padding(pygame.FRect(0,0,*self._font_object.size(self._text))).size
211
+ if self.draw_mode == bf.drawMode.SOLID:
212
+ if cached_value is None:
213
+ params.pop("font_name")
118
214
 
119
- def _build_text(self) -> None:
120
- if self._font_object is None:
121
- print(f"No font for '{self.to_string_id()}' :(")
122
- return
123
- # render(text, antialias, color, bgcolor=None, wraplength=0) -> Surface
215
+ #save old settings
216
+ old_italic = self.font_object.get_italic()
217
+ old_bold = self.font_object.get_bold()
218
+ old_underline = self.font_object.get_underline()
124
219
 
125
- params = {
126
- "font_name":self._font_object.name,
127
- "text":self._text,
128
- "antialias":self._antialias,
129
- "color":self._text_color,
130
- "bgcolor":self._color,
131
- "wraplength":int(self.get_content_width()) if self.auto_wraplength else 0
132
- }
133
-
134
- key = tuple(params.values())
135
- cached_value = Label._text_cache.get(key,None)
136
- if cached_value != None:
137
- self._text_surface = cached_value
220
+ #setup font
221
+ self.font_object.set_italic(self.is_italic)
222
+ self.font_object.set_bold(self.is_bold)
223
+ self.font_object.set_underline(self.is_underlined)
224
+
225
+ surf = self.font_object.render(**params)
226
+
227
+ # reset font
228
+ self.font_object.set_italic(old_italic)
229
+ self.font_object.set_bold(old_bold)
230
+ self.font_object.set_underline(old_underline)
231
+
232
+ if self.do_caching:
233
+ Label._text_cache[key] = surf
234
+ else:
235
+ surf = cached_value
138
236
  else:
139
237
  params.pop("font_name")
140
- self._text_surface = self._font_object.render(**params)
141
- if self._do_caching:
142
- Label._text_cache[key] = self._text_surface
143
- self._text_rect = self._text_surface.get_frect(topleft = self.get_content_rect_rel().topleft)
238
+ surf = self.font_object.render(**params)
239
+
240
+ return surf
144
241
 
145
242
  def _build_layout(self) -> None:
146
- if self._text_rect is None : return
147
- if self.autoresize:
148
- target_rect = self.inflate_rect_by_padding(self._text_rect)
243
+
244
+ params = {
245
+ "font_name": self.font_object.name,
246
+ "text": self.text,
247
+ "antialias": False,
248
+ "color": "white",
249
+ "bgcolor": "black", # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
250
+ "wraplength": int(self.get_padded_width()) if self.auto_wraplength else 0,
251
+ }
252
+
253
+ self.text_rect.size = self._render_font(params).get_size()
254
+ if self.autoresize_h or self.autoresize_w:
255
+ target_rect = self.inflate_rect_by_padding((0,0,*self.text_rect.size))
256
+ if not self.autoresize_w : target_rect.w = self.rect.w
257
+ if not self.autoresize_h : target_rect.h = self.rect.h
149
258
  if self.rect.size != target_rect.size:
150
- self._resized_flag = True
151
- self.set_size(* target_rect.size)
259
+ self.set_size(target_rect.size)
260
+ self.build()
152
261
  return
153
- if self._alignment == bf.Alignment.CENTER:
154
- self._text_rect.center = self.get_content_rect_rel().center
155
- elif self._alignment == bf.Alignment.LEFT:
156
- self._text_rect.topleft = self.get_content_rect_rel().topleft
157
- elif self._alignment == bf.Alignment.RIGHT:
158
- self._text_rect.topright = self.get_content_rect_rel().topright
159
- if self._resized_flag:
160
- self._resized_flag = False
161
- if self.parent:
162
- # print("Children modified call")
163
- self.parent.children_modified()
164
-
165
- self.surface.fblits([(self._text_surface, self._text_rect)])
262
+ padded = self.get_padded_rect().move(-self.rect.x,-self.rect.y)
263
+ self.align_text(self.text_rect,padded,self.alignment)
264
+
265
+ def _paint_text(self) -> None:
266
+ if self.font_object is None:
267
+ print(f"No font for widget with text : '{self}' :(")
268
+ return
269
+
270
+ params = {
271
+ "font_name": self.font_object.name,
272
+ "text": self.text,
273
+ "antialias": self.antialias,
274
+ "color": self.text_color,
275
+ "bgcolor": None, # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
276
+ "wraplength": int(self.get_padded_width()) if self.auto_wraplength else 0,
277
+ }
278
+ self.text_surface = self._render_font(params)
279
+
280
+ if self.show_text_outline:
281
+ self.text_outline_surface = (
282
+ pygame.mask.from_surface(self.text_surface)
283
+ .convolve(self._text_outline_mask)
284
+ .to_surface(setcolor=self.text_outline_color, unsetcolor=(0, 0, 0, 0))
285
+ )
286
+
287
+
288
+ l = []
289
+ if self.show_text_outline:
290
+ l.append(
291
+ (
292
+ self.text_outline_surface,
293
+ self.text_rect.move(-1, self.relief - self.get_relief() - 1),
294
+ )
295
+ )
296
+ l.append(
297
+ (self.text_surface, self.text_rect.move(0, self.relief - self.get_relief()))
298
+ )
299
+ self.surface.fblits(l)
300
+
301
+ def align_text(self,text_rect:pygame.FRect,area:pygame.FRect,alignment: bf.alignment):
302
+ if alignment == bf.alignment.LEFT : alignment = bf.alignment.MIDLEFT
303
+ elif alignment == bf.alignment.MIDRIGHT : alignment = bf.alignment.MIDRIGHT
304
+
305
+ pos = area.__getattribute__(alignment.value)
306
+ text_rect.__setattr__(alignment.value,pos)
166
307
 
167
308
  def build(self) -> None:
168
309
  super().build()
169
- if not self._font_object:
170
- return
171
- self._build_text()
172
310
  self._build_layout()
173
- self.apply_constraints()
311
+
312
+ def paint(self)->None:
313
+ super().paint()
314
+ if self.font_object:
315
+ self._paint_text()