batframework 1.1.0__py3-none-any.whl → 2.0.0__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 (81) hide show
  1. batFramework/__init__.py +84 -51
  2. batFramework/action.py +280 -252
  3. batFramework/actionContainer.py +105 -38
  4. batFramework/animatedSprite.py +81 -117
  5. batFramework/animation.py +91 -0
  6. batFramework/audioManager.py +156 -85
  7. batFramework/baseScene.py +249 -0
  8. batFramework/camera.py +245 -123
  9. batFramework/constants.py +57 -75
  10. batFramework/cutscene.py +239 -119
  11. batFramework/cutsceneManager.py +34 -0
  12. batFramework/drawable.py +107 -0
  13. batFramework/dynamicEntity.py +30 -23
  14. batFramework/easingController.py +58 -0
  15. batFramework/entity.py +130 -123
  16. batFramework/enums.py +171 -0
  17. batFramework/fontManager.py +65 -0
  18. batFramework/gui/__init__.py +28 -14
  19. batFramework/gui/animatedLabel.py +90 -0
  20. batFramework/gui/button.py +18 -84
  21. batFramework/gui/clickableWidget.py +244 -0
  22. batFramework/gui/collapseContainer.py +98 -0
  23. batFramework/gui/constraints/__init__.py +1 -0
  24. batFramework/gui/constraints/constraints.py +1066 -0
  25. batFramework/gui/container.py +220 -49
  26. batFramework/gui/debugger.py +140 -47
  27. batFramework/gui/draggableWidget.py +63 -0
  28. batFramework/gui/image.py +61 -23
  29. batFramework/gui/indicator.py +116 -40
  30. batFramework/gui/interactiveWidget.py +243 -22
  31. batFramework/gui/label.py +147 -110
  32. batFramework/gui/layout.py +442 -81
  33. batFramework/gui/meter.py +155 -0
  34. batFramework/gui/radioButton.py +43 -0
  35. batFramework/gui/root.py +228 -60
  36. batFramework/gui/scrollingContainer.py +282 -0
  37. batFramework/gui/selector.py +232 -0
  38. batFramework/gui/shape.py +286 -86
  39. batFramework/gui/slider.py +353 -0
  40. batFramework/gui/style.py +10 -0
  41. batFramework/gui/styleManager.py +49 -0
  42. batFramework/gui/syncedVar.py +43 -0
  43. batFramework/gui/textInput.py +331 -0
  44. batFramework/gui/textWidget.py +308 -0
  45. batFramework/gui/toggle.py +140 -62
  46. batFramework/gui/tooltip.py +35 -0
  47. batFramework/gui/widget.py +546 -307
  48. batFramework/manager.py +131 -50
  49. batFramework/particle.py +118 -0
  50. batFramework/propertyEaser.py +79 -0
  51. batFramework/renderGroup.py +34 -0
  52. batFramework/resourceManager.py +130 -0
  53. batFramework/scene.py +31 -226
  54. batFramework/sceneLayer.py +134 -0
  55. batFramework/sceneManager.py +200 -165
  56. batFramework/scrollingSprite.py +115 -0
  57. batFramework/sprite.py +46 -0
  58. batFramework/stateMachine.py +49 -51
  59. batFramework/templates/__init__.py +2 -0
  60. batFramework/templates/character.py +15 -0
  61. batFramework/templates/controller.py +158 -0
  62. batFramework/templates/stateMachine.py +39 -0
  63. batFramework/tileset.py +46 -0
  64. batFramework/timeManager.py +213 -0
  65. batFramework/transition.py +162 -157
  66. batFramework/triggerZone.py +22 -22
  67. batFramework/utils.py +306 -184
  68. {batframework-1.1.0.dist-info → batframework-2.0.0.dist-info}/LICENSE +1 -1
  69. {batframework-1.1.0.dist-info → batframework-2.0.0.dist-info}/METADATA +8 -4
  70. batframework-2.0.0.dist-info/RECORD +72 -0
  71. batFramework/cutsceneBlocks.py +0 -176
  72. batFramework/debugger.py +0 -48
  73. batFramework/easing.py +0 -71
  74. batFramework/gui/constraints.py +0 -204
  75. batFramework/gui/frame.py +0 -19
  76. batFramework/particles.py +0 -77
  77. batFramework/time.py +0 -75
  78. batFramework/transitionManager.py +0 -0
  79. batframework-1.1.0.dist-info/RECORD +0 -43
  80. {batframework-1.1.0.dist-info → batframework-2.0.0.dist-info}/WHEEL +0 -0
  81. {batframework-1.1.0.dist-info → batframework-2.0.0.dist-info}/top_level.txt +0 -0
batFramework/entity.py CHANGED
@@ -1,123 +1,130 @@
1
- import pygame
2
- import batFramework as bf
3
- from typing import Any
4
-
5
- class Entity:
6
- instance_num = 0
7
- def __init__(
8
- self,
9
- size : None|tuple[int,int]=None,
10
- no_surface : bool =False,
11
- surface_flags : int =0,
12
- convert_alpha : bool=False
13
- ) -> None:
14
- self.convert_alpha = convert_alpha
15
- if size is None:
16
- size= (100,100)
17
-
18
- if no_surface:
19
- self.surface = None
20
- else:
21
- self.surface = (pygame.Surface(size, surface_flags))
22
-
23
- if convert_alpha and self.surface is not None:
24
- self.surface = self.surface.convert_alpha()
25
- self.surface.fill((0,0,0,0))
26
-
27
- self.uid: Any = Entity.instance_num
28
- self.tags: list[str] = []
29
- self.parent_scene: bf.Scene | None = None
30
- self.rect = pygame.FRect(0, 0, *size)
31
-
32
- self.visible = True
33
- self._debug_color : tuple = bf.color.DARK_RED
34
- self.render_order = 0
35
- self.z_depth = 1
36
- Entity.instance_num += 1
37
-
38
- def get_bounding_box(self):
39
- yield (self.rect,self._debug_color)
40
-
41
- def set_debug_color(self, color):
42
- self._debug_color = color
43
-
44
- def set_visible(self, value: bool):
45
- self.visible = value
46
-
47
- def set_parent_scene(self, scene):
48
- self.parent_scene = scene
49
-
50
- def do_when_added(self):
51
- pass
52
-
53
- def do_when_removed(self):
54
- pass
55
-
56
- def set_position(self, x, y):
57
- self.rect.topleft = (x, y)
58
- return self
59
-
60
- def set_x(self,x):
61
- self.rect.x = x
62
- return self
63
-
64
- def set_y(self,y):
65
- self.rect.y = y
66
- return self
67
-
68
- def set_center(self, x, y):
69
- self.rect.center = (x, y)
70
- return self
71
-
72
- def set_uid(self, uid):
73
- self.uid = uid
74
- return self
75
-
76
- def add_tag(self, *tags):
77
- for tag in tags:
78
- if tag not in self.tags:
79
- self.tags.append(tag)
80
- self.tags.sort()
81
- return self
82
-
83
- def remove_tag(self, *tags):
84
- self.tags = [tag for tag in self.tags if tag not in tags]
85
- self.tags.sort()
86
-
87
- def has_tag(self, tag) -> bool:
88
- return tag in self.tags
89
-
90
- def process_event(self, event: pygame.Event)->bool:
91
- """
92
- Returns bool : True if the method is blocking (no propagation to next children of the scene)
93
- """
94
- self.do_process_actions(event)
95
- res = self.do_handle_event(event)
96
- self.do_reset_actions()
97
- return res
98
-
99
- def do_process_actions(self,event : pygame.Event)->None:
100
- pass
101
-
102
- def do_reset_actions(self)->None:
103
- pass
104
-
105
- def do_handle_event(self, event: pygame.Event) -> bool:
106
- return False
107
-
108
- def update(self, dt: float):
109
- self.do_update(dt)
110
-
111
- def do_update(self,dt:float):
112
- pass
113
-
114
- def draw(self, camera: bf.Camera) -> int:
115
- if not self.visible:
116
- return False
117
- if not self.surface or not camera.intersects(self.rect):
118
- return False
119
- camera.surface.blit(
120
- self.surface,
121
- tuple(round(i * self.z_depth) for i in camera.transpose(self.rect).topleft),
122
- )
123
- return True
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]
@@ -1,14 +1,28 @@
1
- from .constraints import *
2
- from .widget import Widget
3
- from .image import Image
4
- from .interactiveWidget import InteractiveWidget
5
- from .root import Root
6
- from .shape import Shape
7
- from .frame import Frame
8
- from .label import Label
9
- from .button import Button
10
- from .debugger import Debugger
11
- from .layout import *
12
- from .container import Container
13
- from .indicator import *
14
- from .toggle import Toggle
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)])))