@@ -261,51 +261,43 @@ void GameCard::paintEvent(QPaintEvent* event) {
261261 painter.fillRect (cardRect.toRect (), surfaceColor);
262262 }
263263
264- // ── Bottom info area ──
265- int infoHeight = 60 ;
266- QRectF infoRect (cardRect.left (), cardRect.bottom () - infoHeight,
267- cardRect.width (), infoHeight);
268-
269- // Material surface overlay for readability
270- QLinearGradient infoGrad (infoRect.topLeft (), infoRect.bottomLeft ());
271- if (m_hasThumbnail) {
272- infoGrad.setColorAt (0 , QColor (8 , 8 , 12 , 180 ));
273- infoGrad.setColorAt (1 , QColor (8 , 8 , 12 , 220 ));
264+ // ── Bottom info area (animated opacity) ──
265+ if (m_hoverOpacity > 0.01 || m_selected) {
266+ qreal opacity = m_selected ? 1.0 : m_hoverOpacity;
267+ int infoHeight = 70 ;
268+ QRectF infoRect (cardRect.left (), cardRect.bottom () - infoHeight,
269+ cardRect.width (), infoHeight);
270+
271+ // Seamless gradient from transparent to dark (no separator line)
272+ QLinearGradient infoGrad (infoRect.topLeft (), infoRect.bottomLeft ());
273+ infoGrad.setColorAt (0 , QColor (0 , 0 , 0 , 0 ));
274+ infoGrad.setColorAt (0.4 , QColor (8 , 8 , 12 , (int )(160 * opacity)));
275+ infoGrad.setColorAt (1 , QColor (8 , 8 , 12 , (int )(230 * opacity)));
274276 painter.fillRect (infoRect.toRect (), infoGrad);
275- painter.setPen (QPen (QColor (255 , 255 , 255 , 30 ), 1 ));
276- painter.drawLine (infoRect.topLeft (), infoRect.topRight ());
277- } else {
278- // Flat frosted bottom
279- QColor frostedBottom = Colors::toQColor (Colors::SURFACE_CONTAINER_HIGHEST);
280- frostedBottom.setAlpha (180 );
281- painter.fillRect (infoRect.toRect (), frostedBottom);
282- // Subtle top border relative to the bottom section
283- painter.setPen (QPen (QColor (255 ,255 ,255 ,20 ), 1 ));
284- painter.drawLine (infoRect.topLeft (), infoRect.topRight ());
285- }
286277
287- // Game name
288- QString name = m_data.value (" name" , " Unknown" );
289- QFont nameFont (" Roboto" , 10 , QFont::DemiBold);
290- nameFont.setStyleStrategy (QFont::PreferAntialias);
291- painter.setFont (nameFont);
292- painter.setPen (Colors::toQColor (Colors::ON_SURFACE));
293-
294- QRectF nameRect (infoRect.left () + 12 , infoRect.top () + 10 ,
295- infoRect.width () - 24 , 22 );
296- QFontMetrics fm (nameFont);
297- QString elidedName = fm.elidedText (name, Qt::ElideRight, (int )nameRect.width ());
298- painter.drawText (nameRect, Qt::AlignLeft | Qt::AlignVCenter, elidedName);
299-
300- // App ID
301- QFont idFont (" Roboto" , 8 );
302- idFont.setStyleStrategy (QFont::PreferAntialias);
303- painter.setFont (idFont);
304- painter.setPen (Colors::toQColor (Colors::ON_SURFACE_VARIANT));
305- QRectF idRect (infoRect.left () + 12 , infoRect.top () + 34 ,
306- infoRect.width () - 24 , 18 );
307- painter.drawText (idRect, Qt::AlignLeft | Qt::AlignVCenter,
308- QString (" ID: %1" ).arg (m_data.value (" appid" , " ?" )));
278+ // Game name
279+ QString name = m_data.value (" name" , " Unknown" );
280+ QFont nameFont (" Roboto" , 10 , QFont::DemiBold);
281+ nameFont.setStyleStrategy (QFont::PreferAntialias);
282+ painter.setFont (nameFont);
283+ painter.setPen (QColor (255 , 255 , 255 , (int )(240 * opacity)));
284+
285+ QRectF nameRect (infoRect.left () + 12 , infoRect.top () + 22 ,
286+ infoRect.width () - 24 , 22 );
287+ QFontMetrics fm (nameFont);
288+ QString elidedName = fm.elidedText (name, Qt::ElideRight, (int )nameRect.width ());
289+ painter.drawText (nameRect, Qt::AlignLeft | Qt::AlignVCenter, elidedName);
290+
291+ // App ID
292+ QFont idFont (" Roboto" , 8 );
293+ idFont.setStyleStrategy (QFont::PreferAntialias);
294+ painter.setFont (idFont);
295+ painter.setPen (QColor (180 , 180 , 200 , (int )(200 * opacity)));
296+ QRectF idRect (infoRect.left () + 12 , infoRect.top () + 44 ,
297+ infoRect.width () - 24 , 18 );
298+ painter.drawText (idRect, Qt::AlignLeft | Qt::AlignVCenter,
299+ QString (" ID: %1" ).arg (m_data.value (" appid" , " ?" )));
300+ }
309301
310302 // Reset clip for border drawing
311303 painter.setClipRect (rect ());
@@ -356,12 +348,30 @@ void GameCard::enterEvent(QEnterEvent* event) {
356348 Q_UNUSED (event);
357349 if (m_isSkeleton) return ;
358350 m_hovered = true ;
359- update ();
351+
352+ if (!m_hoverAnim) {
353+ m_hoverAnim = new QPropertyAnimation (this , " hoverOpacity" , this );
354+ }
355+ m_hoverAnim->stop ();
356+ m_hoverAnim->setDuration (200 );
357+ m_hoverAnim->setStartValue (m_hoverOpacity);
358+ m_hoverAnim->setEndValue (1.0 );
359+ m_hoverAnim->setEasingCurve (QEasingCurve::OutCubic);
360+ m_hoverAnim->start ();
360361}
361362
362363void GameCard::leaveEvent (QEvent* event) {
363364 Q_UNUSED (event);
364365 if (m_isSkeleton) return ;
365366 m_hovered = false ;
366- update ();
367+
368+ if (!m_hoverAnim) {
369+ m_hoverAnim = new QPropertyAnimation (this , " hoverOpacity" , this );
370+ }
371+ m_hoverAnim->stop ();
372+ m_hoverAnim->setDuration (200 );
373+ m_hoverAnim->setStartValue (m_hoverOpacity);
374+ m_hoverAnim->setEndValue (0.0 );
375+ m_hoverAnim->setEasingCurve (QEasingCurve::InCubic);
376+ m_hoverAnim->start ();
367377}
0 commit comments