crystalwindow 4.6__py3-none-any.whl → 4.7__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.
- crystalwindow/FileHelper.py +159 -38
- crystalwindow/Icons/file_icons.png +0 -0
- crystalwindow/__init__.py +16 -27
- crystalwindow/assets.py +250 -59
- crystalwindow/clock.py +21 -63
- crystalwindow/color_handler.py +3 -21
- crystalwindow/crystal3d.py +135 -56
- crystalwindow/fun_helpers.py +4 -0
- crystalwindow/gametests/3dsquare.py +2 -2
- crystalwindow/gametests/gravitytest.py +96 -23
- crystalwindow/gametests/guitesting.py +4 -2
- crystalwindow/gametests/squaremove.py +4 -1
- crystalwindow/gametests/testtttagain.py +17 -0
- crystalwindow/gui.py +87 -17
- crystalwindow/gui_ext.py +51 -39
- crystalwindow/objects.py +171 -0
- crystalwindow/sprites.py +174 -25
- crystalwindow/tilemap.py +1 -1
- crystalwindow/websearch.py +91 -165
- crystalwindow/window.py +430 -12
- {crystalwindow-4.6.dist-info → crystalwindow-4.7.dist-info}/METADATA +6 -2
- crystalwindow-4.7.dist-info/RECORD +43 -0
- crystalwindow/cw_client.py +0 -95
- crystalwindow/player.py +0 -106
- crystalwindow-4.6.dist-info/RECORD +0 -42
- {crystalwindow-4.6.dist-info → crystalwindow-4.7.dist-info}/WHEEL +0 -0
- {crystalwindow-4.6.dist-info → crystalwindow-4.7.dist-info}/licenses/LICENSE +0 -0
- {crystalwindow-4.6.dist-info → crystalwindow-4.7.dist-info}/top_level.txt +0 -0
crystalwindow/crystal3d.py
CHANGED
|
@@ -1,61 +1,140 @@
|
|
|
1
|
-
import math
|
|
1
|
+
import math
|
|
2
2
|
|
|
3
|
+
def _lerp(a, b, t):
|
|
4
|
+
return a + (b - a) * t
|
|
5
|
+
|
|
6
|
+
class CW3DShape:
|
|
7
|
+
def __init__(self, kind, color, size=1.0, thickness=2, fill=None, texture=None,
|
|
8
|
+
points=5, inner_ratio=0.5):
|
|
9
|
+
"""
|
|
10
|
+
kind: 'cube', 'triangle', 'circle', 'rectangle', 'pentagon', 'star', 'ngon'
|
|
11
|
+
color: outline color (r,g,b)
|
|
12
|
+
size: scale factor
|
|
13
|
+
thickness: line thickness
|
|
14
|
+
fill: optional fill color
|
|
15
|
+
texture: optional texture placeholder
|
|
16
|
+
points: number of points (for stars/ngons)
|
|
17
|
+
inner_ratio: for stars, inner radius ratio (0.0-1.0)
|
|
18
|
+
"""
|
|
19
|
+
self.kind = kind
|
|
20
|
+
self.color = color
|
|
21
|
+
self.size = size
|
|
22
|
+
self.thickness = thickness
|
|
23
|
+
self.fill = fill
|
|
24
|
+
self.texture = texture
|
|
25
|
+
|
|
26
|
+
self.rot_x = 0
|
|
27
|
+
self.rot_y = 0
|
|
28
|
+
self.smooth = False
|
|
29
|
+
self.smooth_strength = 0.15
|
|
30
|
+
self._sx = 0
|
|
31
|
+
self._sy = 0
|
|
32
|
+
|
|
33
|
+
self.points = points
|
|
34
|
+
self.inner_ratio = inner_ratio
|
|
35
|
+
|
|
36
|
+
def spin(self, rx, ry):
|
|
37
|
+
self.rot_x += rx
|
|
38
|
+
self.rot_y += ry
|
|
39
|
+
|
|
40
|
+
def set_smooth(self, enable=True, strength=0.15):
|
|
41
|
+
self.smooth = enable
|
|
42
|
+
self.smooth_strength = strength
|
|
43
|
+
|
|
44
|
+
def _smoothed(self):
|
|
45
|
+
if not self.smooth:
|
|
46
|
+
return self.rot_x, self.rot_y
|
|
47
|
+
self._sx = _lerp(self._sx, self.rot_x, self.smooth_strength)
|
|
48
|
+
self._sy = _lerp(self._sy, self.rot_y, self.smooth_strength)
|
|
49
|
+
return self._sx, self._sy
|
|
50
|
+
|
|
51
|
+
# ----------------------------------------------------------
|
|
52
|
+
# DRAW ROUTE
|
|
53
|
+
# ----------------------------------------------------------
|
|
54
|
+
def draw(self, win):
|
|
55
|
+
w, h = win.width, win.height
|
|
56
|
+
rx, ry = self._smoothed()
|
|
57
|
+
|
|
58
|
+
if self.kind == "cube":
|
|
59
|
+
self._draw_cube(win, w, h, rx, ry)
|
|
60
|
+
elif self.kind == "triangle":
|
|
61
|
+
self._draw_triangle(win, w, h, rx, ry)
|
|
62
|
+
elif self.kind == "circle":
|
|
63
|
+
self._draw_circle(win, w, h, rx, ry)
|
|
64
|
+
elif self.kind == "rectangle":
|
|
65
|
+
self._draw_rectangle(win, w, h, rx, ry)
|
|
66
|
+
elif self.kind == "pentagon":
|
|
67
|
+
self._draw_polygon(win, w, h, 5, rx, ry)
|
|
68
|
+
elif self.kind == "star":
|
|
69
|
+
self._draw_star(win, w, h, rx, ry)
|
|
70
|
+
elif self.kind == "ngon":
|
|
71
|
+
self._draw_polygon(win, w, h, self.points, rx, ry)
|
|
72
|
+
|
|
73
|
+
# ----------------------------------------------------------
|
|
74
|
+
# GENERIC POLYGON
|
|
75
|
+
# ----------------------------------------------------------
|
|
76
|
+
def _draw_polygon(self, win, w, h, sides, rx, ry):
|
|
77
|
+
cx, cy = w//2, h//2
|
|
78
|
+
r = self.size * 80
|
|
79
|
+
verts = []
|
|
80
|
+
for i in range(sides):
|
|
81
|
+
angle = -math.pi/2 + i * 2 * math.pi / sides
|
|
82
|
+
verts.append((cx + math.cos(angle) * r, cy + math.sin(angle) * r))
|
|
83
|
+
if self.fill:
|
|
84
|
+
win.draw_polygon(verts, self.fill)
|
|
85
|
+
win.draw_polygon_outline(verts, self.color, self.thickness)
|
|
86
|
+
|
|
87
|
+
# ----------------------------------------------------------
|
|
88
|
+
# STAR
|
|
89
|
+
# ----------------------------------------------------------
|
|
90
|
+
def _draw_star(self, win, w, h, rx, ry):
|
|
91
|
+
cx, cy = w//2, h//2
|
|
92
|
+
outer_r = self.size * 80
|
|
93
|
+
inner_r = outer_r * self.inner_ratio
|
|
94
|
+
verts = []
|
|
95
|
+
step = math.pi / self.points
|
|
96
|
+
ang = -math.pi / 2
|
|
97
|
+
for i in range(self.points * 2):
|
|
98
|
+
r = outer_r if i % 2 == 0 else inner_r
|
|
99
|
+
verts.append((cx + math.cos(ang) * r, cy + math.sin(ang) * r))
|
|
100
|
+
ang += step
|
|
101
|
+
if self.fill:
|
|
102
|
+
win.draw_polygon(verts, self.fill)
|
|
103
|
+
win.draw_polygon_outline(verts, self.color, self.thickness)
|
|
104
|
+
|
|
105
|
+
# ==========================================================
|
|
106
|
+
# CW3D ENGINE (MANAGER)
|
|
107
|
+
# ==========================================================
|
|
3
108
|
class CW3D:
|
|
4
109
|
def __init__(self, win):
|
|
5
|
-
self.
|
|
6
|
-
self.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
self.
|
|
10
|
-
|
|
11
|
-
def
|
|
12
|
-
""
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def
|
|
27
|
-
""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
[-s, -s, s], [s, -s, s],
|
|
33
|
-
[s, s, s], [-s, s, s],
|
|
34
|
-
]
|
|
35
|
-
edges = [
|
|
36
|
-
(0,1),(1,2),(2,3),(3,0),
|
|
37
|
-
(4,5),(5,6),(6,7),(7,4),
|
|
38
|
-
(0,4),(1,5),(2,6),(3,7)
|
|
39
|
-
]
|
|
40
|
-
self.objects.append(("cube", points, edges, color))
|
|
110
|
+
self.win = win
|
|
111
|
+
self.shapes = []
|
|
112
|
+
|
|
113
|
+
def add_cube(self, size=1.0, color=(255,255,255), thickness=2, fill=None):
|
|
114
|
+
self.shapes.append(CW3DShape("cube", color, size, thickness, fill, None))
|
|
115
|
+
|
|
116
|
+
def add_triangle(self, size=1.0, color=(255,255,255), thickness=2, fill=None):
|
|
117
|
+
self.shapes.append(CW3DShape("triangle", color, size, thickness, fill, None))
|
|
118
|
+
|
|
119
|
+
def add_circle(self, size=1.0, color=(255,255,255), thickness=2):
|
|
120
|
+
self.shapes.append(CW3DShape("circle", color, size, thickness, None, None))
|
|
121
|
+
|
|
122
|
+
def add_rectangle(self, size=1.0, color=(255,255,255), thickness=2, fill=None):
|
|
123
|
+
self.shapes.append(CW3DShape("rectangle", color, size, thickness, fill, None))
|
|
124
|
+
|
|
125
|
+
def add_pentagon(self, size=1.0, color=(255,255,255), thickness=2, fill=None):
|
|
126
|
+
self.shapes.append(CW3DShape("pentagon", color, size, thickness, fill, None))
|
|
127
|
+
|
|
128
|
+
def add_star(self, size=1.0, color=(255,255,255), thickness=2, fill=None, points=5, inner_ratio=0.5):
|
|
129
|
+
self.shapes.append(CW3DShape("star", color, size, thickness, fill, None, points, inner_ratio))
|
|
130
|
+
|
|
131
|
+
def add_ngon(self, size=1.0, points=6, color=(255,255,255), thickness=2, fill=None):
|
|
132
|
+
self.shapes.append(CW3DShape("ngon", color, size, thickness, fill, None, points))
|
|
133
|
+
|
|
134
|
+
def spin(self, rx, ry):
|
|
135
|
+
for s in self.shapes:
|
|
136
|
+
s.spin(rx, ry)
|
|
41
137
|
|
|
42
138
|
def draw(self):
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for obj in self.objects:
|
|
46
|
-
_, pts, edges, color = obj
|
|
47
|
-
rotated = [self.rotate_point(*p) for p in pts]
|
|
48
|
-
for a, b in edges:
|
|
49
|
-
x1, y1 = self.project(*rotated[a])
|
|
50
|
-
x2, y2 = self.project(*rotated[b])
|
|
51
|
-
self.canvas.create_line(x1, y1, x2, y2, fill=color, width=2)
|
|
52
|
-
|
|
53
|
-
def spin(self, ax=0.02, ay=0.03):
|
|
54
|
-
self.angle_x += ax
|
|
55
|
-
self.angle_y += ay
|
|
56
|
-
|
|
57
|
-
def run(self):
|
|
58
|
-
"""Auto loop"""
|
|
59
|
-
self.draw()
|
|
60
|
-
self.spin()
|
|
61
|
-
self.canvas.after(16, self.run)
|
|
139
|
+
for s in self.shapes:
|
|
140
|
+
s.draw(self.win)
|
crystalwindow/fun_helpers.py
CHANGED
|
@@ -12,6 +12,10 @@ def random_name():
|
|
|
12
12
|
syllables = ["ka","zi","lo","ra","mi","to","na","ve"]
|
|
13
13
|
return "".join(random.choice(syllables) for _ in range(3))
|
|
14
14
|
|
|
15
|
+
#Choose a random number
|
|
16
|
+
def random_number(min_val, max_val):
|
|
17
|
+
return random.randint(min_val, max_val)
|
|
18
|
+
|
|
15
19
|
# tween helper
|
|
16
20
|
def lerp(a, b, t):
|
|
17
21
|
return a + (b - a) * t
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from crystalwindow import Window,
|
|
1
|
+
from crystalwindow import Window, CW3DShape, Clock
|
|
2
2
|
|
|
3
3
|
win = Window(800, 600, "CrystalWindow 3D Wavy Background")
|
|
4
4
|
clock = Clock()
|
|
5
|
-
cw3d =
|
|
5
|
+
cw3d = CW3DShape(win)
|
|
6
6
|
cw3d.add_cube(size=1.3, color="cyan")
|
|
7
7
|
|
|
8
8
|
# brightness wave vars
|
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
# --- custom player/ball class ---
|
|
4
|
-
class PlayerRect:
|
|
5
|
-
def __init__(self, x, y, w=32, h=32, color=(255,0,0)):
|
|
6
|
-
self.x = x
|
|
7
|
-
self.y = y
|
|
8
|
-
self.width = w
|
|
9
|
-
self.height = h
|
|
10
|
-
self.color = color
|
|
11
|
-
self.vel_y = 0
|
|
1
|
+
import sys
|
|
2
|
+
sys.path.insert(0, r"c:/Users/USUARIO/Desktop/crystalwindow")
|
|
12
3
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
win.draw_rect(self.color, (self.x, self.y, self.width, self.height))
|
|
4
|
+
from crystalwindow import *
|
|
5
|
+
import time
|
|
16
6
|
|
|
17
|
-
# ---
|
|
18
|
-
win = Window(800, 600, "
|
|
7
|
+
# --- window ---
|
|
8
|
+
win = Window(800, 600, "Debug Panel Test")
|
|
19
9
|
|
|
20
|
-
# --- player
|
|
21
|
-
player =
|
|
10
|
+
# --- player ---
|
|
11
|
+
player = Player(name="Player", pos=(0, 0), size=(32, 32), speed=4,hp=100)
|
|
12
|
+
enemy = Enemy((100, 100), 50, 50, 10, 0, color=(255, 0, 0))
|
|
22
13
|
|
|
23
|
-
# --- platform
|
|
14
|
+
# --- platform ---
|
|
24
15
|
class Platform:
|
|
25
16
|
def __init__(self, x, y, w, h, color=(100,200,100)):
|
|
26
17
|
self.x = x
|
|
@@ -33,15 +24,97 @@ class Platform:
|
|
|
33
24
|
|
|
34
25
|
platform = Platform(0, 500, 800, 50)
|
|
35
26
|
|
|
36
|
-
# ---
|
|
27
|
+
# --- gravity ---
|
|
37
28
|
player.gravity = Gravity(player, force=1, bouncy=True, bounce_strength=0.7)
|
|
38
29
|
|
|
39
|
-
#
|
|
30
|
+
# ==========================================
|
|
31
|
+
# DEBUG PANEL SYSTEM
|
|
32
|
+
# ==========================================
|
|
33
|
+
gui = GUIManager()
|
|
34
|
+
|
|
35
|
+
panel_open = False
|
|
36
|
+
fly_mode = False
|
|
37
|
+
|
|
38
|
+
# panel background (Sprite.rect)
|
|
39
|
+
debug_panel = Sprite.rect((20, 20), 220, 140, color=(0,0,0))
|
|
40
|
+
debug_panel.alpha = 160 # make it transparent
|
|
41
|
+
|
|
42
|
+
# BUTTON CALLBACKS
|
|
43
|
+
def toggle_fly():
|
|
44
|
+
global fly_mode
|
|
45
|
+
fly_mode = not fly_mode
|
|
46
|
+
fly_button.text = f"Fly: {'ON' if fly_mode else 'OFF'}"
|
|
47
|
+
|
|
48
|
+
# create buttons
|
|
49
|
+
fly_button = Button(rect=(35, 60, 180, 40),
|
|
50
|
+
text="Fly: OFF",
|
|
51
|
+
color=(150,150,150),
|
|
52
|
+
hover_color=(200,200,200),
|
|
53
|
+
callback=toggle_fly)
|
|
54
|
+
|
|
55
|
+
gui.add(fly_button)
|
|
56
|
+
|
|
57
|
+
cooldown = 0
|
|
58
|
+
|
|
59
|
+
# ==========================================
|
|
60
|
+
# MAIN UPDATE
|
|
61
|
+
# ==========================================
|
|
40
62
|
def update(win):
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
global panel_open, fly_mode, cooldown
|
|
64
|
+
|
|
65
|
+
win.fill((20,20,50))
|
|
66
|
+
|
|
67
|
+
# PANEL TOGGLE KEY
|
|
68
|
+
if win.key_pressed("f1") and time.time() > cooldown:
|
|
69
|
+
panel_open = not panel_open
|
|
70
|
+
cooldown = time.time() + 0.25
|
|
71
|
+
|
|
72
|
+
if win.key_pressed("f") and not panel_open and time.time() > cooldown:
|
|
73
|
+
fly_mode = not fly_mode
|
|
74
|
+
cooldown = time.time() + 0.2
|
|
75
|
+
|
|
76
|
+
# -------------------------------
|
|
77
|
+
# GAME LOGIC
|
|
78
|
+
# -------------------------------
|
|
79
|
+
if fly_mode:
|
|
80
|
+
# disable gravity
|
|
81
|
+
player.gravity.vel_y = 0
|
|
82
|
+
|
|
83
|
+
# free fly movement
|
|
84
|
+
if win.key_pressed("keyw"): player.y -= 6
|
|
85
|
+
if win.key_pressed("keys"): player.y += 6
|
|
86
|
+
if win.key_pressed("keya"): player.x -= 6
|
|
87
|
+
if win.key_pressed("keyd"): player.x += 6
|
|
88
|
+
else:
|
|
89
|
+
# normal gravity
|
|
90
|
+
player.gravity.update(1/60, [platform])
|
|
91
|
+
|
|
92
|
+
# jumping + movement
|
|
93
|
+
if win.key_pressed("keyw"): player.vel_y = -6
|
|
94
|
+
if win.key_pressed("keya"): player.x -= 10
|
|
95
|
+
if win.key_pressed("keyd"): player.x += 10
|
|
96
|
+
|
|
97
|
+
# draw world
|
|
43
98
|
player.draw(win)
|
|
44
99
|
platform.draw(win)
|
|
100
|
+
enemy.draw(win)
|
|
101
|
+
|
|
102
|
+
enemy.update(60)
|
|
103
|
+
|
|
104
|
+
if enemy.collide_with(player):
|
|
105
|
+
player.hp -= enemy.dmg
|
|
106
|
+
if player.dead:
|
|
107
|
+
player.respawn(win)
|
|
108
|
+
|
|
109
|
+
win.draw_text(f"CurrentHP: {player.hp}", font="Arial", size=16, color=(255, 255, 255))
|
|
110
|
+
# -------------------------------
|
|
111
|
+
# SHOW DEBUG PANEL
|
|
112
|
+
# -------------------------------
|
|
113
|
+
if panel_open:
|
|
114
|
+
debug_panel.draw(win) # panel bg
|
|
115
|
+
gui.draw(win) # buttons
|
|
116
|
+
gui.update(win) # button callbacks
|
|
117
|
+
|
|
45
118
|
|
|
46
119
|
win.run(update)
|
|
47
120
|
win.quit()
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
sys.path.insert(0, r"c:/Users/USUARIO/Desktop/crystalwindow")
|
|
3
|
+
|
|
1
4
|
from crystalwindow import *
|
|
2
5
|
|
|
6
|
+
|
|
3
7
|
# --- Window setup ---
|
|
4
8
|
win = Window(800, 600, "CrystalWindowLib Mega Sandbox")
|
|
5
9
|
|
|
@@ -49,8 +53,6 @@ def update(win):
|
|
|
49
53
|
# --- draw random name + color ---
|
|
50
54
|
win.draw_text(f"Random Name: {random_name()}", pos=(50, 420), size=20, color=random_color())
|
|
51
55
|
|
|
52
|
-
# --- debug overlay ---
|
|
53
|
-
debug.draw(win, fps=int(win.clock.get_fps()))
|
|
54
56
|
|
|
55
57
|
win.run(update)
|
|
56
58
|
win.quit()
|
|
@@ -6,7 +6,7 @@ PlayerCube = Sprite.rect((400, 300), 50, 50, color=(0,100,254))
|
|
|
6
6
|
def gamerun(win):
|
|
7
7
|
PlayerCube.draw(win)
|
|
8
8
|
|
|
9
|
-
win.draw_text("This is a crystalwindow test. (you can leave)", size=
|
|
9
|
+
win.draw_text("This is a crystalwindow test. (you can leave using ESC)", size=21, color=(255,255,255), pos=(25,25))
|
|
10
10
|
|
|
11
11
|
if win.key_pressed("keyw"):
|
|
12
12
|
PlayerCube.y -= 10
|
|
@@ -17,5 +17,8 @@ def gamerun(win):
|
|
|
17
17
|
if win.key_pressed("keyd"):
|
|
18
18
|
PlayerCube.x += 10
|
|
19
19
|
|
|
20
|
+
if win.key_pressed("escape"):
|
|
21
|
+
win.quit()
|
|
22
|
+
|
|
20
23
|
win.run(gamerun)
|
|
21
24
|
win.quit()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
sys.path.insert(0, r"c:/Users/USUARIO/Desktop/crystalwindow")
|
|
3
|
+
|
|
4
|
+
from crystalwindow import *
|
|
5
|
+
|
|
6
|
+
win = Window(800, 600, "Debug Panel Test")
|
|
7
|
+
|
|
8
|
+
# THIS WILL FAIL TO LOAD → fallback → checkerboard
|
|
9
|
+
game = load_image("SAKJDNSAOJDOSAJND;ONJSIOAJVO")
|
|
10
|
+
|
|
11
|
+
player = Sprite.image(game, (100, 100))
|
|
12
|
+
|
|
13
|
+
def gameruns(win):
|
|
14
|
+
player.draw(win)
|
|
15
|
+
|
|
16
|
+
win.run()
|
|
17
|
+
win.quit()
|
crystalwindow/gui.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import random
|
|
3
3
|
import tkinter as tk
|
|
4
4
|
from crystalwindow import *
|
|
5
|
+
import time
|
|
5
6
|
|
|
6
7
|
# ----------------- Color Helpers -----------------
|
|
7
8
|
def hex_to_rgb(hex_str):
|
|
@@ -15,28 +16,96 @@ def lerp_color(c1, c2, t):
|
|
|
15
16
|
|
|
16
17
|
# ----------------- GUI Elements -----------------
|
|
17
18
|
class Button:
|
|
18
|
-
def __init__(self, rect, text,
|
|
19
|
-
|
|
19
|
+
def __init__(self, rect, text,
|
|
20
|
+
color=(200,200,200),
|
|
21
|
+
hover_color=(255,255,255),
|
|
22
|
+
press_color=(180,180,180),
|
|
23
|
+
text_color=(0,0,0)):
|
|
24
|
+
|
|
25
|
+
self.rect = rect
|
|
20
26
|
self.text = text
|
|
27
|
+
|
|
21
28
|
self.color = color
|
|
22
29
|
self.hover_color = hover_color
|
|
23
|
-
self.
|
|
30
|
+
self.press_color = press_color
|
|
31
|
+
self.text_color = text_color
|
|
32
|
+
|
|
33
|
+
# States
|
|
24
34
|
self.hovered = False
|
|
35
|
+
self._held = False
|
|
36
|
+
self._clicked = False
|
|
37
|
+
self._released = False
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
self.hovered = x <= mx <= x+w and y <= my <= y+h
|
|
30
|
-
cur_color = self.hover_color if self.hovered else self.color
|
|
31
|
-
win.draw_rect(cur_color, self.rect)
|
|
32
|
-
win.draw_text(self.text, pos=(x+5, y+5))
|
|
39
|
+
self._was_down = False # mouse prev frame
|
|
40
|
+
self._cooldown = 0.05 # tiny debounce like Toggle
|
|
41
|
+
self._last_click = 0
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
# ======================================
|
|
44
|
+
# UPDATE (same style as slider/toggle)
|
|
45
|
+
# ======================================
|
|
46
|
+
def update(self, win):
|
|
35
47
|
mx, my = win.mouse_pos
|
|
36
48
|
x, y, w, h = self.rect
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
|
|
50
|
+
# Reset transient states
|
|
51
|
+
self._clicked = False
|
|
52
|
+
self._released = False
|
|
53
|
+
|
|
54
|
+
# Hover
|
|
55
|
+
self.hovered = (x <= mx <= x + w and y <= my <= y + h)
|
|
56
|
+
|
|
57
|
+
mouse_down = win.mouse_pressed(1)
|
|
58
|
+
|
|
59
|
+
# HELD
|
|
60
|
+
if self.hovered and mouse_down:
|
|
61
|
+
self._held = True
|
|
62
|
+
else:
|
|
63
|
+
self._held = False
|
|
64
|
+
|
|
65
|
+
# CLICKED (fires once)
|
|
66
|
+
now = time.time()
|
|
67
|
+
if self.hovered and mouse_down and not self._was_down:
|
|
68
|
+
if now - self._last_click >= self._cooldown:
|
|
69
|
+
self._clicked = True
|
|
70
|
+
self._last_click = now
|
|
71
|
+
|
|
72
|
+
# RELEASED (fires once)
|
|
73
|
+
if not mouse_down and self._was_down and self.hovered:
|
|
74
|
+
self._released = True
|
|
75
|
+
|
|
76
|
+
self._was_down = mouse_down
|
|
77
|
+
|
|
78
|
+
# ======================================
|
|
79
|
+
# PUBLIC GETTERS
|
|
80
|
+
# ======================================
|
|
81
|
+
def clicked(self):
|
|
82
|
+
return self._clicked
|
|
83
|
+
|
|
84
|
+
def held(self):
|
|
85
|
+
return self._held
|
|
86
|
+
|
|
87
|
+
def released(self):
|
|
88
|
+
return self._released
|
|
89
|
+
|
|
90
|
+
def hovering(self):
|
|
91
|
+
return self.hovered
|
|
92
|
+
|
|
93
|
+
# ======================================
|
|
94
|
+
# DRAW
|
|
95
|
+
# ======================================
|
|
96
|
+
def draw(self, win):
|
|
97
|
+
x, y, w, h = self.rect
|
|
98
|
+
|
|
99
|
+
# choose color by state
|
|
100
|
+
if self._held:
|
|
101
|
+
col = self.press_color
|
|
102
|
+
elif self.hovered:
|
|
103
|
+
col = self.hover_color
|
|
104
|
+
else:
|
|
105
|
+
col = self.color
|
|
106
|
+
|
|
107
|
+
win.draw_rect(col, (x, y, w, h))
|
|
108
|
+
win.draw_text(self.text, pos=(x + 5, y + 5), color=self.text_color)
|
|
40
109
|
|
|
41
110
|
class Label:
|
|
42
111
|
def __init__(self, pos, text, color=(255,255,255), font="Arial", size=16):
|
|
@@ -106,9 +175,10 @@ class GUIManager:
|
|
|
106
175
|
|
|
107
176
|
def draw(self, win):
|
|
108
177
|
for e in self.elements:
|
|
109
|
-
e
|
|
178
|
+
if hasattr(e, "draw"):
|
|
179
|
+
e.draw(win)
|
|
110
180
|
|
|
111
181
|
def update(self, win):
|
|
112
182
|
for e in self.elements:
|
|
113
|
-
if
|
|
114
|
-
e.
|
|
183
|
+
if hasattr(e, "update"):
|
|
184
|
+
e.update(win)
|