batframework 1.0.9a8__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/constants.py +0 -1
- batFramework/gui/__init__.py +3 -2
- batFramework/gui/clickableWidget.py +32 -31
- batFramework/gui/container.py +9 -14
- batFramework/gui/draggableWidget.py +19 -12
- batFramework/gui/indicator.py +8 -25
- batFramework/gui/interactiveWidget.py +27 -25
- batFramework/gui/label.py +2 -3
- batFramework/gui/layout.py +108 -36
- batFramework/gui/meter.py +37 -29
- batFramework/gui/radioButton.py +20 -68
- batFramework/gui/root.py +5 -5
- batFramework/gui/selector.py +17 -24
- batFramework/gui/shape.py +1 -1
- batFramework/gui/slider.py +76 -88
- batFramework/gui/syncedVar.py +49 -0
- batFramework/gui/textInput.py +1 -1
- batFramework/gui/toggle.py +7 -7
- batFramework/gui/widget.py +24 -15
- batFramework/sceneLayer.py +1 -1
- batFramework/utils.py +6 -65
- {batframework-1.0.9a8.dist-info → batframework-1.0.9a9.dist-info}/METADATA +1 -1
- {batframework-1.0.9a8.dist-info → batframework-1.0.9a9.dist-info}/RECORD +26 -25
- {batframework-1.0.9a8.dist-info → batframework-1.0.9a9.dist-info}/LICENSE +0 -0
- {batframework-1.0.9a8.dist-info → batframework-1.0.9a9.dist-info}/WHEEL +0 -0
- {batframework-1.0.9a8.dist-info → batframework-1.0.9a9.dist-info}/top_level.txt +0 -0
batFramework/gui/layout.py
CHANGED
@@ -37,11 +37,11 @@ class Layout(ABC):
|
|
37
37
|
self.parent.dirty_layout = True
|
38
38
|
|
39
39
|
def update_children_rect(self):
|
40
|
-
if self.parent.
|
41
|
-
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft,*self.parent.
|
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
42
|
|
43
43
|
self.children_rect.unionall(
|
44
|
-
[pygame.FRect(0,0,*c.get_min_required_size()) for c in self.parent.
|
44
|
+
[pygame.FRect(0,0,*c.get_min_required_size()) for c in self.parent.get_layout_children()[1:]]
|
45
45
|
)
|
46
46
|
else:
|
47
47
|
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, 0, 0)
|
@@ -49,7 +49,7 @@ class Layout(ABC):
|
|
49
49
|
|
50
50
|
def update_child_constraints(self):
|
51
51
|
if self.parent:
|
52
|
-
for child in self.parent.
|
52
|
+
for child in self.parent.get_layout_children():
|
53
53
|
child.add_constraints(*self.child_constraints)
|
54
54
|
|
55
55
|
def arrange(self) -> None:
|
@@ -62,7 +62,7 @@ class Layout(ABC):
|
|
62
62
|
"""
|
63
63
|
Returns the size the container should have to encapsulate perfectly all of its widgets
|
64
64
|
"""
|
65
|
-
# print(self,self.parent,len(self.parent.
|
65
|
+
# print(self,self.parent,len(self.parent.get_layout_children()))
|
66
66
|
self.update_children_rect()
|
67
67
|
return self.children_rect.size
|
68
68
|
|
@@ -165,12 +165,12 @@ class Column(SingleAxisLayout):
|
|
165
165
|
self.gap = gap
|
166
166
|
|
167
167
|
def update_children_rect(self):
|
168
|
-
if self.parent.
|
169
|
-
width = max(child.get_min_required_size()[0] for child in self.parent.
|
170
|
-
height = sum(child.get_min_required_size()[1] for child in self.parent.
|
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)
|
171
171
|
|
172
|
-
# width = max(child.rect.w for child in self.parent.
|
173
|
-
# height = sum(child.rect.h for child in self.parent.
|
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)
|
174
174
|
|
175
175
|
|
176
176
|
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
@@ -179,7 +179,7 @@ class Column(SingleAxisLayout):
|
|
179
179
|
self.children_rect.move_ip(-self.parent.scroll.x,-self.parent.scroll.y)
|
180
180
|
|
181
181
|
def handle_event(self, event):
|
182
|
-
if not self.parent.
|
182
|
+
if not self.parent.get_layout_children() or not self.parent.children_has_focus():
|
183
183
|
return
|
184
184
|
|
185
185
|
if event.type == pygame.KEYDOWN:
|
@@ -192,7 +192,7 @@ class Column(SingleAxisLayout):
|
|
192
192
|
def arrange(self) -> None:
|
193
193
|
self.update_children_rect()
|
194
194
|
y = self.children_rect.y
|
195
|
-
for child in self.parent.
|
195
|
+
for child in self.parent.get_layout_children():
|
196
196
|
child.set_position(self.children_rect.x, y)
|
197
197
|
y += child.rect.height + self.gap
|
198
198
|
|
@@ -202,16 +202,16 @@ class Row(SingleAxisLayout):
|
|
202
202
|
self.gap = gap
|
203
203
|
|
204
204
|
def update_children_rect(self):
|
205
|
-
if self.parent.
|
206
|
-
height = max(child.get_min_required_size()[1] for child in self.parent.
|
207
|
-
width = sum(child.get_min_required_size()[0] for child in self.parent.
|
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
208
|
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
209
209
|
else:
|
210
210
|
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, 10,10)
|
211
211
|
self.children_rect.move_ip(-self.parent.scroll.x,-self.parent.scroll.y)
|
212
212
|
|
213
213
|
def handle_event(self, event):
|
214
|
-
if not self.parent.
|
214
|
+
if not self.parent.get_layout_children() or not self.parent.children_has_focus():
|
215
215
|
return
|
216
216
|
|
217
217
|
if event.type == pygame.KEYDOWN:
|
@@ -223,7 +223,7 @@ class Row(SingleAxisLayout):
|
|
223
223
|
def arrange(self) -> None:
|
224
224
|
self.update_children_rect()
|
225
225
|
x = self.children_rect.x
|
226
|
-
for child in self.parent.
|
226
|
+
for child in self.parent.get_layout_children():
|
227
227
|
child.set_position(x,self.children_rect.y)
|
228
228
|
x += child.rect.width + self.gap
|
229
229
|
|
@@ -233,8 +233,8 @@ class RowFill(Row):
|
|
233
233
|
|
234
234
|
def update_children_rect(self):
|
235
235
|
parent_width = self.parent.get_inner_width()
|
236
|
-
if self.parent.
|
237
|
-
height = max(child.get_min_required_size()[1] for child in self.parent.
|
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
238
|
width = parent_width
|
239
239
|
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
240
240
|
else:
|
@@ -248,17 +248,17 @@ class RowFill(Row):
|
|
248
248
|
accounting for the gap between children.
|
249
249
|
"""
|
250
250
|
self.update_children_rect()
|
251
|
-
for child in self.parent.
|
251
|
+
for child in self.parent.get_layout_children():
|
252
252
|
child.set_autoresize_w(False)
|
253
253
|
x = self.children_rect.x
|
254
254
|
# available_height = self.children_rect.height
|
255
255
|
|
256
256
|
# Calculate the width available for each child
|
257
|
-
total_gap = self.gap * (len(self.parent.
|
257
|
+
total_gap = self.gap * (len(self.parent.get_layout_children()) - 1)
|
258
258
|
available_width = max(0, self.children_rect.width - total_gap)
|
259
|
-
child_width = available_width / len(self.parent.
|
259
|
+
child_width = available_width / len(self.parent.get_layout_children()) if self.parent.get_layout_children() else 0
|
260
260
|
|
261
|
-
for child in self.parent.
|
261
|
+
for child in self.parent.get_layout_children():
|
262
262
|
child.set_size((child_width, None)) # Resize child to fill height
|
263
263
|
child.set_position(x, self.children_rect.y) # Position child
|
264
264
|
x += child_width + self.gap
|
@@ -268,8 +268,8 @@ class ColumnFill(Column):
|
|
268
268
|
|
269
269
|
def update_children_rect(self):
|
270
270
|
parent_height = self.parent.get_inner_height()
|
271
|
-
if self.parent.
|
272
|
-
width = max(child.get_min_required_size()[0] for child in self.parent.
|
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
273
|
height = parent_height
|
274
274
|
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
275
275
|
else:
|
@@ -282,16 +282,16 @@ class ColumnFill(Column):
|
|
282
282
|
accounting for the gap between children.
|
283
283
|
"""
|
284
284
|
self.update_children_rect()
|
285
|
-
for child in self.parent.
|
285
|
+
for child in self.parent.get_layout_children():
|
286
286
|
child.set_autoresize_h(False)
|
287
287
|
y = self.children_rect.y
|
288
288
|
|
289
289
|
# Calculate the height available for each child
|
290
|
-
total_gap = self.gap * (len(self.parent.
|
290
|
+
total_gap = self.gap * (len(self.parent.get_layout_children()) - 1)
|
291
291
|
available_height = max(0, self.children_rect.height - total_gap)
|
292
|
-
child_height = available_height / len(self.parent.
|
292
|
+
child_height = available_height / len(self.parent.get_layout_children()) if self.parent.get_layout_children() else 0
|
293
293
|
|
294
|
-
for child in self.parent.
|
294
|
+
for child in self.parent.get_layout_children():
|
295
295
|
child.set_size((None, child_height)) # Resize child to fill width
|
296
296
|
child.set_position(self.children_rect.x, y) # Position child
|
297
297
|
y += child_height + self.gap
|
@@ -304,10 +304,77 @@ class Grid(DoubleAxisLayout):
|
|
304
304
|
self.cols = cols
|
305
305
|
self.gap = gap
|
306
306
|
|
307
|
+
def focus_up_child(self) -> None:
|
308
|
+
l = self.parent.get_interactive_children()
|
309
|
+
if not l:
|
310
|
+
return
|
311
|
+
current_index = self.parent.focused_index
|
312
|
+
if current_index == -1:
|
313
|
+
return
|
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()
|
319
|
+
|
320
|
+
def focus_down_child(self) -> None:
|
321
|
+
l = self.parent.get_interactive_children()
|
322
|
+
if not l:
|
323
|
+
return
|
324
|
+
current_index = self.parent.focused_index
|
325
|
+
if current_index == -1:
|
326
|
+
return
|
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()
|
332
|
+
|
333
|
+
def focus_left_child(self) -> None:
|
334
|
+
l = self.parent.get_interactive_children()
|
335
|
+
if not l:
|
336
|
+
return
|
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()
|
344
|
+
|
345
|
+
def focus_right_child(self) -> None:
|
346
|
+
l = self.parent.get_interactive_children()
|
347
|
+
if not l:
|
348
|
+
return
|
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()
|
356
|
+
|
357
|
+
def handle_event(self, event):
|
358
|
+
if not self.parent.get_layout_children() or not self.parent.children_has_focus():
|
359
|
+
return
|
360
|
+
|
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
|
+
|
307
374
|
def update_children_rect(self):
|
308
|
-
if self.parent.
|
309
|
-
cell_width = max(child.get_min_required_size()[0] for child in self.parent.
|
310
|
-
cell_height = max(child.get_min_required_size()[1] for child in self.parent.
|
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())
|
311
378
|
width = self.cols * cell_width + self.gap * (self.cols - 1)
|
312
379
|
height = self.rows * cell_height + self.gap * (self.rows - 1)
|
313
380
|
self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft, width, height)
|
@@ -317,13 +384,13 @@ class Grid(DoubleAxisLayout):
|
|
317
384
|
|
318
385
|
def arrange(self) -> None:
|
319
386
|
self.update_children_rect()
|
320
|
-
if not self.parent.
|
387
|
+
if not self.parent.get_layout_children():
|
321
388
|
return
|
322
389
|
|
323
390
|
cell_width = (self.children_rect.width - self.gap * (self.cols - 1)) / self.cols
|
324
391
|
cell_height = (self.children_rect.height - self.gap * (self.rows - 1)) / self.rows
|
325
392
|
|
326
|
-
for i, child in enumerate(self.parent.
|
393
|
+
for i, child in enumerate(self.parent.get_layout_children()):
|
327
394
|
row = i // self.cols
|
328
395
|
col = i % self.cols
|
329
396
|
x = self.children_rect.x + col * (cell_width + self.gap)
|
@@ -333,19 +400,24 @@ class Grid(DoubleAxisLayout):
|
|
333
400
|
|
334
401
|
|
335
402
|
class GridFill(Grid):
|
403
|
+
def update_children_rect(self):
|
404
|
+
self.children_rect = self.parent.get_inner_rect()
|
336
405
|
def arrange(self) -> None:
|
337
406
|
"""
|
338
407
|
Arranges children in a grid and resizes them to fill the parent's available space,
|
339
408
|
accounting for the gap between children.
|
340
409
|
"""
|
341
410
|
self.update_children_rect()
|
342
|
-
|
411
|
+
|
412
|
+
if not self.parent.get_layout_children():
|
343
413
|
return
|
414
|
+
for child in self.parent.get_layout_children():
|
415
|
+
child.set_autoresize(False)
|
344
416
|
|
345
417
|
cell_width = (self.children_rect.width - self.gap * (self.cols - 1)) / self.cols
|
346
418
|
cell_height = (self.children_rect.height - self.gap * (self.rows - 1)) / self.rows
|
347
419
|
|
348
|
-
for i, child in enumerate(self.parent.
|
420
|
+
for i, child in enumerate(self.parent.get_layout_children()):
|
349
421
|
row = i // self.cols
|
350
422
|
col = i % self.cols
|
351
423
|
x = self.children_rect.x + col * (cell_width + self.gap)
|
batFramework/gui/meter.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import math
|
1
2
|
import batFramework as bf
|
2
3
|
from .shape import Shape
|
3
4
|
from typing import Self
|
@@ -8,33 +9,18 @@ def custom_top_at(self, x, y):
|
|
8
9
|
return self.parent
|
9
10
|
return None
|
10
11
|
|
11
|
-
|
12
12
|
class Meter(Shape):
|
13
13
|
def __init__(self, min_value: float = 0, max_value: float = 1, step: float = 0.1):
|
14
14
|
super().__init__()
|
15
|
-
self.axis: bf.axis = bf.axis.HORIZONTAL
|
16
15
|
self.min_value, self.max_value = min_value, max_value
|
17
16
|
self.step = step
|
18
17
|
self.snap: bool = False
|
19
18
|
self.value = self.max_value
|
20
|
-
self.content = Shape((0, 0)).set_color(bf.color.BLUE)
|
21
|
-
self.content.set_debug_color("cyan")
|
22
|
-
self.content.top_at = lambda x, y: custom_top_at(self.content, x, y)
|
23
|
-
self.add(self.content)
|
24
|
-
self.set_padding(4)
|
25
|
-
self.set_color("gray20")
|
26
|
-
self.set_outline_width(1)
|
27
|
-
self.set_outline_color(bf.color.BLACK)
|
28
19
|
self.set_debug_color("pink")
|
29
20
|
|
30
21
|
def __str__(self) -> str:
|
31
22
|
return "Meter"
|
32
23
|
|
33
|
-
def set_axis(self,axis:bf.axis)->Self:
|
34
|
-
self.axis = axis
|
35
|
-
self.dirty_shape = True
|
36
|
-
return self
|
37
|
-
|
38
24
|
def set_step(self, step: float) -> Self:
|
39
25
|
self.step = step
|
40
26
|
self.set_value(self.value)
|
@@ -47,10 +33,6 @@ class Meter(Shape):
|
|
47
33
|
self.max_value = range_max
|
48
34
|
self.dirty_shape = True
|
49
35
|
|
50
|
-
def get_debug_outlines(self):
|
51
|
-
yield from super().get_debug_outlines()
|
52
|
-
# yield from self.content.get_debug_outlines()
|
53
|
-
|
54
36
|
def set_value(self, value: float) -> Self:
|
55
37
|
value = max(self.min_value, min(self.max_value, value))
|
56
38
|
value = round(value / self.step) * self.step
|
@@ -67,21 +49,47 @@ class Meter(Shape):
|
|
67
49
|
def get_ratio(self) -> float:
|
68
50
|
return (self.value-self.min_value) / (self.max_value - self.min_value)
|
69
51
|
|
52
|
+
|
53
|
+
class BarMeter(Meter):
|
54
|
+
def __init__(self, min_value: float = 0, max_value: float = 1, step: float = 0.1):
|
55
|
+
super().__init__(min_value, max_value, step)
|
56
|
+
self.axis: bf.axis = bf.axis.HORIZONTAL
|
57
|
+
self.content = Shape((0, 0)).set_color(bf.color.BLUE)
|
58
|
+
self.content.set_debug_color("cyan")
|
59
|
+
self.content.top_at = lambda x, y: custom_top_at(self.content, x, y)
|
60
|
+
self.add(self.content)
|
61
|
+
self.set_padding(4)
|
62
|
+
self.set_color("gray20")
|
63
|
+
self.set_outline_width(1)
|
64
|
+
self.set_outline_color(bf.color.BLACK)
|
65
|
+
self.set_debug_color("pink")
|
66
|
+
|
67
|
+
def __str__(self) -> str:
|
68
|
+
return "BarMeter"
|
69
|
+
|
70
|
+
def set_axis(self,axis:bf.axis)->Self:
|
71
|
+
self.axis = axis
|
72
|
+
self.dirty_shape = True
|
73
|
+
return self
|
74
|
+
|
70
75
|
def _build_content(self) -> None:
|
71
76
|
padded = self.get_inner_rect()
|
72
77
|
ratio = self.get_ratio()
|
73
78
|
|
74
|
-
|
75
|
-
width = padded.width * ratio
|
76
|
-
self.content.set_size((width, padded.height))
|
77
|
-
self.content.rect.topleft = padded.topleft
|
78
|
-
|
79
|
-
else: # VERTICAL
|
80
|
-
height = padded.height * ratio
|
81
|
-
self.content.set_size((padded.width, height))
|
82
|
-
# Content grows from bottom up
|
83
|
-
self.content.rect.bottomleft = padded.bottomleft
|
79
|
+
self.content.set_border_radius(*[round(b/2) for b in self.border_radius])
|
84
80
|
|
81
|
+
if self.axis == bf.axis.HORIZONTAL:
|
82
|
+
width = (padded.width- self.outline_width *2) * ratio
|
83
|
+
self.content.set_size((width, padded.height - self.outline_width*2))
|
84
|
+
self.content.rect.topleft = padded.move(self.outline_width, self.outline_width).topleft
|
85
|
+
|
86
|
+
else: # vertical
|
87
|
+
height = (padded.height - self.outline_width * 2) * ratio
|
88
|
+
self.content.set_size((padded.width - self.outline_width * 2, height))
|
89
|
+
self.content.rect.bottomleft = (
|
90
|
+
padded.move(self.outline_width,-self.outline_width).bottomleft
|
91
|
+
)
|
92
|
+
self.content.rect.height = math.ceil(self.content.rect.height)
|
85
93
|
|
86
94
|
def build(self) -> None:
|
87
95
|
self._build_content()
|
batFramework/gui/radioButton.py
CHANGED
@@ -1,83 +1,35 @@
|
|
1
1
|
import batFramework as bf
|
2
2
|
from typing import Self, Any, Callable
|
3
3
|
from .toggle import Toggle
|
4
|
-
|
5
|
-
|
4
|
+
from .syncedVar import SyncedVar
|
6
5
|
|
7
6
|
|
8
7
|
class RadioButton(Toggle):
|
9
|
-
def __init__(self, text: str = "", radio_value: Any = None) -> None:
|
8
|
+
def __init__(self, text: str = "", radio_value: Any = None, synced_var: SyncedVar = None) -> None:
|
10
9
|
super().__init__(text, None, False)
|
11
|
-
self.radio_value: Any =
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
self.radio_value: Any = radio_value if radio_value is not None else text if text else None
|
11
|
+
self.synced_var : SyncedVar = None
|
12
|
+
if synced_var:
|
13
|
+
self.link(synced_var)
|
14
|
+
|
15
|
+
def link(self,synced_var:SyncedVar)->Self:
|
16
|
+
self.synced_var = synced_var
|
17
|
+
synced_var.bind_widget(self,self._update_state)
|
18
|
+
return self
|
15
19
|
|
16
20
|
def __str__(self) -> str:
|
17
|
-
return (
|
18
|
-
f"RadioButton({self.radio_value}|{'Active' if self.value else 'Inactive'})"
|
19
|
-
)
|
21
|
+
return f"RadioButton({self.radio_value}|{'Active' if self.value else 'Inactive'})"
|
20
22
|
|
21
23
|
def set_radio_value(self, value: Any) -> Self:
|
22
24
|
self.radio_value = value
|
23
|
-
|
24
|
-
if self.radio_variable:
|
25
|
-
self.radio_variable.update_buttons()
|
26
25
|
return self
|
27
26
|
|
28
|
-
def
|
29
|
-
|
30
|
-
if
|
31
|
-
flag = True
|
32
|
-
super().set_text(text)
|
33
|
-
if flag:
|
34
|
-
self.set_radio_value(self.text)
|
35
|
-
return self
|
36
|
-
|
37
|
-
def click(self) -> None:
|
38
|
-
if self.radio_variable is None:
|
39
|
-
return
|
40
|
-
self.radio_variable.set_value(self.radio_value)
|
41
|
-
|
42
|
-
|
43
|
-
class RadioVariable:
|
44
|
-
def __init__(self) -> None:
|
45
|
-
self.buttons: list[RadioButton] = []
|
46
|
-
self.value = None
|
47
|
-
self.modify_callback: Callable[[bool],Any] = None
|
48
|
-
|
49
|
-
def set_modify_callback(self, callback:Callable[[bool],Any]) -> Self:
|
50
|
-
self.modify_callback = callback
|
51
|
-
return self
|
52
|
-
|
53
|
-
def link(self, *buttons: RadioButton) -> Self:
|
54
|
-
if not buttons:
|
55
|
-
return self
|
56
|
-
for b in buttons:
|
57
|
-
b.radio_variable = self
|
58
|
-
self.buttons.extend(buttons)
|
59
|
-
|
60
|
-
if self.value is None:
|
61
|
-
self.set_value(buttons[0].radio_value)
|
62
|
-
else:
|
63
|
-
self.update_buttons()
|
64
|
-
return self
|
65
|
-
|
66
|
-
def unlink(self, *buttons) -> Self:
|
67
|
-
for b in self.buttons:
|
68
|
-
if b in buttons:
|
69
|
-
b.radio_variable = None
|
70
|
-
self.buttons = [b for b in self.buttons if b not in buttons]
|
71
|
-
return self
|
72
|
-
|
73
|
-
def set_value(self, value: Any) -> Self:
|
74
|
-
if value == self.value:
|
75
|
-
return
|
76
|
-
self.value = value
|
77
|
-
if self.modify_callback:
|
78
|
-
self.modify_callback(value)
|
79
|
-
self.update_buttons()
|
80
|
-
return self
|
27
|
+
def set_value(self, value : bool, do_callback=False):
|
28
|
+
super().set_value(value, do_callback)
|
29
|
+
if value : self.synced_var.value = self.radio_value
|
81
30
|
|
82
|
-
def
|
83
|
-
|
31
|
+
def _update_state(self, synced_value: Any) -> None:
|
32
|
+
"""
|
33
|
+
Updates the state of the RadioButton based on the synced variable's value.
|
34
|
+
"""
|
35
|
+
self.set_value(self.radio_value == synced_value, False)
|
batFramework/gui/root.py
CHANGED
@@ -109,13 +109,11 @@ class Root(InteractiveWidget):
|
|
109
109
|
return self
|
110
110
|
|
111
111
|
def process_event(self,event):
|
112
|
-
if event.consumed :
|
113
|
-
|
114
|
-
if event.consumed : return
|
115
|
-
super().process_event(event)
|
112
|
+
if not event.consumed : self.do_handle_event_early(event)
|
113
|
+
if not event.consumed : super().process_event(event)
|
116
114
|
|
117
115
|
def do_handle_event_early(self, event):
|
118
|
-
if event.type == pygame.VIDEORESIZE:
|
116
|
+
if event.type == pygame.VIDEORESIZE and not pygame.SCALED & bf.const.FLAGS:
|
119
117
|
self.set_size((event.w,event.h),force=True)
|
120
118
|
if self.focused:
|
121
119
|
if event.type == pygame.KEYDOWN:
|
@@ -126,6 +124,7 @@ class Root(InteractiveWidget):
|
|
126
124
|
event.consumed = self.focused.on_key_up(event.key)
|
127
125
|
|
128
126
|
if not self.hovered or (not isinstance(self.hovered, InteractiveWidget)):
|
127
|
+
event.consumed = True
|
129
128
|
return
|
130
129
|
|
131
130
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
@@ -200,6 +199,7 @@ class Root(InteractiveWidget):
|
|
200
199
|
# print("START updating tree")
|
201
200
|
self.apply_updates("pre")
|
202
201
|
self.apply_updates("post")
|
202
|
+
|
203
203
|
self.apply_updates("pre")
|
204
204
|
self.apply_updates("post")
|
205
205
|
|
batFramework/gui/selector.py
CHANGED
@@ -21,7 +21,6 @@ class Selector(Button):
|
|
21
21
|
self.on_modify_callback : Callable[[str,int],Any] = None
|
22
22
|
self.options = options if options else []
|
23
23
|
self.gap : int = 2
|
24
|
-
self.indicator_height = 0
|
25
24
|
text_value = ""
|
26
25
|
|
27
26
|
|
@@ -32,13 +31,11 @@ class Selector(Button):
|
|
32
31
|
self.current_index = self.options.index(default_value)
|
33
32
|
|
34
33
|
self.left_indicator : MyArrow = (MyArrow(bf.direction.LEFT)
|
35
|
-
.set_draw_stem(False)
|
36
34
|
.set_color((0,0,0,0)).set_arrow_color(self.text_color)
|
37
35
|
.set_callback(lambda : self.set_by_index(self.get_current_index()-1))
|
38
36
|
)
|
39
37
|
|
40
38
|
self.right_indicator:MyArrow =(MyArrow(bf.direction.RIGHT)
|
41
|
-
.set_draw_stem(False)
|
42
39
|
.set_color((0,0,0,0)).set_arrow_color(self.text_color)
|
43
40
|
.set_callback(lambda : self.set_by_index(self.get_current_index()+1))
|
44
41
|
)
|
@@ -54,7 +51,12 @@ class Selector(Button):
|
|
54
51
|
self.gap = value
|
55
52
|
self.dirty_shape = True
|
56
53
|
return self
|
57
|
-
|
54
|
+
|
55
|
+
def set_arrow_color(self,color)->Self:
|
56
|
+
self.left_indicator.set_arrow_color(color)
|
57
|
+
self.right_indicator.set_arrow_color(color)
|
58
|
+
return self
|
59
|
+
|
58
60
|
def disable(self):
|
59
61
|
super().disable()
|
60
62
|
self.left_indicator.disable()
|
@@ -99,14 +101,13 @@ class Selector(Button):
|
|
99
101
|
self.text = old_text
|
100
102
|
|
101
103
|
# Ensure total_height is always an odd integer
|
102
|
-
total_height = max(
|
104
|
+
total_height = max(self.font_object.get_height()+1, max_text_size[1] * 1.5)
|
103
105
|
total_height += self.unpressed_relief
|
104
106
|
total_height += max(self.right_indicator.outline_width,self.left_indicator.outline_width)
|
105
|
-
self.indicator_height = total_height
|
106
107
|
|
107
108
|
# Calculate total width and height
|
108
109
|
total_width = (
|
109
|
-
|
110
|
+
total_height*2+
|
110
111
|
max_text_size[0] + # Text width
|
111
112
|
self.gap * 2 # Gaps between text and indicators
|
112
113
|
)
|
@@ -115,8 +116,6 @@ class Selector(Button):
|
|
115
116
|
|
116
117
|
return final_size
|
117
118
|
|
118
|
-
|
119
|
-
|
120
119
|
def _align_content(self):
|
121
120
|
"""
|
122
121
|
Builds the selector layout (places and resizes the indicators)
|
@@ -130,7 +129,7 @@ class Selector(Button):
|
|
130
129
|
# left_size = self.left_indicator.rect.size
|
131
130
|
right_size = self.right_indicator.rect.size
|
132
131
|
|
133
|
-
indicator_height =
|
132
|
+
indicator_height = padded.h
|
134
133
|
|
135
134
|
self.left_indicator.set_size((indicator_height,indicator_height))
|
136
135
|
self.right_indicator.set_size((indicator_height,indicator_height))
|
@@ -143,11 +142,8 @@ class Selector(Button):
|
|
143
142
|
self.right_indicator.set_position(padded.right - right_size[0], None)
|
144
143
|
self.right_indicator.set_center(None, padded.centery)
|
145
144
|
|
146
|
-
|
147
|
-
|
148
|
-
def build(self):
|
149
|
-
|
150
|
-
super().build()
|
145
|
+
def apply_post_updates(self, skip_draw = False):
|
146
|
+
super().apply_post_updates(skip_draw)
|
151
147
|
self._align_content()
|
152
148
|
|
153
149
|
def get_current_index(self)->int:
|
@@ -172,8 +168,6 @@ class Selector(Button):
|
|
172
168
|
self.on_modify_callback = function
|
173
169
|
return self
|
174
170
|
|
175
|
-
|
176
|
-
|
177
171
|
def set_by_index(self,index:int)->Self:
|
178
172
|
if self.allow_cycle:
|
179
173
|
index = index%len(self.options)
|
@@ -183,7 +177,6 @@ class Selector(Button):
|
|
183
177
|
if index == self.current_index:
|
184
178
|
return self
|
185
179
|
|
186
|
-
|
187
180
|
self.current_index = index
|
188
181
|
self.set_text(self.options[self.current_index])
|
189
182
|
if self.on_modify_callback:
|
@@ -209,14 +202,14 @@ class Selector(Button):
|
|
209
202
|
self.right_indicator.hide()
|
210
203
|
|
211
204
|
def set_by_value(self,value:str)->Self:
|
212
|
-
if not self.
|
205
|
+
if not self.is_enabled : return
|
213
206
|
if value not in self.options : return self
|
214
207
|
index = self.options.index(value)
|
215
208
|
self.set_by_index(index)
|
216
209
|
return self
|
217
210
|
|
218
211
|
def on_key_down(self, key: int) -> bool:
|
219
|
-
if not self.
|
212
|
+
if not self.is_enabled:
|
220
213
|
return False
|
221
214
|
|
222
215
|
key_actions = {
|
@@ -226,14 +219,14 @@ class Selector(Button):
|
|
226
219
|
}
|
227
220
|
|
228
221
|
indicator = key_actions.get(key)
|
229
|
-
if indicator and indicator.visible and indicator.
|
222
|
+
if indicator and indicator.visible and indicator.is_enabled:
|
230
223
|
indicator.on_click_down(1)
|
231
224
|
return True
|
232
225
|
|
233
226
|
return False
|
234
227
|
|
235
228
|
def on_key_up(self, key: int) -> bool:
|
236
|
-
if not self.
|
229
|
+
if not self.is_enabled:
|
237
230
|
return False
|
238
231
|
|
239
232
|
key_actions = {
|
@@ -243,14 +236,14 @@ class Selector(Button):
|
|
243
236
|
}
|
244
237
|
|
245
238
|
indicator = key_actions.get(key)
|
246
|
-
if indicator and indicator.visible and indicator.
|
239
|
+
if indicator and indicator.visible and indicator.is_enabled:
|
247
240
|
indicator.on_click_up(1)
|
248
241
|
return True
|
249
242
|
|
250
243
|
return False
|
251
244
|
|
252
245
|
def do_on_click_down(self, button) -> None:
|
253
|
-
if self.
|
246
|
+
if self.is_enabled and button == 1:
|
254
247
|
if not self.get_focus():
|
255
248
|
return True
|
256
249
|
return False
|