feat(mining): left pool card (Manual/Auto, metrics, scrollable pool list)

Split the pool-mode hashrate card into a 25%-width left card + 75% chart so the
Manual/Auto toggle and pool metrics no longer reflow the tab:
- left card: Manual|Auto toggle (with tooltips), a Local/Pool/Shares/Uptime
  metric stack, and a fixed-height scrolling pool list — rows click-to-select in
  Manual (reusing the existing save+restart path) and show a "mining here" dot in
  Auto. Fixed height keeps the card constant as pools are added.
- mode-toggle row: keep the suggested-pools dropdown + auto-mode URL disable;
  the toggle and pool list moved into the card.
- mining_controls: show the installed/running miner version ("Current: …")
  before and during mining instead of "none".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 14:35:33 -05:00
parent 5c490c8d53
commit 1a00ec3359
5 changed files with 532 additions and 261 deletions

View File

@@ -10,6 +10,7 @@
#include "../../config/version.h"
#include "../../config/settings.h"
#include "../../util/i18n.h"
#include "../../util/pool_registry.h"
#include "../../util/platform.h"
#include "../schema/ui_schema.h"
#include "../material/type.h"
@@ -148,6 +149,18 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
float inputsStartX = tMax.x + Layout::spacingLg();
ImGui::SetCursorScreenPos(ImVec2(inputsStartX, tMin.y + vertOff));
// Manual vs Auto-balance pool selection. In Auto mode the wallet picks the
// pool by hashrate (App::updatePoolAutoBalance), so the URL group is shown
// disabled and mirrors the auto-selected pool.
const bool autoMode = app->settings()->getPoolSelectMode()
== config::Settings::PoolSelectMode::AutoBalance;
if (autoMode) {
const std::string cur = app->settings()->getPoolUrl();
if (cur != s_pool_url) {
strncpy(s_pool_url, cur.c_str(), sizeof(s_pool_url) - 1);
s_pool_url[sizeof(s_pool_url) - 1] = '\0';
}
}
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f * dp);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8 * dp, 4 * dp));
@@ -168,7 +181,8 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
float urlGroupStartY = ImGui::GetCursorScreenPos().y;
float urlGroupW = urlW + perGroupExtra;
// === Pool URL input ===
// === Pool URL input === (disabled in Auto mode — the wallet picks the pool)
if (autoMode) ImGui::BeginDisabled();
ImGui::SetNextItemWidth(urlW);
if (ImGui::InputTextWithHint("##PoolURL", TR("mining_pool_url"), s_pool_url, sizeof(s_pool_url))) {
s_pool_settings_dirty = true;
@@ -240,6 +254,45 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2 * dp));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
if (ImGui::BeginPopup("##SavedPoolsPopup")) {
// Suggested (official) pools — one click to select, shown above the
// user's saved favorites.
{
const float sPopupInnerW = ImGui::GetContentRegionAvail().x;
const float sRowH = ImGui::GetFrameHeight();
const float sTextPadX = 8 * dp;
ImGui::SetCursorPosX(sTextPadX);
ImGui::PushFont(Type().caption());
ImGui::TextDisabled("%s", TR("mining_suggested_pools"));
ImGui::PopFont();
ImFont* sRowFont = ImGui::GetFont();
const float sRowFontSz = ImGui::GetFontSize();
for (const auto& kp : util::knownPools()) {
ImGui::PushID(kp.id.c_str());
ImVec2 rowMin = ImGui::GetCursorScreenPos();
ImVec2 rowMax(rowMin.x + sPopupInnerW, rowMin.y + sRowH);
ImGui::InvisibleButton("##sugg", ImVec2(sPopupInnerW, sRowH));
bool rowHov = ImGui::IsItemHovered();
bool rowClk = ImGui::IsItemClicked();
bool isCurrent = (std::string(s_pool_url) == kp.stratum);
ImDrawList* pdl = ImGui::GetWindowDrawList();
if (rowHov) pdl->AddRectFilled(rowMin, rowMax, StateHover());
float textY = rowMin.y + (sRowH - sRowFontSz) * 0.5f;
pdl->AddText(sRowFont, sRowFontSz, ImVec2(rowMin.x + sTextPadX, textY),
isCurrent ? Primary() : OnSurface(), kp.stratum.c_str());
if (rowHov) {
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
material::Tooltip("%s", kp.label.c_str());
}
if (rowClk) {
strncpy(s_pool_url, kp.stratum.c_str(), sizeof(s_pool_url) - 1);
s_pool_url[sizeof(s_pool_url) - 1] = '\0';
s_pool_settings_dirty = true;
ImGui::CloseCurrentPopup();
}
ImGui::PopID();
}
ImGui::Separator();
}
const auto& savedUrls = app->settings()->getSavedPoolUrls();
if (savedUrls.empty()) {
ImGui::SetCursorPosX(8 * dp);
@@ -338,6 +391,7 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
ImGui::EndPopup();
}
ImGui::PopStyleVar(3); // WindowRounding, WindowPadding, ItemSpacing for URL popup
if (autoMode) ImGui::EndDisabled();
ImGui::SameLine(0, Layout::spacingSm());
float wrkGroupStartX = ImGui::GetCursorScreenPos().x;
float wrkGroupStartY = ImGui::GetCursorScreenPos().y;