diff --git a/res/themes/ui.toml b/res/themes/ui.toml index da9a034..b8412c0 100644 --- a/res/themes/ui.toml +++ b/res/themes/ui.toml @@ -1327,7 +1327,9 @@ security-combo-width = { size = 120.0 } port-input-min-width = { size = 60.0 } port-input-width-ratio = { size = 0.4 } idle-combo-width = { size = 64.0 } -about-logo-size = { size = 64.0 } +# Reserved height basis for the About-card logo; the logo is drawn scaled to the +# card's actual height (aspect-preserved) and capped to this * aspect in width. +about-logo-size = { size = 150.0 } [components.main-layout] app-bar-height = { size = 64.0 } diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index 14a83b4..c33a8da 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -144,6 +144,7 @@ struct SettingsPageState { std::set debug_categories; bool debug_cats_dirty = false; bool debug_expanded = false; + bool node_advanced_expanded = false; // Node & Security: rare/destructive daemon actions bool effects_expanded = false; bool tools_expanded = false; bool confirm_clear_ztx = false; @@ -1521,7 +1522,10 @@ void RenderSettingsPage(App* app) { ImGui::PushClipRect(ImVec2(leftX, sectionOrigin.y), ImVec2(leftX + leftColW, sectionOrigin.y + 9999), true); - Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("node")); + // Lite has no daemon — this column holds wallet lifecycle/encryption, so label + // it "WALLET" rather than "NODE". + Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), + app->isLiteBuild() ? TR("wallet") : TR("node")); ImGui::Dummy(ImVec2(0, Layout::spacingXs())); { @@ -2029,7 +2033,10 @@ void RenderSettingsPage(App* app) { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); { - // Encrypt / Change passphrase / Lock buttons + // Encrypt / Change passphrase / Lock / Remove — RPC-backed, so full-node + // only. In lite the wallet's encryption controls live in the NODE column + // above (via the lite backend), so this duplicate is hidden. + if (!app->isLiteBuild()) { bool isEncrypted = app->state().isEncrypted(); bool isLocked = app->state().isLocked(); @@ -2072,6 +2079,7 @@ void RenderSettingsPage(App* app) { } ImGui::Dummy(ImVec2(0, Layout::spacingXs())); + } // !isLiteBuild — RPC-backed encrypt/change/lock/remove block // Auto-Lock timeout { @@ -2223,17 +2231,8 @@ void RenderSettingsPage(App* app) { if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_daemon_update_check")); ImGui::Dummy(ImVec2(0, Layout::spacingXs())); - // All node actions on one full-width toolbar row: daemon-binary actions first - // (Install bundled | Refresh), then maintenance (Test connection | Rescan | - // Delete blockchain | Repair wallet). + // Common actions, always visible: Refresh | Test connection. 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()); if (TactileButton(TR("refresh"), ImVec2(0, 0), dbBtnFont)) { s_settingsState.daemon_info_loaded = false; } @@ -2256,27 +2255,61 @@ void RenderSettingsPage(App* app) { } } if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_test_conn")); - ImGui::SameLine(0, Layout::spacingMd()); - 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; + + // 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(contentW, 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 + contentW - arrowW, textY), OnSurfaceMedium(), advArrow); } - 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 (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(); } - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) material::Tooltip("%s", TR("tt_repair_wallet")); - ImGui::EndDisabled(); } } @@ -2373,19 +2406,17 @@ void RenderSettingsPage(App* app) { ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad)); ImGui::Indent(pad); - // Logo on the left side of the about card + // Logo on the left side of the about card. Deferred: reserve horizontal space + // now, but draw the image after the card's final height is known so it scales to + // the full card height (no empty space below it). ImTextureID logoTex = app->getLogoTexture(); float logoAreaW = 0; + ImVec2 logoPos = ImGui::GetCursorScreenPos(); + float logoAspect = (app->getLogoHeight() > 0) + ? (float)app->getLogoWidth() / (float)app->getLogoHeight() : 1.0f; + float logoReserveH = schema::UI().drawElement("components.settings-page", "about-logo-size").sizeOr(150.0f); if (logoTex != 0) { - float logoMaxH = schema::UI().drawElement("components.settings-page", "about-logo-size").sizeOr(64.0f); - float logoH = logoMaxH; - float aspect = (app->getLogoHeight() > 0) ? (float)app->getLogoWidth() / (float)app->getLogoHeight() : 1.0f; - float logoW = logoH * aspect; - ImVec2 logoPos = ImGui::GetCursorScreenPos(); - dl->AddImage(logoTex, - ImVec2(logoPos.x, logoPos.y), - ImVec2(logoPos.x + logoW, logoPos.y + logoH)); - logoAreaW = logoW + Layout::spacingLg(); + logoAreaW = logoReserveH * logoAspect + Layout::spacingLg(); ImGui::Indent(logoAreaW); } @@ -2471,6 +2502,18 @@ void RenderSettingsPage(App* app) { ImGui::Unindent(pad); ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y); + + // Draw the logo now that the card height is known — scaled to the card height + // (inset by padding), aspect-preserved, and capped to the reserved width so it + // never overlaps the text. Still on the content channel (1), above the glass. + if (logoTex != 0) { + float reserveW = logoReserveH * logoAspect; + float logoH = std::max(16.0f, (cardMax.y - logoPos.y) - bottomPad); + float logoW = logoH * logoAspect; + if (logoW > reserveW) { logoW = reserveW; logoH = (logoAspect > 0.0f) ? logoW / logoAspect : logoH; } + dl->AddImage(logoTex, logoPos, ImVec2(logoPos.x + logoW, logoPos.y + logoH)); + } + dl->ChannelsSetCurrent(0); DrawGlassPanel(dl, cardMin, cardMax, glassSpec); dl->ChannelsMerge(); @@ -2482,9 +2525,10 @@ void RenderSettingsPage(App* app) { ImGui::Dummy(ImVec2(0, gap)); // ==================================================================== - // DEBUG LOGGING — collapsible card with glass styling + // DEBUG LOGGING — collapsible card (full-node only: these are dragonxd + // daemon debug= categories written to DRAGONX.conf; lite has no daemon) // ==================================================================== - { + if (app->supportsFullNodeLifecycleActions()) { // Clickable header row ImVec2 headerPos = ImGui::GetCursorScreenPos(); const char* arrow = s_settingsState.debug_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;