batframework 1.0.8a14__py3-none-any.whl → 1.0.9a1__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/__init__.py +9 -13
- batFramework/camera.py +1 -1
- batFramework/constants.py +6 -0
- batFramework/cutscene.py +3 -5
- batFramework/drawable.py +1 -1
- batFramework/dynamicEntity.py +2 -4
- batFramework/entity.py +15 -28
- batFramework/enums.py +1 -0
- batFramework/fontManager.py +3 -3
- batFramework/gui/__init__.py +2 -2
- batFramework/gui/{dialogueBox.py → animatedLabel.py} +18 -36
- batFramework/gui/button.py +30 -0
- batFramework/gui/clickableWidget.py +6 -1
- batFramework/gui/constraints/constraints.py +90 -1
- batFramework/gui/container.py +84 -93
- batFramework/gui/indicator.py +3 -2
- batFramework/gui/interactiveWidget.py +43 -24
- batFramework/gui/label.py +25 -9
- batFramework/gui/layout.py +378 -42
- batFramework/gui/root.py +2 -3
- batFramework/gui/shape.py +2 -0
- batFramework/gui/textInput.py +115 -78
- batFramework/gui/toggle.py +1 -4
- batFramework/gui/widget.py +49 -37
- batFramework/manager.py +42 -31
- batFramework/sceneManager.py +2 -12
- batFramework/scrollingSprite.py +1 -1
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a1.dist-info}/METADATA +1 -1
- batframework-1.0.9a1.dist-info/RECORD +63 -0
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a1.dist-info}/WHEEL +1 -1
- batframework-1.0.8a14.dist-info/RECORD +0 -63
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a1.dist-info}/LICENCE +0 -0
- {batframework-1.0.8a14.dist-info → batframework-1.0.9a1.dist-info}/top_level.txt +0 -0
batFramework/__init__.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import pygame
|
2
2
|
import batFramework as bf
|
3
|
-
import sys
|
4
3
|
from .constants import Constants as const
|
5
4
|
from .utils import Singleton
|
6
5
|
from .enums import *
|
@@ -11,7 +10,7 @@ from .tileset import Tileset
|
|
11
10
|
from .timeManager import TimeManager,Timer,SceneTimer
|
12
11
|
from .easingController import EasingController
|
13
12
|
from .cutscene import Cutscene, CutsceneManager
|
14
|
-
|
13
|
+
import batFramework.cutsceneBlocks
|
15
14
|
from .audioManager import AudioManager
|
16
15
|
import batFramework.transition as transition
|
17
16
|
from .action import Action
|
@@ -29,7 +28,7 @@ from .animatedSprite import AnimatedSprite
|
|
29
28
|
from .character import Character
|
30
29
|
from .stateMachine import State, StateMachine
|
31
30
|
from .scene import Scene
|
32
|
-
from
|
31
|
+
from .gui import *
|
33
32
|
from .sceneManager import SceneManager
|
34
33
|
from .manager import Manager
|
35
34
|
from .templates import *
|
@@ -47,8 +46,6 @@ def init_screen(resolution: tuple[int, int], flags: int = 0, vsync: int = 0):
|
|
47
46
|
f"Window : {resolution[0]}x{resolution[1]}"
|
48
47
|
)
|
49
48
|
|
50
|
-
|
51
|
-
|
52
49
|
def print_version():
|
53
50
|
package_name = "batFramework"
|
54
51
|
try:
|
@@ -57,19 +54,18 @@ def print_version():
|
|
57
54
|
except importlib.metadata.PackageNotFoundError:
|
58
55
|
print(f"{package_name} is not installed")
|
59
56
|
|
60
|
-
|
61
57
|
def init(
|
62
58
|
resolution: tuple[int, int],
|
63
59
|
flags: int = 0,
|
64
|
-
|
65
|
-
default_text_size=None,
|
66
|
-
default_font=None,
|
60
|
+
window_caption: str = "BatFramework Project",
|
67
61
|
resource_path: str | None = None,
|
68
|
-
|
62
|
+
default_font_size=None,
|
63
|
+
default_font=None,
|
69
64
|
fps_limit: int = 0,
|
65
|
+
vsync: int = 0,
|
70
66
|
):
|
71
67
|
print_version()
|
72
|
-
pygame.display.set_caption(
|
68
|
+
pygame.display.set_caption(window_caption)
|
73
69
|
init_screen(resolution, flags, vsync)
|
74
70
|
|
75
71
|
ResourceManager().set_resource_path(
|
@@ -77,8 +73,8 @@ def init(
|
|
77
73
|
)
|
78
74
|
if resource_path is not None:
|
79
75
|
ResourceManager().load_dir(ResourceManager().RESOURCE_PATH)
|
80
|
-
if
|
81
|
-
FontManager().set_default_text_size(
|
76
|
+
if default_font_size is not None:
|
77
|
+
FontManager().set_default_text_size(default_font_size)
|
82
78
|
FontManager().init_font(default_font)
|
83
79
|
const.BF_INITIALIZED = True
|
84
80
|
const.set_fps_limit(fps_limit)
|
batFramework/camera.py
CHANGED
@@ -213,7 +213,7 @@ class Camera:
|
|
213
213
|
|
214
214
|
return surface
|
215
215
|
|
216
|
-
def
|
216
|
+
def set_size(self, size: tuple[int, int] | None = None) -> Self:
|
217
217
|
if size is None:
|
218
218
|
size = bf.const.RESOLUTION
|
219
219
|
self.target_size = size
|
batFramework/constants.py
CHANGED
@@ -16,6 +16,12 @@ class Constants:
|
|
16
16
|
DEFAULT_CLICK_CURSOR = pygame.cursors.Cursor(pygame.SYSTEM_CURSOR_ARROW)
|
17
17
|
|
18
18
|
BF_INITIALIZED: bool = False
|
19
|
+
ALLOW_DEBUG : bool = True
|
20
|
+
|
21
|
+
@staticmethod
|
22
|
+
def set_allow_debug(allow_debug:bool):
|
23
|
+
Constants.ALLOW_DEBUG = allow_debug
|
24
|
+
|
19
25
|
|
20
26
|
@staticmethod
|
21
27
|
def set_resolution(resolution: tuple[int, int]):
|
batFramework/cutscene.py
CHANGED
@@ -37,14 +37,13 @@ class CutsceneManager(metaclass=bf.Singleton):
|
|
37
37
|
self.current_cutscene.on_enter()
|
38
38
|
self.current_cutscene.init_blocks()
|
39
39
|
self.current_cutscene.play()
|
40
|
-
self.manager.set_sharedVar("in_cutscene", True)
|
41
40
|
|
42
41
|
def enable_player_control(self) -> None:
|
43
|
-
|
42
|
+
pass
|
44
43
|
|
45
44
|
def disable_player_control(self) -> None:
|
46
|
-
|
47
|
-
|
45
|
+
pass
|
46
|
+
|
48
47
|
def update(self, dt):
|
49
48
|
if not self.current_cutscene is None:
|
50
49
|
self.current_cutscene.update(dt)
|
@@ -56,7 +55,6 @@ class CutsceneManager(metaclass=bf.Singleton):
|
|
56
55
|
self.play(self.cutscenes.pop(0))
|
57
56
|
else:
|
58
57
|
self.current_cutscene = None
|
59
|
-
self.manager.set_sharedVar("in_cutscene", False)
|
60
58
|
|
61
59
|
|
62
60
|
class Cutscene:
|
batFramework/drawable.py
CHANGED
@@ -66,7 +66,7 @@ class Drawable(Entity):
|
|
66
66
|
"""
|
67
67
|
Draw the entity onto the camera surface
|
68
68
|
"""
|
69
|
-
if not self.visible or not camera.rect.colliderect(self.rect):
|
69
|
+
if not self.visible or not camera.rect.colliderect(self.rect) or self.surface.get_alpha() == 0:
|
70
70
|
return
|
71
71
|
camera.surface.blit(
|
72
72
|
self.surface,
|
batFramework/dynamicEntity.py
CHANGED
@@ -6,11 +6,9 @@ from typing import Self
|
|
6
6
|
class DynamicEntity(bf.Entity):
|
7
7
|
def __init__(
|
8
8
|
self,
|
9
|
-
|
10
|
-
surface_flags: int = 0,
|
11
|
-
convert_alpha: bool = False,*args,**kwargs
|
9
|
+
*args,**kwargs
|
12
10
|
) -> None:
|
13
|
-
super().__init__(
|
11
|
+
super().__init__(*args,**kwargs)
|
14
12
|
self.velocity = pygame.math.Vector2(0, 0)
|
15
13
|
self.ignore_collisions : bool = False
|
16
14
|
|
batFramework/entity.py
CHANGED
@@ -8,27 +8,27 @@ if TYPE_CHECKING:
|
|
8
8
|
|
9
9
|
|
10
10
|
class Entity:
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
_count: int = 0
|
12
|
+
_available_uids: set[int] = set()
|
13
|
+
|
14
|
+
|
15
|
+
def __init__(self,*args,**kwargs) -> None:
|
16
|
+
if Entity._available_uids:
|
17
|
+
self.uid = Entity._available_uids.pop()
|
18
|
+
else:
|
19
|
+
self.uid = Entity._count
|
20
|
+
Entity._count += 1
|
14
21
|
|
15
|
-
def __init__(self) -> None:
|
16
22
|
self.rect = pygame.FRect(0, 0, 0, 0)
|
17
23
|
self.tags: list[str] = []
|
18
24
|
self.parent_scene: bf.Scene | None = None
|
19
25
|
self.debug_color: tuple | str = "red"
|
20
|
-
self.uid: int = Entity.__count
|
21
|
-
Entity.__used_uid.add(self.uid)
|
22
|
-
|
23
|
-
if Entity.__available_uid:
|
24
|
-
self.name = Entity.__available_uid.pop()
|
25
|
-
else:
|
26
|
-
self.name = Entity.__count
|
27
|
-
Entity.__count += 1
|
28
26
|
|
29
27
|
def __del__(self):
|
30
|
-
|
31
|
-
|
28
|
+
try:
|
29
|
+
Entity._available_uids.add(self.uid)
|
30
|
+
except AttributeError:
|
31
|
+
pass
|
32
32
|
def set_position(self, x, y) -> Self:
|
33
33
|
self.rect.topleft = x, y
|
34
34
|
return self
|
@@ -60,16 +60,6 @@ class Entity:
|
|
60
60
|
def do_when_removed(self):
|
61
61
|
pass
|
62
62
|
|
63
|
-
def set_uid(self, uid: int) -> Self:
|
64
|
-
if uid in Entity.__used_uid:
|
65
|
-
print(f"set_uid error : UID '{uid}' is already in use")
|
66
|
-
return self
|
67
|
-
self.uid = uid
|
68
|
-
if uid in Entity.__used_uid:
|
69
|
-
Entity.__used_uid.remove(uid)
|
70
|
-
Entity.__used_uid.add(uid)
|
71
|
-
return self
|
72
|
-
|
73
63
|
def add_tags(self, *tags) -> Self:
|
74
64
|
for tag in tags:
|
75
65
|
if tag not in self.tags:
|
@@ -86,10 +76,7 @@ class Entity:
|
|
86
76
|
def get_tags(self) -> list[str]:
|
87
77
|
return self.tags
|
88
78
|
|
89
|
-
def process_event(self, event: pygame.Event) ->
|
90
|
-
"""
|
91
|
-
Returns bool : True if the method is blocking (no propagation to next Entity of the scene)
|
92
|
-
"""
|
79
|
+
def process_event(self, event: pygame.Event) -> None:
|
93
80
|
if event.consumed:
|
94
81
|
return
|
95
82
|
self.do_process_actions(event)
|
batFramework/enums.py
CHANGED
batFramework/fontManager.py
CHANGED
@@ -9,17 +9,17 @@ import batFramework as bf
|
|
9
9
|
class FontManager(metaclass=Singleton):
|
10
10
|
def __init__(self):
|
11
11
|
pygame.font.init()
|
12
|
-
self.
|
12
|
+
self.DEFAULT_FONT_SIZE = 16
|
13
13
|
self.MIN_FONT_SIZE = 8
|
14
14
|
self.MAX_FONT_SIZE = 64
|
15
15
|
self.DEFAULT_ANTIALIAS = False
|
16
16
|
self.FONTS = {}
|
17
17
|
|
18
|
-
def
|
18
|
+
def set_default_antialias(self, value: bool):
|
19
19
|
self.DEFAULT_ANTIALIAS = value
|
20
20
|
|
21
21
|
def set_default_text_size(self, size: int):
|
22
|
-
self.
|
22
|
+
self.DEFAULT_FONT_SIZE = size
|
23
23
|
|
24
24
|
def init_font(self, raw_path: str | None):
|
25
25
|
try:
|
batFramework/gui/__init__.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
from .constraints import *
|
2
1
|
from .widget import Widget
|
3
2
|
from .styleManager import StyleManager
|
4
3
|
from .style import Style
|
@@ -10,7 +9,7 @@ from .root import Root
|
|
10
9
|
from .shape import Shape
|
11
10
|
from .meter import Meter
|
12
11
|
from .label import Label
|
13
|
-
from .
|
12
|
+
from .animatedLabel import AnimatedLabel
|
14
13
|
from .textInput import TextInput
|
15
14
|
from .button import Button
|
16
15
|
from .debugger import *
|
@@ -20,3 +19,4 @@ from .indicator import *
|
|
20
19
|
from .toggle import Toggle
|
21
20
|
from .radioButton import RadioButton, RadioVariable
|
22
21
|
from .slider import Slider
|
22
|
+
from .constraints import *
|
@@ -3,19 +3,20 @@ import batFramework as bf
|
|
3
3
|
from typing import Self
|
4
4
|
|
5
5
|
|
6
|
-
class
|
7
|
-
def __init__(self) -> None:
|
6
|
+
class AnimatedLabel(Label):
|
7
|
+
def __init__(self,text="") -> None:
|
8
8
|
self.cursor_position: float = 0.0
|
9
9
|
self.text_speed: float = 20.0
|
10
|
-
self.message_queue: list[str] = []
|
11
10
|
self.is_over: bool = True
|
12
11
|
self.is_paused: bool = False
|
12
|
+
self.original_text = ""
|
13
13
|
self.set_autoresize(False)
|
14
14
|
self.set_alignment(bf.alignment.LEFT)
|
15
15
|
super().__init__("")
|
16
|
+
self.set_text(text)
|
16
17
|
|
17
18
|
def __str__(self) -> str:
|
18
|
-
return "
|
19
|
+
return "AnimatedLabel"
|
19
20
|
|
20
21
|
def pause(self) -> Self:
|
21
22
|
self.is_paused = True
|
@@ -53,47 +54,28 @@ class DialogueBox(Label):
|
|
53
54
|
left = index
|
54
55
|
return [text]
|
55
56
|
|
56
|
-
def paint(self) -> None:
|
57
|
-
if self.font_object and self.message_queue:
|
58
|
-
message = self.message_queue.pop(0)
|
59
|
-
message = "\n".join(self.cut_text_to_width(message))
|
60
|
-
self.message_queue.insert(0, message)
|
61
|
-
super().paint()
|
62
57
|
|
63
|
-
def
|
64
|
-
|
65
|
-
self.is_over = False
|
58
|
+
def _set_text_internal(self,text:str)->Self:
|
59
|
+
super().set_text(text)
|
66
60
|
return self
|
67
61
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
def is_current_message_over(self) -> bool:
|
72
|
-
return self.is_over
|
73
|
-
|
74
|
-
def clear_queue(self) -> Self:
|
75
|
-
self.message_queue.clear()
|
76
|
-
self.next_message()
|
77
|
-
return self
|
78
|
-
|
79
|
-
def next_message(self) -> Self:
|
80
|
-
if self.message_queue:
|
81
|
-
self.message_queue.pop(0)
|
62
|
+
def set_text(self,text:str)->Self:
|
63
|
+
self.original_text = text
|
64
|
+
self.is_over = False
|
82
65
|
self.cursor_position = 0
|
83
|
-
self.set_text("")
|
84
|
-
return self
|
85
66
|
|
86
|
-
def
|
87
|
-
|
88
|
-
self.
|
67
|
+
def set_size(self, size):
|
68
|
+
super().set_size(size)
|
69
|
+
self._set_text_internal('\n'.join(self.cut_text_to_width(self.original_text[: int(self.cursor_position)])))
|
89
70
|
|
90
71
|
def do_update(self, dt):
|
91
|
-
if
|
72
|
+
if self.is_over:
|
92
73
|
return
|
93
|
-
if not self.is_over and self.cursor_position == len(self.
|
74
|
+
if not self.is_over and self.cursor_position == len(self.original_text):
|
94
75
|
self.is_over = True
|
95
76
|
return
|
96
77
|
self.cursor_position = min(
|
97
|
-
self.cursor_position + self.text_speed * dt, len(self.
|
78
|
+
self.cursor_position + self.text_speed * dt, len(self.original_text)
|
98
79
|
)
|
99
|
-
self.set_text(self.
|
80
|
+
# self.set_text(self.original_text[: int(self.cursor_position)])
|
81
|
+
self._set_text_internal('\n'.join(self.cut_text_to_width(self.original_text[: int(self.cursor_position)])))
|
batFramework/gui/button.py
CHANGED
@@ -13,3 +13,33 @@ class Button(Label, ClickableWidget):
|
|
13
13
|
|
14
14
|
def __str__(self) -> str:
|
15
15
|
return f"Button({self.text})"
|
16
|
+
|
17
|
+
def get_min_required_size(self) -> tuple[float, float]:
|
18
|
+
if not (self.autoresize_w or self.autoresize_h):
|
19
|
+
return self.rect.size
|
20
|
+
if not self.text_rect:
|
21
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
22
|
+
res = self.inflate_rect_by_padding((0, 0, *self.text_rect.size)).size
|
23
|
+
res = res[0],res[1]+self.unpressed_relief
|
24
|
+
return res[0] if self.autoresize_w else self.rect.w, (
|
25
|
+
res[1] if self.autoresize_h else self.rect.h
|
26
|
+
)
|
27
|
+
|
28
|
+
|
29
|
+
def _build_layout(self) -> None:
|
30
|
+
|
31
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
32
|
+
if self.autoresize_h or self.autoresize_w:
|
33
|
+
target_rect = self.inflate_rect_by_padding((0, 0, *self.text_rect.size))
|
34
|
+
target_rect.h += self.unpressed_relief
|
35
|
+
if not self.autoresize_w:
|
36
|
+
target_rect.w = self.rect.w
|
37
|
+
if not self.autoresize_h:
|
38
|
+
target_rect.h = self.rect.h
|
39
|
+
if self.rect.size != target_rect.size:
|
40
|
+
self.set_size(target_rect.size)
|
41
|
+
self.apply_updates()
|
42
|
+
return
|
43
|
+
offset = self._get_outline_offset() if self.show_text_outline else (0,0)
|
44
|
+
padded = self.get_padded_rect().move(-self.rect.x + offset[0], -self.rect.y + offset[1])
|
45
|
+
self.align_text(self.text_rect, padded, self.alignment)
|
@@ -27,6 +27,11 @@ class ClickableWidget(Shape, InteractiveWidget):
|
|
27
27
|
self.set_debug_color("cyan")
|
28
28
|
self.set_relief(self.unpressed_relief)
|
29
29
|
|
30
|
+
def get_min_required_size(self) -> tuple[float, float]:
|
31
|
+
return self.rect.size.inflate(0,self.unpressed_relief)
|
32
|
+
|
33
|
+
|
34
|
+
|
30
35
|
def set_unpressed_relief(self, relief: int) -> Self:
|
31
36
|
if relief == self.unpressed_relief:
|
32
37
|
return self
|
@@ -217,4 +222,4 @@ class ClickableWidget(Shape, InteractiveWidget):
|
|
217
222
|
self._paint_disabled()
|
218
223
|
elif self.is_hovered:
|
219
224
|
self._paint_hovered()
|
220
|
-
|
225
|
+
|
@@ -598,6 +598,95 @@ class MarginRight(Constraint):
|
|
598
598
|
other.margin == self.margin
|
599
599
|
)
|
600
600
|
|
601
|
+
class RectMarginBottom(Constraint):
|
602
|
+
def __init__(self, margin: float):
|
603
|
+
super().__init__()
|
604
|
+
self.margin = margin
|
605
|
+
|
606
|
+
def evaluate(self, parent_widget, child_widget):
|
607
|
+
return (
|
608
|
+
child_widget.rect.bottom == parent_widget.rect.bottom - self.margin
|
609
|
+
)
|
610
|
+
|
611
|
+
def apply_constraint(self, parent_widget, child_widget):
|
612
|
+
child_widget.set_position(
|
613
|
+
child_widget.rect.x,
|
614
|
+
parent_widget.rect.bottom- child_widget.rect.h - self.margin,
|
615
|
+
)
|
616
|
+
|
617
|
+
def __eq__(self,other:"Constraint")->bool:
|
618
|
+
if not isinstance(other,self.__class__):
|
619
|
+
return False
|
620
|
+
return (
|
621
|
+
other.name == self.name and
|
622
|
+
other.margin == self.margin
|
623
|
+
)
|
624
|
+
|
625
|
+
class RectMarginTop(Constraint):
|
626
|
+
def __init__(self, margin: float):
|
627
|
+
super().__init__()
|
628
|
+
self.margin = margin
|
629
|
+
|
630
|
+
def evaluate(self, parent_widget, child_widget):
|
631
|
+
return child_widget.rect.top == parent_widget.rect.top + self.margin
|
632
|
+
|
633
|
+
def apply_constraint(self, parent_widget, child_widget):
|
634
|
+
child_widget.set_position(
|
635
|
+
child_widget.rect.x, parent_widget.rect.top + self.margin
|
636
|
+
)
|
637
|
+
|
638
|
+
def __eq__(self,other:"Constraint")->bool:
|
639
|
+
if not isinstance(other,self.__class__):
|
640
|
+
return False
|
641
|
+
return (
|
642
|
+
other.name == self.name and
|
643
|
+
other.margin == self.margin
|
644
|
+
)
|
645
|
+
|
646
|
+
class RectMarginLeft(Constraint):
|
647
|
+
def __init__(self, margin: float):
|
648
|
+
super().__init__()
|
649
|
+
self.margin = margin
|
650
|
+
|
651
|
+
def evaluate(self, parent_widget, child_widget):
|
652
|
+
return child_widget.rect.left == parent_widget.rect.left + self.margin
|
653
|
+
|
654
|
+
def apply_constraint(self, parent_widget, child_widget):
|
655
|
+
if not self.evaluate(parent_widget, child_widget):
|
656
|
+
child_widget.set_position(
|
657
|
+
parent_widget.rect.left + self.margin, child_widget.rect.y
|
658
|
+
)
|
659
|
+
|
660
|
+
def __eq__(self,other:"Constraint")->bool:
|
661
|
+
if not isinstance(other,self.__class__):
|
662
|
+
return False
|
663
|
+
return (
|
664
|
+
other.name == self.name and
|
665
|
+
other.margin == self.margin
|
666
|
+
)
|
667
|
+
|
668
|
+
class RectMarginRight(Constraint):
|
669
|
+
def __init__(self, margin: float):
|
670
|
+
super().__init__()
|
671
|
+
self.margin = margin
|
672
|
+
|
673
|
+
def evaluate(self, parent_widget, child_widget):
|
674
|
+
return child_widget.rect.right == parent_widget.rect.right - self.margin
|
675
|
+
|
676
|
+
def apply_constraint(self, parent_widget, child_widget):
|
677
|
+
child_widget.set_position(
|
678
|
+
parent_widget.rect.right - child_widget.rect.w - self.margin,
|
679
|
+
child_widget.rect.y,
|
680
|
+
)
|
681
|
+
|
682
|
+
def __eq__(self,other:"Constraint")->bool:
|
683
|
+
if not isinstance(other,self.__class__):
|
684
|
+
return False
|
685
|
+
return (
|
686
|
+
other.name == self.name and
|
687
|
+
other.margin == self.margin
|
688
|
+
)
|
689
|
+
|
601
690
|
class PercentageMarginBottom(Constraint):
|
602
691
|
def __init__(self, margin: float):
|
603
692
|
super().__init__()
|
@@ -812,4 +901,4 @@ class PercentageRectMarginRight(Constraint):
|
|
812
901
|
return (
|
813
902
|
other.name == self.name and
|
814
903
|
other.margin == self.margin
|
815
|
-
)
|
904
|
+
)
|