eden/src/yuzu/game/game_card.cpp
crueter 7d53849cd3
[desktop] "Fix" card auto-alignment (#3829)
Closes #3707

All this does is anchor the left and right-most cards to their
respective edges, and then equally distributes the gaps between cards
thereafter.

Don't even bother trying to figure out what the hell I just wrote. I'm a
UI designer, not a mathematician.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3829
2026-04-07 00:02:59 +02:00

134 lines
4.7 KiB
C++

// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QPainter>
#include <QPainterPath>
#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<int>(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<QPixmap>();
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()) {
// if "none" is selected, pretend there's a
_iconsize = _iconsize ? _iconsize : 96;
// 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
// If "none" is selected, then default to 8.5 point font.
font.setPointSize(1 + std::max(7.0, _iconsize ? std::sqrt(_iconsize * 0.6) : 7.5));
// 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;
}