batframework 1.0.8a1__py3-none-any.whl → 1.0.8a2__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 +50 -53
- batFramework/action.py +105 -116
- batFramework/actionContainer.py +11 -53
- batFramework/animatedSprite.py +65 -115
- batFramework/audioManager.py +26 -70
- batFramework/camera.py +68 -253
- batFramework/constants.py +54 -16
- batFramework/cutscene.py +25 -34
- batFramework/cutsceneBlocks.py +42 -37
- batFramework/debugger.py +48 -0
- batFramework/dynamicEntity.py +7 -9
- batFramework/easing.py +71 -0
- batFramework/entity.py +98 -42
- batFramework/gui/__init__.py +2 -8
- batFramework/gui/button.py +79 -7
- batFramework/gui/constraints.py +204 -0
- batFramework/gui/container.py +31 -155
- batFramework/gui/debugger.py +43 -124
- batFramework/gui/frame.py +19 -0
- batFramework/gui/image.py +17 -41
- batFramework/gui/indicator.py +21 -41
- batFramework/gui/interactiveWidget.py +13 -116
- batFramework/gui/label.py +73 -278
- batFramework/gui/layout.py +61 -148
- batFramework/gui/root.py +37 -102
- batFramework/gui/shape.py +57 -258
- batFramework/gui/toggle.py +46 -97
- batFramework/gui/widget.py +254 -268
- batFramework/manager.py +19 -40
- batFramework/particles.py +77 -0
- batFramework/scene.py +107 -214
- batFramework/sceneManager.py +107 -150
- batFramework/stateMachine.py +0 -1
- batFramework/time.py +57 -117
- batFramework/transition.py +126 -184
- batFramework/transitionManager.py +0 -0
- batFramework/utils.py +161 -34
- batframework-1.0.8a2.dist-info/METADATA +58 -0
- batframework-1.0.8a2.dist-info/RECORD +42 -0
- {batframework-1.0.8a1.dist-info → batframework-1.0.8a2.dist-info}/WHEEL +1 -1
- batFramework/easingController.py +0 -58
- batFramework/enums.py +0 -104
- batFramework/fontManager.py +0 -65
- batFramework/gui/clickableWidget.py +0 -206
- batFramework/gui/constraints/__init__.py +0 -1
- batFramework/gui/constraints/constraints.py +0 -378
- batFramework/gui/dialogueBox.py +0 -96
- batFramework/gui/draggableWidget.py +0 -38
- batFramework/gui/meter.py +0 -76
- batFramework/gui/radioButton.py +0 -62
- batFramework/gui/slider.py +0 -220
- batFramework/gui/textInput.py +0 -134
- batFramework/object.py +0 -115
- batFramework/particle.py +0 -101
- batFramework/renderGroup.py +0 -62
- batFramework/resourceManager.py +0 -84
- batFramework/scrollingSprite.py +0 -113
- batFramework/sprite.py +0 -45
- batFramework/tileset.py +0 -46
- batframework-1.0.8a1.dist-info/LICENCE +0 -21
- batframework-1.0.8a1.dist-info/METADATA +0 -55
- batframework-1.0.8a1.dist-info/RECORD +0 -56
- {batframework-1.0.8a1.dist-info → batframework-1.0.8a2.dist-info}/top_level.txt +0 -0
batFramework/cutsceneBlocks.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import batFramework as bf
|
2
|
-
from .cutscene import Cutscene,
|
3
|
-
from .transition import *
|
2
|
+
from .cutscene import Cutscene,CutsceneManager
|
4
3
|
|
5
4
|
|
6
5
|
# Define the base CutsceneBlock class
|
@@ -18,18 +17,18 @@ class CutsceneBlock:
|
|
18
17
|
self.ended = False
|
19
18
|
self.started = False
|
20
19
|
|
21
|
-
def get_scene_at(self,
|
22
|
-
return bf.CutsceneManager().manager.
|
23
|
-
|
24
|
-
def set_scene(self, name, index=0):
|
25
|
-
return CutsceneManager().manager.set_scene(name, index)
|
20
|
+
def get_scene_at(self,index):
|
21
|
+
return bf.CutsceneManager().manager._scenes[index]
|
26
22
|
|
23
|
+
def set_scene(self,name,index=0):
|
24
|
+
return CutsceneManager().manager.set_scene(name,index)
|
25
|
+
|
27
26
|
def get_current_scene(self):
|
28
27
|
return CutsceneManager().manager.get_current_scene()
|
29
|
-
|
30
|
-
def get_scene(self,
|
28
|
+
|
29
|
+
def get_scene(self,name):
|
31
30
|
return CutsceneManager().manager.get_scene(name)
|
32
|
-
|
31
|
+
|
33
32
|
# Set the parent cutscene for this block
|
34
33
|
def set_parent_cutscene(self, parent):
|
35
34
|
"""
|
@@ -91,13 +90,17 @@ class ParallelBlock(CutsceneBlock):
|
|
91
90
|
Represents a parallel execution block for multiple Cutscene blocks.
|
92
91
|
"""
|
93
92
|
|
93
|
+
# Constructor for ParallelBlock, taking a variable number of blocks as arguments
|
94
94
|
def __init__(self, *blocks) -> None:
|
95
95
|
super().__init__()
|
96
96
|
# List of blocks to run in parallel
|
97
|
-
self.blocks: list[CutsceneBlock] =
|
97
|
+
self.blocks: list[CutsceneBlock] = blocks
|
98
98
|
|
99
99
|
# Start the parallel block (override the base class method)
|
100
100
|
def start(self):
|
101
|
+
"""
|
102
|
+
Start the parallel execution block.
|
103
|
+
"""
|
101
104
|
super().start()
|
102
105
|
# Start each block in parallel
|
103
106
|
for block in self.blocks:
|
@@ -105,14 +108,32 @@ class ParallelBlock(CutsceneBlock):
|
|
105
108
|
|
106
109
|
# Process an event for each block in parallel
|
107
110
|
def process_event(self, event):
|
111
|
+
"""
|
112
|
+
Process an event for each block in the parallel execution block.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
event: The event to be processed.
|
116
|
+
"""
|
108
117
|
_ = [b.process_event(event) for b in self.blocks]
|
109
118
|
|
110
119
|
# Update each block in parallel
|
111
120
|
def update(self, dt):
|
121
|
+
"""
|
122
|
+
Update each block in the parallel execution block.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
dt: Time elapsed since the last update.
|
126
|
+
"""
|
112
127
|
_ = [b.update(dt) for b in self.blocks]
|
113
128
|
|
114
129
|
# Check if all blocks have ended
|
115
130
|
def has_ended(self):
|
131
|
+
"""
|
132
|
+
Check if all blocks in the parallel execution block have ended.
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
bool: True if all blocks have ended, False otherwise.
|
136
|
+
"""
|
116
137
|
return all(b.has_ended() for b in self.blocks)
|
117
138
|
|
118
139
|
|
@@ -123,16 +144,15 @@ class SceneTransitionBlock(CutsceneBlock):
|
|
123
144
|
"""
|
124
145
|
|
125
146
|
# Constructor for SceneTransitionBlock
|
126
|
-
def __init__(
|
127
|
-
self, scene, transition: Transition = Fade(0.1), index: int = 0
|
128
|
-
) -> None:
|
147
|
+
def __init__(self, scene, transition, duration, **kwargs) -> None:
|
129
148
|
super().__init__()
|
130
149
|
# Target scene, transition type, duration, and additional keyword arguments
|
131
150
|
self.target_scene = scene
|
132
151
|
self.transition = transition
|
133
|
-
self.
|
152
|
+
self.duration = duration
|
153
|
+
self.kwargs = kwargs
|
134
154
|
# Timer to handle the end of the transition
|
135
|
-
self.timer = bf.Timer(
|
155
|
+
self.timer = bf.Timer(name = "scene_transition_block",duration=duration, end_callback=self.end)
|
136
156
|
|
137
157
|
# Start the scene transition block
|
138
158
|
def start(self):
|
@@ -140,32 +160,17 @@ class SceneTransitionBlock(CutsceneBlock):
|
|
140
160
|
Start the scene transition block.
|
141
161
|
"""
|
142
162
|
super().start()
|
163
|
+
print(f"transition to {scene}")
|
164
|
+
|
143
165
|
# Initiate the scene transition
|
144
|
-
if self.get_current_scene().
|
166
|
+
if self.get_current_scene()._name == self.target_scene:
|
145
167
|
self.end()
|
146
168
|
return
|
147
169
|
CutsceneManager().manager.transition_to_scene(
|
148
|
-
self.target_scene, self.transition, self.
|
170
|
+
self.target_scene, self.transition, duration=self.duration, **self.kwargs
|
149
171
|
)
|
150
172
|
# Start the timer to handle the end of the transition
|
151
173
|
self.timer.start()
|
152
174
|
|
153
|
-
|
154
|
-
|
155
|
-
def __init__(self, duration) -> None:
|
156
|
-
super().__init__()
|
157
|
-
self.timer = bf.Timer(duration=duration, end_callback=self.end)
|
158
|
-
|
159
|
-
def start(self):
|
160
|
-
super().start()
|
161
|
-
self.timer.start()
|
162
|
-
|
163
|
-
|
164
|
-
class FunctionBlock(CutsceneBlock):
|
165
|
-
def __init__(self, func) -> None:
|
166
|
-
self.function = func
|
167
|
-
|
168
|
-
def start(self):
|
169
|
-
super().start()
|
170
|
-
self.function()
|
171
|
-
self.end()
|
175
|
+
def end(self):
|
176
|
+
return super().end()
|
batFramework/debugger.py
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
import batFramework as bf
|
2
|
+
import pygame
|
3
|
+
|
4
|
+
|
5
|
+
class Debugger(bf.Label):
|
6
|
+
def __init__(self, manager) -> None:
|
7
|
+
super().__init__()
|
8
|
+
self.manager: bf.Manager = manager
|
9
|
+
self.refresh_rate = 0
|
10
|
+
self._refresh_counter = 0
|
11
|
+
self.dynamic_data = {}
|
12
|
+
self.static_data = {}
|
13
|
+
self.render_order = 99
|
14
|
+
self.set_outline_color((20, 20, 20))
|
15
|
+
# self.set_background_color((0,0,0,0))
|
16
|
+
self.set_text_color("white")
|
17
|
+
# self.set_padding((30,30))
|
18
|
+
self.set_refresh_rate(20)
|
19
|
+
self.add_dynamic_data("FPS", lambda: str(round(self.manager.get_fps())))
|
20
|
+
self.add_dynamic_data("BLITS", lambda: str(self.parent_scene.blit_calls))
|
21
|
+
self.set_visible(False)
|
22
|
+
|
23
|
+
def set_refresh_rate(self, val: int):
|
24
|
+
self.refresh_rate = val
|
25
|
+
|
26
|
+
def update(self, dt: float):
|
27
|
+
visible = self.manager._debugging == 1
|
28
|
+
self.set_visible(visible)
|
29
|
+
if not visible:
|
30
|
+
return
|
31
|
+
self._refresh_counter -= dt * 60
|
32
|
+
if self._refresh_counter < 0:
|
33
|
+
self._refresh_counter = self.refresh_rate
|
34
|
+
self._refresh_debug_info()
|
35
|
+
|
36
|
+
def add_dynamic_data(self, key, func):
|
37
|
+
self.dynamic_data[key] = func
|
38
|
+
|
39
|
+
def set_static_data(self, key, value):
|
40
|
+
self.static_data[key] = value
|
41
|
+
|
42
|
+
def _refresh_debug_info(self):
|
43
|
+
lines = []
|
44
|
+
lines.extend([f"{key}:{value}" for key, value in self.static_data.items()])
|
45
|
+
lines.extend([f"{key}:{func()}" for key, func in self.dynamic_data.items()])
|
46
|
+
debug_text = "\n".join(lines)
|
47
|
+
self.set_text(debug_text)
|
48
|
+
self.update_surface() # Update the surface after modifying the text
|
batFramework/dynamicEntity.py
CHANGED
@@ -2,15 +2,15 @@ import pygame
|
|
2
2
|
import batFramework as bf
|
3
3
|
from typing import Self
|
4
4
|
|
5
|
-
|
6
5
|
class DynamicEntity(bf.Entity):
|
7
6
|
def __init__(
|
8
7
|
self,
|
9
|
-
size: None
|
10
|
-
|
11
|
-
|
8
|
+
size : None|tuple[int,int]=None,
|
9
|
+
no_surface : bool =False,
|
10
|
+
surface_flags : int =0,
|
11
|
+
convert_alpha : bool=False
|
12
12
|
) -> None:
|
13
|
-
super().__init__(size,
|
13
|
+
super().__init__(size,no_surface,surface_flags,convert_alpha)
|
14
14
|
self.velocity = pygame.math.Vector2(0, 0)
|
15
15
|
|
16
16
|
def on_collideX(self, collider: Self):
|
@@ -19,7 +19,5 @@ class DynamicEntity(bf.Entity):
|
|
19
19
|
def on_collideY(self, collider: Self):
|
20
20
|
return False
|
21
21
|
|
22
|
-
def move_by_velocity(self
|
23
|
-
self.set_position(
|
24
|
-
self.rect.x + self.velocity.x * dt, self.rect.y + self.velocity.y * dt
|
25
|
-
)
|
22
|
+
def move_by_velocity(self)->None:
|
23
|
+
self.set_position(self.rect.x + self.velocity.x, self.rect.y + self.velocity.y)
|
batFramework/easing.py
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
import pygame
|
3
|
+
import batFramework as bf
|
4
|
+
|
5
|
+
class Easing(Enum):
|
6
|
+
EASE_IN = (0.12, 0, 0.39, 0)
|
7
|
+
EASE_OUT = (0.61, 1, 0.88, 1)
|
8
|
+
EASE_IN_OUT = (0.37, 0, 0.63, 1)
|
9
|
+
EASE_IN_OUT_ELASTIC = (.7,-0.5,.3,1.5)
|
10
|
+
LINEAR = (1, 1, 0, 0)
|
11
|
+
# Add more easing functions as needed
|
12
|
+
|
13
|
+
def __init__(self, *control_points):
|
14
|
+
self.control_points = control_points
|
15
|
+
|
16
|
+
class EasingAnimation(bf.Timer):
|
17
|
+
_cache = {}
|
18
|
+
def __init__(
|
19
|
+
self,
|
20
|
+
name:str=None,
|
21
|
+
easing_function:Easing=Easing.LINEAR,
|
22
|
+
duration:int=100,
|
23
|
+
update_callback=None,
|
24
|
+
end_callback=None,
|
25
|
+
loop:bool=False,
|
26
|
+
reusable:bool=False
|
27
|
+
):
|
28
|
+
self.easing_function = easing_function
|
29
|
+
self.update_callback = update_callback
|
30
|
+
self.value = 0.0
|
31
|
+
super().__init__(name,duration,loop,end_callback,reusable)
|
32
|
+
|
33
|
+
def get_value(self):
|
34
|
+
return self.value
|
35
|
+
|
36
|
+
def start(self):
|
37
|
+
self.value = 0
|
38
|
+
super().start() # self.elapsed_progress set to 0 here
|
39
|
+
|
40
|
+
def update(self)->bool:
|
41
|
+
if super().update():
|
42
|
+
return True# If timer ended now, end() is called. So don't process value.
|
43
|
+
self._process_value()
|
44
|
+
# if self.name == 0: print("UPDATING (callback) in easing")
|
45
|
+
if self.update_callback: self.update_callback(self.value)
|
46
|
+
return False
|
47
|
+
|
48
|
+
def end(self):
|
49
|
+
# Call update 1 last time with the last value
|
50
|
+
|
51
|
+
self.elapsed_progress = 1
|
52
|
+
self._process_value()
|
53
|
+
if self.update_callback: self.update_callback(self.value)
|
54
|
+
self.value = 0
|
55
|
+
super().end() # sets elapsed_progress to 0
|
56
|
+
|
57
|
+
def _process_value(self):
|
58
|
+
p0, p1, p2, p3 = self.easing_function.control_points
|
59
|
+
cache_key = (self.elapsed_progress, p0, p1, p2, p3)
|
60
|
+
if cache_key in EasingAnimation._cache:
|
61
|
+
y = EasingAnimation._cache[cache_key]
|
62
|
+
else:
|
63
|
+
t = self.elapsed_progress
|
64
|
+
t_inv = 1.0 - t
|
65
|
+
t2 = t * t
|
66
|
+
t3 = t * t2
|
67
|
+
t_inv2 = t_inv * t_inv
|
68
|
+
|
69
|
+
y = 3 * t_inv2 * t * p1 + 3 * t_inv * t2 * p3 + t3
|
70
|
+
EasingAnimation._cache[cache_key] = y
|
71
|
+
self.value = y
|
batFramework/entity.py
CHANGED
@@ -1,67 +1,123 @@
|
|
1
|
-
from typing import Any, Self
|
2
1
|
import pygame
|
3
2
|
import batFramework as bf
|
4
|
-
from
|
3
|
+
from typing import Any
|
5
4
|
|
6
|
-
class Entity
|
7
|
-
|
8
|
-
Basic entity class
|
9
|
-
"""
|
5
|
+
class Entity:
|
6
|
+
instance_num = 0
|
10
7
|
def __init__(
|
11
8
|
self,
|
12
|
-
size: None
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
**kwargs,
|
9
|
+
size : None|tuple[int,int]=None,
|
10
|
+
no_surface : bool =False,
|
11
|
+
surface_flags : int =0,
|
12
|
+
convert_alpha : bool=False
|
17
13
|
) -> None:
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
14
|
+
self.convert_alpha = convert_alpha
|
15
|
+
if size is None:
|
16
|
+
size= (100,100)
|
17
|
+
|
18
|
+
if no_surface:
|
19
|
+
self.surface = None
|
20
|
+
else:
|
21
|
+
self.surface = (pygame.Surface(size, surface_flags))
|
22
|
+
|
23
|
+
if convert_alpha and self.surface is not None:
|
26
24
|
self.surface = self.surface.convert_alpha()
|
27
|
-
self.surface.fill((0,
|
25
|
+
self.surface.fill((0,0,0,0))
|
28
26
|
|
29
|
-
|
30
|
-
self.
|
31
|
-
|
27
|
+
self.uid: Any = Entity.instance_num
|
28
|
+
self.tags: list[str] = []
|
29
|
+
self.parent_scene: bf.Scene | None = None
|
30
|
+
self.rect = pygame.FRect(0, 0, *size)
|
31
|
+
|
32
|
+
self.visible = True
|
33
|
+
self._debug_color : tuple = bf.color.DARK_RED
|
34
|
+
self.render_order = 0
|
35
|
+
self.z_depth = 1
|
36
|
+
Entity.instance_num += 1
|
37
|
+
|
38
|
+
def get_bounding_box(self):
|
39
|
+
yield (self.rect,self._debug_color)
|
32
40
|
|
33
|
-
def
|
34
|
-
|
41
|
+
def set_debug_color(self, color):
|
42
|
+
self._debug_color = color
|
43
|
+
|
44
|
+
def set_visible(self, value: bool):
|
45
|
+
self.visible = value
|
35
46
|
|
36
|
-
def
|
37
|
-
self.
|
47
|
+
def set_parent_scene(self, scene):
|
48
|
+
self.parent_scene = scene
|
49
|
+
|
50
|
+
def do_when_added(self):
|
51
|
+
pass
|
52
|
+
|
53
|
+
def do_when_removed(self):
|
54
|
+
pass
|
55
|
+
|
56
|
+
def set_position(self, x, y):
|
57
|
+
self.rect.topleft = (x, y)
|
38
58
|
return self
|
39
59
|
|
40
|
-
def
|
41
|
-
self.
|
60
|
+
def set_x(self,x):
|
61
|
+
self.rect.x = x
|
42
62
|
return self
|
43
63
|
|
44
|
-
def
|
45
|
-
self.
|
64
|
+
def set_y(self,y):
|
65
|
+
self.rect.y = y
|
46
66
|
return self
|
47
67
|
|
48
|
-
def
|
49
|
-
self.
|
50
|
-
if self.parent_scene:
|
51
|
-
self.parent_scene.sort_entities()
|
68
|
+
def set_center(self, x, y):
|
69
|
+
self.rect.center = (x, y)
|
52
70
|
return self
|
53
71
|
|
54
|
-
def
|
55
|
-
self.
|
72
|
+
def set_uid(self, uid):
|
73
|
+
self.uid = uid
|
56
74
|
return self
|
57
75
|
|
58
|
-
def
|
76
|
+
def add_tag(self, *tags):
|
77
|
+
for tag in tags:
|
78
|
+
if tag not in self.tags:
|
79
|
+
self.tags.append(tag)
|
80
|
+
self.tags.sort()
|
81
|
+
return self
|
82
|
+
|
83
|
+
def remove_tag(self, *tags):
|
84
|
+
self.tags = [tag for tag in self.tags if tag not in tags]
|
85
|
+
self.tags.sort()
|
86
|
+
|
87
|
+
def has_tag(self, tag) -> bool:
|
88
|
+
return tag in self.tags
|
89
|
+
|
90
|
+
def process_event(self, event: pygame.Event)->bool:
|
59
91
|
"""
|
60
|
-
|
92
|
+
Returns bool : True if the method is blocking (no propagation to next children of the scene)
|
61
93
|
"""
|
62
|
-
|
94
|
+
self.do_process_actions(event)
|
95
|
+
res = self.do_handle_event(event)
|
96
|
+
self.do_reset_actions()
|
97
|
+
return res
|
98
|
+
|
99
|
+
def do_process_actions(self,event : pygame.Event)->None:
|
100
|
+
pass
|
101
|
+
|
102
|
+
def do_reset_actions(self)->None:
|
103
|
+
pass
|
104
|
+
|
105
|
+
def do_handle_event(self, event: pygame.Event) -> bool:
|
106
|
+
return False
|
107
|
+
|
108
|
+
def update(self, dt: float):
|
109
|
+
self.do_update(dt)
|
110
|
+
|
111
|
+
def do_update(self,dt:float):
|
112
|
+
pass
|
113
|
+
|
114
|
+
def draw(self, camera: bf.Camera) -> int:
|
115
|
+
if not self.visible:
|
116
|
+
return False
|
117
|
+
if not self.surface or not camera.intersects(self.rect):
|
118
|
+
return False
|
63
119
|
camera.surface.blit(
|
64
120
|
self.surface,
|
65
|
-
camera.
|
66
|
-
special_flags=self.blit_flags,
|
121
|
+
tuple(round(i * self.z_depth) for i in camera.transpose(self.rect).topleft),
|
67
122
|
)
|
123
|
+
return True
|
batFramework/gui/__init__.py
CHANGED
@@ -2,19 +2,13 @@ from .constraints import *
|
|
2
2
|
from .widget import Widget
|
3
3
|
from .image import Image
|
4
4
|
from .interactiveWidget import InteractiveWidget
|
5
|
-
from .draggableWidget import DraggableWidget
|
6
|
-
from .clickableWidget import ClickableWidget
|
7
5
|
from .root import Root
|
8
6
|
from .shape import Shape
|
9
|
-
from .
|
7
|
+
from .frame import Frame
|
10
8
|
from .label import Label
|
11
|
-
from .dialogueBox import DialogueBox
|
12
|
-
from .textInput import TextInput
|
13
9
|
from .button import Button
|
14
|
-
from .debugger import
|
10
|
+
from .debugger import Debugger
|
15
11
|
from .layout import *
|
16
12
|
from .container import Container
|
17
13
|
from .indicator import *
|
18
14
|
from .toggle import Toggle
|
19
|
-
from .radioButton import RadioButton,RadioVariable
|
20
|
-
from .slider import Slider
|
batFramework/gui/button.py
CHANGED
@@ -1,12 +1,84 @@
|
|
1
1
|
from .label import Label
|
2
2
|
import batFramework as bf
|
3
|
-
from
|
4
|
-
from
|
3
|
+
from types import FunctionType
|
4
|
+
from typing import Self
|
5
|
+
from .interactiveWidget import InteractiveWidget
|
5
6
|
import pygame
|
6
|
-
from math import ceil
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
class Button(Label,InteractiveWidget):
|
9
|
+
_cache = {}
|
10
|
+
|
11
|
+
def __init__(self, text: str, callback : FunctionType =None) -> None:
|
12
|
+
# Label.__init__(self,text)
|
13
|
+
self.callback = callback
|
14
|
+
self.click_action = bf.Action("click").add_mouse_control(1)
|
15
|
+
self.hover_action = bf.Action("hover").add_mouse_control(pygame.MOUSEMOTION)
|
16
|
+
self.is_hovered : bool = False
|
17
|
+
self.is_clicking : bool = False
|
11
18
|
super().__init__(text=text)
|
12
|
-
self.
|
19
|
+
self.set_debug_color("cyan")
|
20
|
+
|
21
|
+
|
22
|
+
def set_callback(self,callback : FunctionType = None)->Self:
|
23
|
+
self.callback = callback
|
24
|
+
return self
|
25
|
+
|
26
|
+
def on_get_focus(self):
|
27
|
+
super().on_get_focus()
|
28
|
+
self.build()
|
29
|
+
|
30
|
+
def on_lose_focus(self):
|
31
|
+
super().on_lose_focus()
|
32
|
+
self.build()
|
33
|
+
|
34
|
+
def to_string_id(self)->str:
|
35
|
+
return f"Button({self._text})"
|
36
|
+
|
37
|
+
def click(self)->None:
|
38
|
+
if self.callback and not self.is_clicking:
|
39
|
+
self.is_clicking = True
|
40
|
+
self.callback()
|
41
|
+
self.is_clicking = False
|
42
|
+
|
43
|
+
|
44
|
+
def do_process_actions(self,event):
|
45
|
+
self.click_action.process_event(event)
|
46
|
+
self.hover_action.process_event(event)
|
47
|
+
|
48
|
+
def do_reset_actions(self):
|
49
|
+
self.click_action.reset()
|
50
|
+
self.hover_action.reset()
|
51
|
+
|
52
|
+
def do_handle_event(self,event)->None:
|
53
|
+
res = False
|
54
|
+
if self.click_action.is_active():
|
55
|
+
root = self.get_root()
|
56
|
+
if root.hovered == self:
|
57
|
+
if not self.is_focused : self.get_focus()
|
58
|
+
self.click()
|
59
|
+
elif self.hover_action.is_active():
|
60
|
+
root = self.get_root()
|
61
|
+
if root:
|
62
|
+
if self.is_hovered and root.hovered != self:
|
63
|
+
self.is_hovered = False
|
64
|
+
self.build()
|
65
|
+
if not self.is_hovered and root.hovered == self:
|
66
|
+
self.is_hovered = True
|
67
|
+
self.build()
|
68
|
+
return res
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
def build(self)->None:
|
73
|
+
super().build()
|
74
|
+
if self.is_hovered:
|
75
|
+
hover_surf = Button._cache.get(self.surface.get_size(),None)
|
76
|
+
if hover_surf is None:
|
77
|
+
hover_surf = pygame.Surface(self.surface.get_size()).convert_alpha()
|
78
|
+
hover_surf.fill((30,30,30,0))
|
79
|
+
Button._cache[self.surface.get_size()] = hover_surf
|
80
|
+
self.surface.blit(hover_surf,(0,0),special_flags = pygame.BLEND_ADD)
|
81
|
+
|
82
|
+
|
83
|
+
def apply_contraints(self)->None:
|
84
|
+
super().apply_contraints()
|