From 50873d111235992b5fa3eace57dc027326d336e0 Mon Sep 17 00:00:00 2001 From: DanS Date: Sat, 4 Jul 2026 00:43:43 -0500 Subject: [PATCH] refactor(market): extract pair selector into mktDrawPairSelector Move the exchange/pair chip grid (wrapping flat chips + click-to-switch + attribution line) out of RenderMarketTab into mktDrawPairSelector(app, registry, availWidth), recomputing its own schema/fonts/market. Drop the now-unused ovFont local. Verbatim body. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/market_tab.cpp | 180 ++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 83 deletions(-) diff --git a/src/ui/windows/market_tab.cpp b/src/ui/windows/market_tab.cpp index 22b65c3..00e77ce 100644 --- a/src/ui/windows/market_tab.cpp +++ b/src/ui/windows/market_tab.cpp @@ -1297,6 +1297,102 @@ static void mktDrawCard(const MktCardCtx& cx, ImVec2 gcMin, ImVec2 gcMax, } } +// Pair selector: every trading pair across all exchanges as flat wrapping chips; clicking one +// switches the active exchange/pair (persisted) and refreshes market data. Followed by attribution. +static void mktDrawPairSelector(App* app, const std::vector& registry, float availWidth) +{ + auto& S = schema::UI(); + const auto& market = app->getWalletState().market; + ImDrawList* dl = ImGui::GetWindowDrawList(); + ImFont* capFont = Type().caption(); + ImFont* ovFont = Type().overline(); + float gap = Layout::cardGap(); + char buf[128]; + + ImGui::Dummy(ImVec2(0, S.drawElement("tabs.market", "exchange-top-gap").size)); + { + float chipH = S.drawElement("tabs.market", "pair-chip-height").height; + float chipR = S.drawElement("tabs.market", "pair-chip-radius").radius; + float chipSpacing = S.drawElement("tabs.market", "pair-chip-spacing").size; + float innerGap = Layout::spacingSm(); + float sidePad = Layout::spacingMd(); + + ImVec2 origin = ImGui::GetCursorScreenPos(); + float left = origin.x; + float right = origin.x + availWidth; + float cx = left; + float cy = origin.y; + int btnCounter = 0; + + for (int ex = 0; ex < (int)registry.size(); ex++) { + for (int pr = 0; pr < (int)registry[ex].pairs.size(); pr++) { + const std::string& pairName = registry[ex].pairs[pr].displayName; + const std::string& exName = registry[ex].name; + + float pairW = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, pairName.c_str()).x; + float exW = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, exName.c_str()).x; + float cw = sidePad * 2.0f + pairW + innerGap + exW; + + // Wrap to the next row when the pill would overflow (never on an empty row). + if (cx + cw > right && cx > left) { + cx = left; + cy += chipH + chipSpacing; + } + + ImVec2 cMin(cx, cy); + ImVec2 cMax(cx + cw, cy + chipH); + bool selected = (ex == s_mkt.exchangeIdx && pr == s_mkt.pairIdx); + bool hov = material::IsRectHovered(cMin, cMax); + ImU32 chipBg = selected ? WithAlpha(Primary(), 200) + : (hov ? WithAlpha(OnSurface(), 35) : WithAlpha(OnSurface(), 20)); + ImU32 chipBorder = selected ? Primary() : WithAlpha(OnSurface(), 40); + ImU32 pairCol = selected ? IM_COL32(255, 255, 255, 255) : OnSurface(); + ImU32 exCol = selected ? WithAlpha(IM_COL32(255, 255, 255, 255), 190) : OnSurfaceDisabled(); + + dl->AddRectFilled(cMin, cMax, chipBg, chipR); + dl->AddRect(cMin, cMax, chipBorder, chipR, 0, 1.0f); + + float tx = cx + sidePad; + dl->AddText(capFont, capFont->LegacySize, + ImVec2(tx, cy + (chipH - capFont->LegacySize) * 0.5f), pairCol, pairName.c_str()); + dl->AddText(ovFont, ovFont->LegacySize, + ImVec2(tx + pairW + innerGap, cy + (chipH - ovFont->LegacySize) * 0.5f), + exCol, exName.c_str()); + + ImGui::SetCursorScreenPos(cMin); + snprintf(buf, sizeof(buf), "##PairBtn%d", btnCounter++); + if (ImGui::InvisibleButton(buf, ImVec2(cw, chipH))) { + if (ex != s_mkt.exchangeIdx || pr != s_mkt.pairIdx) { + s_mkt.exchangeIdx = ex; + s_mkt.pairIdx = pr; + app->settings()->setSelectedExchange(exName); + app->settings()->setSelectedPair(pairName); + app->settings()->save(); + app->refreshMarketData(); + } + } + if (ImGui::IsItemHovered()) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + + cx += cw + chipSpacing; + } + } + + // Advance the cursor past the wrapped rows. + ImGui::SetCursorScreenPos(ImVec2(left, cy + chipH)); + ImGui::Dummy(ImVec2(availWidth, 0)); + + // Attribution (moved here from the removed exchange-selector row). + ImGui::Dummy(ImVec2(0, Layout::spacingSm())); + Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("market_attribution")); + if (!market.last_updated.empty()) { + ImGui::SameLine(0, 12); + snprintf(buf, sizeof(buf), " \xc2\xb7 Updated %s", market.last_updated.c_str()); + Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), buf); + } + ImGui::Dummy(ImVec2(0, gap)); + } +} + void RenderMarketTab(App* app) { auto& S = schema::UI(); @@ -1346,7 +1442,6 @@ void RenderMarketTab(App* app) ImDrawList* dl = ImGui::GetWindowDrawList(); GlassPanelSpec glassSpec; glassSpec.rounding = Layout::glassRounding(); - ImFont* ovFont = Type().overline(); ImFont* capFont = Type().caption(); ImFont* sub1 = Type().subtitle1(); ImFont* body2 = Type().body2(); @@ -1794,88 +1889,7 @@ void RenderMarketTab(App* app) // PAIR SELECTOR — every trading pair across all exchanges shown as // round buttons (flat; no exchange dropdown). Wraps to multiple rows. // ================================================================ - ImGui::Dummy(ImVec2(0, S.drawElement("tabs.market", "exchange-top-gap").size)); - { - float chipH = S.drawElement("tabs.market", "pair-chip-height").height; - float chipR = S.drawElement("tabs.market", "pair-chip-radius").radius; - float chipSpacing = S.drawElement("tabs.market", "pair-chip-spacing").size; - float innerGap = Layout::spacingSm(); - float sidePad = Layout::spacingMd(); - - ImVec2 origin = ImGui::GetCursorScreenPos(); - float left = origin.x; - float right = origin.x + availWidth; - float cx = left; - float cy = origin.y; - int btnCounter = 0; - - for (int ex = 0; ex < (int)registry.size(); ex++) { - for (int pr = 0; pr < (int)registry[ex].pairs.size(); pr++) { - const std::string& pairName = registry[ex].pairs[pr].displayName; - const std::string& exName = registry[ex].name; - - float pairW = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, pairName.c_str()).x; - float exW = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, exName.c_str()).x; - float cw = sidePad * 2.0f + pairW + innerGap + exW; - - // Wrap to the next row when the pill would overflow (never on an empty row). - if (cx + cw > right && cx > left) { - cx = left; - cy += chipH + chipSpacing; - } - - ImVec2 cMin(cx, cy); - ImVec2 cMax(cx + cw, cy + chipH); - bool selected = (ex == s_mkt.exchangeIdx && pr == s_mkt.pairIdx); - bool hov = material::IsRectHovered(cMin, cMax); - ImU32 chipBg = selected ? WithAlpha(Primary(), 200) - : (hov ? WithAlpha(OnSurface(), 35) : WithAlpha(OnSurface(), 20)); - ImU32 chipBorder = selected ? Primary() : WithAlpha(OnSurface(), 40); - ImU32 pairCol = selected ? IM_COL32(255, 255, 255, 255) : OnSurface(); - ImU32 exCol = selected ? WithAlpha(IM_COL32(255, 255, 255, 255), 190) : OnSurfaceDisabled(); - - dl->AddRectFilled(cMin, cMax, chipBg, chipR); - dl->AddRect(cMin, cMax, chipBorder, chipR, 0, 1.0f); - - float tx = cx + sidePad; - dl->AddText(capFont, capFont->LegacySize, - ImVec2(tx, cy + (chipH - capFont->LegacySize) * 0.5f), pairCol, pairName.c_str()); - dl->AddText(ovFont, ovFont->LegacySize, - ImVec2(tx + pairW + innerGap, cy + (chipH - ovFont->LegacySize) * 0.5f), - exCol, exName.c_str()); - - ImGui::SetCursorScreenPos(cMin); - snprintf(buf, sizeof(buf), "##PairBtn%d", btnCounter++); - if (ImGui::InvisibleButton(buf, ImVec2(cw, chipH))) { - if (ex != s_mkt.exchangeIdx || pr != s_mkt.pairIdx) { - s_mkt.exchangeIdx = ex; - s_mkt.pairIdx = pr; - app->settings()->setSelectedExchange(exName); - app->settings()->setSelectedPair(pairName); - app->settings()->save(); - app->refreshMarketData(); - } - } - if (ImGui::IsItemHovered()) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - - cx += cw + chipSpacing; - } - } - - // Advance the cursor past the wrapped rows. - ImGui::SetCursorScreenPos(ImVec2(left, cy + chipH)); - ImGui::Dummy(ImVec2(availWidth, 0)); - - // Attribution (moved here from the removed exchange-selector row). - ImGui::Dummy(ImVec2(0, Layout::spacingSm())); - Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("market_attribution")); - if (!market.last_updated.empty()) { - ImGui::SameLine(0, 12); - snprintf(buf, sizeof(buf), " \xc2\xb7 Updated %s", market.last_updated.c_str()); - Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), buf); - } - ImGui::Dummy(ImVec2(0, gap)); - } + mktDrawPairSelector(app, registry, availWidth); // ================================================================ // PORTFOLIO — floating summary (no card background) + custom-group cards