batframework 1.0.9a10__py3-none-any.whl → 1.0.9a12__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 +2 -0
- batFramework/action.py +280 -279
- batFramework/actionContainer.py +105 -82
- batFramework/animatedSprite.py +80 -58
- batFramework/animation.py +91 -77
- batFramework/audioManager.py +156 -131
- batFramework/baseScene.py +249 -240
- batFramework/camera.py +245 -317
- batFramework/constants.py +57 -51
- batFramework/cutscene.py +239 -253
- batFramework/cutsceneManager.py +34 -34
- batFramework/drawable.py +107 -77
- batFramework/dynamicEntity.py +30 -30
- batFramework/easingController.py +58 -58
- batFramework/entity.py +130 -130
- batFramework/enums.py +171 -135
- batFramework/fontManager.py +65 -65
- batFramework/gui/__init__.py +28 -25
- batFramework/gui/animatedLabel.py +90 -89
- batFramework/gui/button.py +17 -17
- batFramework/gui/clickableWidget.py +244 -245
- batFramework/gui/collapseContainer.py +98 -0
- batFramework/gui/constraints/__init__.py +1 -1
- batFramework/gui/constraints/constraints.py +1066 -980
- batFramework/gui/container.py +220 -201
- batFramework/gui/debugger.py +140 -130
- batFramework/gui/draggableWidget.py +63 -44
- batFramework/gui/image.py +61 -58
- batFramework/gui/indicator.py +116 -113
- batFramework/gui/interactiveWidget.py +243 -239
- batFramework/gui/label.py +147 -344
- batFramework/gui/layout.py +442 -426
- batFramework/gui/meter.py +155 -96
- batFramework/gui/radioButton.py +43 -35
- batFramework/gui/root.py +228 -228
- batFramework/gui/scrollingContainer.py +282 -0
- batFramework/gui/selector.py +232 -250
- batFramework/gui/shape.py +286 -276
- batFramework/gui/slider.py +353 -397
- batFramework/gui/style.py +10 -10
- batFramework/gui/styleManager.py +49 -54
- batFramework/gui/syncedVar.py +43 -49
- batFramework/gui/textInput.py +331 -306
- batFramework/gui/textWidget.py +308 -0
- batFramework/gui/toggle.py +140 -128
- batFramework/gui/tooltip.py +35 -30
- batFramework/gui/widget.py +546 -521
- batFramework/manager.py +131 -134
- batFramework/particle.py +118 -118
- batFramework/propertyEaser.py +79 -79
- batFramework/renderGroup.py +34 -34
- batFramework/resourceManager.py +130 -130
- batFramework/scene.py +31 -31
- batFramework/sceneLayer.py +134 -138
- batFramework/sceneManager.py +200 -197
- batFramework/scrollingSprite.py +115 -115
- batFramework/sprite.py +46 -51
- batFramework/stateMachine.py +49 -54
- batFramework/templates/__init__.py +2 -1
- batFramework/templates/character.py +15 -0
- batFramework/templates/controller.py +158 -97
- batFramework/templates/stateMachine.py +39 -0
- batFramework/tileset.py +46 -46
- batFramework/timeManager.py +213 -213
- batFramework/transition.py +162 -162
- batFramework/triggerZone.py +22 -22
- batFramework/utils.py +306 -306
- {batframework-1.0.9a10.dist-info → batframework-1.0.9a12.dist-info}/LICENSE +20 -20
- {batframework-1.0.9a10.dist-info → batframework-1.0.9a12.dist-info}/METADATA +24 -17
- batframework-1.0.9a12.dist-info/RECORD +72 -0
- batframework-1.0.9a10.dist-info/RECORD +0 -67
- {batframework-1.0.9a10.dist-info → batframework-1.0.9a12.dist-info}/WHEEL +0 -0
- {batframework-1.0.9a10.dist-info → batframework-1.0.9a12.dist-info}/top_level.txt +0 -0
batFramework/gui/textInput.py
CHANGED
@@ -1,306 +1,331 @@
|
|
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.
|
62
|
-
self.cursor_timer.
|
63
|
-
self.
|
64
|
-
self.
|
65
|
-
self.
|
66
|
-
self.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
return
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
self
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
if
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
self.
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
x
|
164
|
-
|
165
|
-
y =
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
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.placeholder_text = ""
|
62
|
+
self.cursor_timer = bf.Timer(0.2, self._cursor_toggle, loop=-1).start()
|
63
|
+
self.cursor_timer.pause()
|
64
|
+
self.show_cursor = False
|
65
|
+
self._cursor_blink_show : bool = True
|
66
|
+
self.on_modify: Callable[[str], str]| None = None
|
67
|
+
self.set_click_pass_through(False)
|
68
|
+
super().__init__("")
|
69
|
+
self.set_outline_color("black")
|
70
|
+
self.alignment = bf.alignment.TOPLEFT
|
71
|
+
|
72
|
+
def set_placeholder_text(self, text: str) -> Self:
|
73
|
+
self.placeholder_text = text
|
74
|
+
self.dirty_surface = True
|
75
|
+
return self
|
76
|
+
|
77
|
+
|
78
|
+
def set_modify_callback(self, callback: Callable[[str], str]) -> Self:
|
79
|
+
self.on_modify = callback
|
80
|
+
return self
|
81
|
+
|
82
|
+
def __str__(self) -> str:
|
83
|
+
return f"TextInput"
|
84
|
+
|
85
|
+
def _cursor_toggle(self, value: bool | None = None):
|
86
|
+
if value is None:
|
87
|
+
value = not self._cursor_blink_show
|
88
|
+
self._cursor_blink_show = value
|
89
|
+
self.dirty_surface = True
|
90
|
+
|
91
|
+
def on_click_down(self, button,event):
|
92
|
+
if button == 1:
|
93
|
+
self.get_focus()
|
94
|
+
event.consumed = True
|
95
|
+
super().on_click_down(button,event)
|
96
|
+
|
97
|
+
def do_on_enter(self):
|
98
|
+
pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_IBEAM)
|
99
|
+
|
100
|
+
def do_on_exit(self):
|
101
|
+
pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
|
102
|
+
|
103
|
+
def do_on_get_focus(self):
|
104
|
+
self.cursor_timer.resume()
|
105
|
+
# self._cursor_toggle(True)
|
106
|
+
self.show_cursor = True
|
107
|
+
self.old_key_repeat = pygame.key.get_repeat()
|
108
|
+
pygame.key.set_repeat(200, 50)
|
109
|
+
|
110
|
+
def do_on_lose_focus(self):
|
111
|
+
self.cursor_timer.pause()
|
112
|
+
self.show_cursor = False
|
113
|
+
pygame.key.set_repeat(*self.old_key_repeat)
|
114
|
+
|
115
|
+
def get_line(self, line: int) -> str | None:
|
116
|
+
if line < 0:
|
117
|
+
return None
|
118
|
+
lines = self.text_widget.text.split('\n')
|
119
|
+
if line >= len(lines):
|
120
|
+
return None
|
121
|
+
return lines[line]
|
122
|
+
|
123
|
+
def get_debug_outlines(self):
|
124
|
+
yield from super().get_debug_outlines()
|
125
|
+
if self.visible:
|
126
|
+
# offset = self._get_outline_offset() if self.show_text_outline else (0,0)
|
127
|
+
# yield (self.text_widget.rect.move(self.rect.x - offset[0] - self.scroll.x,self.rect.y - offset[1] - self.scroll.y), "purple")
|
128
|
+
yield (self.get_cursor_rect().move(*self.text_widget.rect.topleft),"green")
|
129
|
+
|
130
|
+
|
131
|
+
def get_min_required_size(self) -> tuple[float, float]:
|
132
|
+
size = self.text_widget.get_min_required_size()
|
133
|
+
return self.expand_rect_with_padding(
|
134
|
+
(0, 0,size[0]+self.get_cursor_rect().w,size[1])
|
135
|
+
).size
|
136
|
+
|
137
|
+
|
138
|
+
def set_cursor_position(self, position: tuple[int, int]) -> Self:
|
139
|
+
x, y = position
|
140
|
+
|
141
|
+
lines = self.text_widget.text.split('\n')
|
142
|
+
y = max(0, min(y, len(lines) - 1))
|
143
|
+
line_length = len(lines[y])
|
144
|
+
x = max(0, min(x, line_length))
|
145
|
+
self.cursor_position = (x,y)
|
146
|
+
return self
|
147
|
+
|
148
|
+
def get_cursor_rect(self) -> pygame.FRect:
|
149
|
+
if not self.text_widget.font_object:
|
150
|
+
return pygame.FRect(0, 0, 0, 0)
|
151
|
+
font = self.text_widget.font_object
|
152
|
+
|
153
|
+
lines = self.text_widget.text.split('\n')
|
154
|
+
line_x, line_y = self.cursor_position
|
155
|
+
line = lines[line_y]
|
156
|
+
|
157
|
+
# # Clamp line_y and line_x to valid ranges
|
158
|
+
# line_y = max(0, min(line_y, len(lines) - 1))
|
159
|
+
# line_x = max(0, min(line_x, len(line)))
|
160
|
+
|
161
|
+
line_height = font.get_linesize()
|
162
|
+
|
163
|
+
# Calculate the pixel x position of the cursor in the current line
|
164
|
+
x = font.size(line[:line_x])[0]
|
165
|
+
y = line_height * line_y
|
166
|
+
|
167
|
+
if self.text_widget.show_text_outline:
|
168
|
+
offset = self.text_widget._get_outline_offset()
|
169
|
+
x+=offset[0]
|
170
|
+
y+=offset[1]
|
171
|
+
|
172
|
+
res = pygame.FRect(x,y,1,line_height)
|
173
|
+
return res
|
174
|
+
|
175
|
+
def ensure_cursor_visible(self):
|
176
|
+
pass
|
177
|
+
|
178
|
+
def cursor_to_absolute(self, position: tuple[int, int]) -> int:
|
179
|
+
x, y = position
|
180
|
+
|
181
|
+
y = max(0, min(y, len(self.text_widget.text.split('\n')) - 1))
|
182
|
+
lines = self.text_widget.text.split('\n')
|
183
|
+
x = max(0, min(x, len(lines[y])))
|
184
|
+
|
185
|
+
absolute_position = sum(len(line) + 1 for line in lines[:y]) + x
|
186
|
+
return absolute_position
|
187
|
+
|
188
|
+
def absolute_to_cursor(self, absolute: int) -> tuple[int, int]:
|
189
|
+
text = self.text_widget.text
|
190
|
+
lines = text.split('\n')
|
191
|
+
current_pos = 0
|
192
|
+
|
193
|
+
for line_no, line in enumerate(lines):
|
194
|
+
if absolute <= current_pos + len(line):
|
195
|
+
return (absolute - current_pos, line_no)
|
196
|
+
current_pos += len(line) + 1
|
197
|
+
|
198
|
+
return (len(lines[-1]), len(lines) - 1)
|
199
|
+
|
200
|
+
def handle_event(self, event):
|
201
|
+
# TODO fix tab_focus not working when textInput in focus
|
202
|
+
super().handle_event(event)
|
203
|
+
if event.consumed or(not self.is_focused or event.type not in [pygame.TEXTINPUT, pygame.KEYDOWN]):
|
204
|
+
return
|
205
|
+
|
206
|
+
text = self.get_text()
|
207
|
+
current_pos = self.cursor_to_absolute(self.cursor_position)
|
208
|
+
pressed = pygame.key.get_pressed()
|
209
|
+
|
210
|
+
if event.type == pygame.TEXTINPUT:
|
211
|
+
# Insert text at the current cursor position
|
212
|
+
self.set_text(f"{text[:current_pos]}{event.text}{text[current_pos:]}")
|
213
|
+
self.set_cursor_position(self.absolute_to_cursor(current_pos + len(event.text)))
|
214
|
+
elif event.type == pygame.KEYDOWN:
|
215
|
+
match event.key:
|
216
|
+
case pygame.K_ESCAPE:
|
217
|
+
self.lose_focus()
|
218
|
+
|
219
|
+
case pygame.K_BACKSPACE if current_pos > 0:
|
220
|
+
# Remove the character before the cursor
|
221
|
+
delta = current_pos-1
|
222
|
+
if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
|
223
|
+
delta = find_prev_word(self.text_widget.text,current_pos-1)
|
224
|
+
if delta <0: delta = 0
|
225
|
+
|
226
|
+
self.set_text(f"{text[:delta]}{text[current_pos:]}")
|
227
|
+
self.set_cursor_position(self.absolute_to_cursor(delta))
|
228
|
+
self._cursor_toggle(True)
|
229
|
+
|
230
|
+
case pygame.K_DELETE if current_pos < len(text):
|
231
|
+
# Remove the character at the cursor
|
232
|
+
self.set_text(f"{text[:current_pos]}{text[current_pos + 1:]}")
|
233
|
+
self._cursor_toggle(True)
|
234
|
+
|
235
|
+
case pygame.K_RIGHT:
|
236
|
+
if current_pos < len(text):
|
237
|
+
self.handle_cursor_movement(pressed, current_pos, direction="right")
|
238
|
+
self._cursor_toggle(True)
|
239
|
+
|
240
|
+
case pygame.K_LEFT:
|
241
|
+
if current_pos > 0:
|
242
|
+
self.handle_cursor_movement(pressed, current_pos, direction="left")
|
243
|
+
self._cursor_toggle(True)
|
244
|
+
|
245
|
+
case pygame.K_UP:
|
246
|
+
# Move cursor up one line
|
247
|
+
self.set_cursor_position((self.cursor_position[0], self.cursor_position[1] - 1))
|
248
|
+
self._cursor_toggle(True)
|
249
|
+
|
250
|
+
case pygame.K_DOWN:
|
251
|
+
# Move cursor down one line
|
252
|
+
self.set_cursor_position((self.cursor_position[0], self.cursor_position[1] + 1))
|
253
|
+
self._cursor_toggle(True)
|
254
|
+
|
255
|
+
case pygame.K_RETURN:
|
256
|
+
# Insert a newline at the current cursor position
|
257
|
+
self.set_text(f"{text[:current_pos]}\n{text[current_pos:]}")
|
258
|
+
self.set_cursor_position(self.absolute_to_cursor(current_pos + 1))
|
259
|
+
self._cursor_toggle(True)
|
260
|
+
case _ :
|
261
|
+
if event.unicode:
|
262
|
+
event.consumed = True
|
263
|
+
return
|
264
|
+
|
265
|
+
event.consumed = True
|
266
|
+
|
267
|
+
def handle_cursor_movement(self, pressed, current_pos, direction):
|
268
|
+
if direction == "right":
|
269
|
+
if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
|
270
|
+
next_word_pos = find_next_word(self.text_widget.text, current_pos)
|
271
|
+
self.set_cursor_position(self.absolute_to_cursor(next_word_pos if next_word_pos != -1 else current_pos + 1))
|
272
|
+
else:
|
273
|
+
self.set_cursor_position(self.absolute_to_cursor(current_pos + 1))
|
274
|
+
elif direction == "left":
|
275
|
+
if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
|
276
|
+
prev_word_pos = find_prev_word(self.text_widget.text, current_pos - 1)
|
277
|
+
self.set_cursor_position(self.absolute_to_cursor(prev_word_pos if prev_word_pos != -1 else current_pos - 1))
|
278
|
+
else:
|
279
|
+
self.set_cursor_position(self.absolute_to_cursor(current_pos - 1))
|
280
|
+
|
281
|
+
|
282
|
+
def set_text(self, text: str) -> Self:
|
283
|
+
if self.on_modify:
|
284
|
+
text = self.on_modify(text)
|
285
|
+
if text == "" and self.placeholder_text:
|
286
|
+
text = self.placeholder_text
|
287
|
+
if text != "" and text == self.placeholder_text:
|
288
|
+
self.text_widget.set_text("")
|
289
|
+
self.text_widget.set_text(text)
|
290
|
+
|
291
|
+
return self
|
292
|
+
def _draw_cursor(self,camera:bf.Camera) -> None:
|
293
|
+
if not self.show_cursor or not self._cursor_blink_show:
|
294
|
+
return
|
295
|
+
|
296
|
+
cursor_rect = self.get_cursor_rect()
|
297
|
+
cursor_rect.move_ip(*self.text_widget.rect.topleft)
|
298
|
+
cursor_rect = camera.world_to_screen(cursor_rect)
|
299
|
+
if self.text_widget.show_text_outline:
|
300
|
+
pygame.draw.rect(camera.surface, self.text_widget.text_outline_color, cursor_rect.inflate(2,2))
|
301
|
+
pygame.draw.rect(camera.surface, self.text_widget.text_color, cursor_rect)
|
302
|
+
|
303
|
+
def draw(self,camera:bf.Camera) -> None:
|
304
|
+
super().draw(camera)
|
305
|
+
self._draw_cursor(camera)
|
306
|
+
|
307
|
+
def align_text(
|
308
|
+
self, text_rect: pygame.FRect, area: pygame.FRect, alignment: bf.alignment
|
309
|
+
):
|
310
|
+
cursor_rect = self.get_cursor_rect()
|
311
|
+
|
312
|
+
if alignment == bf.alignment.LEFT:
|
313
|
+
alignment = bf.alignment.MIDLEFT
|
314
|
+
elif alignment == bf.alignment.MIDRIGHT:
|
315
|
+
alignment = bf.alignment.MIDRIGHT
|
316
|
+
pos = area.__getattribute__(alignment.value)
|
317
|
+
text_rect.__setattr__(alignment.value, pos)
|
318
|
+
scroll = self.text_widget.scroll
|
319
|
+
|
320
|
+
if cursor_rect.right > area.right+scroll.x:
|
321
|
+
scroll.x=cursor_rect.right - area.right
|
322
|
+
elif cursor_rect.x < scroll.x+area.left:
|
323
|
+
scroll.x= cursor_rect.left - area.left
|
324
|
+
# self.scroll.x = 0
|
325
|
+
scroll.x = max(scroll.x,0)
|
326
|
+
|
327
|
+
if cursor_rect.bottom > scroll.y + area.bottom:
|
328
|
+
scroll.y = cursor_rect.bottom - area.bottom
|
329
|
+
elif cursor_rect.y < scroll.y + area.top:
|
330
|
+
scroll.y = cursor_rect.top - area.top
|
331
|
+
scroll.y = max(scroll.y, 0)
|