batframework 1.0.8a2__py3-none-any.whl → 1.0.8a4__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.
Files changed (65) hide show
  1. batFramework/__init__.py +53 -50
  2. batFramework/action.py +126 -99
  3. batFramework/actionContainer.py +53 -9
  4. batFramework/animatedSprite.py +117 -73
  5. batFramework/audioManager.py +69 -26
  6. batFramework/camera.py +259 -69
  7. batFramework/constants.py +16 -54
  8. batFramework/cutscene.py +39 -29
  9. batFramework/cutsceneBlocks.py +36 -43
  10. batFramework/dynamicEntity.py +17 -9
  11. batFramework/easingController.py +58 -0
  12. batFramework/entity.py +48 -97
  13. batFramework/enums.py +113 -0
  14. batFramework/fontManager.py +65 -0
  15. batFramework/gui/__init__.py +10 -2
  16. batFramework/gui/button.py +9 -78
  17. batFramework/gui/clickableWidget.py +221 -0
  18. batFramework/gui/constraints/__init__.py +1 -0
  19. batFramework/gui/constraints/constraints.py +730 -0
  20. batFramework/gui/container.py +174 -32
  21. batFramework/gui/debugger.py +131 -43
  22. batFramework/gui/dialogueBox.py +99 -0
  23. batFramework/gui/draggableWidget.py +40 -0
  24. batFramework/gui/image.py +54 -18
  25. batFramework/gui/indicator.py +38 -21
  26. batFramework/gui/interactiveWidget.py +177 -13
  27. batFramework/gui/label.py +292 -74
  28. batFramework/gui/layout.py +219 -60
  29. batFramework/gui/meter.py +71 -0
  30. batFramework/gui/radioButton.py +84 -0
  31. batFramework/gui/root.py +134 -38
  32. batFramework/gui/shape.py +259 -57
  33. batFramework/gui/slider.py +230 -0
  34. batFramework/gui/style.py +10 -0
  35. batFramework/gui/styleManager.py +48 -0
  36. batFramework/gui/textInput.py +137 -0
  37. batFramework/gui/toggle.py +103 -51
  38. batFramework/gui/widget.py +329 -254
  39. batFramework/manager.py +40 -19
  40. batFramework/object.py +114 -0
  41. batFramework/particle.py +101 -0
  42. batFramework/renderGroup.py +67 -0
  43. batFramework/resourceManager.py +100 -0
  44. batFramework/scene.py +281 -123
  45. batFramework/sceneManager.py +141 -108
  46. batFramework/scrollingSprite.py +114 -0
  47. batFramework/sprite.py +51 -0
  48. batFramework/stateMachine.py +2 -2
  49. batFramework/tileset.py +46 -0
  50. batFramework/time.py +123 -58
  51. batFramework/transition.py +195 -124
  52. batFramework/utils.py +87 -151
  53. batframework-1.0.8a4.dist-info/LICENCE +21 -0
  54. batframework-1.0.8a4.dist-info/METADATA +55 -0
  55. batframework-1.0.8a4.dist-info/RECORD +58 -0
  56. batFramework/debugger.py +0 -48
  57. batFramework/easing.py +0 -71
  58. batFramework/gui/constraints.py +0 -204
  59. batFramework/gui/frame.py +0 -19
  60. batFramework/particles.py +0 -77
  61. batFramework/transitionManager.py +0 -0
  62. batframework-1.0.8a2.dist-info/METADATA +0 -58
  63. batframework-1.0.8a2.dist-info/RECORD +0 -42
  64. {batframework-1.0.8a2.dist-info → batframework-1.0.8a4.dist-info}/WHEEL +0 -0
  65. {batframework-1.0.8a2.dist-info → batframework-1.0.8a4.dist-info}/top_level.txt +0 -0
@@ -1,81 +1,240 @@
1
1
  import batFramework as bf
2
2
  from .widget import Widget
3
- from .constraints import *
4
- from typing import Self
3
+ from .constraints.constraints import *
4
+ from typing import Self, TYPE_CHECKING
5
+ import pygame
6
+
7
+ if TYPE_CHECKING:
8
+ from .container import Container
9
+
5
10
 
6
11
  class Layout:
7
- def __init__(self, parent: Widget=None):
12
+ def __init__(self, parent: "Container" = None):
8
13
  self.parent = parent
9
- self.child_constraints : list[Constraint] = []
10
-
11
- def set_child_constraints(self,*constraints)->Self:
12
- self.child_constraints = constraints
13
- self.arrange()
14
+ self.child_constraints: list[Constraint] = []
15
+ self.children_rect = pygame.FRect(0, 0, 0, 0)
16
+
17
+ def set_child_constraints(self, *constraints) -> Self:
18
+ self.child_constraints = list(constraints)
19
+ self.notify_parent()
14
20
  return self
15
-
16
- def set_parent(self,parent:Widget):
21
+
22
+ def set_parent(self, parent: Widget):
17
23
  self.parent = parent
18
- self.arrange()
24
+ self.notify_parent()
25
+
26
+ def notify_parent(self) -> None:
27
+ if self.parent:
28
+ self.parent.dirty_children = True
29
+
30
+ def arrange(self) -> None:
31
+ return
32
+
33
+ def get_raw_size(self) -> tuple[float, float]:
34
+ """
35
+ Returns the supposed size the container should have to encapsulate perfectly all of its widgets
36
+ """
37
+ return self.parent.rect.size if self.parent else (0, 0)
38
+
39
+ def get_auto_size(self) -> tuple[float, float]:
40
+ """
41
+ Returns the final size the container should have (while keeping the the width and height if they are non-resizable)
42
+ """
43
+ target_size = list(self.get_raw_size())
44
+ if not self.parent.autoresize_w:
45
+ target_size[0] = self.parent.rect.w
46
+ if not self.parent.autoresize_h:
47
+ target_size[1] = self.parent.rect.h
48
+ return target_size
49
+
50
+ def focus_next_child(self) -> None:
51
+ l = self.parent.get_interactive_children()
52
+ self.parent.focused_index = min(self.parent.focused_index + 1, len(l) - 1)
53
+ focused = l[self.parent.focused_index]
54
+ focused.get_focus()
55
+ self.scroll_to_widget(focused)
56
+
57
+ def focus_prev_child(self) -> None:
58
+ l = self.parent.get_interactive_children()
59
+ self.parent.focused_index = max(self.parent.focused_index - 1, 0)
60
+ focused = l[self.parent.focused_index]
61
+ focused.get_focus()
62
+ self.scroll_to_widget(focused)
63
+
64
+ def scroll_to_widget(self, widget: Widget) -> None:
65
+ padded = self.parent.get_padded_rect()
66
+ r = widget.rect
67
+ if padded.contains(r):
68
+ return
69
+ clamped = r.clamp(padded)
70
+ dx, dy = clamped.move(-r.x, -r.y).topleft
71
+ self.parent.scroll_by((-dx, -dy))
72
+
73
+ def handle_event(self, event):
74
+ pass
19
75
 
20
- def arrange(self)->None:
21
- raise NotImplementedError("Subclasses must implement arrange method")
22
76
 
23
77
  class Column(Layout):
24
- def __init__(self,gap:int=0,shrink:bool=False):
78
+ def __init__(self, gap: int = 0, spacing: bf.spacing = bf.spacing.MANUAL):
25
79
  super().__init__()
26
80
  self.gap = gap
27
- self.shrink :bool = shrink
28
-
29
- def arrange(self)->None:
30
- if not self.parent or not self.parent.children : return
31
- if self.shrink:
32
- len_children = len(self.parent.children)
33
- parent_height = sum(c.rect.h for c in self.parent.children)
34
- parent_width = max(c.rect.w for c in self.parent.children)
35
- if self.gap : parent_height += (len_children-1) * self.gap
36
- # print(self.parent.to_string(),len_children,parent_height)
37
- c = self.parent.get_constraint("height")
38
- if not c or c.height != parent_height :
39
- self.parent.add_constraint(ConstraintHeight(parent_height))
40
- c = self.parent.get_constraint("width")
41
- if not c or c.width != parent_width :
42
- self.parent.add_constraint(ConstraintWidth(parent_width))
43
- current_y = self.parent.rect.top
81
+ self.spacing = spacing
82
+
83
+ def set_gap(self, value: float) -> Self:
84
+ self.gap = value
85
+ self.notify_parent()
86
+ return self
87
+
88
+ def set_spacing(self, spacing: bf.spacing) -> Self:
89
+ self.spacing = spacing
90
+ self.notify_parent()
91
+ return self
92
+
93
+ def get_raw_size(self) -> tuple[float, float]:
94
+ len_children = len(self.parent.children)
95
+ if not len_children:
96
+ return self.parent.rect.size
97
+ parent_height = sum(c.get_min_required_size()[1] for c in self.parent.children)
98
+ parent_width = max(c.get_min_required_size()[0] for c in self.parent.children)
99
+ if self.gap:
100
+ parent_height += (len_children - 1) * self.gap
101
+ target_rect = self.parent.inflate_rect_by_padding(
102
+ (0, 0, parent_width, parent_height)
103
+ )
104
+ return target_rect.size
105
+
106
+ def get_auto_size(self) -> tuple[float, float]:
107
+ target_size = list(self.get_raw_size())
108
+ if not self.parent.autoresize_w:
109
+ target_size[0] = self.parent.rect.w
110
+ if not self.parent.autoresize_h:
111
+ target_size[1] = self.parent.rect.h
112
+ return target_size
113
+
114
+ def arrange(self) -> None:
115
+ if not self.parent or not self.parent.children:
116
+ return
117
+ if self.child_constraints:
118
+ for child in self.parent.children:
119
+ child.add_constraints(*self.child_constraints)
120
+ self.child_rect = self.parent.get_padded_rect()
44
121
 
122
+ if self.parent.autoresize_w or self.parent.autoresize_h:
123
+ width, height = self.get_auto_size()
124
+ if self.parent.rect.size != (width, height):
125
+ self.parent.set_size((width, height))
126
+ self.parent.build()
127
+ self.arrange()
128
+ return
129
+ self.child_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
130
+ y = self.child_rect.top
45
131
  for child in self.parent.children:
46
- child.set_position(self.parent.rect.x,current_y)
47
- current_y += child.rect.h + self.gap
48
- for c in self.child_constraints:
49
- if not child.has_constraint(c.name):
50
- child.add_constraint(c)
51
-
132
+ child.set_position(self.child_rect.x, y)
133
+ y += child.get_min_required_size()[1] + self.gap
134
+
135
+ def handle_event(self, event):
136
+ if self.parent.autoresize_h or not self.parent.visible:
137
+ return
138
+
139
+ if not self.parent.children:
140
+ return
141
+
142
+ if event.type == pygame.MOUSEBUTTONDOWN:
143
+ r = self.parent.get_root()
144
+ if not r:
145
+ return
146
+
147
+ if self.parent.rect.collidepoint(
148
+ r.drawing_camera.screen_to_world(pygame.mouse.get_pos())
149
+ ):
150
+ if event.button == 4:
151
+ self.parent.scroll_by((0, -10))
152
+ elif event.button == 5:
153
+ self.parent.scroll_by((0, 10))
154
+ else:
155
+ return
156
+ event.consumed = True
157
+ self.parent.clamp_scroll()
158
+
159
+
52
160
  class Row(Layout):
53
- def __init__(self, gap: int = 0, shrink: bool = False):
161
+ def __init__(self, gap: int = 0, spacing: bf.spacing = bf.spacing.MANUAL):
54
162
  super().__init__()
55
163
  self.gap = gap
56
- self.shrink = shrink
164
+ self.spacing = spacing
165
+
166
+ def set_gap(self, value: float) -> Self:
167
+ self.gap = value
168
+ self.notify_parent()
169
+ return self
170
+
171
+ def set_spacing(self, spacing: bf.spacing) -> Self:
172
+ self.spacing = spacing
173
+ self.notify_parent()
174
+ return self
175
+
176
+ def get_raw_size(self) -> tuple[float, float]:
177
+ len_children = len(self.parent.children)
178
+ if not len_children:
179
+ return self.parent.rect.size
180
+ parent_width = sum(c.get_min_required_size()[0] for c in self.parent.children)
181
+ parent_height = max(c.get_min_required_size()[1] for c in self.parent.children)
182
+ if self.gap:
183
+ parent_width += (len_children - 1) * self.gap
184
+ target_rect = self.parent.inflate_rect_by_padding(
185
+ (0, 0, parent_width, parent_height)
186
+ )
187
+
188
+ return target_rect.size
189
+
190
+ def get_auto_size(self) -> tuple[float, float]:
191
+ target_size = list(self.get_raw_size())
192
+ if not self.parent.autoresize_w:
193
+ target_size[0] = self.parent.rect.w
194
+ if not self.parent.autoresize_h:
195
+ target_size[1] = self.parent.rect.h
196
+ return target_size
57
197
 
58
198
  def arrange(self) -> None:
59
- if not self.parent:
199
+ if not self.parent or not self.parent.children:
60
200
  return
61
- if self.shrink and self.parent.children:
62
- len_children = len(self.parent.children)
63
- parent_width = sum(c.rect.w for c in self.parent.children)
64
- parent_height = max(c.rect.h for c in self.parent.children)
65
- if self.gap:
66
- parent_width += (len_children - 1) * self.gap
67
-
68
- c = self.parent.get_constraint("width")
69
- if not c or c.width != parent_width:
70
- self.parent.add_constraint(ConstraintWidth(parent_width))
71
- c = self.parent.get_constraint("height")
72
- if not c or c.height != parent_height:
73
- self.parent.add_constraint(ConstraintHeight(parent_height))
74
-
75
- current_x = self.parent.rect.left
201
+ if self.child_constraints:
202
+ for child in self.parent.children:
203
+ child.add_constraints(*self.child_constraints)
204
+ self.child_rect = self.parent.get_padded_rect()
205
+
206
+ if self.parent.autoresize_w or self.parent.autoresize_h:
207
+ width, height = self.get_auto_size()
208
+ if self.parent.rect.size != (width, height):
209
+ self.parent.set_size((width, height))
210
+ self.parent.build()
211
+ self.arrange()
212
+ return
213
+ self.child_rect.move_ip(-self.parent.scroll.x, -self.parent.scroll.y)
214
+ x = self.child_rect.left
76
215
  for child in self.parent.children:
77
- child.set_position(current_x,self.parent.rect.y)
78
- current_x += child.rect.w + self.gap
79
- for c in self.child_constraints:
80
- if not child.has_constraint(c.name):
81
- child.add_constraint(c)
216
+ child.set_position(x, self.child_rect.y)
217
+ x += child.get_min_required_size()[0] + self.gap
218
+
219
+ def handle_event(self, event):
220
+ if self.parent.autoresize_w or not self.parent.visible:
221
+ return
222
+
223
+ if not self.parent.children:
224
+ return
225
+
226
+ if event.type == pygame.MOUSEBUTTONDOWN:
227
+ r = self.parent.get_root()
228
+ if not r:
229
+ return
230
+ if self.parent.rect.collidepoint(
231
+ r.drawing_camera.screen_to_world(pygame.mouse.get_pos())
232
+ ):
233
+ if event.button == 4:
234
+ self.parent.scroll_by((-10, 0))
235
+ elif event.button == 5:
236
+ self.parent.scroll_by((10, 0))
237
+ else:
238
+ return
239
+ event.consumed = True
240
+ self.parent.clamp_scroll()
@@ -0,0 +1,71 @@
1
+ import batFramework as bf
2
+ from .shape import Shape
3
+ from typing import Self
4
+
5
+
6
+ def custom_top_at(self, x, y):
7
+ if Shape.top_at(self, x, y) == self:
8
+ return self.parent
9
+ return None
10
+
11
+
12
+ class Meter(Shape):
13
+ def __init__(self, min_value: float = 0, max_value: float = 1, step: float = 0.1):
14
+ super().__init__()
15
+ self.min_value, self.max_value = min_value, max_value
16
+ self.step = step
17
+ self.snap: bool = False
18
+ self.value = self.max_value
19
+ self.content = Shape((0, 0)).set_color(bf.color.BLUE)
20
+ self.content.top_at = lambda x, y: custom_top_at(self.content, x, y)
21
+ self.add(self.content)
22
+ self.set_padding(1)
23
+ self.set_outline_width(1)
24
+ self.set_outline_color(bf.color.BLACK)
25
+ self.set_debug_color("pink")
26
+
27
+ def __str__(self) -> str:
28
+ return "Meter"
29
+
30
+ def set_step(self, step: float) -> Self:
31
+ self.step = step
32
+ self.set_value(self.value)
33
+ return self
34
+
35
+ def set_range(self, range_min: float, range_max: float) -> Self:
36
+ if range_min >= range_max:
37
+ print(
38
+ f"[Warning] : minimum value {range_min} is greater than or equal to maximum value {range_max}"
39
+ )
40
+ return self
41
+ self.min_value, self.max_value = range_min, range_max
42
+ self.dirty_shape = True
43
+
44
+ def get_debug_outlines(self):
45
+ yield from super().get_debug_outlines()
46
+ yield from self.content.get_debug_outlines()
47
+
48
+ def set_value(self, value: float) -> Self:
49
+ value = max(self.min_value, min(self.max_value, value))
50
+ value = round(value / self.step) * self.step
51
+ self.value = value
52
+ self.dirty_surface = True
53
+ return self
54
+
55
+ def get_value(self) -> float:
56
+ return self.value
57
+
58
+ def get_range(self) -> float:
59
+ return self.max_value - self.min_value
60
+
61
+ def get_ratio(self) -> float:
62
+ return self.value / (self.max_value - self.min_value)
63
+
64
+ def _build_content(self) -> None:
65
+ width = self.get_padded_width() * self.get_ratio()
66
+ self.content.set_size((width, self.get_padded_height()))
67
+ self.content.set_position(*self.get_padded_rect().topleft)
68
+
69
+ def build(self) -> None:
70
+ super().build()
71
+ self._build_content()
@@ -0,0 +1,84 @@
1
+ import batFramework as bf
2
+ from typing import Self, Any, Callable
3
+ from .toggle import Toggle
4
+
5
+
6
+ class RadioVariable: ...
7
+
8
+
9
+ class RadioButton(Toggle):
10
+ def __init__(self, text: str, radio_value: Any = None) -> None:
11
+ super().__init__(text, None, False)
12
+ self.radio_value: Any = (
13
+ radio_value if radio_value is not None else text if text else None
14
+ )
15
+ self.radio_variable: RadioVariable = None
16
+
17
+ def __str__(self) -> str:
18
+ return (
19
+ f"RadioButton({self.radio_value}|{'Active' if self.value else 'Inactive'})"
20
+ )
21
+
22
+ def set_radio_value(self, value: Any) -> Self:
23
+ self.radio_value = value
24
+
25
+ if self.radio_variable:
26
+ self.radio_variable.update_buttons()
27
+ return self
28
+
29
+ def set_text(self, text: str) -> Self:
30
+ flag = False
31
+ if self.value == self.text or self.value is None:
32
+ flag = True
33
+ super().set_text(text)
34
+ if flag:
35
+ self.set_radio_value(self.text)
36
+ return self
37
+
38
+ def click(self) -> None:
39
+ if self.radio_variable is None:
40
+ return
41
+ self.radio_variable.set_value(self.radio_value)
42
+
43
+
44
+ class RadioVariable:
45
+ def __init__(self) -> None:
46
+ self.buttons: list[RadioButton] = []
47
+ self.value = None
48
+ self.modify_callback: Callable[[Any],] = None
49
+
50
+ def set_modify_callback(self, callback) -> Self:
51
+ self.modify_callback = callback
52
+ return self
53
+
54
+ def link(self, *buttons: RadioButton) -> Self:
55
+ if not buttons:
56
+ return self
57
+ for b in buttons:
58
+ b.radio_variable = self
59
+ self.buttons.extend(buttons)
60
+
61
+ if self.value is None:
62
+ self.set_value(buttons[0].radio_value)
63
+ else:
64
+ self.update_buttons()
65
+ return self
66
+
67
+ def unlink(self, *buttons) -> Self:
68
+ for b in self.buttons:
69
+ if b in buttons:
70
+ b.radio_variable = None
71
+ self.buttons = [b for b in self.buttons if b not in buttons]
72
+ return self
73
+
74
+ def set_value(self, value: Any) -> Self:
75
+ if value == self.value:
76
+ return
77
+ self.value = value
78
+ if self.modify_callback:
79
+ self.modify_callback(value)
80
+ self.update_buttons()
81
+ return self
82
+
83
+ def update_buttons(self):
84
+ _ = [b.set_value(b.radio_value == self.value, False) for b in self.buttons]
batFramework/gui/root.py CHANGED
@@ -2,59 +2,155 @@ import batFramework as bf
2
2
  from .interactiveWidget import InteractiveWidget
3
3
  from .widget import Widget
4
4
  import pygame
5
+ from typing import Self
6
+ import sys
7
+
5
8
 
6
9
  class Root(InteractiveWidget):
7
- def __init__(self):
10
+ def __init__(self, camera) -> None:
8
11
  super().__init__()
9
- self.surface = None
10
- self.set_root()
12
+ self.is_root = True
13
+ self.drawing_camera: bf.Camera = camera
14
+ self.visible = False
11
15
  self.rect.size = pygame.display.get_surface().get_size()
12
- self.focused : InteractiveWidget = self
13
- self.hovered : Widget|None = self
14
- self.set_debug_color("purple")
16
+ self.focused: InteractiveWidget | None = self
17
+ self.hovered: Widget | None = self
18
+ self.set_debug_color("yellow")
19
+ self.set_render_order(sys.maxsize)
20
+ self.clip_children = False
21
+
22
+ def __str__(self) -> str:
23
+ return "Root"
15
24
 
16
- def to_string(self)->str:
17
- return "ROOT"
25
+ def set_parent_scene(self, parent_scene: bf.Scene) -> Self:
26
+ bf.StyleManager().register_widget(self)
27
+ return super().set_parent_scene(parent_scene)
18
28
 
19
- def get_focused(self)->Widget|None:
29
+ def get_focused(self) -> Widget | None:
20
30
  return self.focused
21
31
 
22
- def get_hovered(self)->Widget|None:
32
+ def get_hovered(self) -> Widget | None:
23
33
  return self.hovered
24
-
25
- def to_string_id(self)->str:
26
- return "ROOT"
27
34
 
28
- def focus_on(self,widget:InteractiveWidget)->None:
29
- if self.focused is not None:
35
+ def clear_focused(self) -> None:
36
+ self.focus_on(None)
37
+
38
+ def clear_hovered(self) -> None:
39
+ if isinstance(self.hovered, InteractiveWidget):
40
+ self.hovered.on_exit()
41
+ self.hovered = None
42
+
43
+ def get_debug_outlines(self):
44
+ yield (self.rect, self.debug_color)
45
+ for child in self.children:
46
+ yield from child.get_debug_outlines()
47
+
48
+ def focus_on(self, widget: InteractiveWidget | None) -> None:
49
+ if widget == self.focused:
50
+ return
51
+ if widget and not widget.allow_focus_to_self():
52
+ return
53
+ if self.focused is not None:
30
54
  self.focused.on_lose_focus()
31
- if widget is None :
55
+ if widget is None:
32
56
  self.focused = self
33
57
  return
34
- self.focused= widget
58
+ self.focused = widget
35
59
  self.focused.on_get_focus()
36
60
 
37
- def set_size(self,width:float,height:float,force:bool=False)->"Root":
38
- if not force : return self
39
- self.rect.size = width,height
40
- self.build(apply_constraints=True)
61
+ def get_by_tags(self, *tags) -> list[Widget]:
62
+ res = []
63
+
64
+ def getter(w: Widget):
65
+ nonlocal res
66
+ if any(t in w.tags for t in tags):
67
+ res.append(w)
68
+
69
+ self.visit(getter)
70
+ return res
71
+
72
+ def focus_next_tab(self, widget):
73
+ return True
74
+
75
+ def focus_prev_tab(self, widget):
76
+ return True
77
+
78
+ def get_by_uid(self, uid: int) -> "Widget":
79
+ def helper(w: "Widget", uid: int) -> "Widget":
80
+ if w.uid == uid:
81
+ return w
82
+ for child in w.children:
83
+ res = helper(child, uid)
84
+ if res is not None:
85
+ return res
86
+ return None
87
+
88
+ return helper(self, uid)
89
+
90
+ def set_size(self, size: tuple[float, float], force: bool = False) -> "Root":
91
+ if not force:
92
+ return self
93
+ self.rect.size = size
94
+ # self.build(resolve_constraints=True)
41
95
  return self
42
-
43
- def build(self,apply_constraints:bool=False)->None:
44
- if apply_constraints : self.apply_all_constraints()
45
- for child in self.children :
46
- child.build()
47
-
48
- def do_handle_event(self,event):
49
- if event.type == pygame.VIDEORESIZE:
50
- self.set_size(event.w,event.h,force=True)
51
- return True
52
- return False
53
-
54
- def update(self,dt:float)->None:
55
- super().update(dt)
56
- self.hovered = self.top_at(*pygame.mouse.get_pos()) if self.top_at(*pygame.mouse.get_pos()) else None
57
-
58
96
 
97
+ def do_handle_event(self, event):
98
+ if not self.parent_scene.get_sharedVar("player_has_control"):
99
+ return
100
+ if self.focused:
101
+ if event.type == pygame.KEYDOWN:
102
+ if self.focused.on_key_down(event.key):
103
+ event.consumed = True
104
+ elif event.type == pygame.KEYUP:
105
+ if self.focused.on_key_up(event.key):
106
+ event.consumed = True
107
+
108
+ if not self.hovered or (not isinstance(self.hovered, InteractiveWidget)):
109
+ return
110
+
111
+ if event.type == pygame.MOUSEBUTTONDOWN:
112
+ if self.hovered.on_click_down(event.button):
113
+ event.consumed = True
114
+
115
+ elif event.type == pygame.MOUSEBUTTONUP:
116
+ if self.hovered.on_click_up(event.button):
117
+ event.consumed = True
118
+
119
+ def do_on_click_down(self, button: int) -> None:
120
+ if button == 1:
121
+ self.clear_focused()
59
122
 
60
-
123
+ def top_at(self, x: float | int, y: float | int) -> "None|Widget":
124
+ if self.children:
125
+ for child in reversed(self.children):
126
+ r = child.top_at(x, y)
127
+ if r is not None:
128
+ return r
129
+ return self if self.rect.collidepoint(x, y) else None
130
+
131
+ def update(self, dt: float) -> None:
132
+ super().update(dt)
133
+ if not self.parent_scene.get_sharedVar("player_has_control",True):
134
+ return
135
+ old = self.hovered
136
+ transposed = self.drawing_camera.screen_to_world(pygame.mouse.get_pos())
137
+ self.hovered = self.top_at(*transposed) if self.top_at(*transposed) else None
138
+ if old == self.hovered and isinstance(self.hovered, InteractiveWidget):
139
+ self.hovered.on_mouse_motion(*transposed)
140
+ return
141
+ if old and isinstance(old, InteractiveWidget):
142
+ old.on_exit()
143
+ if self.hovered and isinstance(self.hovered, InteractiveWidget):
144
+ self.hovered.on_enter()
145
+
146
+ def draw(self, camera: bf.Camera) -> None:
147
+ super().draw(camera)
148
+ if not self.parent_scene.get_sharedVar("player_has_control"):
149
+ return
150
+ if (
151
+ self.parent_scene
152
+ and self.parent_scene.active
153
+ and self.focused
154
+ and self.focused != self
155
+ ):
156
+ self.focused.draw_focused(camera)