batframework 1.0.10__py3-none-any.whl → 2.0.0a1__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 (81) hide show
  1. batFramework/__init__.py +83 -52
  2. batFramework/action.py +280 -252
  3. batFramework/actionContainer.py +105 -38
  4. batFramework/animatedSprite.py +81 -117
  5. batFramework/animation.py +91 -0
  6. batFramework/audioManager.py +156 -85
  7. batFramework/baseScene.py +249 -0
  8. batFramework/camera.py +245 -123
  9. batFramework/constants.py +57 -75
  10. batFramework/cutscene.py +239 -119
  11. batFramework/cutsceneManager.py +34 -0
  12. batFramework/drawable.py +107 -0
  13. batFramework/dynamicEntity.py +30 -23
  14. batFramework/easingController.py +58 -0
  15. batFramework/entity.py +130 -123
  16. batFramework/enums.py +171 -0
  17. batFramework/fontManager.py +65 -0
  18. batFramework/gui/__init__.py +28 -14
  19. batFramework/gui/animatedLabel.py +90 -0
  20. batFramework/gui/button.py +18 -84
  21. batFramework/gui/clickableWidget.py +244 -0
  22. batFramework/gui/collapseContainer.py +98 -0
  23. batFramework/gui/constraints/__init__.py +1 -0
  24. batFramework/gui/constraints/constraints.py +1066 -0
  25. batFramework/gui/container.py +220 -49
  26. batFramework/gui/debugger.py +140 -47
  27. batFramework/gui/draggableWidget.py +63 -0
  28. batFramework/gui/image.py +61 -23
  29. batFramework/gui/indicator.py +116 -40
  30. batFramework/gui/interactiveWidget.py +243 -22
  31. batFramework/gui/label.py +147 -110
  32. batFramework/gui/layout.py +442 -81
  33. batFramework/gui/meter.py +155 -0
  34. batFramework/gui/radioButton.py +43 -0
  35. batFramework/gui/root.py +228 -60
  36. batFramework/gui/scrollingContainer.py +282 -0
  37. batFramework/gui/selector.py +232 -0
  38. batFramework/gui/shape.py +286 -86
  39. batFramework/gui/slider.py +353 -0
  40. batFramework/gui/style.py +10 -0
  41. batFramework/gui/styleManager.py +49 -0
  42. batFramework/gui/syncedVar.py +43 -0
  43. batFramework/gui/textInput.py +331 -0
  44. batFramework/gui/textWidget.py +308 -0
  45. batFramework/gui/toggle.py +140 -62
  46. batFramework/gui/tooltip.py +35 -0
  47. batFramework/gui/widget.py +546 -307
  48. batFramework/manager.py +131 -50
  49. batFramework/particle.py +118 -0
  50. batFramework/propertyEaser.py +79 -0
  51. batFramework/renderGroup.py +34 -0
  52. batFramework/resourceManager.py +130 -0
  53. batFramework/scene.py +31 -226
  54. batFramework/sceneLayer.py +134 -0
  55. batFramework/sceneManager.py +200 -165
  56. batFramework/scrollingSprite.py +115 -0
  57. batFramework/sprite.py +46 -0
  58. batFramework/stateMachine.py +49 -51
  59. batFramework/templates/__init__.py +2 -0
  60. batFramework/templates/character.py +15 -0
  61. batFramework/templates/controller.py +158 -0
  62. batFramework/templates/stateMachine.py +39 -0
  63. batFramework/tileset.py +46 -0
  64. batFramework/timeManager.py +213 -0
  65. batFramework/transition.py +162 -157
  66. batFramework/triggerZone.py +22 -22
  67. batFramework/utils.py +306 -184
  68. {batframework-1.0.10.dist-info → batframework-2.0.0a1.dist-info}/LICENSE +20 -20
  69. {batframework-1.0.10.dist-info → batframework-2.0.0a1.dist-info}/METADATA +2 -2
  70. batframework-2.0.0a1.dist-info/RECORD +72 -0
  71. batFramework/cutsceneBlocks.py +0 -176
  72. batFramework/debugger.py +0 -48
  73. batFramework/easing.py +0 -71
  74. batFramework/gui/constraints.py +0 -204
  75. batFramework/gui/frame.py +0 -19
  76. batFramework/particles.py +0 -77
  77. batFramework/time.py +0 -75
  78. batFramework/transitionManager.py +0 -0
  79. batframework-1.0.10.dist-info/RECORD +0 -43
  80. {batframework-1.0.10.dist-info → batframework-2.0.0a1.dist-info}/WHEEL +0 -0
  81. {batframework-1.0.10.dist-info → batframework-2.0.0a1.dist-info}/top_level.txt +0 -0
@@ -1,49 +1,220 @@
1
- import batFramework as bf
2
- from .widget import Widget
3
- from .layout import Layout
4
- from .constraints import Constraint
5
- from typing import Self
6
-
7
- class Container(Widget):
8
- def __init__(self, layout:Layout=None, *children:Widget):
9
- super().__init__()
10
- self.set_debug_color("green")
11
- self.surface = None
12
- self.layout :Layout = layout
13
- if self.layout : self.layout.set_parent(self)
14
- for child in children:
15
- self.add_child(child)
16
-
17
- def set_layout(self,layout:Layout)->Self:
18
- self.layout = layout
19
- self.apply_constraints()
20
- return self
21
-
22
- def get_bounding_box(self):
23
- yield (self.rect,self._debug_color)
24
- for child in self.children:
25
- yield from child.get_bounding_box()
26
-
27
- def clear_children(self)->None:
28
- self.children.clear()
29
- self.apply_constraints()
30
-
31
- def add_child(self,*child:Widget)->None:
32
- super().add_child(*child)
33
- if self.layout : self.layout.arrange()
34
-
35
- def remove_child(self,child:Widget)->None:
36
- super().remove_child(child)
37
- if self.layout : self.layout.arrange()
38
-
39
- def build(self)->None:
40
- super().build()
41
- if self.layout : self.layout.arrange()
42
-
43
- def apply_constraints(self)->None:
44
- super().apply_constraints()
45
- if self.layout : self.layout.arrange()
46
-
47
- def to_string_id(self)->str:
48
- return f"Container({len(self.children)},{[c.to_string() for c in self.constraints]})"
49
-
1
+ import pygame
2
+ import batFramework as bf
3
+ from .widget import Widget
4
+ from .shape import Shape
5
+ from .interactiveWidget import InteractiveWidget
6
+ from .layout import Layout, Column
7
+ from typing import Self
8
+ from pygame.math import Vector2
9
+
10
+
11
+ class Container(Shape, InteractiveWidget):
12
+ def __init__(self, layout: Layout = None, *children: Widget) -> None:
13
+ super().__init__()
14
+ self.dirty_layout: bool = False
15
+ self.layout = layout if layout else Column()
16
+ self.layout.set_parent(self)
17
+ self.scroll = Vector2(0, 0)
18
+ self.dirty_scroll = False
19
+ self.set_debug_color("green")
20
+ self.add(*children)
21
+
22
+
23
+ def __str__(self) -> str:
24
+ return f"Container({self.uid},{len(self.children)})"
25
+
26
+ def get_min_required_size(self):
27
+ return self.layout.get_auto_size() if self.layout else self.rect.size
28
+
29
+ def reset_scroll(self) -> Self:
30
+ if self.scroll == (0,0):
31
+ return self
32
+ self.set_scroll((0, 0))
33
+ return self
34
+
35
+ def set_scroll(self, value: tuple[float]) -> Self:
36
+ # print("Trying to set scroll to ",value)
37
+ # print("Current scroll is : ",self.scroll)
38
+
39
+ if (self.scroll.x,self.scroll.y) == value:
40
+ return self
41
+ self.scroll.update(value)
42
+ # print("Scroll updated to", value)
43
+ self.clamp_scroll()
44
+ self.dirty_scroll = True
45
+ # self.dirty_layout = True
46
+ return self
47
+
48
+ def scrollX_by(self, x: float) -> Self:
49
+ if x == 0:
50
+ return self
51
+ self.set_scroll((self.scroll.x + x, self.scroll.y))
52
+ return self
53
+
54
+ def scrollY_by(self, y: float) -> Self:
55
+ if y == 0:
56
+ return self
57
+ self.set_scroll((self.scroll.x, self.scroll.y + y))
58
+ return self
59
+
60
+ def scroll_by(self, value: tuple[float]) -> Self:
61
+ self.set_scroll((self.scroll.x + value[0], self.scroll.y + value[1]))
62
+ return self
63
+
64
+ def clamp_scroll(self) -> Self:
65
+ if not self.children:
66
+ return self
67
+ r = self.get_inner_rect()
68
+
69
+ if self.layout:
70
+ # self.layout.update_children_rect()
71
+ children_rect = self.layout.children_rect
72
+ else:
73
+ l = self.get_layout_children()
74
+ if l:
75
+ children_rect = l[0].rect.unionall(
76
+ [c.rect for c in l[1:]]
77
+ ) if len(l) > 1 else l
78
+ else:
79
+ children_rect = pygame.Rect(0,0,0,0)
80
+
81
+ max_scroll_x = max(0, children_rect.width - r.width)
82
+ max_scroll_y = max(0, children_rect.height - r.height)
83
+
84
+ sx = min(max(self.scroll.x, 0), max_scroll_x)
85
+ sy = min(max(self.scroll.y, 0), max_scroll_y)
86
+ # print("_"*20)
87
+ # print("Clamping scroll, children rect is :",children_rect)
88
+ # print("scroll to,",(sx,sy), "while current scroll is",self.scroll)
89
+
90
+ self.set_scroll((sx,sy))
91
+
92
+ return self
93
+
94
+ def set_layout(self, layout: Layout) -> Self:
95
+ tmp = self.layout
96
+ self.layout = layout
97
+ if self.layout != tmp:
98
+ tmp.set_parent(None)
99
+ self.layout.set_parent(self)
100
+ self.reset_scroll()
101
+ self.dirty_layout = True
102
+ return self
103
+
104
+ def get_interactive_children(self) -> list[InteractiveWidget]:
105
+ """Return all children that can be cycled through with focus"""
106
+ return [child for child in self.get_layout_children() \
107
+ if isinstance(child, InteractiveWidget) and not\
108
+ isinstance(child,Container) and child.allow_focus_to_self()]
109
+
110
+ def get_layout_children(self)->list[Widget]:
111
+ """Returns all children affected by layout"""
112
+ return self.children
113
+
114
+ def clear_children(self) -> None:
115
+ for child in self.children:
116
+ child.set_parent(None)
117
+ self.children.clear()
118
+ self.dirty_layout = True
119
+
120
+ def add(self, *child: Widget) -> Self:
121
+ super().add(*child)
122
+ self.dirty_shape = True
123
+ self.dirty_layout = True
124
+ return self
125
+
126
+ def remove(self, *child: Widget) -> Self:
127
+ super().remove(*child)
128
+ self.dirty_shape = True
129
+ self.dirty_layout = True
130
+ return self
131
+
132
+ def top_at(self, x: float | int, y: float | int) -> "None|Widget":
133
+ if self.rect.collidepoint(x, y):
134
+ for child in reversed(self.children):
135
+ result = child.top_at(x, y)
136
+ if result is not None:
137
+ return result
138
+ return self
139
+ return None
140
+
141
+ def get_focus(self) -> bool:
142
+ if not super().get_focus():
143
+ return False
144
+ interactive_children = self.get_interactive_children()
145
+ if not interactive_children:
146
+ return True
147
+ self.focused_index = min(self.focused_index, len(interactive_children) - 1)
148
+ return interactive_children[self.focused_index].get_focus()
149
+
150
+ def children_has_focus(self)->bool:
151
+ """Return true if any direct children is focused"""
152
+ return any(child.is_focused for child in self.get_interactive_children())
153
+
154
+ def handle_event(self, event) -> None:
155
+ super().handle_event(event)
156
+ self.layout.handle_event(event)
157
+
158
+ def set_focused_child(self, child: InteractiveWidget) -> bool:
159
+ interactive_children = self.get_interactive_children()
160
+ try:
161
+ index = interactive_children.index(child)
162
+ self.focused_index = index
163
+ if self.layout :
164
+ self.layout.scroll_to_widget(child)
165
+ return True
166
+ except ValueError:
167
+ return False
168
+
169
+ def allow_focus_to_self(self) -> bool:
170
+ """Return whether the container can get focused"""
171
+ return bool(self.get_interactive_children()) and self.visible
172
+
173
+ def build(self) -> None:
174
+ if self.layout is not None:
175
+ size = self.layout.get_auto_size()
176
+ self.set_size(self.resolve_size(size))
177
+ super().build()
178
+
179
+
180
+ def apply_pre_updates(self):
181
+ if self.dirty_size_constraints or self.dirty_shape:
182
+ self.resolve_constraints(size_only=True)
183
+ self.dirty_size_constraints = False
184
+ self.dirty_position_constraints = True
185
+
186
+
187
+
188
+ if self.dirty_layout:
189
+ self.layout.update_child_constraints()
190
+ self.layout.arrange()
191
+ self.dirty_layout = False
192
+
193
+ if self.dirty_scroll:
194
+ self.layout.scroll_children()
195
+ self.dirty_scroll = False
196
+
197
+ def apply_post_updates(self,skip_draw:bool=False):
198
+ if self.dirty_shape:
199
+ self.layout.update_child_constraints()
200
+ self.build()
201
+ self.dirty_size_constraints = True
202
+ self.dirty_position_constraints = True
203
+ self.dirty_layout = True
204
+ from .container import Container
205
+ if self.parent and isinstance(self.parent, Container):
206
+ self.parent.dirty_layout = True
207
+ self.parent.dirty_shape = True
208
+ self.dirty_shape = False
209
+ self.dirty_surface = True
210
+
211
+ if self.dirty_position_constraints:
212
+ self.resolve_constraints(position_only=True)
213
+ self.dirty_position_constraints= False
214
+
215
+
216
+ if self.dirty_surface and not skip_draw:
217
+ self.paint()
218
+ self.dirty_surface = False
219
+
220
+
@@ -1,47 +1,140 @@
1
- from .label import Label
2
- from typing import Self
3
-
4
- class Debugger(Label):
5
- def __init__(self)->None:
6
- super().__init__("")
7
- self.static_data : dict = {}
8
- self.dynamic_data : dict = {}
9
- self.refresh_rate = 10
10
- self.refresh_counter = 0
11
- self.render_order = 99
12
-
13
- def to_string_id(self)->str:
14
- return "Debugger"
15
-
16
- def set_refresh_rate(self, value:int)-> Self:
17
- self.refresh_rate = value
18
- return self
19
-
20
- def add_data(self,key:str,data):
21
- self.static_data[key] = str(data)
22
- self.update_text()
23
-
24
- def add_dynamic_data(self,key:str,func)->None:
25
- self.dynamic_data[key] = func
26
- self.update_text()
27
-
28
- def set_parent_scene(self,scene)->None:
29
- super().set_parent_scene(scene)
30
- self.update_text()
31
-
32
- def update_text(self)->None:
33
- if not self.parent_scene:return
34
- d = '\n'.join(key+':'+data if key!='' else data for key,data in self.static_data.items())
35
- d2 = '\n'.join(key+':'+str(data()) if key!='' else str(data()) for key,data in self.dynamic_data.items())
36
- self.set_text('\n'.join((d,d2)).strip())
37
-
38
- def update(self,dt:float)->None:
39
- if not self.parent_scene:return
40
- if self.parent_scene.get_sharedVar("is_debugging_func")() != 1:
41
- self.set_visible(False)
42
- return
43
- self.set_visible(True)
44
- self.refresh_counter = self.refresh_counter + (dt * 60)
45
- if self.refresh_counter > self.refresh_rate:
46
- self.refresh_counter = 0
47
- self.update_text()
1
+ from .label import Label
2
+ from typing import Self,Callable,Any
3
+ import batFramework as bf
4
+ import pygame
5
+ import sys
6
+
7
+
8
+ def convert_to_int(*args):
9
+ return [int(arg) for arg in args]
10
+
11
+
12
+ class Debugger(Label):
13
+ def __init__(self) -> None:
14
+ super().__init__("")
15
+ self.root_link = None
16
+ self.static_data: dict[str,Any] = {}
17
+ self.dynamic_data: dict[str,Callable[[],str]] = {}
18
+ self.refresh_interval :float = .01
19
+ self.refresh_counter: float = 0
20
+ self.add_tags("debugger")
21
+ self.set_visible(False)
22
+
23
+
24
+ def set_parent(self, parent):
25
+ super().set_parent(parent)
26
+ self.root_link = self.get_root()
27
+
28
+ def set_refresh_rate(self, value: float) -> Self:
29
+ """
30
+ seet refresh interval, time in seconds between each refresh of the debugger
31
+ """
32
+ self.refresh_interval = value
33
+ self.refresh_counter = 0
34
+ return self
35
+
36
+ def add_static(self, key: str, data):
37
+ self.static_data[key] = str(data)
38
+ self.update_text()
39
+
40
+ def add_dynamic(self, key: str, func:Callable[[],str]) -> None:
41
+ self.dynamic_data[key] = func
42
+ self.update_text()
43
+
44
+ def remove_static(self, key:str) -> bool:
45
+ try:
46
+ self.static_data.pop(key)
47
+ return True
48
+ except KeyError:
49
+ return False
50
+
51
+ def remove_dynamic(self, key:str) -> bool:
52
+ try:
53
+ self.dynamic_data.pop(key)
54
+ return True
55
+ except KeyError:
56
+ return False
57
+
58
+ def set_parent_scene(self, scene) -> Self:
59
+ super().set_parent_scene(scene)
60
+ self.set_render_order(sys.maxsize-100)
61
+ self.update_text()
62
+ return self
63
+
64
+ def set_text(self, text: str) -> Self:
65
+ return super().set_text(text)
66
+
67
+ def update_text(self) -> None:
68
+ if not self.parent_scene:
69
+ return
70
+
71
+ d = "\n".join(
72
+ key + ":" + data if key != "" else data
73
+ for key, data in self.static_data.items()
74
+ )
75
+
76
+ d2 = "\n".join(
77
+ key + ":" + str(data()) if key != "" else str(data())
78
+ for key, data in self.dynamic_data.items()
79
+ )
80
+
81
+ self.set_text("\n".join((d, d2)).strip())
82
+
83
+ def update(self, dt: float) -> None:
84
+ if not self.parent_scene:
85
+ return
86
+
87
+ if bf.ResourceManager().get_sharedVar("debug_mode") != bf.debugMode.DEBUGGER:
88
+ self.set_visible(False)
89
+ return
90
+
91
+ self.set_visible(True)
92
+ self.refresh_counter = self.refresh_counter + dt
93
+
94
+ if self.refresh_counter > self.refresh_interval:
95
+ self.refresh_counter = 0
96
+ self.update_text()
97
+
98
+
99
+ def __str__(self) -> str:
100
+ return "Debugger"
101
+
102
+ def top_at(self, x, y):
103
+ return None
104
+
105
+
106
+ class FPSDebugger(Debugger):
107
+ def __init__(self):
108
+ super().__init__()
109
+
110
+ def do_when_added(self):
111
+ if not self.parent_scene or not self.parent_scene.manager:
112
+ print("Debugger could not link to the manager")
113
+ return
114
+ manager_link = self.parent_scene.manager
115
+ self.add_dynamic("FPS", lambda: str(round(manager_link.get_fps())))
116
+
117
+
118
+ class BasicDebugger(FPSDebugger):
119
+
120
+ def do_when_added(self):
121
+ if not self.parent_scene or not self.parent_scene.manager:
122
+ print("Debugger could not link to the manager")
123
+ return
124
+ self.add_dynamic(
125
+ "Resolution", lambda: "x".join(str(i) for i in bf.const.RESOLUTION)
126
+ )
127
+ super().do_when_added()
128
+ self.add_dynamic("Mouse", pygame.mouse.get_pos)
129
+
130
+ if self.root_link is None:
131
+ return
132
+
133
+
134
+ self.add_dynamic(
135
+ "Hover",
136
+ lambda: (
137
+ str(self.root_link.hovered) if self.root_link.hovered else None
138
+ ),
139
+ )
140
+
@@ -0,0 +1,63 @@
1
+ from .interactiveWidget import InteractiveWidget
2
+ import batFramework as bf
3
+ import pygame
4
+
5
+
6
+ class DraggableWidget(InteractiveWidget):
7
+ def __init__(self, *args, **kwargs) -> None:
8
+ self.drag_start = None
9
+ self.offset = None
10
+ self.click_mask = [True,False,False,False,False]
11
+ self.is_dragged : bool = False # the widget is following the mouse AND the mouse is in the widget
12
+ self.is_dragged_outside : bool = False # the widget is following the mouse BUT the mouse is NOT in the widget
13
+ super().__init__(*args, **kwargs)
14
+
15
+ def set_click_mask(self,b1=0,b2=0,b3=0,b4=0,b5=0):
16
+ self.click_mask = [b1,b2,b3,b4,b5]
17
+
18
+
19
+ def do_on_drag(
20
+ self, drag_start_pos: tuple[float, float], drag_end_pos: tuple[float, float]
21
+ ) -> None:
22
+
23
+ new_pos = drag_end_pos[0] - self.offset[0], drag_end_pos[1] - self.offset[1]
24
+ if self.rect.topleft != new_pos:
25
+
26
+ self.set_position(*new_pos)
27
+
28
+ def on_click_down(self, button, event=None):
29
+ if button < 1 or button > 5 :
30
+ return
31
+ self.is_clicked_down[button-1] = True
32
+ self.is_dragged = any(i==j and i== True for i,j in zip(self.is_clicked_down,self.click_mask))
33
+ if self.is_dragged:
34
+ event.consumed = True
35
+ self.do_on_click_down(button,event)
36
+
37
+ def on_click_up(self, button, event=None):
38
+ if button < 1 or button > 5 :
39
+ return
40
+ self.is_clicked_down[button-1] = False
41
+ self.is_dragged = any(i==j and i== True for i,j in zip(self.is_clicked_down,self.click_mask))
42
+ self.do_on_click_up(button,event)
43
+
44
+ def update(self, dt: float):
45
+ super().update(dt)
46
+ self.is_dragged_outside = any(i==j and i== True for i,j in zip(pygame.mouse.get_pressed(5),self.click_mask))
47
+
48
+
49
+ if self.is_dragged and self.is_dragged_outside:
50
+ x, y = self.parent_layer.camera.get_mouse_pos()
51
+ if self.drag_start == None:
52
+ self.offset = x - self.rect.x, y - self.rect.y
53
+ self.drag_start = x, y
54
+ else:
55
+ self.do_on_drag(self.drag_start, (x, y))
56
+
57
+ else:
58
+ self.drag_start = None
59
+ self.offset = None
60
+ self.is_clicked_down = [False]*5
61
+
62
+ if not self.is_dragged_outside:
63
+ self.is_dragged = False
batFramework/gui/image.py CHANGED
@@ -1,23 +1,61 @@
1
- import batFramework as bf
2
- from .widget import Widget
3
- import pygame
4
- class Image(Widget):
5
- def __init__(self,data:pygame.Surface|str,size:None|tuple[int,int]=None,convert_alpha=True):
6
- super().__init__(False)
7
- self.surface = None
8
- if isinstance(data,str):
9
- path = bf.utils.get_path(data)
10
- self.original_surface=pygame.image.load(path)
11
- elif isinstance(data,pygame.Surface):
12
- self.original_surface = data
13
-
14
- if convert_alpha: self.original_surface = self.original_surface.convert_alpha()
15
- if not size : size = self.original_surface.get_size()
16
- self.set_size(*size)
17
-
18
-
19
- def build(self)->None:
20
- if not self.surface or self.surface.get_size() != self.get_size_int():
21
- self.surface = pygame.transform.scale(self.original_surface,self.get_size_int())
22
-
23
-
1
+ import batFramework as bf
2
+ from .widget import Widget
3
+ from .shape import Shape
4
+ import pygame
5
+ from typing import Self
6
+
7
+
8
+ class Image(Shape):
9
+ def __init__(
10
+ self,
11
+ path: str = None,
12
+ convert_alpha=True,
13
+ ):
14
+ self.original_surface = None
15
+ super().__init__(convert_alpha=convert_alpha)
16
+ if path is not None:
17
+ self.from_path(path)
18
+
19
+ def __str__(self) -> str:
20
+ return "Image"
21
+
22
+ def paint(self) -> None:
23
+ super().paint()
24
+ if self.original_surface is None:
25
+ return
26
+ padded = self.get_inner_rect().move(-self.rect.x,-self.rect.y)
27
+ target_size = padded.size
28
+ if self.original_surface.get_size() != target_size:
29
+ self.surface.blit(pygame.transform.scale(self.original_surface, target_size), padded.topleft)
30
+ else:
31
+ self.surface.blit(self.original_surface, padded.topleft)
32
+
33
+ def build(self) -> None:
34
+ if self.original_surface is not None:
35
+ self.set_size(
36
+ self.expand_rect_with_padding((0,0,*self.original_surface.get_size())).size
37
+ )
38
+ super().build()
39
+
40
+
41
+ def from_path(self, path: str) -> Self:
42
+ tmp = bf.ResourceManager().get_image(path, self.convert_alpha)
43
+ if tmp is None:
44
+ return self
45
+ self.original_surface = tmp
46
+ # size = self.original_surface.get_size()
47
+ # self.set_size(size)
48
+ self.dirty_shape = True
49
+ self.dirty_surface = True
50
+ return self
51
+
52
+ def from_surface(self, surface: pygame.Surface) -> Self:
53
+ if surface is None:
54
+ return self
55
+ self.original_surface = surface
56
+ # size = self.original_surface.get_size()
57
+ # self.set_size(size)
58
+ self.dirty_shape = True
59
+
60
+ self.dirty_surface = True
61
+ return self