From c53b7f771e1d2151817c94fb58ff73c37a297141 Mon Sep 17 00:00:00 2001 From: DanS Date: Wed, 22 Jul 2026 16:01:51 -0500 Subject: [PATCH] fix(ui): overview/mining polish + default-pool cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - balance: inset the address-row favorite (star) button by the card's inner padding so it mirrors the left margin instead of hugging the card edge. - mining: remove pool.dragonx.cc from the built-in default pools (pool.dragonx.is is now the sole default); update the pool-registry test accordingly. - mining: middle-truncate saved pool-URL and payout-address dropdown rows (new shared material::TruncateToWidth helper) so a full z-address no longer runs under the trailing delete (X) button. - mining: fix the thread-grid cells overflowing the card at >100% display scaling — the reserved Mine-button width used a raw clamp that didn't scale; scale it by dp so cols is estimated correctly (no-op at 100%). Full-node build + test suite green. Co-Authored-By: Claude Fable 5 --- src/ui/material/draw_helpers.h | 15 ++++++++++++++ src/ui/windows/balance_address_list.cpp | 5 ++++- src/ui/windows/mining_controls.cpp | 5 ++++- src/ui/windows/mining_mode_toggle.cpp | 14 +++++++++---- src/util/pool_registry_core.cpp | 8 -------- tests/test_phase4.cpp | 27 ++++++++++++------------- 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/ui/material/draw_helpers.h b/src/ui/material/draw_helpers.h index 422d541..0111650 100644 --- a/src/ui/material/draw_helpers.h +++ b/src/ui/material/draw_helpers.h @@ -57,6 +57,21 @@ inline ImU32 ReadableError() { return IM_COL32(r, g, b, (e >> IM_COL32_A_SHIFT) & 0xFF); } +// Middle-ellipsis truncation ("front...back", roughly equal halves) so `text` fits within +// maxWidth pixels when drawn with `font` at `fontSize`. Returns `text` unchanged if it already +// fits (or maxWidth is non-positive). Display-only — never mutate the underlying value with this. +inline std::string TruncateToWidth(const std::string& text, ImFont* font, float fontSize, float maxWidth) { + if (text.empty() || !font || maxWidth <= 0.0f) return text; + if (font->CalcTextSizeA(fontSize, FLT_MAX, 0.0f, text.c_str()).x <= maxWidth) return text; + const int n = static_cast(text.size()); + for (int f = n / 2; f >= 3; --f) { + const int b = (f - 2 > 3) ? (f - 2) : 3; // keep the two halves roughly equal + std::string t = text.substr(0, f) + "..." + text.substr(n - b); + if (font->CalcTextSizeA(fontSize, FLT_MAX, 0.0f, t.c_str()).x <= maxWidth) return t; + } + return n > 6 ? (text.substr(0, 3) + "..." + text.substr(n - 3)) : text; +} + // Animated "loading" ellipsis: "", ".", "..", "..." cycling on a ~3Hz phase. inline const char* LoadingDots() { int n = ((int)(ImGui::GetTime() * 3.0f)) % 4; diff --git a/src/ui/windows/balance_address_list.cpp b/src/ui/windows/balance_address_list.cpp index 818c2e6..937baec 100644 --- a/src/ui/windows/balance_address_list.cpp +++ b/src/ui/windows/balance_address_list.cpp @@ -70,6 +70,7 @@ AddressRowLayout ComputeAddressRowLayout(float rowX, float spacingSm, float spacingXs) { + (void)spacingXs; // trailing button now insets by rowPadLeft (mirrors the left margin) AddressRowLayout layout; layout.contentStartX = rowX + rowPadLeft; layout.contentStartY = rowY + spacingMd; @@ -77,7 +78,9 @@ AddressRowLayout ComputeAddressRowLayout(float rowX, const float buttonY = rowY + (rowHeight - layout.buttonSize) * 0.5f; const float rightEdge = rowX + rowWidth; - const float favoriteX = rightEdge - layout.buttonSize - spacingXs; + // Inset the trailing (favorite/star) button by the card's inner padding — the same margin the + // left content uses (rowPadLeft) — so it mirrors the left edge instead of hugging the card edge. + const float favoriteX = rightEdge - layout.buttonSize - rowPadLeft; const float visibilityX = favoriteX - spacingSm - layout.buttonSize; layout.favoriteButton = {favoriteX, buttonY, layout.buttonSize, layout.buttonSize}; diff --git a/src/ui/windows/mining_controls.cpp b/src/ui/windows/mining_controls.cpp index 009fd02..133db30 100644 --- a/src/ui/windows/mining_controls.cpp +++ b/src/ui/windows/mining_controls.cpp @@ -57,7 +57,10 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo& // --- Compute thread grid layout based on controls card width --- // Estimate controlsW first to compute cols correctly - float estControlsW = availWidth - std::min(schema::UI().drawElement("tabs.mining", "button-max-width-clamp").size, miningBtnMaxW) - miningBtnGap; + // The Mine button is square (= card height, which scales with DPI), so the width we + // reserve for it here must scale too — a RAW clamp under-reserves at >100% scaling, which + // over-estimates the grid width and lets the thread cells overflow the card (e.g. at 150%). + float estControlsW = availWidth - std::min(schema::UI().drawElement("tabs.mining", "button-max-width-clamp").size * dp, miningBtnMaxW) - miningBtnGap; float innerW = estControlsW - pad * 2; float cellSz = std::clamp(schema::UI().drawElement("tabs.mining", "cell-size").size * vs, schema::UI().drawElement("tabs.mining", "cell-min-size").size, schema::UI().drawElement("tabs.mining", "cell-max-size").sizeOr(42.0f)); float cellGap = std::max(schema::UI().drawElement("tabs.mining", "cell-gap-min").size, cellSz * schema::UI().drawElement("tabs.mining", "cell-gap-ratio").size); diff --git a/src/ui/windows/mining_mode_toggle.cpp b/src/ui/windows/mining_mode_toggle.cpp index bad367e..862fd36 100644 --- a/src/ui/windows/mining_mode_toggle.cpp +++ b/src/ui/windows/mining_mode_toggle.cpp @@ -308,11 +308,14 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10)); if (rowHov && !inXZone) pdl->AddRectFilled(rowMin, rowMax, StateHover()); - // Item text with internal padding + // Item text with internal padding, middle-truncated so a long saved URL + // can't run under the trailing X (delete) button. float textY = rowMin.y + (rowH - rowFontSz) * 0.5f; + float maxTextW = popupInnerW - xZoneW - textPadX * 2.0f; + std::string urlDisp = material::TruncateToWidth(url, rowFont, rowFontSz, maxTextW); pdl->AddText(rowFont, rowFontSz, ImVec2(rowMin.x + textPadX, textY), - isCurrent ? Primary() : OnSurface(), url.c_str()); + isCurrent ? Primary() : OnSurface(), urlDisp.c_str()); // X button — flush with right edge, icon centered { ImVec2 xMin(rowMax.x - xZoneW, rowMin.y); @@ -467,11 +470,14 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10)); if (rowHov && !inXZone) pdl->AddRectFilled(rowMin, rowMax, StateHover()); - // Full address text with internal padding + // Address text with internal padding, middle-truncated so a full z-address + // (~78 chars) can't run under the trailing X (delete) button. float textY = rowMin.y + (wRowH - wRowFontSz) * 0.5f; + float wMaxTextW = wPopupInnerW - wXZoneW - wTextPadX * 2.0f; + std::string addrDisp = material::TruncateToWidth(addr, wRowFont, wRowFontSz, wMaxTextW); pdl->AddText(wRowFont, wRowFontSz, ImVec2(rowMin.x + wTextPadX, textY), - isCurrent ? Primary() : OnSurface(), addr.c_str()); + isCurrent ? Primary() : OnSurface(), addrDisp.c_str()); // Tooltip for long addresses if (rowHov && !inXZone) material::Tooltip("%s", addr.c_str()); diff --git a/src/util/pool_registry_core.cpp b/src/util/pool_registry_core.cpp index e6cfcc7..619cd76 100644 --- a/src/util/pool_registry_core.cpp +++ b/src/util/pool_registry_core.cpp @@ -30,14 +30,6 @@ const std::vector& knownPools() "https://pool.dragonx.is/api/stats", PoolStatsSchema::DragonXIs, /*miningcorePoolId=*/"", /*feePercent=*/0.0, /*official=*/true, }, - KnownPool{ - // The mining (stratum) host is us.dragonx.cc — pool.dragonx.cc is the - // Cloudflare-proxied web/API host and does NOT accept stratum on :3333. - // Stats still come from pool.dragonx.cc/api/pools (proxied HTTP is fine). - "dragonx-cc-pplns", "pool.dragonx.cc", "us.dragonx.cc:3333", "rx/dragonx", - "https://pool.dragonx.cc/api/pools", PoolStatsSchema::Miningcore, - /*miningcorePoolId=*/"dragonx-pplns", /*feePercent=*/3.0, /*official=*/true, - }, }; return pools; } diff --git a/tests/test_phase4.cpp b/tests/test_phase4.cpp index cd826f5..6925049 100644 --- a/tests/test_phase4.cpp +++ b/tests/test_phase4.cpp @@ -3434,9 +3434,11 @@ void testBalanceAddressListModel() EXPECT_NEAR(layout.contentStartX, 22.0, 0.0001); EXPECT_NEAR(layout.contentStartY, 26.0, 0.0001); EXPECT_NEAR(layout.buttonSize, 38.0, 0.0001); - EXPECT_NEAR(layout.favoriteButton.x, 270.0, 0.0001); - EXPECT_NEAR(layout.visibilityButton.x, 228.0, 0.0001); - EXPECT_NEAR(layout.contentRight, 224.0, 0.0001); + // Trailing (favorite) button is inset by rowPadLeft (12) so it mirrors the left margin + // instead of hugging the card edge: 310 - 38 - 12 = 260. + EXPECT_NEAR(layout.favoriteButton.x, 260.0, 0.0001); + EXPECT_NEAR(layout.visibilityButton.x, 218.0, 0.0001); + EXPECT_NEAR(layout.contentRight, 214.0, 0.0001); EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(2.0, 3.5), std::string("$7.00")); EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.001, 2.0), std::string("$0.002000")); EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.0, 2.0), std::string("")); @@ -5774,23 +5776,20 @@ void testXmrigLiveInstall() void testPoolRegistryLookup() { using namespace dragonx::util; - EXPECT_EQ(knownPools().size(), static_cast(2)); + // pool.dragonx.cc was removed from the built-in defaults; pool.dragonx.is is the sole entry. + EXPECT_EQ(knownPools().size(), static_cast(1)); - // The algo follows the pool. Note pool.dragonx.cc's stratum host is us.dragonx.cc - // (the .cc domain is only the Cloudflare-proxied web/API host). - EXPECT_EQ(resolvePoolAlgo("us.dragonx.cc:3333", "rx/hush"), std::string("rx/dragonx")); + // The algo follows the pool: pool.dragonx.is resolves to rx/hush regardless of the caller default. EXPECT_EQ(resolvePoolAlgo("pool.dragonx.is:3433", "rx/dragonx"), std::string("rx/hush")); - // Bare host (no port) still matches; scheme + path are tolerated. - EXPECT_EQ(resolvePoolAlgo("us.dragonx.cc", "rx/hush"), std::string("rx/dragonx")); - EXPECT_EQ(resolvePoolAlgo("stratum+tcp://us.dragonx.cc:3333/x", "rx/hush"), - std::string("rx/dragonx")); - // Unknown host -> fallback algo. + // The former us.dragonx.cc (pool.dragonx.cc) host is now unknown -> caller's fallback algo. + EXPECT_EQ(resolvePoolAlgo("us.dragonx.cc:3333", "rx/hush"), std::string("rx/hush")); EXPECT_EQ(resolvePoolAlgo("my.pool.example:1234", "rx/hush"), std::string("rx/hush")); - EXPECT_TRUE(findKnownPoolByUrl("us.dragonx.cc:3333") != nullptr); + EXPECT_TRUE(findKnownPoolByUrl("pool.dragonx.is:3433") != nullptr); + EXPECT_TRUE(findKnownPoolByUrl("us.dragonx.cc:3333") == nullptr); // removed built-in default EXPECT_TRUE(findKnownPoolByUrl("unknown.host:1") == nullptr); // A mismatched explicit port must NOT match a known pool. - EXPECT_TRUE(findKnownPoolByUrl("us.dragonx.cc:9999") == nullptr); + EXPECT_TRUE(findKnownPoolByUrl("pool.dragonx.is:9999") == nullptr); } // Schema-aware pool hashrate parsing (the two pools speak different APIs).