From 0ae52ee09526a4a4a2f89b8639ba676e10b67502 Mon Sep 17 00:00:00 2001 From: DanS Date: Sat, 4 Jul 2026 00:38:08 -0500 Subject: [PATCH] refactor(market): extract pure portfolio grid-layout into pfComputeGridLayout Pull the ~30-line row-major cell placement (honor stored placement else auto-place) out of RenderMarketTab into a pure, deterministic helper taking (entries, cols, minW, minH). Also bump the grid resize-readout buffer 16->32 to silence a worst-case -Wformat-truncation the extraction surfaced. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/market_tab.cpp | 72 +++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index 4c1f710..c2b0e5a 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -1143,6 +1143,44 @@ static void RenderPortfolioEditor(App* app) bool PortfolioEditorActive() { return s_pfEdit.open; } +// Resolve each portfolio group's grid cell: honor a stored placement when it fits, else auto-place +// row-major into the first free cells. Pure + deterministic (no ImGui). One PfCell per entry. +static std::vector pfComputeGridLayout( + const std::vector& entries, int cols, int minW, int minH) +{ + int n = (int)entries.size(); + std::vector layout((size_t)std::max(0, n)); + std::vector> occ; + auto ensure = [&](int rEnd) { while ((int)occ.size() < rEnd) occ.emplace_back(cols, 0); }; + auto fits = [&](int c, int r, int w, int h) -> bool { + if (c < 0 || r < 0 || c + w > cols) return false; + ensure(r + h); + for (int rr = r; rr < r + h; rr++) for (int cc = c; cc < c + w; cc++) if (occ[rr][cc]) return false; + return true; + }; + auto mark = [&](int c, int r, int w, int h) { ensure(r + h); + for (int rr = r; rr < r + h; rr++) for (int cc = c; cc < c + w; cc++) occ[rr][cc] = 1; }; + std::vector placed((size_t)std::max(0, n), 0); + for (int i = 0; i < n; i++) { + const auto& e = entries[i]; + int w = std::max(minW, std::min(e.gridW, cols)), h = std::max(minH, e.gridH); + if (e.gridCol >= 0 && e.gridRow >= 0 && fits(e.gridCol, e.gridRow, w, h)) { + layout[i] = {e.gridCol, e.gridRow, w, h}; mark(e.gridCol, e.gridRow, w, h); placed[i] = 1; + } + } + for (int i = 0; i < n; i++) { + if (placed[i]) continue; + const auto& e = entries[i]; + int w = std::max(minW, std::min(e.gridW, cols)), h = std::max(minH, e.gridH); + int fc = 0, fr = 0; bool found = false; + for (int r = 0; !found && r < 4096; r++) + for (int c = 0; c + w <= cols; c++) + if (fits(c, r, w, h)) { fc = c; fr = r; found = true; break; } + layout[i] = {fc, fr, w, h}; mark(fc, fr, w, h); + } + return layout; +} + void RenderMarketTab(App* app) { auto& S = schema::UI(); @@ -1237,37 +1275,7 @@ void RenderMarketTab(App* app) float pfCellH = pfCellW; // Resolve each group's grid cell: honor a stored placement when it fits, else auto-place // row-major into the first free cells. Deterministic each frame. - std::vector pfLayout((size_t)std::max(0, pfN)); - { - std::vector> occ; - auto ensure = [&](int rEnd) { while ((int)occ.size() < rEnd) occ.emplace_back(pfCols, 0); }; - auto fits = [&](int c, int r, int w, int h) -> bool { - if (c < 0 || r < 0 || c + w > pfCols) return false; - ensure(r + h); - for (int rr = r; rr < r + h; rr++) for (int cc = c; cc < c + w; cc++) if (occ[rr][cc]) return false; - return true; - }; - auto mark = [&](int c, int r, int w, int h) { ensure(r + h); - for (int rr = r; rr < r + h; rr++) for (int cc = c; cc < c + w; cc++) occ[rr][cc] = 1; }; - std::vector placed((size_t)pfN, 0); - for (int i = 0; i < pfN; i++) { - const auto& e = pfEntriesGeo[i]; - int w = std::max(pfMinW, std::min(e.gridW, pfCols)), h = std::max(pfMinH, e.gridH); - if (e.gridCol >= 0 && e.gridRow >= 0 && fits(e.gridCol, e.gridRow, w, h)) { - pfLayout[i] = {e.gridCol, e.gridRow, w, h}; mark(e.gridCol, e.gridRow, w, h); placed[i] = 1; - } - } - for (int i = 0; i < pfN; i++) { - if (placed[i]) continue; - const auto& e = pfEntriesGeo[i]; - int w = std::max(pfMinW, std::min(e.gridW, pfCols)), h = std::max(pfMinH, e.gridH); - int fc = 0, fr = 0; bool found = false; - for (int r = 0; !found && r < 4096; r++) - for (int c = 0; c + w <= pfCols; c++) - if (fits(c, r, w, h)) { fc = c; fr = r; found = true; break; } - pfLayout[i] = {fc, fr, w, h}; mark(fc, fr, w, h); - } - } + std::vector pfLayout = pfComputeGridLayout(pfEntriesGeo, pfCols, pfMinW, pfMinH); int pfMaxRow = 0; for (const auto& L : pfLayout) pfMaxRow = std::max(pfMaxRow, L.row + L.h); // Content height = the grid plus a couple of empty rows so cards can be dragged below the // current bottom (the tab scrolls when the total exceeds the window). @@ -2035,7 +2043,7 @@ void RenderMarketTab(App* app) // Live size readout (e.g. "8\xC3\x972") so the reached grid span is visible — the // minimum is pfMinW x pfMinH (8x2). { - char szb[16]; snprintf(szb, sizeof(szb), "%d\xC3\x97%d", nw, nh); + char szb[32]; snprintf(szb, sizeof(szb), "%d\xC3\x97%d", nw, nh); ImVec2 tsz = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, szb); ImVec2 tp((mn.x + mx.x) * 0.5f - tsz.x * 0.5f, (mn.y + mx.y) * 0.5f - tsz.y * 0.5f); dl->AddRectFilled(ImVec2(tp.x - 6, tp.y - 3), ImVec2(tp.x + tsz.x + 6, tp.y + tsz.y + 3),