style(balance): use the shared SegmentedControl for the earnings toolbar

The Overview earnings controls (interval 1H/1D/1M/1Y, received/net mode, and the
global show-all toggle) were ad-hoc ImGui buttons. They now use
material::SegmentedControl -- the same rounded-track pill the Receive tab's
All/Shielded/Mining filter uses -- so the interval is a text segmented control,
the mode is two icon segments with per-segment tooltips, and the toggle is a
single matching segment (highlighted when on). Verified in a headless GL capture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 16:48:33 -05:00
co-authored by Claude Opus 4.8
parent 46371c69df
commit e28cacbe8e
+49 -30
View File
@@ -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()));