batframework 1.1.0__py3-none-any.whl → 2.0.0a1__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 +84 -52
- batFramework/action.py +280 -252
- batFramework/actionContainer.py +105 -38
- batFramework/animatedSprite.py +81 -117
- batFramework/animation.py +91 -0
- batFramework/audioManager.py +156 -85
- batFramework/baseScene.py +249 -0
- batFramework/camera.py +245 -123
- batFramework/constants.py +57 -75
- batFramework/cutscene.py +239 -119
- batFramework/cutsceneManager.py +34 -0
- batFramework/drawable.py +107 -0
- batFramework/dynamicEntity.py +30 -23
- batFramework/easingController.py +58 -0
- batFramework/entity.py +130 -123
- batFramework/enums.py +171 -0
- batFramework/fontManager.py +65 -0
- batFramework/gui/__init__.py +28 -14
- batFramework/gui/animatedLabel.py +90 -0
- batFramework/gui/button.py +18 -84
- batFramework/gui/clickableWidget.py +244 -0
- batFramework/gui/collapseContainer.py +98 -0
- batFramework/gui/constraints/__init__.py +1 -0
- batFramework/gui/constraints/constraints.py +1066 -0
- batFramework/gui/container.py +220 -49
- batFramework/gui/debugger.py +140 -47
- batFramework/gui/draggableWidget.py +63 -0
- batFramework/gui/image.py +61 -23
- batFramework/gui/indicator.py +116 -40
- batFramework/gui/interactiveWidget.py +243 -22
- batFramework/gui/label.py +147 -110
- batFramework/gui/layout.py +442 -81
- batFramework/gui/meter.py +155 -0
- batFramework/gui/radioButton.py +43 -0
- batFramework/gui/root.py +228 -60
- batFramework/gui/scrollingContainer.py +282 -0
- batFramework/gui/selector.py +232 -0
- batFramework/gui/shape.py +286 -86
- batFramework/gui/slider.py +353 -0
- batFramework/gui/style.py +10 -0
- batFramework/gui/styleManager.py +49 -0
- batFramework/gui/syncedVar.py +43 -0
- batFramework/gui/textInput.py +331 -0
- batFramework/gui/textWidget.py +308 -0
- batFramework/gui/toggle.py +140 -62
- batFramework/gui/tooltip.py +35 -0
- batFramework/gui/widget.py +546 -307
- batFramework/manager.py +131 -50
- batFramework/particle.py +118 -0
- batFramework/propertyEaser.py +79 -0
- batFramework/renderGroup.py +34 -0
- batFramework/resourceManager.py +130 -0
- batFramework/scene.py +31 -226
- batFramework/sceneLayer.py +134 -0
- batFramework/sceneManager.py +200 -165
- batFramework/scrollingSprite.py +115 -0
- batFramework/sprite.py +46 -0
- batFramework/stateMachine.py +49 -51
- batFramework/templates/__init__.py +2 -0
- batFramework/templates/character.py +15 -0
- batFramework/templates/controller.py +158 -0
- batFramework/templates/stateMachine.py +39 -0
- batFramework/tileset.py +46 -0
- batFramework/timeManager.py +213 -0
- batFramework/transition.py +162 -157
- batFramework/triggerZone.py +22 -22
- batFramework/utils.py +306 -184
- {batframework-1.1.0.dist-info → batframework-2.0.0a1.dist-info}/LICENSE +20 -20
- {batframework-1.1.0.dist-info → batframework-2.0.0a1.dist-info}/METADATA +7 -2
- batframework-2.0.0a1.dist-info/RECORD +72 -0
- batFramework/cutsceneBlocks.py +0 -176
- batFramework/debugger.py +0 -48
- batFramework/easing.py +0 -71
- batFramework/gui/constraints.py +0 -204
- batFramework/gui/frame.py +0 -19
- batFramework/particles.py +0 -77
- batFramework/time.py +0 -75
- batFramework/transitionManager.py +0 -0
- batframework-1.1.0.dist-info/RECORD +0 -43
- {batframework-1.1.0.dist-info → batframework-2.0.0a1.dist-info}/WHEEL +0 -0
- {batframework-1.1.0.dist-info → batframework-2.0.0a1.dist-info}/top_level.txt +0 -0
batFramework/entity.py
CHANGED
@@ -1,123 +1,130 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
if
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
self.
|
34
|
-
self
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
self
|
46
|
-
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
self
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
self.
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
)
|
123
|
-
|
1
|
+
from typing import Any, Self
|
2
|
+
import pygame
|
3
|
+
import batFramework as bf
|
4
|
+
from typing import TYPE_CHECKING
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from .camera import Camera
|
8
|
+
|
9
|
+
|
10
|
+
class Entity:
|
11
|
+
_count: int = 0
|
12
|
+
_available_uids: set[int] = set()
|
13
|
+
|
14
|
+
def __init__(self,*args,**kwargs) -> None:
|
15
|
+
if Entity._available_uids:
|
16
|
+
self.uid = Entity._available_uids.pop()
|
17
|
+
else:
|
18
|
+
self.uid = Entity._count
|
19
|
+
Entity._count += 1
|
20
|
+
size = kwargs.get("size",(10,10))
|
21
|
+
self.rect = pygame.FRect(0, 0, *size)
|
22
|
+
self.tags: list[str] = []
|
23
|
+
self.parent_scene: bf.Scene | None = None
|
24
|
+
self.parent_layer: bf.SceneLayer | None = None
|
25
|
+
self.debug_color: tuple | str = "red"
|
26
|
+
|
27
|
+
def __del__(self):
|
28
|
+
try:
|
29
|
+
Entity._available_uids.add(self.uid)
|
30
|
+
except AttributeError:
|
31
|
+
pass
|
32
|
+
def set_position(self, x, y) -> Self:
|
33
|
+
self.rect.topleft = x, y
|
34
|
+
return self
|
35
|
+
|
36
|
+
def set_center(self, x, y) -> Self:
|
37
|
+
self.rect.center = x, y
|
38
|
+
return self
|
39
|
+
|
40
|
+
def get_debug_outlines(self):
|
41
|
+
yield (self.rect, self.debug_color)
|
42
|
+
|
43
|
+
def set_debug_color(self, color) -> Self:
|
44
|
+
self.debug_color = color
|
45
|
+
return self
|
46
|
+
|
47
|
+
def kill(self):
|
48
|
+
"""
|
49
|
+
Removes the entity from a scene layer
|
50
|
+
"""
|
51
|
+
if self.parent_layer:
|
52
|
+
self.parent_layer.remove(self)
|
53
|
+
|
54
|
+
def set_parent_layer(self, layer):
|
55
|
+
self.parent_layer = layer
|
56
|
+
|
57
|
+
def set_parent_scene(self, scene) -> Self:
|
58
|
+
if scene == self.parent_scene:
|
59
|
+
return self
|
60
|
+
if self.parent_scene is not None:
|
61
|
+
self.do_when_removed()
|
62
|
+
self.parent_scene = scene
|
63
|
+
if scene is not None:
|
64
|
+
self.do_when_added()
|
65
|
+
return self
|
66
|
+
|
67
|
+
def do_when_added(self):
|
68
|
+
pass
|
69
|
+
|
70
|
+
def do_when_removed(self):
|
71
|
+
pass
|
72
|
+
|
73
|
+
def add_tags(self, *tags) -> Self:
|
74
|
+
for tag in tags:
|
75
|
+
if tag not in self.tags:
|
76
|
+
self.tags.append(tag)
|
77
|
+
self.tags.sort()
|
78
|
+
return self
|
79
|
+
|
80
|
+
def remove_tags(self, *tags):
|
81
|
+
self.tags = [tag for tag in self.tags if tag not in tags]
|
82
|
+
|
83
|
+
def has_tags(self, *tags) -> bool:
|
84
|
+
"""
|
85
|
+
return True if entity contains all given tags
|
86
|
+
"""
|
87
|
+
return all(tag in self.tags for tag in tags)
|
88
|
+
|
89
|
+
def has_any_tags(self, *tags) -> bool:
|
90
|
+
"""
|
91
|
+
return True if entity contains any of given tags
|
92
|
+
"""
|
93
|
+
return any(tag in self.tags for tag in tags)
|
94
|
+
|
95
|
+
def get_tags(self) -> list[str]:
|
96
|
+
return self.tags
|
97
|
+
|
98
|
+
def process_event(self, event: pygame.Event) -> None:
|
99
|
+
if event.consumed:
|
100
|
+
return
|
101
|
+
self.process_actions(event)
|
102
|
+
self.handle_event(event)
|
103
|
+
|
104
|
+
def process_actions(self, event: pygame.Event) -> None:
|
105
|
+
"""
|
106
|
+
Process entity actions you may have set
|
107
|
+
"""
|
108
|
+
|
109
|
+
def reset_actions(self) -> None:
|
110
|
+
"""
|
111
|
+
Reset entity actions you may have set
|
112
|
+
"""
|
113
|
+
|
114
|
+
def handle_event(self, event: pygame.Event):
|
115
|
+
"""
|
116
|
+
Handle specific events with no action support
|
117
|
+
"""
|
118
|
+
return False
|
119
|
+
|
120
|
+
def update(self, dt: float) -> None:
|
121
|
+
"""
|
122
|
+
Update method to be overriden by subclasses of Entity (must call do_update and reset_actions)
|
123
|
+
"""
|
124
|
+
self.do_update(dt)
|
125
|
+
self.reset_actions()
|
126
|
+
|
127
|
+
def do_update(self, dt: float) -> None:
|
128
|
+
"""
|
129
|
+
Update method to be overriden for specific behavior by the end user
|
130
|
+
"""
|
batFramework/enums.py
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
import pygame
|
3
|
+
|
4
|
+
playerInput = [pygame.KEYDOWN,pygame.MOUSEBUTTONDOWN,pygame.KEYUP,pygame.MOUSEBUTTONUP]
|
5
|
+
|
6
|
+
class color:
|
7
|
+
WHITE = pygame.Color(255, 255, 255)
|
8
|
+
LIGHTER_GRAY = pygame.Color(236, 240, 241)
|
9
|
+
LIGHT_GRAY = pygame.Color(189, 195, 199)
|
10
|
+
DARK_GRAY = pygame.Color(66, 66, 66)
|
11
|
+
DARKER_GRAY = pygame.Color(23, 23, 23)
|
12
|
+
BLACK = pygame.Color(0, 0, 0)
|
13
|
+
|
14
|
+
TURQUOISE = pygame.Color(26, 188, 156)
|
15
|
+
TURQUOISE_SHADE = pygame.Color(22, 160, 133)
|
16
|
+
|
17
|
+
GREEN = pygame.Color(46, 204, 113)
|
18
|
+
GREEN_SHADE = pygame.Color(39, 174, 96)
|
19
|
+
|
20
|
+
BLUE = pygame.Color(52, 152, 219)
|
21
|
+
BLUE_SHADE = pygame.Color(41, 128, 185)
|
22
|
+
|
23
|
+
PURPLE = pygame.Color(155, 89, 182)
|
24
|
+
PURPLE_SHADE = pygame.Color(142, 68, 173)
|
25
|
+
|
26
|
+
CHARCOAL = pygame.Color(52, 73, 94)
|
27
|
+
CHARCOAL_SHADE = pygame.Color(44, 62, 80)
|
28
|
+
|
29
|
+
GOLD = pygame.Color(241, 196, 15)
|
30
|
+
GOLD_SHADE = pygame.Color(243, 156, 18)
|
31
|
+
|
32
|
+
ORANGE = pygame.Color(230, 126, 34)
|
33
|
+
ORANGE_SHADE = pygame.Color(211, 84, 0)
|
34
|
+
|
35
|
+
RED = pygame.Color(231, 76, 60)
|
36
|
+
RED_SHADE = pygame.Color(192, 57, 43)
|
37
|
+
|
38
|
+
CLOUD = pygame.Color(236, 240, 241)
|
39
|
+
CLOUD_SHADE = pygame.Color(189, 195, 199)
|
40
|
+
|
41
|
+
CONCRETE = pygame.Color(149, 165, 166)
|
42
|
+
CONCRETE_SHADE = pygame.Color(127, 140, 141)
|
43
|
+
|
44
|
+
# GB
|
45
|
+
DARKER_GB = pygame.Color(27, 42, 9)
|
46
|
+
DARK_GB = pygame.Color(14, 69, 11)
|
47
|
+
LIGHT_GB = pygame.Color(73, 107, 34)
|
48
|
+
LIGHTER_GB = pygame.Color(154, 158, 63)
|
49
|
+
|
50
|
+
@classmethod
|
51
|
+
def __iter__(cls):
|
52
|
+
for name, value in vars(cls).items():
|
53
|
+
if not name.startswith("__") and isinstance(value, pygame.Color):
|
54
|
+
yield value
|
55
|
+
|
56
|
+
@classmethod
|
57
|
+
def items(cls):
|
58
|
+
"""Iterate over (name, color) pairs."""
|
59
|
+
for name, value in vars(cls).items():
|
60
|
+
if not name.startswith("__") and isinstance(value, pygame.Color):
|
61
|
+
yield name, value
|
62
|
+
|
63
|
+
@staticmethod
|
64
|
+
def mult(color: pygame.typing.ColorLike , factor: float):
|
65
|
+
color = pygame.Color(color)
|
66
|
+
return pygame.Color(
|
67
|
+
min(max(0, int(color[0] * factor)), 255),
|
68
|
+
min(max(0, int(color[1] * factor)), 255),
|
69
|
+
min(max(0, int(color[2] * factor)), 255),
|
70
|
+
color[3] if len(color)== 4 else 255
|
71
|
+
)
|
72
|
+
|
73
|
+
@staticmethod
|
74
|
+
def lerp(color1: pygame.Color | tuple[int, int, int, int], color2: pygame.Color | tuple[int, int, int, int], t: float) -> pygame.Color:
|
75
|
+
"""Linearly interpolate between two colors."""
|
76
|
+
t = max(0.0, min(1.0, t))
|
77
|
+
c1 = color1 if isinstance(color1, (tuple, list)) else (color1.r, color1.g, color1.b, color1.a)
|
78
|
+
c2 = color2 if isinstance(color2, (tuple, list)) else (color2.r, color2.g, color2.b, color2.a)
|
79
|
+
return pygame.Color(
|
80
|
+
int(c1[0] + (c2[0] - c1[0]) * t),
|
81
|
+
int(c1[1] + (c2[1] - c1[1]) * t),
|
82
|
+
int(c1[2] + (c2[2] - c1[2]) * t),
|
83
|
+
int((c1[3] if len(c1) > 3 else 255) + ((c2[3] if len(c2) > 3 else 255) - (c1[3] if len(c1) > 3 else 255)) * t)
|
84
|
+
)
|
85
|
+
|
86
|
+
@staticmethod
|
87
|
+
def get_name(color_value:pygame.Color):
|
88
|
+
for name, val in color.__dict__.items():
|
89
|
+
# Only consider attributes that are pygame.Color instances
|
90
|
+
if isinstance(val, pygame.Color) and val == color_value:
|
91
|
+
return name
|
92
|
+
return str(color_value)
|
93
|
+
|
94
|
+
|
95
|
+
class easing(Enum):
|
96
|
+
LINEAR = (0, 0, 1, 1)
|
97
|
+
EASE_IN = (0.95, 0, 1, 0.55)
|
98
|
+
EASE_OUT = (0.5, 1, 0.5, 1)
|
99
|
+
EASE_IN_OUT = (0.55, 0, 0.45, 1)
|
100
|
+
EASE_IN_OUT_ELASTIC = (0.76,-0.36,0.41,1.34)
|
101
|
+
|
102
|
+
def __init__(self, *control_points):
|
103
|
+
self.control_points = control_points
|
104
|
+
|
105
|
+
@classmethod
|
106
|
+
def create(cls, *control_points):
|
107
|
+
"""Create a custom easing instance."""
|
108
|
+
instance = object.__new__(cls)
|
109
|
+
instance._value_ = control_points
|
110
|
+
instance.control_points = control_points
|
111
|
+
return instance
|
112
|
+
|
113
|
+
class axis(Enum):
|
114
|
+
HORIZONTAL = "horizontal"
|
115
|
+
VERTICAL = "vertical"
|
116
|
+
|
117
|
+
|
118
|
+
class spacing(Enum):
|
119
|
+
MIN = "min"
|
120
|
+
HALF = "half"
|
121
|
+
MAX = "max"
|
122
|
+
MANUAL = "manual"
|
123
|
+
|
124
|
+
|
125
|
+
class alignment(Enum):
|
126
|
+
LEFT = "left"
|
127
|
+
RIGHT = "right"
|
128
|
+
CENTER = "center"
|
129
|
+
TOP = "top"
|
130
|
+
BOTTOM = "bottom"
|
131
|
+
TOPLEFT = "topleft"
|
132
|
+
TOPRIGHT = "topright"
|
133
|
+
MIDLEFT = "midleft"
|
134
|
+
MIDRIGHT = "midright"
|
135
|
+
MIDTOP = "midtop"
|
136
|
+
MIDBOTTOM = "midbottom"
|
137
|
+
BOTTOMLEFT = "bottomleft"
|
138
|
+
BOTTOMRIGHT = "bottomright"
|
139
|
+
|
140
|
+
|
141
|
+
class direction(Enum):
|
142
|
+
LEFT = 0
|
143
|
+
UP = 1
|
144
|
+
RIGHT = 2
|
145
|
+
DOWN = 3
|
146
|
+
|
147
|
+
|
148
|
+
class drawMode(Enum):
|
149
|
+
SOLID = 0
|
150
|
+
TEXTURED = 1
|
151
|
+
|
152
|
+
|
153
|
+
class debugMode(Enum):
|
154
|
+
HIDDEN = 0
|
155
|
+
DEBUGGER = 1
|
156
|
+
OUTLINES = 2
|
157
|
+
|
158
|
+
|
159
|
+
class actionType(Enum):
|
160
|
+
INSTANTANEOUS = 0
|
161
|
+
CONTINUOUS = 1
|
162
|
+
HOLDING = 2
|
163
|
+
|
164
|
+
|
165
|
+
class textMode(Enum):
|
166
|
+
ALPHABETICAL = 0
|
167
|
+
NUMERICAL = 1
|
168
|
+
ALPHANUMERICAL = 3
|
169
|
+
|
170
|
+
|
171
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from .utils import Singleton
|
2
|
+
|
3
|
+
# put font stuff here later
|
4
|
+
import pygame
|
5
|
+
import os
|
6
|
+
import batFramework as bf
|
7
|
+
|
8
|
+
|
9
|
+
class FontManager(metaclass=Singleton):
|
10
|
+
def __init__(self):
|
11
|
+
pygame.font.init()
|
12
|
+
self.DEFAULT_FONT_SIZE = 16
|
13
|
+
self.MIN_FONT_SIZE = 8
|
14
|
+
self.MAX_FONT_SIZE = 64
|
15
|
+
self.DEFAULT_ANTIALIAS = False
|
16
|
+
self.FONTS = {}
|
17
|
+
|
18
|
+
def set_default_antialias(self, value: bool):
|
19
|
+
self.DEFAULT_ANTIALIAS = value
|
20
|
+
|
21
|
+
def set_default_text_size(self, size: int):
|
22
|
+
self.DEFAULT_FONT_SIZE = size
|
23
|
+
|
24
|
+
def init_font(self, raw_path: str | None):
|
25
|
+
try:
|
26
|
+
if raw_path is not None:
|
27
|
+
self.load_font(raw_path if raw_path else None, None)
|
28
|
+
self.load_font(raw_path)
|
29
|
+
except FileNotFoundError:
|
30
|
+
self.load_sysfont(raw_path)
|
31
|
+
self.load_sysfont(raw_path, None)
|
32
|
+
|
33
|
+
def load_font(self, path: str | None, name: str | None = ""):
|
34
|
+
if path is not None:
|
35
|
+
path = bf.ResourceManager().get_path(path) # convert path if given
|
36
|
+
filename = None
|
37
|
+
if path is not None:
|
38
|
+
filename = os.path.basename(path).split(".")[0]
|
39
|
+
|
40
|
+
# get filename if path is given, else None
|
41
|
+
if name != "":
|
42
|
+
filename = name # if name is not given, name is the filename
|
43
|
+
self.FONTS[filename] = {}
|
44
|
+
# fill the dict
|
45
|
+
for size in range(self.MIN_FONT_SIZE, self.MAX_FONT_SIZE+1, 2):
|
46
|
+
self.FONTS[filename][size] = pygame.font.Font(path, size=size)
|
47
|
+
|
48
|
+
def load_sysfont(self, font_name: str | None, key: str | None = ""):
|
49
|
+
if key == "":
|
50
|
+
key = font_name
|
51
|
+
if font_name is None or pygame.font.match_font(font_name) is None:
|
52
|
+
raise FileNotFoundError(f"Requested font '{font_name}' was not found")
|
53
|
+
self.FONTS[font_name] = {}
|
54
|
+
|
55
|
+
for size in range(self.MIN_FONT_SIZE, self.MAX_FONT_SIZE+1, 2):
|
56
|
+
self.FONTS[key][size] = pygame.font.SysFont(font_name, size=size)
|
57
|
+
|
58
|
+
def get_font(
|
59
|
+
self, name: str | None = None, text_size: int = 12
|
60
|
+
) -> pygame.Font | None:
|
61
|
+
if not name in self.FONTS:
|
62
|
+
return None
|
63
|
+
if not text_size in self.FONTS[name]:
|
64
|
+
return None
|
65
|
+
return self.FONTS[name][text_size]
|
batFramework/gui/__init__.py
CHANGED
@@ -1,14 +1,28 @@
|
|
1
|
-
from .
|
2
|
-
from .
|
3
|
-
from .
|
4
|
-
from .
|
5
|
-
from .
|
6
|
-
from .
|
7
|
-
from .
|
8
|
-
from .
|
9
|
-
from .
|
10
|
-
from .
|
11
|
-
from .
|
12
|
-
from .
|
13
|
-
from .
|
14
|
-
from .
|
1
|
+
from .widget import Widget
|
2
|
+
from .styleManager import StyleManager
|
3
|
+
from .style import Style
|
4
|
+
from .image import Image
|
5
|
+
from .interactiveWidget import InteractiveWidget
|
6
|
+
from .draggableWidget import DraggableWidget
|
7
|
+
from .clickableWidget import ClickableWidget
|
8
|
+
from .root import Root
|
9
|
+
from .shape import Shape
|
10
|
+
from .meter import BarMeter
|
11
|
+
from .textWidget import TextWidget
|
12
|
+
from .label import Label
|
13
|
+
from .tooltip import ToolTip
|
14
|
+
from .animatedLabel import AnimatedLabel
|
15
|
+
from .textInput import TextInput
|
16
|
+
from .button import Button
|
17
|
+
from .debugger import *
|
18
|
+
from .layout import *
|
19
|
+
from .container import Container
|
20
|
+
from .indicator import *
|
21
|
+
from .toggle import Toggle
|
22
|
+
from .syncedVar import SyncedVar
|
23
|
+
from .radioButton import RadioButton
|
24
|
+
from .slider import Slider
|
25
|
+
from .selector import Selector
|
26
|
+
from .scrollingContainer import ScrollingContainer
|
27
|
+
from .collapseContainer import CollapseContainer
|
28
|
+
import batFramework.gui.constraints as constraints
|
@@ -0,0 +1,90 @@
|
|
1
|
+
from .label import Label
|
2
|
+
import batFramework as bf
|
3
|
+
from typing import Self,Callable,Any
|
4
|
+
|
5
|
+
|
6
|
+
class AnimatedLabel(Label):
|
7
|
+
def __init__(self,text="") -> None:
|
8
|
+
self.cursor_position: float = 0.0
|
9
|
+
self.text_speed: float = 20.0
|
10
|
+
self.is_over: bool = True
|
11
|
+
self.is_paused: bool = False
|
12
|
+
self.original_text = ""
|
13
|
+
self.end_callback : Callable[[],Any]= None
|
14
|
+
self.set_autoresize(False)
|
15
|
+
self.set_alignment(bf.alignment.LEFT)
|
16
|
+
super().__init__("")
|
17
|
+
self.set_text(text)
|
18
|
+
|
19
|
+
def __str__(self) -> str:
|
20
|
+
return "AnimatedLabel"
|
21
|
+
|
22
|
+
def set_end_callback(self,callback:Callable[[],Any]):
|
23
|
+
self.end_callback = callback
|
24
|
+
|
25
|
+
def pause(self) -> Self:
|
26
|
+
self.is_paused = True
|
27
|
+
return self
|
28
|
+
|
29
|
+
def resume(self) -> Self:
|
30
|
+
self.is_paused = False
|
31
|
+
return self
|
32
|
+
|
33
|
+
def set_text_speed(self, speed: float) -> Self:
|
34
|
+
self.text_speed = speed
|
35
|
+
return self
|
36
|
+
|
37
|
+
def cut_text_to_width(self, text: str) -> list[str]:
|
38
|
+
w = self.get_inner_width()
|
39
|
+
if text == "" or not self.text_widget.font_object or w < self.text_widget.font_object.point_size:
|
40
|
+
return [text]
|
41
|
+
left = 0
|
42
|
+
font_object = self.text_widget.font_object
|
43
|
+
for index in range(len(text)):
|
44
|
+
width = font_object.size(text[left:index])[0]
|
45
|
+
|
46
|
+
if width > w:
|
47
|
+
cut_point_start = index - 1
|
48
|
+
cut_point_end = index - 1
|
49
|
+
last_space = text.rfind(" ", 0, cut_point_start)
|
50
|
+
last_nline = text.rfind("\n", 0, cut_point_start)
|
51
|
+
|
52
|
+
if last_space != -1 or last_nline != -1: # space was found !:
|
53
|
+
cut_point_start = max(last_space, last_nline)
|
54
|
+
cut_point_end = cut_point_start + 1
|
55
|
+
res = [text[:cut_point_start].strip()]
|
56
|
+
res.extend(self.cut_text_to_width(text[cut_point_end:].strip()))
|
57
|
+
return res
|
58
|
+
elif text[index] == "\n":
|
59
|
+
left = index
|
60
|
+
return [text]
|
61
|
+
|
62
|
+
|
63
|
+
def _set_text_internal(self,text:str)->Self:
|
64
|
+
super().set_text(text)
|
65
|
+
return self
|
66
|
+
|
67
|
+
def set_text(self,text:str)->Self:
|
68
|
+
self.original_text = text
|
69
|
+
self.is_over = False
|
70
|
+
self.cursor_position = 0
|
71
|
+
|
72
|
+
def set_size(self, size):
|
73
|
+
super().set_size(size)
|
74
|
+
self._set_text_internal('\n'.join(self.cut_text_to_width(self.original_text[: int(self.cursor_position)])))
|
75
|
+
|
76
|
+
def do_update(self, dt):
|
77
|
+
if self.is_over:
|
78
|
+
return
|
79
|
+
if not self.is_over and self.cursor_position == len(self.original_text):
|
80
|
+
if len(self.original_text) == 0:
|
81
|
+
self._set_text_internal("")
|
82
|
+
self.is_over = True
|
83
|
+
if self.end_callback is not None:
|
84
|
+
self.end_callback()
|
85
|
+
return
|
86
|
+
self.cursor_position = min(
|
87
|
+
self.cursor_position + self.text_speed * dt, len(self.original_text)
|
88
|
+
)
|
89
|
+
# self.set_text(self.original_text[: int(self.cursor_position)])
|
90
|
+
self._set_text_internal('\n'.join(self.cut_text_to_width(self.original_text[: int(self.cursor_position)])))
|