refactor(ui): add material::IconButton helper + adopt at 8 frameless icon buttons
UI-standardization audit (icon-button discovery pass): the app had ~22 clean hand-rolled icon buttons repeating the same "InvisibleButton hit-target + centered glyph + hover feedback + tooltip" boilerplate. Add a shared material::IconButton(id, glyph, font, size, IconButtonStyle) — centered glyph, optional resting + hover backgrounds (pill/rounded-rect), hover recolor, hand cursor + tooltip; caller resolves state-dependent glyph/colors and returns clicked. Adopt at the homogeneous, self-contained sites, pixel-faithful: - mining_mode_toggle: pool/worker dropdown + bookmark + reset (×5) - network: server hide/unhide eye (×1) - mining_controls: pool benchmark start (×1) - mining_stats: pool-balance refresh (×1) Deferred (own follow-up + sweep): the idle/scale/gpu active-pill toggles use a "draw pill+glyph earlier, hit-test later" idiom needing restructuring; the raw-rect (IsRectHovered, no InvisibleButton) sites in market/mining_stats/ balance are a different hit-test mechanism left intentionally. Framed icon pagers already ride TactileButton after the prior commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -354,6 +354,47 @@ inline ImVec2 DrawPill(ImDrawList* dl, const ImVec2& pos, const char* text, ImFo
|
||||
return sz;
|
||||
}
|
||||
|
||||
// ── Frameless icon button ────────────────────────────────────────────────
|
||||
// Style for IconButton. All colors default to 0 (= unset): color falls back to OnSurfaceMedium();
|
||||
// hoverColor==0 means "no hover recolor"; hoverBg/restBg==0 mean "no background".
|
||||
struct IconButtonStyle {
|
||||
ImU32 color = 0; // resting glyph color (0 => OnSurfaceMedium())
|
||||
ImU32 hoverColor = 0; // glyph color while hovered (0 => same as color)
|
||||
ImU32 hoverBg = 0; // fill drawn behind the glyph on hover only (0 => none)
|
||||
ImU32 restBg = 0; // fill drawn every frame, e.g. an "active" pill (0 => none)
|
||||
float bgRounding = -1.0f; // <0 => size.y*0.5 (pill/circle for a square); else that radius
|
||||
bool handCursor = true;
|
||||
const char* tooltip = nullptr;
|
||||
};
|
||||
|
||||
// A frameless / pill icon button: an InvisibleButton(id, size) at the current cursor with a centered
|
||||
// `glyph` (in `font`), optional resting + hover backgrounds, hover recolor, hand cursor + tooltip.
|
||||
// Returns true the frame it is clicked. The caller resolves any state-dependent glyph/colors/tooltip
|
||||
// before the call and positions the cursor (SetCursorScreenPos / SameLine) as needed.
|
||||
inline bool IconButton(const char* id, const char* glyph, ImFont* font, const ImVec2& size,
|
||||
const IconButtonStyle& st)
|
||||
{
|
||||
const ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
ImGui::InvisibleButton(id, size);
|
||||
const bool hov = ImGui::IsItemHovered();
|
||||
const bool clk = ImGui::IsItemClicked();
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
const ImVec2 pMax(pos.x + size.x, pos.y + size.y);
|
||||
const float round = st.bgRounding < 0.0f ? size.y * 0.5f : st.bgRounding;
|
||||
if (st.restBg != 0) dl->AddRectFilled(pos, pMax, st.restBg, round);
|
||||
if (hov && st.hoverBg != 0) dl->AddRectFilled(pos, pMax, st.hoverBg, round);
|
||||
if (hov) {
|
||||
if (st.handCursor) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (st.tooltip && st.tooltip[0]) Tooltip("%s", st.tooltip);
|
||||
}
|
||||
const ImU32 gcol = (hov && st.hoverColor != 0) ? st.hoverColor
|
||||
: (st.color != 0 ? st.color : OnSurfaceMedium());
|
||||
const ImVec2 gsz = font->CalcTextSizeA(font->LegacySize, FLT_MAX, 0, glyph);
|
||||
dl->AddText(font, font->LegacySize,
|
||||
ImVec2(pos.x + (size.x - gsz.x) * 0.5f, pos.y + (size.y - gsz.y) * 0.5f), gcol, glyph);
|
||||
return clk;
|
||||
}
|
||||
|
||||
// ── Tactile wrappers ────────────────────────────────────────────────────
|
||||
// Drop-in replacements for ImGui::Button / SmallButton that automatically
|
||||
// add the tactile highlight overlay after rendering.
|
||||
|
||||
Reference in New Issue
Block a user