batframework 1.0.9a11__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 -244
- 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 -206
- 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 -429
- 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.9a11.dist-info → batframework-1.0.9a12.dist-info}/LICENSE +20 -20
- {batframework-1.0.9a11.dist-info → batframework-1.0.9a12.dist-info}/METADATA +24 -17
- batframework-1.0.9a12.dist-info/RECORD +72 -0
- batframework-1.0.9a11.dist-info/RECORD +0 -67
- {batframework-1.0.9a11.dist-info → batframework-1.0.9a12.dist-info}/WHEEL +0 -0
- {batframework-1.0.9a11.dist-info → batframework-1.0.9a12.dist-info}/top_level.txt +0 -0
@@ -1,244 +1,244 @@
|
|
1
|
-
import batFramework as bf
|
2
|
-
from typing import Self, Callable, Any
|
3
|
-
from .interactiveWidget import InteractiveWidget
|
4
|
-
from .shape import Shape
|
5
|
-
import pygame
|
6
|
-
|
7
|
-
|
8
|
-
class ClickableWidget(Shape, InteractiveWidget):
|
9
|
-
_cache: dict = {}
|
10
|
-
|
11
|
-
def __init__(self, callback: Callable[[],Any] = None, *args, **kwargs) -> None:
|
12
|
-
super().__init__(*args, **kwargs)
|
13
|
-
self.callback = callback
|
14
|
-
self.is_pressed: bool = False # the state where the button is being held down (releasing will trigger callback)
|
15
|
-
self.is_enabled: bool = True #
|
16
|
-
self.hover_cursor = bf.const.DEFAULT_HOVER_CURSOR
|
17
|
-
self.click_cursor = bf.const.DEFAULT_CLICK_CURSOR
|
18
|
-
|
19
|
-
self.click_down_sound = None
|
20
|
-
self.click_up_sound = None
|
21
|
-
self.get_focus_sound = None
|
22
|
-
self.lose_focus_sound = None
|
23
|
-
|
24
|
-
self.pressed_relief: int = 1 # Depth effect height when pressed
|
25
|
-
self.unpressed_relief: int = 2 # Depth effect height when released (default)
|
26
|
-
self.silent_focus: bool = False
|
27
|
-
self.set_debug_color("cyan")
|
28
|
-
self.set_relief(self.unpressed_relief)
|
29
|
-
|
30
|
-
|
31
|
-
def get_min_required_size(self) -> tuple[float, float]:
|
32
|
-
res = super().get_min_required_size()
|
33
|
-
res = res[0],res[1]+self.unpressed_relief
|
34
|
-
return res
|
35
|
-
|
36
|
-
def set_unpressed_relief(self, relief: int) -> Self:
|
37
|
-
if relief == self.unpressed_relief:
|
38
|
-
return self
|
39
|
-
self.unpressed_relief = relief
|
40
|
-
self.dirty_shape = True
|
41
|
-
if not self.is_pressed:
|
42
|
-
self.set_relief(relief)
|
43
|
-
return self
|
44
|
-
|
45
|
-
def set_pressed_relief(self, relief: int) -> Self:
|
46
|
-
if relief == self.pressed_relief:
|
47
|
-
return self
|
48
|
-
self.pressed_relief = relief
|
49
|
-
self.dirty_shape = True
|
50
|
-
if self.is_pressed:
|
51
|
-
self.set_relief(relief)
|
52
|
-
return self
|
53
|
-
|
54
|
-
def set_silent_focus(self, value: bool) -> Self:
|
55
|
-
self.silent_focus = value
|
56
|
-
return self
|
57
|
-
|
58
|
-
def set_click_down_sound(self, sound_name: str) -> Self:
|
59
|
-
self.click_down_sound = sound_name
|
60
|
-
return self
|
61
|
-
|
62
|
-
def set_click_up_sound(self, sound_name: str) -> Self:
|
63
|
-
self.click_up_sound = sound_name
|
64
|
-
return self
|
65
|
-
|
66
|
-
def set_get_focus_sound(self, sound_name: str) -> Self:
|
67
|
-
self.get_focus_sound = sound_name
|
68
|
-
return self
|
69
|
-
|
70
|
-
def set_lose_focus_sound(self, sound_name: str) -> Self:
|
71
|
-
self.lose_focus_sound = sound_name
|
72
|
-
return self
|
73
|
-
|
74
|
-
def set_hover_cursor(self, cursor: pygame.Cursor) -> Self:
|
75
|
-
self.hover_cursor = cursor
|
76
|
-
return self
|
77
|
-
|
78
|
-
def set_click_cursor(self, cursor: pygame.Cursor) -> Self:
|
79
|
-
self.click_cursor = cursor
|
80
|
-
return self
|
81
|
-
|
82
|
-
def get_surface_filter(self) -> pygame.Surface | None:
|
83
|
-
size = int(self.rect.w), int(self.rect.h)
|
84
|
-
surface_filter = ClickableWidget._cache.get((size, *self.border_radius), None)
|
85
|
-
if surface_filter is None:
|
86
|
-
# Create a mask from the original surface
|
87
|
-
mask = pygame.mask.from_surface(self.surface, threshold=0)
|
88
|
-
|
89
|
-
silhouette_surface = mask.to_surface(
|
90
|
-
setcolor=(30, 30, 30), unsetcolor=(0, 0, 0)
|
91
|
-
)
|
92
|
-
|
93
|
-
ClickableWidget._cache[(size, *self.border_radius)] = silhouette_surface
|
94
|
-
|
95
|
-
surface_filter = silhouette_surface
|
96
|
-
|
97
|
-
return surface_filter
|
98
|
-
|
99
|
-
def allow_focus_to_self(self) -> bool:
|
100
|
-
return True
|
101
|
-
|
102
|
-
def enable(self) -> Self:
|
103
|
-
self.is_enabled = True
|
104
|
-
self.dirty_surface = True
|
105
|
-
return self
|
106
|
-
|
107
|
-
def disable(self) -> Self:
|
108
|
-
self.is_enabled = False
|
109
|
-
self.dirty_surface = True
|
110
|
-
return self
|
111
|
-
|
112
|
-
def set_callback(self, callback: Callable[[],Any]) -> Self:
|
113
|
-
self.callback = callback
|
114
|
-
return self
|
115
|
-
|
116
|
-
def on_get_focus(self):
|
117
|
-
super().on_get_focus()
|
118
|
-
if self.get_focus_sound and not self.silent_focus:
|
119
|
-
if self.parent_scene and self.parent_scene.visible:
|
120
|
-
bf.AudioManager().play_sound(self.get_focus_sound)
|
121
|
-
if self.silent_focus:
|
122
|
-
self.silent_focus = False
|
123
|
-
|
124
|
-
def on_lose_focus(self):
|
125
|
-
super().on_lose_focus()
|
126
|
-
if self.lose_focus_sound and not self.silent_focus:
|
127
|
-
if self.parent_scene and self.parent_scene.visible:
|
128
|
-
bf.AudioManager().play_sound(self.lose_focus_sound)
|
129
|
-
if self.silent_focus:
|
130
|
-
self.silent_focus = False
|
131
|
-
|
132
|
-
def __str__(self) -> str:
|
133
|
-
return f"ClickableWidget"
|
134
|
-
|
135
|
-
def click(self, force=False) -> None:
|
136
|
-
if not self.is_enabled and not force:
|
137
|
-
return False
|
138
|
-
if self.callback is not None:
|
139
|
-
self.callback()
|
140
|
-
return True
|
141
|
-
return False
|
142
|
-
|
143
|
-
def on_key_down(self, key):
|
144
|
-
if key == pygame.K_SPACE:
|
145
|
-
self.on_click_down(1)
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
if button
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
self.
|
178
|
-
|
179
|
-
self.
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
if not self.is_enabled:
|
186
|
-
return
|
187
|
-
super().on_enter()
|
188
|
-
self.dirty_surface = True
|
189
|
-
pygame.mouse.set_cursor(self.hover_cursor)
|
190
|
-
|
191
|
-
def on_exit(self) -> None:
|
192
|
-
super().on_exit()
|
193
|
-
if self.is_pressed:
|
194
|
-
self.set_relief(self.unpressed_relief)
|
195
|
-
self.is_pressed = False
|
196
|
-
self.dirty_surface = True
|
197
|
-
pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
|
198
|
-
|
199
|
-
def on_lose_focus(self):
|
200
|
-
super().on_lose_focus()
|
201
|
-
self.on_exit()
|
202
|
-
|
203
|
-
def _paint_disabled(self) -> None:
|
204
|
-
self.surface.blit(
|
205
|
-
self.get_surface_filter(), (0, 0), special_flags=pygame.BLEND_RGB_SUB
|
206
|
-
)
|
207
|
-
|
208
|
-
def _paint_hovered(self) -> None:
|
209
|
-
self.surface.blit(
|
210
|
-
self.get_surface_filter(), (0, 0), special_flags=pygame.BLEND_RGB_ADD
|
211
|
-
)
|
212
|
-
|
213
|
-
def get_inner_rect(self) -> pygame.FRect:
|
214
|
-
return pygame.FRect(
|
215
|
-
self.rect.x + self.padding[0],
|
216
|
-
self.rect.y + self.padding[1] + (self.unpressed_relief - self.pressed_relief if self.is_pressed else 0),
|
217
|
-
self.rect.w - self.padding[2] - self.padding[0],
|
218
|
-
self.rect.h - self.unpressed_relief - self.padding[1] - self.padding[3],
|
219
|
-
)
|
220
|
-
|
221
|
-
def get_local_inner_rect(self) -> pygame.FRect:
|
222
|
-
return pygame.FRect(
|
223
|
-
self.padding[0],
|
224
|
-
self.padding[1] + (self.unpressed_relief - self.pressed_relief if self.is_pressed else 0),
|
225
|
-
self.rect.w - self.padding[2] - self.padding[0],
|
226
|
-
self.rect.h - self.unpressed_relief - self.padding[1] - self.padding[3],
|
227
|
-
)
|
228
|
-
|
229
|
-
|
230
|
-
def _get_elevated_rect(self) -> pygame.FRect:
|
231
|
-
return pygame.FRect(
|
232
|
-
0,
|
233
|
-
self.unpressed_relief - self.pressed_relief if self.is_pressed else 0,
|
234
|
-
self.rect.w,
|
235
|
-
self.rect.h - self.unpressed_relief,
|
236
|
-
)
|
237
|
-
|
238
|
-
def paint(self) -> None:
|
239
|
-
super().paint()
|
240
|
-
if not self.is_enabled:
|
241
|
-
self._paint_disabled()
|
242
|
-
elif self.is_hovered:
|
243
|
-
self._paint_hovered()
|
244
|
-
|
1
|
+
import batFramework as bf
|
2
|
+
from typing import Self, Callable, Any
|
3
|
+
from .interactiveWidget import InteractiveWidget
|
4
|
+
from .shape import Shape
|
5
|
+
import pygame
|
6
|
+
|
7
|
+
|
8
|
+
class ClickableWidget(Shape, InteractiveWidget):
|
9
|
+
_cache: dict = {}
|
10
|
+
|
11
|
+
def __init__(self, callback: Callable[[],Any] = None, *args, **kwargs) -> None:
|
12
|
+
super().__init__(*args, **kwargs)
|
13
|
+
self.callback = callback
|
14
|
+
self.is_pressed: bool = False # the state where the button is being held down (releasing will trigger callback)
|
15
|
+
self.is_enabled: bool = True #
|
16
|
+
self.hover_cursor = bf.const.DEFAULT_HOVER_CURSOR
|
17
|
+
self.click_cursor = bf.const.DEFAULT_CLICK_CURSOR
|
18
|
+
|
19
|
+
self.click_down_sound = None
|
20
|
+
self.click_up_sound = None
|
21
|
+
self.get_focus_sound = None
|
22
|
+
self.lose_focus_sound = None
|
23
|
+
|
24
|
+
self.pressed_relief: int = 1 # Depth effect height when pressed
|
25
|
+
self.unpressed_relief: int = 2 # Depth effect height when released (default)
|
26
|
+
self.silent_focus: bool = False
|
27
|
+
self.set_debug_color("cyan")
|
28
|
+
self.set_relief(self.unpressed_relief)
|
29
|
+
self.set_click_pass_through(False)
|
30
|
+
|
31
|
+
def get_min_required_size(self) -> tuple[float, float]:
|
32
|
+
res = super().get_min_required_size()
|
33
|
+
res = res[0],res[1]+self.unpressed_relief
|
34
|
+
return res
|
35
|
+
|
36
|
+
def set_unpressed_relief(self, relief: int) -> Self:
|
37
|
+
if relief == self.unpressed_relief:
|
38
|
+
return self
|
39
|
+
self.unpressed_relief = relief
|
40
|
+
self.dirty_shape = True
|
41
|
+
if not self.is_pressed:
|
42
|
+
self.set_relief(relief)
|
43
|
+
return self
|
44
|
+
|
45
|
+
def set_pressed_relief(self, relief: int) -> Self:
|
46
|
+
if relief == self.pressed_relief:
|
47
|
+
return self
|
48
|
+
self.pressed_relief = relief
|
49
|
+
self.dirty_shape = True
|
50
|
+
if self.is_pressed:
|
51
|
+
self.set_relief(relief)
|
52
|
+
return self
|
53
|
+
|
54
|
+
def set_silent_focus(self, value: bool) -> Self:
|
55
|
+
self.silent_focus = value
|
56
|
+
return self
|
57
|
+
|
58
|
+
def set_click_down_sound(self, sound_name: str) -> Self:
|
59
|
+
self.click_down_sound = sound_name
|
60
|
+
return self
|
61
|
+
|
62
|
+
def set_click_up_sound(self, sound_name: str) -> Self:
|
63
|
+
self.click_up_sound = sound_name
|
64
|
+
return self
|
65
|
+
|
66
|
+
def set_get_focus_sound(self, sound_name: str) -> Self:
|
67
|
+
self.get_focus_sound = sound_name
|
68
|
+
return self
|
69
|
+
|
70
|
+
def set_lose_focus_sound(self, sound_name: str) -> Self:
|
71
|
+
self.lose_focus_sound = sound_name
|
72
|
+
return self
|
73
|
+
|
74
|
+
def set_hover_cursor(self, cursor: pygame.Cursor) -> Self:
|
75
|
+
self.hover_cursor = cursor
|
76
|
+
return self
|
77
|
+
|
78
|
+
def set_click_cursor(self, cursor: pygame.Cursor) -> Self:
|
79
|
+
self.click_cursor = cursor
|
80
|
+
return self
|
81
|
+
|
82
|
+
def get_surface_filter(self) -> pygame.Surface | None:
|
83
|
+
size = int(self.rect.w), int(self.rect.h)
|
84
|
+
surface_filter = ClickableWidget._cache.get((size, *self.border_radius), None)
|
85
|
+
if surface_filter is None:
|
86
|
+
# Create a mask from the original surface
|
87
|
+
mask = pygame.mask.from_surface(self.surface, threshold=0)
|
88
|
+
|
89
|
+
silhouette_surface = mask.to_surface(
|
90
|
+
setcolor=(30, 30, 30), unsetcolor=(0, 0, 0)
|
91
|
+
)
|
92
|
+
|
93
|
+
ClickableWidget._cache[(size, *self.border_radius)] = silhouette_surface
|
94
|
+
|
95
|
+
surface_filter = silhouette_surface
|
96
|
+
|
97
|
+
return surface_filter
|
98
|
+
|
99
|
+
def allow_focus_to_self(self) -> bool:
|
100
|
+
return True
|
101
|
+
|
102
|
+
def enable(self) -> Self:
|
103
|
+
self.is_enabled = True
|
104
|
+
self.dirty_surface = True
|
105
|
+
return self
|
106
|
+
|
107
|
+
def disable(self) -> Self:
|
108
|
+
self.is_enabled = False
|
109
|
+
self.dirty_surface = True
|
110
|
+
return self
|
111
|
+
|
112
|
+
def set_callback(self, callback: Callable[[],Any]) -> Self:
|
113
|
+
self.callback = callback
|
114
|
+
return self
|
115
|
+
|
116
|
+
def on_get_focus(self):
|
117
|
+
super().on_get_focus()
|
118
|
+
if self.get_focus_sound and not self.silent_focus:
|
119
|
+
if self.parent_scene and self.parent_scene.visible:
|
120
|
+
bf.AudioManager().play_sound(self.get_focus_sound)
|
121
|
+
if self.silent_focus:
|
122
|
+
self.silent_focus = False
|
123
|
+
|
124
|
+
def on_lose_focus(self):
|
125
|
+
super().on_lose_focus()
|
126
|
+
if self.lose_focus_sound and not self.silent_focus:
|
127
|
+
if self.parent_scene and self.parent_scene.visible:
|
128
|
+
bf.AudioManager().play_sound(self.lose_focus_sound)
|
129
|
+
if self.silent_focus:
|
130
|
+
self.silent_focus = False
|
131
|
+
|
132
|
+
def __str__(self) -> str:
|
133
|
+
return f"ClickableWidget"
|
134
|
+
|
135
|
+
def click(self, force=False) -> None:
|
136
|
+
if not self.is_enabled and not force:
|
137
|
+
return False
|
138
|
+
if self.callback is not None:
|
139
|
+
self.callback()
|
140
|
+
return True
|
141
|
+
return False
|
142
|
+
|
143
|
+
def on_key_down(self, key,event):
|
144
|
+
if key == pygame.K_SPACE:
|
145
|
+
self.on_click_down(1,event)
|
146
|
+
self.do_on_key_down(key,event)
|
147
|
+
|
148
|
+
def on_key_up(self, key,event):
|
149
|
+
if key == pygame.K_SPACE:
|
150
|
+
self.on_click_up(1,event)
|
151
|
+
self.do_on_key_down(key,event)
|
152
|
+
|
153
|
+
def on_click_down(self, button,event) -> None :
|
154
|
+
if button < 1 or button > 5 :
|
155
|
+
return
|
156
|
+
self.is_clicked_down[button-1] = True
|
157
|
+
if button != 1:
|
158
|
+
return
|
159
|
+
event.consumed = not self.click_pass_through
|
160
|
+
if self.is_enabled and self.get_focus():
|
161
|
+
self.is_pressed = True
|
162
|
+
if self.click_down_sound:
|
163
|
+
bf.AudioManager().play_sound(self.click_down_sound)
|
164
|
+
pygame.mouse.set_cursor(self.click_cursor)
|
165
|
+
self.set_relief(self.pressed_relief)
|
166
|
+
self.do_on_click_down(button,event)
|
167
|
+
|
168
|
+
def on_click_up(self, button,event):
|
169
|
+
if button < 1 or button > 5 :
|
170
|
+
return
|
171
|
+
self.is_clicked_down[button-1] = False
|
172
|
+
if button != 1 :
|
173
|
+
return
|
174
|
+
event.consumed = not self.click_pass_through
|
175
|
+
if self.is_enabled and self.is_pressed:
|
176
|
+
self.is_pressed = False
|
177
|
+
if self.click_up_sound:
|
178
|
+
bf.AudioManager().play_sound(self.click_up_sound)
|
179
|
+
self.set_relief(self.unpressed_relief)
|
180
|
+
self.click()
|
181
|
+
self.do_on_click_up(button,event)
|
182
|
+
|
183
|
+
def on_enter(self) -> None:
|
184
|
+
self.is_hovered = True
|
185
|
+
if not self.is_enabled:
|
186
|
+
return
|
187
|
+
super().on_enter()
|
188
|
+
self.dirty_surface = True
|
189
|
+
pygame.mouse.set_cursor(self.hover_cursor)
|
190
|
+
|
191
|
+
def on_exit(self) -> None:
|
192
|
+
super().on_exit()
|
193
|
+
if self.is_pressed:
|
194
|
+
self.set_relief(self.unpressed_relief)
|
195
|
+
self.is_pressed = False
|
196
|
+
self.dirty_surface = True
|
197
|
+
pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
|
198
|
+
|
199
|
+
def on_lose_focus(self):
|
200
|
+
super().on_lose_focus()
|
201
|
+
self.on_exit()
|
202
|
+
|
203
|
+
def _paint_disabled(self) -> None:
|
204
|
+
self.surface.blit(
|
205
|
+
self.get_surface_filter(), (0, 0), special_flags=pygame.BLEND_RGB_SUB
|
206
|
+
)
|
207
|
+
|
208
|
+
def _paint_hovered(self) -> None:
|
209
|
+
self.surface.blit(
|
210
|
+
self.get_surface_filter(), (0, 0), special_flags=pygame.BLEND_RGB_ADD
|
211
|
+
)
|
212
|
+
|
213
|
+
def get_inner_rect(self) -> pygame.FRect:
|
214
|
+
return pygame.FRect(
|
215
|
+
self.rect.x + self.padding[0],
|
216
|
+
self.rect.y + self.padding[1] + (self.unpressed_relief - self.pressed_relief if self.is_pressed else 0),
|
217
|
+
self.rect.w - self.padding[2] - self.padding[0],
|
218
|
+
self.rect.h - self.unpressed_relief - self.padding[1] - self.padding[3],
|
219
|
+
)
|
220
|
+
|
221
|
+
def get_local_inner_rect(self) -> pygame.FRect:
|
222
|
+
return pygame.FRect(
|
223
|
+
self.padding[0],
|
224
|
+
self.padding[1] + (self.unpressed_relief - self.pressed_relief if self.is_pressed else 0),
|
225
|
+
self.rect.w - self.padding[2] - self.padding[0],
|
226
|
+
self.rect.h - self.unpressed_relief - self.padding[1] - self.padding[3],
|
227
|
+
)
|
228
|
+
|
229
|
+
|
230
|
+
def _get_elevated_rect(self) -> pygame.FRect:
|
231
|
+
return pygame.FRect(
|
232
|
+
0,
|
233
|
+
self.unpressed_relief - self.pressed_relief if self.is_pressed else 0,
|
234
|
+
self.rect.w,
|
235
|
+
self.rect.h - self.unpressed_relief,
|
236
|
+
)
|
237
|
+
|
238
|
+
def paint(self) -> None:
|
239
|
+
super().paint()
|
240
|
+
if not self.is_enabled:
|
241
|
+
self._paint_disabled()
|
242
|
+
elif self.is_hovered:
|
243
|
+
self._paint_hovered()
|
244
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
from .container import Container
|
2
|
+
from .scrollingContainer import ScrollingContainer
|
3
|
+
from .interactiveWidget import InteractiveWidget
|
4
|
+
from .toggle import Toggle
|
5
|
+
from .shape import Shape
|
6
|
+
from .indicator import ArrowIndicator
|
7
|
+
from .syncedVar import SyncedVar
|
8
|
+
from .layout import Column
|
9
|
+
import batFramework as bf
|
10
|
+
from .syncedVar import SyncedVar
|
11
|
+
from .widget import Widget
|
12
|
+
from .layout import Layout
|
13
|
+
|
14
|
+
|
15
|
+
class CollapseIndicator(ArrowIndicator):
|
16
|
+
def __init__(self,synced_var:SyncedVar[bool]):
|
17
|
+
super().__init__(bf.direction.RIGHT)
|
18
|
+
synced_var.bind(self,self.on_state_change)
|
19
|
+
|
20
|
+
def on_state_change(self,state:bool):
|
21
|
+
self.set_arrow_direction(bf.direction.DOWN if state else bf.direction.RIGHT)
|
22
|
+
|
23
|
+
def top_at(self, x: float, y: float) -> "None|Widget":
|
24
|
+
r = super().top_at(x, y)
|
25
|
+
if r is self:
|
26
|
+
return None
|
27
|
+
return r
|
28
|
+
|
29
|
+
|
30
|
+
class CollapseContainer(Shape,InteractiveWidget):
|
31
|
+
def __init__(self, text:str, layout:Layout=None, *children:Widget, **kwargs):
|
32
|
+
super().__init__(**kwargs)
|
33
|
+
self.state = SyncedVar[bool](False)
|
34
|
+
self.state.bind(self,self._on_state_change)
|
35
|
+
self.toggle = Toggle(text,synced_var=self.state)
|
36
|
+
self.toggle.set_autoresize_w(False)
|
37
|
+
self.container = Container(layout,*children)
|
38
|
+
self.container.set_autoresize_w(False)
|
39
|
+
self.add(self.toggle,self.container)
|
40
|
+
self.state.value = False
|
41
|
+
self.toggle.set_indicator(CollapseIndicator(self.state))
|
42
|
+
self.toggle.indicator.add_constraints(bf.gui.AspectRatio(1,reference_axis=bf.axis.VERTICAL))
|
43
|
+
|
44
|
+
def __str__(self):
|
45
|
+
return "CollapseContainer"
|
46
|
+
|
47
|
+
def _on_state_change(self,value):
|
48
|
+
print("state is ",value)
|
49
|
+
self.container.show() if value else self.container.hide()
|
50
|
+
self.dirty_shape = True
|
51
|
+
|
52
|
+
def allow_focus_to_self(self):
|
53
|
+
return super().allow_focus_to_self() and self.toggle.allow_focus_to_self()
|
54
|
+
|
55
|
+
def get_focus(self):
|
56
|
+
if self.allow_focus_to_self() and ((r := self.get_root()) is not None):
|
57
|
+
r.focus_on(self.toggle)
|
58
|
+
return True
|
59
|
+
return False
|
60
|
+
|
61
|
+
def build(self):
|
62
|
+
res = False
|
63
|
+
min_size = self.get_min_required_size()
|
64
|
+
target_size = self.resolve_size(min_size)
|
65
|
+
if self.rect.size != target_size :
|
66
|
+
self.set_size(target_size)
|
67
|
+
res = True
|
68
|
+
|
69
|
+
inner = self.get_inner_rect()
|
70
|
+
self.toggle.set_size((inner.w,None))
|
71
|
+
self.toggle.set_position(*inner.topleft)
|
72
|
+
if self.state.value:
|
73
|
+
self.container.set_size((inner.w,None))
|
74
|
+
self.container.set_position(*self.toggle.rect.bottomleft)
|
75
|
+
return res
|
76
|
+
|
77
|
+
def apply_updates(self, pass_type):
|
78
|
+
|
79
|
+
if pass_type == "pre":
|
80
|
+
self.apply_pre_updates()
|
81
|
+
if self.state.value : self.container.apply_updates("pre")
|
82
|
+
self.toggle.apply_updates("pre")
|
83
|
+
elif pass_type == "post":
|
84
|
+
if self.state.value : self.container.apply_updates("post")
|
85
|
+
self.toggle.apply_updates("post")
|
86
|
+
self.apply_post_updates(skip_draw=not self.visible)
|
87
|
+
|
88
|
+
|
89
|
+
def get_min_required_size(self):
|
90
|
+
t_min = self.toggle.get_min_required_size()
|
91
|
+
if self.container.layout:
|
92
|
+
c_min = self.container.expand_rect_with_padding((0,0,*self.container.layout.get_raw_size())).size
|
93
|
+
else:
|
94
|
+
c_min = self.container.get_min_required_size()
|
95
|
+
if not self.state.value:
|
96
|
+
c_min = (c_min[0],0)
|
97
|
+
|
98
|
+
return self.expand_rect_with_padding((0,0,max(t_min[0],c_min[0]),t_min[1]+c_min[1])).size
|
@@ -1 +1 @@
|
|
1
|
-
from . import constraints
|
1
|
+
from . import constraints
|