diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index 8eee47e..b21ab6e 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -144,7 +144,6 @@ struct SettingsPageState { std::set debug_categories; bool debug_cats_dirty = false; bool debug_expanded = false; - bool node_advanced_expanded = true; // Node & Security: rare/destructive daemon actions (shown by default) bool effects_expanded = false; bool tools_expanded = false; bool confirm_clear_ztx = false; @@ -2024,8 +2023,12 @@ void RenderSettingsPage(App* app) { // gets whatever width is left after the data-dir label, wallet-size // label+value, and the trailing button. ImFont* nodeBtnFont = S.resolveFont("button"); - float openBtnW = ImGui::CalcTextSize(TR("settings_open_data_dir")).x + - ImGui::GetStyle().FramePadding.x * 2.0f + btnPad; + if (!nodeBtnFont) nodeBtnFont = Type().button(); + // Measure with the BUTTON font (not the current font) so the width matches + // the real TactileButton size — the old estimate mis-measured and the button + // overflowed the card's right edge, worse at high DPI. + float openBtnW = nodeBtnFont->CalcTextSizeA(nodeBtnFont->LegacySize, FLT_MAX, 0, + TR("settings_open_data_dir")).x + ImGui::GetStyle().FramePadding.x * 2.0f; float dataDirLabelW = ImGui::CalcTextSize(TR("settings_data_dir")).x; float sizeLabelW = ImGui::CalcTextSize(TR("settings_wallet_size_label")).x; float sizeValueW = ImGui::CalcTextSize(size_str.c_str()).x; @@ -2084,18 +2087,17 @@ void RenderSettingsPage(App* app) { else ImGui::TextDisabled("%s", TR("settings_not_found")); - // Open-data-folder button: right-aligned when there is room, else trailing — - // kept on the data-dir row (rowTopY), not the next line. + // Open-data-folder button: right-aligned on the data-dir row (rowTopY), with + // an EXPLICIT width (openBtnW) so its right edge lands exactly on the card + // edge and never overflows, regardless of DPI / font-scale timing. float rowY = rowTopY; float rightEdge = sectionOrigin.x + contentW; - float actualOpenW = ImGui::CalcTextSize(TR("settings_open_data_dir")).x + - ImGui::GetStyle().FramePadding.x * 2.0f; - float openX = rightEdge - actualOpenW; + float openX = rightEdge - openBtnW; float afterSizeX = ImGui::GetItemRectMax().x + spMd; ImGui::SameLine(0, spMd); if (openX > afterSizeX) ImGui::SetCursorScreenPos(ImVec2(openX, rowY)); - if (TactileButton(TR("settings_open_data_dir"), ImVec2(0, 0), nodeBtnFont)) { + if (TactileButton(TR("settings_open_data_dir"), ImVec2(openBtnW, 0), nodeBtnFont)) { util::Platform::openFolder(dirPath); } if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_open_data_dir")); @@ -2250,8 +2252,10 @@ void RenderSettingsPage(App* app) { if (ui::DaemonUpdateDialog::consumeInstalled()) s_settingsState.daemon_info_loaded = false; - // Common action row: Check for updates | Refresh | Test connection. + // Action row: Check for updates | Refresh | Test connection 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; if (TactileButton(TR("daemon_update_check"), ImVec2(0, 0), dbBtnFont)) { ui::DaemonUpdateDialog::show(app, inst.exists ? inst.version : std::string()); } @@ -2281,10 +2285,11 @@ void RenderSettingsPage(App* app) { if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_test_conn")); ImGui::EndDisabled(); - // The FOUR destructive maintenance actions, in one horizontal row prefixed - // by a visible "Advanced" toggle. Every BeginDisabled/EndDisabled predicate - // is preserved exactly. - auto renderDaemonAdvancedButtons = [&]() { + // 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; @@ -2314,40 +2319,28 @@ void RenderSettingsPage(App* app) { if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_repair_wallet")); ImGui::EndDisabled(); }; - - // Advanced — a clearly-clickable toggle (label + arrow) on its own line, - // controlling the rare + destructive daemon maintenance actions. When - // expanded the four buttons render on one horizontal row: inline with the - // toggle when there is room, otherwise wrapped to the next full-width row. - ImGui::Dummy(ImVec2(0, Layout::spacingSm())); { - char advBuf[64]; - const char* advArrow = s_settingsState.node_advanced_expanded - ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE; - snprintf(advBuf, sizeof(advBuf), "%s %s##NodeAdvancedToggle", - TR("advanced"), advArrow); - // Width of the four Advanced buttons + gaps, to decide inline vs. wrap. - float advBtnsW = spMd * 3; - const char* advLabels[] = { TR("daemon_install_bundled"), TR("rescan"), - TR("delete_blockchain"), TR("repair_wallet") }; - for (const char* l : advLabels) - advBtnsW += ImGui::CalcTextSize(l).x + ImGui::GetStyle().FramePadding.x * 2.0f; - float advToggleW = ImGui::CalcTextSize(advBuf).x + - ImGui::GetStyle().FramePadding.x * 2.0f; - - if (TactileButton(advBuf, ImVec2(0, 0), dbBtnFont)) { - s_settingsState.node_advanced_expanded = !s_settingsState.node_advanced_expanded; - } - if (s_settingsState.node_advanced_expanded) { - const bool inline_ = (advToggleW + spMd + advBtnsW) <= contentW; - if (inline_) { - ImGui::SameLine(0, spMd); - } else { - ImGui::Dummy(ImVec2(0, Layout::spacingXs())); - ImGui::SetCursorScreenPos(ImVec2(sectionOrigin.x, ImGui::GetCursorScreenPos().y)); - } - renderDaemonAdvancedButtons(); + // Group width (button font) to right-align the four maintenance actions. + const char* mLabels[] = { TR("daemon_install_bundled"), 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 rightEdge = sectionOrigin.x + contentW; + float groupX = rightEdge - groupW; + if (groupX >= afterTestX) { + // Fits on the Test-connection row, right-aligned. + ImGui::SameLine(0, 0); + ImGui::SetCursorScreenPos(ImVec2(groupX, daemonBtnRowY)); + } else { + // Too narrow: wrap to the next row, right-aligned (clamped to left edge). + ImGui::Dummy(ImVec2(0, Layout::spacingXs())); + float wrapX = std::max(sectionOrigin.x, rightEdge - groupW); + ImGui::SetCursorScreenPos(ImVec2(wrapX, ImGui::GetCursorScreenPos().y)); } + renderDaemonMaintenanceButtons(); } }