diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss index c3db81f4e3..c6f297dac8 100644 --- a/dist/qt_themes/default/style.qss +++ b/dist/qt_themes/default/style.qss @@ -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 */ diff --git a/dist/qt_themes/default_dark/style.qss b/dist/qt_themes/default_dark/style.qss index 8135a4ba42..5e1256a332 100644 --- a/dist/qt_themes/default_dark/style.qss +++ b/dist/qt_themes/default_dark/style.qss @@ -695,7 +695,7 @@ QMenuBar { QMenuBar::item { background-color: transparent; - padding: 4px 10px; + padding: 18px 18px; color: #d0d0d0; } diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index 41638d333d..b93439c84c 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -164,7 +164,7 @@ QMenuBar { QMenuBar::item { background: transparent; - padding: 4px 10px; + padding: 18px 18px; } QMenuBar::item:selected { diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss index 35672edb22..ddf4717063 100644 --- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss +++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss @@ -374,7 +374,7 @@ QMenuBar { QMenuBar::item { background: transparent; - padding: 4px 10px; + padding: 18px 18px; color: #eff0f1; /* Explicitly set item text color */ } diff --git a/src/citron/game_grid_delegate.cpp b/src/citron/game_grid_delegate.cpp index 0f141c1722..b34d7d5260 100644 --- a/src/citron/game_grid_delegate.cpp +++ b/src/citron/game_grid_delegate.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #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(icon_size) / 128.0f; - return QSize(icon_size + static_cast(40 * scale), icon_size + static_cast(85 * scale)); + return QSize(icon_size + static_cast(40 * scale), + icon_size + static_cast(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(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(-4 * scale), static_cast(-4 * scale), - static_cast(4 * scale), static_cast(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(GameListItemType::Favorites)); + bool is_fav = (index.data(GameListItem::TypeRole).toInt() == + static_cast(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(); - if (pixmap.isNull()) pixmap = index.data(Qt::DecorationRole).value(); + if (pixmap.isNull()) + pixmap = index.data(Qt::DecorationRole).value(); 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(10 * scale); - int p_c = 6; - int p_w = (card_rect.width() - static_cast(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(20 * scale) + i * p_w, pins_y, p_w - static_cast(6 * scale), static_cast(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(); +} diff --git a/src/citron/game_list.cpp b/src/citron/game_list.cpp index 83d091edf6..7b1b14f99f 100644 --- a/src/citron/game_list.cpp +++ b/src/citron/game_list.cpp @@ -1398,9 +1398,17 @@ GameList::GameList(std::shared_ptr 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()) { - case GameListItemType::Game: { + case GameListItemType::Game: + case GameListItemType::Favorites: { const QString file_path = selected.data(GameListItemPath::FullPathRole).toString(); if (file_path.isEmpty()) return; diff --git a/src/citron/ui/game_grid_view.cpp b/src/citron/ui/game_grid_view.cpp index 4d9441c226..4a07eda578 100644 --- a/src/citron/ui/game_grid_view.cpp +++ b/src/citron/ui/game_grid_view.cpp @@ -1,21 +1,151 @@ +#include +#include +#include #include #include #include -#include -#include -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include "citron/ui/game_grid_view.h" +#include +#include #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(icon_size) / 128.0f; + int card_h = icon_size + static_cast(64 * scale); + + QList 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(5 * scale)); + const int shelf_edge = qMax(1, static_cast(2 * scale)); + + for (int ry : row_tops) { + int shelf_y = ry + static_cast(12 * scale) + card_h; + int item_h = icon_size + static_cast(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(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(scale)))); + painter.drawLine(0, gy, w, gy + qMax(1, static_cast(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(is) / 128.0f; const int bw = qMax(is + static_cast(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); } - diff --git a/src/citron/ui/game_grid_view.h b/src/citron/ui/game_grid_view.h index fa34a5b58a..729ba69bf0 100644 --- a/src/citron/ui/game_grid_view.h +++ b/src/citron/ui/game_grid_view.h @@ -54,6 +54,7 @@ signals: protected: void keyPressEvent(QKeyEvent* event) override; + void showEvent(QShowEvent* event) override; void resizeEvent(QResizeEvent* event) override; private: