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 CHANGED
@@ -49,4 +49,3 @@ class Constants:
49
49
  @staticmethod
50
50
  def set_fps_limit(value: int):
51
51
  Constants.FPS = value
52
- print("FPS limit to : ", value)
@@ -7,7 +7,7 @@ from .draggableWidget import DraggableWidget
7
7
  from .clickableWidget import ClickableWidget
8
8
  from .root import Root
9
9
  from .shape import Shape
10
- from .meter import Meter
10
+ from .meter import BarMeter
11
11
  from .label import Label
12
12
  from .tooltip import ToolTip
13
13
  from .animatedLabel import AnimatedLabel
@@ -18,7 +18,8 @@ from .layout import *
18
18
  from .container import Container
19
19
  from .indicator import *
20
20
  from .toggle import Toggle
21
- from .radioButton import RadioButton, RadioVariable
21
+ from .syncedVar import SyncedVar
22
+ from .radioButton import RadioButton
22
23
  from .slider import Slider
23
24
  from .selector import Selector
24
25
  import batFramework.gui.constraints as constraints
@@ -11,16 +11,18 @@ class ClickableWidget(Shape, InteractiveWidget):
11
11
  def __init__(self, callback: Callable[[],Any] = None, *args, **kwargs) -> None:
12
12
  super().__init__(*args, **kwargs)
13
13
  self.callback = callback
14
- self.is_pressed: bool = False
15
- self.enabled: bool = True
14
+ self.is_pressed: bool = False # the state where the button is being held down (releasing will trigger callback)
15
+ self.is_enabled: bool = True #
16
16
  self.hover_cursor = bf.const.DEFAULT_HOVER_CURSOR
17
17
  self.click_cursor = bf.const.DEFAULT_CLICK_CURSOR
18
+
18
19
  self.click_down_sound = None
19
20
  self.click_up_sound = None
20
21
  self.get_focus_sound = None
21
22
  self.lose_focus_sound = None
22
- self.pressed_relief: int = 1
23
- self.unpressed_relief: int = 2
23
+
24
+ self.pressed_relief: int = 1 # Depth effect height when pressed
25
+ self.unpressed_relief: int = 2 # Depth effect height when released (default)
24
26
  self.silent_focus: bool = False
25
27
  self.set_debug_color("cyan")
26
28
  self.set_relief(self.unpressed_relief)
@@ -98,18 +100,15 @@ class ClickableWidget(Shape, InteractiveWidget):
98
100
  return True
99
101
 
100
102
  def enable(self) -> Self:
101
- self.enabled = True
103
+ self.is_enabled = True
102
104
  self.dirty_surface = True
103
105
  return self
104
106
 
105
107
  def disable(self) -> Self:
106
- self.enabled = False
108
+ self.is_enabled = False
107
109
  self.dirty_surface = True
108
110
  return self
109
111
 
110
- def is_enabled(self) -> bool:
111
- return self.enabled
112
-
113
112
  def set_callback(self, callback: Callable[[],Any]) -> Self:
114
113
  self.callback = callback
115
114
  return self
@@ -134,7 +133,7 @@ class ClickableWidget(Shape, InteractiveWidget):
134
133
  return f"ClickableWidget"
135
134
 
136
135
  def click(self, force=False) -> None:
137
- if not self.enabled and not force:
136
+ if not self.is_enabled and not force:
138
137
  return False
139
138
  if self.callback is not None:
140
139
  self.callback()
@@ -154,33 +153,36 @@ class ClickableWidget(Shape, InteractiveWidget):
154
153
  return self.do_on_key_down(key)
155
154
 
156
155
 
157
- def on_click_down(self, button) -> None:
158
- if super().on_click_down(button):
159
- return True
160
- if self.enabled and button == 1:
161
- if not self.get_focus():
162
- return False
163
- self.is_pressed = True
164
- if self.click_down_sound:
165
- bf.AudioManager().play_sound(self.click_down_sound)
166
- pygame.mouse.set_cursor(self.click_cursor)
167
- self.set_relief(self.pressed_relief)
156
+ def on_click_down(self, button) -> bool:
157
+ if button < 1 or button > 5 : return False
158
+ self.is_clicked_down[button-1] = True
159
+ if self.is_enabled and button == 1:
160
+ if self.get_focus():
161
+ self.is_pressed = True
162
+ if self.click_down_sound:
163
+ bf.AudioManager().play_sound(self.click_down_sound)
164
+ pygame.mouse.set_cursor(self.click_cursor)
165
+ self.set_relief(self.pressed_relief)
166
+ self.do_on_click_down(button)
168
167
  return True
169
168
  return False
170
169
 
171
- def on_click_up(self, button) -> None:
172
- if super().on_click_up(button):
173
- return True
174
- if self.enabled and button == 1 and self.is_pressed:
170
+ def on_click_up(self, button):
171
+ if button < 1 or button > 5 : return False
172
+ self.is_clicked_down[button-1] = False
173
+ if self.is_enabled and button == 1 and self.is_pressed:
175
174
  self.is_pressed = False
176
175
  if self.click_up_sound:
177
176
  bf.AudioManager().play_sound(self.click_up_sound)
178
177
  self.set_relief(self.unpressed_relief)
179
- return self.click()
178
+ self.click()
179
+ self.do_on_click_up(button)
180
+ return True
180
181
  return False
181
-
182
+
183
+
182
184
  def on_enter(self) -> None:
183
- if not self.enabled:
185
+ if not self.is_enabled:
184
186
  return
185
187
  super().on_enter()
186
188
  self.dirty_surface = True
@@ -192,7 +194,6 @@ class ClickableWidget(Shape, InteractiveWidget):
192
194
  self.set_relief(self.unpressed_relief)
193
195
  self.is_pressed = False
194
196
  self.dirty_surface = True
195
-
196
197
  pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
197
198
 
198
199
  def on_lose_focus(self):
@@ -218,7 +219,7 @@ class ClickableWidget(Shape, InteractiveWidget):
218
219
  self.rect.h - self.unpressed_relief - self.padding[1] - self.padding[3],
219
220
  )
220
221
 
221
- def get_local_padded_rect(self) -> pygame.FRect:
222
+ def get_local_inner_rect(self) -> pygame.FRect:
222
223
  return pygame.FRect(
223
224
  self.padding[0],
224
225
  self.padding[1] + (self.unpressed_relief - self.pressed_relief if self.is_pressed else 0),
@@ -237,7 +238,7 @@ class ClickableWidget(Shape, InteractiveWidget):
237
238
 
238
239
  def paint(self) -> None:
239
240
  super().paint()
240
- if not self.enabled:
241
+ if not self.is_enabled:
241
242
  self._paint_disabled()
242
243
  elif self.is_hovered:
243
244
  self._paint_hovered()
@@ -41,25 +41,19 @@ class Container(Shape, InteractiveWidget):
41
41
  def scrollX_by(self, x: float | int) -> Self:
42
42
  if x == 0:
43
43
  return self
44
- self.scroll.x += x
45
- self.clamp_scroll()
46
- self.dirty_layout = True
44
+ self.set_scroll((self.scroll.x + x, self.scroll.y))
47
45
  return self
48
46
 
49
47
  def scrollY_by(self, y: float | int) -> Self:
50
48
  if y == 0:
51
49
  return self
52
- self.scroll.y += y
53
- self.clamp_scroll()
54
- self.dirty_layout = True
50
+ self.set_scroll((self.scroll.x, self.scroll.y + y))
55
51
  return self
56
52
 
57
53
  def scroll_by(self, value: tuple[float | int, float | int]) -> Self:
58
54
  if value[0] == 0 and value[1] == 0:
59
55
  return self
60
- self.scroll += value
61
- self.clamp_scroll()
62
- self.dirty_layout = True
56
+ self.set_scroll((self.scroll.x + value[0], self.scroll.y + value[1]))
63
57
  return self
64
58
 
65
59
  def clamp_scroll(self) -> Self:
@@ -77,9 +71,7 @@ class Container(Shape, InteractiveWidget):
77
71
  new_x = min(max(self.scroll.x, 0), max_scroll_x)
78
72
  new_y = min(max(self.scroll.y, 0), max_scroll_y)
79
73
 
80
- if self.scroll.x != new_x or self.scroll.y != new_y:
81
- self.scroll.x = new_x
82
- self.scroll.y = new_y
74
+ self.set_scroll((new_x, new_y))
83
75
  return self
84
76
 
85
77
  def set_layout(self, layout: Layout) -> Self:
@@ -93,7 +85,10 @@ class Container(Shape, InteractiveWidget):
93
85
  return self
94
86
 
95
87
  def get_interactive_children(self) -> list[InteractiveWidget]:
96
- return [child for child in self.children if isinstance(child, InteractiveWidget) and not isinstance(child,Container) and child.allow_focus_to_self()]
88
+ return [child for child in self.get_layout_children() if isinstance(child, InteractiveWidget) and not isinstance(child,Container) and child.allow_focus_to_self()]
89
+
90
+ def get_layout_children(self)->list[Widget]:
91
+ return self.children
97
92
 
98
93
  def clear_children(self) -> None:
99
94
  self.children.clear()
@@ -202,5 +197,5 @@ class Container(Shape, InteractiveWidget):
202
197
  if self.dirty_surface and not skip_draw:
203
198
  self.paint()
204
199
  self.dirty_surface = False
205
-
200
+
206
201
 
@@ -5,33 +5,40 @@ import pygame
5
5
 
6
6
  class DraggableWidget(InteractiveWidget):
7
7
  def __init__(self, *args, **kwargs) -> None:
8
-
9
8
  self.drag_start = None
10
9
  self.offset = None
10
+ self.click_mask = [True,False,False,False,False]
11
+ self.is_dragged : bool = False # the widget is following the mouse AND the mouse is in the widget
12
+ self.is_dragged_outside : bool = False # the widget is following the mouse BUT the mouse is NOT in the widget
11
13
  super().__init__(*args, **kwargs)
14
+
15
+ def set_click_mask(self,b1=0,b2=0,b3=0,b4=0,b5=0):
16
+ self.click_mask = [b1,b2,b3,b4,b5]
12
17
 
13
18
  def on_click_down(self, button):
14
- if super().on_click_down(button)==False:
15
- return button == 1 # capture event
19
+ super().on_click_down(button)
20
+ return any(i==j and i== True for i,j in zip(self.is_clicked_down,self.click_mask))
16
21
 
17
22
  def do_on_drag(
18
- self, drag_start: tuple[float, float], drag_end: tuple[float, float]
23
+ self, drag_start_pos: tuple[float, float], drag_end_pos: tuple[float, float]
19
24
  ) -> None:
20
- self.set_position(drag_end[0] - self.offset[0], drag_end[1] - self.offset[1])
21
-
25
+ new_pos = drag_end_pos[0] - self.offset[0], drag_end_pos[1] - self.offset[1]
26
+ if self.rect.topleft != new_pos:
27
+ self.set_position(*new_pos)
22
28
 
23
29
  def update(self, dt: float):
24
- if self.is_clicked_down and pygame.mouse.get_pressed(3)[0]:
25
- r = self.get_root()
26
- x, y = r.drawing_camera.screen_to_world(pygame.mouse.get_pos())
27
- if self.drag_start == None and self.is_clicked_down:
30
+ self.is_dragged_outside = any(i==j and i== True for i,j in zip(pygame.mouse.get_pressed(5),self.click_mask))
31
+ self.is_dragged = any(i==j and i== True for i,j in zip(self.is_clicked_down,self.click_mask))
32
+
33
+ if self.is_dragged and self.is_dragged_outside:
34
+ x, y = self.parent_layer.camera.screen_to_world(pygame.mouse.get_pos())
35
+ if self.drag_start == None:
28
36
  self.offset = x - self.rect.x, y - self.rect.y
29
37
  self.drag_start = x, y
30
38
  else:
31
39
  self.do_on_drag(self.drag_start, (x, y))
32
-
33
40
  else:
34
41
  self.drag_start = None
35
42
  self.offset = None
36
- self.is_clicked_down = False
43
+ self.is_clicked_down = [False]*5
37
44
  super().update(dt)
@@ -68,61 +68,44 @@ class ArrowIndicator(Indicator):
68
68
  self.direction : bf.direction = direction
69
69
  self.arrow_color = bf.color.WHITE
70
70
  self.line_width : int = 1
71
- self.angle : float = 45
72
- self.spread : float = None
73
- self.draw_stem : bool = True
74
-
75
- def set_draw_stem(self,value:bool)->Self:
76
- self.draw_stem = value
77
- self.dirty_surface = False
78
- return self
79
-
80
- def set_spread(self,value:float)->Self:
81
- self.spread = value
82
- self.dirty_surface = True
83
- return self
84
71
 
85
- def set_angle(self,value:float)->Self:
86
- self.angle = value
87
- self.dirty_surface = True
88
- return self
89
-
90
72
  def set_arrow_color(self,color)-> Self:
91
73
  self.arrow_color = color
92
74
  self.dirty_surface = True
93
75
  return self
94
76
 
95
- def set_direction(self,direction:bf.direction)->Self:
77
+ def set_arrow_direction(self,direction:bf.direction)->Self:
96
78
  self.direction = direction
97
79
  self.dirty_surface = True
98
80
  return self
99
81
 
100
- def set_line_width(self,value:int)->Self:
82
+ def set_arrow_line_width(self,value:int)->Self:
101
83
  self.line_width = value
102
84
  self.dirty_surface = True
103
85
  return self
104
86
 
105
87
  def paint(self):
106
88
  super().paint()
107
- r = self.get_local_padded_rect()
89
+ r = self.get_local_inner_rect()
108
90
  size = min(r.width, r.height)
109
91
  if size %2 == 0:
110
92
  size -= 1
111
93
  r.width = size
112
94
  r.height = size
95
+
96
+ #pixel alignment
113
97
  if (self.padding[1]+self.padding[3] )%2 ==0:
114
98
  r.height-=1
115
99
  if (self.padding[0]+self.padding[2] )%2 ==0:
116
100
  r.width-=1
117
- r.center = self.get_local_padded_rect().center
118
- # r.inflate_ip(3,3)
119
- # r.normalize()
120
- # r.move_ip(-self.rect.left,-self.rect.right)
101
+ r.center = self.get_local_inner_rect().center
102
+
121
103
  bf.utils.draw_triangle(
122
104
  surface = self.surface,
123
105
  color = self.arrow_color,
124
106
  rect =r,
125
107
  direction = self.direction,
108
+ width = self.line_width
126
109
 
127
110
  )
128
111
 
@@ -18,10 +18,11 @@ def children_has_focus(widget)->bool:
18
18
 
19
19
 
20
20
  class InteractiveWidget(Widget):
21
+ __focus_effect_cache = {}
21
22
  def __init__(self, *args, **kwargs) -> None:
22
23
  self.is_focused: bool = False
23
24
  self.is_hovered: bool = False
24
- self.is_clicked_down: bool = False
25
+ self.is_clicked_down: list[bool] = [False]*5
25
26
  self.focused_index = 0
26
27
  self.focusable = True
27
28
  super().__init__(*args, **kwargs)
@@ -136,13 +137,13 @@ class InteractiveWidget(Widget):
136
137
 
137
138
  def on_key_down(self, key) -> bool:
138
139
  """
139
- return True to stop event progpagation
140
+ return True to stop event propagation
140
141
  """
141
142
  return self.do_on_key_down(key)
142
143
 
143
144
  def on_key_up(self, key) -> bool:
144
145
  """
145
- return True to stop event progpagation
146
+ return True to stop event propagation
146
147
  """
147
148
  return self.do_on_key_up(key)
148
149
 
@@ -154,42 +155,37 @@ class InteractiveWidget(Widget):
154
155
 
155
156
  def do_on_key_down(self, key) -> bool:
156
157
  """
157
- return True to stop event progpagation
158
+ return True to stop event propagation
158
159
  """
159
160
  return False
160
161
 
161
162
  def do_on_key_up(self, key) -> bool:
162
163
  """
163
- return True to stop event progpagation
164
+ return True to stop event propagation
164
165
  """
165
166
  return False
166
167
 
167
-
168
168
  def on_click_down(self, button: int) -> bool:
169
169
  """
170
- return True to stop event progpagation
170
+ return True to stop event propagation
171
171
  """
172
- self.is_clicked_down = True
172
+ if button < 1 or button > 5 : return False
173
+ self.is_clicked_down[button-1] = True
173
174
  return self.do_on_click_down(button)
174
175
 
175
176
  def on_click_up(self, button: int) -> bool:
176
177
  """
177
- return True to stop event progpagation
178
+ return True to stop event propagation
178
179
  """
179
- self.is_clicked_down = False
180
+ if button < 1 or button > 5 : return False
181
+ self.is_clicked_down[button-1] = False
180
182
  return self.do_on_click_up(button)
181
183
 
182
- def do_on_click_down(self, button: int) -> bool:
183
- """
184
- return True to stop event progpagation
185
- """
186
- return False
184
+ def do_on_click_down(self, button: int) -> None:
185
+ return
187
186
 
188
- def do_on_click_up(self, button: int) -> bool:
189
- """
190
- return True to stop event progpagation
191
- """
192
- return False
187
+ def do_on_click_up(self, button: int) -> None:
188
+ return
193
189
 
194
190
  def on_enter(self) -> None:
195
191
  self.is_hovered = True
@@ -197,7 +193,7 @@ class InteractiveWidget(Widget):
197
193
 
198
194
  def on_exit(self) -> None:
199
195
  self.is_hovered = False
200
- self.is_clicked_down = False
196
+ self.is_clicked_down = [False]*5
201
197
  self.do_on_exit()
202
198
 
203
199
  def do_on_enter(self) -> None:
@@ -226,12 +222,18 @@ class InteractiveWidget(Widget):
226
222
  # Shrink for inner pulsing border
227
223
  inner = screen_rect.inflate(-delta, -delta)
228
224
  inner.topleft = 0,0
229
- surface = pygame.Surface(inner.size)
225
+ inner.w = round(inner.w)
226
+ inner.h = round(inner.h)
227
+
228
+ surface = InteractiveWidget.__focus_effect_cache.get(inner.size)
229
+ if surface is None:
230
+ surface = pygame.Surface(inner.size)
231
+ InteractiveWidget.__focus_effect_cache[inner.size] = surface
232
+
230
233
  surface.set_colorkey((0,0,0))
231
234
  pygame.draw.rect(surface, "white", inner, 2, *self.border_radius)
232
- pygame.draw.rect(surface, "black", inner.inflate(-16,0), 2)
233
- pygame.draw.rect(surface, "black", inner.inflate(0,-16), 2)
235
+ pygame.draw.rect(surface, "black", inner.inflate(-1 * min(16,inner.w*0.75),0), 2)
236
+ pygame.draw.rect(surface, "black", inner.inflate(0,-1 * min(16,inner.h*0.75)), 2)
234
237
  inner.center = screen_rect.center
235
238
  camera.surface.blit(surface,inner)
236
239
 
237
- # pygame.draw.rect(camera.surface, "white", inner, 2, *self.border_radius)
batFramework/gui/label.py CHANGED
@@ -273,7 +273,7 @@ class Label(Shape):
273
273
  # return True
274
274
 
275
275
  self.text_rect.size = self._get_text_rect_required_size()
276
- padded = self.get_local_padded_rect()
276
+ padded = self.get_local_inner_rect()
277
277
  self.align_text(self.text_rect, padded, self.alignment)
278
278
  return ret
279
279
 
@@ -312,7 +312,7 @@ class Label(Shape):
312
312
  # clip surface
313
313
 
314
314
  old = self.surface.get_clip()
315
- self.surface.set_clip(self.get_local_padded_rect())
315
+ self.surface.set_clip(self.get_local_inner_rect())
316
316
  self.surface.fblits(l)
317
317
  self.surface.set_clip(old)
318
318
 
@@ -338,7 +338,6 @@ class Label(Shape):
338
338
  return size_changed
339
339
 
340
340
 
341
-
342
341
  def paint(self) -> None:
343
342
  super().paint()
344
343
  if self.font_object: