Skip to content

Commit 239f1fd

Browse files
committed
massive ui update
1 parent c71a2c7 commit 239f1fd

11 files changed

Lines changed: 825 additions & 344 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ qrc_*.cpp
5050
webserver/games_index.json
5151
webserver/fetch_progress.json
5252
/webserver/__pycache__
53+
/build_debug

src/gamecard.cpp

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "gamecard.h"
22
#include "utils/colors.h"
3+
#include "materialicons.h"
34

45
#include <QPainter>
56
#include <QPainterPath>
@@ -56,134 +57,164 @@ void GameCard::paintEvent(QPaintEvent* event) {
5657
painter.setRenderHint(QPainter::Antialiasing);
5758
painter.setRenderHint(QPainter::SmoothPixmapTransform);
5859

59-
QRect cardRect = rect().adjusted(5, 5, -5, -5);
60-
int radius = 14;
60+
QRectF cardRect = QRectF(rect()).adjusted(4, 4, -4, -4);
61+
int radius = 16; // Material M3 standard
6162
bool supported = (m_data.value("supported") == "true");
6263

64+
// ── Elevation shadow ──
65+
if (m_hovered || m_selected) {
66+
// Level 2 elevation
67+
for (int i = 4; i >= 1; --i) {
68+
QColor shadowColor(0, 0, 0, 12 * i);
69+
QPen shadowPen(shadowColor, 0.5);
70+
painter.setPen(shadowPen);
71+
painter.setBrush(Qt::NoBrush);
72+
painter.drawRoundedRect(cardRect.adjusted(-i, -i + 1, i, i + 1), radius + i, radius + i);
73+
}
74+
} else {
75+
// Level 1 elevation
76+
for (int i = 2; i >= 1; --i) {
77+
QColor shadowColor(0, 0, 0, 15 * i);
78+
QPen shadowPen(shadowColor, 0.5);
79+
painter.setPen(shadowPen);
80+
painter.setBrush(Qt::NoBrush);
81+
painter.drawRoundedRect(cardRect.adjusted(-i, i * 0.5, i, i + 0.5), radius + i, radius + i);
82+
}
83+
}
84+
6385
// Clip to rounded rect
6486
QPainterPath clipPath;
6587
clipPath.addRoundedRect(cardRect, radius, radius);
6688
painter.setClipPath(clipPath);
6789

6890
if (m_hasThumbnail) {
69-
// Stretch thumbnail to fill the entire card
70-
painter.drawPixmap(cardRect, m_thumbnail);
71-
72-
// Subtle dark vignette overlay for depth
73-
QRadialGradient vignette(cardRect.center(), cardRect.width() * 0.7);
74-
vignette.setColorAt(0, QColor(0, 0, 0, 0));
75-
vignette.setColorAt(1, QColor(0, 0, 0, 80));
76-
painter.fillRect(cardRect, vignette);
91+
// Stretch thumbnail to fill card
92+
painter.drawPixmap(cardRect.toRect(), m_thumbnail);
7793
} else {
78-
// Rich glass background gradient
79-
QLinearGradient bg(cardRect.topLeft(), cardRect.bottomRight());
80-
bg.setColorAt(0, QColor(30, 41, 59, 220));
81-
bg.setColorAt(0.5, QColor(20, 30, 48, 230));
82-
bg.setColorAt(1, QColor(10, 18, 32, 240));
83-
painter.fillRect(cardRect, bg);
84-
85-
// Subtle pattern: inner glow
86-
QRadialGradient glow(cardRect.center(), cardRect.height() * 0.5);
87-
glow.setColorAt(0, QColor(59, 130, 246, 15));
88-
glow.setColorAt(1, QColor(0, 0, 0, 0));
89-
painter.fillRect(cardRect, glow);
90-
91-
// Large default gamepad icon
92-
QFont iconFont("Segoe UI Emoji", 48);
93-
painter.setFont(iconFont);
94-
painter.setPen(QColor(148, 163, 184, 80));
94+
// Material surface container background
95+
QColor surfaceColor = Colors::toQColor(Colors::SURFACE_CONTAINER_HIGH);
96+
painter.fillRect(cardRect.toRect(), surfaceColor);
9597

96-
QRect iconArea = cardRect.adjusted(0, -15, 0, -55);
97-
painter.drawText(iconArea, Qt::AlignCenter, QString::fromUtf8("\xF0\x9F\x8E\xAE"));
98+
// Subtle tonal overlay
99+
QRadialGradient glow(cardRect.center(), cardRect.height() * 0.6);
100+
glow.setColorAt(0, QColor(208, 188, 255, 10)); // Primary tint
101+
glow.setColorAt(1, QColor(0, 0, 0, 0));
102+
painter.fillRect(cardRect.toRect(), glow);
103+
104+
// Gamepad icon placeholder
105+
QRectF iconArea(
106+
cardRect.center().x() - 28,
107+
cardRect.center().y() - 40,
108+
56, 56
109+
);
110+
QColor iconColor = Colors::toQColor(Colors::ON_SURFACE_VARIANT);
111+
iconColor.setAlpha(60);
112+
MaterialIcons::draw(painter, iconArea, iconColor, MaterialIcons::Gamepad);
98113
}
99114

100-
// ---- Bottom info gradient overlay ----
101-
int infoHeight = 65;
102-
QRect infoRect(cardRect.left(), cardRect.bottom() - infoHeight, cardRect.width(), infoHeight);
115+
// ── Bottom info area ──
116+
int infoHeight = 62;
117+
QRectF infoRect(cardRect.left(), cardRect.bottom() - infoHeight,
118+
cardRect.width(), infoHeight);
103119

120+
// Material surface overlay for readability
104121
QLinearGradient infoGrad(infoRect.topLeft(), infoRect.bottomLeft());
105-
infoGrad.setColorAt(0, QColor(0, 0, 0, 0));
106-
infoGrad.setColorAt(0.25, QColor(0, 0, 0, 140));
107-
infoGrad.setColorAt(1, QColor(0, 0, 0, 230));
108-
painter.fillRect(infoRect, infoGrad);
122+
if (m_hasThumbnail) {
123+
infoGrad.setColorAt(0, QColor(0, 0, 0, 0));
124+
infoGrad.setColorAt(0.3, QColor(28, 27, 31, 180));
125+
infoGrad.setColorAt(1, QColor(28, 27, 31, 240));
126+
} else {
127+
infoGrad.setColorAt(0, QColor(0, 0, 0, 0));
128+
infoGrad.setColorAt(0.3, QColor(20, 18, 24, 120));
129+
infoGrad.setColorAt(1, QColor(20, 18, 24, 180));
130+
}
131+
painter.fillRect(infoRect.toRect(), infoGrad);
109132

110-
// Game name (larger, bolder)
133+
// Game name
111134
QString name = m_data.value("name", "Unknown");
112-
QFont nameFont("Segoe UI", 10, QFont::Bold);
135+
QFont nameFont("Roboto", 10, QFont::DemiBold);
136+
nameFont.setStyleStrategy(QFont::PreferAntialias);
113137
painter.setFont(nameFont);
114-
painter.setPen(Qt::white);
138+
painter.setPen(Colors::toQColor(Colors::ON_SURFACE));
115139

116-
QRect nameRect(infoRect.left() + 12, infoRect.top() + 10, infoRect.width() - 24, 22);
140+
QRectF nameRect(infoRect.left() + 12, infoRect.top() + 10,
141+
infoRect.width() - 24, 22);
117142
QFontMetrics fm(nameFont);
118-
QString elidedName = fm.elidedText(name, Qt::ElideRight, nameRect.width());
143+
QString elidedName = fm.elidedText(name, Qt::ElideRight, (int)nameRect.width());
119144
painter.drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, elidedName);
120145

121-
// App ID line
122-
QFont idFont("Segoe UI", 8);
146+
// App ID
147+
QFont idFont("Roboto", 8);
148+
idFont.setStyleStrategy(QFont::PreferAntialias);
123149
painter.setFont(idFont);
124-
painter.setPen(QColor(180, 190, 210, 180));
125-
QRect idRect(infoRect.left() + 12, infoRect.top() + 36, infoRect.width() - 24, 20);
150+
painter.setPen(Colors::toQColor(Colors::ON_SURFACE_VARIANT));
151+
QRectF idRect(infoRect.left() + 12, infoRect.top() + 34,
152+
infoRect.width() - 24, 18);
126153
painter.drawText(idRect, Qt::AlignLeft | Qt::AlignVCenter,
127154
QString("ID: %1").arg(m_data.value("appid", "?")));
128155

129156
// Reset clip for border drawing
130157
painter.setClipRect(rect());
131158

132-
// ---- Border & glow effects ----
159+
// ── Border & selection state ──
133160
if (m_selected) {
134-
// Outer glow
135-
for (int i = 3; i >= 1; --i) {
136-
QPen glowPen(QColor(59, 130, 246, 30 * i), i * 1.5);
137-
painter.setPen(glowPen);
138-
painter.setBrush(Qt::NoBrush);
139-
painter.drawRoundedRect(cardRect.adjusted(-i, -i, i, i), radius + i, radius + i);
140-
}
141-
// Main selection border
142-
QPen selPen(Colors::toQColor(Colors::ACCENT_BLUE), 2);
161+
// Material primary border
162+
QPen selPen(Colors::toQColor(Colors::PRIMARY), 2.5);
143163
painter.setPen(selPen);
144164
painter.setBrush(Qt::NoBrush);
145165
painter.drawRoundedRect(cardRect, radius, radius);
146166
} else if (m_hovered) {
147-
// Hover glow
148-
QPen hovPen(QColor(255, 255, 255, 50), 1.5);
167+
// Subtle outline on hover
168+
QPen hovPen(Colors::toQColor(Colors::OUTLINE), 1.2);
149169
painter.setPen(hovPen);
150170
painter.setBrush(Qt::NoBrush);
151171
painter.drawRoundedRect(cardRect, radius, radius);
152172

153-
// Subtle inner highlight at top
173+
// Top highlight shimmer
154174
painter.setClipPath(clipPath);
155-
QLinearGradient topShine(cardRect.topLeft(), QPoint(cardRect.left(), cardRect.top() + 40));
156-
topShine.setColorAt(0, QColor(255, 255, 255, 20));
175+
QLinearGradient topShine(cardRect.topLeft(),
176+
QPointF(cardRect.left(), cardRect.top() + 30));
177+
topShine.setColorAt(0, QColor(255, 255, 255, 12));
157178
topShine.setColorAt(1, QColor(255, 255, 255, 0));
158-
painter.fillRect(QRect(cardRect.left(), cardRect.top(), cardRect.width(), 40), topShine);
179+
painter.fillRect(QRectF(cardRect.left(), cardRect.top(),
180+
cardRect.width(), 30), topShine);
159181
painter.setClipRect(rect());
160182
} else {
161-
// Resting border
162-
QPen borderPen(QColor(255, 255, 255, 15), 1);
183+
// Resting outline variant
184+
QPen borderPen(Colors::toQColor(Colors::OUTLINE_VARIANT), 1);
163185
painter.setPen(borderPen);
164186
painter.setBrush(Qt::NoBrush);
165187
painter.drawRoundedRect(cardRect, radius, radius);
166188
}
167189

168-
// Supported badge (top-right corner)
190+
// ── Supported badge ──
169191
if (supported) {
170192
painter.setClipPath(clipPath);
171-
// Green gradient badge
172-
QRect badge(cardRect.right() - 28, cardRect.top() + 6, 22, 22);
173-
QRadialGradient badgeGrad(badge.center(), 11);
174-
badgeGrad.setColorAt(0, Colors::toQColor(Colors::ACCENT_GREEN));
175-
badgeGrad.setColorAt(1, QColor(16, 185, 129, 180));
193+
194+
QRectF badgeRect(cardRect.right() - 30, cardRect.top() + 6, 24, 24);
195+
196+
// Material container background
197+
QColor badgeColor = Colors::toQColor(Colors::ACCENT_GREEN);
198+
QPainterPath badgePath;
199+
badgePath.addRoundedRect(badgeRect, 12, 12);
200+
painter.fillPath(badgePath, badgeColor);
201+
202+
// Check icon
203+
QRectF checkRect = badgeRect.adjusted(4, 4, -4, -4);
176204
painter.setPen(Qt::NoPen);
177-
painter.setBrush(badgeGrad);
178-
painter.drawEllipse(badge);
205+
painter.setBrush(Qt::NoBrush);
206+
207+
QPen checkPen(QColor("#FFFFFF"), 2.2);
208+
checkPen.setCapStyle(Qt::RoundCap);
209+
checkPen.setJoinStyle(Qt::RoundJoin);
210+
painter.setPen(checkPen);
179211

180-
// Checkmark
181-
painter.setPen(QPen(Qt::white, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
182212
QPainterPath check;
183-
check.moveTo(badge.left() + 5, badge.center().y());
184-
check.lineTo(badge.center().x() - 1, badge.bottom() - 6);
185-
check.lineTo(badge.right() - 5, badge.top() + 6);
213+
check.moveTo(checkRect.left() + 1, checkRect.center().y());
214+
check.lineTo(checkRect.center().x() - 1, checkRect.bottom() - 2);
215+
check.lineTo(checkRect.right() - 1, checkRect.top() + 2);
186216
painter.drawPath(check);
217+
187218
painter.setClipRect(rect());
188219
}
189220
}

0 commit comments

Comments
 (0)