fix(ui): theme the balance-card coin icon per skin too (not just the header)

The prominent DragonX mark on the Overview (next to the balance) is the coin /
currency icon (coin_logo_tex_), a separate texture from the header logo — and it
was still loaded from the fixed logo_dragonx_128.png, so it never recolored when
switching themes. Route it through the same themed SVG: ensureLogoTexture() now
rasterizes BOTH the header logo and the coin icon from logo_dragonx.svg recolored
to the theme accent, re-rasterizing both together on a skin / dark-light change
(coin done first, since the header branch early-returns on success). The old
one-shot PNG coin-logo load in render() is removed (superseded), the PNG remains
a fallback, and the coin texture is now freed on theme switch (was leaked).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-21 14:02:13 -05:00
parent 3a2be661ea
commit e7f38c2a45

View File

@@ -1378,6 +1378,29 @@ void App::ensureLogoTexture()
if (logo_tex_) util::DestroyTexture(logo_tex_); // free the previous texture before replacing if (logo_tex_) util::DestroyTexture(logo_tex_); // free the previous texture before replacing
logo_tex_ = 0; logo_w_ = 0; logo_h_ = 0; logo_tex_ = 0; logo_w_ = 0; logo_h_ = 0;
// Coin/currency icon (the big DragonX mark on the balance card) — the SAME themed SVG recolored to
// the accent, rasterized here so it changes with the skin too (done first, since the header block
// below early-returns on success). Falls back to the PNG coin icon if rasterization fails.
if (coin_logo_tex_) util::DestroyTexture(coin_logo_tex_);
coin_logo_tex_ = 0; coin_logo_w_ = 0; coin_logo_h_ = 0;
coin_logo_loaded_ = true;
if (!util::LoadTextureFromSvg(embedded::kLogoDragonXSvg, 128, logoAccent, IM_COL32(255, 255, 255, 255),
&coin_logo_tex_, &coin_logo_w_, &coin_logo_h_)) {
auto coinElem = ui::schema::UI().drawElement("components.main-window", "coin-icon");
auto cit = coinElem.extraColors.find("icon");
std::string coinFile = (cit != coinElem.extraColors.end() && !cit->second.empty())
? cit->second : "logos/logo_dragonx_128.png";
std::string coinPath = util::getExecutableDirectory() + "/res/img/" + coinFile;
std::error_code coinEc;
if (!(std::filesystem::exists(coinPath, coinEc) &&
util::LoadTextureFromFile(coinPath.c_str(), &coin_logo_tex_, &coin_logo_w_, &coin_logo_h_))) {
std::string coinBasename = std::filesystem::path(coinFile).filename().string();
const auto* coinRes = resources::getEmbeddedResource(coinBasename);
if (coinRes && coinRes->data && coinRes->size > 0)
util::LoadTextureFromMemory(coinRes->data, coinRes->size, &coin_logo_tex_, &coin_logo_w_, &coin_logo_h_);
}
}
// 0) DragonX mark — rasterize the embedded SVG recolored to the theme (body = accent, detail = white) // 0) DragonX mark — rasterize the embedded SVG recolored to the theme (body = accent, detail = white)
// at ~2x the 128px viewBox for crisp downscaling. This is the branding on every skin; the per-skin // at ~2x the 128px viewBox for crisp downscaling. This is the branding on every skin; the per-skin
// PNG path below is only a fallback if rasterization ever fails. // PNG path below is only a fallback if rasterization ever fails.
@@ -1639,36 +1662,7 @@ void App::render()
// (DragonX logo is loaded at the top of render() via ensureLogoTexture().) // (DragonX logo is loaded at the top of render() via ensureLogoTexture().)
// Load coin logo texture lazily (DragonX currency icon for balance tab) // (coin logo is loaded/themed at the top of render() via ensureLogoTexture().)
if (!coin_logo_loaded_) {
coin_logo_loaded_ = true;
coin_logo_tex_ = 0; coin_logo_w_ = 0; coin_logo_h_ = 0;
// Read coin icon filename from ui.toml
auto coinElem = ui::schema::UI().drawElement("components.main-window", "coin-icon");
auto cit = coinElem.extraColors.find("icon");
std::string coinFile = (cit != coinElem.extraColors.end() && !cit->second.empty())
? cit->second : "logos/logo_dragonx_128.png";
std::string coinPath = util::getExecutableDirectory() + "/res/img/" + coinFile;
std::error_code coinEc;
if (std::filesystem::exists(coinPath, coinEc) &&
util::LoadTextureFromFile(coinPath.c_str(), &coin_logo_tex_, &coin_logo_w_, &coin_logo_h_)) {
DEBUG_LOGF("Loaded coin logo from %s (%dx%d)\n", coinPath.c_str(), coin_logo_w_, coin_logo_h_);
} else {
// Try embedded resource fallback (Windows single-file distribution)
std::string coinBasename = std::filesystem::path(coinFile).filename().string();
const auto* coinRes = resources::getEmbeddedResource(coinBasename);
if (coinRes && coinRes->data && coinRes->size > 0) {
if (util::LoadTextureFromMemory(coinRes->data, coinRes->size, &coin_logo_tex_, &coin_logo_w_, &coin_logo_h_)) {
DEBUG_LOGF("Loaded coin logo from embedded: %s (%dx%d)\n", coinBasename.c_str(), coin_logo_w_, coin_logo_h_);
} else {
DEBUG_LOGF("Note: Failed to decode embedded coin logo\n");
}
} else {
DEBUG_LOGF("Note: Coin logo not found at %s\n", coinPath.c_str());
}
}
}
if (logo_tex_ != 0) { if (logo_tex_ != 0) {
sbStatus.logoTexID = logo_tex_; sbStatus.logoTexID = logo_tex_;
@@ -2458,6 +2452,7 @@ void App::reloadThemeImages(const std::string& bgPath, const std::string& logoPa
logo_w_ = 0; logo_w_ = 0;
logo_h_ = 0; logo_h_ = 0;
coin_logo_loaded_ = false; coin_logo_loaded_ = false;
if (coin_logo_tex_) util::DestroyTexture(coin_logo_tex_);
coin_logo_tex_ = 0; coin_logo_tex_ = 0;
coin_logo_w_ = 0; coin_logo_w_ = 0;
coin_logo_h_ = 0; coin_logo_h_ = 0;