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) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 15:15:17 -05:00
parent 021ba88522
commit 4e15a2b1ba
3 changed files with 27 additions and 22 deletions

View File

@@ -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<double> 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());