From d689b02de921ed5aeda07fc765cd34133092047e Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 3 Jul 2026 23:54:10 -0500 Subject: [PATCH] refactor(market): extract pfProjectSeries + pfRightAlignX helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dedup the identical min/max→rect projection shared by pfDrawSparkline and pfDrawSparklineFilled into pfProjectSeries, and the repeated right-align cursor math (footer Close, detail Revert/Save, summary Manage button) into pfRightAlignX. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/market_tab.cpp | 54 +++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index 328af6d..c4423be 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -260,38 +260,47 @@ static std::vector> pfChartSeries(const MarketInf // A group's value trend tracks the DRGX price, so a group sparkline plots the price history // (normalized) across a rect. Colored by overall direction. Only meaningful for market bases. -static void pfDrawSparkline(ImDrawList* dl, ImVec2 mn, ImVec2 mx, - const std::vector& hist, ImU32 col) +// Normalize a numeric series into evenly-spaced screen points within [mn, mx], y scaled to the +// series min..max. Returns false (leaving `out` untouched) when there are fewer than 2 points. +static bool pfProjectSeries(const std::vector& hist, ImVec2 mn, ImVec2 mx, + std::vector& out) { int n = (int)hist.size(); - if (n < 2) return; + if (n < 2) return false; double lo = *std::min_element(hist.begin(), hist.end()); double hi = *std::max_element(hist.begin(), hist.end()); if (hi <= lo) hi = lo + 1e-9; float w = mx.x - mn.x, h = mx.y - mn.y; - std::vector pts(n); + out.resize(n); for (int i = 0; i < n; i++) { - float t = (n > 1) ? (float)i / (float)(n - 1) : 0.0f; - pts[i] = ImVec2(mn.x + t * w, mx.y - (float)((hist[i] - lo) / (hi - lo)) * h); + float t = (float)i / (float)(n - 1); + out[i] = ImVec2(mn.x + t * w, mx.y - (float)((hist[i] - lo) / (hi - lo)) * h); } - dl->AddPolyline(pts.data(), n, col, ImDrawFlags_None, 1.2f); + return true; +} + +// Right-align the next item(s) of total width `w` within the remaining content region (cursor form). +static void pfRightAlignX(float w) +{ + float avail = ImGui::GetContentRegionAvail().x; + if (avail > w) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + avail - w); +} + +static void pfDrawSparkline(ImDrawList* dl, ImVec2 mn, ImVec2 mx, + const std::vector& hist, ImU32 col) +{ + std::vector pts; + if (!pfProjectSeries(hist, mn, mx, pts)) return; + dl->AddPolyline(pts.data(), (int)pts.size(), col, ImDrawFlags_None, 1.2f); } // Sparkline with a soft fill under the line — for the card's dedicated bottom strip. static void pfDrawSparklineFilled(ImDrawList* dl, ImVec2 mn, ImVec2 mx, const std::vector& hist, ImU32 lineCol) { - int n = (int)hist.size(); - if (n < 2) return; - double lo = *std::min_element(hist.begin(), hist.end()); - double hi = *std::max_element(hist.begin(), hist.end()); - if (hi <= lo) hi = lo + 1e-9; - float w = mx.x - mn.x, h = mx.y - mn.y; - std::vector pts(n); - for (int i = 0; i < n; i++) { - float t = (n > 1) ? (float)i / (float)(n - 1) : 0.0f; - pts[i] = ImVec2(mn.x + t * w, mx.y - (float)((hist[i] - lo) / (hi - lo)) * h); - } + std::vector pts; + if (!pfProjectSeries(hist, mn, mx, pts)) return; + int n = (int)pts.size(); for (int i = 0; i < n; i++) dl->PathLineTo(pts[i]); dl->PathLineTo(ImVec2(pts[n - 1].x, mx.y)); dl->PathLineTo(ImVec2(pts[0].x, mx.y)); @@ -1138,8 +1147,7 @@ static void RenderPortfolioEditor(App* app) { float bw = 104.0f * dp, sp = Layout::spacingSm(); float grp = bw * 2 + sp; - float availD = ImGui::GetContentRegionAvail().x; - if (availD > grp) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availD - grp); + pfRightAlignX(grp); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + Layout::spacingSm()); ImGui::BeginDisabled(!selDirty); if (ImGui::Button(TR("portfolio_revert"), ImVec2(bw, addH))) @@ -1156,8 +1164,7 @@ static void RenderPortfolioEditor(App* app) // ================= FOOTER: close (modal-level action, larger → higher hierarchy) ================= { float bh = 40.0f * dp, bw = 120.0f * dp; - float availF = ImGui::GetContentRegionAvail().x; - if (availF > bw) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availF - bw); + pfRightAlignX(bw); if (ImGui::Button(TR("portfolio_close"), ImVec2(bw, bh))) { commitIfNeeded(); s_portfolio_editor_open = false; } } @@ -1804,8 +1811,7 @@ void RenderMarketTab(App* app) const char* ml = TR("portfolio_manage"); float mBtnW = body2->CalcTextSizeA(body2->LegacySize, FLT_MAX, 0, ml).x + Layout::spacingMd() * 2; ImGui::SameLine(); - float availX = ImGui::GetContentRegionAvail().x; - if (availX > mBtnW) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availX - mBtnW); + pfRightAlignX(mBtnW); if (ImGui::Button(ml, ImVec2(mBtnW, 0))) { // Open the combined editor selecting the first group (or the empty state if none). PortfolioBeginEdit(app, app->settings()->getPortfolioEntries().empty() ? -1 : 0);