// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "mining_mode_toggle.h" #include "mining_tab_helpers.h" #include "mining_pool_panel.h" #include "../../app.h" #include "../../config/version.h" #include "../../config/settings.h" #include "../../util/i18n.h" #include "../../util/platform.h" #include "../schema/ui_schema.h" #include "../material/type.h" #include "../material/draw_helpers.h" #include "../material/colors.h" #include "../material/components/buttons.h" #include "../layout.h" #include "../notifications.h" #include "../../embedded/IconsMaterialDesign.h" #include "imgui.h" #include "imgui_internal.h" #include #include #include #include #include #include namespace dragonx { namespace ui { using namespace material; void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo& mining, ImDrawList* dl, ImFont* capFont, ImFont* ovFont, float dp, float hs, float gap, float availWidth, bool& s_pool_mode, char (&s_pool_url)[256], char (&s_pool_worker)[256], bool& s_pool_settings_dirty) { const bool soloMiningAvailable = app->supportsSoloMining(); float toggleW = schema::UI().drawElement("tabs.mining", "mode-toggle-width").size * hs; float toggleH = schema::UI().drawElement("tabs.mining", "mode-toggle-height").size; float toggleRnd = schema::UI().drawElement("tabs.mining", "mode-toggle-rounding").size; float totalW = soloMiningAvailable ? (toggleW * 2) : toggleW; ImVec2 tMin = ImGui::GetCursorScreenPos(); ImVec2 tMax(tMin.x + totalW, tMin.y + toggleH); // Glass background for the segmented control dl->AddRectFilled(tMin, tMax, WithAlpha(OnSurface(), 15), toggleRnd); dl->AddRect(tMin, tMax, WithAlpha(OnSurface(), 40), toggleRnd); ImVec2 soloMin = tMin; ImVec2 soloMax(tMin.x + toggleW, tMax.y); bool soloHov = material::IsRectHovered(soloMin, soloMax); if (soloMiningAvailable) { // SOLO button (left half) if (!s_pool_mode) { dl->AddRectFilled(soloMin, soloMax, WithAlpha(Primary(), 180), toggleRnd); } else if (soloHov) { dl->AddRectFilled(soloMin, soloMax, WithAlpha(OnSurface(), 20), toggleRnd); } const char* label = TR("mining_solo"); ImVec2 sz = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, label); float lx = soloMin.x + (toggleW - sz.x) * 0.5f; float ly = soloMin.y + (toggleH - sz.y) * 0.5f; ImU32 col = !s_pool_mode ? IM_COL32(255, 255, 255, 230) : OnSurfaceMedium(); dl->AddText(ovFont, ovFont->LegacySize, ImVec2(lx, ly), col, label); } // POOL button (right half) — disabled when solo mining is active bool soloMiningActive = mining.generate; ImVec2 poolMin(tMin.x + (soloMiningAvailable ? toggleW : 0.0f), tMin.y); ImVec2 poolMax = tMax; bool poolHov = material::IsRectHovered(poolMin, poolMax); if (s_pool_mode) { dl->AddRectFilled(poolMin, poolMax, WithAlpha(Primary(), 180), toggleRnd); } else if (soloMiningActive) { // Dimmed — solo mining blocks pool mode } else if (poolHov) { dl->AddRectFilled(poolMin, poolMax, WithAlpha(OnSurface(), 20), toggleRnd); } { const char* label = TR("mining_pool"); ImVec2 sz = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, label); float lx = poolMin.x + (toggleW - sz.x) * 0.5f; float ly = poolMin.y + (toggleH - sz.y) * 0.5f; ImU32 col = s_pool_mode ? IM_COL32(255, 255, 255, 230) : (soloMiningActive ? OnSurfaceDisabled() : OnSurfaceMedium()); dl->AddText(ovFont, ovFont->LegacySize, ImVec2(lx, ly), col, label); } // Invisible buttons for click targets if (soloMiningAvailable) { ImGui::SetCursorScreenPos(soloMin); ImGui::InvisibleButton("##SoloMode", ImVec2(toggleW, toggleH)); if (ImGui::IsItemClicked() && s_pool_mode) { s_pool_mode = false; app->settings()->setPoolMode(false); app->settings()->save(); app->stopPoolMining(); } if (soloHov) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); } ImGui::SetCursorScreenPos(poolMin); ImGui::InvisibleButton("##PoolMode", ImVec2(toggleW, toggleH)); if (soloMiningAvailable && ImGui::IsItemClicked() && !s_pool_mode && !soloMiningActive) { s_pool_mode = true; app->settings()->setPoolMode(true); app->settings()->save(); // Note: soloMiningActive is already false (checked above), // so no need to call stopMining() — it would just set the // toggle-in-progress flag and make the button show "STARTING". } if (soloMiningAvailable && poolHov && !soloMiningActive) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); if (poolHov && soloMiningActive && !s_pool_mode) { ImGui::SetTooltip("%s", TR("mining_stop_solo_for_pool")); } ImGui::SetCursorScreenPos(ImVec2(tMin.x, tMax.y)); ImGui::Dummy(ImVec2(totalW, 0)); // Pool URL + Worker inputs inline next to toggle (pool mode only) if (s_pool_mode && soloMiningActive) { // Solo mining is active — show disabled message instead of inputs float inputFrameH = ImGui::GetFrameHeight(); float vertOff = (toggleH - inputFrameH) * 0.5f; ImGui::SetCursorScreenPos(ImVec2(tMax.x + Layout::spacingLg(), tMin.y + vertOff)); ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Warning())); ImGui::AlignTextToFramePadding(); ImGui::PushFont(Type().iconSmall()); ImGui::TextUnformatted(ICON_MD_INFO); ImGui::PopFont(); ImGui::SameLine(0, Layout::spacingSm()); ImGui::PushFont(capFont); ImGui::TextUnformatted(TR("mining_stop_solo_for_pool_settings")); ImGui::PopFont(); ImGui::PopStyleColor(); } else if (s_pool_mode) { // Position inputs to the right of the toggle float inputFrameH = ImGui::GetFrameHeight(); float vertOff = (toggleH - inputFrameH) * 0.5f; float inputsStartX = tMax.x + Layout::spacingLg(); ImGui::SetCursorScreenPos(ImVec2(inputsStartX, tMin.y + vertOff)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f * dp); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8 * dp, 4 * dp)); float inputFrameH2 = ImGui::GetFrameHeight(); float iconBtnW = inputFrameH2; float resetBtnW = iconBtnW; float contentEndX = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; // Each input group: [input][▼][bookmark] // Layout: [URL group] [spacing] [Worker group] [spacing] [reset] float perGroupExtra = iconBtnW * 2; // dropdown + bookmark float remainW = contentEndX - inputsStartX - Layout::spacingSm() - resetBtnW - Layout::spacingSm() - perGroupExtra * 2; float urlW = std::max(60.0f, remainW * 0.30f); float wrkW = std::max(40.0f, remainW * 0.70f); // Track positions for popup alignment float urlGroupStartX = ImGui::GetCursorScreenPos().x; float urlGroupStartY = ImGui::GetCursorScreenPos().y; float urlGroupW = urlW + perGroupExtra; // === Pool URL input === ImGui::SetNextItemWidth(urlW); if (ImGui::InputTextWithHint("##PoolURL", TR("mining_pool_url"), s_pool_url, sizeof(s_pool_url))) { s_pool_settings_dirty = true; } // --- 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); ImGui::SetTooltip("%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) { ImGui::OpenPopup("##SavedPoolsPopup"); } } // --- 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); ImGui::SetTooltip("%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) { app->settings()->addSavedPoolUrl(currentUrl); app->settings()->save(); } } // --- URL: Popup positioned below the input group --- // Match popup width to input group; zero horizontal padding so // item highlights are flush with the popup container edges. ImGui::SetNextWindowPos(ImVec2(urlGroupStartX, urlGroupStartY + inputFrameH2)); ImGui::SetNextWindowSize(ImVec2(urlGroupW, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f * dp); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2 * dp)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); if (ImGui::BeginPopup("##SavedPoolsPopup")) { const auto& savedUrls = app->settings()->getSavedPoolUrls(); if (savedUrls.empty()) { ImGui::SetCursorPosX(8 * dp); ImGui::PushFont(Type().caption()); ImGui::TextDisabled("%s", TR("mining_no_saved_pools")); ImGui::PopFont(); ImGui::SetCursorPosX(8 * dp); ImGui::PushFont(Type().caption()); ImGui::TextDisabled("%s", TR("mining_click")); ImGui::PopFont(); ImGui::SameLine(0, 2 * dp); ImGui::PushFont(Type().iconSmall()); ImGui::TextDisabled(ICON_MD_BOOKMARK_BORDER); ImGui::PopFont(); ImGui::SameLine(0, 2 * dp); ImGui::PushFont(Type().caption()); ImGui::TextDisabled("%s", TR("mining_to_save")); ImGui::PopFont(); } else { std::string urlToRemove; float popupInnerW = ImGui::GetContentRegionAvail().x; float xZoneW = ImGui::GetFrameHeight(); float textPadX = 8 * dp; ImFont* rowFont = ImGui::GetFont(); float rowFontSz = ImGui::GetFontSize(); float rowH = ImGui::GetFrameHeight(); for (const auto& url : savedUrls) { ImGui::PushID(url.c_str()); bool isCurrent = (std::string(s_pool_url) == url); ImVec2 rowMin = ImGui::GetCursorScreenPos(); ImVec2 rowMax(rowMin.x + popupInnerW, rowMin.y + rowH); ImGui::InvisibleButton("##row", ImVec2(popupInnerW, rowH)); bool rowHov = ImGui::IsItemHovered(); bool rowClk = ImGui::IsItemClicked(); ImDrawList* pdl = ImGui::GetWindowDrawList(); bool inXZone = rowHov && ImGui::GetMousePos().x >= rowMax.x - xZoneW; // Row background — flush with popup edges if (isCurrent) pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10)); if (rowHov && !inXZone) pdl->AddRectFilled(rowMin, rowMax, StateHover()); // Item text with internal padding float textY = rowMin.y + (rowH - rowFontSz) * 0.5f; pdl->AddText(rowFont, rowFontSz, ImVec2(rowMin.x + textPadX, textY), isCurrent ? Primary() : OnSurface(), url.c_str()); // X button — flush with right edge, icon centered { ImVec2 xMin(rowMax.x - xZoneW, rowMin.y); ImVec2 xMax(rowMax.x, rowMax.y); if (inXZone) { pdl->AddRectFilled(xMin, xMax, IM_COL32(255, 80, 80, 30)); ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); ImGui::SetTooltip("%s", TR("mining_remove")); } else if (rowHov) { // Show faint X when row is hovered ImFont* icoF = Type().iconSmall(); const char* xIcon = ICON_MD_CLOSE; ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon); ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f); pdl->AddText(icoF, icoF->LegacySize, ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f), OnSurfaceDisabled(), xIcon); } // Always draw icon when hovering X zone if (inXZone) { ImFont* icoF = Type().iconSmall(); const char* xIcon = ICON_MD_CLOSE; ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon); ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f); pdl->AddText(icoF, icoF->LegacySize, ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f), Error(), xIcon); } } // Click handling if (rowClk) { if (inXZone) { urlToRemove = url; } else { strncpy(s_pool_url, url.c_str(), sizeof(s_pool_url) - 1); s_pool_url[sizeof(s_pool_url) - 1] = '\0'; s_pool_settings_dirty = true; ImGui::CloseCurrentPopup(); } } if (rowHov && !inXZone) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); ImGui::PopID(); } if (!urlToRemove.empty()) { app->settings()->removeSavedPoolUrl(urlToRemove); app->settings()->save(); } } ImGui::EndPopup(); } ImGui::PopStyleVar(3); // WindowRounding, WindowPadding, ItemSpacing for URL popup ImGui::SameLine(0, Layout::spacingSm()); float wrkGroupStartX = ImGui::GetCursorScreenPos().x; float wrkGroupStartY = ImGui::GetCursorScreenPos().y; float wrkGroupW = wrkW + perGroupExtra; ImGui::SetNextItemWidth(wrkW); if (ImGui::InputTextWithHint("##PoolWorker", TR("mining_payout_address"), s_pool_worker, sizeof(s_pool_worker))) { s_pool_settings_dirty = true; } if (ImGui::IsItemHovered()) { std::string currentWorkerStr(s_pool_worker); if (currentWorkerStr.empty()) { ImGui::SetTooltip("%s", TR("mining_generate_z_address_hint")); } else { ImGui::SetTooltip("%s", TR("mining_payout_tooltip")); } } // --- 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); ImGui::SetTooltip("%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) { ImGui::OpenPopup("##SavedWorkersPopup"); } } // --- 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); ImGui::SetTooltip("%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) { app->settings()->addSavedPoolWorker(currentWorker); app->settings()->save(); } } // --- Worker: Popup positioned below the input group --- // Popup sized to fit full z-addresses without truncation; // zero horizontal padding so item highlights are flush with edges. float addrPopupW = std::max(wrkGroupW, availWidth * 0.55f); ImGui::SetNextWindowPos(ImVec2(wrkGroupStartX, wrkGroupStartY + inputFrameH2)); ImGui::SetNextWindowSize(ImVec2(addrPopupW, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f * dp); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2 * dp)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); if (ImGui::BeginPopup("##SavedWorkersPopup")) { const auto& savedWorkers = app->settings()->getSavedPoolWorkers(); if (savedWorkers.empty()) { ImGui::SetCursorPosX(8 * dp); ImGui::PushFont(Type().caption()); ImGui::TextDisabled("%s", TR("mining_no_saved_addresses")); ImGui::PopFont(); ImGui::SetCursorPosX(8 * dp); ImGui::PushFont(Type().caption()); ImGui::TextDisabled("%s", TR("mining_click")); ImGui::PopFont(); ImGui::SameLine(0, 2 * dp); ImGui::PushFont(Type().iconSmall()); ImGui::TextDisabled(ICON_MD_BOOKMARK_BORDER); ImGui::PopFont(); ImGui::SameLine(0, 2 * dp); ImGui::PushFont(Type().caption()); ImGui::TextDisabled("%s", TR("mining_to_save")); ImGui::PopFont(); } else { std::string addrToRemove; float wPopupInnerW = ImGui::GetContentRegionAvail().x; float wXZoneW = ImGui::GetFrameHeight(); float wTextPadX = 8 * dp; ImFont* wRowFont = ImGui::GetFont(); float wRowFontSz = ImGui::GetFontSize(); float wRowH = ImGui::GetFrameHeight(); for (const auto& addr : savedWorkers) { ImGui::PushID(addr.c_str()); bool isCurrent = (std::string(s_pool_worker) == addr); ImVec2 rowMin = ImGui::GetCursorScreenPos(); ImVec2 rowMax(rowMin.x + wPopupInnerW, rowMin.y + wRowH); ImGui::InvisibleButton("##row", ImVec2(wPopupInnerW, wRowH)); bool rowHov = ImGui::IsItemHovered(); bool rowClk = ImGui::IsItemClicked(); ImDrawList* pdl = ImGui::GetWindowDrawList(); bool inXZone = rowHov && ImGui::GetMousePos().x >= rowMax.x - wXZoneW; // Row background — flush with popup edges if (isCurrent) pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10)); if (rowHov && !inXZone) pdl->AddRectFilled(rowMin, rowMax, StateHover()); // Full address text with internal padding float textY = rowMin.y + (wRowH - wRowFontSz) * 0.5f; pdl->AddText(wRowFont, wRowFontSz, ImVec2(rowMin.x + wTextPadX, textY), isCurrent ? Primary() : OnSurface(), addr.c_str()); // Tooltip for long addresses if (rowHov && !inXZone) ImGui::SetTooltip("%s", addr.c_str()); // X button — flush with right edge, icon centered { ImVec2 xMin(rowMax.x - wXZoneW, rowMin.y); ImVec2 xMax(rowMax.x, rowMax.y); if (inXZone) { pdl->AddRectFilled(xMin, xMax, IM_COL32(255, 80, 80, 30)); ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); ImGui::SetTooltip("%s", TR("mining_remove")); } else if (rowHov) { ImFont* icoF = Type().iconSmall(); const char* xIcon = ICON_MD_CLOSE; ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon); ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f); pdl->AddText(icoF, icoF->LegacySize, ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f), OnSurfaceDisabled(), xIcon); } if (inXZone) { ImFont* icoF = Type().iconSmall(); const char* xIcon = ICON_MD_CLOSE; ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon); ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f); pdl->AddText(icoF, icoF->LegacySize, ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f), Error(), xIcon); } } // Click handling if (rowClk) { if (inXZone) { addrToRemove = addr; } else { strncpy(s_pool_worker, addr.c_str(), sizeof(s_pool_worker) - 1); s_pool_worker[sizeof(s_pool_worker) - 1] = '\0'; s_pool_settings_dirty = true; ImGui::CloseCurrentPopup(); } } if (rowHov && !inXZone) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); ImGui::PopID(); } if (!addrToRemove.empty()) { app->settings()->removeSavedPoolWorker(addrToRemove); app->settings()->save(); } } ImGui::EndPopup(); } ImGui::PopStyleVar(3); // WindowRounding, WindowPadding, ItemSpacing for Worker popup // === 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); ImGui::SetTooltip("%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) { 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. std::string defaultAddr = defaultPoolWorkerAddress(state.addresses); strncpy(s_pool_worker, defaultAddr.c_str(), sizeof(s_pool_worker) - 1); s_pool_worker[sizeof(s_pool_worker) - 1] = '\0'; s_pool_settings_dirty = true; } } ImGui::PopStyleVar(2); } // Ensure cursor Y is at toggle bottom regardless of pool input widgets, // so the cards below stay at the same position in both solo and pool modes. ImGui::SetCursorScreenPos(ImVec2(tMin.x, tMax.y)); ImGui::Dummy(ImVec2(0, gap * 0.5f)); } } // namespace ui } // namespace dragonx