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:
@@ -190,26 +190,13 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
||||
// --- URL: Dropdown arrow button ---
|
||||
ImGui::SameLine(0, 0);
|
||||
{
|
||||
ImVec2 btnPos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 btnSize(iconBtnW, inputFrameH2);
|
||||
ImGui::InvisibleButton("##PoolDropdown", btnSize);
|
||||
bool btnHov = ImGui::IsItemHovered();
|
||||
bool btnClk = ImGui::IsItemClicked();
|
||||
ImDrawList* dl2 = ImGui::GetWindowDrawList();
|
||||
ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
|
||||
if (btnHov) {
|
||||
dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
|
||||
StateHover(), 4.0f * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", TR("mining_saved_pools"));
|
||||
}
|
||||
ImFont* icoFont = Type().iconSmall();
|
||||
const char* dropIcon = ICON_MD_ARROW_DROP_DOWN;
|
||||
ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, dropIcon);
|
||||
dl2->AddText(icoFont, icoFont->LegacySize,
|
||||
ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
|
||||
OnSurfaceMedium(), dropIcon);
|
||||
if (btnClk) {
|
||||
material::IconButtonStyle st;
|
||||
st.color = OnSurfaceMedium();
|
||||
st.hoverBg = StateHover();
|
||||
st.bgRounding = 4.0f * dp;
|
||||
st.tooltip = TR("mining_saved_pools");
|
||||
if (material::IconButton("##PoolDropdown", ICON_MD_ARROW_DROP_DOWN, Type().iconSmall(),
|
||||
ImVec2(iconBtnW, inputFrameH2), st)) {
|
||||
ImGui::OpenPopup("##SavedPoolsPopup");
|
||||
}
|
||||
}
|
||||
@@ -217,28 +204,16 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
||||
// --- URL: Bookmark button ---
|
||||
ImGui::SameLine(0, 0);
|
||||
{
|
||||
ImVec2 btnPos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 btnSize(iconBtnW, inputFrameH2);
|
||||
ImGui::InvisibleButton("##SavePoolUrl", btnSize);
|
||||
bool btnHov = ImGui::IsItemHovered();
|
||||
bool btnClk = ImGui::IsItemClicked();
|
||||
ImDrawList* dl2 = ImGui::GetWindowDrawList();
|
||||
ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
|
||||
std::string currentUrl(s_pool_url);
|
||||
bool alreadySaved = miningValueAlreadySaved(app->settings()->getSavedPoolUrls(), currentUrl);
|
||||
if (btnHov) {
|
||||
dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
|
||||
StateHover(), 4.0f * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", alreadySaved ? TR("mining_already_saved") : TR("mining_save_pool_url"));
|
||||
}
|
||||
ImFont* icoFont = Type().iconSmall();
|
||||
const char* saveIcon = alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER;
|
||||
ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, saveIcon);
|
||||
dl2->AddText(icoFont, icoFont->LegacySize,
|
||||
ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
|
||||
alreadySaved ? Primary() : OnSurfaceMedium(), saveIcon);
|
||||
if (btnClk && !currentUrl.empty() && !alreadySaved) {
|
||||
material::IconButtonStyle st;
|
||||
st.color = alreadySaved ? Primary() : OnSurfaceMedium();
|
||||
st.hoverBg = StateHover();
|
||||
st.bgRounding = 4.0f * dp;
|
||||
st.tooltip = alreadySaved ? TR("mining_already_saved") : TR("mining_save_pool_url");
|
||||
if (material::IconButton("##SavePoolUrl", alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER,
|
||||
Type().iconSmall(), ImVec2(iconBtnW, inputFrameH2), st)
|
||||
&& !currentUrl.empty() && !alreadySaved) {
|
||||
app->settings()->addSavedPoolUrl(currentUrl);
|
||||
app->settings()->save();
|
||||
}
|
||||
@@ -412,26 +387,13 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
||||
// --- Worker: Dropdown arrow button ---
|
||||
ImGui::SameLine(0, 0);
|
||||
{
|
||||
ImVec2 btnPos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 btnSize(iconBtnW, inputFrameH2);
|
||||
ImGui::InvisibleButton("##WorkerDropdown", btnSize);
|
||||
bool btnHov = ImGui::IsItemHovered();
|
||||
bool btnClk = ImGui::IsItemClicked();
|
||||
ImDrawList* dl2 = ImGui::GetWindowDrawList();
|
||||
ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
|
||||
if (btnHov) {
|
||||
dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
|
||||
StateHover(), 4.0f * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", TR("mining_saved_addresses"));
|
||||
}
|
||||
ImFont* icoFont = Type().iconSmall();
|
||||
const char* dropIcon = ICON_MD_ARROW_DROP_DOWN;
|
||||
ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, dropIcon);
|
||||
dl2->AddText(icoFont, icoFont->LegacySize,
|
||||
ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
|
||||
OnSurfaceMedium(), dropIcon);
|
||||
if (btnClk) {
|
||||
material::IconButtonStyle st;
|
||||
st.color = OnSurfaceMedium();
|
||||
st.hoverBg = StateHover();
|
||||
st.bgRounding = 4.0f * dp;
|
||||
st.tooltip = TR("mining_saved_addresses");
|
||||
if (material::IconButton("##WorkerDropdown", ICON_MD_ARROW_DROP_DOWN, Type().iconSmall(),
|
||||
ImVec2(iconBtnW, inputFrameH2), st)) {
|
||||
ImGui::OpenPopup("##SavedWorkersPopup");
|
||||
}
|
||||
}
|
||||
@@ -439,28 +401,16 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
||||
// --- Worker: Bookmark button ---
|
||||
ImGui::SameLine(0, 0);
|
||||
{
|
||||
ImVec2 btnPos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 btnSize(iconBtnW, inputFrameH2);
|
||||
ImGui::InvisibleButton("##SaveWorkerAddr", btnSize);
|
||||
bool btnHov = ImGui::IsItemHovered();
|
||||
bool btnClk = ImGui::IsItemClicked();
|
||||
ImDrawList* dl2 = ImGui::GetWindowDrawList();
|
||||
ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
|
||||
std::string currentWorker(s_pool_worker);
|
||||
bool alreadySaved = miningValueAlreadySaved(app->settings()->getSavedPoolWorkers(), currentWorker);
|
||||
if (btnHov) {
|
||||
dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
|
||||
StateHover(), 4.0f * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", alreadySaved ? TR("mining_already_saved") : TR("mining_save_payout_address"));
|
||||
}
|
||||
ImFont* icoFont = Type().iconSmall();
|
||||
const char* saveIcon = alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER;
|
||||
ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, saveIcon);
|
||||
dl2->AddText(icoFont, icoFont->LegacySize,
|
||||
ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
|
||||
alreadySaved ? Primary() : OnSurfaceMedium(), saveIcon);
|
||||
if (btnClk && !currentWorker.empty() && currentWorker != "x" && !alreadySaved) {
|
||||
material::IconButtonStyle st;
|
||||
st.color = alreadySaved ? Primary() : OnSurfaceMedium();
|
||||
st.hoverBg = StateHover();
|
||||
st.bgRounding = 4.0f * dp;
|
||||
st.tooltip = alreadySaved ? TR("mining_already_saved") : TR("mining_save_payout_address");
|
||||
if (material::IconButton("##SaveWorkerAddr", alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER,
|
||||
Type().iconSmall(), ImVec2(iconBtnW, inputFrameH2), st)
|
||||
&& !currentWorker.empty() && currentWorker != "x" && !alreadySaved) {
|
||||
app->settings()->addSavedPoolWorker(currentWorker);
|
||||
app->settings()->save();
|
||||
}
|
||||
@@ -579,32 +529,13 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
||||
// === Reset to defaults button ===
|
||||
ImGui::SameLine(0, Layout::spacingSm());
|
||||
{
|
||||
ImVec2 btnPos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 btnSize(inputFrameH2, inputFrameH2);
|
||||
ImGui::InvisibleButton("##ResetPoolDefaults", btnSize);
|
||||
bool btnHov = ImGui::IsItemHovered();
|
||||
bool btnClk = ImGui::IsItemClicked();
|
||||
|
||||
ImDrawList* dl2 = ImGui::GetWindowDrawList();
|
||||
ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
|
||||
|
||||
// Hover highlight
|
||||
if (btnHov) {
|
||||
dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
|
||||
StateHover(), 4.0f * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", TR("mining_reset_defaults"));
|
||||
}
|
||||
|
||||
// Icon
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
const char* resetIcon = ICON_MD_REFRESH;
|
||||
ImVec2 iconSz = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, resetIcon);
|
||||
dl2->AddText(iconFont, iconFont->LegacySize,
|
||||
ImVec2(btnCenter.x - iconSz.x * 0.5f, btnCenter.y - iconSz.y * 0.5f),
|
||||
OnSurfaceMedium(), resetIcon);
|
||||
|
||||
if (btnClk) {
|
||||
material::IconButtonStyle st;
|
||||
st.color = OnSurfaceMedium();
|
||||
st.hoverBg = StateHover();
|
||||
st.bgRounding = 4.0f * dp;
|
||||
st.tooltip = TR("mining_reset_defaults");
|
||||
if (material::IconButton("##ResetPoolDefaults", ICON_MD_REFRESH, Type().iconSmall(),
|
||||
ImVec2(inputFrameH2, inputFrameH2), st)) {
|
||||
strncpy(s_pool_url, defaultPoolUrl(), sizeof(s_pool_url) - 1);
|
||||
// Default to user's first shielded (z) address for pool payouts.
|
||||
// Leave blank if no z-address exists yet.
|
||||
|
||||
Reference in New Issue
Block a user