From e3b53258b7dda8b95fcd0e62d32e69e7d3d22b56 Mon Sep 17 00:00:00 2001 From: DanS Date: Thu, 2 Jul 2026 03:09:43 -0500 Subject: [PATCH] fix(settings): swap Install bundled/Test connection; unique daemon Refresh ID Swap the "Install bundled" and "Test connection" daemon-binary buttons: Install bundled now sits in the left common row (Check for updates | Refresh | Install bundled); Test connection leads the right-aligned maintenance group. Each button keeps its own disabled-predicates, action, and tooltip. Give the daemon-binary Refresh button a unique ImGui ID ("Refresh##daemonRefresh") so it no longer collides with the theme-section Refresh buttons, which triggered Dear ImGui's "2 visible items with conflicting ID!" error. The label still displays "Refresh". Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/pages/settings_page.cpp | 58 ++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index 7fb4846..0a259a2 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -2251,7 +2251,7 @@ void RenderSettingsPage(App* app) { if (ui::DaemonUpdateDialog::consumeInstalled()) s_settingsState.daemon_info_loaded = false; - // Action row: Check for updates | Refresh | Test connection on the left, + // Action row: Check for updates | Refresh | Install bundled on the left, // with the four maintenance actions right-aligned on the same row. ImGui::Dummy(ImVec2(0, Layout::spacingXs())); float daemonBtnRowY = ImGui::GetCursorScreenPos().y; @@ -2260,40 +2260,42 @@ void RenderSettingsPage(App* app) { } if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_update_check")); ImGui::SameLine(0, spMd); - if (TactileButton(TR("refresh"), ImVec2(0, 0), dbBtnFont)) { + // Unique ImGui ID (label still displays "Refresh") — the theme sections + // also have "Refresh" buttons, so a bare label collides on this page. + if (TactileButton((std::string(TR("refresh")) + "##daemonRefresh").c_str(), ImVec2(0, 0), dbBtnFont)) { s_settingsState.daemon_info_loaded = false; } ImGui::SameLine(0, spMd); - ImGui::BeginDisabled(!app->isConnected()); - if (TactileButton(TR("test_connection"), ImVec2(0, 0), dbBtnFont)) { - if (app->rpc() && app->rpc()->isConnected() && app->worker()) { - app->worker()->post([rpc = app->rpc()]() -> rpc::RPCWorker::MainCb { - try { - rpc::RPCClient::TraceScope trace("Settings / Test connection"); - rpc->call("getinfo"); - return []() { Notifications::instance().success("RPC connection OK"); }; - } catch (const std::exception& e) { - std::string err = e.what(); - return [err]() { Notifications::instance().error("RPC error: " + err); }; - } - }); - } else { - Notifications::instance().warning("Not connected to daemon"); - } + ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon() || !bun.available); + if (TactileButton(TR("daemon_install_bundled"), ImVec2(0, 0), dbBtnFont)) { + s_settingsState.confirm_reinstall_daemon = true; } - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_test_conn")); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_install_bundled")); ImGui::EndDisabled(); - // The four maintenance actions (Install bundled / Rescan / Delete blockchain - // / Repair wallet), right-aligned on the SAME row as Test connection — no + // The four maintenance actions (Test connection / Rescan / Delete blockchain + // / Repair wallet), right-aligned on the SAME row as Install bundled — no // separate "Advanced" toggle. Every BeginDisabled/EndDisabled predicate is // preserved exactly. Wraps to a right-aligned next row when too narrow. auto renderDaemonMaintenanceButtons = [&]() { - ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon() || !bun.available); - if (TactileButton(TR("daemon_install_bundled"), ImVec2(0, 0), dbBtnFont)) { - s_settingsState.confirm_reinstall_daemon = true; + ImGui::BeginDisabled(!app->isConnected()); + if (TactileButton(TR("test_connection"), ImVec2(0, 0), dbBtnFont)) { + if (app->rpc() && app->rpc()->isConnected() && app->worker()) { + app->worker()->post([rpc = app->rpc()]() -> rpc::RPCWorker::MainCb { + try { + rpc::RPCClient::TraceScope trace("Settings / Test connection"); + rpc->call("getinfo"); + return []() { Notifications::instance().success("RPC connection OK"); }; + } catch (const std::exception& e) { + std::string err = e.what(); + return [err]() { Notifications::instance().error("RPC error: " + err); }; + } + }); + } else { + Notifications::instance().warning("Not connected to daemon"); + } } - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_install_bundled")); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_test_conn")); ImGui::EndDisabled(); ImGui::SameLine(0, spMd); ImGui::BeginDisabled(!app->isConnected()); @@ -2320,17 +2322,17 @@ void RenderSettingsPage(App* app) { }; { // Group width (button font) to right-align the four maintenance actions. - const char* mLabels[] = { TR("daemon_install_bundled"), TR("rescan"), + const char* mLabels[] = { TR("test_connection"), TR("rescan"), TR("delete_blockchain"), TR("repair_wallet") }; float groupW = spMd * 3.0f; for (const char* l : mLabels) groupW += dbBtnFont->CalcTextSizeA(dbBtnFont->LegacySize, FLT_MAX, 0, l).x + ImGui::GetStyle().FramePadding.x * 2.0f; - float afterTestX = ImGui::GetItemRectMax().x + spMd; // right edge of Test connection + float afterTestX = ImGui::GetItemRectMax().x + spMd; // right edge of Install bundled float rightEdge = sectionOrigin.x + contentW; float groupX = rightEdge - groupW; if (groupX >= afterTestX) { - // Fits on the Test-connection row, right-aligned. + // Fits on the Install-bundled row, right-aligned. ImGui::SameLine(0, 0); ImGui::SetCursorScreenPos(ImVec2(groupX, daemonBtnRowY)); } else {