diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index 5f42efe..094ad6e 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -1515,10 +1515,10 @@ void RenderSettingsPage(App* app) { // grid especially), so stack them into one full-width column instead. float stackWidth = S.drawElement("components.settings-page", "node-sec-stack-width").sizeOr(600.0f); bool stacked = contentW < stackWidth; - // 70/30 when side by side: NODE holds far more (data dir + RPC grid) than the - // short SECURITY column, so give it the width and shrink the right-side gap. - float leftColW = stacked ? contentW : (contentW - colGap) * 0.7f; - float rightColW = stacked ? contentW : (contentW - colGap) * 0.3f; + // Even 50/50 columns so the Daemon Binary section (masonry-placed into the + // shorter column below) has enough width, and Node/RPC vs Security balance. + float leftColW = stacked ? contentW : (contentW - colGap) * 0.5f; + float rightColW = stacked ? contentW : (contentW - colGap) * 0.5f; ImVec2 sectionOrigin = ImGui::GetCursorScreenPos(); float leftX = sectionOrigin.x; float rightX = stacked ? sectionOrigin.x : (sectionOrigin.x + leftColW + colGap); @@ -2148,68 +2148,24 @@ void RenderSettingsPage(App* app) { } } - // Advanced — rare/destructive daemon actions (full-node only), placed in the - // right column below Security so that space isn't left empty. Buttons stack - // vertically to fit the narrow column. - if (app->supportsFullNodeLifecycleActions()) { - ImGui::Dummy(ImVec2(0, Layout::spacingSm())); - ImGui::SetCursorScreenPos(ImVec2(rightX, ImGui::GetCursorScreenPos().y)); - ImVec2 advPos = ImGui::GetCursorScreenPos(); - const char* advArrow = s_settingsState.node_advanced_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f)); - if (ImGui::Button("##NodeAdvancedToggle", ImVec2(rightColW, ImGui::GetFrameHeight()))) - s_settingsState.node_advanced_expanded = !s_settingsState.node_advanced_expanded; - ImGui::PopStyleColor(3); - ImFont* ovA = Type().overline(); - float advTextY = advPos.y + (ImGui::GetFrameHeight() - ovA->LegacySize) * 0.5f; - dl->AddText(ovA, ovA->LegacySize, ImVec2(advPos.x, advTextY), OnSurfaceMedium(), TR("advanced")); - ImFont* icoA = Type().iconSmall(); if (!icoA) icoA = ovA; - float arrowW = icoA->CalcTextSizeA(icoA->LegacySize, FLT_MAX, 0, advArrow).x; - dl->AddText(icoA, icoA->LegacySize, ImVec2(advPos.x + rightColW - arrowW, advTextY), OnSurfaceMedium(), advArrow); - - if (s_settingsState.node_advanced_expanded) { - ImFont* advBtnFont = S.resolveFont("button"); - auto advRow = [&]() { ImGui::SetCursorScreenPos(ImVec2(rightX, ImGui::GetCursorScreenPos().y)); }; - ImGui::Dummy(ImVec2(0, Layout::spacingXs())); - advRow(); - ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon() || !s_settingsState.bundled_daemon.available); - if (TactileButton(TR("daemon_install_bundled"), ImVec2(rightColW, 0), advBtnFont)) - s_settingsState.confirm_reinstall_daemon = true; - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_install_bundled")); - ImGui::EndDisabled(); - advRow(); - ImGui::BeginDisabled(!app->isConnected()); - if (TactileButton(TR("rescan"), ImVec2(rightColW, 0), advBtnFont)) { - s_settingsState.confirm_rescan = true; - s_settingsState.rescan_height_detecting = false; - s_settingsState.rescan_height_detected = false; - } - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_rescan")); - ImGui::EndDisabled(); - advRow(); - ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon()); - if (TactileButton(TR("delete_blockchain"), ImVec2(rightColW, 0), advBtnFont)) - s_settingsState.confirm_delete_blockchain = true; - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_delete_blockchain")); - ImGui::EndDisabled(); - advRow(); - if (TactileButton(TR("repair_wallet"), ImVec2(rightColW, 0), advBtnFont)) - s_settingsState.confirm_repair_wallet = true; - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_repair_wallet")); - } - } - float rightBottom = ImGui::GetCursorScreenPos().y; ImGui::PopClipRect(); - // Advance cursor past both columns - float maxBottom = std::max(leftBottom, rightBottom); - ImGui::SetCursorScreenPos(ImVec2(sectionOrigin.x, maxBottom)); - - // ---- DAEMON BINARY — full-width row beneath the NODE + SECURITY columns ---- + // ---- DAEMON BINARY — masonry-placed into the shorter column ---- + // Node/RPC (left) is taller than Security (right), so Daemon Binary drops under + // Security, filling the empty bottom-right and balancing the two columns. When + // the columns are stacked (narrow), it spans full width below both. if (app->supportsFullNodeLifecycleActions()) { + const bool dbFull = stacked; + const bool dbLeftCol = leftBottom <= rightBottom; + const float dbColW = dbFull ? contentW : (dbLeftCol ? leftColW : rightColW); + const float dbTopY = dbFull ? std::max(leftBottom, rightBottom) + : (dbLeftCol ? leftBottom : rightBottom); + const float dbLeftX = dbFull ? sectionOrigin.x : (dbLeftCol ? leftX : rightX); + ImGui::SetCursorScreenPos(ImVec2(dbLeftX, dbTopY)); + ImGui::PushClipRect(ImVec2(dbLeftX, dbTopY), + ImVec2(dbLeftX + dbColW, dbTopY + 9999.0f), true); + ImFont* dbBtnFont = S.resolveFont("button"); if (!s_settingsState.daemon_info_loaded) { s_settingsState.installed_daemon = dragonx::resources::getInstalledDaemonInfo(); @@ -2233,7 +2189,6 @@ void RenderSettingsPage(App* app) { return std::string(buf); }; - const float dbLeftX = sectionOrigin.x; const ImVec4 dbDim = ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()); ImGui::Dummy(ImVec2(0, Layout::spacingLg())); @@ -2243,7 +2198,7 @@ void RenderSettingsPage(App* app) { // Installed | Bundled side by side across the full container width. const float dbStartY = ImGui::GetCursorScreenPos().y; const float dbLineH = ImGui::GetTextLineHeightWithSpacing(); - const float dbCol2X = dbLeftX + std::min(contentW * 0.4f, 340.0f); + const float dbCol2X = dbLeftX + std::min(dbColW * 0.4f, 340.0f); ImGui::SetCursorScreenPos(ImVec2(dbLeftX, dbStartY)); Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("daemon_installed")); @@ -2317,9 +2272,70 @@ void RenderSettingsPage(App* app) { } if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_test_conn")); ImGui::EndDisabled(); - // (The "Advanced" destructive-actions expander now lives in the Security - // column above, filling what used to be empty space.) + + // Advanced — rare + destructive daemon actions behind a collapsible header + // (reuses the DEBUG LOGGING expander idiom). + ImGui::Dummy(ImVec2(0, Layout::spacingSm())); + { + ImVec2 advPos = ImGui::GetCursorScreenPos(); + const char* advArrow = s_settingsState.node_advanced_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE; + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f)); + if (ImGui::Button("##NodeAdvancedToggle", ImVec2(dbColW, ImGui::GetFrameHeight()))) { + s_settingsState.node_advanced_expanded = !s_settingsState.node_advanced_expanded; + } + ImGui::PopStyleColor(3); + ImFont* ovFont = Type().overline(); + float textY = advPos.y + (ImGui::GetFrameHeight() - ovFont->LegacySize) * 0.5f; + dl->AddText(ovFont, ovFont->LegacySize, ImVec2(advPos.x, textY), OnSurfaceMedium(), TR("advanced")); + ImFont* iconFont = Type().iconSmall(); + if (!iconFont) iconFont = ovFont; + float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, advArrow).x; + dl->AddText(iconFont, iconFont->LegacySize, ImVec2(advPos.x + dbColW - arrowW, textY), OnSurfaceMedium(), advArrow); + } + if (s_settingsState.node_advanced_expanded) { + ImGui::Dummy(ImVec2(0, Layout::spacingXs())); + ImGui::SetCursorScreenPos(ImVec2(dbLeftX, ImGui::GetCursorScreenPos().y)); + 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, Layout::spacingMd()); + ImGui::BeginDisabled(!app->isConnected()); + if (TactileButton(TR("rescan"), ImVec2(0, 0), dbBtnFont)) { + s_settingsState.confirm_rescan = true; + // Re-probe the available block range each time the dialog is opened. + s_settingsState.rescan_height_detecting = false; + s_settingsState.rescan_height_detected = false; + } + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_rescan")); + ImGui::EndDisabled(); + ImGui::SameLine(0, Layout::spacingMd()); + ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon()); + if (TactileButton(TR("delete_blockchain"), ImVec2(0, 0), dbBtnFont)) { + s_settingsState.confirm_delete_blockchain = true; + } + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_delete_blockchain")); + ImGui::SameLine(0, Layout::spacingMd()); + if (TactileButton(TR("repair_wallet"), ImVec2(0, 0), dbBtnFont)) { + s_settingsState.confirm_repair_wallet = true; + } + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_repair_wallet")); + ImGui::EndDisabled(); + } + + float dbBottom = ImGui::GetCursorScreenPos().y; + ImGui::PopClipRect(); + if (dbFull || dbLeftCol) leftBottom = std::max(leftBottom, dbBottom); + if (dbFull || !dbLeftCol) rightBottom = std::max(rightBottom, dbBottom); } + + // Advance the card cursor past the taller column. + float maxBottom = std::max(leftBottom, rightBottom); + ImGui::SetCursorScreenPos(ImVec2(sectionOrigin.x, maxBottom)); } ImGui::Dummy(ImVec2(0, bottomPad));