batframework 1.0.6__py3-none-any.whl → 1.0.8a1__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 (59) hide show
  1. batFramework/__init__.py +23 -14
  2. batFramework/action.py +95 -106
  3. batFramework/actionContainer.py +11 -8
  4. batFramework/animatedSprite.py +60 -43
  5. batFramework/audioManager.py +52 -22
  6. batFramework/camera.py +87 -72
  7. batFramework/constants.py +19 -41
  8. batFramework/cutscene.py +12 -11
  9. batFramework/cutsceneBlocks.py +12 -14
  10. batFramework/dynamicEntity.py +5 -4
  11. batFramework/easingController.py +58 -0
  12. batFramework/entity.py +37 -130
  13. batFramework/enums.py +93 -3
  14. batFramework/fontManager.py +15 -7
  15. batFramework/gui/__init__.py +5 -1
  16. batFramework/gui/button.py +6 -144
  17. batFramework/gui/clickableWidget.py +206 -0
  18. batFramework/gui/constraints/__init__.py +1 -0
  19. batFramework/gui/constraints/constraints.py +378 -0
  20. batFramework/gui/container.py +147 -34
  21. batFramework/gui/debugger.py +39 -22
  22. batFramework/gui/dialogueBox.py +69 -43
  23. batFramework/gui/draggableWidget.py +38 -0
  24. batFramework/gui/image.py +33 -28
  25. batFramework/gui/indicator.py +30 -16
  26. batFramework/gui/interactiveWidget.py +72 -20
  27. batFramework/gui/label.py +240 -98
  28. batFramework/gui/layout.py +125 -53
  29. batFramework/gui/meter.py +76 -0
  30. batFramework/gui/radioButton.py +62 -0
  31. batFramework/gui/root.py +72 -48
  32. batFramework/gui/shape.py +257 -49
  33. batFramework/gui/slider.py +217 -2
  34. batFramework/gui/textInput.py +106 -60
  35. batFramework/gui/toggle.py +85 -42
  36. batFramework/gui/widget.py +259 -288
  37. batFramework/manager.py +30 -16
  38. batFramework/object.py +115 -0
  39. batFramework/{particles.py → particle.py} +37 -34
  40. batFramework/renderGroup.py +62 -0
  41. batFramework/resourceManager.py +36 -24
  42. batFramework/scene.py +94 -88
  43. batFramework/sceneManager.py +140 -57
  44. batFramework/scrollingSprite.py +113 -0
  45. batFramework/sprite.py +35 -23
  46. batFramework/tileset.py +34 -52
  47. batFramework/time.py +105 -56
  48. batFramework/transition.py +213 -1
  49. batFramework/utils.py +38 -18
  50. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/METADATA +1 -1
  51. batframework-1.0.8a1.dist-info/RECORD +56 -0
  52. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/WHEEL +1 -1
  53. batFramework/easing.py +0 -76
  54. batFramework/gui/constraints.py +0 -277
  55. batFramework/gui/frame.py +0 -25
  56. batFramework/transitionManager.py +0 -0
  57. batframework-1.0.6.dist-info/RECORD +0 -50
  58. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/LICENCE +0 -0
  59. {batframework-1.0.6.dist-info → batframework-1.0.8a1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,206 @@
1
+ from .label import Label
2
+ import batFramework as bf
3
+ from typing import Self, Callable
4
+ from .interactiveWidget import InteractiveWidget
5
+ from .shape import Shape
6
+ import pygame
7
+ from math import ceil
8
+
9
+
10
+ class ClickableWidget(Shape,InteractiveWidget):
11
+ _cache: dict = {}
12
+
13
+ def __init__(self,callback: None | Callable = None,*args,**kwargs) -> None:
14
+ super().__init__(*args, **kwargs)
15
+ self.callback = callback
16
+ self.is_pressed: bool = False
17
+ self.enabled: bool = True
18
+ self.hover_cursor = bf.const.DEFAULT_HOVER_CURSOR
19
+ self.click_cursor = bf.const.DEFAULT_CLICK_CURSOR
20
+ self.click_down_sound = None
21
+ self.click_up_sound = None
22
+ self.get_focus_sound = None
23
+ self.lose_focus_sound = None
24
+ self.pressed_relief : int = 1
25
+ self.unpressed_relief : int = 2
26
+ self.silent_focus : bool = False
27
+ self.set_debug_color("cyan")
28
+ self.set_relief(self.unpressed_relief)
29
+
30
+ def set_unpressed_relief(self,relief:int=0)->Self:
31
+ if relief == self.unpressed_relief : return self
32
+ self.unpressed_relief = relief
33
+ self.dirty_shape = True
34
+ if not self.is_pressed : self.set_relief(relief)
35
+ return self
36
+
37
+ def set_silent_focus(self,value:bool)->Self:
38
+ self.silent_focus = value
39
+ return self
40
+
41
+
42
+ def set_pressed_relief(self,relief:int=0)->Self:
43
+ if relief == self.pressed_relief : return self
44
+ self.pressed_relief = relief
45
+ self.dirty_shape = True
46
+ if self.is_pressed : self.set_relief(relief)
47
+
48
+ return self
49
+
50
+ def set_click_down_sound(self, sound_name: str) -> Self:
51
+ self.click_down_sound = sound_name
52
+ return self
53
+
54
+ def set_click_up_sound(self, sound_name: str) -> Self:
55
+ self.click_up_sound = sound_name
56
+ return self
57
+
58
+ def set_get_focus_sound(self, sound_name: str) -> Self:
59
+ self.get_focus_sound = sound_name
60
+ return self
61
+
62
+ def set_lose_focus_sound(self, sound_name: str) -> Self:
63
+ self.lose_focus_sound = sound_name
64
+ return self
65
+
66
+ def set_hover_cursor(self, cursor: pygame.Cursor) -> Self:
67
+ self.hover_cursor = cursor
68
+ return self
69
+
70
+ def set_click_cursor(self, cursor: pygame.Cursor) -> Self:
71
+ self.click_cursor = cursor
72
+ return self
73
+
74
+ def get_surface_filter(self) -> pygame.Surface | None:
75
+ size = int(self.rect.w),int(self.rect.h)
76
+ surface_filter = ClickableWidget._cache.get((size, *self.border_radius), None)
77
+ if surface_filter is None:
78
+ # Create a mask from the original surface
79
+ mask = pygame.mask.from_surface(self.surface, threshold=0)
80
+
81
+ silhouette_surface = mask.to_surface(setcolor=(30, 30, 30), unsetcolor=(0,0,0))
82
+
83
+ ClickableWidget._cache[(size, *self.border_radius)] = silhouette_surface
84
+
85
+ surface_filter = silhouette_surface
86
+
87
+ return surface_filter
88
+
89
+ def allow_focus_to_self(self)->bool:
90
+ return True
91
+
92
+ def enable(self) -> Self:
93
+ self.enabled = True
94
+ self.dirty_surface = True
95
+ return self
96
+
97
+ def disable(self) -> Self:
98
+ self.enabled = False
99
+ self.dirty_surface = True
100
+ return self
101
+
102
+ def is_enabled(self) -> bool:
103
+ return self.enabled
104
+
105
+ def set_callback(self, callback: Callable) -> Self:
106
+ self.callback = callback
107
+ return self
108
+
109
+ def on_get_focus(self):
110
+ super().on_get_focus()
111
+ if self.get_focus_sound and not self.silent_focus:
112
+ if self.parent_scene and self.parent_scene.visible:
113
+ bf.AudioManager().play_sound(self.get_focus_sound)
114
+ if self.silent_focus :
115
+ self.silent_focus = False
116
+
117
+ def on_lose_focus(self):
118
+ super().on_lose_focus()
119
+ if self.lose_focus_sound and not self.silent_focus:
120
+ if self.parent_scene and self.parent_scene.visible:
121
+ bf.AudioManager().play_sound(self.lose_focus_sound)
122
+ if self.silent_focus :
123
+ self.silent_focus = False
124
+
125
+ def __str__(self) -> str:
126
+ return f"ClickableWidget"
127
+
128
+ def click(self, force=False) -> None:
129
+ if not self.enabled and not force:
130
+ return
131
+ if self.callback is not None:
132
+ self.callback()
133
+
134
+ def do_on_click_down(self, button) -> None:
135
+ if self.enabled and button == 1 :
136
+ if not self.get_focus():
137
+ return
138
+ self.is_pressed = True
139
+ bf.AudioManager().play_sound(self.click_down_sound)
140
+
141
+ pygame.mouse.set_cursor(self.click_cursor)
142
+ self.set_relief(self.pressed_relief)
143
+
144
+ def do_on_click_up(self, button) -> None:
145
+ if self.enabled and button == 1 and self.is_pressed:
146
+ self.is_pressed = False
147
+ bf.AudioManager().play_sound(self.click_up_sound)
148
+ self.set_relief(self.unpressed_relief)
149
+ self.click()
150
+
151
+ def on_enter(self) -> None:
152
+ if not self.enabled:
153
+ return
154
+ super().on_enter()
155
+ self.dirty_surface = True
156
+ pygame.mouse.set_cursor(self.hover_cursor)
157
+
158
+ def on_exit(self) -> None:
159
+ super().on_exit()
160
+ if self.is_pressed:
161
+ self.set_relief(self.unpressed_relief)
162
+ self.is_pressed = False
163
+ self.dirty_surface = True
164
+
165
+ pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
166
+
167
+ def on_lose_focus(self):
168
+ super().on_lose_focus()
169
+ self.on_exit()
170
+
171
+ def on_key_down(self, key):
172
+ if key == pygame.K_SPACE:
173
+ self.on_click_down(1)
174
+ super().on_key_down(key)
175
+
176
+ def on_key_up(self, key):
177
+ if key == pygame.K_SPACE:
178
+ self.on_click_up(1)
179
+ super().on_key_up(key)
180
+
181
+ def _paint_disabled(self) -> None:
182
+ self.surface.blit(
183
+ self.get_surface_filter(), (0, 0), special_flags=pygame.BLEND_RGB_SUB
184
+ )
185
+
186
+ def _paint_hovered(self) -> None:
187
+ self.surface.blit(
188
+ self.get_surface_filter(), (0, 0), special_flags=pygame.BLEND_RGB_ADD
189
+ )
190
+
191
+ def get_padded_rect(self)->pygame.FRect:
192
+ return pygame.FRect(
193
+ self.rect.x + self.padding[0], self.rect.y + self.padding[1] + (self.unpressed_relief - self.pressed_relief if self.is_pressed else 0),
194
+ self.rect.w - self.padding[2] - self.padding[0],
195
+ self.rect.h - self.unpressed_relief - self.padding[1] - self.padding[3] #
196
+ )
197
+
198
+ def _get_elevated_rect(self) -> pygame.FRect:
199
+ return pygame.FRect(0, self.unpressed_relief - self.pressed_relief if self.is_pressed else 0 , self.rect.w, self.rect.h - self.unpressed_relief)
200
+
201
+ def paint(self) -> None:
202
+ super().paint()
203
+ if not self.enabled:
204
+ self._paint_disabled()
205
+ elif self.is_hovered:
206
+ self._paint_hovered()
@@ -0,0 +1 @@
1
+ from . import constraints
@@ -0,0 +1,378 @@
1
+ from ..widget import Widget
2
+ import batFramework as bf
3
+ import pygame
4
+
5
+
6
+ class Constraint:
7
+ def __init__(self, name="Constraint", priority=0):
8
+ self.priority = priority
9
+ self.name = name
10
+
11
+ def set_priority(self, priority) -> "Constraint":
12
+ self.priority = priority
13
+ return self
14
+
15
+ def __str__(self) -> str:
16
+ return f"{self.name.upper()}"
17
+
18
+ def evaluate(self, parent_widget: Widget, child_widget: Widget) -> bool:
19
+ raise NotImplementedError("Subclasses must implement evaluate method")
20
+
21
+ def apply(self, parent_widget: Widget, child_widget: Widget = None) -> bool:
22
+ if not self.evaluate(parent_widget, child_widget):
23
+ self.apply_constraint(parent_widget, child_widget)
24
+ return False
25
+ return True
26
+
27
+ def apply_constraint(self, parent_widget: Widget, child_widget: Widget):
28
+ raise NotImplementedError("Subclasses must implement apply_constraint method")
29
+
30
+
31
+ class MinWidth(Constraint):
32
+ def __init__(self, width: float):
33
+ super().__init__(name="min_width")
34
+ self.min_width = width
35
+
36
+ def evaluate(self, parent_widget, child_widget):
37
+ return child_widget.rect.width >= self.min_width
38
+
39
+ def apply_constraint(self, parent_widget, child_widget):
40
+ child_widget.set_size((self.min_width,None))
41
+
42
+
43
+ class MinHeight(Constraint):
44
+ def __init__(self, height: float):
45
+ super().__init__(name="min_height")
46
+ self.min_height = height
47
+
48
+ def evaluate(self, parent_widget, child_widget):
49
+ return child_widget.rect.h >= self.min_height
50
+
51
+ def apply_constraint(self, parent_widget, child_widget):
52
+ child_widget.set_size((None, self.min_height))
53
+
54
+
55
+ class CenterX(Constraint):
56
+ def __init__(self):
57
+ super().__init__(name="centerx")
58
+
59
+ def evaluate(self, parent_widget, child_widget):
60
+ return int(child_widget.rect.centerx - parent_widget.get_padded_center()[0]) == 0
61
+
62
+ def apply_constraint(self, parent_widget, child_widget):
63
+ child_widget.set_center(
64
+ parent_widget.get_padded_center()[0], child_widget.rect.centery
65
+ )
66
+
67
+
68
+ class CenterY(Constraint):
69
+ def __init__(self):
70
+ super().__init__(name="centery")
71
+
72
+ def evaluate(self, parent_widget, child_widget):
73
+ return int(child_widget.rect.centery - parent_widget.get_padded_center()[1]) == 0
74
+
75
+ def apply_constraint(self, parent_widget, child_widget):
76
+ child_widget.set_center(
77
+ child_widget.rect.centerx, parent_widget.get_padded_center()[1]
78
+ )
79
+
80
+
81
+ class Center(Constraint):
82
+ def __init__(self):
83
+ super().__init__(name="center")
84
+
85
+ def evaluate(self, parent_widget, child_widget):
86
+ return int(child_widget.rect.centerx - parent_widget.get_padded_center()[0]) == 0 and \
87
+ int(child_widget.rect.centery - parent_widget.get_padded_center()[1]) == 0
88
+
89
+ def apply_constraint(self, parent_widget, child_widget):
90
+ child_widget.set_center(*parent_widget.get_padded_center())
91
+
92
+
93
+ class PercentageWidth(Constraint):
94
+ def __init__(self, percentage: float, keep_autoresize: bool = False):
95
+ super().__init__(name="percentage_width")
96
+ self.percentage: float = percentage
97
+ self.keep_autoresize: bool = keep_autoresize
98
+
99
+ def __str__(self) -> str:
100
+ return f"{super().__str__()}.[{self.percentage*100}%,keep_autoresize={self.keep_autoresize}]"
101
+
102
+ def evaluate(self, parent_widget, child_widget):
103
+ return child_widget.rect.width == round(
104
+ parent_widget.get_padded_width() * self.percentage
105
+ )
106
+
107
+ def apply_constraint(self, parent_widget, child_widget):
108
+ if child_widget.autoresize_w:
109
+ if self.keep_autoresize:
110
+ print(
111
+ f"WARNING: Constraint on {child_widget.__str__()} can't resize, autoresize set to True"
112
+ )
113
+ return
114
+ child_widget.set_autoresize_w(False)
115
+
116
+ child_widget.set_size(
117
+ (
118
+ round(parent_widget.get_padded_width() * self.percentage),None)
119
+ )
120
+
121
+ class PercentageHeight(Constraint):
122
+ def __init__(self, percentage: float, keep_autoresize: bool = False):
123
+ super().__init__(name="percentage_height")
124
+ self.percentage: float = percentage
125
+ self.keep_autoresize: bool = keep_autoresize
126
+
127
+ def evaluate(self, parent_widget, child_widget):
128
+ return child_widget.rect.height == round(parent_widget.get_padded_height() * self.percentage)
129
+
130
+ def __str__(self) -> str:
131
+ return f"{super().__str__()}.[{self.percentage*100}%,keep_autoresize={self.keep_autoresize}]"
132
+
133
+ def apply_constraint(self, parent_widget, child_widget):
134
+ if child_widget.autoresize_h:
135
+ if self.keep_autoresize:
136
+ print(f"WARNING: Constraint on {child_widget} can't resize, autoresize set to True")
137
+ return
138
+ child_widget.set_autoresize_h(False)
139
+ child_widget.set_size((None,round(parent_widget.get_padded_height() * self.percentage)))
140
+
141
+ class FillX(PercentageWidth):
142
+ def __init__(self, keep_autoresize: bool = False):
143
+ super().__init__(1,keep_autoresize)
144
+ self.name = "fill_x"
145
+
146
+ class FillY(PercentageHeight):
147
+ def __init__(self, keep_autoresize: bool = False):
148
+ super().__init__(1,keep_autoresize)
149
+ self.name = "fill_y"
150
+
151
+
152
+ class Height(Constraint):
153
+ def __init__(self, height: float, keep_autoresize: bool = False):
154
+ if height < 0:
155
+ raise ValueError("height can't be negative")
156
+ super().__init__(name="height")
157
+ self.height = height
158
+ self.keep_autoresize: bool = keep_autoresize
159
+
160
+ def __str__(self) -> str:
161
+ return f"{super().__str__()}.(height={self.height})"
162
+
163
+ def evaluate(self, parent_widget, child_widget):
164
+ return child_widget.rect.height == self.height
165
+
166
+ def apply_constraint(self, parent_widget, child_widget):
167
+ if child_widget.autoresize_h:
168
+ if self.keep_autoresize:
169
+ print(f"WARNING: Constraint on {child_widget.__str__()} can't resize, autoresize set to True")
170
+ return
171
+ child_widget.set_autoresize_h(False)
172
+ child_widget.set_size((None, self.height))
173
+
174
+
175
+ class Width(Constraint):
176
+ def __init__(self, width: float, keep_autoresize: bool = False):
177
+ if width < 0:
178
+ raise ValueError("width can't be negative")
179
+ super().__init__(name="width")
180
+ self.width = width
181
+ self.keep_autoresize: bool = keep_autoresize
182
+
183
+ def __str__(self) -> str:
184
+ return f"{super().__str__()}.(width={self.width})"
185
+
186
+ def evaluate(self, parent_widget, child_widget):
187
+ return child_widget.rect.width == self.width
188
+
189
+ def apply_constraint(self, parent_widget, child_widget):
190
+ if child_widget.autoresize_w:
191
+ if self.keep_autoresize:
192
+ print(
193
+ f"WARNING: Constraint on {child_widget.__str__()} can't resize, autoresize set to True"
194
+ )
195
+ return
196
+ child_widget.set_autoresize_w(False)
197
+ child_widget.set_size((self.width, None))
198
+
199
+
200
+ class AspectRatio(Constraint):
201
+ def __init__(
202
+ self,
203
+ ratio: int | float | pygame.rect.FRectType = 1,
204
+ reference_axis: bf.axis = bf.axis.HORIZONTAL,
205
+ keep_autoresize=False,
206
+ ):
207
+ super().__init__(name="aspect_ratio")
208
+ self.ref_axis : bf.axis = reference_axis
209
+ self.keep_autoresize: bool = keep_autoresize
210
+
211
+ if isinstance(ratio, float | int):
212
+ self.ratio = ratio
213
+ elif isinstance(ratio, pygame.rect.FRect):
214
+ self.ratio = (ratio.w / ratio.h) if reference_axis == bf.axis.HORIZONTAL else (ratio.h / ratio.w)
215
+ else:
216
+ raise TypeError(f"Ratio must be float or FRect")
217
+
218
+ def evaluate(self, parent_widget, child_widget):
219
+ if self.ref_axis == bf.axis.HORIZONTAL:
220
+ return self.ratio == child_widget.rect.w / child_widget.rect.h
221
+ if self.ref_axis == bf.axis.VERTICAL:
222
+ return self.ratio == child_widget.rect.h / child_widget.rect.w
223
+
224
+ def apply_constraint(self, parent_widget, child_widget):
225
+
226
+ if self.ref_axis == bf.axis.VERTICAL:
227
+ if child_widget.autoresize_w :
228
+ if self.keep_autoresize:
229
+ print(
230
+ f"WARNING: Constraint on {child_widget.__str__()} can't resize, autoresize set to True"
231
+ )
232
+ return
233
+ child_widget.set_autoresize_w(False)
234
+ child_widget.set_size((child_widget.rect.h * self.ratio,None))
235
+
236
+ if self.ref_axis == bf.axis.HORIZONTAL:
237
+ if child_widget.autoresize_h :
238
+ if self.keep_autoresize:
239
+ print(
240
+ f"WARNING: Constraint on {child_widget.__str__()} can't resize, autoresize set to True"
241
+ )
242
+ return
243
+ child_widget.set_autoresize_h(False)
244
+ print("HERE")
245
+ child_widget.set_size((None,child_widget.rect.w * self.ratio))
246
+
247
+ class AnchorBottom(Constraint):
248
+ def __init__(self):
249
+ super().__init__(name="anchor_bottom")
250
+
251
+ def evaluate(self, parent_widget, child_widget):
252
+ return child_widget.rect.top == parent_widget.get_padded_bottom() - child_widget.rect.h
253
+
254
+ def apply_constraint(self, parent_widget, child_widget):
255
+ child_widget.set_position(child_widget.rect.x,parent_widget.get_padded_bottom() - child_widget.rect.h)
256
+
257
+ class AnchorBottom(Constraint):
258
+ def __init__(self):
259
+ super().__init__(name="anchor_bottom")
260
+
261
+ def evaluate(self, parent_widget, child_widget):
262
+ return child_widget.rect.top == parent_widget.get_padded_bottom() - child_widget.rect.h
263
+
264
+ def apply_constraint(self, parent_widget, child_widget):
265
+ child_widget.set_position(child_widget.rect.x,parent_widget.get_padded_bottom() - child_widget.rect.h)
266
+
267
+
268
+
269
+ class AnchorTopRight(Constraint):
270
+ def __init__(self):
271
+ super().__init__(name="anchor_topright")
272
+
273
+ def evaluate(self, parent_widget, child_widget):
274
+ return child_widget.rect.topright == parent_widget.get_padded_rect().topright
275
+
276
+ def apply_constraint(self, parent_widget, child_widget):
277
+ child_widget.set_position(
278
+ parent_widget.get_padded_right() - child_widget.rect.w,
279
+ parent_widget.get_padded_top(),
280
+ )
281
+
282
+
283
+ class AnchorBottomRight(Constraint):
284
+ def __init__(self):
285
+ super().__init__(name="anchor_bottomright")
286
+
287
+ def evaluate(self, parent_widget, child_widget):
288
+ return (
289
+ child_widget.rect.bottomright == parent_widget.get_padded_rect().bottomright
290
+ )
291
+
292
+ def apply_constraint(self, parent_widget, child_widget):
293
+ child_widget.set_position(
294
+ parent_widget.get_padded_right() - child_widget.rect.w,
295
+ parent_widget.get_padded_bottom() - child_widget.rect.h,
296
+ )
297
+
298
+
299
+ class AnchorRight(Constraint):
300
+ def __init__(self):
301
+ super().__init__(name="anchor_right")
302
+
303
+ def evaluate(self, parent_widget, child_widget):
304
+ return child_widget.rect.right == parent_widget.get_padded_right()
305
+
306
+ def apply_constraint(self, parent_widget, child_widget):
307
+ child_widget.set_position(
308
+ parent_widget.get_padded_right() - child_widget.rect.w,
309
+ child_widget.rect.top,
310
+ )
311
+
312
+
313
+ class AnchorLeft(Constraint):
314
+ def __init__(self):
315
+ super().__init__(name="anchor_left")
316
+
317
+ def evaluate(self, parent_widget, child_widget):
318
+ return child_widget.rect.left == parent_widget.get_padded_left()
319
+
320
+ def apply_constraint(self, parent_widget, child_widget):
321
+ child_widget.set_position(
322
+ parent_widget.get_padded_left(), child_widget.rect.top
323
+ )
324
+
325
+
326
+ class MarginBottom(Constraint):
327
+ def __init__(self, margin: float):
328
+ super().__init__(name="margin_bottom")
329
+ self.margin = margin
330
+
331
+ def evaluate(self, parent_widget, child_widget):
332
+ return (
333
+ child_widget.rect.bottom == parent_widget.get_padded_bottom() - self.margin
334
+ )
335
+
336
+ def apply_constraint(self, parent_widget, child_widget):
337
+ child_widget.set_position(child_widget.rect.x,
338
+ parent_widget.get_padded_bottom() - child_widget.rect.h - self.margin
339
+ )
340
+
341
+ class MarginTop(Constraint):
342
+ def __init__(self, margin: float):
343
+ super().__init__(name="margin_top")
344
+ self.margin = margin
345
+
346
+ def evaluate(self, parent_widget, child_widget):
347
+ return child_widget.rect.top == parent_widget.get_padded_top() + self.margin
348
+
349
+ def apply_constraint(self, parent_widget, child_widget):
350
+ child_widget.set_position(child_widget.rect.x,parent_widget.get_padded_top() + self.margin)
351
+
352
+
353
+ class MarginLeft(Constraint):
354
+ def __init__(self, margin: float):
355
+ super().__init__(name="margin_left")
356
+ self.margin = margin
357
+
358
+ def evaluate(self, parent_widget, child_widget):
359
+ return child_widget.rect.left == parent_widget.get_padded_left() + self.margin
360
+
361
+ def apply_constraint(self, parent_widget, child_widget):
362
+ if not self.evaluate(parent_widget, child_widget):
363
+ child_widget.set_position(parent_widget.get_padded_left() + self.margin,child_widget.rect.y)
364
+
365
+
366
+ class MarginRight(Constraint):
367
+ def __init__(self, margin: float):
368
+ super().__init__(name="margin_right")
369
+ self.margin = margin
370
+
371
+ def evaluate(self, parent_widget, child_widget):
372
+ return child_widget.rect.right == parent_widget.get_padded_right() - self.margin
373
+
374
+ def apply_constraint(self, parent_widget, child_widget):
375
+ child_widget.set_position(
376
+ parent_widget.get_padded_right() - child_widget.rect.w - self.margin,
377
+ child_widget.rect.y
378
+ )