batframework 1.0.8a14__py3-none-any.whl → 1.0.9a2__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 +9 -13
- batFramework/camera.py +1 -1
- batFramework/constants.py +6 -0
- batFramework/cutscene.py +3 -5
- batFramework/drawable.py +1 -1
- batFramework/dynamicEntity.py +2 -4
- batFramework/entity.py +15 -28
- batFramework/enums.py +1 -0
- batFramework/fontManager.py +3 -3
- batFramework/gui/__init__.py +2 -2
- batFramework/gui/{dialogueBox.py → animatedLabel.py} +18 -36
- batFramework/gui/button.py +30 -0
- batFramework/gui/clickableWidget.py +6 -1
- batFramework/gui/constraints/constraints.py +90 -1
- batFramework/gui/container.py +84 -93
- batFramework/gui/indicator.py +3 -2
- batFramework/gui/interactiveWidget.py +43 -24
- batFramework/gui/label.py +25 -9
- batFramework/gui/layout.py +378 -42
- batFramework/gui/root.py +2 -3
- batFramework/gui/shape.py +2 -0
- batFramework/gui/slider.py +1 -0
- batFramework/gui/textInput.py +115 -78
- batFramework/gui/toggle.py +2 -4
- batFramework/gui/widget.py +49 -37
- batFramework/manager.py +42 -31
- batFramework/sceneManager.py +2 -12
- batFramework/scrollingSprite.py +1 -1
- batFramework/utils.py +0 -1
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a2.dist-info}/METADATA +1 -1
- batframework-1.0.9a2.dist-info/RECORD +63 -0
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a2.dist-info}/WHEEL +1 -1
- batframework-1.0.8a14.dist-info/RECORD +0 -63
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a2.dist-info}/LICENCE +0 -0
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a2.dist-info}/top_level.txt +0 -0
batFramework/gui/layout.py
CHANGED
@@ -2,7 +2,7 @@ import batFramework as bf
|
|
2
2
|
from .widget import Widget
|
3
3
|
from .constraints.constraints import *
|
4
4
|
from typing import Self, TYPE_CHECKING
|
5
|
-
from abc import ABC
|
5
|
+
from abc import ABC,abstractmethod
|
6
6
|
import pygame
|
7
7
|
|
8
8
|
if TYPE_CHECKING:
|
@@ -26,7 +26,7 @@ class Layout(ABC):
|
|
26
26
|
|
27
27
|
def notify_parent(self) -> None:
|
28
28
|
if self.parent:
|
29
|
-
self.parent.
|
29
|
+
self.parent.dirty_layout = True
|
30
30
|
|
31
31
|
def arrange(self) -> None:
|
32
32
|
return
|
@@ -39,7 +39,7 @@ class Layout(ABC):
|
|
39
39
|
|
40
40
|
def get_auto_size(self) -> tuple[float, float]:
|
41
41
|
"""
|
42
|
-
Returns the final size the container should have (while keeping the
|
42
|
+
Returns the final size the container should have (while keeping the width and height if they are non-resizable)
|
43
43
|
"""
|
44
44
|
target_size = list(self.get_raw_size())
|
45
45
|
if not self.parent.autoresize_w:
|
@@ -48,25 +48,20 @@ class Layout(ABC):
|
|
48
48
|
target_size[1] = self.parent.rect.h
|
49
49
|
return target_size
|
50
50
|
|
51
|
-
def focus_next_child(self) -> None:
|
52
|
-
pass
|
53
|
-
|
54
|
-
def focus_prev_child(self) -> None:
|
55
|
-
pass
|
56
|
-
|
57
51
|
def scroll_to_widget(self, widget: Widget) -> None:
|
58
|
-
padded = self.parent.get_padded_rect()
|
59
|
-
r = widget.rect
|
60
|
-
if padded.contains(r):
|
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
|
61
55
|
return
|
62
56
|
clamped = r.clamp(padded)
|
63
|
-
|
64
|
-
|
57
|
+
# clamped.move_ip(-self.parent.rect.x,-self.parent.rect.y)
|
58
|
+
dx,dy = clamped.x - r.x,clamped.y-r.y
|
65
59
|
|
60
|
+
self.parent.scroll_by((-dx, -dy)) # on scroll la différence pour afficher le bouton en entier
|
61
|
+
|
66
62
|
def handle_event(self, event):
|
67
63
|
pass
|
68
64
|
|
69
|
-
|
70
65
|
class SingleAxisLayout(Layout):
|
71
66
|
def focus_next_child(self) -> None:
|
72
67
|
l = self.parent.get_interactive_children()
|
@@ -83,6 +78,7 @@ class SingleAxisLayout(Layout):
|
|
83
78
|
self.scroll_to_widget(focused)
|
84
79
|
|
85
80
|
|
81
|
+
|
86
82
|
class Column(SingleAxisLayout):
|
87
83
|
def __init__(self, gap: int = 0, spacing: bf.spacing = bf.spacing.MANUAL):
|
88
84
|
super().__init__()
|
@@ -126,29 +122,39 @@ class Column(SingleAxisLayout):
|
|
126
122
|
if self.child_constraints:
|
127
123
|
for child in self.parent.children:
|
128
124
|
child.add_constraints(*self.child_constraints)
|
129
|
-
self.
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
125
|
+
self.children_rect = self.parent.get_padded_rect()
|
126
|
+
|
127
|
+
width, height = self.get_auto_size()
|
128
|
+
if self.parent.autoresize_w and self.parent.rect.w !=width:
|
129
|
+
self.parent.set_size((width,None))
|
130
|
+
if self.parent.autoresize_h and self.parent.rect.h !=height:
|
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
|
140
142
|
for child in self.parent.children:
|
141
|
-
child.set_position(self.
|
143
|
+
child.set_position(self.children_rect.x, y)
|
142
144
|
y += child.get_min_required_size()[1] + self.gap
|
143
145
|
|
144
146
|
def handle_event(self, event):
|
145
|
-
if self.parent.autoresize_h or not self.parent.visible:
|
146
|
-
return
|
147
|
-
|
148
147
|
if not self.parent.children:
|
149
148
|
return
|
150
149
|
|
151
|
-
if event.type == pygame.
|
150
|
+
if event.type == pygame.KEYDOWN:
|
151
|
+
if event.key == pygame.K_DOWN:
|
152
|
+
self.focus_next_child()
|
153
|
+
elif event.key == pygame.K_UP:
|
154
|
+
self.focus_prev_child()
|
155
|
+
else:
|
156
|
+
return
|
157
|
+
elif event.type == pygame.MOUSEBUTTONDOWN:
|
152
158
|
r = self.parent.get_root()
|
153
159
|
if not r:
|
154
160
|
return
|
@@ -162,9 +168,12 @@ class Column(SingleAxisLayout):
|
|
162
168
|
self.parent.scroll_by((0, 10))
|
163
169
|
else:
|
164
170
|
return
|
165
|
-
event.consumed = True
|
166
171
|
self.parent.clamp_scroll()
|
167
|
-
|
172
|
+
else:
|
173
|
+
return
|
174
|
+
else:
|
175
|
+
return
|
176
|
+
event.consumed = True
|
168
177
|
|
169
178
|
class Row(SingleAxisLayout):
|
170
179
|
def __init__(self, gap: int = 0, spacing: bf.spacing = bf.spacing.MANUAL):
|
@@ -210,7 +219,7 @@ class Row(SingleAxisLayout):
|
|
210
219
|
if self.child_constraints:
|
211
220
|
for child in self.parent.children:
|
212
221
|
child.add_constraints(*self.child_constraints)
|
213
|
-
self.
|
222
|
+
self.children_rect = self.parent.get_padded_rect()
|
214
223
|
|
215
224
|
if self.parent.autoresize_w or self.parent.autoresize_h:
|
216
225
|
width, height = self.get_auto_size()
|
@@ -219,20 +228,25 @@ class Row(SingleAxisLayout):
|
|
219
228
|
self.parent.build()
|
220
229
|
self.arrange()
|
221
230
|
return
|
222
|
-
self.
|
223
|
-
x = self.
|
231
|
+
self.children_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
|
232
|
+
x = self.children_rect.left
|
224
233
|
for child in self.parent.children:
|
225
|
-
child.set_position(x, self.
|
234
|
+
child.set_position(x, self.children_rect.y)
|
226
235
|
x += child.get_min_required_size()[0] + self.gap
|
227
236
|
|
228
237
|
def handle_event(self, event):
|
229
|
-
if self.parent.autoresize_w or not self.parent.visible:
|
230
|
-
return
|
231
|
-
|
232
238
|
if not self.parent.children:
|
233
239
|
return
|
234
240
|
|
235
|
-
if event.type == pygame.
|
241
|
+
if event.type == pygame.KEYDOWN:
|
242
|
+
if event.key == pygame.K_RIGHT:
|
243
|
+
self.focus_next_child()
|
244
|
+
elif event.key == pygame.K_LEFT:
|
245
|
+
self.focus_prev_child()
|
246
|
+
else:
|
247
|
+
return
|
248
|
+
|
249
|
+
elif event.type == pygame.MOUSEBUTTONDOWN:
|
236
250
|
r = self.parent.get_root()
|
237
251
|
if not r:
|
238
252
|
return
|
@@ -245,5 +259,327 @@ class Row(SingleAxisLayout):
|
|
245
259
|
self.parent.scroll_by((10, 0))
|
246
260
|
else:
|
247
261
|
return
|
248
|
-
event.consumed = True
|
249
262
|
self.parent.clamp_scroll()
|
263
|
+
else:
|
264
|
+
return
|
265
|
+
else:
|
266
|
+
return
|
267
|
+
|
268
|
+
|
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
|
+
def arrange(self) -> None:
|
276
|
+
if self.parent.autoresize_h :
|
277
|
+
super().arrange()
|
278
|
+
return
|
279
|
+
if not self.parent or not self.parent.children:
|
280
|
+
return
|
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()
|
286
|
+
|
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
|
+
|
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
|
+
|
308
|
+
def get_raw_size(self) -> tuple[float, float]:
|
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
|
318
|
+
|
319
|
+
|
320
|
+
class ColumnFill(Column):
|
321
|
+
def __init__(self, gap: int = 0, spacing: bf.spacing = bf.spacing.MANUAL):
|
322
|
+
super().__init__(gap, spacing)
|
323
|
+
|
324
|
+
def arrange(self) -> None:
|
325
|
+
if self.parent.autoresize_h :
|
326
|
+
super().arrange()
|
327
|
+
return
|
328
|
+
if not self.parent or not self.parent.children:
|
329
|
+
return
|
330
|
+
if self.child_constraints:
|
331
|
+
for child in self.parent.children:
|
332
|
+
child.add_constraints(*self.child_constraints)
|
333
|
+
self.children_rect = self.parent.get_padded_rect()
|
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)
|
348
|
+
|
349
|
+
y = self.children_rect.top
|
350
|
+
for child in self.parent.children:
|
351
|
+
child.set_position(self.children_rect.x, y)
|
352
|
+
child.set_autoresize_h(False)
|
353
|
+
child.set_size((None, child_height))
|
354
|
+
y += child_height + self.gap
|
355
|
+
|
356
|
+
def get_raw_size(self) -> tuple[float, float]:
|
357
|
+
"""Calculate total size with children heights filling the available space."""
|
358
|
+
if self.parent.autoresize_w :
|
359
|
+
return super().get_raw_size()
|
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
|
366
|
+
|
367
|
+
|
368
|
+
|
369
|
+
class DoubleAxisLayout(Layout):
|
370
|
+
"""Abstract layout class for layouts that arrange widgets in two dimensions."""
|
371
|
+
|
372
|
+
@abstractmethod
|
373
|
+
def arrange(self) -> None:
|
374
|
+
"""Arrange child widgets across both axes, implementation required in subclasses."""
|
375
|
+
pass
|
376
|
+
|
377
|
+
def focus_up_child(self) -> None:...
|
378
|
+
def focus_down_child(self) -> None:...
|
379
|
+
def focus_right_child(self) -> None:...
|
380
|
+
def focus_left_child(self) -> None:...
|
381
|
+
|
382
|
+
|
383
|
+
|
384
|
+
class Grid(DoubleAxisLayout):
|
385
|
+
def __init__(self, rows: int, cols: int, gap: int = 0):
|
386
|
+
super().__init__()
|
387
|
+
self.rows = rows
|
388
|
+
self.cols = cols
|
389
|
+
self.gap = gap
|
390
|
+
|
391
|
+
def set_gap(self, value: int) -> Self:
|
392
|
+
self.gap = value
|
393
|
+
self.notify_parent()
|
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:
|
420
|
+
return
|
421
|
+
|
422
|
+
if self.child_constraints:
|
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:
|
481
|
+
return
|
482
|
+
event.consumed = True
|
483
|
+
|
484
|
+
def focus_down_child(self) -> None:
|
485
|
+
l = self.parent.get_interactive_children()
|
486
|
+
new_index = self.parent.focused_index + self.cols
|
487
|
+
if new_index >= len(l):
|
488
|
+
return
|
489
|
+
self.parent.focused_index = new_index
|
490
|
+
focused = l[self.parent.focused_index]
|
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:
|
498
|
+
return
|
499
|
+
self.parent.focused_index = new_index
|
500
|
+
focused = l[self.parent.focused_index]
|
501
|
+
focused.get_focus()
|
502
|
+
self.scroll_to_widget(focused)
|
503
|
+
|
504
|
+
def focus_left_child(self) -> None:
|
505
|
+
l = self.parent.get_interactive_children()
|
506
|
+
new_index = (self.parent.focused_index % self.cols) -1
|
507
|
+
if new_index < 0:
|
508
|
+
return
|
509
|
+
self.parent.focused_index -=1
|
510
|
+
focused = l[self.parent.focused_index]
|
511
|
+
focused.get_focus()
|
512
|
+
self.scroll_to_widget(focused)
|
513
|
+
|
514
|
+
def focus_right_child(self) -> None:
|
515
|
+
l = self.parent.get_interactive_children()
|
516
|
+
new_index = (self.parent.focused_index % self.cols) +1
|
517
|
+
if new_index >= self.cols or self.parent.focused_index+1 >= len(l):
|
518
|
+
return
|
519
|
+
self.parent.focused_index += 1
|
520
|
+
focused = l[self.parent.focused_index]
|
521
|
+
focused.get_focus()
|
522
|
+
self.scroll_to_widget(focused)
|
523
|
+
|
524
|
+
|
525
|
+
class GridFill(Grid):
|
526
|
+
def __init__(self, rows: int, cols: int, gap: int = 0):
|
527
|
+
super().__init__(rows,cols,gap)
|
528
|
+
|
529
|
+
def arrange(self) -> None:
|
530
|
+
"""Arrange widgets to fill each grid cell, adjusting to available space."""
|
531
|
+
if not self.parent or not self.parent.children:
|
532
|
+
return
|
533
|
+
|
534
|
+
if self.child_constraints:
|
535
|
+
for child in self.parent.children:
|
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)
|
564
|
+
|
565
|
+
child.set_position(x, y)
|
566
|
+
child.set_autoresize_w(False)
|
567
|
+
child.set_autoresize_h(False)
|
568
|
+
child.set_size((cell_width, cell_height))
|
569
|
+
|
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
|
+
|
575
|
+
# Determine minimum cell size required by the largest child
|
576
|
+
max_child_width = max(child.get_min_required_size()[0] for child in self.parent.children)
|
577
|
+
max_child_height = max(child.get_min_required_size()[1] for child in self.parent.children)
|
578
|
+
|
579
|
+
# Calculate total required size for the grid
|
580
|
+
grid_width = self.cols * max_child_width + (self.cols - 1) * self.gap
|
581
|
+
grid_height = self.rows * max_child_height + (self.rows - 1) * self.gap
|
582
|
+
|
583
|
+
# Adjust for padding and return
|
584
|
+
target_rect = self.parent.inflate_rect_by_padding((0, 0, grid_width, grid_height))
|
585
|
+
return target_rect.size
|
batFramework/gui/root.py
CHANGED
@@ -70,10 +70,10 @@ class Root(InteractiveWidget):
|
|
70
70
|
return res
|
71
71
|
|
72
72
|
def focus_next_tab(self, widget):
|
73
|
-
return
|
73
|
+
return
|
74
74
|
|
75
75
|
def focus_prev_tab(self, widget):
|
76
|
-
return
|
76
|
+
return
|
77
77
|
|
78
78
|
def get_by_uid(self, uid: int) -> "Widget":
|
79
79
|
def helper(w: "Widget", uid: int) -> "Widget":
|
@@ -91,7 +91,6 @@ class Root(InteractiveWidget):
|
|
91
91
|
if not force:
|
92
92
|
return self
|
93
93
|
self.rect.size = size
|
94
|
-
# self.build(resolve_constraints=True)
|
95
94
|
return self
|
96
95
|
|
97
96
|
def do_handle_event(self, event):
|
batFramework/gui/shape.py
CHANGED
batFramework/gui/slider.py
CHANGED
@@ -198,6 +198,7 @@ class Slider(Button):
|
|
198
198
|
|
199
199
|
if self.autoresize_h or self.autoresize_w:
|
200
200
|
target_rect = self.inflate_rect_by_padding(joined_rect)
|
201
|
+
target_rect.h += self.unpressed_relief
|
201
202
|
if not self.autoresize_w:
|
202
203
|
target_rect.w = self.rect.w
|
203
204
|
if not self.autoresize_h:
|