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) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 03:09:43 -05:00
parent f9e4db4abf
commit e3b53258b7

View File

@@ -2251,7 +2251,7 @@ void RenderSettingsPage(App* app) {
if (ui::DaemonUpdateDialog::consumeInstalled()) if (ui::DaemonUpdateDialog::consumeInstalled())
s_settingsState.daemon_info_loaded = false; 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. // with the four maintenance actions right-aligned on the same row.
ImGui::Dummy(ImVec2(0, Layout::spacingXs())); ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
float daemonBtnRowY = ImGui::GetCursorScreenPos().y; 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")); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_update_check"));
ImGui::SameLine(0, spMd); 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; s_settingsState.daemon_info_loaded = false;
} }
ImGui::SameLine(0, spMd); ImGui::SameLine(0, spMd);
ImGui::BeginDisabled(!app->isConnected()); ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon() || !bun.available);
if (TactileButton(TR("test_connection"), ImVec2(0, 0), dbBtnFont)) { if (TactileButton(TR("daemon_install_bundled"), ImVec2(0, 0), dbBtnFont)) {
if (app->rpc() && app->rpc()->isConnected() && app->worker()) { s_settingsState.confirm_reinstall_daemon = true;
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_test_conn")); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_install_bundled"));
ImGui::EndDisabled(); ImGui::EndDisabled();
// The four maintenance actions (Install bundled / Rescan / Delete blockchain // The four maintenance actions (Test connection / Rescan / Delete blockchain
// / Repair wallet), right-aligned on the SAME row as Test connection — no // / Repair wallet), right-aligned on the SAME row as Install bundled — no
// separate "Advanced" toggle. Every BeginDisabled/EndDisabled predicate is // separate "Advanced" toggle. Every BeginDisabled/EndDisabled predicate is
// preserved exactly. Wraps to a right-aligned next row when too narrow. // preserved exactly. Wraps to a right-aligned next row when too narrow.
auto renderDaemonMaintenanceButtons = [&]() { auto renderDaemonMaintenanceButtons = [&]() {
ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon() || !bun.available); ImGui::BeginDisabled(!app->isConnected());
if (TactileButton(TR("daemon_install_bundled"), ImVec2(0, 0), dbBtnFont)) { if (TactileButton(TR("test_connection"), ImVec2(0, 0), dbBtnFont)) {
s_settingsState.confirm_reinstall_daemon = true; 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::EndDisabled();
ImGui::SameLine(0, spMd); ImGui::SameLine(0, spMd);
ImGui::BeginDisabled(!app->isConnected()); ImGui::BeginDisabled(!app->isConnected());
@@ -2320,17 +2322,17 @@ void RenderSettingsPage(App* app) {
}; };
{ {
// Group width (button font) to right-align the four maintenance actions. // 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") }; TR("delete_blockchain"), TR("repair_wallet") };
float groupW = spMd * 3.0f; float groupW = spMd * 3.0f;
for (const char* l : mLabels) for (const char* l : mLabels)
groupW += dbBtnFont->CalcTextSizeA(dbBtnFont->LegacySize, FLT_MAX, 0, l).x groupW += dbBtnFont->CalcTextSizeA(dbBtnFont->LegacySize, FLT_MAX, 0, l).x
+ ImGui::GetStyle().FramePadding.x * 2.0f; + 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 rightEdge = sectionOrigin.x + contentW;
float groupX = rightEdge - groupW; float groupX = rightEdge - groupW;
if (groupX >= afterTestX) { if (groupX >= afterTestX) {
// Fits on the Test-connection row, right-aligned. // Fits on the Install-bundled row, right-aligned.
ImGui::SameLine(0, 0); ImGui::SameLine(0, 0);
ImGui::SetCursorScreenPos(ImVec2(groupX, daemonBtnRowY)); ImGui::SetCursorScreenPos(ImVec2(groupX, daemonBtnRowY));
} else { } else {