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.
@@ -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.children:
41
- self.children_rect = pygame.FRect(*self.parent.get_inner_rect().topleft,*self.parent.children[0].get_min_required_size())
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.children[1:]]
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.children:
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.children))
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.children:
169
- width = max(child.get_min_required_size()[0] for child in self.parent.children )
170
- height = sum(child.get_min_required_size()[1] for child in self.parent.children) + self.gap * (len(self.parent.children) - 1)
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.children )
173
- # height = sum(child.rect.h for child in self.parent.children) + self.gap * (len(self.parent.children) - 1)
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.children or not self.parent.children_has_focus():
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.children:
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.children:
206
- height = max(child.get_min_required_size()[1] for child in self.parent.children)
207
- width = sum(child.get_min_required_size()[0] for child in self.parent.children) + self.gap * (len(self.parent.children) - 1)
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.children or not self.parent.children_has_focus():
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.children:
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.children:
237
- height = max(child.get_min_required_size()[1] for child in self.parent.children)
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.children:
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.children) - 1)
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.children) if self.parent.children else 0
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.children:
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.children:
272
- width = max(child.get_min_required_size()[0] for child in self.parent.children)
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.children:
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.children) - 1)
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.children) if self.parent.children else 0
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.children:
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.children:
309
- cell_width = max(child.get_min_required_size()[0] for child in self.parent.children)
310
- cell_height = max(child.get_min_required_size()[1] for child in self.parent.children)
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.children:
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.children):
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
- if not self.parent.children:
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.children):
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
- if self.axis == bf.axis.HORIZONTAL:
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()
@@ -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
- radio_value if radio_value is not None else text if text else None
13
- )
14
- self.radio_variable: RadioVariable = None
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 set_text(self, text: str) -> Self:
29
- flag = False
30
- if self.value == self.text or self.value is None:
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 update_buttons(self):
83
- _ = [b.set_value(b.radio_value == self.value, False) for b in self.buttons]
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 : return
113
- self.do_handle_event_early(event)
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
 
@@ -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(8, max_text_size[1] * 1.5)
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
- self.indicator_height*2+
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 = self.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.enabled : return
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.enabled:
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.enabled:
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.enabled:
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.enabled:
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.enabled and button == 1:
246
+ if self.is_enabled and button == 1:
254
247
  if not self.get_focus():
255
248
  return True
256
249
  return False