refactor(mining): extract the Mode toggle into mining_mode_toggle.{h,cpp} (audit #10, slice 4)
Final slice of decomposing mining_tab.cpp. The ~529-line "Mode toggle" section (SOLO | POOL segmented control + pool URL/worker inputs) is moved verbatim into RenderMiningModeToggle(). mining_tab.cpp is now 311 lines (was 2628) — just the tab dispatch, thread-sync glue, benchmark advance, section-budget setup, and four card calls. State the toggle mutates is passed BY REFERENCE so behaviour is identical: the pool-mode flag, the settings-dirty flag, and the pool URL / worker char[256] buffers (the text inputs write into them) — passed as char(&)[256] references and named with their original identifiers so the body stays byte-identical. Verified: full-node + Windows + lite build, tests, hygiene. Audit #10 complete: the 2628-line monolith is now five focused files (earnings, stats, controls, mode-toggle + the 311-line shell). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -445,6 +445,7 @@ set(APP_SOURCES
|
||||
src/ui/windows/mining_earnings.cpp
|
||||
src/ui/windows/mining_stats.cpp
|
||||
src/ui/windows/mining_controls.cpp
|
||||
src/ui/windows/mining_mode_toggle.cpp
|
||||
src/ui/windows/mining_benchmark.cpp
|
||||
src/ui/windows/mining_pool_panel.cpp
|
||||
src/ui/windows/mining_tab_helpers.cpp
|
||||
|
||||
575
src/ui/windows/mining_mode_toggle.cpp
Normal file
575
src/ui/windows/mining_mode_toggle.cpp
Normal file
@@ -0,0 +1,575 @@
|
||||
// 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 <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
25
src/ui/windows/mining_mode_toggle.h
Normal file
25
src/ui/windows/mining_mode_toggle.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../data/wallet_state.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dragonx {
|
||||
class App;
|
||||
namespace ui {
|
||||
|
||||
// Renders the mining "Mode toggle" (SOLO | POOL segmented control + pool URL/worker inputs).
|
||||
// Extracted verbatim from the monolithic mining tab. The state it mutates is passed BY REFERENCE:
|
||||
// the pool-mode flag, the pool URL / worker text buffers, and the settings-dirty flag (named with
|
||||
// their original identifiers so the moved body is byte-identical).
|
||||
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);
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "mining_earnings.h"
|
||||
#include "mining_stats.h"
|
||||
#include "mining_controls.h"
|
||||
#include "mining_mode_toggle.h"
|
||||
#include "xmrig_download_dialog.h"
|
||||
#include "../../util/xmrig_updater.h"
|
||||
#include "../../app.h"
|
||||
@@ -258,537 +259,8 @@ static void RenderMiningTabContent(App* app)
|
||||
// ================================================================
|
||||
// MODE TOGGLE — SOLO | POOL segmented control
|
||||
// ================================================================
|
||||
{
|
||||
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));
|
||||
}
|
||||
RenderMiningModeToggle(app, state, mining, dl, capFont, ovFont, dp, hs, gap, availWidth,
|
||||
s_pool_mode, s_pool_url, s_pool_worker, s_pool_settings_dirty);
|
||||
|
||||
// ================================================================
|
||||
// CONTROLS — Glass card with CPU core grid (no heading)
|
||||
|
||||
Reference in New Issue
Block a user