batframework 1.0.9a10__py3-none-any.whl → 1.0.10__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 (76) hide show
  1. batFramework/__init__.py +53 -76
  2. batFramework/action.py +99 -126
  3. batFramework/actionContainer.py +9 -53
  4. batFramework/animatedSprite.py +114 -56
  5. batFramework/audioManager.py +36 -82
  6. batFramework/camera.py +69 -263
  7. batFramework/constants.py +53 -29
  8. batFramework/cutscene.py +109 -243
  9. batFramework/cutsceneBlocks.py +176 -0
  10. batFramework/debugger.py +48 -0
  11. batFramework/dynamicEntity.py +9 -16
  12. batFramework/easing.py +71 -0
  13. batFramework/entity.py +85 -92
  14. batFramework/gui/__init__.py +3 -14
  15. batFramework/gui/button.py +78 -12
  16. batFramework/gui/constraints.py +204 -0
  17. batFramework/gui/container.py +31 -183
  18. batFramework/gui/debugger.py +43 -126
  19. batFramework/gui/frame.py +19 -0
  20. batFramework/gui/image.py +20 -55
  21. batFramework/gui/indicator.py +22 -95
  22. batFramework/gui/interactiveWidget.py +12 -229
  23. batFramework/gui/label.py +77 -311
  24. batFramework/gui/layout.py +66 -411
  25. batFramework/gui/root.py +35 -203
  26. batFramework/gui/shape.py +57 -247
  27. batFramework/gui/toggle.py +48 -114
  28. batFramework/gui/widget.py +243 -457
  29. batFramework/manager.py +29 -113
  30. batFramework/particles.py +77 -0
  31. batFramework/scene.py +217 -22
  32. batFramework/sceneManager.py +129 -161
  33. batFramework/stateMachine.py +8 -11
  34. batFramework/time.py +75 -0
  35. batFramework/transition.py +124 -129
  36. batFramework/transitionManager.py +0 -0
  37. batFramework/triggerZone.py +4 -4
  38. batFramework/utils.py +144 -266
  39. {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/METADATA +24 -17
  40. batframework-1.0.10.dist-info/RECORD +43 -0
  41. batFramework/animation.py +0 -77
  42. batFramework/baseScene.py +0 -240
  43. batFramework/cutsceneManager.py +0 -34
  44. batFramework/drawable.py +0 -77
  45. batFramework/easingController.py +0 -58
  46. batFramework/enums.py +0 -135
  47. batFramework/fontManager.py +0 -65
  48. batFramework/gui/animatedLabel.py +0 -89
  49. batFramework/gui/clickableWidget.py +0 -245
  50. batFramework/gui/constraints/__init__.py +0 -1
  51. batFramework/gui/constraints/constraints.py +0 -980
  52. batFramework/gui/draggableWidget.py +0 -44
  53. batFramework/gui/meter.py +0 -96
  54. batFramework/gui/radioButton.py +0 -35
  55. batFramework/gui/selector.py +0 -250
  56. batFramework/gui/slider.py +0 -397
  57. batFramework/gui/style.py +0 -10
  58. batFramework/gui/styleManager.py +0 -54
  59. batFramework/gui/syncedVar.py +0 -49
  60. batFramework/gui/textInput.py +0 -306
  61. batFramework/gui/tooltip.py +0 -30
  62. batFramework/particle.py +0 -118
  63. batFramework/propertyEaser.py +0 -79
  64. batFramework/renderGroup.py +0 -34
  65. batFramework/resourceManager.py +0 -130
  66. batFramework/sceneLayer.py +0 -138
  67. batFramework/scrollingSprite.py +0 -115
  68. batFramework/sprite.py +0 -51
  69. batFramework/templates/__init__.py +0 -1
  70. batFramework/templates/controller.py +0 -97
  71. batFramework/tileset.py +0 -46
  72. batFramework/timeManager.py +0 -213
  73. batframework-1.0.9a10.dist-info/RECORD +0 -67
  74. {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/LICENSE +0 -0
  75. {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/WHEEL +0 -0
  76. {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/top_level.txt +0 -0
@@ -1,239 +1,22 @@
1
1
  from .widget import Widget
2
- from typing import Self
3
- from typing import TYPE_CHECKING
4
- import pygame
5
- from math import cos,floor,ceil
6
-
7
- if TYPE_CHECKING:
8
- from .container import Container
9
- import batFramework as bf
10
-
11
- def children_has_focus(widget)->bool:
12
- if isinstance(widget,InteractiveWidget) and widget.is_focused:
13
- return True
14
- for child in widget.children:
15
- if children_has_focus(child):
16
- return True
17
- return False
18
-
19
2
 
20
3
  class InteractiveWidget(Widget):
21
- __focus_effect_cache = {}
22
- def __init__(self, *args, **kwargs) -> None:
23
- self.is_focused: bool = False
24
- self.is_hovered: bool = False
25
- self.is_clicked_down: list[bool] = [False]*5
26
- self.focused_index = 0
4
+ def __init__(self,*args,**kwargs):
5
+ super().__init__(convert_alpha = True)
27
6
  self.focusable = True
28
- super().__init__(*args, **kwargs)
29
-
30
- def set_focusable(self, value: bool) -> Self:
31
- self.focusable = value
32
- return self
33
-
34
- def allow_focus_to_self(self) -> bool:
35
- return self.visible
7
+ self.is_focused : bool = False
36
8
 
37
- def get_focus(self) -> bool:
38
- if self.focusable and ((r := self.get_root()) is not None):
39
- r.focus_on(self)
40
- if self.parent and isinstance(self.parent, InteractiveWidget):
41
- self.parent.set_focused_child(self)
42
- return True
43
- return False
9
+ def get_focus(self)->bool:
10
+ if self.parent is None or not self.focusable: return False
11
+ self.get_root().focus_on(self)
44
12
 
45
- def lose_focus(self) -> bool:
46
- if self.is_focused and ((r := self.get_root()) is not None):
47
- r.focus_on(None)
48
- return True
49
- return False
50
-
51
- def set_parent(self, parent: Widget) -> Self:
52
- if parent is None and children_has_focus(self):
53
- self.get_root().clear_focused()
54
- # pass focus on
55
-
56
- return super().set_parent(parent)
57
-
58
- def on_get_focus(self) -> None:
13
+ def on_get_focus(self)->None:
59
14
  self.is_focused = True
60
- if isinstance(self.parent,bf.gui.Container):
61
- self.parent.layout.scroll_to_widget(self)
62
- self.do_on_get_focus()
63
15
 
64
- def on_lose_focus(self) -> None:
16
+ def on_lose_focus(self)->None:
65
17
  self.is_focused = False
66
- self.do_on_lose_focus()
67
-
68
18
 
69
-
70
- def get_interactive_widgets(self):
71
- """Retrieve all interactive widgets in the tree, in depth-first order."""
72
- widgets = []
73
- stack = [self]
74
- while stack:
75
- widget = stack.pop()
76
- if isinstance(widget, InteractiveWidget) and widget.allow_focus_to_self():
77
- widgets.append(widget)
78
- stack.extend(reversed(widget.children)) # Add children in reverse for left-to-right traversal
79
- return widgets
80
-
81
- def find_next_widget(self, current):
82
- """Find the next interactive widget, considering parent and sibling relationships."""
83
- if current.is_root:
84
- return None # Root has no parent
85
-
86
- siblings = current.parent.children
87
- start_index = siblings.index(current)
88
- good_index = -1
89
- for i in range(start_index + 1, len(siblings)):
90
- if isinstance(siblings[i], InteractiveWidget) and siblings[i].allow_focus_to_self():
91
- good_index = i
92
- break
93
- if good_index >= 0:
94
- # Not the last child, return the next sibling
95
- return siblings[good_index]
96
- else:
97
- # Current is the last child, move to parent's next sibling
98
- return self.find_next_widget(current.parent)
99
-
100
- def find_prev_widget(self, current : "Widget"):
101
- """Find the previous interactive widget, considering parent and sibling relationships."""
102
- if current.is_root:
103
- return None # Root has no parent
104
-
105
- # siblings = [c for c in current.parent.children if isinstance(c,InteractiveWidget) and c.allow_focus_to_self()]
106
- siblings = current.parent.children
107
- start_index = siblings.index(current)
108
- good_index = -1
109
- for i in range(start_index-1,-1,-1):
110
- sibling = siblings[i]
111
- if isinstance(sibling,InteractiveWidget):
112
- if sibling.allow_focus_to_self():
113
- good_index = i
114
- break
115
- if good_index >= 0:
116
- # Not the first child, return the previous sibling
117
- return siblings[good_index]
118
- else:
119
- # Current is the first child, move to parent's previous sibling
120
- return self.find_prev_widget(current.parent)
121
-
122
- def focus_next_tab(self, previous_widget):
123
- """Focus the next interactive widget."""
124
- if previous_widget:
125
- next_widget = self.find_next_widget(previous_widget)
126
- if next_widget:
127
- next_widget.get_focus()
128
-
129
- def focus_prev_tab(self, previous_widget):
130
- """Focus the previous interactive widget."""
131
- if previous_widget:
132
- prev_widget = self.find_prev_widget(previous_widget)
133
- if prev_widget:
134
- prev_widget.get_focus()
135
-
136
-
137
-
138
- def on_key_down(self, key) -> bool:
139
- """
140
- return True to stop event propagation
141
- """
142
- return self.do_on_key_down(key)
143
-
144
- def on_key_up(self, key) -> bool:
145
- """
146
- return True to stop event propagation
147
- """
148
- return self.do_on_key_up(key)
149
-
150
- def do_on_get_focus(self) -> None:
151
- pass
152
-
153
- def do_on_lose_focus(self) -> None:
154
- pass
155
-
156
- def do_on_key_down(self, key) -> bool:
157
- """
158
- return True to stop event propagation
159
- """
160
- return False
161
-
162
- def do_on_key_up(self, key) -> bool:
163
- """
164
- return True to stop event propagation
165
- """
166
- return False
167
-
168
- def on_click_down(self, button: int) -> bool:
169
- """
170
- return True to stop event propagation
171
- """
172
- if button < 1 or button > 5 : return False
173
- self.is_clicked_down[button-1] = True
174
- return self.do_on_click_down(button)
175
-
176
- def on_click_up(self, button: int) -> bool:
177
- """
178
- return True to stop event propagation
179
- """
180
- if button < 1 or button > 5 : return False
181
- self.is_clicked_down[button-1] = False
182
- return self.do_on_click_up(button)
183
-
184
- def do_on_click_down(self, button: int) -> None:
185
- return
186
-
187
- def do_on_click_up(self, button: int) -> None:
188
- return
189
-
190
- def on_enter(self) -> None:
191
- self.is_hovered = True
192
- self.do_on_enter()
193
-
194
- def on_exit(self) -> None:
195
- self.is_hovered = False
196
- self.is_clicked_down = [False]*5
197
- self.do_on_exit()
198
-
199
- def do_on_enter(self) -> None:
200
- pass
201
-
202
- def do_on_exit(self) -> None:
203
- pass
204
-
205
- def on_mouse_motion(self, x, y) -> None:
206
- self.do_on_mouse_motion(x, y)
207
-
208
- def do_on_mouse_motion(self, x, y) -> None:
209
- pass
210
-
211
- def set_focused_child(self, child: "InteractiveWidget"):
212
- pass
213
-
214
- def draw_focused(self, camera: bf.Camera) -> None:
215
- prop = 16
216
- pulse = int(prop * 0.75 - (prop * cos(pygame.time.get_ticks() / 100) / 4))
217
- delta = (pulse // 2) * 2 # ensure even
218
-
219
- # Get rect in screen space, inflated for visual effect
220
- screen_rect = camera.world_to_screen(self.rect.inflate(prop, prop))
221
-
222
- # Shrink for inner pulsing border
223
- inner = screen_rect.inflate(-delta, -delta)
224
- inner.topleft = 0,0
225
- inner.w = round(inner.w)
226
- inner.h = round(inner.h)
227
-
228
- surface = InteractiveWidget.__focus_effect_cache.get(inner.size)
229
- if surface is None:
230
- surface = pygame.Surface(inner.size)
231
- InteractiveWidget.__focus_effect_cache[inner.size] = surface
232
-
233
- surface.set_colorkey((0,0,0))
234
- pygame.draw.rect(surface, "white", inner, 2, *self.border_radius)
235
- pygame.draw.rect(surface, "black", inner.inflate(-1 * min(16,inner.w*0.75),0), 2)
236
- pygame.draw.rect(surface, "black", inner.inflate(0,-1 * min(16,inner.h*0.75)), 2)
237
- inner.center = screen_rect.center
238
- camera.surface.blit(surface,inner)
239
-
19
+ def lose_focus(self)->bool:
20
+ if self.is_focused and self.parent is not None:
21
+ self.get_root().focus_on(None)
22
+
batFramework/gui/label.py CHANGED
@@ -1,344 +1,110 @@
1
1
  import batFramework as bf
2
2
  import pygame
3
3
  from .shape import Shape
4
- from typing import Literal, Self,Union
5
- from math import ceil
4
+ from typing import Self
6
5
 
7
6
  class Label(Shape):
8
- _text_cache = {}
9
-
10
- def __init__(self, text: str = "") -> None:
11
- self.text = text
12
-
13
- # Allows scrolling the text
14
- self.allow_scroll : bool = True
15
-
16
- # Scroll variable
17
- self.scroll :pygame.Vector2 = pygame.Vector2(0,0)
18
-
19
- self.resized_flag: bool = False
20
-
7
+ def __init__(self,text:str) -> None:
8
+ self._text = ""
21
9
  # Enable/Disable antialiasing
22
- self.antialias: bool = bf.FontManager().DEFAULT_ANTIALIAS
23
-
24
- self.text_size = bf.FontManager().DEFAULT_FONT_SIZE
25
-
26
- self.auto_wraplength: bool = False
27
-
28
- self.alignment: bf.alignment = bf.alignment.CENTER
29
-
30
- self.text_color: tuple[int, int, int] | str = "black"
31
-
32
- self.text_outline_color: tuple[int, int, int] | str = "gray50"
33
-
34
- self.text_outline_surface: pygame.Surface = None
35
-
36
- self._text_outline_mask = pygame.Mask((3, 3), fill=True)
37
-
38
- self.line_alignment = pygame.FONT_LEFT
10
+ self._antialias : bool = True
11
+
12
+ self._text_size = bf.const.DEFAULT_TEXT_SIZE
13
+
14
+ self._text_color : tuple[int,int,int]|str = "black"
39
15
  # font name (given when loaded by utils) to use for the text
40
- self.font_name = None
16
+ self._font_name = None
41
17
  # reference to the font object
42
- self.font_object = None
18
+ self._font_object = None
43
19
  # Rect containing the text of the label
44
- self.text_rect = pygame.FRect(0, 0, 0, 0)
20
+ self._text_rect = None
45
21
  # text surface (result of font.render)
46
- self.text_surface: pygame.Surface = pygame.Surface((0, 0))
47
-
48
- self.show_text_outline: bool = False
49
-
50
- self.is_italic: bool = False
51
-
52
- self.is_bold: bool = False
53
-
54
- self.is_underlined: bool = False
55
-
56
- super().__init__((0, 0))
57
- self.set_padding((10, 4))
22
+ self._text_surface : pygame.Surface | None= None
23
+ super().__init__(width=0,height=0)
24
+ self.set_padding((10,4))
58
25
  self.set_debug_color("blue")
59
- self.set_color("gray50")
26
+ self.set_color("white")
60
27
  self.set_autoresize(True)
61
28
  self.set_font(force=True)
29
+ self.set_text(text)
62
30
 
63
-
64
- def __str__(self) -> str:
65
- return f"Label({repr(self.text)})"
66
-
67
-
68
- def set_allow_scroll(self, value:bool)->Self:
69
- if self.allow_scroll == value: return self
70
- self.allow_scroll = value
71
- self.dirty_shape = True
31
+ def set_text_color(self,color)->Self:
32
+ self._text_color = color
33
+ self.build()
72
34
  return self
73
35
 
74
- def set_text_color(self, color) -> Self:
75
- self.text_color = color
76
- self.dirty_surface = True
77
- return self
36
+ def to_string_id(self)->str:
37
+ return f"Label({self._text})"
78
38
 
79
- def set_line_alignment(self, alignment: Union[Literal["left"], Literal["right"], Literal["center"]]) -> Self:
80
- self.line_alignment = alignment
81
- self.dirty_surface = True
82
- return self
83
39
 
84
- def set_italic(self, value: bool) -> Self:
85
- if value == self.is_italic:
86
- return self
87
- self.is_italic = value
88
- if self.autoresize_h or self.autoresize_w:
89
- self.dirty_shape = True
90
- else:
91
- self.dirty_surface = True
92
- return self
40
+ def get_bounding_box(self):
41
+ yield from super().get_bounding_box()
42
+ if self._text_rect : yield self._text_rect.move(*self.rect.topleft)
93
43
 
94
- def set_bold(self, value: bool) -> Self:
95
- if value == self.is_bold:
96
- return self
97
- self.is_bold = value
98
- if self.autoresize_h or self.autoresize_w:
99
- self.dirty_shape = True
100
- else:
101
- self.dirty_surface = True
44
+ def set_font(self,font_name:str=None,force:bool = False)-> "Label":
45
+ if font_name == self._font_name and not force: return self
46
+ self._font_name = font_name
47
+ self._font_object = bf.utils.get_font(self._font_name,self._text_size)
48
+ self.build()
102
49
  return self
103
50
 
104
- def set_underlined(self, value: bool) -> Self:
105
- if value == self.is_underlined:
106
- return self
107
- self.is_underlined = value
108
- self.dirty_surface = True
51
+ def set_text_size(self,text_size:int) -> "Label":
52
+ text_size = round(text_size/2) * 2
53
+ if text_size == self._text_size : return self
54
+ self._text_size = text_size
55
+ self._font_object = bf.utils.get_font(self._font_name,self._text_size)
56
+ self.build()
109
57
  return self
110
58
 
111
- def set_text_outline_mask_size(self,size:tuple[int,int])->Self:
112
- old_size = self._text_outline_mask.get_size()
113
- m = [[self._text_outline_mask.get_at((x,y)) for x in range(min(old_size[0],size[0]))] for y in range(min(old_size[1],size[1]))]
114
- self._text_outline_mask = pygame.Mask(size, fill=True)
115
- self.set_text_outline_matrix(m)
116
- return self
117
-
118
- def set_text_outline_matrix(self, matrix: list[list[0 | 1]]) -> Self:
119
- if matrix is None:
120
- matrix = [[0 for _ in range(3)] for _ in range(3)]
121
- for y in range(3):
122
- for x in range(3):
123
- self._text_outline_mask.set_at((x, y), matrix[2 - y][2 - x])
124
- self.dirty_shape = True
125
- return self
126
-
127
- def set_text_outline_color(self, color) -> Self:
128
- self.text_outline_color = color
129
- self.dirty_surface = True
130
- return self
131
-
132
- def enable_text_outline(self) -> Self:
133
- self.show_text_outline = True
134
- self.dirty_shape = True
135
- return self
59
+ def get_text_size(self)-> int:
60
+ return self._text_size
136
61
 
137
- def disable_text_outline(self) -> Self:
138
- self.show_text_outline = False
139
- self.dirty_shape = True
140
- return self
62
+ def is_antialias(self)->bool:
63
+ return self._antialias
141
64
 
142
- def set_alignment(self, alignment: bf.alignment) -> Self:
143
- self.alignment = alignment
144
- self.dirty_surface = True
65
+ def set_antialias(self,value:bool)->"Label":
66
+ self._antialias = value
67
+ self.build()
145
68
  return self
146
69
 
147
- def set_auto_wraplength(self, val: bool) -> Self:
148
- self.auto_wraplength = val
149
- if self.autoresize_h or self.autoresize_w:
150
- self.dirty_shape = True
151
- else:
152
- self.dirty_surface = True
70
+ def set_text(self,text:str) -> "Label":
71
+ if text == self._text : return self
72
+ self._text = text
73
+ self.build()
153
74
  return self
154
-
155
- def get_debug_outlines(self):
156
- yield from super().get_debug_outlines()
157
- if self.visible:
158
- yield (self.text_rect.move(self.rect.x - self.scroll.x,self.rect.y - self.scroll.y), "purple")
159
-
160
- # offset = self._get_outline_offset() if self.show_text_outline else (0,0)
161
- # yield (self.text_rect.move(self.rect.x - offset[0] - self.scroll.x,self.rect.y - offset[1] - self.scroll.y), "purple")
162
-
163
- def set_font(self, font_name: str = None, force: bool = False) -> Self:
164
- if font_name == self.font_name and not force:
165
- return self
166
- self.font_name = font_name
167
- self.font_object = bf.FontManager().get_font(self.font_name, self.text_size)
168
- if self.autoresize_h or self.autoresize_w:
169
- self.dirty_shape = True
170
- else:
171
- self.dirty_surface = True
172
- return self
173
-
174
- def set_text_size(self, text_size: int) -> Self:
175
- text_size = (text_size // 2) * 2
176
- if text_size == self.text_size:
177
- return self
178
- self.text_size = text_size
179
- self.font_object = bf.FontManager().get_font(self.font_name, self.text_size)
180
- self.dirty_shape = True
181
- return self
182
-
183
- def get_text_size(self) -> int:
184
- return self.text_size
185
-
186
- def is_antialias(self) -> bool:
187
- return self.antialias
188
-
189
- def set_antialias(self, value: bool) -> Self:
190
- self.antialias = value
191
- self.dirty_surface = True
192
- return self
193
-
194
- def set_text(self, text: str) -> Self:
195
- if text == self.text:
196
- return self
197
- self.text = text
198
- self.dirty_shape = True
199
- return self
200
-
201
- def get_min_required_size(self) -> tuple[float, float]:
202
- return self.expand_rect_with_padding(
203
- (0, 0, *self._get_text_rect_required_size())
204
- ).size
205
-
206
- def get_text(self) -> str:
207
- return self.text
208
-
209
- def _render_font(self, params: dict) -> pygame.Surface:
210
-
211
- if self.draw_mode == bf.drawMode.SOLID:
212
- params.pop("font_name")
213
-
214
- # save old settings
215
- old_italic = self.font_object.get_italic()
216
- old_bold = self.font_object.get_bold()
217
- old_underline = self.font_object.get_underline()
218
- old_align = self.font_object.align
219
- # setup font
220
- self.font_object.set_italic(self.is_italic)
221
- self.font_object.set_bold(self.is_bold)
222
- self.font_object.set_underline(self.is_underlined)
223
- self.font_object.align = self.line_alignment
224
- surf = self.font_object.render(**params)
225
-
226
- # reset font
227
- self.font_object.set_italic(old_italic)
228
- self.font_object.set_bold(old_bold)
229
- self.font_object.set_underline(old_underline)
230
- self.font_object.align = old_align
231
- else:
232
- params.pop("font_name")
233
- surf = self.font_object.render(**params)
234
-
235
- return surf
236
-
237
- def _get_outline_offset(self)->tuple[int,int]:
238
- mask_size = self._text_outline_mask.get_size()
239
- return mask_size[0]//2,mask_size[1]//2
240
75
 
241
- def _get_text_rect_required_size(self):
242
- font_height = self.font_object.get_ascent() - self.font_object.get_descent()
243
- if not self.text:
244
- size = (0,font_height)
245
- else:
246
- tmp_text = self.text
247
- if self.text.endswith('\n'):
248
- tmp_text+=" "
249
- params = {
250
- "font_name": self.font_object.name,
251
- "text": tmp_text,
252
- "antialias": self.antialias,
253
- "color": self.text_color,
254
- "bgcolor": None, # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
255
- "wraplength": int(self.get_inner_width()) if self.auto_wraplength and not self.autoresize_w else 0,
256
- }
257
-
258
- size = self._render_font(params).get_size()
259
- size = size[0],max(font_height,size[1])
260
-
261
- # print(self.text,size)
262
- s = self._get_outline_offset() if self.show_text_outline else (0,0)
263
- return size[0] + s[0]*2, size[1] + s[1]*2
264
-
265
- def _build_layout(self) -> bool:
266
- ret = False
267
- target_size = self.resolve_size(self.get_min_required_size())
76
+ def get_text(self)->str:
77
+ return self._text
268
78
 
269
- if self.rect.size != target_size :
270
- self.set_size(target_size)
271
- ret = True
272
- # self.apply_post_updates(skip_draw=True)
273
- # return True
274
79
 
275
- self.text_rect.size = self._get_text_rect_required_size()
276
- padded = self.get_local_inner_rect()
277
- self.align_text(self.text_rect, padded, self.alignment)
278
- return ret
279
-
280
- def _paint_text(self) -> None:
281
- if self.font_object is None:
282
- print(f"No font for widget with text : '{self}' :(")
80
+ def _build_text(self)-> None:
81
+ if self._font_object is None:
82
+ print("No font :(")
283
83
  return
284
- wrap = int(self.get_inner_width()) if self.auto_wraplength and not self.autoresize_w else 0
285
- params = {
286
- "font_name": self.font_object.name,
287
- "text": self.text,
288
- "antialias": self.antialias,
289
- "color": self.text_color,
290
- "bgcolor": None,
291
- "wraplength": wrap,
292
- }
293
-
294
- self.text_surface = self._render_font(params)
295
- if self.show_text_outline:
296
- self.text_outline_surface = (
297
- pygame.mask.from_surface(self.text_surface)
298
- .convolve(self._text_outline_mask)
299
- .to_surface(setcolor=self.text_outline_color, unsetcolor=(0, 0, 0, 0))
300
- )
301
-
302
- outline_offset = list(self._get_outline_offset())
303
- outline_offset[0]-=self.scroll.x
304
- outline_offset[1]-=self.scroll.y
305
- l = [
306
- (self.text_outline_surface,(self.text_rect.x - self.scroll.x,self.text_rect.y - self.scroll.y)),
307
- (self.text_surface, self.text_rect.move(*outline_offset))
308
- ]
309
- else:
310
- l = [(self.text_surface, self.text_rect.move(-self.scroll))]
311
-
312
- # clip surface
313
-
314
- old = self.surface.get_clip()
315
- self.surface.set_clip(self.get_local_inner_rect())
316
- self.surface.fblits(l)
317
- self.surface.set_clip(old)
318
-
319
- def align_text(
320
- self, text_rect: pygame.FRect, area: pygame.FRect, alignment: bf.alignment
321
- ):
322
- if alignment == bf.alignment.LEFT:
323
- alignment = bf.alignment.MIDLEFT
324
- elif alignment == bf.alignment.MIDRIGHT:
325
- alignment = bf.alignment.MIDRIGHT
326
-
327
- pos = area.__getattribute__(alignment.value)
328
- text_rect.__setattr__(alignment.value, pos)
329
- text_rect.y = ceil(text_rect.y)
330
-
331
- def build(self) -> bool:
332
- """
333
- return True if size changed
334
- """
335
-
336
- size_changed = self._build_layout()
84
+ # render(text, antialias, color, bgcolor=None, wraplength=0) -> Surface
85
+ self._text_surface = self._font_object.render(
86
+ text = self._text,
87
+ antialias = self._antialias,
88
+ color = self._text_color,
89
+ bgcolor = self._color,
90
+ wraplength = 0
91
+ )
92
+ self._text_rect = self._text_surface.get_frect()
93
+
94
+ def _build_layout(self)->None:
95
+ if self.autoresize:
96
+ if self.rect.size != self.inflate_rect_by_padding(self._text_rect).size :
97
+ self.set_size(
98
+ self._text_rect.w + self.padding[0]+self.padding[2],
99
+ self._text_rect.h + self.padding[1]+self.padding[3]
100
+ )
101
+ return
102
+ self._text_rect.center = self.get_content_rect_rel().center
103
+ self.surface.blit(self._text_surface,self._text_rect)
104
+
105
+ def build(self)->None:
337
106
  super().build()
338
- return size_changed
339
-
340
-
341
- def paint(self) -> None:
342
- super().paint()
343
- if self.font_object:
344
- self._paint_text()
107
+ if not self._font_object:return
108
+ self._build_text()
109
+ self._build_layout()
110
+