batframework 1.0.8a3__py3-none-any.whl → 1.0.8a6__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 +16 -2
- batFramework/animatedSprite.py +94 -85
- batFramework/audioManager.py +2 -2
- batFramework/character.py +27 -0
- batFramework/cutscene.py +5 -2
- batFramework/cutsceneBlocks.py +3 -5
- batFramework/dynamicEntity.py +11 -4
- batFramework/enums.py +2 -2
- batFramework/fontManager.py +2 -2
- batFramework/gui/clickableWidget.py +10 -9
- batFramework/gui/constraints/constraints.py +282 -57
- batFramework/gui/image.py +14 -14
- batFramework/gui/interactiveWidget.py +16 -1
- batFramework/gui/label.py +58 -37
- batFramework/gui/layout.py +23 -14
- batFramework/gui/meter.py +10 -7
- batFramework/gui/radioButton.py +1 -1
- batFramework/gui/root.py +7 -1
- batFramework/gui/shape.py +21 -37
- batFramework/gui/slider.py +52 -58
- batFramework/gui/textInput.py +161 -51
- batFramework/gui/toggle.py +27 -41
- batFramework/gui/widget.py +68 -35
- batFramework/manager.py +17 -5
- batFramework/object.py +17 -8
- batFramework/particle.py +22 -8
- batFramework/resourceManager.py +18 -2
- batFramework/scene.py +50 -20
- batFramework/sceneManager.py +52 -28
- batFramework/scrollingSprite.py +7 -8
- batFramework/stateMachine.py +9 -6
- batFramework/templates/__init__.py +2 -0
- batFramework/templates/character.py +44 -0
- batFramework/templates/states.py +166 -0
- batFramework/time.py +54 -28
- batFramework/transition.py +25 -12
- batFramework/triggerZone.py +1 -1
- batFramework/utils.py +92 -2
- {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/METADATA +3 -15
- batframework-1.0.8a6.dist-info/RECORD +62 -0
- {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/WHEEL +1 -1
- batframework-1.0.8a3.dist-info/RECORD +0 -58
- {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/LICENCE +0 -0
- {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/top_level.txt +0 -0
batFramework/gui/slider.py
CHANGED
@@ -11,11 +11,14 @@ class SliderHandle(Indicator, DraggableWidget):
|
|
11
11
|
def __init__(self):
|
12
12
|
super().__init__()
|
13
13
|
self.set_color(bf.color.CLOUD_SHADE)
|
14
|
-
|
14
|
+
self.old_key_repeat: tuple = (0, 0)
|
15
|
+
self.parent : bf.ClickableWidget = self.parent
|
15
16
|
def __str__(self) -> str:
|
16
17
|
return "SliderHandle"
|
17
18
|
|
18
19
|
def on_click_down(self, button: int) -> None:
|
20
|
+
if not self.parent.is_enabled():
|
21
|
+
return
|
19
22
|
super().on_click_down(button)
|
20
23
|
if button == 1:
|
21
24
|
self.parent.get_focus()
|
@@ -27,6 +30,8 @@ class SliderHandle(Indicator, DraggableWidget):
|
|
27
30
|
def do_on_drag(
|
28
31
|
self, drag_start: tuple[float, float], drag_end: tuple[float, float]
|
29
32
|
) -> None:
|
33
|
+
if not self.parent.is_enabled():
|
34
|
+
return
|
30
35
|
super().do_on_drag(drag_start, drag_end)
|
31
36
|
m: Meter = self.parent.meter
|
32
37
|
r = m.get_padded_rect()
|
@@ -42,21 +47,20 @@ class SliderHandle(Indicator, DraggableWidget):
|
|
42
47
|
|
43
48
|
|
44
49
|
class SliderMeter(Meter, InteractiveWidget):
|
45
|
-
|
46
50
|
def __str__(self) -> str:
|
47
51
|
return "SliderMeter"
|
48
52
|
|
49
53
|
def on_click_down(self, button: int) -> None:
|
54
|
+
if not self.parent.is_enabled():
|
55
|
+
return
|
50
56
|
if button == 1:
|
51
57
|
self.parent.get_focus()
|
52
58
|
r = self.get_root()
|
53
59
|
if r:
|
54
60
|
pos = r.drawing_camera.screen_to_world(pygame.mouse.get_pos())[0]
|
55
61
|
self.parent.set_value(self.parent.position_to_value(pos))
|
56
|
-
|
57
62
|
self.do_on_click_down(button)
|
58
63
|
|
59
|
-
|
60
64
|
class Slider(Button):
|
61
65
|
def __init__(self, text: str, default_value: float = 1.0) -> None:
|
62
66
|
super().__init__(text, None)
|
@@ -68,8 +72,6 @@ class Slider(Button):
|
|
68
72
|
self.add(self.meter, self.handle)
|
69
73
|
self.meter.set_debug_color(bf.color.RED)
|
70
74
|
self.set_value(default_value, True)
|
71
|
-
# print(self.handle.rect)
|
72
|
-
# self.handle.set_visible(False)
|
73
75
|
|
74
76
|
def set_visible(self, value: bool) -> Self:
|
75
77
|
self.handle.set_visible(value)
|
@@ -84,22 +86,14 @@ class Slider(Button):
|
|
84
86
|
self.gap = value
|
85
87
|
return self
|
86
88
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
"bgcolor": "black", # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
|
96
|
-
"wraplength": (
|
97
|
-
int(self.get_padded_width()) if self.auto_wraplength else 0
|
98
|
-
),
|
99
|
-
}
|
100
|
-
self.text_rect.size = self._render_font(params).get_size()
|
101
|
-
w, h = self.text_rect.size
|
102
|
-
return self.inflate_rect_by_padding((0, 0, w + gap + self.meter.rect.w, h)).size
|
89
|
+
def do_on_get_focus(self) -> None:
|
90
|
+
super().do_on_get_focus()
|
91
|
+
self.old_key_repeat = pygame.key.get_repeat()
|
92
|
+
pygame.key.set_repeat(200, 50)
|
93
|
+
|
94
|
+
def do_on_lose_focus(self) -> None:
|
95
|
+
super().do_on_lose_focus()
|
96
|
+
pygame.key.set_repeat(*self.old_key_repeat)
|
103
97
|
|
104
98
|
def set_spacing(self, spacing: bf.spacing) -> Self:
|
105
99
|
if spacing == self.spacing:
|
@@ -114,10 +108,12 @@ class Slider(Button):
|
|
114
108
|
|
115
109
|
def set_range(self, range_min: float, range_max: float) -> Self:
|
116
110
|
self.meter.set_range(range_min, range_max)
|
111
|
+
self.dirty_shape = True
|
117
112
|
return self
|
118
113
|
|
119
114
|
def set_step(self, step: float) -> Self:
|
120
115
|
self.meter.set_step(step)
|
116
|
+
self.dirty_shape = True
|
121
117
|
return self
|
122
118
|
|
123
119
|
def set_value(self, value, no_callback: bool = False) -> Self:
|
@@ -132,14 +128,16 @@ class Slider(Button):
|
|
132
128
|
return self.meter.get_value()
|
133
129
|
|
134
130
|
def do_on_key_down(self, key):
|
131
|
+
if not self.is_enabled():
|
132
|
+
return
|
135
133
|
if key == pygame.K_RIGHT:
|
136
134
|
self.set_value(self.meter.get_value() + self.meter.step)
|
137
|
-
bf.AudioManager().play_sound(self.click_down_sound)
|
138
135
|
elif key == pygame.K_LEFT:
|
139
136
|
self.set_value(self.meter.get_value() - self.meter.step)
|
140
|
-
bf.AudioManager().play_sound(self.click_down_sound)
|
141
137
|
|
142
138
|
def do_on_click_down(self, button) -> None:
|
139
|
+
if not self.is_enabled():
|
140
|
+
return
|
143
141
|
if button == 1:
|
144
142
|
self.get_focus()
|
145
143
|
|
@@ -151,8 +149,6 @@ class Slider(Button):
|
|
151
149
|
value_range = self.meter.get_range()
|
152
150
|
value = round(value / self.meter.step) * self.meter.step
|
153
151
|
position_ratio = (value - self.meter.min_value) / value_range
|
154
|
-
# print(self.handle.rect)
|
155
|
-
# print(rect.left + (self.handle.rect.w/2) + position_ratio * (rect.width - self.handle.rect.w),self.handle.rect.w,self.rect.right)
|
156
152
|
return (
|
157
153
|
rect.left
|
158
154
|
+ (self.handle.rect.w / 2)
|
@@ -174,31 +170,34 @@ class Slider(Button):
|
|
174
170
|
value = self.meter.min_value + position_ratio * value_range
|
175
171
|
return round(value / self.meter.step) * self.meter.step
|
176
172
|
|
177
|
-
def
|
178
|
-
|
173
|
+
def get_min_required_size(self) -> tuple[float, float]:
|
179
174
|
gap = self.gap if self.text else 0
|
175
|
+
if not self.text_rect:
|
176
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
177
|
+
w, h = self.text_rect.size
|
178
|
+
return self.inflate_rect_by_padding((0, 0, w + gap + self.meter.get_min_required_size()[1], h)).size
|
180
179
|
|
181
|
-
|
182
|
-
"font_name": self.font_object.name,
|
183
|
-
"text": self.text,
|
184
|
-
"antialias": False,
|
185
|
-
"color": "white",
|
186
|
-
"bgcolor": "black", # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
|
187
|
-
"wraplength": int(self.get_padded_width()) if self.auto_wraplength else 0,
|
188
|
-
}
|
180
|
+
def _build_layout(self) -> None:
|
189
181
|
|
190
|
-
self.
|
182
|
+
gap = self.gap if self.text else 0
|
183
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
191
184
|
|
192
|
-
|
185
|
+
#right part size
|
186
|
+
meter_width = self.text_rect.h * 10
|
193
187
|
if not self.autoresize_w:
|
194
|
-
|
188
|
+
meter_width = self.get_padded_width() - self.text_rect.w - gap
|
189
|
+
right_part_height = min(self.text_rect.h, self.font_object.point_size)
|
190
|
+
self.meter.set_size_if_autoresize((meter_width,right_part_height))
|
191
|
+
self.handle.set_size_if_autoresize((None,right_part_height))
|
192
|
+
|
195
193
|
|
196
|
-
|
197
|
-
|
194
|
+
#join left and right
|
195
|
+
joined_rect = pygame.FRect(
|
196
|
+
0, 0, self.text_rect.w + gap + meter_width, self.text_rect.h
|
198
197
|
)
|
199
198
|
|
200
199
|
if self.autoresize_h or self.autoresize_w:
|
201
|
-
target_rect = self.inflate_rect_by_padding(
|
200
|
+
target_rect = self.inflate_rect_by_padding(joined_rect)
|
202
201
|
if not self.autoresize_w:
|
203
202
|
target_rect.w = self.rect.w
|
204
203
|
if not self.autoresize_h:
|
@@ -209,36 +208,31 @@ class Slider(Button):
|
|
209
208
|
return
|
210
209
|
|
211
210
|
# ------------------------------------ size is ok
|
212
|
-
|
213
|
-
|
214
|
-
self.meter.set_size_if_autoresize(meter_size)
|
215
|
-
handle_size = 2 * [self.meter.get_padded_height()]
|
211
|
+
|
216
212
|
|
217
|
-
self.
|
213
|
+
offset = self._get_outline_offset() if self.show_text_outline else (0,0)
|
214
|
+
padded_rect = self.get_padded_rect()
|
215
|
+
padded_relative = padded_rect.move(-self.rect.x, -self.rect.y)
|
218
216
|
|
219
|
-
self.align_text(
|
220
|
-
self.text_rect.midleft =
|
217
|
+
self.align_text(joined_rect, padded_relative.move( offset), self.alignment)
|
218
|
+
self.text_rect.midleft = joined_rect.midleft
|
221
219
|
|
222
220
|
if self.text:
|
223
221
|
match self.spacing:
|
224
222
|
case bf.spacing.MAX:
|
225
|
-
gap =
|
223
|
+
gap = padded_relative.right - self.text_rect.right - self.meter.rect.w
|
226
224
|
case bf.spacing.MIN:
|
227
225
|
gap = 0
|
228
226
|
|
229
227
|
# place meter
|
230
228
|
|
231
|
-
self.
|
232
|
-
|
233
|
-
self.rect.
|
234
|
-
self.rect.y + (self.text_rect.h / 2) - meter_size[1] / 2,
|
229
|
+
pos = self.text_rect.move(
|
230
|
+
self.rect.x + gap -offset[0],
|
231
|
+
self.rect.y + (self.text_rect.h / 2) - (right_part_height / 2) -offset[1],
|
235
232
|
).topright
|
236
|
-
|
233
|
+
self.meter.rect.topleft = pos
|
237
234
|
# place handle
|
238
235
|
|
239
|
-
# print(self.meter.rect.top - self.rect.top)
|
240
|
-
# print(self.meter.rect.h)
|
241
|
-
|
242
236
|
x = self.value_to_position(self.meter.value)
|
243
237
|
r = self.meter.get_padded_rect()
|
244
238
|
self.handle.set_center(x, r.centery)
|
batFramework/gui/textInput.py
CHANGED
@@ -4,27 +4,77 @@ from .label import Label
|
|
4
4
|
from .interactiveWidget import InteractiveWidget
|
5
5
|
import pygame
|
6
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
|
+
|
7
56
|
|
8
57
|
class TextInput(Label, InteractiveWidget):
|
9
58
|
def __init__(self) -> None:
|
10
|
-
self.cursor_position = 0
|
11
|
-
self.old_key_repeat
|
59
|
+
self.cursor_position = (0, 0)
|
60
|
+
self.old_key_repeat = (0, 0)
|
12
61
|
self.cursor_timer = bf.Timer(0.3, self._cursor_toggle, loop=True).start()
|
13
62
|
self.cursor_timer.pause()
|
14
|
-
self.show_cursor
|
63
|
+
self.show_cursor = False
|
15
64
|
self.on_modify: Callable[[str], str] = None
|
16
65
|
self.set_focusable(True)
|
17
66
|
self.set_outline_color("black")
|
18
67
|
super().__init__("")
|
68
|
+
self.alignment = bf.alignment.TOPLEFT
|
19
69
|
|
20
70
|
def set_modify_callback(self, callback: Callable[[str], str]) -> Self:
|
21
71
|
self.on_modify = callback
|
22
72
|
return self
|
23
73
|
|
24
74
|
def __str__(self) -> str:
|
25
|
-
return f"TextInput({self.text})"
|
75
|
+
return f"TextInput({repr(self.text)})"
|
26
76
|
|
27
|
-
def _cursor_toggle(self, value: bool = None):
|
77
|
+
def _cursor_toggle(self, value: bool | None = None):
|
28
78
|
if value is None:
|
29
79
|
value = not self.show_cursor
|
30
80
|
self.show_cursor = value
|
@@ -42,10 +92,9 @@ class TextInput(Label, InteractiveWidget):
|
|
42
92
|
pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
|
43
93
|
|
44
94
|
def do_on_get_focus(self):
|
45
|
-
self.old_key_repeat = pygame.key.get_repeat()
|
46
95
|
self.cursor_timer.resume()
|
47
96
|
self._cursor_toggle(True)
|
48
|
-
self.
|
97
|
+
self.old_key_repeat = pygame.key.get_repeat()
|
49
98
|
pygame.key.set_repeat(200, 50)
|
50
99
|
|
51
100
|
def do_on_lose_focus(self):
|
@@ -53,42 +102,105 @@ class TextInput(Label, InteractiveWidget):
|
|
53
102
|
self._cursor_toggle(False)
|
54
103
|
pygame.key.set_repeat(*self.old_key_repeat)
|
55
104
|
|
56
|
-
def
|
57
|
-
if
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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)
|
62
122
|
self.show_cursor = True
|
63
|
-
self.
|
64
|
-
if self.text_rect.w > self.get_padded_width():
|
65
|
-
self.dirty_shape = True
|
123
|
+
self.dirty_shape = True
|
66
124
|
return self
|
67
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
|
+
|
68
148
|
def do_handle_event(self, event):
|
69
149
|
if not self.is_focused:
|
70
150
|
return
|
71
|
-
text = self.get_text()
|
72
|
-
cursor_position = self.cursor_position
|
73
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)
|
74
157
|
if event.type == pygame.TEXTINPUT:
|
75
|
-
self.set_text(text[:
|
76
|
-
self.set_cursor_position(
|
158
|
+
self.set_text(text[:current] + event.text + text[current:])
|
159
|
+
self.set_cursor_position(self.absolute_to_cursor(current + len(event.text)))
|
77
160
|
elif event.type == pygame.KEYDOWN:
|
161
|
+
pressed = pygame.key.get_pressed()
|
78
162
|
if event.key == pygame.K_ESCAPE:
|
79
163
|
self.lose_focus()
|
80
|
-
|
81
164
|
elif event.key == pygame.K_BACKSPACE:
|
82
|
-
if
|
83
|
-
self.set_text(text[:
|
84
|
-
self.set_cursor_position(
|
85
|
-
|
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:])
|
86
171
|
elif event.key == pygame.K_RIGHT:
|
87
|
-
self.
|
88
|
-
|
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))
|
89
183
|
elif event.key == pygame.K_LEFT:
|
90
|
-
self.set_cursor_position(cursor_position - 1)
|
91
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))
|
92
204
|
else:
|
93
205
|
return
|
94
206
|
else:
|
@@ -104,34 +216,32 @@ class TextInput(Label, InteractiveWidget):
|
|
104
216
|
def _paint_cursor(self) -> None:
|
105
217
|
if not self.font_object or not self.show_cursor:
|
106
218
|
return
|
107
|
-
partial_text_size = self.font_object.size(
|
108
|
-
self.get_text()[: self.cursor_position]
|
109
|
-
)
|
110
219
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
116
227
|
|
228
|
+
cursor_rect = pygame.Rect(cursor_x, cursor_y, 2, self.font_object.get_height())
|
117
229
|
pygame.draw.rect(self.surface, self.text_color, cursor_rect)
|
118
230
|
|
119
231
|
def paint(self) -> None:
|
120
232
|
super().paint()
|
121
233
|
self._paint_cursor()
|
122
234
|
|
123
|
-
def
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
elif self.text_rect.x + w < area.left:
|
137
|
-
self.text_rect.left = area.left - w
|
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
@@ -6,13 +6,14 @@ import pygame
|
|
6
6
|
|
7
7
|
|
8
8
|
class Toggle(Button):
|
9
|
-
def __init__(self, text: str, callback=None, default_value: bool = False) -> None:
|
9
|
+
def __init__(self, text: str = "", callback=None, default_value: bool = False) -> None:
|
10
10
|
self.value: bool = default_value
|
11
11
|
self.indicator: ToggleIndicator = ToggleIndicator(default_value)
|
12
12
|
self.gap: float | int = 0
|
13
13
|
self.spacing: bf.spacing = bf.spacing.MANUAL
|
14
14
|
super().__init__(text, callback)
|
15
15
|
self.add(self.indicator)
|
16
|
+
self.set_clip_children(False)
|
16
17
|
# self.set_gap(int(max(4, self.get_padded_width() / 3)))
|
17
18
|
|
18
19
|
def set_visible(self, value: bool) -> Self:
|
@@ -53,51 +54,32 @@ class Toggle(Button):
|
|
53
54
|
|
54
55
|
def get_min_required_size(self) -> tuple[float, float]:
|
55
56
|
if not self.text_rect:
|
56
|
-
|
57
|
-
"font_name": self.font_object.name,
|
58
|
-
"text": self.text,
|
59
|
-
"antialias": False,
|
60
|
-
"color": "white",
|
61
|
-
"bgcolor": "black", # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
|
62
|
-
"wraplength": (
|
63
|
-
int(self.get_padded_width()) if self.auto_wraplength else 0
|
64
|
-
),
|
65
|
-
}
|
66
|
-
self.text_rect.size = self._render_font(params).get_size()
|
67
|
-
w, h = self.text_rect.size
|
57
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
68
58
|
size = (
|
69
59
|
max(
|
70
|
-
self.indicator.
|
71
|
-
w + self.font_object.point_size + (self.gap if self.text else 0),
|
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),
|
72
62
|
),
|
73
63
|
self.text_rect.h,
|
74
64
|
)
|
75
65
|
return self.inflate_rect_by_padding((0, 0, *size)).size
|
76
66
|
|
77
67
|
def _build_layout(self) -> None:
|
78
|
-
|
79
68
|
gap = self.gap if self.text else 0
|
69
|
+
self.text_rect.size = self._get_text_rect_required_size()
|
80
70
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
"antialias": False,
|
85
|
-
"color": "white",
|
86
|
-
"bgcolor": "black", # if (self.has_alpha_color() or self.draw_mode == bf.drawMode.TEXTURED) else self.color,
|
87
|
-
"wraplength": int(self.get_padded_width()) if self.auto_wraplength else 0,
|
88
|
-
}
|
89
|
-
self.text_rect.size = self._render_font(params).get_size()
|
90
|
-
|
91
|
-
indicator_height = self.font_object.point_size
|
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))
|
92
74
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
0, 0, self.text_rect.w + gap + indicator_height, self.text_rect.h
|
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
|
97
78
|
)
|
98
79
|
|
80
|
+
|
99
81
|
if self.autoresize_h or self.autoresize_w:
|
100
|
-
target_rect = self.inflate_rect_by_padding(
|
82
|
+
target_rect = self.inflate_rect_by_padding(joined_rect)
|
101
83
|
if not self.autoresize_w:
|
102
84
|
target_rect.w = self.rect.w
|
103
85
|
if not self.autoresize_h:
|
@@ -107,20 +89,24 @@ class Toggle(Button):
|
|
107
89
|
self.build()
|
108
90
|
return
|
109
91
|
|
110
|
-
|
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
|
111
100
|
|
112
|
-
self.align_text(tmp_rect, padded, self.alignment)
|
113
|
-
self.text_rect.midleft = tmp_rect.midleft
|
114
101
|
if self.text:
|
115
102
|
match self.spacing:
|
116
103
|
case bf.spacing.MAX:
|
117
|
-
gap =
|
104
|
+
gap = padded_relative.right - self.text_rect.right - self.indicator.rect.w
|
118
105
|
case bf.spacing.MIN:
|
119
106
|
gap = 0
|
120
107
|
|
121
|
-
self.
|
122
|
-
|
123
|
-
self.rect.
|
124
|
-
self.rect.y + (self.text_rect.h / 2) - self.indicator.rect.h / 2,
|
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],
|
125
111
|
).topright
|
126
|
-
|
112
|
+
self.indicator.rect.topleft = pos
|