diff --git a/src/ui/windows/balance_components.cpp b/src/ui/windows/balance_components.cpp index e71d152..e998b57 100644 --- a/src/ui/windows/balance_components.cpp +++ b/src/ui/windows/balance_components.cpp @@ -260,45 +260,64 @@ void RenderSharedAddressList(App* app, float listH, float availW, } } - // ---- Inline period-earnings controls: interval pills (1H/1D/1M/1Y), mode toggle (received / - // net), and a global show-all toggle. The create-address buttons moved below the list (footer). ---- + // ---- Inline period-earnings controls: interval (1H/1D/1M/1Y) + mode (received / net) + a global + // show-all toggle, all drawn as Material segmented controls to match the Receive-tab filter. The + // create-address buttons moved below the list (footer) to make room. ---- if (app->settings()) { auto* est = app->settings(); + ImFont* capF = Type().caption(); ImFont* icoF = Type().iconSmall(); - const float segH = ImGui::GetFrameHeight(); // fixed height so pills + icon buttons align - // A compact selected/unselected toggle. `font` nullptr = default font (text pills). - auto segBtn = [&](const char* idlabel, bool selected, const char* tip, ImFont* font) -> bool { - if (font) ImGui::PushFont(font); - ImGui::PushStyleColor(ImGuiCol_Button, selected ? WithAlpha(Primary(), 70) : IM_COL32(255, 255, 255, 12)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, WithAlpha(Primary(), 110)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, WithAlpha(Primary(), 140)); - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(selected ? OnSurface() : OnSurfaceMedium())); - bool clk = ImGui::Button(idlabel, ImVec2(0, segH)); - ImGui::PopStyleColor(4); - if (font) ImGui::PopFont(); - if (tip && ImGui::IsItemHovered()) material::Tooltip("%s", tip); - return clk; - }; + ImDrawList* wdl = ImGui::GetWindowDrawList(); + const float segH = ImGui::GetFrameHeight(); ImGui::SameLine(0, Layout::spacingLg()); - // Interval pills + const float rowStartX = ImGui::GetCursorScreenPos().x; + const float rowY = ImGui::GetCursorScreenPos().y; + float x = rowStartX; + + // Interval (1H/1D/1M/1Y) — text segments sized to the widest label, like the Receive filter. const char* ivText[4] = { "1H", "1D", "1M", "1Y" }; + float ivCellW = segH; + for (const char* t : ivText) + ivCellW = std::max(ivCellW, capF->CalcTextSizeA(capF->LegacySize, FLT_MAX, 0, t).x + 2.0f * Layout::spacingMd()); + const float ivTotalW = ivCellW * 4; int iv = est->getEarningsInterval(); - for (int k = 0; k < 4; ++k) { - char lbl[16]; snprintf(lbl, sizeof(lbl), "%s##earniv%d", ivText[k], k); - if (segBtn(lbl, iv == k, TR("tt_earn_interval"), nullptr)) { est->setEarningsInterval(k); est->save(); } - if (k < 3) ImGui::SameLine(0, 2.0f * dp); - } - // Mode toggle (received-only vs net) - ImGui::SameLine(0, Layout::spacingMd()); + int ivClk = material::SegmentedControl(wdl, ImVec2(x, rowY), ivTotalW, segH, ivText, 4, iv, capF, "##earniv", dp); + if (ivClk >= 0) { est->setEarningsInterval(ivClk); est->save(); } + if (ImGui::IsMouseHoveringRect(ImVec2(x, rowY), ImVec2(x + ivTotalW, rowY + segH))) + material::Tooltip("%s", TR("tt_earn_interval")); + x += ivTotalW + Layout::spacingMd(); + + // Mode (received-only vs net) — two icon segments. + const char* modeIcons[2] = { ICON_MD_CALL_RECEIVED, ICON_MD_SWAP_VERT }; + float modeCellW = segH; + for (const char* t : modeIcons) + modeCellW = std::max(modeCellW, icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, t).x + 2.0f * Layout::spacingSm()); + const float modeTotalW = modeCellW * 2; int emode = est->getEarningsMode(); - if (segBtn(ICON_MD_CALL_RECEIVED "##earnmodein", emode == 0, TR("tt_earn_mode_inflows"), icoF)) { est->setEarningsMode(0); est->save(); } - ImGui::SameLine(0, 2.0f * dp); - if (segBtn(ICON_MD_SWAP_VERT "##earnmodenet", emode == 1, TR("tt_earn_mode_net"), icoF)) { est->setEarningsMode(1); est->save(); } - // Global earnings-column toggle - ImGui::SameLine(0, Layout::spacingMd()); + int modeClk = material::SegmentedControl(wdl, ImVec2(x, rowY), modeTotalW, segH, modeIcons, 2, emode, icoF, "##earnmode", dp); + if (modeClk >= 0) { est->setEarningsMode(modeClk); est->save(); } + for (int i = 0; i < 2; ++i) { + ImVec2 cMin(x + i * modeCellW, rowY), cMax(cMin.x + modeCellW, rowY + segH); + if (ImGui::IsMouseHoveringRect(cMin, cMax)) + material::Tooltip("%s", i == 0 ? TR("tt_earn_mode_inflows") : TR("tt_earn_mode_net")); + } + x += modeTotalW + Layout::spacingMd(); + + // Global earnings-column toggle — a single matching segment (highlighted when on). + const char* togIcon[1] = { ICON_MD_TRENDING_UP }; + float togW = std::max(segH, icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, togIcon[0]).x + 2.0f * Layout::spacingSm()); bool showAll = est->getEarningsShowAll(); - if (segBtn(ICON_MD_TRENDING_UP "##earnshowall", showAll, TR("tt_earn_show_all"), icoF)) { est->setEarningsShowAll(!showAll); est->save(); } + int togClk = material::SegmentedControl(wdl, ImVec2(x, rowY), togW, segH, togIcon, 1, showAll ? 0 : -1, icoF, "##earnshowall", dp); + if (togClk == 0) { est->setEarningsShowAll(!showAll); est->save(); } + if (ImGui::IsMouseHoveringRect(ImVec2(x, rowY), ImVec2(x + togW, rowY + segH))) + material::Tooltip("%s", TR("tt_earn_show_all")); + x += togW; + + // Reserve the controls' row as one layout item so the cursor drops below it (the segmented + // controls draw absolutely + hit-test with their own InvisibleButtons). + ImGui::SetCursorScreenPos(ImVec2(rowStartX, rowY)); + ImGui::Dummy(ImVec2(x - rowStartX, segH)); } ImGui::Dummy(ImVec2(0, Layout::spacingXs()));