mirror of
https://github.com/citron-neo/emulator.git
synced 2026-07-05 15:21:57 -07:00
Merge pull request #126 from CollectingW/main
fix: Padding for Toolbar Text, New Shelf Grid View UI
This commit is contained in:
Vendored
+5
@@ -691,6 +691,11 @@ QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled {
|
||||
image: url(:/overlay/osk_button_Y_disabled.png);
|
||||
}
|
||||
|
||||
QMenuBar::item {
|
||||
background-color: transparent;
|
||||
padding: 18px 18px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adaptive Dropdown Menu Styling
|
||||
*/
|
||||
|
||||
+1
-1
@@ -695,7 +695,7 @@ QMenuBar {
|
||||
|
||||
QMenuBar::item {
|
||||
background-color: transparent;
|
||||
padding: 4px 10px;
|
||||
padding: 18px 18px;
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -164,7 +164,7 @@ QMenuBar {
|
||||
|
||||
QMenuBar::item {
|
||||
background: transparent;
|
||||
padding: 4px 10px;
|
||||
padding: 18px 18px;
|
||||
}
|
||||
|
||||
QMenuBar::item:selected {
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@ QMenuBar {
|
||||
|
||||
QMenuBar::item {
|
||||
background: transparent;
|
||||
padding: 4px 10px;
|
||||
padding: 18px 18px;
|
||||
color: #eff0f1; /* Explicitly set item text color */
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <cmath>
|
||||
#include <QApplication>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <QStyle>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include <QTimer>
|
||||
#include <QTransform>
|
||||
|
||||
#include "citron/game_grid_delegate.h"
|
||||
#include "citron/game_list_p.h"
|
||||
@@ -29,23 +31,28 @@ void GameGridDelegate::setGridMode(GridMode mode) {
|
||||
m_grid_mode = mode;
|
||||
}
|
||||
|
||||
QSize GameGridDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
|
||||
QSize GameGridDelegate::sizeHint(const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) const {
|
||||
const int icon_size = UISettings::values.game_icon_size.GetValue();
|
||||
const float scale = static_cast<float>(icon_size) / 128.0f;
|
||||
return QSize(icon_size + static_cast<int>(40 * scale), icon_size + static_cast<int>(85 * scale));
|
||||
return QSize(icon_size + static_cast<int>(40 * scale),
|
||||
icon_size + static_cast<int>(104 * scale));
|
||||
}
|
||||
|
||||
void GameGridDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) const {
|
||||
if (!index.isValid()) return;
|
||||
const QModelIndex& index) const {
|
||||
if (!index.isValid())
|
||||
return;
|
||||
painter->save();
|
||||
painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
|
||||
painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing |
|
||||
QPainter::SmoothPixmapTransform);
|
||||
PaintGridItem(painter, option, index);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void GameGridDelegate::AdvanceAnimations() {
|
||||
if (!m_view || !m_view->isVisible()) return;
|
||||
if (!m_view || !m_view->isVisible())
|
||||
return;
|
||||
|
||||
auto it_pulse = m_pulse_states.begin();
|
||||
while (it_pulse != m_pulse_states.end()) {
|
||||
@@ -59,10 +66,12 @@ void GameGridDelegate::AdvanceAnimations() {
|
||||
bool& dir = m_pulse_direction[key];
|
||||
if (dir) {
|
||||
val += 0.04;
|
||||
if (val >= 1.0) dir = false;
|
||||
if (val >= 1.0)
|
||||
dir = false;
|
||||
} else {
|
||||
val -= 0.04;
|
||||
if (val <= 0.0) dir = true;
|
||||
if (val <= 0.0)
|
||||
dir = true;
|
||||
}
|
||||
++it_pulse;
|
||||
}
|
||||
@@ -77,7 +86,8 @@ void GameGridDelegate::AdvanceAnimations() {
|
||||
qreal& val = it_entry.value();
|
||||
if (val < 1.0) {
|
||||
val += 0.06;
|
||||
if (val >= 1.0) val = 1.0;
|
||||
if (val >= 1.0)
|
||||
val = 1.0;
|
||||
++it_entry;
|
||||
} else {
|
||||
it_entry = m_entry_animations.erase(it_entry);
|
||||
@@ -88,7 +98,8 @@ void GameGridDelegate::AdvanceAnimations() {
|
||||
m_population_fade_global -= 0.02;
|
||||
} else if (!m_is_populating && m_population_fade_global < 1.0) {
|
||||
m_population_fade_global += 0.03;
|
||||
if (m_population_fade_global > 1.0) m_population_fade_global = 1.0;
|
||||
if (m_population_fade_global > 1.0)
|
||||
m_population_fade_global = 1.0;
|
||||
}
|
||||
|
||||
m_pulse_tick++;
|
||||
@@ -96,7 +107,7 @@ void GameGridDelegate::AdvanceAnimations() {
|
||||
}
|
||||
|
||||
void GameGridDelegate::PaintGridItem(QPainter* painter, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) const {
|
||||
const QModelIndex& index) const {
|
||||
const bool is_selected = option.state & QStyle::State_Selected;
|
||||
QRect rect = option.rect;
|
||||
const int icon_size = UISettings::values.game_icon_size.GetValue();
|
||||
@@ -104,8 +115,9 @@ void GameGridDelegate::PaintGridItem(QPainter* painter, const QStyleOptionViewIt
|
||||
|
||||
qreal entry_val = 1.0;
|
||||
const QPersistentModelIndex key(index);
|
||||
if (m_entry_animations.contains(key)) entry_val = m_entry_animations[key];
|
||||
|
||||
if (m_entry_animations.contains(key))
|
||||
entry_val = m_entry_animations[key];
|
||||
|
||||
qreal final_opacity = entry_val * m_population_fade_global;
|
||||
painter->setOpacity(final_opacity);
|
||||
|
||||
@@ -116,23 +128,69 @@ void GameGridDelegate::PaintGridItem(QPainter* painter, const QStyleOptionViewIt
|
||||
|
||||
const int radius = static_cast<int>(14 * scale);
|
||||
|
||||
painter->save();
|
||||
if (is_selected) {
|
||||
double time_t = m_pulse_tick * 0.032;
|
||||
double hover_y = std::sin(time_t* 3.0) * (4.0 * scale);
|
||||
double yaw_angle = std::sin(time_t* 2.5) * 20.0;
|
||||
double pitch_angle = std::cos(time_t* 1.5) * 10.0;
|
||||
|
||||
painter->translate(rect.center());
|
||||
|
||||
QTransform transform;
|
||||
transform.scale(1.04, 1.04);
|
||||
transform.translate(0, hover_y);
|
||||
|
||||
QTransform rot;
|
||||
rot.rotate(yaw_angle, Qt::YAxis);
|
||||
rot.rotate(pitch_angle, Qt::XAxis);
|
||||
|
||||
painter->setTransform(rot * transform, true);
|
||||
painter->translate(-rect.center());
|
||||
|
||||
// --- 1. Selection Glow ---
|
||||
QColor glow = AccentColor();
|
||||
glow.setAlphaF(0.2f);
|
||||
painter->save();
|
||||
glow.setAlphaF(0.12f);
|
||||
painter->setBrush(glow);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(card_rect.adjusted(static_cast<int>(-4 * scale), static_cast<int>(-4 * scale),
|
||||
static_cast<int>(4 * scale), static_cast<int>(4 * scale)),
|
||||
painter->drawRoundedRect(card_rect.adjusted(-4 * scale, -4 * scale, 4 * scale, 4 * scale),
|
||||
radius + 2, radius + 2);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QColor onyx(22, 22, 26);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(onyx);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(card_rect, radius, radius);
|
||||
|
||||
{
|
||||
painter->save();
|
||||
int pin_count = 12;
|
||||
qreal total_w = card_rect.width() * 0.85;
|
||||
qreal pin_w = (total_w / pin_count) * 0.4;
|
||||
qreal spacing = total_w / (pin_count - 1);
|
||||
qreal start_x = card_rect.left() + (card_rect.width() - total_w) / 2.0;
|
||||
|
||||
for (int i = 0; i < pin_count; ++i) {
|
||||
QRectF pr(start_x + (i * spacing) - (pin_w / 2.0), card_rect.bottom() - (18 * scale),
|
||||
pin_w, 14 * scale);
|
||||
|
||||
QLinearGradient pg(pr.topLeft(), pr.bottomLeft());
|
||||
pg.setColorAt(0, QColor(10, 10, 12));
|
||||
pg.setColorAt(0.35, QColor(220, 200, 120)); // Bright Metallic Gold
|
||||
pg.setColorAt(0.65, QColor(160, 140, 70));
|
||||
pg.setColorAt(1, QColor(25, 25, 30));
|
||||
|
||||
painter->setBrush(pg);
|
||||
painter->setPen(QPen(QColor(0, 0, 0, 180), 0.3 * scale));
|
||||
painter->drawRoundedRect(pr, 1 * scale, 1 * scale);
|
||||
|
||||
painter->setPen(QPen(QColor(255, 255, 255, 50), 0.5 * scale));
|
||||
painter->drawLine(pr.left() + pr.width() * 0.2, pr.top() + pr.height() * 0.2,
|
||||
pr.left() + pr.width() * 0.2, pr.top() + pr.height() * 0.5);
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
if (is_selected) {
|
||||
QColor border = AccentColor();
|
||||
qreal pulse = (m_pulse_states.contains(key)) ? m_pulse_states[key] : 0.0;
|
||||
@@ -146,19 +204,24 @@ void GameGridDelegate::PaintGridItem(QPainter* painter, const QStyleOptionViewIt
|
||||
label_path.addRoundedRect(label_rect, radius - 6, radius - 6);
|
||||
|
||||
// --- Favorites Indicator ---
|
||||
bool is_fav = (index.data(GameListItem::TypeRole).toInt() == static_cast<int>(GameListItemType::Favorites));
|
||||
bool is_fav = (index.data(GameListItem::TypeRole).toInt() ==
|
||||
static_cast<int>(GameListItemType::Favorites));
|
||||
if (is_fav) {
|
||||
painter->save();
|
||||
QColor fav_gold(255, 215, 0, 220); // Vibrant Gold
|
||||
painter->setPen(QPen(fav_gold, 1.2f * scale));
|
||||
painter->setBrush(QColor(40, 40, 45, 180));
|
||||
qreal star_size = 22.0f * scale;
|
||||
QRectF star_rect(card_rect.right() - (10.0f * scale) - star_size, card_rect.top() + (10.0f * scale),
|
||||
star_size, star_size);
|
||||
QRectF star_rect(card_rect.right() - (10.0f * scale) - star_size,
|
||||
card_rect.top() + (10.0f * scale), star_size, star_size);
|
||||
painter->drawRoundedRect(star_rect, 6 * scale, 6 * scale);
|
||||
painter->setPen(fav_gold);
|
||||
QFont sf = painter->font(); sf.setBold(true); sf.setPointSizeF(11.0f * scale); painter->setFont(sf);
|
||||
painter->drawText(star_rect.adjusted(0, -1 * scale, 0, 0), Qt::AlignCenter, QStringLiteral("★"));
|
||||
QFont sf = painter->font();
|
||||
sf.setBold(true);
|
||||
sf.setPointSizeF(11.0f * scale);
|
||||
painter->setFont(sf);
|
||||
painter->drawText(star_rect.adjusted(0, -1 * scale, 0, 0), Qt::AlignCenter,
|
||||
QStringLiteral("★"));
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
@@ -166,15 +229,17 @@ void GameGridDelegate::PaintGridItem(QPainter* painter, const QStyleOptionViewIt
|
||||
|
||||
painter->save();
|
||||
painter->setClipPath(label_path);
|
||||
|
||||
|
||||
qreal bar_h = 28.0f * scale;
|
||||
QRectF bottom_bar(label_rect.left(), label_rect.bottom() - bar_h, label_rect.width(), bar_h);
|
||||
painter->fillRect(bottom_bar, QColor(10, 10, 12));
|
||||
|
||||
QPixmap pixmap = index.data(GameListItemPath::HighResIconRole).value<QPixmap>();
|
||||
if (pixmap.isNull()) pixmap = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
if (pixmap.isNull())
|
||||
pixmap = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
if (!pixmap.isNull()) {
|
||||
QRectF mid_area(label_rect.left(), label_rect.top(), label_rect.width(), bottom_bar.top() - label_rect.top());
|
||||
QRectF mid_area(label_rect.left(), label_rect.top(), label_rect.width(),
|
||||
bottom_bar.top() - label_rect.top());
|
||||
painter->drawPixmap(mid_area, pixmap, pixmap.rect());
|
||||
}
|
||||
|
||||
@@ -188,27 +253,37 @@ void GameGridDelegate::PaintGridItem(QPainter* painter, const QStyleOptionViewIt
|
||||
QRectF text_rect = bottom_bar.adjusted(10 * scale, 0, -10 * scale, 0);
|
||||
QString elided = painter->fontMetrics().elidedText(title, Qt::ElideRight, text_rect.width());
|
||||
painter->drawText(text_rect, Qt::AlignCenter, elided);
|
||||
|
||||
|
||||
painter->restore();
|
||||
|
||||
int pins_y = card_rect.bottom() - static_cast<int>(10 * scale);
|
||||
int p_c = 6;
|
||||
int p_w = (card_rect.width() - static_cast<int>(40 * scale)) / p_c;
|
||||
painter->setOpacity(0.8 * final_opacity);
|
||||
for (int i = 0; i < p_c; ++i) {
|
||||
painter->fillRect(card_rect.left() + static_cast<int>(20 * scale) + i * p_w, pins_y, p_w - static_cast<int>(6 * scale), static_cast<int>(6 * scale), QColor(40, 40, 45));
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QColor GameGridDelegate::CardBg() const { return QColor(22, 22, 26); }
|
||||
QColor GameGridDelegate::TextColor() const { return QColor(255, 255, 255); }
|
||||
QColor GameGridDelegate::DimColor() const { return QColor(120, 120, 130); }
|
||||
QColor GameGridDelegate::SelectionColor() const { return QColor(35, 35, 40); }
|
||||
QColor GameGridDelegate::CardBg() const {
|
||||
return QColor(22, 22, 26);
|
||||
}
|
||||
QColor GameGridDelegate::TextColor() const {
|
||||
return QColor(255, 255, 255);
|
||||
}
|
||||
QColor GameGridDelegate::DimColor() const {
|
||||
return QColor(120, 120, 130);
|
||||
}
|
||||
QColor GameGridDelegate::SelectionColor() const {
|
||||
return QColor(35, 35, 40);
|
||||
}
|
||||
QColor GameGridDelegate::AccentColor() const {
|
||||
const QString hex = QString::fromStdString(UISettings::values.accent_color.GetValue());
|
||||
return QColor(hex).isValid() ? QColor(hex) : QColor(0, 150, 255);
|
||||
}
|
||||
|
||||
void GameGridDelegate::SetPopulating(bool populating) { m_is_populating = populating; }
|
||||
void GameGridDelegate::RegisterEntryAnimation(const QModelIndex& index) { if (index.isValid()) m_entry_animations[QPersistentModelIndex(index)] = 0.0; }
|
||||
void GameGridDelegate::ClearAnimations() { m_entry_animations.clear(); m_pulse_states.clear(); }
|
||||
void GameGridDelegate::SetPopulating(bool populating) {
|
||||
m_is_populating = populating;
|
||||
}
|
||||
void GameGridDelegate::RegisterEntryAnimation(const QModelIndex& index) {
|
||||
if (index.isValid())
|
||||
m_entry_animations[QPersistentModelIndex(index)] = 0.0;
|
||||
}
|
||||
void GameGridDelegate::ClearAnimations() {
|
||||
m_entry_animations.clear();
|
||||
m_pulse_states.clear();
|
||||
}
|
||||
|
||||
@@ -1398,9 +1398,17 @@ GameList::GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
|
||||
else
|
||||
current = carousel_view->view()->currentIndex();
|
||||
|
||||
if (!current.isValid() && !pathName.isEmpty()) {
|
||||
// Attempt to find the index if the view lost focus
|
||||
auto matches = item_model->match(item_model->index(0, 0), GameListItemPath::FullPathRole, pathName, 1, Qt::MatchExactly | Qt::MatchRecursive);
|
||||
if (!matches.isEmpty()) {
|
||||
current = matches.first();
|
||||
}
|
||||
}
|
||||
|
||||
if (current.isValid()) {
|
||||
StartLaunchAnimation(current);
|
||||
} else {
|
||||
} else if (!pathName.isEmpty()) {
|
||||
emit BootGame(pathName, StartGameType::Normal);
|
||||
}
|
||||
} else if (action == QStringLiteral("favorite")) {
|
||||
@@ -2096,7 +2104,8 @@ void GameList::ValidateEntry(const QModelIndex& item) {
|
||||
}
|
||||
const auto selected = item.sibling(item.row(), 0);
|
||||
switch (selected.data(GameListItem::TypeRole).value<GameListItemType>()) {
|
||||
case GameListItemType::Game: {
|
||||
case GameListItemType::Game:
|
||||
case GameListItemType::Favorites: {
|
||||
const QString file_path = selected.data(GameListItemPath::FullPathRole).toString();
|
||||
if (file_path.isEmpty())
|
||||
return;
|
||||
|
||||
@@ -1,21 +1,151 @@
|
||||
#include <QAbstractItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QLabel>
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QWheelEvent>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QLabel>
|
||||
#include <QItemSelectionModel>
|
||||
|
||||
|
||||
#include <QColor>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#include <QLinearGradient>
|
||||
#include <QList>
|
||||
#include <QListView>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QRect>
|
||||
#include <QScrollArea>
|
||||
#include "citron/ui/game_grid_view.h"
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
#include "citron/game_grid_delegate.h"
|
||||
#include "citron/game_list_p.h"
|
||||
#include "citron/ui/game_grid_view.h"
|
||||
#include "citron/uisettings.h"
|
||||
|
||||
|
||||
class WoodBackgroundContainer : public QWidget {
|
||||
public:
|
||||
using QWidget::QWidget;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* e) override {
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
int w = width();
|
||||
|
||||
painter.fillRect(e->rect(), QColor(16, 8, 3));
|
||||
|
||||
int panel_w = 90;
|
||||
for (int x = 0; x < w; x += panel_w) {
|
||||
QRect panel_rect(x, e->rect().top(), panel_w, e->rect().height());
|
||||
QLinearGradient panel_grad(x, 0, x + panel_w, 0);
|
||||
|
||||
int var = (x / panel_w) % 3;
|
||||
QColor base_color = (var == 0) ? QColor(36, 18, 8)
|
||||
: (var == 1) ? QColor(30, 14, 6)
|
||||
: QColor(40, 20, 9);
|
||||
|
||||
panel_grad.setColorAt(0.0, base_color);
|
||||
panel_grad.setColorAt(0.2, base_color.lighter(105));
|
||||
panel_grad.setColorAt(0.8, base_color.darker(110));
|
||||
panel_grad.setColorAt(1.0, QColor(10, 4, 1, 150));
|
||||
|
||||
painter.fillRect(panel_rect, panel_grad);
|
||||
|
||||
painter.setPen(QPen(QColor(15, 6, 2, 60), 1));
|
||||
for (int line = 1; line < 4; ++line) {
|
||||
int lx = x + line * (panel_w / 4) + (var * 2);
|
||||
painter.drawLine(lx, e->rect().top(), lx, e->rect().bottom());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ContentHeightListView : public QListView {
|
||||
public:
|
||||
using QListView::QListView;
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent* e) override { e->ignore(); }
|
||||
void wheelEvent(QWheelEvent* e) override {
|
||||
e->ignore();
|
||||
}
|
||||
void paintEvent(QPaintEvent* e) override {
|
||||
QPainter painter(this->viewport());
|
||||
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
int w = viewport()->width();
|
||||
|
||||
if (this->model() && this->model()->rowCount() > 0) {
|
||||
int icon_size = UISettings::values.game_icon_size.GetValue();
|
||||
float scale = static_cast<float>(icon_size) / 128.0f;
|
||||
int card_h = icon_size + static_cast<int>(64 * scale);
|
||||
|
||||
QList<int> row_tops;
|
||||
for (int i = 0; i < this->model()->rowCount(); ++i) {
|
||||
QRect r = this->visualRect(this->model()->index(i, 0));
|
||||
// If any part of the row's indicator item is visible or near visible, collect its
|
||||
// row Y
|
||||
if (r.isValid() && r.bottom() >= -200 && r.top() <= viewport()->height() + 200) {
|
||||
if (!row_tops.contains(r.y())) {
|
||||
row_tops.append(r.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int shelf_hl = qMax(3, static_cast<int>(5 * scale));
|
||||
const int shelf_edge = qMax(1, static_cast<int>(2 * scale));
|
||||
|
||||
for (int ry : row_tops) {
|
||||
int shelf_y = ry + static_cast<int>(12 * scale) + card_h;
|
||||
int item_h = icon_size + static_cast<int>(104 * scale);
|
||||
int shelf_h = ry + item_h - shelf_y;
|
||||
|
||||
QRect shelf_rect(0, shelf_y, w, shelf_h);
|
||||
|
||||
// Drop shadow from back edge
|
||||
QLinearGradient back_shadow(0, shelf_y, 0, shelf_y - 12 * scale);
|
||||
back_shadow.setColorAt(0.0, QColor(0, 0, 0, 100));
|
||||
back_shadow.setColorAt(1.0, Qt::transparent);
|
||||
painter.fillRect(QRect(0, shelf_y - 12 * scale, w, 12 * scale), back_shadow);
|
||||
|
||||
painter.fillRect(QRect(0, shelf_y, w, qMax(1, static_cast<int>(4 * scale))),
|
||||
QColor(0, 0, 0, 180));
|
||||
|
||||
QLinearGradient wood_grad(0, shelf_y, 0, shelf_y + shelf_h);
|
||||
wood_grad.setColorAt(0.00, QColor(110, 65, 20));
|
||||
wood_grad.setColorAt(0.25, QColor(88, 50, 14));
|
||||
wood_grad.setColorAt(0.65, QColor(65, 34, 8));
|
||||
wood_grad.setColorAt(1.00, QColor(42, 22, 4));
|
||||
painter.fillRect(shelf_rect, wood_grad);
|
||||
|
||||
painter.save();
|
||||
painter.setClipRect(shelf_rect);
|
||||
for (int g = 0; g < 6; ++g) {
|
||||
int gy = shelf_y + (shelf_h * g) / 6;
|
||||
int alpha = (g % 2 == 0) ? 28 : 14;
|
||||
painter.setPen(QPen(QColor(180, 110, 40, alpha),
|
||||
qMax(0.8, 0.8 * static_cast<double>(scale))));
|
||||
painter.drawLine(0, gy, w, gy + qMax(1, static_cast<int>(2 * scale)));
|
||||
}
|
||||
painter.restore();
|
||||
|
||||
QLinearGradient hl_grad(0, shelf_y, 0, shelf_y + shelf_hl);
|
||||
hl_grad.setColorAt(0.0, QColor(215, 145, 58, 245));
|
||||
hl_grad.setColorAt(0.6, QColor(175, 105, 35, 120));
|
||||
hl_grad.setColorAt(1.0, QColor(140, 80, 22, 0));
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(hl_grad);
|
||||
painter.drawRect(QRect(0, shelf_y, w, shelf_hl));
|
||||
|
||||
painter.fillRect(QRect(0, shelf_rect.bottom() - shelf_edge + 1, w, shelf_edge),
|
||||
QColor(10, 4, 1, 220));
|
||||
}
|
||||
}
|
||||
|
||||
QListView::paintEvent(e);
|
||||
}
|
||||
};
|
||||
|
||||
GameGridView::GameGridView(QWidget* parent) : QWidget(parent) {
|
||||
@@ -26,14 +156,17 @@ GameGridView::GameGridView(QWidget* parent) : QWidget(parent) {
|
||||
m_scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_scroll_area->setStyleSheet(QStringLiteral("QScrollArea { background: transparent; }"));
|
||||
|
||||
m_container = new QWidget(m_scroll_area);
|
||||
m_container = new WoodBackgroundContainer(m_scroll_area);
|
||||
m_layout = new QVBoxLayout(m_container);
|
||||
m_layout->setContentsMargins(0, 20, 0, 20);
|
||||
m_layout->setSpacing(0);
|
||||
|
||||
m_top_help = new QLabel(m_container);
|
||||
m_top_help->setText(tr("if using controller* Press X for Next Alphabetical Letter | Press -/R/ZR for Details Tab | Press B for Back to List"));
|
||||
m_top_help->setStyleSheet(QStringLiteral("QLabel { color: rgba(255, 255, 255, 140); font-weight: bold; font-family: 'Outfit', 'Inter', sans-serif; font-size: 14px; }"));
|
||||
m_top_help->setText(tr("if using controller* Press X for Next Alphabetical Letter | Press "
|
||||
"-/R/ZR for Details Tab | Press B for Back to List"));
|
||||
m_top_help->setStyleSheet(
|
||||
QStringLiteral("QLabel { color: rgba(255, 255, 255, 140); font-weight: bold; font-family: "
|
||||
"'Outfit', 'Inter', sans-serif; font-size: 14px; }"));
|
||||
m_top_help->setAlignment(Qt::AlignCenter);
|
||||
m_layout->addSpacing(10);
|
||||
m_layout->addWidget(m_top_help);
|
||||
@@ -55,26 +188,55 @@ GameGridView::GameGridView(QWidget* parent) : QWidget(parent) {
|
||||
delegate = new GameGridDelegate(view, this);
|
||||
view->setItemDelegate(delegate);
|
||||
connect(view, &QListView::activated, this, &GameGridView::itemActivated);
|
||||
connect(view, &QListView::doubleClicked, this, &GameGridView::itemActivated);
|
||||
};
|
||||
|
||||
m_fav_label = new QLabel(tr("★ FAVORITES"), m_container);
|
||||
m_fav_label->setStyleSheet(QStringLiteral("QLabel { color: #ffd700; font-weight: bold; font-size: 18px; padding-left: 24px; margin-bottom: 10px; }"));
|
||||
m_fav_label = new QLabel(tr("★ FAVORITES"), m_container);
|
||||
m_fav_label->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: #f5f5f5; font-weight: bold; font-size: 16px; "
|
||||
"padding: 8px 0px 6px 28px; "
|
||||
"border-bottom: 2px solid rgba(255, 255, 255, 90); background: transparent; }"));
|
||||
QGraphicsDropShadowEffect* fav_shadow = new QGraphicsDropShadowEffect(m_fav_label);
|
||||
fav_shadow->setBlurRadius(3);
|
||||
fav_shadow->setOffset(1, 1);
|
||||
fav_shadow->setColor(QColor(0, 0, 0, 255));
|
||||
m_fav_label->setGraphicsEffect(fav_shadow);
|
||||
m_layout->addWidget(m_fav_label);
|
||||
setupView(m_fav_view, m_fav_delegate);
|
||||
m_fav_view->setStyleSheet(
|
||||
QStringLiteral("QListView { background: transparent; border: none; }"
|
||||
"QListView::item { background: transparent; }"
|
||||
"QListView::item:hover { background: transparent; }"
|
||||
"QListView::item:selected { background: transparent; }"));
|
||||
m_layout->addWidget(m_fav_view);
|
||||
|
||||
m_main_label = new QLabel(tr("ALL GAMES"), m_container);
|
||||
m_main_label->setStyleSheet(QStringLiteral("QLabel { color: #0096ff; font-weight: bold; font-size: 18px; padding-left: 24px; margin-top: 20px; margin-bottom: 10px; }"));
|
||||
m_main_label->setStyleSheet(
|
||||
QStringLiteral("QLabel { color: #f5f5f5; font-weight: bold; font-size: 16px; "
|
||||
"padding: 8px 0px 6px 28px; "
|
||||
"border-bottom: 2px solid rgba(255, 255, 255, 90); background: transparent; "
|
||||
"margin-top: 18px; }"));
|
||||
QGraphicsDropShadowEffect* main_shadow = new QGraphicsDropShadowEffect(m_main_label);
|
||||
main_shadow->setBlurRadius(3);
|
||||
main_shadow->setOffset(1, 1);
|
||||
main_shadow->setColor(QColor(0, 0, 0, 255));
|
||||
m_main_label->setGraphicsEffect(main_shadow);
|
||||
m_layout->addWidget(m_main_label);
|
||||
setupView(m_main_view, m_main_delegate);
|
||||
m_main_view->setStyleSheet(
|
||||
QStringLiteral("QListView { background: transparent; border: none; }"
|
||||
"QListView::item { background: transparent; }"
|
||||
"QListView::item:hover { background: transparent; }"
|
||||
"QListView::item:selected { background: transparent; }"));
|
||||
m_layout->addWidget(m_main_view);
|
||||
|
||||
m_bottom_hint = new QLabel(m_container);
|
||||
m_bottom_hint->setText(tr("*You can use your Mouse Wheel or the Scrollbar to navigate the Grid View*"));
|
||||
m_bottom_hint->setStyleSheet(QStringLiteral("QLabel { color: rgba(255, 255, 255, 100); font-style: italic; font-size: 13px; }"));
|
||||
m_bottom_hint->setText(
|
||||
tr("*You can use your Mouse Wheel or the Scrollbar to navigate the Grid View*"));
|
||||
m_bottom_hint->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: rgba(255, 255, 255, 100); font-style: italic; font-size: 13px; }"));
|
||||
m_bottom_hint->setAlignment(Qt::AlignCenter);
|
||||
m_layout->addWidget(m_bottom_hint);
|
||||
|
||||
m_scroll_area->setWidget(m_container);
|
||||
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -84,12 +246,26 @@ GameGridView::GameGridView(QWidget* parent) : QWidget(parent) {
|
||||
}
|
||||
|
||||
void GameGridView::ApplyTheme() {
|
||||
QString style = QStringLiteral(
|
||||
m_container->setAutoFillBackground(false);
|
||||
m_scroll_area->setAutoFillBackground(false);
|
||||
if (m_scroll_area->viewport()) {
|
||||
m_scroll_area->viewport()->setAutoFillBackground(false);
|
||||
QPalette vpal = m_scroll_area->viewport()->palette();
|
||||
vpal.setColor(QPalette::Base, Qt::transparent);
|
||||
vpal.setColor(QPalette::Window, Qt::transparent);
|
||||
m_scroll_area->viewport()->setPalette(vpal);
|
||||
}
|
||||
m_scroll_area->setStyleSheet(
|
||||
QStringLiteral("QScrollArea { background: transparent; border: none; } QScrollArea > "
|
||||
"QWidget > QWidget { background: transparent; }"));
|
||||
|
||||
QString list_style = QStringLiteral(
|
||||
"QListView { background: transparent; border: none; outline: 0; padding: 0px; }"
|
||||
"QListView::item { padding: 0px; margin: 0px; background: transparent; }"
|
||||
);
|
||||
m_fav_view->setStyleSheet(style);
|
||||
m_main_view->setStyleSheet(style);
|
||||
"QListView::item:hover { background: transparent; }"
|
||||
"QListView::item:selected { background: transparent; }");
|
||||
m_fav_view->setStyleSheet(list_style);
|
||||
m_main_view->setStyleSheet(list_style);
|
||||
}
|
||||
|
||||
void GameGridView::setModels(QAbstractItemModel* fav_model, QAbstractItemModel* main_model) {
|
||||
@@ -101,8 +277,8 @@ void GameGridView::setModels(QAbstractItemModel* fav_model, QAbstractItemModel*
|
||||
m_fav_view->setVisible(has_favs);
|
||||
|
||||
if (m_fav_view->selectionModel()) {
|
||||
connect(m_fav_view->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, [this](const QModelIndex& current) {
|
||||
connect(m_fav_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
|
||||
[this](const QModelIndex& current) {
|
||||
if (current.isValid()) {
|
||||
emit itemSelectionChanged(current);
|
||||
m_main_view->clearSelection();
|
||||
@@ -111,8 +287,8 @@ void GameGridView::setModels(QAbstractItemModel* fav_model, QAbstractItemModel*
|
||||
});
|
||||
}
|
||||
if (m_main_view->selectionModel()) {
|
||||
connect(m_main_view->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, [this](const QModelIndex& current) {
|
||||
connect(m_main_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
|
||||
[this](const QModelIndex& current) {
|
||||
if (current.isValid()) {
|
||||
emit itemSelectionChanged(current);
|
||||
m_fav_view->clearSelection();
|
||||
@@ -124,20 +300,25 @@ void GameGridView::setModels(QAbstractItemModel* fav_model, QAbstractItemModel*
|
||||
}
|
||||
|
||||
QModelIndex GameGridView::currentIndex() const {
|
||||
if (m_fav_view->hasFocus()) return m_fav_view->currentIndex();
|
||||
if (m_fav_view->hasFocus())
|
||||
return m_fav_view->currentIndex();
|
||||
return m_main_view->currentIndex();
|
||||
}
|
||||
|
||||
void GameGridView::setCurrentIndex(const QModelIndex& index) {
|
||||
if (index.model() == m_fav_view->model()) m_fav_view->setCurrentIndex(index);
|
||||
else m_main_view->setCurrentIndex(index);
|
||||
if (index.model() == m_fav_view->model())
|
||||
m_fav_view->setCurrentIndex(index);
|
||||
else
|
||||
m_main_view->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
QModelIndex GameGridView::indexAt(const QPoint& p) const {
|
||||
QPoint fp = m_fav_view->mapFrom(this, p);
|
||||
if (m_fav_view->rect().contains(fp)) return m_fav_view->indexAt(fp);
|
||||
if (m_fav_view->rect().contains(fp))
|
||||
return m_fav_view->indexAt(fp);
|
||||
QPoint mp = m_main_view->mapFrom(this, p);
|
||||
if (m_main_view->rect().contains(mp)) return m_main_view->indexAt(mp);
|
||||
if (m_main_view->rect().contains(mp))
|
||||
return m_main_view->indexAt(mp);
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
@@ -146,18 +327,22 @@ void GameGridView::setControllerFocus(bool focus) {
|
||||
if (focus) {
|
||||
if (m_fav_view->isVisible() && m_fav_view->model()->rowCount() > 0) {
|
||||
m_fav_view->setFocus();
|
||||
if (!m_fav_view->currentIndex().isValid()) m_fav_view->setCurrentIndex(m_fav_view->model()->index(0, 0));
|
||||
if (!m_fav_view->currentIndex().isValid())
|
||||
m_fav_view->setCurrentIndex(m_fav_view->model()->index(0, 0));
|
||||
} else {
|
||||
m_main_view->setFocus();
|
||||
if (!m_main_view->currentIndex().isValid()) m_main_view->setCurrentIndex(m_main_view->model()->index(0, 0));
|
||||
if (!m_main_view->currentIndex().isValid())
|
||||
m_main_view->setCurrentIndex(m_main_view->model()->index(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameGridView::onNavigated(int dx, int dy) {
|
||||
if (!m_has_focus) return;
|
||||
if (!m_has_focus)
|
||||
return;
|
||||
QListView* current = m_fav_view->hasFocus() ? m_fav_view : m_main_view;
|
||||
if (!current->model()) return;
|
||||
if (!current->model())
|
||||
return;
|
||||
|
||||
QModelIndex idx = current->currentIndex();
|
||||
if (!idx.isValid()) {
|
||||
@@ -176,14 +361,19 @@ void GameGridView::onNavigated(int dx, int dy) {
|
||||
}
|
||||
if (dy < 0 && row < cols && current == m_main_view && m_fav_view->isVisible()) {
|
||||
m_fav_view->setFocus();
|
||||
m_fav_view->setCurrentIndex(m_fav_view->model()->index(m_fav_view->model()->rowCount() - 1, 0));
|
||||
m_fav_view->setCurrentIndex(
|
||||
m_fav_view->model()->index(m_fav_view->model()->rowCount() - 1, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (dx > 0) row++;
|
||||
else if (dx < 0) row--;
|
||||
if (dy > 0) row += cols;
|
||||
else if (dy < 0) row -= cols;
|
||||
if (dx > 0)
|
||||
row++;
|
||||
else if (dx < 0)
|
||||
row--;
|
||||
if (dy > 0)
|
||||
row += cols;
|
||||
else if (dy < 0)
|
||||
row -= cols;
|
||||
|
||||
row = qBound(0, row, total - 1);
|
||||
current->setCurrentIndex(current->model()->index(row, 0));
|
||||
@@ -191,9 +381,11 @@ void GameGridView::onNavigated(int dx, int dy) {
|
||||
}
|
||||
|
||||
void GameGridView::onActivated() {
|
||||
if (!m_has_focus) return;
|
||||
if (!m_has_focus)
|
||||
return;
|
||||
QModelIndex cur = currentIndex();
|
||||
if (cur.isValid()) emit itemActivated(cur);
|
||||
if (cur.isValid())
|
||||
emit itemActivated(cur);
|
||||
}
|
||||
|
||||
void GameGridView::onCancelled() {
|
||||
@@ -210,9 +402,11 @@ void GameGridView::keyPressEvent(QKeyEvent* event) {
|
||||
m_main_view->setCurrentIndex(m_main_view->model()->index(0, 0));
|
||||
return;
|
||||
}
|
||||
if (!current->model()) return;
|
||||
if (!current->model())
|
||||
return;
|
||||
QModelIndex cur = current->currentIndex();
|
||||
if (!cur.isValid()) return;
|
||||
if (!cur.isValid())
|
||||
return;
|
||||
QString title = cur.data(Qt::DisplayRole).toString();
|
||||
QChar cc = title.isEmpty() ? QLatin1Char(' ') : title[0].toUpper();
|
||||
int tot = current->model()->rowCount();
|
||||
@@ -231,16 +425,30 @@ void GameGridView::keyPressEvent(QKeyEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
void GameGridView::showEvent(QShowEvent* event) {
|
||||
QWidget::showEvent(event);
|
||||
QTimer::singleShot(10, this, &GameGridView::UpdateGridSize);
|
||||
}
|
||||
|
||||
void GameGridView::resizeEvent(QResizeEvent* event) {
|
||||
QWidget::resizeEvent(event);
|
||||
UpdateGridSize();
|
||||
}
|
||||
|
||||
void GameGridView::UpdateGridSize() {
|
||||
if (!m_scroll_area || !m_scroll_area->viewport())
|
||||
return;
|
||||
const int is = UISettings::values.game_icon_size.GetValue();
|
||||
const float s = static_cast<float>(is) / 128.0f;
|
||||
const int bw = qMax(is + static_cast<int>(40 * s), is + 24);
|
||||
const int tw = m_scroll_area->viewport()->width();
|
||||
|
||||
// If width is too small (widget hidden or layouting), retry shortly
|
||||
if (tw < 100) {
|
||||
QTimer::singleShot(100, this, &GameGridView::UpdateGridSize);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tw > 50) {
|
||||
int cols = qMax(1, tw / bw);
|
||||
int aw = tw / cols;
|
||||
@@ -262,18 +470,23 @@ void GameGridView::UpdateLayoutHeights() {
|
||||
}
|
||||
const int count = view->model()->rowCount();
|
||||
const QSize gs = view->gridSize();
|
||||
if (gs.width() <= 0 || gs.height() <= 0) return;
|
||||
if (gs.width() <= 0 || gs.height() <= 0)
|
||||
return;
|
||||
const int tw = m_scroll_area->viewport()->width();
|
||||
const int cols = qMax(1, tw / gs.width());
|
||||
const int rows = (count + cols - 1) / cols;
|
||||
view->setFixedHeight(rows * gs.height() + 40);
|
||||
view->setFixedHeight(rows * gs.height() + 60);
|
||||
};
|
||||
updateHeight(m_fav_view);
|
||||
updateHeight(m_main_view);
|
||||
}
|
||||
|
||||
QAbstractItemModel* GameGridView::favModel() const { return m_fav_view->model(); }
|
||||
QAbstractItemModel* GameGridView::mainModel() const { return m_main_view->model(); }
|
||||
QAbstractItemModel* GameGridView::favModel() const {
|
||||
return m_fav_view->model();
|
||||
}
|
||||
QAbstractItemModel* GameGridView::mainModel() const {
|
||||
return m_main_view->model();
|
||||
}
|
||||
|
||||
QItemSelectionModel* GameGridView::selectionModel() const {
|
||||
return m_main_view->selectionModel();
|
||||
@@ -287,14 +500,16 @@ void GameGridView::setModel(QAbstractItemModel* model) {
|
||||
UpdateGridSize();
|
||||
}
|
||||
QRect GameGridView::visualRect(const QModelIndex& index) const {
|
||||
if (index.model() == m_fav_view->model()) return m_fav_view->visualRect(index);
|
||||
if (index.model() == m_fav_view->model())
|
||||
return m_fav_view->visualRect(index);
|
||||
return m_main_view->visualRect(index);
|
||||
}
|
||||
QWidget* GameGridView::viewport() const { return m_scroll_area->viewport(); }
|
||||
QWidget* GameGridView::viewport() const {
|
||||
return m_scroll_area->viewport();
|
||||
}
|
||||
void GameGridView::scrollTo(const QModelIndex& index) {
|
||||
QListView* view = (index.model() == m_fav_view->model()) ? m_fav_view : m_main_view;
|
||||
QRect rect = view->visualRect(index);
|
||||
rect.moveTop(rect.top() + view->y());
|
||||
m_scroll_area->ensureVisible(rect.center().x(), rect.center().y(), 50, 50);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ signals:
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user