From 1faaa54a07452b36538512a90b7e9be85c4d2b91 Mon Sep 17 00:00:00 2001 From: DanS Date: Wed, 1 Jul 2026 21:31:29 -0500 Subject: [PATCH] feat(market): merge price + chart into one card; simplify stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Combine the top two Market cards: the price-summary hero and the chart now share a single glass panel (one panel spans the price/stats header + the chart drawn below it; the chart no longer draws its own panel or an inter-card gap). Simplify the stat strip from three columns to two — drop the niche BTC-price column, keeping 24h Volume + Market Cap — and remove the "updated Ns ago" staleness line (data freshness is still shown by the attribution "Updated" line under the pair buttons). Full-node + lite build clean; ctest green; source hygiene clean. Completes the Market "round buttons + combine/simplify top cards" item. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/market_tab.cpp | 47 +++++++++++------------------------ 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index f440649..cb7287f 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -162,7 +162,11 @@ void RenderMarketTab(App* app) float statsRowH = ovFont->LegacySize + Layout::spacingXs() + sub1->LegacySize + pad; float cardH = std::max(heroMinH + statsRowH + pad, (S.drawElement("tabs.market", "hero-card-height").size + statsRowH + pad) * vs); ImVec2 cardMax(cardMin.x + availWidth, cardMin.y + cardH); - DrawGlassPanel(dl, cardMin, cardMax, glassSpec); + // Combined hero + chart: draw ONE glass panel spanning the price/stats header AND the + // chart below it. The chart block no longer draws its own panel or an inter-card gap. + float mergedChartH = std::max(60.0f, chartElem.height * vs); + ImVec2 mergedMax(cardMin.x + availWidth, cardMax.y + mergedChartH); + DrawGlassPanel(dl, cardMin, mergedMax, glassSpec); float cx = cardMin.x + Layout::spacingLg(); float cy = cardMin.y + Layout::spacingLg(); @@ -201,20 +205,19 @@ void RenderMarketTab(App* app) ImVec2(cardMax.x - rnd * 0.5f, sepY), WithAlpha(OnSurface(), 15), 1.0f * dp); - // ---- STATS ROW (BTC Price | Volume | Market Cap) ---- + // ---- STATS ROW (24h Volume | Market Cap) ---- + // (BTC-price column + the "updated Ns ago" staleness line were dropped to + // simplify; freshness is still shown by the attribution "Updated" line.) float statsY = sepY + Layout::spacingSm(); - float colW = (availWidth - Layout::spacingLg() * 2) / 3.0f; + float colW = (availWidth - Layout::spacingLg() * 2) / 2.0f; struct StatItem { const char* label; std::string value; ImU32 valueCol; }; - StatItem stats[3] = { - {TR("market_btc_price"), "", OnSurface()}, + StatItem stats[2] = { {TR("market_24h_volume"), FormatCompactUSD(market.volume_24h), OnSurface()}, {TR("market_cap"), FormatCompactUSD(market.market_cap), OnSurface()}, }; - snprintf(buf, sizeof(buf), "%.10f", market.price_btc); - stats[0].value = buf; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 2; i++) { float sx = cardMin.x + Layout::spacingLg() + i * colW; float centerX = sx + colW * 0.5f; @@ -230,26 +233,6 @@ void RenderMarketTab(App* app) ImVec2(centerX - valSz.x * 0.5f, valY), stats[i].valueCol, stats[i].value.c_str()); } - // ---- STALENESS INDICATOR ---- - { - auto fetchTime = market.last_fetch_time; - if (fetchTime.time_since_epoch().count() > 0) { - auto elapsed = std::chrono::steady_clock::now() - fetchTime; - int ageSecs = (int)std::chrono::duration_cast(elapsed).count(); - bool stale = ageSecs > 300; // 5 minutes - if (ageSecs < 60) - snprintf(buf, sizeof(buf), "%s %ds %s", stale ? ICON_MD_WARNING : "", ageSecs, TR("ago")); - else - snprintf(buf, sizeof(buf), "%s %dm %s", stale ? ICON_MD_WARNING : "", ageSecs / 60, TR("ago")); - ImFont* staleFont = capFont; - ImU32 staleCol = stale ? Warning() : WithAlpha(OnSurface(), 100); - float staleY = statsY + ovFont->LegacySize + Layout::spacingXs() + sub1->LegacySize + Layout::spacingSm(); - ImVec2 staleSz = staleFont->CalcTextSizeA(staleFont->LegacySize, FLT_MAX, 0, buf); - dl->AddText(staleFont, staleFont->LegacySize, - ImVec2(cardMin.x + Layout::spacingLg(), staleY), staleCol, buf); - } - } - // ---- TRADE BUTTON (top-right of card) ---- if (!currentExchange.pairs.empty()) { const char* pairName = currentExchange.pairs[s_pair_idx].displayName.c_str(); @@ -315,13 +298,13 @@ void RenderMarketTab(App* app) } } + // No inter-card gap — the chart is drawn immediately below, inside the same panel. ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y)); ImGui::Dummy(ImVec2(availWidth, 0)); - ImGui::Dummy(ImVec2(0, gap)); } // ================================================================ - // PRICE CHART — Custom drawn inside glass panel (matches app design) + // PRICE CHART — drawn inside the combined hero card's glass panel (above) // ================================================================ { // Plot the REAL accumulated price history (one point per refresh, oldest→newest). @@ -330,11 +313,11 @@ void RenderMarketTab(App* app) s_price_history = market.price_history; s_history_initialized = true; - // Chart height from schema + // Chart height from schema. No glass panel here — it shares the combined hero+chart + // card's panel drawn in PRICE SUMMARY above (mergedChartH uses this same height). float chartH = std::max(60.0f, chartElem.height * vs); ImVec2 chartMin = ImGui::GetCursorScreenPos(); ImVec2 chartMax(chartMin.x + availWidth, chartMin.y + chartH); - DrawGlassPanel(dl, chartMin, chartMax, glassSpec); if (!s_price_history.empty() && s_price_history.size() >= 2) { float chartPad = pad;