batframework 1.0.9a7__py3-none-any.whl → 1.0.9a9__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 +20 -11
- batFramework/action.py +1 -1
- batFramework/animatedSprite.py +47 -116
- batFramework/animation.py +30 -5
- batFramework/audioManager.py +8 -5
- batFramework/baseScene.py +240 -0
- batFramework/camera.py +4 -0
- batFramework/constants.py +6 -2
- batFramework/cutscene.py +221 -21
- batFramework/cutsceneManager.py +5 -2
- batFramework/drawable.py +7 -5
- batFramework/easingController.py +10 -11
- batFramework/entity.py +21 -2
- batFramework/enums.py +48 -33
- batFramework/gui/__init__.py +6 -3
- batFramework/gui/animatedLabel.py +10 -2
- batFramework/gui/button.py +4 -31
- batFramework/gui/clickableWidget.py +63 -50
- batFramework/gui/constraints/constraints.py +212 -136
- batFramework/gui/container.py +77 -58
- batFramework/gui/debugger.py +12 -17
- batFramework/gui/draggableWidget.py +21 -17
- batFramework/gui/image.py +3 -10
- batFramework/gui/indicator.py +56 -1
- batFramework/gui/interactiveWidget.py +127 -108
- batFramework/gui/label.py +73 -64
- batFramework/gui/layout.py +286 -445
- batFramework/gui/meter.py +42 -20
- batFramework/gui/radioButton.py +20 -69
- batFramework/gui/root.py +99 -29
- batFramework/gui/selector.py +250 -0
- batFramework/gui/shape.py +13 -5
- batFramework/gui/slider.py +262 -107
- batFramework/gui/syncedVar.py +49 -0
- batFramework/gui/textInput.py +46 -22
- batFramework/gui/toggle.py +70 -52
- batFramework/gui/tooltip.py +30 -0
- batFramework/gui/widget.py +222 -135
- batFramework/manager.py +7 -8
- batFramework/particle.py +4 -1
- batFramework/propertyEaser.py +79 -0
- batFramework/renderGroup.py +17 -50
- batFramework/resourceManager.py +43 -13
- batFramework/scene.py +15 -335
- batFramework/sceneLayer.py +138 -0
- batFramework/sceneManager.py +31 -36
- batFramework/scrollingSprite.py +8 -3
- batFramework/sprite.py +1 -1
- batFramework/templates/__init__.py +1 -2
- batFramework/templates/controller.py +97 -0
- batFramework/timeManager.py +76 -22
- batFramework/transition.py +37 -103
- batFramework/utils.py +125 -66
- {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/METADATA +24 -3
- batframework-1.0.9a9.dist-info/RECORD +67 -0
- {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/WHEEL +1 -1
- batFramework/character.py +0 -27
- batFramework/templates/character.py +0 -43
- batFramework/templates/states.py +0 -166
- batframework-1.0.9a7.dist-info/RECORD +0 -63
- /batframework-1.0.9a7.dist-info/LICENCE → /batframework-1.0.9a9.dist-info/LICENSE +0 -0
- {batframework-1.0.9a7.dist-info → batframework-1.0.9a9.dist-info}/top_level.txt +0 -0
batFramework/gui/container.py
CHANGED
@@ -4,7 +4,6 @@ from .shape import Shape
|
|
4
4
|
from .interactiveWidget import InteractiveWidget
|
5
5
|
from .layout import Layout, Column
|
6
6
|
from typing import Self
|
7
|
-
import pygame
|
8
7
|
from pygame.math import Vector2
|
9
8
|
|
10
9
|
|
@@ -42,39 +41,37 @@ class Container(Shape, InteractiveWidget):
|
|
42
41
|
def scrollX_by(self, x: float | int) -> Self:
|
43
42
|
if x == 0:
|
44
43
|
return self
|
45
|
-
self.scroll.x
|
46
|
-
self.clamp_scroll()
|
47
|
-
self.dirty_layout = True
|
44
|
+
self.set_scroll((self.scroll.x + x, self.scroll.y))
|
48
45
|
return self
|
49
46
|
|
50
47
|
def scrollY_by(self, y: float | int) -> Self:
|
51
48
|
if y == 0:
|
52
49
|
return self
|
53
|
-
self.scroll.y
|
54
|
-
self.clamp_scroll()
|
55
|
-
self.dirty_layout = True
|
50
|
+
self.set_scroll((self.scroll.x, self.scroll.y + y))
|
56
51
|
return self
|
57
52
|
|
58
53
|
def scroll_by(self, value: tuple[float | int, float | int]) -> Self:
|
59
54
|
if value[0] == 0 and value[1] == 0:
|
60
55
|
return self
|
61
|
-
self.scroll
|
62
|
-
self.clamp_scroll()
|
63
|
-
self.dirty_layout = True
|
56
|
+
self.set_scroll((self.scroll.x + value[0], self.scroll.y + value[1]))
|
64
57
|
return self
|
65
58
|
|
66
59
|
def clamp_scroll(self) -> Self:
|
67
60
|
if not self.children:
|
68
61
|
return self
|
69
|
-
r = self.
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
62
|
+
r = self.get_inner_rect()
|
63
|
+
# Compute the bounding rect of all children in one go
|
64
|
+
children_rect = self.children[0].rect.copy()
|
65
|
+
for child in self.children[1:]:
|
66
|
+
children_rect.union_ip(child.rect)
|
67
|
+
max_scroll_x = max(0, children_rect.width - r.width)
|
68
|
+
max_scroll_y = max(0, children_rect.height - r.height)
|
69
|
+
|
70
|
+
# Clamp scroll values only if needed
|
71
|
+
new_x = min(max(self.scroll.x, 0), max_scroll_x)
|
72
|
+
new_y = min(max(self.scroll.y, 0), max_scroll_y)
|
73
|
+
|
74
|
+
self.set_scroll((new_x, new_y))
|
78
75
|
return self
|
79
76
|
|
80
77
|
def set_layout(self, layout: Layout) -> Self:
|
@@ -83,11 +80,15 @@ class Container(Shape, InteractiveWidget):
|
|
83
80
|
if self.layout != tmp:
|
84
81
|
tmp.set_parent(None)
|
85
82
|
self.layout.set_parent(self)
|
83
|
+
self.reset_scroll()
|
86
84
|
self.dirty_layout = True
|
87
85
|
return self
|
88
86
|
|
89
87
|
def get_interactive_children(self) -> list[InteractiveWidget]:
|
90
|
-
return [child for child in self.
|
88
|
+
return [child for child in self.get_layout_children() if isinstance(child, InteractiveWidget) and not isinstance(child,Container) and child.allow_focus_to_self()]
|
89
|
+
|
90
|
+
def get_layout_children(self)->list[Widget]:
|
91
|
+
return self.children
|
91
92
|
|
92
93
|
def clear_children(self) -> None:
|
93
94
|
self.children.clear()
|
@@ -96,13 +97,13 @@ class Container(Shape, InteractiveWidget):
|
|
96
97
|
def add(self, *child: Widget) -> Self:
|
97
98
|
super().add(*child)
|
98
99
|
self.dirty_shape = True
|
99
|
-
self.
|
100
|
+
self.dirty_layout = True
|
100
101
|
return self
|
101
102
|
|
102
103
|
def remove(self, *child: Widget) -> Self:
|
103
104
|
super().remove(*child)
|
104
105
|
self.dirty_shape = True
|
105
|
-
self.
|
106
|
+
self.dirty_layout = True
|
106
107
|
return self
|
107
108
|
|
108
109
|
def top_at(self, x: float | int, y: float | int) -> "None|Widget":
|
@@ -123,60 +124,78 @@ class Container(Shape, InteractiveWidget):
|
|
123
124
|
self.focused_index = min(self.focused_index, len(interactive_children) - 1)
|
124
125
|
return interactive_children[self.focused_index].get_focus()
|
125
126
|
|
127
|
+
def children_has_focus(self)->bool:
|
128
|
+
return any(child.is_focused for child in self.get_interactive_children())
|
129
|
+
|
126
130
|
def do_handle_event(self, event) -> None:
|
127
|
-
if
|
128
|
-
|
131
|
+
if event.consumed:
|
132
|
+
return
|
133
|
+
self.layout.handle_event(event)
|
129
134
|
|
130
135
|
def set_focused_child(self, child: InteractiveWidget) -> bool:
|
131
136
|
interactive_children = self.get_interactive_children()
|
132
137
|
try:
|
133
138
|
index = interactive_children.index(child)
|
134
139
|
self.focused_index = index
|
140
|
+
if self.layout :
|
141
|
+
self.layout.scroll_to_widget(child)
|
135
142
|
return True
|
136
143
|
except ValueError:
|
137
144
|
return False
|
138
145
|
|
139
146
|
def allow_focus_to_self(self) -> bool:
|
140
147
|
return bool(self.get_interactive_children()) and self.visible
|
148
|
+
|
149
|
+
def build(self) -> None:
|
150
|
+
if self.layout is not None:
|
151
|
+
# print("I'm building !",self)
|
152
|
+
# size = self.expand_rect_with_padding((0,0,*self.layout.get_auto_size())).size
|
153
|
+
size = self.layout.get_auto_size()
|
154
|
+
self.set_size(self.resolve_size(size))
|
155
|
+
super().build()
|
156
|
+
|
157
|
+
def apply_pre_updates(self):
|
158
|
+
if self.dirty_size_constraints or self.dirty_shape:
|
159
|
+
self.resolve_constraints(size_only=True)
|
160
|
+
self.dirty_size_constraints = False
|
161
|
+
self.dirty_position_constraints = True
|
141
162
|
|
163
|
+
if self.dirty_layout:
|
164
|
+
self.layout.update_child_constraints()
|
165
|
+
self.layout.arrange()
|
166
|
+
self.dirty_layout = False
|
142
167
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
self.resolve_constraints() # Finalize positioning based on size
|
149
|
-
|
150
|
-
# Step 1: Build shape if needed
|
168
|
+
def apply_post_updates(self,skip_draw:bool=False):
|
169
|
+
"""
|
170
|
+
BOTTOM TO TOP
|
171
|
+
for cases when widget attributes depend on children attributes
|
172
|
+
"""
|
151
173
|
if self.dirty_shape:
|
152
|
-
self.
|
174
|
+
self.layout.update_child_constraints()
|
175
|
+
self.build()
|
153
176
|
self.dirty_shape = False
|
154
|
-
self.dirty_surface = True
|
155
|
-
self.dirty_layout =
|
156
|
-
|
157
|
-
|
158
|
-
|
177
|
+
self.dirty_surface = True
|
178
|
+
self.dirty_layout = True
|
179
|
+
self.dirty_size_constraints = True
|
180
|
+
self.dirty_position_constraints = True
|
181
|
+
from .container import Container
|
182
|
+
if self.parent and isinstance(self.parent, Container):
|
183
|
+
self.parent.dirty_layout = True
|
184
|
+
self.parent.dirty_shape = True
|
159
185
|
|
160
|
-
|
161
|
-
|
186
|
+
# trigger layout or constraint updates in parent
|
187
|
+
|
162
188
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
self.
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
if self.dirty_constraints:
|
173
|
-
self.resolve_constraints() # Finalize positioning based on size
|
174
|
-
for child in self.children:
|
175
|
-
child.dirty_constraints = True # Children inherit updated positioning
|
176
|
-
self.dirty_constraints = False
|
177
|
-
|
178
|
-
# Step 4: Paint the surface if marked as dirty
|
179
|
-
if self.dirty_surface:
|
180
|
-
# print("PAINT !!")
|
189
|
+
# force recheck of constraints
|
190
|
+
|
191
|
+
|
192
|
+
if self.dirty_position_constraints:
|
193
|
+
self.resolve_constraints(position_only=True)
|
194
|
+
self.dirty_position_constraints= False
|
195
|
+
|
196
|
+
|
197
|
+
if self.dirty_surface and not skip_draw:
|
181
198
|
self.paint()
|
182
199
|
self.dirty_surface = False
|
200
|
+
|
201
|
+
|
batFramework/gui/debugger.py
CHANGED
@@ -2,6 +2,7 @@ from .label import Label
|
|
2
2
|
from typing import Self,Callable,Any
|
3
3
|
import batFramework as bf
|
4
4
|
import pygame
|
5
|
+
import sys
|
5
6
|
|
6
7
|
|
7
8
|
def convert_to_int(*args):
|
@@ -46,7 +47,7 @@ class Debugger(Label):
|
|
46
47
|
|
47
48
|
def set_parent_scene(self, scene) -> Self:
|
48
49
|
super().set_parent_scene(scene)
|
49
|
-
self.set_render_order(
|
50
|
+
self.set_render_order(sys.maxsize-100)
|
50
51
|
self.update_text()
|
51
52
|
return self
|
52
53
|
|
@@ -72,15 +73,19 @@ class Debugger(Label):
|
|
72
73
|
def update(self, dt: float) -> None:
|
73
74
|
if not self.parent_scene:
|
74
75
|
return
|
76
|
+
|
75
77
|
if bf.ResourceManager().get_sharedVar("debug_mode") != bf.debugMode.DEBUGGER:
|
76
78
|
self.set_visible(False)
|
77
79
|
return
|
80
|
+
|
78
81
|
self.set_visible(True)
|
79
82
|
self.refresh_counter = self.refresh_counter + (dt * 60)
|
83
|
+
|
80
84
|
if self.refresh_counter > self.refresh_rate:
|
81
85
|
self.refresh_counter = 0
|
82
86
|
self.update_text()
|
83
87
|
|
88
|
+
|
84
89
|
def __str__(self) -> str:
|
85
90
|
return "Debugger"
|
86
91
|
|
@@ -109,23 +114,13 @@ class BasicDebugger(FPSDebugger):
|
|
109
114
|
"Resolution", lambda: "x".join(str(i) for i in bf.const.RESOLUTION)
|
110
115
|
)
|
111
116
|
super().do_when_added()
|
112
|
-
parent_scene = self.parent_scene
|
113
|
-
|
114
117
|
self.add_dynamic("Mouse", pygame.mouse.get_pos)
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
self.add_dynamic(
|
122
|
-
"Hud",
|
123
|
-
lambda: convert_to_int(
|
124
|
-
*parent_scene.hud_camera.screen_to_world(pygame.mouse.get_pos())
|
125
|
-
),
|
126
|
-
)
|
127
|
-
self.add_dynamic("W. Ent.", lambda: parent_scene.get_world_entity_count())
|
128
|
-
self.add_dynamic("H. Ent.", lambda: parent_scene.get_hud_entity_count())
|
118
|
+
|
119
|
+
if not hasattr(self.parent_scene,"root"):
|
120
|
+
print("Debugger couldn't find 'root' widget in parent scene")
|
121
|
+
return
|
122
|
+
|
123
|
+
parent_scene = self.parent_scene
|
129
124
|
|
130
125
|
self.add_dynamic(
|
131
126
|
"Hover",
|
@@ -5,36 +5,40 @@ import pygame
|
|
5
5
|
|
6
6
|
class DraggableWidget(InteractiveWidget):
|
7
7
|
def __init__(self, *args, **kwargs) -> None:
|
8
|
-
self.drag_action = bf.Action("dragging").add_mouse_control(1).set_holding()
|
9
|
-
|
10
8
|
self.drag_start = None
|
11
9
|
self.offset = None
|
10
|
+
self.click_mask = [True,False,False,False,False]
|
11
|
+
self.is_dragged : bool = False # the widget is following the mouse AND the mouse is in the widget
|
12
|
+
self.is_dragged_outside : bool = False # the widget is following the mouse BUT the mouse is NOT in the widget
|
12
13
|
super().__init__(*args, **kwargs)
|
14
|
+
|
15
|
+
def set_click_mask(self,b1=0,b2=0,b3=0,b4=0,b5=0):
|
16
|
+
self.click_mask = [b1,b2,b3,b4,b5]
|
13
17
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
self.drag_action.reset()
|
19
|
-
|
18
|
+
def on_click_down(self, button):
|
19
|
+
super().on_click_down(button)
|
20
|
+
return any(i==j and i== True for i,j in zip(self.is_clicked_down,self.click_mask))
|
21
|
+
|
20
22
|
def do_on_drag(
|
21
|
-
self,
|
23
|
+
self, drag_start_pos: tuple[float, float], drag_end_pos: tuple[float, float]
|
22
24
|
) -> None:
|
23
|
-
|
25
|
+
new_pos = drag_end_pos[0] - self.offset[0], drag_end_pos[1] - self.offset[1]
|
26
|
+
if self.rect.topleft != new_pos:
|
27
|
+
self.set_position(*new_pos)
|
24
28
|
|
25
29
|
def update(self, dt: float):
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
self.is_dragged_outside = any(i==j and i== True for i,j in zip(pygame.mouse.get_pressed(5),self.click_mask))
|
31
|
+
self.is_dragged = any(i==j and i== True for i,j in zip(self.is_clicked_down,self.click_mask))
|
32
|
+
|
33
|
+
if self.is_dragged and self.is_dragged_outside:
|
34
|
+
x, y = self.parent_layer.camera.screen_to_world(pygame.mouse.get_pos())
|
35
|
+
if self.drag_start == None:
|
30
36
|
self.offset = x - self.rect.x, y - self.rect.y
|
31
37
|
self.drag_start = x, y
|
32
|
-
return
|
33
38
|
else:
|
34
39
|
self.do_on_drag(self.drag_start, (x, y))
|
35
|
-
return
|
36
40
|
else:
|
37
41
|
self.drag_start = None
|
38
42
|
self.offset = None
|
39
|
-
self.is_clicked_down = False
|
43
|
+
self.is_clicked_down = [False]*5
|
40
44
|
super().update(dt)
|
batFramework/gui/image.py
CHANGED
@@ -23,7 +23,7 @@ class Image(Shape):
|
|
23
23
|
super().paint()
|
24
24
|
if self.original_surface is None:
|
25
25
|
return
|
26
|
-
padded = self.
|
26
|
+
padded = self.get_inner_rect().move(-self.rect.x,-self.rect.y)
|
27
27
|
target_size = padded.size
|
28
28
|
if self.original_surface.get_size() != target_size:
|
29
29
|
self.surface.blit(pygame.transform.scale(self.original_surface, target_size), padded.topleft)
|
@@ -32,17 +32,11 @@ class Image(Shape):
|
|
32
32
|
|
33
33
|
def build(self) -> None:
|
34
34
|
if self.original_surface is not None:
|
35
|
-
self.
|
36
|
-
self.
|
35
|
+
self.set_size(
|
36
|
+
self.expand_rect_with_padding((0,0,*self.original_surface.get_size())).size
|
37
37
|
)
|
38
38
|
super().build()
|
39
39
|
|
40
|
-
def get_min_required_size(self) -> tuple[float, float]:
|
41
|
-
res = self.rect.size
|
42
|
-
return self.inflate_rect_by_padding((0, 0, *res)).size
|
43
|
-
|
44
|
-
|
45
|
-
|
46
40
|
|
47
41
|
def from_path(self, path: str) -> Self:
|
48
42
|
tmp = bf.ResourceManager().get_image(path, self.convert_alpha)
|
@@ -60,6 +54,5 @@ class Image(Shape):
|
|
60
54
|
self.original_surface = surface
|
61
55
|
size = self.original_surface.get_size()
|
62
56
|
self.set_size(size)
|
63
|
-
|
64
57
|
self.dirty_surface = True
|
65
58
|
return self
|
batFramework/gui/indicator.py
CHANGED
@@ -8,6 +8,12 @@ import batFramework as bf
|
|
8
8
|
|
9
9
|
|
10
10
|
class Indicator(Shape):
|
11
|
+
"""
|
12
|
+
Shape intended to be used as icons/indicators
|
13
|
+
due to its nature, it overrides the top_at function (it can not be 'seen' by the mouse)
|
14
|
+
|
15
|
+
"""
|
16
|
+
|
11
17
|
def __init__(self, size: tuple[int | float] = (10, 10)) -> None:
|
12
18
|
super().__init__(size)
|
13
19
|
self.debug_color = "magenta"
|
@@ -35,7 +41,7 @@ class ToggleIndicator(Indicator):
|
|
35
41
|
self.set_value(default_value)
|
36
42
|
self.callback(default_value)
|
37
43
|
# TODO aspect ratio would be good right about here
|
38
|
-
|
44
|
+
self.add_constraints(bf.gui.AspectRatio(1,reference_axis=bf.axis.VERTICAL))
|
39
45
|
|
40
46
|
def set_callback(self, callback : Callable[[bool],Any]) -> Self:
|
41
47
|
self.callback = callback
|
@@ -56,3 +62,52 @@ class ToggleIndicator(Indicator):
|
|
56
62
|
return None
|
57
63
|
return r
|
58
64
|
|
65
|
+
class ArrowIndicator(Indicator):
|
66
|
+
def __init__(self,direction:bf.direction):
|
67
|
+
super().__init__()
|
68
|
+
self.direction : bf.direction = direction
|
69
|
+
self.arrow_color = bf.color.WHITE
|
70
|
+
self.line_width : int = 1
|
71
|
+
|
72
|
+
def set_arrow_color(self,color)-> Self:
|
73
|
+
self.arrow_color = color
|
74
|
+
self.dirty_surface = True
|
75
|
+
return self
|
76
|
+
|
77
|
+
def set_arrow_direction(self,direction:bf.direction)->Self:
|
78
|
+
self.direction = direction
|
79
|
+
self.dirty_surface = True
|
80
|
+
return self
|
81
|
+
|
82
|
+
def set_arrow_line_width(self,value:int)->Self:
|
83
|
+
self.line_width = value
|
84
|
+
self.dirty_surface = True
|
85
|
+
return self
|
86
|
+
|
87
|
+
def paint(self):
|
88
|
+
super().paint()
|
89
|
+
r = self.get_local_inner_rect()
|
90
|
+
size = min(r.width, r.height)
|
91
|
+
if size %2 == 0:
|
92
|
+
size -= 1
|
93
|
+
r.width = size
|
94
|
+
r.height = size
|
95
|
+
|
96
|
+
#pixel alignment
|
97
|
+
if (self.padding[1]+self.padding[3] )%2 ==0:
|
98
|
+
r.height-=1
|
99
|
+
if (self.padding[0]+self.padding[2] )%2 ==0:
|
100
|
+
r.width-=1
|
101
|
+
r.center = self.get_local_inner_rect().center
|
102
|
+
|
103
|
+
bf.utils.draw_triangle(
|
104
|
+
surface = self.surface,
|
105
|
+
color = self.arrow_color,
|
106
|
+
rect =r,
|
107
|
+
direction = self.direction,
|
108
|
+
width = self.line_width
|
109
|
+
|
110
|
+
)
|
111
|
+
|
112
|
+
|
113
|
+
|