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.
- batFramework/__init__.py +20 -11
- batFramework/action.py +1 -1
- batFramework/animatedSprite.py +47 -116
- batFramework/animation.py +30 -5
- batFramework/audioManager.py +8 -5
- batFramework/baseScene.py +240 -0
- batFramework/camera.py +4 -0
- batFramework/constants.py +6 -2
- batFramework/cutscene.py +221 -21
- batFramework/cutsceneManager.py +5 -2
- batFramework/drawable.py +7 -5
- batFramework/easingController.py +10 -11
- batFramework/entity.py +21 -2
- batFramework/enums.py +48 -33
- batFramework/gui/__init__.py +6 -3
- batFramework/gui/animatedLabel.py +10 -2
- batFramework/gui/button.py +4 -31
- batFramework/gui/clickableWidget.py +63 -50
- batFramework/gui/constraints/constraints.py +212 -136
- batFramework/gui/container.py +77 -58
- batFramework/gui/debugger.py +12 -17
- batFramework/gui/draggableWidget.py +21 -17
- batFramework/gui/image.py +3 -10
- batFramework/gui/indicator.py +56 -1
- batFramework/gui/interactiveWidget.py +127 -108
- batFramework/gui/label.py +73 -64
- batFramework/gui/layout.py +286 -445
- batFramework/gui/meter.py +42 -20
- batFramework/gui/radioButton.py +20 -69
- batFramework/gui/root.py +99 -29
- batFramework/gui/selector.py +250 -0
- batFramework/gui/shape.py +13 -5
- batFramework/gui/slider.py +262 -107
- batFramework/gui/syncedVar.py +49 -0
- batFramework/gui/textInput.py +46 -22
- batFramework/gui/toggle.py +70 -52
- batFramework/gui/tooltip.py +30 -0
- batFramework/gui/widget.py +222 -135
- batFramework/manager.py +7 -8
- batFramework/particle.py +4 -1
- batFramework/propertyEaser.py +79 -0
- batFramework/renderGroup.py +17 -50
- batFramework/resourceManager.py +43 -13
- batFramework/scene.py +15 -335
- batFramework/sceneLayer.py +138 -0
- batFramework/sceneManager.py +31 -36
- batFramework/scrollingSprite.py +8 -3
- batFramework/sprite.py +1 -1
- batFramework/templates/__init__.py +1 -2
- batFramework/templates/controller.py +97 -0
- batFramework/timeManager.py +76 -22
- batFramework/transition.py +37 -103
- batFramework/utils.py +125 -66
- {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/METADATA +24 -3
- batframework-1.0.9a9.dist-info/RECORD +67 -0
- {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/WHEEL +1 -1
- batFramework/character.py +0 -27
- batFramework/templates/character.py +0 -43
- batFramework/templates/states.py +0 -166
- batframework-1.0.9a7.dist-info/RECORD +0 -63
- /batframework-1.0.9a7.dist-info/LICENCE → /batframework-1.0.9a9.dist-info/LICENSE +0 -0
- {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/top_level.txt +0 -0
batFramework/gui/layout.py
CHANGED
@@ -4,6 +4,7 @@ from .constraints.constraints import *
|
|
4
4
|
from typing import Self, TYPE_CHECKING
|
5
5
|
from abc import ABC,abstractmethod
|
6
6
|
import pygame
|
7
|
+
from .interactiveWidget import InteractiveWidget
|
7
8
|
|
8
9
|
if TYPE_CHECKING:
|
9
10
|
from .container import Container
|
@@ -15,8 +16,15 @@ class Layout(ABC):
|
|
15
16
|
self.child_constraints: list[Constraint] = []
|
16
17
|
self.children_rect = pygame.FRect(0, 0, 0, 0)
|
17
18
|
|
19
|
+
def get_free_space(self)->tuple[float,float]:
|
20
|
+
"""
|
21
|
+
return the space available for Growing widgets to use
|
22
|
+
"""
|
23
|
+
return self.parent.get_inner_rect()
|
24
|
+
|
18
25
|
def set_child_constraints(self, *constraints) -> Self:
|
19
26
|
self.child_constraints = list(constraints)
|
27
|
+
self.update_child_constraints()
|
20
28
|
self.notify_parent()
|
21
29
|
return self
|
22
30
|
|
@@ -28,14 +36,35 @@ class Layout(ABC):
|
|
28
36
|
if self.parent:
|
29
37
|
self.parent.dirty_layout = True
|
30
38
|
|
39
|
+
def update_children_rect(self):
|
40
|
+
if self.parent.get_layout_children():
|
41
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft,*self.parent.get_layout_children()[0].get_min_required_size())
|
42
|
+
|
43
|
+
self.children_rect.unionall(
|
44
|
+
[pygame.FRect(0,0,*c.get_min_required_size()) for c in self.parent.get_layout_children()[1:]]
|
45
|
+
)
|
46
|
+
else:
|
47
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, 0, 0)
|
48
|
+
self.children_rect.move_ip(-self.parent.scroll.x,-self.parent.scroll.y)
|
49
|
+
|
50
|
+
def update_child_constraints(self):
|
51
|
+
if self.parent:
|
52
|
+
for child in self.parent.get_layout_children():
|
53
|
+
child.add_constraints(*self.child_constraints)
|
54
|
+
|
31
55
|
def arrange(self) -> None:
|
56
|
+
"""
|
57
|
+
updates the position of children in the parent
|
58
|
+
"""
|
32
59
|
return
|
33
60
|
|
34
|
-
def get_raw_size(self)
|
61
|
+
def get_raw_size(self):
|
35
62
|
"""
|
36
|
-
Returns the
|
63
|
+
Returns the size the container should have to encapsulate perfectly all of its widgets
|
37
64
|
"""
|
38
|
-
|
65
|
+
# print(self,self.parent,len(self.parent.get_layout_children()))
|
66
|
+
self.update_children_rect()
|
67
|
+
return self.children_rect.size
|
39
68
|
|
40
69
|
def get_auto_size(self) -> tuple[float, float]:
|
41
70
|
"""
|
@@ -43,342 +72,229 @@ class Layout(ABC):
|
|
43
72
|
"""
|
44
73
|
target_size = list(self.get_raw_size())
|
45
74
|
if not self.parent.autoresize_w:
|
46
|
-
target_size[0] = self.parent.
|
75
|
+
target_size[0] = self.parent.get_inner_width()
|
47
76
|
if not self.parent.autoresize_h:
|
48
|
-
target_size[1] = self.parent.
|
49
|
-
|
77
|
+
target_size[1] = self.parent.get_inner_height()
|
78
|
+
|
79
|
+
return self.parent.expand_rect_with_padding((0,0,*target_size)).size
|
80
|
+
# return target_size
|
81
|
+
|
82
|
+
def scroll_to_widget(self, widget: "Widget"):
|
83
|
+
"""
|
84
|
+
Scrolls parent containers so that the widget becomes visible.
|
85
|
+
Handles deeply nested widgets and large widgets gracefully.
|
86
|
+
"""
|
87
|
+
target = widget
|
88
|
+
container = self.parent
|
89
|
+
|
90
|
+
while container and not container.is_root:
|
91
|
+
target_rect = target.rect # global
|
92
|
+
padded_rect = container.get_inner_rect() # global
|
93
|
+
|
94
|
+
dx = dy = 0
|
95
|
+
|
96
|
+
# --- Horizontal ---
|
97
|
+
if target_rect.width <= padded_rect.width:
|
98
|
+
if target_rect.left < padded_rect.left:
|
99
|
+
dx = target_rect.left - padded_rect.left
|
100
|
+
elif target_rect.right > padded_rect.right:
|
101
|
+
dx = target_rect.right - padded_rect.right
|
102
|
+
else:
|
103
|
+
# Widget is wider than viewport: align left side
|
104
|
+
if target_rect.left < padded_rect.left:
|
105
|
+
dx = target_rect.left - padded_rect.left
|
106
|
+
elif target_rect.right > padded_rect.right:
|
107
|
+
dx = target_rect.right - padded_rect.right
|
108
|
+
|
109
|
+
# --- Vertical ---
|
110
|
+
if target_rect.height <= padded_rect.height:
|
111
|
+
if target_rect.top < padded_rect.top:
|
112
|
+
dy = target_rect.top - padded_rect.top
|
113
|
+
elif target_rect.bottom > padded_rect.bottom:
|
114
|
+
dy = target_rect.bottom - padded_rect.bottom
|
115
|
+
else:
|
116
|
+
# Widget is taller than viewport: align top side
|
117
|
+
if target_rect.top < padded_rect.top:
|
118
|
+
dy = target_rect.top - padded_rect.top
|
119
|
+
elif target_rect.bottom > padded_rect.bottom:
|
120
|
+
dy = target_rect.bottom - padded_rect.bottom
|
121
|
+
|
122
|
+
# Convert global delta into local scroll delta for container
|
123
|
+
container.scroll_by((dx, dy))
|
124
|
+
|
125
|
+
# Now the target for the next iteration is the container itself
|
126
|
+
target = container
|
127
|
+
container = container.parent
|
50
128
|
|
51
|
-
def scroll_to_widget(self, widget: Widget) -> None:
|
52
|
-
padded = self.parent.get_padded_rect() # le carré intérieur
|
53
|
-
r = widget.rect # le carré du widget = Button
|
54
|
-
if padded.contains(r): # le widget ne depasse pas -> OK
|
55
|
-
return
|
56
|
-
clamped = r.clamp(padded)
|
57
|
-
# clamped.move_ip(-self.parent.rect.x,-self.parent.rect.y)
|
58
|
-
dx,dy = clamped.x - r.x,clamped.y-r.y
|
59
129
|
|
60
|
-
self.parent.scroll_by((-dx, -dy)) # on scroll la différence pour afficher le bouton en entier
|
61
|
-
|
62
130
|
def handle_event(self, event):
|
63
131
|
pass
|
64
132
|
|
133
|
+
class FreeLayout(Layout):...
|
134
|
+
|
65
135
|
class SingleAxisLayout(Layout):
|
136
|
+
|
137
|
+
def __init__(self, parent = None):
|
138
|
+
super().__init__(parent)
|
139
|
+
|
66
140
|
def focus_next_child(self) -> None:
|
67
141
|
l = self.parent.get_interactive_children()
|
68
142
|
self.parent.focused_index = min(self.parent.focused_index + 1, len(l) - 1)
|
69
143
|
focused = l[self.parent.focused_index]
|
70
144
|
focused.get_focus()
|
71
|
-
self.scroll_to_widget(focused)
|
72
145
|
|
73
146
|
def focus_prev_child(self) -> None:
|
74
147
|
l = self.parent.get_interactive_children()
|
75
148
|
self.parent.focused_index = max(self.parent.focused_index - 1, 0)
|
76
149
|
focused = l[self.parent.focused_index]
|
77
150
|
focused.get_focus()
|
78
|
-
self.scroll_to_widget(focused)
|
79
151
|
|
80
152
|
|
153
|
+
class DoubleAxisLayout(Layout):
|
154
|
+
"""Abstract layout class for layouts that arrange widgets in two dimensions."""
|
155
|
+
|
156
|
+
def focus_up_child(self) -> None:...
|
157
|
+
def focus_down_child(self) -> None:...
|
158
|
+
def focus_right_child(self) -> None:...
|
159
|
+
def focus_left_child(self) -> None:...
|
160
|
+
|
81
161
|
|
82
162
|
class Column(SingleAxisLayout):
|
83
|
-
def __init__(self, gap: int = 0
|
163
|
+
def __init__(self, gap: int = 0):
|
84
164
|
super().__init__()
|
85
165
|
self.gap = gap
|
86
|
-
self.spacing = spacing
|
87
166
|
|
88
|
-
def
|
89
|
-
self.
|
90
|
-
|
91
|
-
|
167
|
+
def update_children_rect(self):
|
168
|
+
if self.parent.get_layout_children():
|
169
|
+
width = max(child.get_min_required_size()[0] for child in self.parent.get_layout_children() )
|
170
|
+
height = sum(child.get_min_required_size()[1] for child in self.parent.get_layout_children()) + self.gap * (len(self.parent.get_layout_children()) - 1)
|
92
171
|
|
93
|
-
|
94
|
-
|
95
|
-
self.notify_parent()
|
96
|
-
return self
|
97
|
-
|
98
|
-
def get_raw_size(self) -> tuple[float, float]:
|
99
|
-
len_children = len(self.parent.children)
|
100
|
-
if not len_children:
|
101
|
-
return self.parent.rect.size
|
102
|
-
parent_height = sum(c.get_min_required_size()[1] for c in self.parent.children)
|
103
|
-
parent_width = max(c.get_min_required_size()[0] for c in self.parent.children)
|
104
|
-
if self.gap:
|
105
|
-
parent_height += (len_children - 1) * self.gap
|
106
|
-
target_rect = self.parent.inflate_rect_by_padding(
|
107
|
-
(0, 0, parent_width, parent_height)
|
108
|
-
)
|
109
|
-
return target_rect.size
|
110
|
-
|
111
|
-
def get_auto_size(self) -> tuple[float, float]:
|
112
|
-
target_size = list(self.get_raw_size())
|
113
|
-
if not self.parent.autoresize_w:
|
114
|
-
target_size[0] = self.parent.rect.w
|
115
|
-
if not self.parent.autoresize_h:
|
116
|
-
target_size[1] = self.parent.rect.h
|
117
|
-
return target_size
|
172
|
+
# width = max(child.rect.w for child in self.parent.get_layout_children() )
|
173
|
+
# height = sum(child.rect.h for child in self.parent.get_layout_children()) + self.gap * (len(self.parent.get_layout_children()) - 1)
|
118
174
|
|
119
|
-
def arrange(self) -> None:
|
120
|
-
if not self.parent or not self.parent.children:
|
121
|
-
return
|
122
|
-
if self.child_constraints:
|
123
|
-
for child in self.parent.children:
|
124
|
-
child.add_constraints(*self.child_constraints)
|
125
|
-
self.children_rect = self.parent.get_padded_rect()
|
126
175
|
|
127
|
-
|
128
|
-
|
129
|
-
self.parent.
|
130
|
-
|
131
|
-
self.parent.set_size((None,height))
|
132
|
-
|
133
|
-
# if self.parent.dirty_shape:
|
134
|
-
# print("parent set dirty shape")
|
135
|
-
# self.parent.dirty_layout = True
|
136
|
-
# self.parent.apply_updates()
|
137
|
-
# self.arrange()
|
138
|
-
# return
|
139
|
-
|
140
|
-
self.children_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
141
|
-
y = self.children_rect.top
|
142
|
-
for child in self.parent.children:
|
143
|
-
child.set_position(self.children_rect.x, y)
|
144
|
-
y += child.get_min_required_size()[1] + self.gap
|
176
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
177
|
+
else:
|
178
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, 10, 10)
|
179
|
+
self.children_rect.move_ip(-self.parent.scroll.x,-self.parent.scroll.y)
|
145
180
|
|
146
181
|
def handle_event(self, event):
|
147
|
-
if not self.parent.
|
182
|
+
if not self.parent.get_layout_children() or not self.parent.children_has_focus():
|
148
183
|
return
|
149
184
|
|
150
185
|
if event.type == pygame.KEYDOWN:
|
151
|
-
if event.key
|
152
|
-
self.focus_next_child()
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
r.drawing_camera.screen_to_world(pygame.mouse.get_pos())
|
164
|
-
):
|
165
|
-
if event.button == 4:
|
166
|
-
self.parent.scroll_by((0, -10))
|
167
|
-
elif event.button == 5:
|
168
|
-
self.parent.scroll_by((0, 10))
|
169
|
-
else:
|
170
|
-
return
|
171
|
-
self.parent.clamp_scroll()
|
172
|
-
else:
|
173
|
-
return
|
174
|
-
else:
|
175
|
-
return
|
176
|
-
event.consumed = True
|
186
|
+
if event.key in (pygame.K_DOWN, pygame.K_UP):
|
187
|
+
self.focus_next_child() if event.key == pygame.K_DOWN else self.focus_prev_child()
|
188
|
+
event.consumed = True
|
189
|
+
event.consumed = True
|
190
|
+
|
191
|
+
|
192
|
+
def arrange(self) -> None:
|
193
|
+
self.update_children_rect()
|
194
|
+
y = self.children_rect.y
|
195
|
+
for child in self.parent.get_layout_children():
|
196
|
+
child.set_position(self.children_rect.x, y)
|
197
|
+
y += child.rect.height + self.gap
|
177
198
|
|
178
199
|
class Row(SingleAxisLayout):
|
179
|
-
def __init__(self, gap: int = 0
|
200
|
+
def __init__(self, gap: int = 0):
|
180
201
|
super().__init__()
|
181
202
|
self.gap = gap
|
182
|
-
self.spacing = spacing
|
183
203
|
|
184
|
-
def
|
185
|
-
self.
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
self.
|
192
|
-
return self
|
193
|
-
|
194
|
-
def get_raw_size(self) -> tuple[float, float]:
|
195
|
-
len_children = len(self.parent.children)
|
196
|
-
if not len_children:
|
197
|
-
return self.parent.rect.size
|
198
|
-
parent_width = sum(c.get_min_required_size()[0] for c in self.parent.children)
|
199
|
-
parent_height = max(c.get_min_required_size()[1] for c in self.parent.children)
|
200
|
-
if self.gap:
|
201
|
-
parent_width += (len_children - 1) * self.gap
|
202
|
-
target_rect = self.parent.inflate_rect_by_padding(
|
203
|
-
(0, 0, parent_width, parent_height)
|
204
|
-
)
|
205
|
-
|
206
|
-
return target_rect.size
|
207
|
-
|
208
|
-
def get_auto_size(self) -> tuple[float, float]:
|
209
|
-
target_size = list(self.get_raw_size())
|
210
|
-
if not self.parent.autoresize_w:
|
211
|
-
target_size[0] = self.parent.rect.w
|
212
|
-
if not self.parent.autoresize_h:
|
213
|
-
target_size[1] = self.parent.rect.h
|
214
|
-
return target_size
|
215
|
-
|
216
|
-
def arrange(self) -> None:
|
217
|
-
if not self.parent or not self.parent.children:
|
218
|
-
return
|
219
|
-
if self.child_constraints:
|
220
|
-
for child in self.parent.children:
|
221
|
-
child.add_constraints(*self.child_constraints)
|
222
|
-
self.children_rect = self.parent.get_padded_rect()
|
223
|
-
|
224
|
-
if self.parent.autoresize_w or self.parent.autoresize_h:
|
225
|
-
width, height = self.get_auto_size()
|
226
|
-
if self.parent.rect.size != (width, height):
|
227
|
-
self.parent.set_size((width, height))
|
228
|
-
self.parent.build()
|
229
|
-
self.arrange()
|
230
|
-
return
|
231
|
-
self.children_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
232
|
-
x = self.children_rect.left
|
233
|
-
for child in self.parent.children:
|
234
|
-
child.set_position(x, self.children_rect.y)
|
235
|
-
x += child.get_min_required_size()[0] + self.gap
|
204
|
+
def update_children_rect(self):
|
205
|
+
if self.parent.get_layout_children():
|
206
|
+
height = max(child.get_min_required_size()[1] for child in self.parent.get_layout_children())
|
207
|
+
width = sum(child.get_min_required_size()[0] for child in self.parent.get_layout_children()) + self.gap * (len(self.parent.get_layout_children()) - 1)
|
208
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
209
|
+
else:
|
210
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, 10,10)
|
211
|
+
self.children_rect.move_ip(-self.parent.scroll.x,-self.parent.scroll.y)
|
236
212
|
|
237
213
|
def handle_event(self, event):
|
238
|
-
if not self.parent.
|
214
|
+
if not self.parent.get_layout_children() or not self.parent.children_has_focus():
|
239
215
|
return
|
240
216
|
|
241
217
|
if event.type == pygame.KEYDOWN:
|
242
|
-
if event.key
|
243
|
-
self.focus_next_child()
|
244
|
-
|
245
|
-
self.focus_prev_child()
|
246
|
-
else:
|
247
|
-
return
|
248
|
-
|
249
|
-
elif event.type == pygame.MOUSEBUTTONDOWN:
|
250
|
-
r = self.parent.get_root()
|
251
|
-
if not r:
|
252
|
-
return
|
253
|
-
if self.parent.rect.collidepoint(
|
254
|
-
r.drawing_camera.screen_to_world(pygame.mouse.get_pos())
|
255
|
-
):
|
256
|
-
if event.button == 4:
|
257
|
-
self.parent.scroll_by((-10, 0))
|
258
|
-
elif event.button == 5:
|
259
|
-
self.parent.scroll_by((10, 0))
|
260
|
-
else:
|
261
|
-
return
|
262
|
-
self.parent.clamp_scroll()
|
263
|
-
else:
|
264
|
-
return
|
265
|
-
else:
|
266
|
-
return
|
218
|
+
if event.key in (pygame.K_RIGHT, pygame.K_LEFT):
|
219
|
+
self.focus_next_child() if event.key == pygame.K_RIGHT else self.focus_prev_child()
|
220
|
+
event.consumed = True
|
267
221
|
|
268
222
|
|
269
|
-
event.consumed = True
|
270
|
-
|
271
|
-
class RowFill(Row):
|
272
|
-
def __init__(self, gap: int = 0, spacing: bf.spacing = bf.spacing.MANUAL):
|
273
|
-
super().__init__(gap, spacing)
|
274
|
-
|
275
223
|
def arrange(self) -> None:
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
if self.child_constraints:
|
283
|
-
for child in self.parent.children:
|
284
|
-
child.add_constraints(*self.child_constraints)
|
285
|
-
self.children_rect = self.parent.get_padded_rect()
|
224
|
+
self.update_children_rect()
|
225
|
+
x = self.children_rect.x
|
226
|
+
for child in self.parent.get_layout_children():
|
227
|
+
child.set_position(x,self.children_rect.y)
|
228
|
+
x += child.rect.width + self.gap
|
286
229
|
|
287
|
-
if self.parent.autoresize_w or self.parent.autoresize_h:
|
288
|
-
width, height = self.get_auto_size()
|
289
|
-
if self.parent.rect.size != (width, height):
|
290
|
-
self.parent.set_size((width, height))
|
291
|
-
self.parent.build()
|
292
|
-
self.arrange()
|
293
|
-
return
|
294
230
|
|
295
|
-
self.children_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
296
|
-
|
297
|
-
# Calculate the width each child should fill
|
298
|
-
available_width = self.children_rect.width - (len(self.parent.children) - 1) * self.gap
|
299
|
-
child_width = available_width / len(self.parent.children)
|
300
|
-
|
301
|
-
x = self.children_rect.left
|
302
|
-
for child in self.parent.children:
|
303
|
-
child.set_position(x, self.children_rect.y)
|
304
|
-
child.set_autoresize_w(False)
|
305
|
-
child.set_size((child_width, None))
|
306
|
-
x += child_width + self.gap
|
307
231
|
|
308
|
-
|
309
|
-
"""Calculate total size with children widths filling the available space."""
|
310
|
-
if self.parent.autoresize_h :
|
311
|
-
return super().get_raw_size()
|
312
|
-
len_children = len(self.parent.children)
|
313
|
-
if not len_children:
|
314
|
-
return self.parent.rect.size
|
315
|
-
parent_height = max(c.get_min_required_size()[1] for c in self.parent.children)
|
316
|
-
target_rect = self.parent.inflate_rect_by_padding((0, 0, self.children_rect.width, parent_height))
|
317
|
-
return target_rect.size
|
232
|
+
class RowFill(Row):
|
318
233
|
|
234
|
+
def update_children_rect(self):
|
235
|
+
parent_width = self.parent.get_inner_width()
|
236
|
+
if self.parent.get_layout_children():
|
237
|
+
height = max(child.get_min_required_size()[1] for child in self.parent.get_layout_children())
|
238
|
+
width = parent_width
|
239
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
240
|
+
else:
|
241
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, parent_width,10)
|
242
|
+
self.children_rect.move_ip(-self.parent.scroll.x,-self.parent.scroll.y)
|
319
243
|
|
320
|
-
class ColumnFill(Column):
|
321
|
-
def __init__(self, gap: int = 0, spacing: bf.spacing = bf.spacing.MANUAL):
|
322
|
-
super().__init__(gap, spacing)
|
323
244
|
|
324
245
|
def arrange(self) -> None:
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
if self.parent.autoresize_w or self.parent.autoresize_h:
|
336
|
-
width, height = self.get_auto_size()
|
337
|
-
if self.parent.rect.size != (width, height):
|
338
|
-
self.parent.set_size((width, height))
|
339
|
-
self.parent.build()
|
340
|
-
self.arrange()
|
341
|
-
return
|
342
|
-
|
343
|
-
self.children_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
344
|
-
|
345
|
-
# Calculate the height each child should fill
|
346
|
-
available_height = self.children_rect.height - (len(self.parent.children) - 1) * self.gap
|
347
|
-
child_height = available_height / len(self.parent.children)
|
246
|
+
"""
|
247
|
+
Arranges children in a row and resizes them to fill the parent's height,
|
248
|
+
accounting for the gap between children.
|
249
|
+
"""
|
250
|
+
self.update_children_rect()
|
251
|
+
for child in self.parent.get_layout_children():
|
252
|
+
child.set_autoresize_w(False)
|
253
|
+
x = self.children_rect.x
|
254
|
+
# available_height = self.children_rect.height
|
348
255
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
child.set_size((None, child_height))
|
354
|
-
y += child_height + self.gap
|
256
|
+
# Calculate the width available for each child
|
257
|
+
total_gap = self.gap * (len(self.parent.get_layout_children()) - 1)
|
258
|
+
available_width = max(0, self.children_rect.width - total_gap)
|
259
|
+
child_width = available_width / len(self.parent.get_layout_children()) if self.parent.get_layout_children() else 0
|
355
260
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
len_children = len(self.parent.children)
|
361
|
-
if not len_children:
|
362
|
-
return self.parent.rect.size
|
363
|
-
parent_width = max(c.get_min_required_size()[0] for c in self.parent.children)
|
364
|
-
target_rect = self.parent.inflate_rect_by_padding((0, 0, parent_width, self.children_rect.height))
|
365
|
-
return target_rect.size
|
261
|
+
for child in self.parent.get_layout_children():
|
262
|
+
child.set_size((child_width, None)) # Resize child to fill height
|
263
|
+
child.set_position(x, self.children_rect.y) # Position child
|
264
|
+
x += child_width + self.gap
|
366
265
|
|
367
266
|
|
267
|
+
class ColumnFill(Column):
|
368
268
|
|
369
|
-
|
370
|
-
|
269
|
+
def update_children_rect(self):
|
270
|
+
parent_height = self.parent.get_inner_height()
|
271
|
+
if self.parent.get_layout_children():
|
272
|
+
width = max(child.get_min_required_size()[0] for child in self.parent.get_layout_children())
|
273
|
+
height = parent_height
|
274
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
275
|
+
else:
|
276
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, 10, parent_height)
|
277
|
+
self.children_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
371
278
|
|
372
|
-
@abstractmethod
|
373
279
|
def arrange(self) -> None:
|
374
|
-
"""
|
375
|
-
|
280
|
+
"""
|
281
|
+
Arranges children in a column and resizes them to fill the parent's width,
|
282
|
+
accounting for the gap between children.
|
283
|
+
"""
|
284
|
+
self.update_children_rect()
|
285
|
+
for child in self.parent.get_layout_children():
|
286
|
+
child.set_autoresize_h(False)
|
287
|
+
y = self.children_rect.y
|
376
288
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
289
|
+
# Calculate the height available for each child
|
290
|
+
total_gap = self.gap * (len(self.parent.get_layout_children()) - 1)
|
291
|
+
available_height = max(0, self.children_rect.height - total_gap)
|
292
|
+
child_height = available_height / len(self.parent.get_layout_children()) if self.parent.get_layout_children() else 0
|
381
293
|
|
294
|
+
for child in self.parent.get_layout_children():
|
295
|
+
child.set_size((None, child_height)) # Resize child to fill width
|
296
|
+
child.set_position(self.children_rect.x, y) # Position child
|
297
|
+
y += child_height + self.gap
|
382
298
|
|
383
299
|
|
384
300
|
class Grid(DoubleAxisLayout):
|
@@ -388,198 +304,123 @@ class Grid(DoubleAxisLayout):
|
|
388
304
|
self.cols = cols
|
389
305
|
self.gap = gap
|
390
306
|
|
391
|
-
def
|
392
|
-
|
393
|
-
|
394
|
-
return self
|
395
|
-
|
396
|
-
def set_dimensions(self, rows: int, cols: int) -> Self:
|
397
|
-
self.rows = rows
|
398
|
-
self.cols = cols
|
399
|
-
self.notify_parent()
|
400
|
-
return self
|
401
|
-
|
402
|
-
def get_raw_size(self) -> tuple[float, float]:
|
403
|
-
"""Calculate raw size based on the max width and height needed to fit all children."""
|
404
|
-
if not self.parent.children:
|
405
|
-
return self.parent.rect.size
|
406
|
-
|
407
|
-
# Calculate necessary width and height for the grid
|
408
|
-
max_child_width = max(child.get_min_required_size()[0] for child in self.parent.children)
|
409
|
-
max_child_height = max(child.get_min_required_size()[1] for child in self.parent.children)
|
410
|
-
|
411
|
-
grid_width = self.cols * max_child_width + (self.cols - 1) * self.gap
|
412
|
-
grid_height = self.rows * max_child_height + (self.rows - 1) * self.gap
|
413
|
-
target_rect = self.parent.inflate_rect_by_padding((0, 0, grid_width, grid_height))
|
414
|
-
|
415
|
-
return target_rect.size
|
416
|
-
|
417
|
-
def arrange(self) -> None:
|
418
|
-
"""Arrange widgets in a grid with specified rows and columns."""
|
419
|
-
if not self.parent or not self.parent.children:
|
307
|
+
def focus_up_child(self) -> None:
|
308
|
+
l = self.parent.get_interactive_children()
|
309
|
+
if not l:
|
420
310
|
return
|
421
|
-
|
422
|
-
if
|
423
|
-
for child in self.parent.children:
|
424
|
-
child.add_constraints(*self.child_constraints)
|
425
|
-
|
426
|
-
|
427
|
-
if self.parent.autoresize_w or self.parent.autoresize_h:
|
428
|
-
width, height = self.get_auto_size()
|
429
|
-
if self.parent.rect.size != (width, height):
|
430
|
-
self.parent.set_size((width, height))
|
431
|
-
self.parent.build()
|
432
|
-
self.arrange()
|
433
|
-
return
|
434
|
-
|
435
|
-
self.child_rect = self.parent.get_padded_rect()
|
436
|
-
|
437
|
-
# Calculate cell width and height based on parent size and gaps
|
438
|
-
cell_width = (self.child_rect.width - (self.cols - 1) * self.gap) / self.cols
|
439
|
-
cell_height = (self.child_rect.height - (self.rows - 1) * self.gap) / self.rows
|
440
|
-
|
441
|
-
for i, child in enumerate(self.parent.children):
|
442
|
-
row = i // self.cols
|
443
|
-
col = i % self.cols
|
444
|
-
x = self.child_rect.left + col * (cell_width + self.gap)
|
445
|
-
y = self.child_rect.top + row * (cell_height + self.gap)
|
446
|
-
|
447
|
-
child.set_position(x, y)
|
448
|
-
child.set_size((cell_width, cell_height))
|
449
|
-
|
450
|
-
def handle_event(self, event):
|
451
|
-
|
452
|
-
if event.type == pygame.KEYDOWN:
|
453
|
-
if event.key == pygame.K_DOWN:
|
454
|
-
self.focus_down_child()
|
455
|
-
elif event.key == pygame.K_UP:
|
456
|
-
self.focus_up_child()
|
457
|
-
elif event.key == pygame.K_LEFT:
|
458
|
-
self.focus_left_child()
|
459
|
-
elif event.key == pygame.K_RIGHT:
|
460
|
-
self.focus_right_child()
|
461
|
-
else:
|
462
|
-
return
|
463
|
-
elif event.type == pygame.MOUSEBUTTONDOWN:
|
464
|
-
r = self.parent.get_root()
|
465
|
-
if not r:
|
466
|
-
return
|
467
|
-
|
468
|
-
if self.parent.rect.collidepoint(
|
469
|
-
r.drawing_camera.screen_to_world(pygame.mouse.get_pos())
|
470
|
-
):
|
471
|
-
if event.button == 4:
|
472
|
-
self.parent.scroll_by((0, -10))
|
473
|
-
elif event.button == 5:
|
474
|
-
self.parent.scroll_by((0, 10))
|
475
|
-
else:
|
476
|
-
return
|
477
|
-
self.parent.clamp_scroll()
|
478
|
-
else:
|
479
|
-
return
|
480
|
-
else:
|
311
|
+
current_index = self.parent.focused_index
|
312
|
+
if current_index == -1:
|
481
313
|
return
|
482
|
-
|
314
|
+
current_row = current_index // self.cols
|
315
|
+
target_index = max(0, current_index - self.cols)
|
316
|
+
if target_index // self.cols < current_row:
|
317
|
+
self.parent.focused_index = target_index
|
318
|
+
l[target_index].get_focus()
|
483
319
|
|
484
320
|
def focus_down_child(self) -> None:
|
485
321
|
l = self.parent.get_interactive_children()
|
486
|
-
|
487
|
-
if new_index >= len(l):
|
322
|
+
if not l:
|
488
323
|
return
|
489
|
-
self.parent.focused_index
|
490
|
-
|
491
|
-
focused.get_focus()
|
492
|
-
self.scroll_to_widget(focused)
|
493
|
-
|
494
|
-
def focus_up_child(self) -> None:
|
495
|
-
l = self.parent.get_interactive_children()
|
496
|
-
new_index = self.parent.focused_index - self.cols
|
497
|
-
if new_index < 0:
|
324
|
+
current_index = self.parent.focused_index
|
325
|
+
if current_index == -1:
|
498
326
|
return
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
327
|
+
current_row = current_index // self.cols
|
328
|
+
target_index = min(len(l) - 1, current_index + self.cols)
|
329
|
+
if target_index // self.cols > current_row:
|
330
|
+
self.parent.focused_index = target_index
|
331
|
+
l[target_index].get_focus()
|
503
332
|
|
504
333
|
def focus_left_child(self) -> None:
|
505
334
|
l = self.parent.get_interactive_children()
|
506
|
-
|
507
|
-
if new_index < 0:
|
335
|
+
if not l:
|
508
336
|
return
|
509
|
-
self.parent.focused_index
|
510
|
-
|
511
|
-
|
512
|
-
|
337
|
+
current_index = self.parent.focused_index
|
338
|
+
if current_index == -1:
|
339
|
+
return
|
340
|
+
target_index = max(0, current_index - 1)
|
341
|
+
if target_index // self.cols == current_index // self.cols:
|
342
|
+
self.parent.focused_index = target_index
|
343
|
+
l[target_index].get_focus()
|
513
344
|
|
514
345
|
def focus_right_child(self) -> None:
|
515
346
|
l = self.parent.get_interactive_children()
|
516
|
-
|
517
|
-
if new_index >= self.cols or self.parent.focused_index+1 >= len(l):
|
347
|
+
if not l:
|
518
348
|
return
|
519
|
-
self.parent.focused_index
|
520
|
-
|
521
|
-
|
522
|
-
|
349
|
+
current_index = self.parent.focused_index
|
350
|
+
if current_index == -1:
|
351
|
+
return
|
352
|
+
target_index = min(len(l) - 1, current_index + 1)
|
353
|
+
if target_index // self.cols == current_index // self.cols:
|
354
|
+
self.parent.focused_index = target_index
|
355
|
+
l[target_index].get_focus()
|
523
356
|
|
357
|
+
def handle_event(self, event):
|
358
|
+
if not self.parent.get_layout_children() or not self.parent.children_has_focus():
|
359
|
+
return
|
524
360
|
|
525
|
-
|
526
|
-
|
527
|
-
|
361
|
+
if event.type == pygame.KEYDOWN:
|
362
|
+
if event.key in (pygame.K_RIGHT, pygame.K_LEFT, pygame.K_UP, pygame.K_DOWN):
|
363
|
+
if event.key == pygame.K_RIGHT:
|
364
|
+
self.focus_right_child()
|
365
|
+
elif event.key == pygame.K_LEFT:
|
366
|
+
self.focus_left_child()
|
367
|
+
elif event.key == pygame.K_UP:
|
368
|
+
self.focus_up_child()
|
369
|
+
elif event.key == pygame.K_DOWN:
|
370
|
+
self.focus_down_child()
|
371
|
+
|
372
|
+
event.consumed = True
|
373
|
+
|
374
|
+
def update_children_rect(self):
|
375
|
+
if self.parent.get_layout_children():
|
376
|
+
cell_width = max(child.get_min_required_size()[0] for child in self.parent.get_layout_children())
|
377
|
+
cell_height = max(child.get_min_required_size()[1] for child in self.parent.get_layout_children())
|
378
|
+
width = self.cols * cell_width + self.gap * (self.cols - 1)
|
379
|
+
height = self.rows * cell_height + self.gap * (self.rows - 1)
|
380
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
381
|
+
else:
|
382
|
+
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, 10, 10)
|
383
|
+
self.children_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
528
384
|
|
529
385
|
def arrange(self) -> None:
|
530
|
-
|
531
|
-
if not self.parent
|
386
|
+
self.update_children_rect()
|
387
|
+
if not self.parent.get_layout_children():
|
532
388
|
return
|
533
389
|
|
534
|
-
|
535
|
-
|
536
|
-
child.add_constraints(*self.child_constraints)
|
537
|
-
|
538
|
-
self.child_rect = self.parent.get_padded_rect()
|
539
|
-
|
540
|
-
# If autoresize is enabled, calculate required dimensions
|
541
|
-
if self.parent.autoresize_w or self.parent.autoresize_h:
|
542
|
-
width, height = self.get_auto_size()
|
543
|
-
if self.parent.rect.size != (width, height):
|
544
|
-
self.parent.set_size((width, height))
|
545
|
-
self.parent.build()
|
546
|
-
self.arrange()
|
547
|
-
return
|
548
|
-
|
549
|
-
# Adjust for scrolling offset
|
550
|
-
self.child_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
551
|
-
|
552
|
-
# Calculate cell dimensions based on available space
|
553
|
-
available_width = self.child_rect.width - (self.cols - 1) * self.gap
|
554
|
-
available_height = self.child_rect.height - (self.rows - 1) * self.gap
|
555
|
-
cell_width = available_width / self.cols
|
556
|
-
cell_height = available_height / self.rows
|
557
|
-
|
558
|
-
# Position each child in the grid
|
559
|
-
for index, child in enumerate(self.parent.children):
|
560
|
-
row = index // self.cols
|
561
|
-
col = index % self.cols
|
562
|
-
x = self.child_rect.left + col * (cell_width + self.gap)
|
563
|
-
y = self.child_rect.top + row * (cell_height + self.gap)
|
390
|
+
cell_width = (self.children_rect.width - self.gap * (self.cols - 1)) / self.cols
|
391
|
+
cell_height = (self.children_rect.height - self.gap * (self.rows - 1)) / self.rows
|
564
392
|
|
565
|
-
|
566
|
-
|
567
|
-
|
393
|
+
for i, child in enumerate(self.parent.get_layout_children()):
|
394
|
+
row = i // self.cols
|
395
|
+
col = i % self.cols
|
396
|
+
x = self.children_rect.x + col * (cell_width + self.gap)
|
397
|
+
y = self.children_rect.y + row * (cell_height + self.gap)
|
568
398
|
child.set_size((cell_width, cell_height))
|
399
|
+
child.set_position(x, y)
|
569
400
|
|
570
|
-
def get_raw_size(self) -> tuple[float, float]:
|
571
|
-
"""Calculate the grid’s raw size based on child minimums and the grid dimensions."""
|
572
|
-
if not self.parent.children:
|
573
|
-
return self.parent.rect.size
|
574
401
|
|
575
|
-
|
576
|
-
|
577
|
-
|
402
|
+
class GridFill(Grid):
|
403
|
+
def update_children_rect(self):
|
404
|
+
self.children_rect = self.parent.get_inner_rect()
|
405
|
+
def arrange(self) -> None:
|
406
|
+
"""
|
407
|
+
Arranges children in a grid and resizes them to fill the parent's available space,
|
408
|
+
accounting for the gap between children.
|
409
|
+
"""
|
410
|
+
self.update_children_rect()
|
411
|
+
|
412
|
+
if not self.parent.get_layout_children():
|
413
|
+
return
|
414
|
+
for child in self.parent.get_layout_children():
|
415
|
+
child.set_autoresize(False)
|
578
416
|
|
579
|
-
|
580
|
-
|
581
|
-
grid_height = self.rows * max_child_height + (self.rows - 1) * self.gap
|
417
|
+
cell_width = (self.children_rect.width - self.gap * (self.cols - 1)) / self.cols
|
418
|
+
cell_height = (self.children_rect.height - self.gap * (self.rows - 1)) / self.rows
|
582
419
|
|
583
|
-
|
584
|
-
|
585
|
-
|
420
|
+
for i, child in enumerate(self.parent.get_layout_children()):
|
421
|
+
row = i // self.cols
|
422
|
+
col = i % self.cols
|
423
|
+
x = self.children_rect.x + col * (cell_width + self.gap)
|
424
|
+
y = self.children_rect.y + row * (cell_height + self.gap)
|
425
|
+
child.set_size((cell_width, cell_height))
|
426
|
+
child.set_position(x, y)
|