batframework 1.0.8a9__py3-none-any.whl → 1.0.8a11__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.
- batFramework/__init__.py +68 -51
- batFramework/action.py +126 -99
- batFramework/actionContainer.py +53 -9
- batFramework/animatedSprite.py +141 -82
- batFramework/audioManager.py +69 -26
- batFramework/camera.py +259 -69
- batFramework/character.py +27 -0
- batFramework/constants.py +16 -54
- batFramework/cutscene.py +39 -29
- batFramework/cutsceneBlocks.py +36 -43
- batFramework/dynamicEntity.py +18 -9
- batFramework/easingController.py +58 -0
- batFramework/entity.py +48 -97
- batFramework/enums.py +113 -0
- batFramework/fontManager.py +65 -0
- batFramework/gui/__init__.py +10 -2
- batFramework/gui/button.py +9 -78
- batFramework/gui/clickableWidget.py +220 -0
- batFramework/gui/constraints/__init__.py +1 -0
- batFramework/gui/constraints/constraints.py +815 -0
- batFramework/gui/container.py +174 -32
- batFramework/gui/debugger.py +131 -43
- batFramework/gui/dialogueBox.py +99 -0
- batFramework/gui/draggableWidget.py +40 -0
- batFramework/gui/image.py +56 -20
- batFramework/gui/indicator.py +38 -21
- batFramework/gui/interactiveWidget.py +192 -13
- batFramework/gui/label.py +309 -74
- batFramework/gui/layout.py +231 -63
- batFramework/gui/meter.py +74 -0
- batFramework/gui/radioButton.py +84 -0
- batFramework/gui/root.py +134 -38
- batFramework/gui/shape.py +237 -57
- batFramework/gui/slider.py +240 -0
- batFramework/gui/style.py +10 -0
- batFramework/gui/styleManager.py +48 -0
- batFramework/gui/textInput.py +247 -0
- batFramework/gui/toggle.py +101 -51
- batFramework/gui/widget.py +358 -250
- batFramework/manager.py +90 -19
- batFramework/object.py +123 -0
- batFramework/particle.py +115 -0
- batFramework/renderGroup.py +67 -0
- batFramework/resourceManager.py +100 -0
- batFramework/scene.py +281 -123
- batFramework/sceneManager.py +178 -116
- batFramework/scrollingSprite.py +114 -0
- batFramework/sprite.py +51 -0
- batFramework/stateMachine.py +11 -8
- batFramework/templates/__init__.py +2 -0
- batFramework/templates/character.py +44 -0
- batFramework/templates/states.py +166 -0
- batFramework/tileset.py +46 -0
- batFramework/time.py +145 -58
- batFramework/transition.py +195 -124
- batFramework/triggerZone.py +1 -1
- batFramework/utils.py +112 -147
- batframework-1.0.8a11.dist-info/LICENCE +21 -0
- batframework-1.0.8a11.dist-info/METADATA +43 -0
- batframework-1.0.8a11.dist-info/RECORD +62 -0
- {batframework-1.0.8a9.dist-info → batframework-1.0.8a11.dist-info}/WHEEL +1 -1
- batFramework/debugger.py +0 -48
- batFramework/easing.py +0 -71
- batFramework/gui/constraints.py +0 -204
- batFramework/gui/frame.py +0 -19
- batFramework/particles.py +0 -77
- batFramework/transitionManager.py +0 -0
- batframework-1.0.8a9.dist-info/METADATA +0 -53
- batframework-1.0.8a9.dist-info/RECORD +0 -42
- {batframework-1.0.8a9.dist-info → batframework-1.0.8a11.dist-info}/top_level.txt +0 -0
batFramework/gui/widget.py
CHANGED
@@ -1,307 +1,415 @@
|
|
1
|
-
from
|
2
|
-
from
|
3
|
-
if TYPE_CHECKING:
|
4
|
-
from .constraints import Constraint
|
5
|
-
from .root import Root
|
6
|
-
from typing import Self
|
7
|
-
|
1
|
+
from typing import TYPE_CHECKING, Self, Callable
|
2
|
+
from collections.abc import Iterable
|
8
3
|
import batFramework as bf
|
9
4
|
import pygame
|
10
|
-
from math import ceil
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
class Widget(bf.Entity):
|
15
|
-
def __init__(self,convert_alpha=True)->None:
|
16
|
-
super().__init__(convert_alpha=convert_alpha)
|
17
|
-
self.autoresize = False
|
18
|
-
self.parent : None|Self = None
|
19
|
-
self.is_root :bool = False
|
20
|
-
self.children : list["Widget"] = []
|
21
|
-
self.focusable :bool= False
|
22
|
-
self.constraints : list[Constraint] = []
|
23
|
-
self.gui_depth : int = 0
|
24
|
-
if self.surface : self.surface.fill("white")
|
25
|
-
self.set_debug_color("red")
|
26
|
-
self.padding :tuple[float|int,...]= (0,0,0,0)
|
27
|
-
|
28
|
-
def set_padding(self,value : float|int|tuple|list)->Self:
|
29
|
-
old_raw_size = (
|
30
|
-
self.rect.w - self.padding[0] - self.padding[2],
|
31
|
-
self.rect.h - self.padding[1] - self.padding[3]
|
32
|
-
)
|
33
|
-
if isinstance(value,list) or isinstance(value,tuple):
|
34
|
-
if len(value) > 4 : return self
|
35
|
-
if any(v<0 for v in value) : return self
|
36
|
-
if len(value) == 2:
|
37
|
-
self.padding = (value[0],value[1],value[0],value[1])
|
38
|
-
else:
|
39
|
-
self.padding = (*value, *self.padding[len(value):])
|
40
|
-
else:
|
41
|
-
self.padding = (value,)*4
|
42
5
|
|
43
|
-
|
44
|
-
|
45
|
-
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from .constraints.constraints import Constraint
|
8
|
+
from .root import Root
|
9
|
+
MAX_CONSTRAINTS = 10
|
10
|
+
|
11
|
+
|
12
|
+
class WidgetMeta(type):
|
13
|
+
def __call__(cls, *args, **kwargs):
|
14
|
+
"""Called when you call MyNewClass()"""
|
15
|
+
obj = type.__call__(cls, *args, **kwargs)
|
16
|
+
# obj.new_init()
|
17
|
+
bf.StyleManager().register_widget(obj)
|
18
|
+
return obj
|
19
|
+
|
20
|
+
|
21
|
+
class Widget(bf.Entity, metaclass=WidgetMeta):
|
22
|
+
def __init__(self, *args, **kwargs) -> None:
|
23
|
+
super().__init__(*args, **kwargs)
|
24
|
+
self.children: list["Widget"] = []
|
25
|
+
self.constraints: list[Constraint] = []
|
26
|
+
self.parent: "Widget" = None
|
27
|
+
self.do_sort_children = False
|
28
|
+
self.clip_children: bool = True
|
29
|
+
self.padding = (0, 0, 0, 0)
|
30
|
+
self.dirty_surface: bool = True # if true will call paint before drawing
|
31
|
+
self.dirty_shape: bool = (
|
32
|
+
True # if true will call (build+paint) before drawing
|
46
33
|
)
|
34
|
+
self.dirty_constraints: bool = False
|
35
|
+
self.is_root: bool = False
|
36
|
+
self.autoresize_w, self.autoresize_h = True, True
|
37
|
+
self.__constraint_iteration = 0
|
38
|
+
self.__constraints_to_ignore = []
|
39
|
+
self.__constraints_capture = None
|
40
|
+
|
41
|
+
def show(self) -> Self:
|
42
|
+
self.visit(lambda w: w.set_visible(True))
|
47
43
|
return self
|
48
|
-
|
49
|
-
def inflate_rect_by_padding(self,rect:pygame.FRect)->pygame.FRect:
|
50
|
-
return pygame.FRect(
|
51
|
-
rect[0] - self.padding[0],
|
52
|
-
rect[1] - self.padding[1],
|
53
|
-
rect[2] + self.padding[0]+self.padding[2],
|
54
|
-
rect[3] + self.padding[1]+self.padding[3]
|
55
|
-
)
|
56
|
-
def get_content_left(self)->float:
|
57
|
-
return self.rect.left + self.padding[0]
|
58
44
|
|
59
|
-
def
|
60
|
-
|
45
|
+
def hide(self) -> Self:
|
46
|
+
self.visit(lambda w: w.set_visible(False))
|
47
|
+
return self
|
61
48
|
|
62
|
-
def
|
63
|
-
|
49
|
+
def set_clip_children(self, value: bool) -> Self:
|
50
|
+
self.clip_children = value
|
51
|
+
self.dirty_surface = True
|
52
|
+
return self
|
64
53
|
|
65
|
-
def
|
66
|
-
return
|
54
|
+
def __str__(self) -> str:
|
55
|
+
return "Widget"
|
67
56
|
|
68
|
-
def
|
69
|
-
|
57
|
+
def set_autoresize(self, value: bool) -> Self:
|
58
|
+
self.autoresize_w = self.autoresize_h = value
|
59
|
+
self.dirty_shape = True
|
60
|
+
return self
|
70
61
|
|
71
|
-
def
|
72
|
-
|
62
|
+
def set_autoresize_w(self, value: bool) -> Self:
|
63
|
+
self.autoresize_w = value
|
64
|
+
self.dirty_shape = True
|
65
|
+
return self
|
66
|
+
|
67
|
+
def set_autoresize_h(self, value: bool) -> Self:
|
68
|
+
self.autoresize_h = value
|
69
|
+
self.dirty_shape = True
|
70
|
+
return self
|
71
|
+
|
72
|
+
def set_render_order(self, render_order: int) -> Self:
|
73
|
+
super().set_render_order(render_order)
|
74
|
+
if self.parent:
|
75
|
+
self.parent.do_sort_children = True
|
76
|
+
return self
|
73
77
|
|
74
|
-
def
|
78
|
+
def inflate_rect_by_padding(
|
79
|
+
self, rect: pygame.Rect | pygame.FRect
|
80
|
+
) -> pygame.Rect | pygame.FRect:
|
75
81
|
return pygame.FRect(
|
76
|
-
|
77
|
-
|
78
|
-
self.
|
79
|
-
self.
|
82
|
+
rect[0] - self.padding[0],
|
83
|
+
rect[1] - self.padding[1],
|
84
|
+
rect[2] + self.padding[0] + self.padding[2],
|
85
|
+
rect[3] + self.padding[1] + self.padding[3],
|
80
86
|
)
|
81
87
|
|
82
|
-
def
|
83
|
-
|
88
|
+
def set_position(self, x, y) -> Self:
|
89
|
+
if x is None:
|
90
|
+
x = self.rect.x
|
91
|
+
if y is None:
|
92
|
+
y = self.rect.y
|
93
|
+
if (x, y) == self.rect.topleft:
|
94
|
+
return self
|
95
|
+
dx, dy = x - self.rect.x, y - self.rect.y
|
96
|
+
self.rect.topleft = x, y
|
97
|
+
_ = [c.set_position(c.rect.x + dx, c.rect.y + dy) for c in self.children]
|
98
|
+
return self
|
84
99
|
|
85
|
-
def
|
86
|
-
|
100
|
+
def set_center(self, x, y) -> Self:
|
101
|
+
if x is None:
|
102
|
+
x = self.rect.centerx
|
103
|
+
if y is None:
|
104
|
+
y = self.rect.centery
|
105
|
+
if (x, y) == self.rect.center:
|
106
|
+
return self
|
107
|
+
dx, dy = x - self.rect.centerx, y - self.rect.centery
|
108
|
+
self.rect.center = x, y
|
109
|
+
_ = [
|
110
|
+
c.set_center(c.rect.centerx + dx, c.rect.centery + dy)
|
111
|
+
for c in self.children
|
112
|
+
]
|
113
|
+
return self
|
87
114
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
self.gui_depth = self.parent.get_depth() + 1
|
93
|
-
return self.gui_depth
|
115
|
+
def set_parent_scene(self, parent_scene: bf.Scene | None) -> Self:
|
116
|
+
super().set_parent_scene(parent_scene)
|
117
|
+
if parent_scene is None:
|
118
|
+
bf.StyleManager().remove_widget(self)
|
94
119
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
if self.rect.collidepoint(x,y) and self.visible:
|
120
|
+
for c in self.children:
|
121
|
+
c.set_parent_scene(parent_scene)
|
122
|
+
return self
|
123
|
+
|
124
|
+
def set_parent(self, parent: "Widget") -> Self:
|
125
|
+
if parent == self.parent:
|
102
126
|
return self
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
return next((c for c in self.constraints if c.name == name), None)
|
107
|
-
|
108
|
-
def add_constraints(self,*constraints:Constraint)->Self:
|
109
|
-
for c in constraints:
|
110
|
-
self.add_constraint(c,False)
|
111
|
-
self.apply_constraints()
|
127
|
+
# if self.parent is not None and self.parent != parent:
|
128
|
+
# self.parent.remove(self)
|
129
|
+
self.parent = parent
|
112
130
|
return self
|
113
|
-
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
131
|
+
|
132
|
+
def set_padding(self, value: float | int | tuple | list) -> Self:
|
133
|
+
if isinstance(value, Iterable):
|
134
|
+
if len(value) > 4:
|
135
|
+
pass
|
136
|
+
elif any(v < 0 for v in value):
|
137
|
+
pass
|
138
|
+
elif len(value) == 2:
|
139
|
+
self.padding = (value[0], value[1], value[0], value[1])
|
140
|
+
else:
|
141
|
+
self.padding = (*value, *self.padding[len(value) :])
|
142
|
+
else:
|
143
|
+
self.padding = (value,) * 4
|
144
|
+
|
145
|
+
self.dirty_shape = True
|
120
146
|
return self
|
121
147
|
|
122
|
-
def
|
123
|
-
|
148
|
+
def get_padded_rect(self) -> pygame.FRect:
|
149
|
+
r = self.rect.inflate(
|
150
|
+
-self.padding[0] - self.padding[2], -self.padding[1] - self.padding[3]
|
151
|
+
)
|
152
|
+
# r.normalize()
|
153
|
+
return r
|
124
154
|
|
125
|
-
def
|
126
|
-
|
127
|
-
self.apply_constraints()
|
128
|
-
for child in self.children : child.apply_all_constraints()
|
129
|
-
|
130
|
-
def apply_constraints(self, max_iterations: int = 10) -> None:
|
131
|
-
if not self.parent:
|
132
|
-
# print(f"Warning : can't apply constraints on {self.to_string()} without parent widget")
|
133
|
-
return
|
134
|
-
if not self.constraints:
|
135
|
-
return
|
136
|
-
# Sort constraints based on priority
|
137
|
-
self.constraints.sort(key=lambda c: c.priority)
|
155
|
+
def get_min_required_size(self) -> tuple[float, float]:
|
156
|
+
return self.rect.size
|
138
157
|
|
139
|
-
|
140
|
-
|
158
|
+
def get_padded_width(self) -> float:
|
159
|
+
return self.rect.w - self.padding[0] - self.padding[2]
|
141
160
|
|
142
|
-
|
143
|
-
|
144
|
-
unsatisfied.append(constraint)
|
145
|
-
constraint.apply(self.parent, self)
|
146
|
-
if not unsatisfied:
|
147
|
-
# data = ''.join(f"\n\t->{c.to_string()}" for c in self.constraints)
|
148
|
-
# print(self.get_depth()*'\t'+f"Following constraints of {self.to_string()} were all satisfied :{data}")
|
149
|
-
break
|
150
|
-
# print(f"pass {iteration}/{max_iterations} : unsatisfied = {';'.join(c.to_string() for c in unsatisfied)}")
|
151
|
-
if iteration == max_iterations - 1:
|
152
|
-
raise ValueError(f"[WARNING] Following constraints for {self.to_string()} were not satisfied : \n\t{';'.join([c.to_string() for c in unsatisfied])}")
|
161
|
+
def get_padded_height(self) -> float:
|
162
|
+
return self.rect.h - self.padding[1] - self.padding[3]
|
153
163
|
|
154
|
-
|
155
|
-
|
156
|
-
if self.is_root: return self
|
157
|
-
if self.parent_scene is not None : return self.parent_scene.root
|
158
|
-
return None if self.parent is None else self.parent.get_root()
|
164
|
+
def get_padded_left(self) -> float:
|
165
|
+
return self.rect.left + self.padding[0]
|
159
166
|
|
160
|
-
def
|
161
|
-
return
|
167
|
+
def get_padded_right(self) -> float:
|
168
|
+
return self.rect.right + self.padding[2]
|
162
169
|
|
170
|
+
def get_padded_center(self) -> tuple[float, float]:
|
171
|
+
return self.get_padded_rect().center
|
163
172
|
|
164
|
-
def
|
165
|
-
return self.rect.
|
173
|
+
def get_padded_top(self) -> float:
|
174
|
+
return self.rect.y + self.padding[1]
|
166
175
|
|
176
|
+
def get_padded_bottom(self) -> float:
|
177
|
+
return self.rect.bottom - self.padding[3]
|
167
178
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
179
|
+
def get_debug_outlines(self):
|
180
|
+
if not self.visible:
|
181
|
+
return
|
182
|
+
yield (self.rect, self.debug_color)
|
183
|
+
if any(self.padding):
|
184
|
+
yield (self.get_padded_rect(), self.debug_color)
|
171
185
|
for child in self.children:
|
172
|
-
yield from child.
|
186
|
+
yield from child.get_debug_outlines()
|
187
|
+
|
188
|
+
def add_constraints(self, *constraints: "Constraint") -> Self:
|
189
|
+
self.constraints.extend(constraints)
|
190
|
+
seen = set()
|
191
|
+
result = []
|
192
|
+
for c in self.constraints:
|
193
|
+
if not any(c == o for o in seen):
|
194
|
+
result.append(c)
|
195
|
+
seen.add(c.name)
|
196
|
+
self.constraints = result
|
197
|
+
self.constraints.sort(key=lambda c: c.priority)
|
198
|
+
self.dirty_constraints = True
|
199
|
+
self.__constraint_to_ignore = []
|
173
200
|
|
174
|
-
def set_autoresize(self,value:bool)-> Self:
|
175
|
-
self.autoresize = value
|
176
|
-
self.build()
|
177
201
|
return self
|
178
202
|
|
179
|
-
def set_parent(self,parent:Self|None)->None:
|
180
|
-
if self.parent:
|
181
|
-
self.parent.remove_child(self)
|
182
|
-
self.parent = parent
|
183
|
-
self.apply_all_constraints()
|
184
|
-
# SETTERS
|
185
|
-
|
186
|
-
def set_root(self) -> Self:
|
187
|
-
self.is_root = True
|
188
|
-
return self
|
189
|
-
|
190
|
-
def set_parent_scene(self,scene)->None:
|
191
|
-
super().set_parent_scene(scene)
|
192
|
-
for child in self.children :
|
193
|
-
child.set_parent_scene(scene)
|
194
203
|
|
195
|
-
def
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
self.
|
204
|
+
def remove_constraints(self, *names: str) -> Self:
|
205
|
+
for c in self.constraints:
|
206
|
+
if c.name in names:
|
207
|
+
c.on_removal(self)
|
208
|
+
self.constraints = [c for c in self.constraints if c.name not in names]
|
209
|
+
self.__constraint_to_ignore = []
|
201
210
|
return self
|
202
211
|
|
203
|
-
def
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
child.set_y(child.rect.y + delta)
|
208
|
-
self.apply_constraints()
|
209
|
-
|
210
|
-
return self
|
212
|
+
def resolve_constraints(self) -> None:
|
213
|
+
if self.parent is None or not self.constraints:
|
214
|
+
self.dirty_constraints = False
|
215
|
+
return
|
211
216
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
217
|
+
if not self.__constraint_iteration:
|
218
|
+
self.__constraints_capture = None
|
219
|
+
else:
|
220
|
+
capture = tuple([c.priority for c in self.constraints])
|
221
|
+
if capture != self.__constraints_capture:
|
222
|
+
self.__constraints_capture = capture
|
223
|
+
self.__constraint_to_ignore = []
|
224
|
+
|
225
|
+
constraints = self.constraints.copy()
|
226
|
+
# If all are resolved early exit
|
227
|
+
if all(c.evaluate(self.parent,self) for c in constraints if c not in self.__constraint_to_ignore):
|
228
|
+
self.dirty_constraints = False
|
229
|
+
return
|
220
230
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
231
|
+
# # Here there might be a conflict between 2 or more constraints
|
232
|
+
# we have to determine which ones causes conflict and ignore the one with least priority
|
233
|
+
|
234
|
+
stop = False
|
235
|
+
|
236
|
+
while True:
|
237
|
+
stop = True
|
238
|
+
# first pass with 2 iterations to sort out the transformative constraints
|
239
|
+
for _ in range(2):
|
240
|
+
for c in constraints:
|
241
|
+
if c in self.__constraints_to_ignore:continue
|
242
|
+
if not c.evaluate(self.parent,self) :
|
243
|
+
c.apply(self.parent,self)
|
244
|
+
# second pass where we check conflicts
|
245
|
+
for c in constraints:
|
246
|
+
if c in self.__constraints_to_ignore:
|
247
|
+
continue
|
248
|
+
if not c.evaluate(self.parent,self):
|
249
|
+
# first pass invalidated this constraint
|
250
|
+
self.__constraints_to_ignore.append(c)
|
251
|
+
stop = False
|
252
|
+
break
|
253
|
+
|
254
|
+
if stop:
|
255
|
+
break
|
230
256
|
|
231
|
-
|
232
|
-
|
233
|
-
self.build()
|
234
|
-
return self
|
257
|
+
if self.__constraints_to_ignore:
|
258
|
+
print("Constraints ignored : ",[str(c) for c in self.__constraints_to_ignore])
|
235
259
|
|
236
260
|
|
237
|
-
|
261
|
+
self.dirty_constraints = False
|
238
262
|
|
239
|
-
def
|
240
|
-
|
241
|
-
for child in self.children :
|
242
|
-
child.print_tree(ident+1)
|
263
|
+
def has_constraint(self, name: str) -> bool:
|
264
|
+
return any(c.name == name for c in self.constraints)
|
243
265
|
|
244
|
-
def
|
245
|
-
|
246
|
-
|
266
|
+
def get_root(self) -> "Root":
|
267
|
+
if self.is_root:
|
268
|
+
return self
|
269
|
+
if self.parent:
|
270
|
+
return self.parent.get_root()
|
271
|
+
return None
|
247
272
|
|
273
|
+
def top_at(self, x: float | int, y: float | int) -> "None|Widget":
|
274
|
+
if self.children:
|
275
|
+
for child in reversed(self.children):
|
276
|
+
if child.visible:
|
277
|
+
r = child.top_at(x, y)
|
278
|
+
if r is not None:
|
279
|
+
return r
|
280
|
+
return self if self.visible and self.rect.collidepoint(x, y) else None
|
281
|
+
|
282
|
+
def add(self, *children: "Widget") -> Self:
|
283
|
+
self.children.extend(children)
|
284
|
+
i = len(self.children)
|
285
|
+
for child in children:
|
286
|
+
|
287
|
+
child.set_render_order(i).set_parent(self).set_parent_scene(
|
288
|
+
self.parent_scene
|
289
|
+
)
|
290
|
+
i += 1
|
291
|
+
if self.parent:
|
292
|
+
self.parent.do_sort_children = True
|
293
|
+
return self
|
248
294
|
|
249
|
-
def
|
250
|
-
|
295
|
+
def remove(self, *children: "Widget") -> Self:
|
296
|
+
for child in self.children:
|
297
|
+
if child in children:
|
298
|
+
child.set_parent(None).set_parent_scene(None)
|
299
|
+
self.children.remove(child)
|
300
|
+
if self.parent:
|
301
|
+
self.parent.do_sort_children = True
|
251
302
|
|
303
|
+
def set_size_if_autoresize(self, size: tuple[float, float]) -> Self:
|
304
|
+
size = list(size)
|
305
|
+
size[0] = size[0] if self.autoresize_w else None
|
306
|
+
size[1] = size[1] if self.autoresize_h else None
|
307
|
+
self.set_size(size)
|
308
|
+
return self
|
252
309
|
|
253
|
-
def
|
254
|
-
|
255
|
-
|
310
|
+
def set_size(self, size: tuple) -> Self:
|
311
|
+
size = list(size)
|
312
|
+
if size[0] is None:
|
313
|
+
size[0] = self.rect.w
|
314
|
+
if size[1] is None:
|
315
|
+
size[1] = self.rect.h
|
316
|
+
if size == self.rect.size:
|
317
|
+
return self
|
318
|
+
self.rect.size = size
|
319
|
+
self.dirty_shape = True
|
320
|
+
return self
|
256
321
|
|
257
|
-
|
322
|
+
def process_event(self, event: pygame.Event) -> bool:
|
323
|
+
# First propagate to children
|
324
|
+
for child in self.children:
|
325
|
+
child.process_event(event)
|
326
|
+
# return True if the method is blocking (no propagation to next children of the scene)
|
327
|
+
super().process_event(event)
|
328
|
+
|
329
|
+
def update(self, dt) -> None:
|
330
|
+
if self.do_sort_children:
|
331
|
+
self.children.sort(key=lambda c: c.render_order)
|
332
|
+
self.do_sort_children = False
|
333
|
+
_ = [c.update(dt) for c in self.children]
|
334
|
+
super().update(dt)
|
335
|
+
|
336
|
+
def build(self) -> None:
|
337
|
+
new_size = tuple(map(int, self.rect.size))
|
338
|
+
if self.surface.get_size() != new_size:
|
339
|
+
old_alpha = self.surface.get_alpha()
|
340
|
+
new_size = [max(0, i) for i in new_size]
|
341
|
+
self.surface = pygame.Surface(new_size, self.surface_flags)
|
342
|
+
if self.convert_alpha:
|
343
|
+
self.surface = self.surface.convert_alpha()
|
344
|
+
self.surface.set_alpha(old_alpha)
|
345
|
+
|
346
|
+
def paint(self) -> None:
|
347
|
+
self.surface.fill((0, 0, 0, 0))
|
348
|
+
return self
|
258
349
|
|
259
|
-
def
|
260
|
-
|
261
|
-
self
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
350
|
+
def visit(self, func: Callable, top_down: bool = True, *args, **kwargs) -> None:
|
351
|
+
if top_down:
|
352
|
+
func(self, *args, **kwargs)
|
353
|
+
for child in self.children:
|
354
|
+
child.visit(func, top_down)
|
355
|
+
if not top_down:
|
356
|
+
func(self, *args, **kwargs)
|
266
357
|
|
267
|
-
def
|
268
|
-
self
|
269
|
-
|
270
|
-
|
271
|
-
|
358
|
+
def visit_up(self, func, *args, **kwargs) -> None:
|
359
|
+
if func(self, *args, **kwargs):
|
360
|
+
return
|
361
|
+
if self.parent:
|
362
|
+
self.parent.visit_up(func, *args, **kwargs)
|
363
|
+
|
364
|
+
def selective_up(self, widget: "Widget"):
|
365
|
+
# if returns True then stop climbing widget tree
|
366
|
+
if widget.parent and widget.parent.dirty_constraints:
|
367
|
+
return False
|
368
|
+
widget.visit(self.selective_down)
|
369
|
+
return True
|
370
|
+
|
371
|
+
def selective_down(self, widget: "Widget"):
|
372
|
+
if widget.constraints:
|
373
|
+
widget.resolve_constraints()
|
374
|
+
else:
|
375
|
+
widget.dirty_constraints = False
|
376
|
+
if widget.dirty_shape:
|
377
|
+
widget.build()
|
378
|
+
widget.dirty_shape = False
|
379
|
+
self.dirty_surface = True
|
380
|
+
|
381
|
+
def draw(self, camera: bf.Camera) -> None:
|
382
|
+
if self.dirty_shape:
|
383
|
+
self.dirty_constraints = True
|
384
|
+
self.dirty_surface = True
|
385
|
+
self.build()
|
386
|
+
self.dirty_shape = False
|
387
|
+
for child in self.children:
|
388
|
+
child.dirty_constraints = True
|
389
|
+
|
390
|
+
if self.dirty_constraints:
|
391
|
+
if self.parent and self.parent.dirty_constraints:
|
392
|
+
self.parent.visit_up(self.selective_up)
|
393
|
+
else:
|
394
|
+
self.visit(lambda c: c.resolve_constraints())
|
272
395
|
|
396
|
+
if self.dirty_surface:
|
397
|
+
self.paint()
|
398
|
+
self.dirty_surface = False
|
273
399
|
|
400
|
+
super().draw(camera)
|
274
401
|
|
402
|
+
if self.clip_children:
|
275
403
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
if child.process_event(event):
|
281
|
-
return True
|
282
|
-
#return True if the method is blocking (no propagation to next children of the scene)
|
283
|
-
return super().process_event(event)
|
404
|
+
new_clip = camera.world_to_screen(self.get_padded_rect())
|
405
|
+
old_clip = camera.surface.get_clip()
|
406
|
+
new_clip = new_clip.clip(old_clip)
|
407
|
+
camera.surface.set_clip(new_clip)
|
284
408
|
|
409
|
+
_ = [
|
410
|
+
child.draw(camera)
|
411
|
+
for child in sorted(self.children, key=lambda c: c.render_order)
|
412
|
+
]
|
285
413
|
|
286
|
-
|
287
|
-
|
288
|
-
child.update(dt)
|
289
|
-
|
290
|
-
def draw(self, camera: bf.Camera) -> int:
|
291
|
-
self.children.sort(key=lambda e: (e.z_depth,e.render_order))
|
292
|
-
return super().draw(camera) + sum([child.draw(camera) for child in self.children])
|
293
|
-
|
294
|
-
def build(self)->None:
|
295
|
-
"""
|
296
|
-
This function is called each time the widget's surface has to be updated
|
297
|
-
It usually has to be overriden if inherited to suit the needs of the new class
|
298
|
-
"""
|
299
|
-
if not self.surface: return
|
300
|
-
if self.surface.get_size() != self.get_size_int():
|
301
|
-
self.surface = pygame.Surface(self.get_size_int())
|
302
|
-
if self.parent : self.parent.children_modified()
|
303
|
-
|
304
|
-
def children_modified(self)->None:
|
305
|
-
self.apply_constraints()
|
306
|
-
if self.parent and not self.is_root:
|
307
|
-
self.parent.children_modified()
|
414
|
+
if self.clip_children:
|
415
|
+
camera.surface.set_clip(old_clip)
|