// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "game_card.h" #include "qt_common/config/uisettings.h" GameCard::GameCard(QObject* parent) : QStyledItemDelegate{parent} { setObjectName("GameCard"); } void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { if (!index.isValid()) return; painter->save(); painter->setRenderHint(QPainter::Antialiasing); // Padding, dimensions, alignment... const int column = index.row() % m_columns; const int cell_width = option.rect.width(); const int fixed_card_width = cell_width - m_padding; const int margins = 8; // The gist of it is that this anchors the left and right sides to the edges, // while maintaining an even gap between each card. // I just smashed random keys into my keyboard until something worked. // Don't even bother trying to figure out what the hell this is doing. const auto total_row_width = m_columns * cell_width; const auto total_gap_space = total_row_width - (margins * 2) - (m_columns * fixed_card_width); const auto gap = (m_columns > 1) ? (total_gap_space / (m_columns - 1)) : 0; const auto relative_x = margins + (column * (fixed_card_width + gap)); const auto x_pos = option.rect.left() - (column * cell_width) + static_cast(relative_x); // also, add some additional padding here to prevent card overlap QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8, option.rect.height() - margins); // colors QPalette palette = option.palette; QColor backgroundColor = palette.window().color(); QColor borderColor = palette.dark().color(); QColor textColor = palette.text().color(); // if it's selected add a blue background if (option.state & QStyle::State_Selected) { backgroundColor = palette.highlight().color(); borderColor = palette.highlight().color().lighter(150); textColor = palette.highlightedText().color(); } else if (option.state & QStyle::State_MouseOver) { backgroundColor = backgroundColor.lighter(120); } // bg painter->setBrush(backgroundColor); painter->setPen(QPen(borderColor, 1)); painter->drawRoundedRect(cardRect, 10, 10); // icon int _iconsize = UISettings::values.game_icon_size.GetValue(); QSize iconSize(_iconsize, _iconsize); QPixmap iconPixmap = index.data(Qt::DecorationRole).value(); QRect iconRect; if (!iconPixmap.isNull()) { QSize scaledSize = iconPixmap.size(); scaledSize.scale(iconSize, Qt::KeepAspectRatio); int x = cardRect.left() + (cardRect.width() - scaledSize.width()) / 2; int y = cardRect.top() + margins; iconRect = QRect(x, y, scaledSize.width(), scaledSize.height()); painter->setRenderHint(QPainter::SmoothPixmapTransform, true); // Put this in a separate thing on the painter stack to prevent clipping the text. painter->save(); // round image edges QPainterPath path; path.addRoundedRect(iconRect, 10, 10); painter->setClipPath(path); painter->drawPixmap(iconRect, iconPixmap); painter->restore(); } else { // if there is no icon just draw a blank rect iconRect = QRect(cardRect.left() + margins, cardRect.top() + margins, _iconsize, _iconsize); } if (UISettings::values.show_game_name.GetValue()) { // padding + text QRect textRect = cardRect; textRect.setTop(iconRect.bottom() + margins); textRect.adjust(margins, 0, -margins, -margins); // We are already crammed on space, ignore the row 2 QString title = index.data(Qt::DisplayRole).toString(); title = title.split(QLatin1Char('\n')).first(); // now draw text painter->setPen(textColor); QFont font = option.font; font.setBold(true); // TODO(crueter): fix this abysmal scaling font.setPixelSize(1.5 + std::max(10.0, std::sqrt(_iconsize))); // TODO(crueter): elide mode painter->setFont(font); painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, title); } painter->restore(); } QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { return m_size; } void GameCard::setSize(const QSize& newSize, const int padding, const int columns) { m_size = newSize; m_padding = padding; m_columns = columns; }