VertexEngine 1.2rc4__4-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.
- VertexEngine/AdvancedVWidgets/ChekBox.py +8 -0
- VertexEngine/AdvancedVWidgets/Dropdown.py +12 -0
- VertexEngine/AdvancedVWidgets/List.py +12 -0
- VertexEngine/AdvancedVWidgets/WindowMods.py +71 -0
- VertexEngine/AdvancedVWidgets/Windows.py +66 -0
- VertexEngine/InputSystem/BtnInputs.py +63 -0
- VertexEngine/InputSystem/Events.py +73 -0
- VertexEngine/InputSystem/KeyInputs.py +64 -0
- VertexEngine/SimpleGUI.py +142 -0
- VertexEngine/Vertex/__init__.py +224 -0
- VertexEngine/VertexScreenModifiers.py +70 -0
- VertexEngine/__init__.py +322 -0
- VertexEngine/assets.py +63 -0
- VertexEngine/audio.py +25 -0
- VertexEngine/engine.py +70 -0
- VertexEngine/example.py +59 -0
- VertexEngine/examples/games/Dodge/main.py +233 -0
- VertexEngine/examples/games/platformer/main.py +205 -0
- VertexEngine/examples/games/snake/main.py +135 -0
- VertexEngine/examples/test.py +55 -0
- VertexEngine/scenes.py +46 -0
- vertexengine-1.2.0rc4.dist-info/METADATA +100 -0
- vertexengine-1.2.0rc4.dist-info/RECORD +26 -0
- vertexengine-1.2.0rc4.dist-info/WHEEL +5 -0
- vertexengine-1.2.0rc4.dist-info/licenses/LICENSE +21 -0
- vertexengine-1.2.0rc4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""VertexEngine.Vertex: VWidget, Layouts, Dialogs, App Management"""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from PyQt6.QtWidgets import (
|
|
5
|
+
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
|
6
|
+
QDialog, QLabel, QPushButton, QLineEdit
|
|
7
|
+
)
|
|
8
|
+
from PyQt6.QtCore import Qt
|
|
9
|
+
|
|
10
|
+
# ------------------------
|
|
11
|
+
# Base widget wrapper
|
|
12
|
+
# ------------------------
|
|
13
|
+
class VWidget(QWidget):
|
|
14
|
+
"""Base widget wrapper. Handles dark mode and size; no layout by default."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, parent=None, width=None, height=None, dark_mode=False):
|
|
17
|
+
super().__init__(parent)
|
|
18
|
+
if width and height:
|
|
19
|
+
self.setFixedSize(width, height)
|
|
20
|
+
self.dark_mode = dark_mode
|
|
21
|
+
if dark_mode:
|
|
22
|
+
self.set_dark_mode(True)
|
|
23
|
+
|
|
24
|
+
def set_dark_mode(self, enabled=True):
|
|
25
|
+
self.dark_mode = enabled
|
|
26
|
+
if enabled:
|
|
27
|
+
self.setStyleSheet("""
|
|
28
|
+
QWidget {
|
|
29
|
+
background-color: #2c3e50;
|
|
30
|
+
color: #ecf0f1;
|
|
31
|
+
}
|
|
32
|
+
QPushButton {
|
|
33
|
+
background-color: #2980b9;
|
|
34
|
+
color: white;
|
|
35
|
+
}
|
|
36
|
+
""")
|
|
37
|
+
else:
|
|
38
|
+
self.setStyleSheet("")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ------------------------
|
|
42
|
+
# Layout wrappers
|
|
43
|
+
# ------------------------
|
|
44
|
+
class VBox(VWidget):
|
|
45
|
+
"""Vertical layout wrapper."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, *args, padding=10, margin=10, **kwargs):
|
|
48
|
+
super().__init__(*args, **kwargs)
|
|
49
|
+
self.layout = QVBoxLayout()
|
|
50
|
+
self.layout.setContentsMargins(margin, margin, margin, margin)
|
|
51
|
+
self.layout.setSpacing(padding)
|
|
52
|
+
self.setLayout(self.layout)
|
|
53
|
+
|
|
54
|
+
def add(self, widget):
|
|
55
|
+
self.layout.addWidget(widget)
|
|
56
|
+
|
|
57
|
+
def clear(self):
|
|
58
|
+
while self.layout.count():
|
|
59
|
+
child = self.layout.takeAt(0)
|
|
60
|
+
if child.widget():
|
|
61
|
+
child.widget().deleteLater()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class HBox(VWidget):
|
|
65
|
+
"""Horizontal layout wrapper."""
|
|
66
|
+
|
|
67
|
+
def __init__(self, *args, padding=10, margin=10, **kwargs):
|
|
68
|
+
super().__init__(*args, **kwargs)
|
|
69
|
+
self.layout = QHBoxLayout()
|
|
70
|
+
self.layout.setContentsMargins(margin, margin, margin, margin)
|
|
71
|
+
self.layout.setSpacing(padding)
|
|
72
|
+
self.setLayout(self.layout)
|
|
73
|
+
|
|
74
|
+
def add(self, widget):
|
|
75
|
+
self.layout.addWidget(widget)
|
|
76
|
+
|
|
77
|
+
def clear(self):
|
|
78
|
+
while self.layout.count():
|
|
79
|
+
child = self.layout.takeAt(0)
|
|
80
|
+
if child.widget():
|
|
81
|
+
child.widget().deleteLater()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ------------------------
|
|
85
|
+
# Application wrapper
|
|
86
|
+
# ------------------------
|
|
87
|
+
class App(QApplication):
|
|
88
|
+
def __init__(self):
|
|
89
|
+
super().__init__(sys.argv)
|
|
90
|
+
|
|
91
|
+
def run(self):
|
|
92
|
+
sys.exit(self.exec())
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ------------------------
|
|
96
|
+
# Dialog base class
|
|
97
|
+
# ------------------------
|
|
98
|
+
class Dialog(QDialog):
|
|
99
|
+
def __init__(self, parent=None, title="Dialog", size=(400, 200), modal=True):
|
|
100
|
+
super().__init__(parent)
|
|
101
|
+
self.setWindowTitle(title)
|
|
102
|
+
self.resize(*size)
|
|
103
|
+
self.setModal(modal)
|
|
104
|
+
self.setWindowFlags(self.windowFlags() | Qt.WindowType.Window)
|
|
105
|
+
|
|
106
|
+
def open_dialog(self):
|
|
107
|
+
self.exec()
|
|
108
|
+
|
|
109
|
+
def close_dialog(self):
|
|
110
|
+
self.close()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ------------------------
|
|
114
|
+
# Message dialog
|
|
115
|
+
# ------------------------
|
|
116
|
+
class MessageDialog(Dialog):
|
|
117
|
+
def __init__(self, parent=None, message="", title="Message"):
|
|
118
|
+
super().__init__(parent, title, (300, 150))
|
|
119
|
+
layout = QVBoxLayout()
|
|
120
|
+
self.setLayout(layout)
|
|
121
|
+
|
|
122
|
+
label = QLabel(message)
|
|
123
|
+
layout.addWidget(label)
|
|
124
|
+
|
|
125
|
+
ok = QPushButton("OK")
|
|
126
|
+
ok.clicked.connect(self.accept)
|
|
127
|
+
layout.addWidget(ok)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# ------------------------
|
|
131
|
+
# Confirm dialog
|
|
132
|
+
# ------------------------
|
|
133
|
+
class ConfirmDialog(Dialog):
|
|
134
|
+
def __init__(self, parent=None, message="", title="Confirm"):
|
|
135
|
+
super().__init__(parent, title, (350, 160))
|
|
136
|
+
layout = QVBoxLayout()
|
|
137
|
+
self.setLayout(layout)
|
|
138
|
+
|
|
139
|
+
layout.addWidget(QLabel(message))
|
|
140
|
+
|
|
141
|
+
buttons = QHBoxLayout()
|
|
142
|
+
yes = QPushButton("Yes")
|
|
143
|
+
no = QPushButton("No")
|
|
144
|
+
yes.clicked.connect(self.accept)
|
|
145
|
+
no.clicked.connect(self.reject)
|
|
146
|
+
buttons.addWidget(yes)
|
|
147
|
+
buttons.addWidget(no)
|
|
148
|
+
layout.addLayout(buttons)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def ask(parent=None, message="", title="Confirm"):
|
|
152
|
+
dlg = ConfirmDialog(parent, message, title)
|
|
153
|
+
return dlg.exec() == QDialog.DialogCode.Accepted
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ------------------------
|
|
157
|
+
# Input dialog
|
|
158
|
+
# ------------------------
|
|
159
|
+
class InputDialog(Dialog):
|
|
160
|
+
def __init__(self, parent=None, label="Enter:", title="Input"):
|
|
161
|
+
super().__init__(parent, title, (350, 180))
|
|
162
|
+
self.input = QLineEdit()
|
|
163
|
+
|
|
164
|
+
layout = QVBoxLayout()
|
|
165
|
+
self.setLayout(layout)
|
|
166
|
+
|
|
167
|
+
layout.addWidget(QLabel(label))
|
|
168
|
+
layout.addWidget(self.input)
|
|
169
|
+
|
|
170
|
+
ok = QPushButton("OK")
|
|
171
|
+
ok.clicked.connect(self.accept)
|
|
172
|
+
layout.addWidget(ok)
|
|
173
|
+
|
|
174
|
+
def value(self):
|
|
175
|
+
return self.input.text()
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def get(parent=None, label="Enter:", title="Input"):
|
|
179
|
+
dlg = InputDialog(parent, label, title)
|
|
180
|
+
if dlg.exec():
|
|
181
|
+
return dlg.value()
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ------------------------
|
|
186
|
+
# Static dialog helper
|
|
187
|
+
# ------------------------
|
|
188
|
+
class Dialogs:
|
|
189
|
+
@staticmethod
|
|
190
|
+
def message(parent=None, text=""):
|
|
191
|
+
MessageDialog(parent, text).open_dialog()
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def confirm(parent=None, text=""):
|
|
195
|
+
return ConfirmDialog.ask(parent, text)
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def input(parent=None, text=""):
|
|
199
|
+
return InputDialog.get(parent, text)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# ------------------------
|
|
203
|
+
# Example usage
|
|
204
|
+
# ------------------------
|
|
205
|
+
if __name__ == "__main__":
|
|
206
|
+
app = App()
|
|
207
|
+
|
|
208
|
+
main = VBox(dark_mode=True, width=500, height=400)
|
|
209
|
+
main.add(QLabel("Welcome to the VWidget wrapper!"))
|
|
210
|
+
|
|
211
|
+
btn_msg = QPushButton("Show Message")
|
|
212
|
+
btn_msg.clicked.connect(lambda: Dialogs.message(main, "Hello!"))
|
|
213
|
+
main.add(btn_msg)
|
|
214
|
+
|
|
215
|
+
btn_confirm = QPushButton("Ask Confirm")
|
|
216
|
+
btn_confirm.clicked.connect(lambda: print(Dialogs.confirm(main, "Are you sure?")))
|
|
217
|
+
main.add(btn_confirm)
|
|
218
|
+
|
|
219
|
+
btn_input = QPushButton("Ask Input")
|
|
220
|
+
btn_input.clicked.connect(lambda: print(Dialogs.input(main, "Enter your name:")))
|
|
221
|
+
main.add(btn_input)
|
|
222
|
+
|
|
223
|
+
main.show()
|
|
224
|
+
app.run()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""WARNING: This library of Vertex is not made for games, if you want to make a game window, use the `GameEngine` Class instead."""
|
|
2
|
+
from PyQt6.QtWidgets import QMainWindow, QVBoxLayout, QWidget, QApplication
|
|
3
|
+
import sys
|
|
4
|
+
from VertexEngine import VertexUI
|
|
5
|
+
# ===== Base window class =====
|
|
6
|
+
class BaseWindow(QMainWindow):
|
|
7
|
+
def __init__(self, title="Base Window", width=400, height=300):
|
|
8
|
+
super().__init__()
|
|
9
|
+
self.setWindowTitle(title)
|
|
10
|
+
self.setGeometry(100, 100, width, height)
|
|
11
|
+
self.central_widget = QWidget()
|
|
12
|
+
self.setCentralWidget(self.central_widget)
|
|
13
|
+
self.layout = QVBoxLayout()
|
|
14
|
+
self.central_widget.setLayout(self.layout)
|
|
15
|
+
print(f"[DEBUG] Initialized window '{title}' with size {width}x{height}")
|
|
16
|
+
|
|
17
|
+
def add_label(self, text):
|
|
18
|
+
label = VertexUI.Text(text)
|
|
19
|
+
self.layout.addWidget(label)
|
|
20
|
+
print(f"[DEBUG] Added label: '{text}'")
|
|
21
|
+
|
|
22
|
+
def add_button(self, text, callback=None):
|
|
23
|
+
button = VertexUI.FancyButton(text)
|
|
24
|
+
if callback:
|
|
25
|
+
button.clicked.connect(callback)
|
|
26
|
+
self.layout.addWidget(button)
|
|
27
|
+
print(f"[DEBUG] Added button: '{text}'")
|
|
28
|
+
|
|
29
|
+
# ===== Fancy themed window =====
|
|
30
|
+
class FancyWindow(BaseWindow):
|
|
31
|
+
def __init__(self, title="Fancy Window", width=500, height=400, theme_color="#FF69B4"):
|
|
32
|
+
super().__init__(title, width, height)
|
|
33
|
+
self.theme_color = theme_color
|
|
34
|
+
self.apply_theme()
|
|
35
|
+
|
|
36
|
+
def apply_theme(self):
|
|
37
|
+
self.setStyleSheet(f"""
|
|
38
|
+
QMainWindow {{
|
|
39
|
+
background-color: {self.theme_color};
|
|
40
|
+
}}
|
|
41
|
+
QLabel {{
|
|
42
|
+
color: white;
|
|
43
|
+
font-size: 18px;
|
|
44
|
+
}}
|
|
45
|
+
QPushButton {{
|
|
46
|
+
background-color: white;
|
|
47
|
+
color: {self.theme_color};
|
|
48
|
+
border-radius: 5px;
|
|
49
|
+
padding: 5px 10px;
|
|
50
|
+
}}
|
|
51
|
+
""")
|
|
52
|
+
print(f"[DEBUG] Applied theme color: {self.theme_color}")
|
|
53
|
+
|
|
54
|
+
# ===== Random fun window with auto-close button =====
|
|
55
|
+
class FunWindow(BaseWindow):
|
|
56
|
+
def __init__(self, title="Fun Window"):
|
|
57
|
+
super().__init__(title)
|
|
58
|
+
self.add_label("Welcome to FunWindow! 🎉")
|
|
59
|
+
self.add_button("Close me", self.close)
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
app = QApplication(sys.argv)
|
|
63
|
+
|
|
64
|
+
# Choose your boredom mode
|
|
65
|
+
window = FancyWindow(theme_color="#8A2BE2")
|
|
66
|
+
window.add_label("This is super fancy 💎")
|
|
67
|
+
window.add_button("Click me!", lambda: print("You clicked the fancy button!"))
|
|
68
|
+
|
|
69
|
+
window.show()
|
|
70
|
+
sys.exit(app.exec())
|
VertexEngine/__init__.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# VertexEngine/__init__.py
|
|
2
|
+
# Copyright (C) 2025
|
|
3
|
+
# This library/SDK is free. You can redistribute it.
|
|
4
|
+
# Tyrel Gomez (email as annbasilan)
|
|
5
|
+
# annbasilan0828@gmail.com
|
|
6
|
+
"""Vertex 3 is an SDK for RainOS GameDev. It's also supported by many others.
|
|
7
|
+
|
|
8
|
+
Supported OSes
|
|
9
|
+
--------------
|
|
10
|
+
- RainOS
|
|
11
|
+
- Windows
|
|
12
|
+
- MacOS,
|
|
13
|
+
- OS X
|
|
14
|
+
- BeOS
|
|
15
|
+
- FreeBSD
|
|
16
|
+
- IRIX
|
|
17
|
+
- and Linux
|
|
18
|
+
|
|
19
|
+
It is written on top of the excellent Pygame library which is ran on the even more excellent SDL library which runs on every Desktop OS with SDL."""
|
|
20
|
+
import pygame
|
|
21
|
+
from .engine import GameEngine
|
|
22
|
+
from .scenes import Scene, SceneManager
|
|
23
|
+
from .assets import AssetManager
|
|
24
|
+
from .audio import AudioManager
|
|
25
|
+
from pygame.base import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
|
|
26
|
+
from pygame import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
|
|
27
|
+
import sys
|
|
28
|
+
from PyQt6.QtWidgets import QPushButton, QLineEdit
|
|
29
|
+
from PyQt6.QtGui import QFont
|
|
30
|
+
from PyQt6.QtWidgets import QPushButton
|
|
31
|
+
from PyQt6.QtGui import QCursor
|
|
32
|
+
from PyQt6.QtCore import Qt
|
|
33
|
+
from PyQt6.QtWidgets import QLabel
|
|
34
|
+
from typing_extensions import deprecated as dep
|
|
35
|
+
|
|
36
|
+
class VertexScreen():
|
|
37
|
+
"""Draw on VertexEngine's Screen."""
|
|
38
|
+
def __init__(self):
|
|
39
|
+
pass
|
|
40
|
+
class Draw():
|
|
41
|
+
"The Draw class to draw on VertexEngine"
|
|
42
|
+
def __init__(self):
|
|
43
|
+
pass
|
|
44
|
+
def rect(self, surface, color, rect=None):
|
|
45
|
+
"""Draw a Rectangle of a solid color."""
|
|
46
|
+
pygame.draw.rect(surface, color, rect)
|
|
47
|
+
def polygon(self, surface, color, points, width):
|
|
48
|
+
"Draw a polygon by marking points on the screen to make an n-gon"
|
|
49
|
+
pygame.draw.polygon(surface, color, points, width)
|
|
50
|
+
def circle(self, circle_surface, color, center, radius, width, draw_top_right, draw_top_left, draw_bottom_right, draw_bottom_left):
|
|
51
|
+
"""Draw a Circle with radius, color, etc."""
|
|
52
|
+
pygame.draw.circle(circle_surface, color, center, radius, width, draw_top_right, draw_top_left, draw_bottom_left, draw_bottom_right)
|
|
53
|
+
def ellipse(self, surface, color, rect, width):
|
|
54
|
+
"""Draw an elipse with surface, color, rect and width"""
|
|
55
|
+
pygame.draw.ellipse(surface, color, rect, width)
|
|
56
|
+
def arc(self, surface, color, rect, start_angle, stop_angle, width):
|
|
57
|
+
"Draw an arc with a lot of values"
|
|
58
|
+
pygame.draw.arc(surface, color, rect, start_angle, stop_angle, width)
|
|
59
|
+
def line(self, surface, color, start_pos, end_pos, width):
|
|
60
|
+
"""Draw a line"""
|
|
61
|
+
pygame.draw.line(surface, color, start_pos, end_pos, width)
|
|
62
|
+
def lines(self, surface, color, closed, points, width):
|
|
63
|
+
'''Draw a pair of lines'''
|
|
64
|
+
pygame.draw.lines(surface, color, closed, points, width)
|
|
65
|
+
def aaline(
|
|
66
|
+
self,
|
|
67
|
+
surface,
|
|
68
|
+
color,
|
|
69
|
+
start_pos,
|
|
70
|
+
end_pos,
|
|
71
|
+
blend,
|
|
72
|
+
):
|
|
73
|
+
'''Draw an aaline'''
|
|
74
|
+
pygame.draw.aaline(surface, color, start_pos, end_pos, blend)
|
|
75
|
+
def aalines(
|
|
76
|
+
self,
|
|
77
|
+
surface,
|
|
78
|
+
color,
|
|
79
|
+
closed,
|
|
80
|
+
points,
|
|
81
|
+
blend
|
|
82
|
+
):
|
|
83
|
+
'''Draw a set of aalines'''
|
|
84
|
+
pygame.draw.aalines(surface, color, closed, points, blend)
|
|
85
|
+
class Font():
|
|
86
|
+
"""VertexEngine's offical Font Engine"""
|
|
87
|
+
def __init__(self, path=None, size=24, antialias=True):
|
|
88
|
+
self.font = pygame.font.Font(path, size)
|
|
89
|
+
self.size = size
|
|
90
|
+
self.antialias = antialias
|
|
91
|
+
|
|
92
|
+
def draw(
|
|
93
|
+
self,
|
|
94
|
+
surface,
|
|
95
|
+
text,
|
|
96
|
+
pos,
|
|
97
|
+
color=(255, 255, 255),
|
|
98
|
+
align="topleft",
|
|
99
|
+
center=False,
|
|
100
|
+
shadow=False,
|
|
101
|
+
shadow_color=(0, 0, 0),
|
|
102
|
+
shadow_offset=(2, 2),
|
|
103
|
+
outline=False,
|
|
104
|
+
outline_color=(0, 0, 0),
|
|
105
|
+
outline_thickness=1
|
|
106
|
+
):
|
|
107
|
+
text_surf = self.font.render(text, self.antialias, color)
|
|
108
|
+
rect = text_surf.get_rect()
|
|
109
|
+
|
|
110
|
+
if center:
|
|
111
|
+
rect.center = pos
|
|
112
|
+
else:
|
|
113
|
+
setattr(rect, align, pos)
|
|
114
|
+
|
|
115
|
+
# 🔲 Outline
|
|
116
|
+
if outline:
|
|
117
|
+
for dx in range(-outline_thickness, outline_thickness + 1):
|
|
118
|
+
for dy in range(-outline_thickness, outline_thickness + 1):
|
|
119
|
+
if dx == 0 and dy == 0:
|
|
120
|
+
continue
|
|
121
|
+
outline_surf = self.font.render(text, self.antialias, outline_color)
|
|
122
|
+
surface.blit(outline_surf, rect.move(dx, dy))
|
|
123
|
+
|
|
124
|
+
# 🌑 Shadow
|
|
125
|
+
if shadow:
|
|
126
|
+
shadow_surf = self.font.render(text, self.antialias, shadow_color)
|
|
127
|
+
shadow_rect = rect.move(shadow_offset)
|
|
128
|
+
surface.blit(shadow_surf, shadow_rect)
|
|
129
|
+
|
|
130
|
+
surface.blit(text_surf, rect)
|
|
131
|
+
|
|
132
|
+
@dep("Unlike internal APIs, this one is fully deprecated, NEVER USE THIS. EVER :)")
|
|
133
|
+
def draw_text(
|
|
134
|
+
surface,
|
|
135
|
+
text,
|
|
136
|
+
font,
|
|
137
|
+
color,
|
|
138
|
+
pos,
|
|
139
|
+
align="topleft",
|
|
140
|
+
shadow=False,
|
|
141
|
+
shadow_color=(0, 0, 0),
|
|
142
|
+
shadow_offset=(2, 2)
|
|
143
|
+
):
|
|
144
|
+
text_surf = font.render(text, True, color)
|
|
145
|
+
text_rect = text_surf.get_rect()
|
|
146
|
+
|
|
147
|
+
setattr(text_rect, align, pos)
|
|
148
|
+
|
|
149
|
+
if shadow:
|
|
150
|
+
shadow_surf = font.render(text, True, shadow_color)
|
|
151
|
+
shadow_rect = shadow_surf.get_rect()
|
|
152
|
+
setattr(shadow_rect, align, (pos[0] + shadow_offset[0], pos[1] + shadow_offset[1]))
|
|
153
|
+
surface.blit(shadow_surf, shadow_rect)
|
|
154
|
+
|
|
155
|
+
surface.blit(text_surf, text_rect)
|
|
156
|
+
class Rect():
|
|
157
|
+
'''Define a rect to pass into VertexScreen.Draw.rect()'''
|
|
158
|
+
def __init__(self, left, top, width, height):
|
|
159
|
+
pygame.Rect(left, top, width, height)
|
|
160
|
+
|
|
161
|
+
class VertexUI():
|
|
162
|
+
# QPushButton *button = new QPushButton(tr("Ro&ck && Roll"), this)
|
|
163
|
+
def __init__(self):
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
class FancyButton(QPushButton):
|
|
167
|
+
def __init__(
|
|
168
|
+
self,
|
|
169
|
+
text="Button",
|
|
170
|
+
width=120,
|
|
171
|
+
height=40,
|
|
172
|
+
bg_color="#4CAF50",
|
|
173
|
+
hover_color="#45A049",
|
|
174
|
+
text_color="white",
|
|
175
|
+
border_radius=10,
|
|
176
|
+
border_color="#388E3C",
|
|
177
|
+
border_width=2,
|
|
178
|
+
font_name="Arial",
|
|
179
|
+
font_size=12,
|
|
180
|
+
font_weight=QFont.Weight.Bold,
|
|
181
|
+
parent=None
|
|
182
|
+
):
|
|
183
|
+
super().__init__(text, parent)
|
|
184
|
+
|
|
185
|
+
# Store params
|
|
186
|
+
self.bg_color = bg_color
|
|
187
|
+
self.hover_color = hover_color
|
|
188
|
+
self.text_color = text_color
|
|
189
|
+
self.border_radius = border_radius
|
|
190
|
+
self.border_color = border_color
|
|
191
|
+
self.border_width = border_width
|
|
192
|
+
self.setFont(QFont(font_name, font_size, font_weight))
|
|
193
|
+
self.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
|
194
|
+
self.setMinimumSize(width, height)
|
|
195
|
+
|
|
196
|
+
# Apply default style
|
|
197
|
+
self.setStyleSheet(self.default_style())
|
|
198
|
+
|
|
199
|
+
def enterEvent(self, event):
|
|
200
|
+
self.setStyleSheet(self.hover_style())
|
|
201
|
+
super().enterEvent(event)
|
|
202
|
+
|
|
203
|
+
def leaveEvent(self, event):
|
|
204
|
+
self.setStyleSheet(self.default_style())
|
|
205
|
+
super().leaveEvent(event)
|
|
206
|
+
|
|
207
|
+
def default_style(self):
|
|
208
|
+
return f"""
|
|
209
|
+
QPushButton {{
|
|
210
|
+
background-color: {self.bg_color};
|
|
211
|
+
color: {self.text_color};
|
|
212
|
+
border-radius: {self.border_radius}px;
|
|
213
|
+
border: {self.border_width}px solid {self.border_color};
|
|
214
|
+
}}
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
def hover_style(self):
|
|
218
|
+
return f"""
|
|
219
|
+
QPushButton {{
|
|
220
|
+
background-color: {self.hover_color};
|
|
221
|
+
color: {self.text_color};
|
|
222
|
+
border-radius: {self.border_radius}px;
|
|
223
|
+
border: {self.border_width}px solid {self.border_color};
|
|
224
|
+
}}
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
class Text(QLabel):
|
|
228
|
+
def __init__(
|
|
229
|
+
self,
|
|
230
|
+
text="Text",
|
|
231
|
+
font_name="Arial",
|
|
232
|
+
font_size=14,
|
|
233
|
+
font_weight=QFont.Weight.Normal,
|
|
234
|
+
text_color="#FFFFFF",
|
|
235
|
+
bg_color=None,
|
|
236
|
+
padding=8,
|
|
237
|
+
border_radius=8,
|
|
238
|
+
alignment=Qt.AlignmentFlag.AlignCenter,
|
|
239
|
+
parent=None
|
|
240
|
+
):
|
|
241
|
+
super().__init__(text, parent)
|
|
242
|
+
|
|
243
|
+
self.setFont(QFont(font_name, font_size, font_weight))
|
|
244
|
+
self.setAlignment(alignment)
|
|
245
|
+
|
|
246
|
+
bg = "transparent" if bg_color is None else bg_color
|
|
247
|
+
|
|
248
|
+
self.setStyleSheet(f"""
|
|
249
|
+
QLabel {{
|
|
250
|
+
color: {text_color};
|
|
251
|
+
background-color: {bg};
|
|
252
|
+
padding: {padding}px;
|
|
253
|
+
border-radius: {border_radius}px;
|
|
254
|
+
}}
|
|
255
|
+
""")
|
|
256
|
+
|
|
257
|
+
def set_text(self, text):
|
|
258
|
+
self.setText(text)
|
|
259
|
+
|
|
260
|
+
def set_color(self, color):
|
|
261
|
+
self.setStyleSheet(self.styleSheet() + f"color: {color};")
|
|
262
|
+
|
|
263
|
+
class InputField(QLineEdit):
|
|
264
|
+
def __init__(
|
|
265
|
+
self,
|
|
266
|
+
placeholder="Enter text...",
|
|
267
|
+
width=240,
|
|
268
|
+
height=36,
|
|
269
|
+
font_name="Arial",
|
|
270
|
+
font_size=12,
|
|
271
|
+
text_color="#FFFFFF",
|
|
272
|
+
bg_color="#1E1E1E",
|
|
273
|
+
border_color="#555555",
|
|
274
|
+
focus_color="#00E5FF",
|
|
275
|
+
border_radius=8,
|
|
276
|
+
padding=8,
|
|
277
|
+
parent=None
|
|
278
|
+
):
|
|
279
|
+
super().__init__(parent)
|
|
280
|
+
|
|
281
|
+
self.setPlaceholderText(placeholder)
|
|
282
|
+
self.setFont(QFont(font_name, font_size))
|
|
283
|
+
self.setMinimumSize(width, height)
|
|
284
|
+
|
|
285
|
+
self.text_color = text_color
|
|
286
|
+
self.bg_color = bg_color
|
|
287
|
+
self.border_color = border_color
|
|
288
|
+
self.focus_color = focus_color
|
|
289
|
+
self.border_radius = border_radius
|
|
290
|
+
self.padding = padding
|
|
291
|
+
|
|
292
|
+
self.apply_style(focused=False)
|
|
293
|
+
|
|
294
|
+
def apply_style(self, focused=False):
|
|
295
|
+
border = self.focus_color if focused else self.border_color
|
|
296
|
+
|
|
297
|
+
self.setStyleSheet(f"""
|
|
298
|
+
QLineEdit {{
|
|
299
|
+
color: {self.text_color};
|
|
300
|
+
background-color: {self.bg_color};
|
|
301
|
+
border: 2px solid {border};
|
|
302
|
+
border-radius: {self.border_radius}px;
|
|
303
|
+
padding: {self.padding}px;
|
|
304
|
+
}}
|
|
305
|
+
QLineEdit::placeholder {{
|
|
306
|
+
color: #888888;
|
|
307
|
+
}}
|
|
308
|
+
""")
|
|
309
|
+
|
|
310
|
+
def focusInEvent(self, event):
|
|
311
|
+
self.apply_style(focused=True)
|
|
312
|
+
super().focusInEvent(event)
|
|
313
|
+
|
|
314
|
+
def focusOutEvent(self, event):
|
|
315
|
+
self.apply_style(focused=False)
|
|
316
|
+
super().focusOutEvent(event)
|
|
317
|
+
|
|
318
|
+
def get_value(self):
|
|
319
|
+
return self.text()
|
|
320
|
+
|
|
321
|
+
def set_value(self, value):
|
|
322
|
+
self.setText(value)
|
VertexEngine/assets.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import pygame
|
|
2
|
+
from PyQt6.QtGui import QImage
|
|
3
|
+
import typing_extensions as typing
|
|
4
|
+
|
|
5
|
+
@typing.deprecated('This is not a public API, use AssetManager pls :D')
|
|
6
|
+
class QtRenderer:
|
|
7
|
+
def create_texture(self, image_asset):
|
|
8
|
+
surf = image_asset.surface
|
|
9
|
+
width, height = surf.get_size()
|
|
10
|
+
|
|
11
|
+
# Ensure predictable format
|
|
12
|
+
surf = surf.convert_alpha() if surf.get_alpha() else surf.convert()
|
|
13
|
+
|
|
14
|
+
# pygame → raw RGBA
|
|
15
|
+
pixel_data = pygame.image.tostring(surf, "RGBA", False)
|
|
16
|
+
|
|
17
|
+
# Create REAL QImage (no OpenGL required)
|
|
18
|
+
qimage = QImage(
|
|
19
|
+
pixel_data,
|
|
20
|
+
width,
|
|
21
|
+
height,
|
|
22
|
+
QImage.Format.Format_RGBA8888
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# IMPORTANT: deep copy so data survives after function returns
|
|
26
|
+
qimage = qimage.copy()
|
|
27
|
+
|
|
28
|
+
return Texture(qimage)
|
|
29
|
+
|
|
30
|
+
@typing.deprecated('This is not a public API, use AssetManager pls :D')
|
|
31
|
+
class Texture:
|
|
32
|
+
def __init__(self, qimage: QImage):
|
|
33
|
+
self.image = qimage
|
|
34
|
+
self.width = qimage.width()
|
|
35
|
+
self.height = qimage.height()
|
|
36
|
+
|
|
37
|
+
def __repr__(self):
|
|
38
|
+
return f"<Texture {self.width}x{self.height} (QImage)>"
|
|
39
|
+
|
|
40
|
+
@typing.deprecated('This is not a public API, use AssetManager pls :D')
|
|
41
|
+
class ImageAsset:
|
|
42
|
+
def __init__(self, surface):
|
|
43
|
+
self.surface = surface # pygame.Surface
|
|
44
|
+
|
|
45
|
+
class AssetManager:
|
|
46
|
+
def __init__(self):
|
|
47
|
+
self.images = {}
|
|
48
|
+
|
|
49
|
+
def load_image(self, name: str, path: str):
|
|
50
|
+
if name in self.images:
|
|
51
|
+
return self.images[name]
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
surface = pygame.image.load(path).convert_alpha()
|
|
55
|
+
self.images[name] = surface
|
|
56
|
+
return surface
|
|
57
|
+
|
|
58
|
+
except FileNotFoundError:
|
|
59
|
+
print(f"[Warning] Image '{path}' not found!")
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
def get_image(self, name: str):
|
|
63
|
+
return self.images.get(name)
|
VertexEngine/audio.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import pygame
|
|
2
|
+
|
|
3
|
+
class AudioManager:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
pygame.mixer.init()
|
|
6
|
+
self.sounds = {}
|
|
7
|
+
self.music = None
|
|
8
|
+
|
|
9
|
+
def load_sound(self, name, path):
|
|
10
|
+
self.sounds[name] = pygame.mixer.Sound(path)
|
|
11
|
+
|
|
12
|
+
def play_sound(self, name, loops=0):
|
|
13
|
+
if name in self.sounds:
|
|
14
|
+
self.sounds[name].play(loops=loops)
|
|
15
|
+
|
|
16
|
+
def load_music(self, path):
|
|
17
|
+
self.music = path
|
|
18
|
+
pygame.mixer.music.load(path)
|
|
19
|
+
|
|
20
|
+
def play_music(self, loops=-1):
|
|
21
|
+
if self.music:
|
|
22
|
+
pygame.mixer.music.play(loops=loops)
|
|
23
|
+
|
|
24
|
+
def stop_music(self):
|
|
25
|
+
pygame.mixer.music.stop()
|