From 721fb292c4a720f4458f2ca97a7eb8083bd0a6c1 Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 27 Feb 2026 00:48:13 -0600 Subject: [PATCH] added visual feedback to mining button to show miner is in process of startup/shutdown --- src/ui/windows/mining_tab.cpp | 104 ++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 23 deletions(-) diff --git a/src/ui/windows/mining_tab.cpp b/src/ui/windows/mining_tab.cpp index 9c33f33..e4f4a23 100644 --- a/src/ui/windows/mining_tab.cpp +++ b/src/ui/windows/mining_tab.cpp @@ -607,12 +607,20 @@ void RenderMiningTab(App* app) bool btnClicked = btnHovered && ImGui::IsMouseClicked(0); bool isSyncing = state.sync.syncing; bool poolBlockedBySolo = s_pool_mode && mining.generate && !state.pool_mining.xmrig_running; - bool disabled = !app->isConnected() || app->isMiningToggleInProgress() || isSyncing || poolBlockedBySolo; + bool isToggling = app->isMiningToggleInProgress(); + bool disabled = !app->isConnected() || isToggling || isSyncing || poolBlockedBySolo; // Glass panel background with state-dependent tint GlassPanelSpec btnGlass; btnGlass.rounding = Layout::glassRounding(); - if (isMiningActive) { + if (isToggling) { + // Toggling: subtle pulsing glow to indicate activity + float pulse = effects::isLowSpecMode() + ? 0.5f + : 0.5f + 0.5f * (float)std::sin((double)ImGui::GetTime() * 4.0); + int glowA = (int)(15 + 25 * pulse); + btnGlass.fillAlpha = glowA; + } else if (isMiningActive) { // Active mining: warm glow float pulse = effects::isLowSpecMode() ? schema::UI().drawElement("animations", "pulse-base-glow").size @@ -638,36 +646,84 @@ void RenderMiningTab(App* app) float cx = bMin.x + btnW * 0.5f; float cy = bMin.y + btnH * schema::UI().drawElement("tabs.mining", "button-icon-y-ratio").size; // shift up to leave room for label - ImU32 iconCol; - if (disabled) { - iconCol = OnSurfaceDisabled(); - } else if (isMiningActive) { - float pulse = effects::isLowSpecMode() - ? schema::UI().drawElement("animations", "pulse-base-normal").size - : schema::UI().drawElement("animations", "pulse-base-normal").size + schema::UI().drawElement("animations", "pulse-amp-normal").size * (float)std::sin((double)ImGui::GetTime() * schema::UI().drawElement("animations", "pulse-speed-normal").size); - iconCol = WithAlpha(Error(), (int)(200 + 55 * pulse)); - } else { - iconCol = btnHovered ? WithAlpha(Success(), 255) : OnSurfaceMedium(); - } + if (isToggling) { + // Draw a spinning arc spinner to indicate progress + float spinnerR = std::min(btnW, btnH) * 0.18f; + float thickness = std::max(2.5f, spinnerR * 0.15f); + float time = (float)ImGui::GetTime(); - // Use XL icon for the large mining button - ImFont* iconFont = Type().iconXL(); - const char* mineIcon = isMiningActive ? ICON_MD_CLOSE : ICON_MD_CONSTRUCTION; - ImVec2 iconSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, mineIcon); - dl->AddText(iconFont, iconFont->LegacySize, - ImVec2(cx - iconSz.x * 0.5f, cy - iconSz.y * 0.5f), iconCol, mineIcon); + // Track circle (faint) + ImU32 trackCol = WithAlpha(Primary(), 40); + dl->AddCircle(ImVec2(cx, cy), spinnerR, trackCol, 0, thickness); + + // Animated arc + float rotation = fmodf(time * 2.0f * IM_PI / 1.4f, IM_PI * 2.0f); + float cycleTime = fmodf(time, 1.333f); + float arcLength = (cycleTime < 0.666f) + ? (cycleTime / 0.666f) * 0.75f + 0.1f + : ((1.333f - cycleTime) / 0.666f) * 0.75f + 0.1f; + + float startAngle = rotation - IM_PI * 0.5f; + float endAngle = startAngle + IM_PI * 2.0f * arcLength; + int segments = (int)(32 * arcLength) + 1; + float angleStep = (endAngle - startAngle) / segments; + ImU32 arcCol = Primary(); + + for (int i = 0; i < segments; i++) { + float a1 = startAngle + angleStep * i; + float a2 = startAngle + angleStep * (i + 1); + ImVec2 p1(cx + cosf(a1) * spinnerR, cy + sinf(a1) * spinnerR); + ImVec2 p2(cx + cosf(a2) * spinnerR, cy + sinf(a2) * spinnerR); + dl->AddLine(p1, p2, arcCol, thickness); + } + } else { + ImU32 iconCol; + if (disabled) { + iconCol = OnSurfaceDisabled(); + } else if (isMiningActive) { + float pulse = effects::isLowSpecMode() + ? schema::UI().drawElement("animations", "pulse-base-normal").size + : schema::UI().drawElement("animations", "pulse-base-normal").size + schema::UI().drawElement("animations", "pulse-amp-normal").size * (float)std::sin((double)ImGui::GetTime() * schema::UI().drawElement("animations", "pulse-speed-normal").size); + iconCol = WithAlpha(Error(), (int)(200 + 55 * pulse)); + } else { + iconCol = btnHovered ? WithAlpha(Success(), 255) : OnSurfaceMedium(); + } + + // Use XL icon for the large mining button + ImFont* iconFont = Type().iconXL(); + const char* mineIcon = isMiningActive ? ICON_MD_CLOSE : ICON_MD_CONSTRUCTION; + ImVec2 iconSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, mineIcon); + dl->AddText(iconFont, iconFont->LegacySize, + ImVec2(cx - iconSz.x * 0.5f, cy - iconSz.y * 0.5f), iconCol, mineIcon); + } } // Label below icon { float btnW = bMax.x - bMin.x; float btnH = bMax.y - bMin.y; - const char* label = isMiningActive ? "STOP" : "MINE"; + const char* label; + ImU32 lblCol; + if (isToggling) { + label = isMiningActive ? "STOPPING" : "STARTING"; + // Animated dots effect via alpha pulse + float pulse = effects::isLowSpecMode() + ? 0.7f + : 0.5f + 0.5f * (float)std::sin((double)ImGui::GetTime() * 3.0); + lblCol = WithAlpha(Primary(), (int)(120 + 135 * pulse)); + } else if (isMiningActive) { + label = "STOP"; + lblCol = WithAlpha(Error(), 220); + } else if (disabled) { + label = "MINE"; + lblCol = WithAlpha(OnSurface(), 50); + } else { + label = "MINE"; + lblCol = WithAlpha(OnSurface(), 160); + } ImVec2 lblSz = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, label); float lblX = bMin.x + (btnW - lblSz.x) * 0.5f; float lblY = bMin.y + btnH * schema::UI().drawElement("tabs.mining", "button-label-y-ratio").size; - ImU32 lblCol = disabled ? WithAlpha(OnSurface(), 50) : - (isMiningActive ? WithAlpha(Error(), 220) : WithAlpha(OnSurface(), 160)); dl->AddText(ovFont, ovFont->LegacySize, ImVec2(lblX, lblY), lblCol, label); } @@ -675,7 +731,9 @@ void RenderMiningTab(App* app) if (btnHovered) { if (!disabled) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - if (isSyncing) + if (isToggling) + ImGui::SetTooltip(isMiningActive ? "Stopping miner..." : "Starting miner..."); + else if (isSyncing) ImGui::SetTooltip("Syncing blockchain... (%.1f%%)", state.sync.verification_progress * 100.0); else if (poolBlockedBySolo) ImGui::SetTooltip("Stop solo mining before starting pool mining");