From a767702ff67840b00ccb0471afc3defc23b3d4d1 Mon Sep 17 00:00:00 2001 From: DanS Date: Sun, 12 Jul 2026 11:46:26 -0500 Subject: [PATCH] feat(market): OHLC hover readout on the candlestick chart Candles had no hover (the line's close tooltip is gated off for them). Add a candle-aware readout: the candle under the cursor gets a column highlight + crosshair, and a small box shows its date and open/high/low/close (colored green up / red down). Verified via a forced-hover render (date + O/H/L/C over the highlighted candle). Line charts keep their existing single-price tooltip. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/market_tab.cpp | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index 1349bcd..22c0313 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -1526,6 +1526,43 @@ static void mktDrawPriceChart(const MktCtx& cx) dl->AddRectFilled(ImVec2(xc - bodyW * 0.5f, bT), ImVec2(xc + bodyW * 0.5f, bB), WithAlpha(col, 230), 1.0f); } + // Hover: highlight the candle under the cursor + an OHLC readout (date + open/high/low/close). + ImVec2 mp = ImGui::GetIO().MousePos; + if (mp.x >= plotLeft && mp.x <= plotRight && mp.y >= plotTop && mp.y <= plotBottom) { + int hi = (int)((mp.x - plotLeft) / slotW); + if (hi < 0) hi = 0; + if (hi >= cn) hi = cn - 1; + const auto& hc = candles[hi]; + const float hx = plotLeft + ((float)hi + 0.5f) * slotW; + dl->AddRectFilled(ImVec2(hx - slotW * 0.5f, plotTop), ImVec2(hx + slotW * 0.5f, plotBottom), + IM_COL32(255, 255, 255, 12)); + dl->AddLine(ImVec2(hx, plotTop), ImVec2(hx, plotBottom), IM_COL32(255, 255, 255, 45), 1.0f); + char when[40] = ""; + std::time_t tt = hc.time; + if (std::tm* tmv = std::localtime(&tt)) + std::strftime(when, sizeof(when), s_mkt.chartInterval <= 2 ? "%b %d %H:%M" : "%b %d, %Y", tmv); + char l2[80], l3[80]; + snprintf(l2, sizeof(l2), "O %s H %s", FormatPrice(hc.open).c_str(), FormatPrice(hc.high).c_str()); + snprintf(l3, sizeof(l3), "L %s C %s", FormatPrice(hc.low).c_str(), FormatPrice(hc.close).c_str()); + const float lh = capFont->LegacySize; + const float pad = Layout::spacingSm(); + const float gap = 3.0f * mktDp; + float tw = std::max(capFont->CalcTextSizeA(lh, FLT_MAX, 0, when).x, + std::max(capFont->CalcTextSizeA(lh, FLT_MAX, 0, l2).x, + capFont->CalcTextSizeA(lh, FLT_MAX, 0, l3).x)); + float th = lh * 3 + gap * 2 + pad * 2; + float tipX = hx + 10.0f * mktDp; + if (tipX + tw + pad * 2 > plotRight) tipX = hx - tw - pad * 2 - 10.0f * mktDp; + tipX = std::max(plotLeft, tipX); + float tipY = plotTop + 4.0f * mktDp; + ImVec2 tMin(tipX, tipY), tMax(tipX + tw + pad * 2, tipY + th); + dl->AddRectFilled(tMin, tMax, IM_COL32(20, 20, 30, 235), 4.0f); + dl->AddRect(tMin, tMax, IM_COL32(255, 255, 255, 30), 4.0f, 0, 1.0f); + ImU32 cCol = (hc.close >= hc.open) ? Success() : Error(); + dl->AddText(capFont, lh, ImVec2(tipX + pad, tipY + pad), OnSurface(), when); + dl->AddText(capFont, lh, ImVec2(tipX + pad, tipY + pad + lh + gap), cCol, l2); + dl->AddText(capFont, lh, ImVec2(tipX + pad, tipY + pad + 2 * lh + 2 * gap), cCol, l3); + } } else { for (size_t i = 0; i < n; i++) { float t = (n > 1) ? (float)i / (float)(n - 1) : 0.0f;