feat(market): 2-cell min group height; declutter + polish the price chart
- Portfolio group minimum height is now 2 cells (was 3). Added a 2-cell card tier that also drops the sparkline (not just the footer) so a ~58px card keeps the header + hero value legible. - Price chart: stop drawing a dot on every point — for a dense historical series (>40 points) just the clean line + fill, and always mark the latest point (current price) with a dot + ring. - Y-axis labels use the shared adaptive price formatter (e.g. $0.0143) instead of a fixed 6 decimals, so they're cleaner and narrower. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,7 +97,7 @@ public:
|
||||
int gridCol = -1;
|
||||
int gridRow = -1;
|
||||
int gridW = 6;
|
||||
int gridH = 3; // minimum group height (cells); card design adapts below ~4
|
||||
int gridH = 3; // default group height (cells); minimum is 2, card design adapts
|
||||
};
|
||||
|
||||
// Theme
|
||||
|
||||
@@ -891,7 +891,7 @@ void RenderMarketTab(App* app)
|
||||
// this grid and span many cells; they're drawn with an inner inset so adjacent cards don't touch.
|
||||
float pfCardGap = 0.0f; // gapless cell grid
|
||||
float pfInset = 3.0f * mktDp; // per-card inset for visual separation
|
||||
const int pfMinW = 4, pfMinH = 3; // minimum card size in cells (~128x96)
|
||||
const int pfMinW = 4, pfMinH = 2; // minimum card size in cells (~128x64)
|
||||
int pfCols = std::max(pfMinW, (int)(availWidth / (32.0f * mktDp)));
|
||||
float pfCellW = availWidth / (float)pfCols; // square cell edge (>= ~32px)
|
||||
float pfCellH = pfCellW;
|
||||
@@ -1168,7 +1168,8 @@ void RenderMarketTab(App* app)
|
||||
dl->AddLine(ImVec2(plotLeft, gy), ImVec2(plotRight, gy),
|
||||
IM_COL32(255, 255, 255, 12), 1.0f);
|
||||
double labelVal = yMax - (yMax - yMin) * (double)g / 4.0;
|
||||
snprintf(buf, sizeof(buf), "$%.6f", labelVal);
|
||||
// Adaptive precision (via the shared price formatter) — cleaner than a fixed 6 decimals.
|
||||
snprintf(buf, sizeof(buf), "%s", FormatPrice(labelVal).c_str());
|
||||
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.
|
||||
@@ -1208,11 +1209,14 @@ void RenderMarketTab(App* app)
|
||||
// Line
|
||||
dl->AddPolyline(points.data(), (int)points.size(), lineCol, ImDrawFlags_None, S.drawElement("tabs.market", "chart-line-thickness").size);
|
||||
|
||||
// Dots
|
||||
// Dots: only for a sparse series — a dense historical curve reads better as a clean line.
|
||||
// Always mark the latest point (current price) with a dot + ring.
|
||||
float dotR = std::max(S.drawElement("tabs.market", "chart-dot-min-radius").size, S.drawElement("tabs.market", "chart-dot-radius").size * hs);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dl->AddCircleFilled(points[i], dotR, dotCol);
|
||||
if (n <= 40) {
|
||||
for (size_t i = 0; i < n; i++) dl->AddCircleFilled(points[i], dotR, dotCol);
|
||||
}
|
||||
dl->AddCircleFilled(points[n - 1], dotR + 1.0f * mktDp, dotCol);
|
||||
dl->AddCircle(points[n - 1], dotR + 3.5f * mktDp, WithAlpha(dotCol, 130), 0, 1.4f * mktDp);
|
||||
|
||||
// X-axis time labels. Each point spans one interval unit (minute/hour/day/week/month);
|
||||
// convert points-before-now to a "~<time> ago" label (rightmost = "now"). Approximate — "~".
|
||||
@@ -1550,14 +1554,18 @@ 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;
|
||||
// Card layout adapts to height (cells are ~32px): tall cards get the full stat tile;
|
||||
// shorter cards drop the redundant DRGX footer; the shortest (2-cell) also drop the
|
||||
// sparkline so the header + hero value stay legible.
|
||||
float cardHeight = gcMax.y - gcMin.y;
|
||||
bool cardTall = cardHeight > 112.0f * mktDp; // footer + large hero
|
||||
bool showFooter = !footStr.empty() && cardTall;
|
||||
bool showSpark = e.showSparkline && (e.priceBasis == 0 || e.priceBasis == 1)
|
||||
&& cardHeight > 76.0f * mktDp;
|
||||
|
||||
// Sparkline strip along the bottom.
|
||||
float sparkH = 0.0f;
|
||||
if (e.showSparkline && (e.priceBasis == 0 || e.priceBasis == 1)) {
|
||||
if (showSpark) {
|
||||
std::vector<double> hh = pfSparklineSeries(market, e.sparklineInterval);
|
||||
if (hh.size() >= 2) {
|
||||
sparkH = std::max(18.0f * mktDp, (gcMax.y - gcMin.y) * 0.24f);
|
||||
@@ -1657,8 +1665,8 @@ void RenderMarketTab(App* app)
|
||||
ImVec2 sz = cellSize(nw, nh), mx(mn.x + sz.x, mn.y + sz.y);
|
||||
drawCard(mn, mx, pfEntriesGeo[i], true);
|
||||
dl->AddRect(mn, mx, WithAlpha(Primary(), 200), 10.0f, 0, 2.0f * mktDp);
|
||||
// Live size readout (e.g. "6\xC3\x973") so the reached grid span is visible — the
|
||||
// minimum is pfMinW x pfMinH (4x3).
|
||||
// Live size readout (e.g. "6\xC3\x972") so the reached grid span is visible — the
|
||||
// minimum is pfMinW x pfMinH (4x2).
|
||||
{
|
||||
char szb[16]; snprintf(szb, sizeof(szb), "%d\xC3\x97%d", nw, nh);
|
||||
ImVec2 tsz = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, szb);
|
||||
|
||||
Reference in New Issue
Block a user