batframework 1.0.8a9__py3-none-any.whl → 1.0.8a11__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 +68 -51
- batFramework/action.py +126 -99
- batFramework/actionContainer.py +53 -9
- batFramework/animatedSprite.py +141 -82
- batFramework/audioManager.py +69 -26
- batFramework/camera.py +259 -69
- batFramework/character.py +27 -0
- batFramework/constants.py +16 -54
- batFramework/cutscene.py +39 -29
- batFramework/cutsceneBlocks.py +36 -43
- batFramework/dynamicEntity.py +18 -9
- batFramework/easingController.py +58 -0
- batFramework/entity.py +48 -97
- batFramework/enums.py +113 -0
- batFramework/fontManager.py +65 -0
- batFramework/gui/__init__.py +10 -2
- batFramework/gui/button.py +9 -78
- batFramework/gui/clickableWidget.py +220 -0
- batFramework/gui/constraints/__init__.py +1 -0
- batFramework/gui/constraints/constraints.py +815 -0
- batFramework/gui/container.py +174 -32
- batFramework/gui/debugger.py +131 -43
- batFramework/gui/dialogueBox.py +99 -0
- batFramework/gui/draggableWidget.py +40 -0
- batFramework/gui/image.py +56 -20
- batFramework/gui/indicator.py +38 -21
- batFramework/gui/interactiveWidget.py +192 -13
- batFramework/gui/label.py +309 -74
- batFramework/gui/layout.py +231 -63
- batFramework/gui/meter.py +74 -0
- batFramework/gui/radioButton.py +84 -0
- batFramework/gui/root.py +134 -38
- batFramework/gui/shape.py +237 -57
- batFramework/gui/slider.py +240 -0
- batFramework/gui/style.py +10 -0
- batFramework/gui/styleManager.py +48 -0
- batFramework/gui/textInput.py +247 -0
- batFramework/gui/toggle.py +101 -51
- batFramework/gui/widget.py +358 -250
- batFramework/manager.py +90 -19
- batFramework/object.py +123 -0
- batFramework/particle.py +115 -0
- batFramework/renderGroup.py +67 -0
- batFramework/resourceManager.py +100 -0
- batFramework/scene.py +281 -123
- batFramework/sceneManager.py +178 -116
- batFramework/scrollingSprite.py +114 -0
- batFramework/sprite.py +51 -0
- batFramework/stateMachine.py +11 -8
- batFramework/templates/__init__.py +2 -0
- batFramework/templates/character.py +44 -0
- batFramework/templates/states.py +166 -0
- batFramework/tileset.py +46 -0
- batFramework/time.py +145 -58
- batFramework/transition.py +195 -124
- batFramework/triggerZone.py +1 -1
- batFramework/utils.py +112 -147
- batframework-1.0.8a11.dist-info/LICENCE +21 -0
- batframework-1.0.8a11.dist-info/METADATA +43 -0
- batframework-1.0.8a11.dist-info/RECORD +62 -0
- {batframework-1.0.8a9.dist-info → batframework-1.0.8a11.dist-info}/WHEEL +1 -1
- 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/transitionManager.py +0 -0
- batframework-1.0.8a9.dist-info/METADATA +0 -53
- batframework-1.0.8a9.dist-info/RECORD +0 -42
- {batframework-1.0.8a9.dist-info → batframework-1.0.8a11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,247 @@
|
|
1
|
+
import batFramework as bf
|
2
|
+
from typing import Self, Callable
|
3
|
+
from .label import Label
|
4
|
+
from .interactiveWidget import InteractiveWidget
|
5
|
+
import pygame
|
6
|
+
|
7
|
+
def find_next_word(s: str, start_index: int) -> int:
|
8
|
+
length = len(s)
|
9
|
+
# Ensure the starting index is within the bounds of the string
|
10
|
+
if start_index < 0 or start_index >= length:
|
11
|
+
raise ValueError("Starting index is out of bounds")
|
12
|
+
index = start_index
|
13
|
+
# If the start_index is at a space, skip leading spaces
|
14
|
+
if s[index] in [' ','\n']:
|
15
|
+
while index < length and s[index] in [' ','\n']:
|
16
|
+
index += 1
|
17
|
+
# If we've reached the end of the string
|
18
|
+
if index >= length:
|
19
|
+
return -1
|
20
|
+
else:
|
21
|
+
# If the start_index is within a word, move to the end of that word
|
22
|
+
while index < length and s[index] not in [' ','\n']:
|
23
|
+
index += 1
|
24
|
+
if index == length:
|
25
|
+
return index
|
26
|
+
# Return the index of the start of the next word or -1 if no more words are found
|
27
|
+
return index if index < length else -1
|
28
|
+
|
29
|
+
def find_prev_word(s: str, start_index: int) -> int:
|
30
|
+
if start_index <= 0 : return 0
|
31
|
+
length = len(s)
|
32
|
+
|
33
|
+
# Ensure the starting index is within the bounds of the string
|
34
|
+
if start_index < 0 or start_index >= length:
|
35
|
+
raise ValueError("Starting index is out of bounds")
|
36
|
+
|
37
|
+
index = start_index
|
38
|
+
|
39
|
+
# If the start_index is at a space, skip trailing spaces
|
40
|
+
if s[index] in [' ', '\n']:
|
41
|
+
while index > 0 and s[index-1] in [' ', '\n']:
|
42
|
+
index -= 1
|
43
|
+
# If we've reached the beginning of the string
|
44
|
+
if index <= 0:
|
45
|
+
return 0 if s[0] not in [' ', '\n'] else -1
|
46
|
+
else:
|
47
|
+
# If the start_index is within a word, move to the start of that word
|
48
|
+
while index > 0 and s[index-1] not in [' ', '\n']:
|
49
|
+
index -= 1
|
50
|
+
if index == 0 and s[index] not in [' ', '\n']:
|
51
|
+
return 0
|
52
|
+
|
53
|
+
# Return the index of the start of the previous word or -1 if no more words are found
|
54
|
+
return index if index > 0 or (index == 0 and s[0] not in [' ', '\n']) else -1
|
55
|
+
|
56
|
+
|
57
|
+
class TextInput(Label, InteractiveWidget):
|
58
|
+
def __init__(self) -> None:
|
59
|
+
self.cursor_position = (0, 0)
|
60
|
+
self.old_key_repeat = (0, 0)
|
61
|
+
self.cursor_timer = bf.Timer(0.3, self._cursor_toggle, loop=True).start()
|
62
|
+
self.cursor_timer.pause()
|
63
|
+
self.show_cursor = False
|
64
|
+
self.on_modify: Callable[[str], str] = None
|
65
|
+
self.set_focusable(True)
|
66
|
+
self.set_outline_color("black")
|
67
|
+
super().__init__("")
|
68
|
+
self.alignment = bf.alignment.TOPLEFT
|
69
|
+
|
70
|
+
def set_modify_callback(self, callback: Callable[[str], str]) -> Self:
|
71
|
+
self.on_modify = callback
|
72
|
+
return self
|
73
|
+
|
74
|
+
def __str__(self) -> str:
|
75
|
+
return f"TextInput({repr(self.text)})"
|
76
|
+
|
77
|
+
def _cursor_toggle(self, value: bool | None = None):
|
78
|
+
if value is None:
|
79
|
+
value = not self.show_cursor
|
80
|
+
self.show_cursor = value
|
81
|
+
self.dirty_surface = True
|
82
|
+
|
83
|
+
def do_on_click_down(self, button):
|
84
|
+
if button != 1:
|
85
|
+
return
|
86
|
+
self.get_focus()
|
87
|
+
|
88
|
+
def do_on_enter(self):
|
89
|
+
pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_IBEAM)
|
90
|
+
|
91
|
+
def do_on_exit(self):
|
92
|
+
pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
|
93
|
+
|
94
|
+
def do_on_get_focus(self):
|
95
|
+
self.cursor_timer.resume()
|
96
|
+
self._cursor_toggle(True)
|
97
|
+
self.old_key_repeat = pygame.key.get_repeat()
|
98
|
+
pygame.key.set_repeat(200, 50)
|
99
|
+
|
100
|
+
def do_on_lose_focus(self):
|
101
|
+
self.cursor_timer.pause()
|
102
|
+
self._cursor_toggle(False)
|
103
|
+
pygame.key.set_repeat(*self.old_key_repeat)
|
104
|
+
|
105
|
+
def get_line(self, line: int) -> str | None:
|
106
|
+
if line < 0:
|
107
|
+
return None
|
108
|
+
lines = self.text.split('\n')
|
109
|
+
if line >= len(lines):
|
110
|
+
return None
|
111
|
+
return lines[line]
|
112
|
+
|
113
|
+
def set_cursor_position(self, position: tuple[int, int]) -> Self:
|
114
|
+
x, y = position
|
115
|
+
|
116
|
+
lines = self.text.split('\n')
|
117
|
+
y = max(0, min(y, len(lines) - 1))
|
118
|
+
line_length = len(lines[y])
|
119
|
+
x = max(0, min(x, line_length))
|
120
|
+
|
121
|
+
self.cursor_position = (x, y)
|
122
|
+
self.show_cursor = True
|
123
|
+
self.dirty_shape = True
|
124
|
+
return self
|
125
|
+
|
126
|
+
def cursor_to_absolute(self, position: tuple[int, int]) -> int:
|
127
|
+
x, y = position
|
128
|
+
|
129
|
+
y = max(0, min(y, len(self.text.split('\n')) - 1))
|
130
|
+
lines = self.text.split('\n')
|
131
|
+
x = max(0, min(x, len(lines[y])))
|
132
|
+
|
133
|
+
absolute_position = sum(len(line) + 1 for line in lines[:y]) + x
|
134
|
+
return absolute_position
|
135
|
+
|
136
|
+
def absolute_to_cursor(self, absolute: int) -> tuple[int, int]:
|
137
|
+
text = self.text
|
138
|
+
lines = text.split('\n')
|
139
|
+
current_pos = 0
|
140
|
+
|
141
|
+
for line_no, line in enumerate(lines):
|
142
|
+
if absolute <= current_pos + len(line):
|
143
|
+
return (absolute - current_pos, line_no)
|
144
|
+
current_pos += len(line) + 1
|
145
|
+
|
146
|
+
return (len(lines[-1]), len(lines) - 1)
|
147
|
+
|
148
|
+
def do_handle_event(self, event):
|
149
|
+
if not self.is_focused:
|
150
|
+
return
|
151
|
+
|
152
|
+
if event.type not in [pygame.TEXTINPUT, pygame.KEYDOWN]:
|
153
|
+
return
|
154
|
+
|
155
|
+
text = self.get_text()
|
156
|
+
current = self.cursor_to_absolute(self.cursor_position)
|
157
|
+
if event.type == pygame.TEXTINPUT:
|
158
|
+
self.set_text(text[:current] + event.text + text[current:])
|
159
|
+
self.set_cursor_position(self.absolute_to_cursor(current + len(event.text)))
|
160
|
+
elif event.type == pygame.KEYDOWN:
|
161
|
+
pressed = pygame.key.get_pressed()
|
162
|
+
if event.key == pygame.K_ESCAPE:
|
163
|
+
self.lose_focus()
|
164
|
+
elif event.key == pygame.K_BACKSPACE:
|
165
|
+
if current > 0:
|
166
|
+
self.set_text(text[:current - 1] + text[current:])
|
167
|
+
self.set_cursor_position(self.absolute_to_cursor(current - 1))
|
168
|
+
elif event.key == pygame.K_DELETE:
|
169
|
+
if current < len(text):
|
170
|
+
self.set_text(text[:current] + text[current + 1:])
|
171
|
+
elif event.key == pygame.K_RIGHT:
|
172
|
+
if self.cursor_to_absolute(self.cursor_position)>=len(self.text):
|
173
|
+
return
|
174
|
+
if self.cursor_position[0] == len(self.get_line(self.cursor_position[1])):
|
175
|
+
self.set_cursor_position((0, self.cursor_position[1] + 1))
|
176
|
+
else:
|
177
|
+
if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
|
178
|
+
index = find_next_word(self.text,current)
|
179
|
+
if index ==-1 : index = current+1
|
180
|
+
self.set_cursor_position(self.absolute_to_cursor(index))
|
181
|
+
else:
|
182
|
+
self.set_cursor_position(self.absolute_to_cursor(current + 1))
|
183
|
+
elif event.key == pygame.K_LEFT:
|
184
|
+
|
185
|
+
|
186
|
+
if self.cursor_position[0] == 0 and self.cursor_position[1] > 0:
|
187
|
+
self.set_cursor_position((len(self.get_line(self.cursor_position[1] - 1)), self.cursor_position[1] - 1))
|
188
|
+
else:
|
189
|
+
if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
|
190
|
+
index = find_prev_word(self.text,current-1)
|
191
|
+
if index ==-1 : index = current-1
|
192
|
+
self.set_cursor_position(self.absolute_to_cursor(index))
|
193
|
+
else:
|
194
|
+
self.set_cursor_position(self.absolute_to_cursor(current - 1))
|
195
|
+
elif event.key == pygame.K_UP:
|
196
|
+
x, y = self.cursor_position
|
197
|
+
self.set_cursor_position((x, y - 1))
|
198
|
+
elif event.key == pygame.K_DOWN:
|
199
|
+
x, y = self.cursor_position
|
200
|
+
self.set_cursor_position((x, y + 1))
|
201
|
+
elif event.key == pygame.K_RETURN:
|
202
|
+
self.set_text(text[:current] + '\n' + text[current:])
|
203
|
+
self.set_cursor_position(self.absolute_to_cursor(current + 1))
|
204
|
+
else:
|
205
|
+
return
|
206
|
+
else:
|
207
|
+
return
|
208
|
+
|
209
|
+
event.consumed = True
|
210
|
+
|
211
|
+
def set_text(self, text: str) -> Self:
|
212
|
+
if self.on_modify:
|
213
|
+
text = self.on_modify(text)
|
214
|
+
return super().set_text(text)
|
215
|
+
|
216
|
+
def _paint_cursor(self) -> None:
|
217
|
+
if not self.font_object or not self.show_cursor:
|
218
|
+
return
|
219
|
+
|
220
|
+
lines = self.text.split('\n')
|
221
|
+
line_x, line_y = self.cursor_position
|
222
|
+
|
223
|
+
cursor_y = self.padding[1]
|
224
|
+
cursor_y += line_y * self.font_object.get_linesize()
|
225
|
+
cursor_x = self.padding[0]
|
226
|
+
cursor_x += self.font_object.size(lines[line_y][:line_x])[0] if line_x > 0 else 0
|
227
|
+
|
228
|
+
cursor_rect = pygame.Rect(cursor_x, cursor_y, 2, self.font_object.get_height())
|
229
|
+
pygame.draw.rect(self.surface, self.text_color, cursor_rect)
|
230
|
+
|
231
|
+
def paint(self) -> None:
|
232
|
+
super().paint()
|
233
|
+
self._paint_cursor()
|
234
|
+
|
235
|
+
# def set_alignment(self, alignment: bf.alignment) -> Self:
|
236
|
+
# return self
|
237
|
+
|
238
|
+
# def align_text(
|
239
|
+
# self, text_rect: pygame.FRect, area: pygame.FRect, alignment: bf.alignment
|
240
|
+
# ):
|
241
|
+
# if alignment == bf.alignment.LEFT:
|
242
|
+
# alignment = bf.alignment.MIDLEFT
|
243
|
+
# elif alignment == bf.alignment.MIDRIGHT:
|
244
|
+
# alignment = bf.alignment.MIDRIGHT
|
245
|
+
# pos = area.__getattribute__(alignment.value)
|
246
|
+
# text_rect.__setattr__(alignment.value, pos)
|
247
|
+
# return
|
batFramework/gui/toggle.py
CHANGED
@@ -1,62 +1,112 @@
|
|
1
1
|
from .button import Button
|
2
|
-
from .indicator import Indicator,ToggleIndicator
|
3
|
-
import pygame
|
2
|
+
from .indicator import Indicator, ToggleIndicator
|
4
3
|
import batFramework as bf
|
5
4
|
from typing import Self
|
5
|
+
import pygame
|
6
|
+
|
6
7
|
|
7
8
|
class Toggle(Button):
|
8
|
-
def __init__(self,text:str,default_value
|
9
|
-
self.value
|
10
|
-
self.
|
11
|
-
self.
|
12
|
-
self.
|
13
|
-
super().__init__(text,
|
14
|
-
self.
|
15
|
-
self.
|
16
|
-
# self.set_gap()
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
def __init__(self, text: str = "", callback=None, default_value: bool = False) -> None:
|
10
|
+
self.value: bool = default_value
|
11
|
+
self.indicator: ToggleIndicator = ToggleIndicator(default_value)
|
12
|
+
self.gap: float | int = 0
|
13
|
+
self.spacing: bf.spacing = bf.spacing.MANUAL
|
14
|
+
super().__init__(text, callback)
|
15
|
+
self.add(self.indicator)
|
16
|
+
self.set_clip_children(False)
|
17
|
+
# self.set_gap(int(max(4, self.get_padded_width() / 3)))
|
18
|
+
|
19
|
+
def set_visible(self, value: bool) -> Self:
|
20
|
+
self.indicator.set_visible(value)
|
21
|
+
return super().set_visible(value)
|
22
|
+
|
23
|
+
def set_value(self, value: bool, do_callback=False) -> Self:
|
24
|
+
self.value = value
|
25
|
+
self.indicator.set_value(value)
|
26
|
+
self.dirty_surface = True
|
27
|
+
if do_callback and self.callback:
|
28
|
+
self.callback(self.value)
|
29
|
+
return self
|
30
|
+
|
31
|
+
def set_spacing(self, spacing: bf.spacing) -> Self:
|
32
|
+
if spacing == self.spacing:
|
33
|
+
return self
|
34
|
+
self.spacing = spacing
|
35
|
+
self.dirty_shape = True
|
36
|
+
return self
|
37
|
+
|
38
|
+
def click(self) -> None:
|
39
|
+
self.set_value(not self.value, True)
|
40
|
+
|
41
|
+
def set_gap(self, value: int | float) -> Self:
|
42
|
+
value = max(0, value)
|
43
|
+
if value == self.gap:
|
44
|
+
return self
|
21
45
|
self.gap = value
|
22
|
-
self.
|
23
|
-
if self.parent : self.parent.children_modified()
|
46
|
+
self.dirty_shape = True
|
24
47
|
return self
|
25
|
-
|
48
|
+
|
49
|
+
def __str__(self) -> str:
|
26
50
|
return f"Toggle({self.value})"
|
27
|
-
|
28
|
-
def toggle(self)->None:
|
29
|
-
self.value = not self.value
|
30
|
-
self.build()
|
31
|
-
if self.on_toggle : self.on_toggle(self.value)
|
32
|
-
|
33
|
-
def set_toggle_callback(self,callback)->Self:
|
34
|
-
self.on_toggle = callback
|
35
|
-
return self
|
36
51
|
|
37
|
-
def
|
38
|
-
self.
|
39
|
-
|
52
|
+
def toggle(self) -> None:
|
53
|
+
self.set_value(not self.value, do_callback=True)
|
54
|
+
|
55
|
+
def get_min_required_size(self) -> tuple[float, float]:
|
56
|
+
if not self.text_rect:
|
57
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
40
58
|
size = (
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
59
|
+
max(
|
60
|
+
self.indicator.get_min_required_size()[0],
|
61
|
+
self.text_rect.w + self.font_object.point_size + (self.gap if self.text else 0),
|
62
|
+
),
|
63
|
+
self.text_rect.h,
|
64
|
+
)
|
65
|
+
return self.inflate_rect_by_padding((0, 0, *size)).size
|
66
|
+
|
67
|
+
def _build_layout(self) -> None:
|
68
|
+
gap = self.gap if self.text else 0
|
69
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
70
|
+
|
71
|
+
#right part size
|
72
|
+
right_part_height = min(self.text_rect.h, self.font_object.point_size)
|
73
|
+
self.indicator.set_size_if_autoresize((right_part_height,right_part_height))
|
74
|
+
|
75
|
+
#join left and right
|
76
|
+
joined_rect = pygame.FRect(
|
77
|
+
0, 0, self.text_rect.w + gap + self.indicator.rect.w, self.text_rect.h
|
45
78
|
)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
self.
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
self.
|
62
|
-
|
79
|
+
|
80
|
+
|
81
|
+
if self.autoresize_h or self.autoresize_w:
|
82
|
+
target_rect = self.inflate_rect_by_padding(joined_rect)
|
83
|
+
if not self.autoresize_w:
|
84
|
+
target_rect.w = self.rect.w
|
85
|
+
if not self.autoresize_h:
|
86
|
+
target_rect.h = self.rect.h
|
87
|
+
if self.rect.size != target_rect.size:
|
88
|
+
self.set_size(target_rect.size)
|
89
|
+
self.build()
|
90
|
+
return
|
91
|
+
|
92
|
+
# ------------------------------------ size is ok
|
93
|
+
|
94
|
+
offset = self._get_outline_offset() if self.show_text_outline else (0,0)
|
95
|
+
padded_rect = self.get_padded_rect()
|
96
|
+
padded_relative = padded_rect.move(-self.rect.x, -self.rect.y)
|
97
|
+
|
98
|
+
self.align_text(joined_rect, padded_relative.move( offset), self.alignment)
|
99
|
+
self.text_rect.midleft = joined_rect.midleft
|
100
|
+
|
101
|
+
if self.text:
|
102
|
+
match self.spacing:
|
103
|
+
case bf.spacing.MAX:
|
104
|
+
gap = padded_relative.right - self.text_rect.right - self.indicator.rect.w
|
105
|
+
case bf.spacing.MIN:
|
106
|
+
gap = 0
|
107
|
+
|
108
|
+
pos = self.text_rect.move(
|
109
|
+
self.rect.x + gap -offset[0],
|
110
|
+
self.rect.y + (self.text_rect.h / 2) - (right_part_height/ 2) -offset[1],
|
111
|
+
).topright
|
112
|
+
self.indicator.rect.topleft = pos
|