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,10 +2260,24 @@ 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->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_daemon_install_bundled"));
ImGui::EndDisabled();
// 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->isConnected()); ImGui::BeginDisabled(!app->isConnected());
if (TactileButton(TR("test_connection"), ImVec2(0, 0), dbBtnFont)) { if (TactileButton(TR("test_connection"), ImVec2(0, 0), dbBtnFont)) {
if (app->rpc() && app->rpc()->isConnected() && app->worker()) { if (app->rpc() && app->rpc()->isConnected() && app->worker()) {
@@ -2283,18 +2297,6 @@ void RenderSettingsPage(App* app) {
} }
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_test_conn")); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_test_conn"));
ImGui::EndDisabled(); ImGui::EndDisabled();
// The four maintenance actions (Install bundled / Rescan / Delete blockchain
// / Repair wallet), right-aligned on the SAME row as Test connection — 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;
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_install_bundled"));
ImGui::EndDisabled();
ImGui::SameLine(0, spMd); ImGui::SameLine(0, spMd);
ImGui::BeginDisabled(!app->isConnected()); ImGui::BeginDisabled(!app->isConnected());
if (TactileButton(TR("rescan"), ImVec2(0, 0), dbBtnFont)) { if (TactileButton(TR("rescan"), ImVec2(0, 0), dbBtnFont)) {
@@ -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 {