batframework 1.0.9a7__py3-none-any.whl → 1.0.9a9__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 (62) hide show
  1. batFramework/__init__.py +20 -11
  2. batFramework/action.py +1 -1
  3. batFramework/animatedSprite.py +47 -116
  4. batFramework/animation.py +30 -5
  5. batFramework/audioManager.py +8 -5
  6. batFramework/baseScene.py +240 -0
  7. batFramework/camera.py +4 -0
  8. batFramework/constants.py +6 -2
  9. batFramework/cutscene.py +221 -21
  10. batFramework/cutsceneManager.py +5 -2
  11. batFramework/drawable.py +7 -5
  12. batFramework/easingController.py +10 -11
  13. batFramework/entity.py +21 -2
  14. batFramework/enums.py +48 -33
  15. batFramework/gui/__init__.py +6 -3
  16. batFramework/gui/animatedLabel.py +10 -2
  17. batFramework/gui/button.py +4 -31
  18. batFramework/gui/clickableWidget.py +63 -50
  19. batFramework/gui/constraints/constraints.py +212 -136
  20. batFramework/gui/container.py +77 -58
  21. batFramework/gui/debugger.py +12 -17
  22. batFramework/gui/draggableWidget.py +21 -17
  23. batFramework/gui/image.py +3 -10
  24. batFramework/gui/indicator.py +56 -1
  25. batFramework/gui/interactiveWidget.py +127 -108
  26. batFramework/gui/label.py +73 -64
  27. batFramework/gui/layout.py +286 -445
  28. batFramework/gui/meter.py +42 -20
  29. batFramework/gui/radioButton.py +20 -69
  30. batFramework/gui/root.py +99 -29
  31. batFramework/gui/selector.py +250 -0
  32. batFramework/gui/shape.py +13 -5
  33. batFramework/gui/slider.py +262 -107
  34. batFramework/gui/syncedVar.py +49 -0
  35. batFramework/gui/textInput.py +46 -22
  36. batFramework/gui/toggle.py +70 -52
  37. batFramework/gui/tooltip.py +30 -0
  38. batFramework/gui/widget.py +222 -135
  39. batFramework/manager.py +7 -8
  40. batFramework/particle.py +4 -1
  41. batFramework/propertyEaser.py +79 -0
  42. batFramework/renderGroup.py +17 -50
  43. batFramework/resourceManager.py +43 -13
  44. batFramework/scene.py +15 -335
  45. batFramework/sceneLayer.py +138 -0
  46. batFramework/sceneManager.py +31 -36
  47. batFramework/scrollingSprite.py +8 -3
  48. batFramework/sprite.py +1 -1
  49. batFramework/templates/__init__.py +1 -2
  50. batFramework/templates/controller.py +97 -0
  51. batFramework/timeManager.py +76 -22
  52. batFramework/transition.py +37 -103
  53. batFramework/utils.py +125 -66
  54. {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/METADATA +24 -3
  55. batframework-1.0.9a9.dist-info/RECORD +67 -0
  56. {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/WHEEL +1 -1
  57. batFramework/character.py +0 -27
  58. batFramework/templates/character.py +0 -43
  59. batFramework/templates/states.py +0 -166
  60. batframework-1.0.9a7.dist-info/RECORD +0 -63
  61. /batframework-1.0.9a7.dist-info/LICENCE → /batframework-1.0.9a9.dist-info/LICENSE +0 -0
  62. {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/top_level.txt +0 -0
batFramework/cutscene.py CHANGED
@@ -1,42 +1,92 @@
1
1
  import batFramework as bf
2
2
  from .transition import Transition
3
+ from typing import Callable,Any
4
+
3
5
  class Cutscene:
6
+ def __init__(self):
7
+ """
8
+ Create a base Cutscene (ends immediately)
9
+ """
10
+
11
+ def start(self):
12
+ """
13
+ Called by the manager or the parent cutscene
14
+ Has to return to blank init state
15
+ """
16
+ self.end()
17
+
18
+ def process_event(self,event):
19
+ pass
20
+
21
+ def update(self,dt):
22
+ pass
23
+
24
+
25
+ def end(self):
26
+ """
27
+ Mark self as over
28
+ """
29
+ self.is_over = True
30
+
31
+ class Sequence(Cutscene):
4
32
  def __init__(self,*cutscenes):
5
- self.is_over : bool = False
6
- self.sub_cutscenes : list[Cutscene] = list(cutscenes)
7
- self.sub_index = -1
33
+ self.sub_cutscenes :list[Cutscene] = list(cutscenes)
34
+ self.index = 0
8
35
 
9
36
  def start(self):
37
+ self.is_over = False
38
+ self.index = 0
10
39
  if self.sub_cutscenes:
11
- self.sub_index = 0
12
- self.sub_cutscenes[self.sub_index].start()
13
- else:
14
- self.end()
40
+ self.sub_cutscenes[0].start()
15
41
 
16
42
  def process_event(self,event):
17
- if self.sub_index > 0:
18
- self.sub_cutscenes[self.sub_index].process_event(event)
19
-
43
+ """
44
+ propagate process event for current sub cutscene
45
+ """
46
+ if self.index >0 and not self.is_over:
47
+ self.sub_cutscenes[self.index].process_event(event)
48
+
49
+
50
+
20
51
  def update(self,dt):
21
- if self.sub_index >= 0:
22
- self.sub_cutscenes[self.sub_index].update(dt)
23
- if self.sub_cutscenes[self.sub_index].is_over:
24
- self.sub_index +=1
25
- if self.sub_index >= len(self.sub_cutscenes):
52
+ """
53
+ Update current sub cutscene (if any)
54
+ if current is over, start next one
55
+ if current was last, then end self
56
+ """
57
+ if self.index < len(self.sub_cutscenes):
58
+ self.sub_cutscenes[self.index].update(dt)
59
+ if self.sub_cutscenes[self.index].is_over:
60
+ self.index += 1
61
+ if self.index == len(self.sub_cutscenes):
26
62
  self.end()
27
63
  return
28
- self.sub_cutscenes[self.sub_index].start()
29
-
30
- def end(self):
31
- self.is_over = True
64
+ self.sub_cutscenes[self.index].start()
65
+
66
+
67
+ class Parallel(Cutscene):
68
+ def __init__(self,*cutscenes:Cutscene):
69
+ self.sub_cutscenes : list[Cutscene] = list(cutscenes)
70
+
71
+ def start(self):
72
+ self.is_over = False
73
+ if not self.sub_cutscenes:
74
+ self.end()
75
+ for s in self.sub_cutscenes:
76
+ s.start()
32
77
 
78
+ def update(self,dt):
79
+ for s in self.sub_cutscenes:
80
+ s.update(dt)
81
+ if all(s.is_over for s in self.sub_cutscenes):
82
+ self.end()
33
83
 
34
84
  class Wait(Cutscene):
35
85
  def __init__(self,duration:float,scene_name:str="global"):
36
- super().__init__()
37
86
  self.duration = duration
38
87
  self.scene_name = scene_name
39
88
  def start(self):
89
+ self.is_over = False
40
90
  self.timer = bf.SceneTimer(duration=self.duration,end_callback=self.end,scene_name=self.scene_name)
41
91
  self.timer.start()
42
92
 
@@ -44,10 +94,160 @@ class Wait(Cutscene):
44
94
 
45
95
  class TransitionToScene(Cutscene):
46
96
  def __init__(self,scene_name:str,transition:Transition):
47
- super().__init__()
48
97
  self.scene_name = scene_name
49
98
  self.transition: Transition = transition
50
99
 
51
100
  def start(self):
101
+ self.is_over = False
52
102
  bf.CutsceneManager().manager.transition_to_scene(self.scene_name,self.transition)
53
103
  bf.Timer(self.transition.duration,end_callback=self.end).start()
104
+
105
+
106
+
107
+
108
+
109
+
110
+ class GlideWorldCameraFromTo(Cutscene):
111
+ def __init__(self,start:tuple[float,float], stop:tuple[float,float],duration:float=1,easing:bf.easing=bf.easing.EASE_IN_OUT,scene_name:str=None):
112
+ super().__init__()
113
+ self.scene = None
114
+ self.scene_name = scene_name
115
+ self.start_pos = start
116
+ self.stop_pos = stop
117
+ self.controller = bf.EasingController(duration,easing,update_callback=self.internal,end_callback=self.end)
118
+
119
+
120
+ def start(self):
121
+ self.is_over = False
122
+ if self.scene_name is None:
123
+ self.scene_name = bf.CutsceneManager().manager.get_scene_at(0).name
124
+
125
+ self.scene = bf.CutsceneManager().manager.get_scene(self.scene_name)
126
+ self.controller.start()
127
+
128
+
129
+ def internal(self,progression:float):
130
+ self.scene.camera.set_center(
131
+ self.start_pos[0]+progression*(self.stop_pos[0]-self.start_pos[0]),
132
+ self.start_pos[1]+progression*(self.stop_pos[1]-self.start_pos[1])
133
+ )
134
+
135
+ def end(self):
136
+ if self.scene:
137
+ self.scene.camera.set_center(self.stop_pos[0],self.stop_pos[1])
138
+
139
+ super().end()
140
+
141
+
142
+
143
+ class Function(Cutscene):
144
+ def __init__(self, function:Callable[[],Any],*args,**kwargs):
145
+ super().__init__()
146
+ self.function:Callable[[],Any] = function
147
+ self.args = args
148
+ self.kwargs = kwargs
149
+
150
+ def start(self):
151
+ self.is_over = False
152
+ self.function(*self.args,**self.kwargs)
153
+ self.end()
154
+
155
+ class GlideCamera(Cutscene):
156
+ def __init__(
157
+ self,
158
+ start: tuple[float, float] | None = None,
159
+ stop: tuple[float, float] | None = None,
160
+ delta: tuple[float, float] | None = None,
161
+ duration: float = 1,
162
+ easing: bf.easing = bf.easing.EASE_IN_OUT,
163
+ scene_name: str | None = None,
164
+ layer_name: str = "world",
165
+ ):
166
+ super().__init__()
167
+ self.scene = None
168
+ self.layer = None
169
+ self.scene_name = scene_name
170
+ self.layer_name = layer_name
171
+ self.start_pos = start
172
+ self.stop_pos = stop
173
+ self.delta = delta
174
+ self.controller = bf.EasingController(
175
+ duration, easing, update_callback=self.internal, end_callback=self.end
176
+ )
177
+
178
+ def start(self):
179
+ self.is_over = False
180
+
181
+ if self.scene_name is None:
182
+ self.scene_name = bf.CutsceneManager().manager.get_scene_at(0).name
183
+
184
+ self.scene = bf.CutsceneManager().manager.get_scene(self.scene_name)
185
+ self.layer = self.scene.get_layer(self.layer_name)
186
+
187
+ if not self.layer:
188
+ raise ValueError(f"Layer '{self.layer_name}' not found in scene '{self.scene_name}'.")
189
+
190
+ # Fallback to current camera position
191
+ if self.start_pos is None:
192
+ self.start_pos = self.layer.camera.get_center()
193
+
194
+ # Compute stop_pos if not set
195
+ if self.stop_pos is None:
196
+ if self.delta is not None:
197
+ self.stop_pos = (
198
+ self.start_pos[0] + self.delta[0],
199
+ self.start_pos[1] + self.delta[1],
200
+ )
201
+ else:
202
+ raise ValueError("Must specify either stop or delta position")
203
+
204
+ self.controller.start()
205
+
206
+ def internal(self, progression: float):
207
+ self.layer.camera.set_center(
208
+ self.start_pos[0] + progression * (self.stop_pos[0] - self.start_pos[0]),
209
+ self.start_pos[1] + progression * (self.stop_pos[1] - self.start_pos[1]),
210
+ )
211
+
212
+ def end(self):
213
+ if self.layer:
214
+ self.layer.camera.set_center(*self.stop_pos)
215
+ super().end()
216
+
217
+ class GlideCameraTo(GlideCamera):
218
+ def __init__(
219
+ self,
220
+ stop: tuple[float, float],
221
+ duration: float = 1,
222
+ easing: bf.easing = bf.easing.EASE_IN_OUT,
223
+ scene_name: str | None = None,
224
+ layer_name: str = "world",
225
+ ):
226
+ super().__init__(
227
+ start=None,
228
+ stop=stop,
229
+ delta=None,
230
+ duration=duration,
231
+ easing=easing,
232
+ scene_name=scene_name,
233
+ layer_name=layer_name,
234
+ )
235
+
236
+ class GlideCameraBy(GlideCamera):
237
+ def __init__(
238
+ self,
239
+ delta: tuple[float, float],
240
+ duration: float = 1,
241
+ easing: bf.easing = bf.easing.EASE_IN_OUT,
242
+ scene_name: str | None = None,
243
+ layer_name: str = "world",
244
+ ):
245
+ super().__init__(
246
+ start=None,
247
+ stop=None,
248
+ delta=delta,
249
+ duration=duration,
250
+ easing=easing,
251
+ scene_name=scene_name,
252
+ layer_name=layer_name,
253
+ )
@@ -9,7 +9,7 @@ class CutsceneManager(metaclass=bf.Singleton):
9
9
  def __init__(self) -> None:
10
10
  self.current_cutscene: Cutscene = None
11
11
  self.manager: bf.Manager = None
12
-
12
+ self.is_playing : bool = False
13
13
  def set_manager(self, manager):
14
14
  self.manager = manager
15
15
 
@@ -21,11 +21,14 @@ class CutsceneManager(metaclass=bf.Singleton):
21
21
 
22
22
  def play(self,cutscene:Cutscene):
23
23
  if self.current_cutscene is not None:return
24
+
24
25
  self.current_cutscene = cutscene
25
26
  cutscene.start()
26
-
27
+ self.is_playing = True
28
+
27
29
  def update(self,dt):
28
30
  if self.current_cutscene:
29
31
  self.current_cutscene.update(dt)
30
32
  if self.current_cutscene.is_over:
31
33
  self.current_cutscene = None
34
+ self.is_playing = False
batFramework/drawable.py CHANGED
@@ -11,7 +11,7 @@ class Drawable(Entity):
11
11
 
12
12
  def __init__(
13
13
  self,
14
- size: None | tuple[int, int] = None,
14
+ size: None | tuple[int|float] = None,
15
15
  surface_flags: int = 0,
16
16
  convert_alpha: bool = False,
17
17
  *args,
@@ -20,10 +20,12 @@ class Drawable(Entity):
20
20
  super().__init__()
21
21
  self.visible: bool = True
22
22
  self.render_order: int = 0
23
- self.rect.size = (10, 10) if size is None else size
23
+ if size is not None:
24
+ self.rect.size = size
24
25
  self.convert_alpha: bool = convert_alpha
25
26
  self.surface_flags: int = surface_flags
26
27
  self.blit_flags: int = 0
28
+ self.drawn_by_group : bool = False # flag for render group
27
29
  self.surface: pygame.Surface = pygame.Surface(self.rect.size, surface_flags)
28
30
  if convert_alpha:
29
31
  self.surface = self.surface.convert_alpha()
@@ -54,8 +56,8 @@ class Drawable(Entity):
54
56
 
55
57
  def set_render_order(self, render_order: int) -> Self:
56
58
  self.render_order = render_order
57
- if self.parent_scene:
58
- self.parent_scene.sort_entities()
59
+ if self.parent_layer:
60
+ self.parent_layer.update_draw_order()
59
61
  return self
60
62
 
61
63
  def set_visible(self, value: bool) -> Self:
@@ -66,7 +68,7 @@ class Drawable(Entity):
66
68
  """
67
69
  Draw the entity onto the camera surface
68
70
  """
69
- if not self.visible or not camera.rect.colliderect(self.rect) or self.surface.get_alpha() == 0:
71
+ if not self.visible or self.drawn_by_group or not camera.rect.colliderect(self.rect) or self.surface.get_alpha() == 0:
70
72
  return
71
73
  camera.surface.blit(
72
74
  self.surface,
@@ -1,7 +1,6 @@
1
1
  import pygame
2
2
  import batFramework as bf
3
3
  from functools import lru_cache
4
- from .enums import easing
5
4
  from typing import Callable,Any
6
5
 
7
6
 
@@ -20,16 +19,17 @@ def process_value(progress: float, p0: float, p1: float, p2: float, p3: float) -
20
19
  class EasingController(bf.Timer):
21
20
  def __init__(
22
21
  self,
23
- easing_function: easing = easing.LINEAR,
24
22
  duration: float = 1,
23
+ easing: bf.easing = bf.easing.LINEAR,
25
24
  update_callback=None,
26
- end_callback: Callable[[],Any]=None,
27
- loop: bool = False,
25
+ end_callback: Callable[[], Any] = None,
26
+ loop: int = 0,
27
+ register:str="global"
28
28
  ) -> None:
29
- self.easing_function = easing_function
30
- self.update_callback: Callable[[float],Any] = update_callback
29
+ self.easing_function = easing
30
+ self.update_callback: Callable[[float], Any] = update_callback
31
31
  self.value: float = 0.0
32
- super().__init__(duration, end_callback, loop)
32
+ super().__init__(duration, end_callback, loop, register)
33
33
 
34
34
  def get_value(self) -> float:
35
35
  return self.value
@@ -44,12 +44,11 @@ class EasingController(bf.Timer):
44
44
  super().update(dt)
45
45
  if self.get_progression() == 0:
46
46
  return
47
- if self.easing_function == easing.LINEAR:
47
+ if self.easing_function == bf.easing.LINEAR: # avoid calculating if linear (just use progression as is)
48
48
  self.value = self.get_progression()
49
49
  else:
50
- self.value = process_value(
51
- self.get_progression(), *self.easing_function.control_points
52
- )
50
+ self.value = process_value(self.get_progression(), *self.easing_function.control_points)
51
+
53
52
  if self.update_callback:
54
53
  self.update_callback(self.value)
55
54
 
batFramework/entity.py CHANGED
@@ -11,7 +11,6 @@ class Entity:
11
11
  _count: int = 0
12
12
  _available_uids: set[int] = set()
13
13
 
14
-
15
14
  def __init__(self,*args,**kwargs) -> None:
16
15
  if Entity._available_uids:
17
16
  self.uid = Entity._available_uids.pop()
@@ -19,9 +18,10 @@ class Entity:
19
18
  self.uid = Entity._count
20
19
  Entity._count += 1
21
20
 
22
- self.rect = pygame.FRect(0, 0, 0, 0)
21
+ self.rect = pygame.FRect(0, 0, 10, 10)
23
22
  self.tags: list[str] = []
24
23
  self.parent_scene: bf.Scene | None = None
24
+ self.parent_layer: bf.SceneLayer | None = None
25
25
  self.debug_color: tuple | str = "red"
26
26
 
27
27
  def __del__(self):
@@ -44,6 +44,16 @@ class Entity:
44
44
  self.debug_color = color
45
45
  return self
46
46
 
47
+ def kill(self):
48
+ """
49
+ Removes the entity from a scene layer
50
+ """
51
+ if self.parent_layer:
52
+ self.parent_layer.remove(self)
53
+
54
+ def set_parent_layer(self, layer):
55
+ self.parent_layer = layer
56
+
47
57
  def set_parent_scene(self, scene) -> Self:
48
58
  if scene == self.parent_scene:
49
59
  return self
@@ -71,8 +81,17 @@ class Entity:
71
81
  self.tags = [tag for tag in self.tags if tag not in tags]
72
82
 
73
83
  def has_tags(self, *tags) -> bool:
84
+ """
85
+ return True if entity contains all given tags
86
+ """
74
87
  return all(tag in self.tags for tag in tags)
75
88
 
89
+ def has_any_tags(self, *tags) -> bool:
90
+ """
91
+ return True if entity contains any of given tags
92
+ """
93
+ return any(tag in self.tags for tag in tags)
94
+
76
95
  def get_tags(self) -> list[str]:
77
96
  return self.tags
78
97
 
batFramework/enums.py CHANGED
@@ -3,52 +3,58 @@ import pygame
3
3
 
4
4
  playerInput = [pygame.KEYDOWN,pygame.MOUSEBUTTONDOWN,pygame.KEYUP,pygame.MOUSEBUTTONUP]
5
5
 
6
-
7
-
8
6
  class color:
9
- WHITE = (255, 255, 255)
10
- LIGHTER_GRAY = (236, 240, 241)
11
- LIGHT_GRAY = (189, 195, 199)
12
- DARK_GRAY = (66, 66, 66)
13
- DARKER_GRAY = (23, 23, 23)
14
- BLACK = (0, 0, 0)
7
+ WHITE = pygame.Color(255, 255, 255)
8
+ LIGHTER_GRAY = pygame.Color(236, 240, 241)
9
+ LIGHT_GRAY = pygame.Color(189, 195, 199)
10
+ DARK_GRAY = pygame.Color(66, 66, 66)
11
+ DARKER_GRAY = pygame.Color(23, 23, 23)
12
+ BLACK = pygame.Color(0, 0, 0)
15
13
 
16
- TURQUOISE = (26, 188, 156)
17
- TURQUOISE_SHADE = (22, 160, 133)
14
+ TURQUOISE = pygame.Color(26, 188, 156)
15
+ TURQUOISE_SHADE = pygame.Color(22, 160, 133)
18
16
 
19
- GREEN = (46, 204, 113)
20
- GREEN_SHADE = (39, 174, 96)
17
+ GREEN = pygame.Color(46, 204, 113)
18
+ GREEN_SHADE = pygame.Color(39, 174, 96)
21
19
 
22
- BLUE = (52, 152, 219)
23
- BLUE_SHADE = (41, 128, 185)
20
+ BLUE = pygame.Color(52, 152, 219)
21
+ BLUE_SHADE = pygame.Color(41, 128, 185)
24
22
 
25
- PURPLE = (155, 89, 182)
26
- PURPLE_SHADE = (142, 68, 173)
23
+ PURPLE = pygame.Color(155, 89, 182)
24
+ PURPLE_SHADE = pygame.Color(142, 68, 173)
27
25
 
28
- CHARCOAL = (52, 73, 94)
29
- CHARCOAL_SHADE = (44, 62, 80)
26
+ CHARCOAL = pygame.Color(52, 73, 94)
27
+ CHARCOAL_SHADE = pygame.Color(44, 62, 80)
30
28
 
31
- GOLD = (241, 196, 15)
32
- GOLD_SHADE = (243, 156, 18)
29
+ GOLD = pygame.Color(241, 196, 15)
30
+ GOLD_SHADE = pygame.Color(243, 156, 18)
33
31
 
34
- ORANGE = (230, 126, 34)
35
- ORANGE_SHADE = (211, 84, 0)
32
+ ORANGE = pygame.Color(230, 126, 34)
33
+ ORANGE_SHADE = pygame.Color(211, 84, 0)
36
34
 
37
- RED = (231, 76, 60)
38
- RED_SHADE = (192, 57, 43)
35
+ RED = pygame.Color(231, 76, 60)
36
+ RED_SHADE = pygame.Color(192, 57, 43)
39
37
 
40
- CLOUD = (236, 240, 241)
41
- CLOUD_SHADE = (189, 195, 199)
38
+ CLOUD = pygame.Color(236, 240, 241)
39
+ CLOUD_SHADE = pygame.Color(189, 195, 199)
42
40
 
43
- CONCRETE = (149, 165, 166)
44
- CONCRETE_SHADE = (127, 140, 141)
41
+ CONCRETE = pygame.Color(149, 165, 166)
42
+ CONCRETE_SHADE = pygame.Color(127, 140, 141)
45
43
 
46
44
  # GB
47
- DARKER_GB = (27, 42, 9)
48
- DARK_GB = (14, 69, 11)
49
- LIGHT_GB = (73, 107, 34)
50
- LIGHTER_GB = (154, 158, 63)
51
-
45
+ DARKER_GB = pygame.Color(27, 42, 9)
46
+ DARK_GB = pygame.Color(14, 69, 11)
47
+ LIGHT_GB = pygame.Color(73, 107, 34)
48
+ LIGHTER_GB = pygame.Color(154, 158, 63)
49
+
50
+ @staticmethod
51
+ def mult(color: pygame.Color, factor: float):
52
+ return pygame.Color(
53
+ min(max(0, int(color.r * factor)), 255),
54
+ min(max(0, int(color.g * factor)), 255),
55
+ min(max(0, int(color.b * factor)), 255),
56
+ color.a
57
+ )
52
58
 
53
59
  class easing(Enum):
54
60
  LINEAR = (0, 0, 1, 1)
@@ -60,6 +66,13 @@ class easing(Enum):
60
66
  def __init__(self, *control_points):
61
67
  self.control_points = control_points
62
68
 
69
+ @classmethod
70
+ def create(cls, *control_points):
71
+ """Create a custom easing instance."""
72
+ instance = object.__new__(cls)
73
+ instance._value_ = control_points
74
+ instance.control_points = control_points
75
+ return instance
63
76
 
64
77
  class axis(Enum):
65
78
  HORIZONTAL = "horizontal"
@@ -83,6 +96,8 @@ class alignment(Enum):
83
96
  TOPRIGHT = "topright"
84
97
  MIDLEFT = "midleft"
85
98
  MIDRIGHT = "midright"
99
+ MIDTOP = "midtop"
100
+ MIDBOTTOM = "midbottom"
86
101
  BOTTOMLEFT = "bottomleft"
87
102
  BOTTOMRIGHT = "bottomright"
88
103
 
@@ -7,8 +7,9 @@ from .draggableWidget import DraggableWidget
7
7
  from .clickableWidget import ClickableWidget
8
8
  from .root import Root
9
9
  from .shape import Shape
10
- from .meter import Meter
10
+ from .meter import BarMeter
11
11
  from .label import Label
12
+ from .tooltip import ToolTip
12
13
  from .animatedLabel import AnimatedLabel
13
14
  from .textInput import TextInput
14
15
  from .button import Button
@@ -17,6 +18,8 @@ from .layout import *
17
18
  from .container import Container
18
19
  from .indicator import *
19
20
  from .toggle import Toggle
20
- from .radioButton import RadioButton, RadioVariable
21
+ from .syncedVar import SyncedVar
22
+ from .radioButton import RadioButton
21
23
  from .slider import Slider
22
- from .constraints import *
24
+ from .selector import Selector
25
+ import batFramework.gui.constraints as constraints
@@ -1,6 +1,6 @@
1
1
  from .label import Label
2
2
  import batFramework as bf
3
- from typing import Self
3
+ from typing import Self,Callable,Any
4
4
 
5
5
 
6
6
  class AnimatedLabel(Label):
@@ -10,6 +10,7 @@ class AnimatedLabel(Label):
10
10
  self.is_over: bool = True
11
11
  self.is_paused: bool = False
12
12
  self.original_text = ""
13
+ self.end_callback : Callable[[],Any]= None
13
14
  self.set_autoresize(False)
14
15
  self.set_alignment(bf.alignment.LEFT)
15
16
  super().__init__("")
@@ -18,6 +19,9 @@ class AnimatedLabel(Label):
18
19
  def __str__(self) -> str:
19
20
  return "AnimatedLabel"
20
21
 
22
+ def set_end_callback(self,callback:Callable[[],Any]):
23
+ self.end_callback = callback
24
+
21
25
  def pause(self) -> Self:
22
26
  self.is_paused = True
23
27
  return self
@@ -31,7 +35,7 @@ class AnimatedLabel(Label):
31
35
  return self
32
36
 
33
37
  def cut_text_to_width(self, text: str) -> list[str]:
34
- w = self.get_padded_width()
38
+ w = self.get_inner_width()
35
39
  if text == "" or not self.font_object or w < self.font_object.point_size:
36
40
  return [text]
37
41
  left = 0
@@ -72,7 +76,11 @@ class AnimatedLabel(Label):
72
76
  if self.is_over:
73
77
  return
74
78
  if not self.is_over and self.cursor_position == len(self.original_text):
79
+ if len(self.original_text) == 0:
80
+ self._set_text_internal("")
75
81
  self.is_over = True
82
+ if self.end_callback is not None:
83
+ self.end_callback()
76
84
  return
77
85
  self.cursor_position = min(
78
86
  self.cursor_position + self.text_speed * dt, len(self.original_text)
@@ -2,8 +2,6 @@ from .label import Label
2
2
  import batFramework as bf
3
3
  from typing import Self, Callable,Any
4
4
  from .clickableWidget import ClickableWidget
5
- import pygame
6
- from math import ceil
7
5
 
8
6
 
9
7
  class Button(Label, ClickableWidget):
@@ -12,34 +10,9 @@ class Button(Label, ClickableWidget):
12
10
  self.set_callback(callback)
13
11
 
14
12
  def __str__(self) -> str:
15
- return f"Button({self.text})"
13
+ return f"Button('{self.text}')"
16
14
 
17
- def get_min_required_size(self) -> tuple[float, float]:
18
- if not (self.autoresize_w or self.autoresize_h):
19
- return self.rect.size
20
- if not self.text_rect:
21
- self.text_rect.size = self._get_text_rect_required_size()
22
- res = self.inflate_rect_by_padding((0, 0, *self.text_rect.size)).size
15
+ def get_min_required_size(self):
16
+ res = super().get_min_required_size()
23
17
  res = res[0],res[1]+self.unpressed_relief
24
- return res[0] if self.autoresize_w else self.rect.w, (
25
- res[1] if self.autoresize_h else self.rect.h
26
- )
27
-
28
-
29
- def _build_layout(self) -> None:
30
-
31
- self.text_rect.size = self._get_text_rect_required_size()
32
- if self.autoresize_h or self.autoresize_w:
33
- target_rect = self.inflate_rect_by_padding((0, 0, *self.text_rect.size))
34
- target_rect.h += self.unpressed_relief
35
- if not self.autoresize_w:
36
- target_rect.w = self.rect.w
37
- if not self.autoresize_h:
38
- target_rect.h = self.rect.h
39
- if self.rect.size != target_rect.size:
40
- self.set_size(target_rect.size)
41
- self.apply_updates()
42
- return
43
- offset = self._get_outline_offset() if self.show_text_outline else (0,0)
44
- padded = self.get_padded_rect().move(-self.rect.x + offset[0], -self.rect.y + offset[1])
45
- self.align_text(self.text_rect, padded, self.alignment)
18
+ return res