diff --git a/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.cpp b/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.cpp index 15f201e9..967eaa8f 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.cpp +++ b/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.cpp @@ -39,7 +39,10 @@ ReloadableGlobalHiscores::ReloadableGlobalHiscores( int displayOffset, FontManager* font, float baseColumnPadding, - float baseRowPadding) + float baseRowPadding, + std::string renderType, + float qrDelaySec, + float qrFadeSec) : Component(p) , fontInst_(font) , textFormat_(std::move(textFormat)) @@ -81,7 +84,29 @@ ReloadableGlobalHiscores::ReloadableGlobalHiscores( , qrDelaySec_(2.0f) , qrFadeSec_(1.0f) , qrPlacement_(QrPlacement::TopLeft) - , qrMarginPx_(8) { + , qrMarginPx_(8) + , renderMode_(RenderMode::Both) + , renderTable_(true) + , renderQr_(true) { + + const std::string mode = Utils::toLower(Utils::trimEnds(renderType)); + if (mode == "qr") { + renderMode_ = RenderMode::QrOnly; + renderTable_ = false; + renderQr_ = true; + } + else if (mode == "table") { + renderMode_ = RenderMode::TableOnly; + renderTable_ = true; + renderQr_ = false; + } + + if (std::isfinite(qrDelaySec) && qrDelaySec >= 0.0f) { + qrDelaySec_ = qrDelaySec; + } + if (std::isfinite(qrFadeSec) && qrFadeSec >= 0.0f) { + qrFadeSec_ = qrFadeSec; + } } ReloadableGlobalHiscores::~ReloadableGlobalHiscores() { @@ -1167,59 +1192,62 @@ void ReloadableGlobalHiscores::reloadTexture() { return; } + std::vector qrTextures(Nvisible, nullptr); + std::vector> qrSizes(Nvisible, { 0,0 }); + // --- Resolve the list of QR IDs for the selected game --- - std::vector gameIds; - if (!selectedItem->iscoredId.empty()) { - Utils::listToVector(selectedItem->iscoredId, gameIds, ','); - } + if (renderQr_) { + std::vector gameIds; + if (!selectedItem->iscoredId.empty()) { + Utils::listToVector(selectedItem->iscoredId, gameIds, ','); + } - // Check QR cache validity by IDs - bool qrCacheValid = (cachedQrGameIds_.size() == gameIds.size()); - if (qrCacheValid) { - for (size_t i = 0; i < gameIds.size(); ++i) { - if (i >= cachedQrGameIds_.size() || cachedQrGameIds_[i] != gameIds[i]) { - qrCacheValid = false; break; + // Check QR cache validity by IDs + bool qrCacheValid = (cachedQrGameIds_.size() == gameIds.size()); + if (qrCacheValid) { + for (size_t i = 0; i < gameIds.size(); ++i) { + if (i >= cachedQrGameIds_.size() || cachedQrGameIds_[i] != gameIds[i]) { + qrCacheValid = false; break; + } } } - } - - // (Re)load QR textures only if IDs changed - if (!qrCacheValid) { - for (SDL_Texture* tex : cachedQrTextures_) if (tex) SDL_DestroyTexture(tex); - cachedQrTextures_.clear(); - cachedQrSizes_.clear(); - cachedQrGameIds_.clear(); - cachedQrTextures_.resize(gameIds.size(), nullptr); - cachedQrSizes_.resize(gameIds.size(), { 0,0 }); - - for (size_t i = 0; i < gameIds.size(); ++i) { - if (gameIds[i].empty()) continue; - const std::string path = Configuration::absolutePath + "/iScored/qr/" + gameIds[i] + ".png"; - if (SDL_Surface* surf = IMG_Load(path.c_str())) { - if (SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf)) { - SDL_SetTextureScaleMode(tex, SDL_ScaleModeNearest); - SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); // set once - cachedQrTextures_[i] = tex; - cachedQrSizes_[i] = { surf->w, surf->h }; + // (Re)load QR textures only if IDs changed + if (!qrCacheValid) { + for (SDL_Texture* tex : cachedQrTextures_) if (tex) SDL_DestroyTexture(tex); + cachedQrTextures_.clear(); + cachedQrSizes_.clear(); + cachedQrGameIds_.clear(); + + cachedQrTextures_.resize(gameIds.size(), nullptr); + cachedQrSizes_.resize(gameIds.size(), { 0,0 }); + + for (size_t i = 0; i < gameIds.size(); ++i) { + if (gameIds[i].empty()) continue; + const std::string path = Configuration::absolutePath + "/iScored/qr/" + gameIds[i] + ".png"; + if (SDL_Surface* surf = IMG_Load(path.c_str())) { + if (SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf)) { + SDL_SetTextureScaleMode(tex, SDL_ScaleModeNearest); + SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); // set once + cachedQrTextures_[i] = tex; + cachedQrSizes_[i] = { surf->w, surf->h }; + } + SDL_FreeSurface(surf); } - SDL_FreeSurface(surf); } - } - cachedQrGameIds_ = gameIds; - // IDs changed → sizes likely changed somewhere; mark all dirty - for (auto& L : cachedTableLayouts_) L.qrDstDirty = true; - } + cachedQrGameIds_ = gameIds; + // IDs changed → sizes likely changed somewhere; mark all dirty + for (auto& L : cachedTableLayouts_) L.qrDstDirty = true; + } - // Map cached QR textures/sizes to the visible range - std::vector qrTextures(Nvisible, nullptr); - std::vector> qrSizes(Nvisible, { 0,0 }); - for (int t = 0; t < Nvisible; ++t) { - const int gi = startIdx + t; - if (gi < (int)cachedQrTextures_.size()) { - qrTextures[t] = cachedQrTextures_[gi]; - qrSizes[t] = cachedQrSizes_[gi]; + // Map cached QR textures/sizes to the visible range + for (int t = 0; t < Nvisible; ++t) { + const int gi = startIdx + t; + if (gi < (int)cachedQrTextures_.size()) { + qrTextures[t] = cachedQrTextures_[gi]; + qrSizes[t] = cachedQrSizes_[gi]; + } } } @@ -1515,7 +1543,17 @@ void ReloadableGlobalHiscores::reloadTexture() { L.qrDstDirty = true; // size change → re-place } if (L.qrDstDirty) { - L.qrDst = placeQrInPanel(L.panelRect, L.qrSrcW, L.qrSrcH); + if (renderMode_ == RenderMode::QrOnly) { + L.qrDst = { + 0.0f, + 0.0f, + static_cast(compositeW), + static_cast(compositeH) + }; + } + else { + L.qrDst = placeQrInPanel(L.panelRect, L.qrSrcW, L.qrSrcH); + } L.qrDstDirty = false; } } @@ -1538,12 +1576,12 @@ void ReloadableGlobalHiscores::reloadTexture() { SDL_RenderClear(renderer); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); - if (tableTexture_) { + if (renderTable_ && tableTexture_) { SDL_RenderCopy(renderer, tableTexture_, nullptr, nullptr); } // Add QRs on top (if QR phase allows) - if (qrPhase_ == QrPhase::FadingIn || qrPhase_ == QrPhase::Visible) { + if (renderQr_ && (qrPhase_ == QrPhase::FadingIn || qrPhase_ == QrPhase::Visible)) { float qrAlphaF = 1.0f; if (qrPhase_ == QrPhase::FadingIn && qrFadeSec_ > 0.f) { qrAlphaF = std::clamp(qrT_ / qrFadeSec_, 0.f, 1.f); diff --git a/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.h b/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.h index 75a6dfc4..4f12d28a 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.h +++ b/RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.h @@ -32,7 +32,10 @@ class ReloadableGlobalHiscores : public Component { ReloadableGlobalHiscores(Configuration& config, std::string textFormat, Page& p, int displayOffset, FontManager* font, - float baseColumnPadding, float baseRowPadding); + float baseColumnPadding, float baseRowPadding, + std::string renderType, + float qrDelaySec, + float qrFadeSec); ~ReloadableGlobalHiscores() override; bool update(float dt) override; @@ -72,6 +75,12 @@ class ReloadableGlobalHiscores : public Component { LeftMiddle }; + enum class RenderMode { + Both, + QrOnly, + TableOnly + }; + // Per-table layout cache struct struct TableLayout { @@ -202,6 +211,9 @@ class ReloadableGlobalHiscores : public Component { // --- QR configuration --- QrPlacement qrPlacement_; // where to place QR codes int qrMarginPx_; // margin between QR and panel + RenderMode renderMode_; // both/table-only/qr-only + bool renderTable_; + bool renderQr_; // Layout cache (populated in Stage 1, reused in Stage 2) std::vector cachedTableLayouts_; diff --git a/RetroFE/Source/Graphics/PageBuilder.cpp b/RetroFE/Source/Graphics/PageBuilder.cpp index 3065b26d..8199e584 100644 --- a/RetroFE/Source/Graphics/PageBuilder.cpp +++ b/RetroFE/Source/Graphics/PageBuilder.cpp @@ -775,6 +775,8 @@ void PageBuilder::loadReloadableImages(const xml_node<>* layout, const std::stri xml_attribute<> const* locationXml = componentXml->first_attribute("location"); xml_attribute<> const* baseColumnPaddingXml = componentXml->first_attribute("baseColumnPadding"); xml_attribute<> const* baseRowPaddingXml = componentXml->first_attribute("baseRowPadding"); + xml_attribute<> const* qrDelaySecXml = componentXml->first_attribute("qrDelaySec"); + xml_attribute<> const* qrFadeSecXml = componentXml->first_attribute("qrFadeSec"); xml_attribute<> const* maxRowsXml = componentXml->first_attribute("maxRows"); xml_attribute<> const* excludedColumnsXml = componentXml->first_attribute("excludedColumns"); @@ -878,12 +880,15 @@ void PageBuilder::loadReloadableImages(const xml_node<>* layout, const std::stri else if (tagName == "reloadableGlobalHiscores") { FontManager* font = addFont(componentXml, nullptr, cMonitor); std::string textFormat = textFormatXml ? textFormatXml->value() : ""; + std::string renderType = type ? type->value() : ""; float baseColumnPadding = baseColumnPaddingXml ? Utils::convertFloat(baseColumnPaddingXml->value()) : 1.5f; float baseRowPadding = baseRowPaddingXml ? Utils::convertFloat(baseRowPaddingXml->value()) : 0.5f; + float qrDelaySec = qrDelaySecXml ? Utils::convertFloat(qrDelaySecXml->value()) : 2.0f; + float qrFadeSec = qrFadeSecXml ? Utils::convertFloat(qrFadeSecXml->value()) : 1.0f; std::string excludedColumns = excludedColumnsXml ? excludedColumnsXml->value() : ""; c = new ReloadableGlobalHiscores(config_, textFormat, *page, selectedOffset, - font, baseColumnPadding, baseRowPadding); + font, baseColumnPadding, baseRowPadding, renderType, qrDelaySec, qrFadeSec); } else if (tagName == "musicPlayer") {