feat(market): merge price + chart into one card; simplify stats
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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<std::chrono::seconds>(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;
|
||||
|
||||
Reference in New Issue
Block a user