From 4e15a2b1baf21558e7723a87ade3d67aa2f0d454 Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 3 Jul 2026 15:15:17 -0500 Subject: [PATCH] feat(market): 3-cell min groups, full accent outline, tighter chart, no scrollbar - Portfolio groups now default to the 3-cell minimum height (was 4). The card design adapts: below ~112px it drops the redundant DRGX footer and uses the compact hero font so a 3-cell card stays legible. - Group accent color is now a colored outline around the whole card (and the editor's live preview) instead of a stripe on the left edge; the text no longer reserves the old left-stripe offset. - Price chart: trim the empty band between the 24H VOLUME stats and the chart (smaller hero-header bottom pad), and keep the Y-axis price labels inside the card on narrow windows (clamp to the card's left edge + bump min y-axis padding 40->54) so they no longer spill onto the tab background. - Market tab no longer draws a scrollbar (NoScrollbar); the portfolio grid still scrolls via the mouse wheel. Co-Authored-By: Claude Opus 4.8 (1M context) --- res/themes/ui.toml | 2 +- src/config/settings.h | 2 +- src/ui/windows/market_tab.cpp | 45 +++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/res/themes/ui.toml b/res/themes/ui.toml index 95e1f1c..d956299 100644 --- a/res/themes/ui.toml +++ b/res/themes/ui.toml @@ -874,7 +874,7 @@ accent-stripe-inset-ratio = { size = 0.0 } accent-stripe-left-offset = { size = 0.0 } accent-stripe-width = { size = 4.0 } accent-stripe-rounding = { size = 1.5 } -chart-y-axis-min-padding = { size = 40.0 } +chart-y-axis-min-padding = { size = 54.0 } chart-y-axis-padding = { size = 70.0 } chart-dot-min-radius = { size = 1.5 } chart-dot-radius = { size = 2.0 } diff --git a/src/config/settings.h b/src/config/settings.h index c13c778..e702c53 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -96,7 +96,7 @@ public: int gridCol = -1; int gridRow = -1; int gridW = 6; - int gridH = 4; + int gridH = 3; // minimum group height (cells); card design adapts below ~4 }; // Theme diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index 7060416..17df053 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -383,21 +383,18 @@ static void RenderPortfolioEditor(App* app) ImU32 accent = s_pf_color ? (ImU32)s_pf_color : 0; GlassPanelSpec g; g.rounding = 10.0f; g.fillAlpha = 22; g.borderAlpha = 40; DrawGlassPanel(pdl, pMin, pMax, g); - if (accent) { - float bx = pMin.x + 2.0f; - pdl->AddRectFilled(ImVec2(bx, pMin.y + ppad * 0.6f), - ImVec2(bx + 3.0f, pMax.y - ppad * 0.6f), accent, 2.0f); - } + // Colored outline around the whole preview card (matches the live group cards). + if (accent) pdl->AddRect(pMin, pMax, accent, 10.0f, 0, 2.0f); if (s_pf_show_sparkline && (s_pf_price_basis == 0 || s_pf_price_basis == 1)) { std::vector h = pfSparklineSeries(state.market, s_pf_spark_interval); if (h.size() >= 2) { ImU32 spCol = WithAlpha(h.back() >= h.front() ? Success() : Error(), 80); - ImVec2 spMin(pMin.x + ppad + (accent ? 6.0f : 0.0f), pMin.y + ph * 0.46f); + ImVec2 spMin(pMin.x + ppad, pMin.y + ph * 0.46f); ImVec2 spMax(pMax.x - ppad, pMax.y - ppad * 0.6f); pfDrawSparkline(pdl, spMin, spMax, h, spCol); } } - float rowY = pMin.y + ppad, nameX = pMin.x + ppad + (accent ? 6.0f : 0.0f); + float rowY = pMin.y + ppad, nameX = pMin.x + ppad; if (!s_pf_icon.empty()) { material::project_icons::drawByName(pdl, s_pf_icon, ImVec2(nameX + sub1f->LegacySize * 0.5f, rowY + sub1f->LegacySize * 0.5f), @@ -804,10 +801,11 @@ void RenderMarketTab(App* app) const auto& currentExchange = registry[s_exchange_idx]; if (s_pair_idx >= (int)currentExchange.pairs.size()) s_pair_idx = 0; - // Non-scrolling container — content resizes to fit available height ImVec2 marketAvail = ImGui::GetContentRegionAvail(); - // Scrollable: the portfolio grid can extend past the window and scroll into view. - ImGui::BeginChild("##MarketScroll", marketAvail, false, ImGuiWindowFlags_NoBackground); + // Scrollable (the portfolio grid can extend past the window) but with no visible scrollbar — + // scrolling still works via the mouse wheel. + ImGui::BeginChild("##MarketScroll", marketAvail, false, + ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar); // Responsive: scale factors per frame float availWidth = ImGui::GetContentRegionAvail().x; @@ -842,7 +840,7 @@ void RenderMarketTab(App* app) + mktH3->LegacySize + Layout::spacingMd() // price row -> separator + Layout::spacingSm() // separator -> stats + ovFont->LegacySize + Layout::spacingXs() + sub1->LegacySize // stats label + value - + Layout::spacingLg(); // bottom pad before the chart + + Layout::spacingSm(); // bottom pad before the chart (tight) // -- Portfolio geometry. No parent card background: the summary floats under the header // and the custom-group cards float below it (each with its own glass background). -- @@ -1096,8 +1094,11 @@ void RenderMarketTab(App* app) double labelVal = yMax - (yMax - yMin) * (double)g / 4.0; snprintf(buf, sizeof(buf), "$%.6f", labelVal); ImVec2 labelSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf); + // Keep the axis label inside the card even on narrow windows (min-padding may be + // smaller than the label width) so it never spills onto the tab background. + float lblX = std::max(chartMin.x + 3.0f, plotLeft - labelSz.x - 6); dl->AddText(capFont, capFont->LegacySize, - ImVec2(plotLeft - labelSz.x - 6, gy - labelSz.y * 0.5f), + ImVec2(lblX, gy - labelSz.y * 0.5f), OnSurfaceDisabled(), buf); } @@ -1468,12 +1469,11 @@ void RenderMarketTab(App* app) GlassPanelSpec gcGlass; gcGlass.rounding = 10.0f; gcGlass.fillAlpha = hov ? 34 : 22; gcGlass.borderAlpha = 40; DrawGlassPanel(dl, gcMin, gcMax, gcGlass); - if (hov) dl->AddRect(gcMin, gcMax, WithAlpha(OnSurface(), 70), 10.0f, 0, 1.0f); - if (accent) - dl->AddRectFilled(ImVec2(gcMin.x + 2.0f, gcMin.y + pad * 0.6f), - ImVec2(gcMin.x + 5.0f, gcMax.y - pad * 0.6f), accent, 2.0f); + // Accented groups get a colored outline around the whole card; others a subtle hover ring. + if (accent) dl->AddRect(gcMin, gcMax, accent, 10.0f, 0, hov ? 2.5f : 2.0f); + else if (hov) dl->AddRect(gcMin, gcMax, WithAlpha(OnSurface(), 70), 10.0f, 0, 1.0f); - float left = gcMin.x + pad + (accent ? 6.0f : 0.0f); + float left = gcMin.x + pad; float right = gcMax.x - pad; float availW = right - left; auto fit = [&](std::string s, ImFont* f, float maxW) { @@ -1513,6 +1513,11 @@ void RenderMarketTab(App* app) dl->AddText(body2, body2->LegacySize, ImVec2(nameX, hy), OnSurfaceMedium(), fit(e.label, body2, nameMaxW).c_str()); + // Short cards (near the 3-cell minimum) drop the redundant DRGX footer and use the + // compact hero font so the value keeps room; taller cards show the full layout. + bool cardTall = (gcMax.y - gcMin.y) > 112.0f * mktDp; + bool showFooter = !footStr.empty() && cardTall; + // Sparkline strip along the bottom. float sparkH = 0.0f; if (e.showSparkline && (e.priceBasis == 0 || e.priceBasis == 1)) { @@ -1527,7 +1532,7 @@ void RenderMarketTab(App* app) // Footer: DRGX amount just above the sparkline. float footBottom = gcMax.y - pad - (sparkH > 0 ? sparkH + Layout::spacingXs() : 0.0f); float footTop = footBottom; - if (!footStr.empty()) { + if (showFooter) { footTop = footBottom - capFont->LegacySize; dl->AddText(capFont, capFont->LegacySize, ImVec2(left, footTop), OnSurfaceDisabled(), fit(footStr, capFont, availW).c_str()); @@ -1535,9 +1540,9 @@ void RenderMarketTab(App* app) // Hero value: large, centered in the band between the header and footer. if (!heroStr.empty()) { - ImFont* heroFont = ((gcMax.y - gcMin.y) > 120.0f * mktDp) ? h4 : sub1; + ImFont* heroFont = cardTall ? h4 : sub1; float bandTop = hy + body2->LegacySize + Layout::spacingSm(); - float bandBot = (footStr.empty() ? footBottom : footTop) - Layout::spacingXs(); + float bandBot = (showFooter ? footTop : footBottom) - Layout::spacingXs(); float heroY = std::max(bandTop, (bandTop + bandBot) * 0.5f - heroFont->LegacySize * 0.5f); dl->AddText(heroFont, heroFont->LegacySize, ImVec2(left, heroY), OnSurface(), fit(heroStr, heroFont, availW).c_str());