feat(settings): About logo scales to card height; Node&Security Advanced expander; lite gating
- About: draw the logo deferred, scaled to the card's full height (aspect-preserved, capped to the reserved width) so there's no empty space below it. - Node & Security: split the daemon toolbar — Refresh + Test connection stay visible; the rare/destructive actions (Install bundled, Rescan, Delete blockchain, Repair wallet) move behind a collapsible "Advanced" header (reusing the DEBUG LOGGING idiom). - Lite gating: hide the DEBUG LOGGING card (dragonxd debug= categories, no daemon in lite) and the RPC-backed encrypt/change/lock/remove block (lite has its own encryption in the NODE column) behind full-node guards; label the lite left column "WALLET" not "NODE". (Auto-lock + PIN remain in both.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1327,7 +1327,9 @@ security-combo-width = { size = 120.0 }
|
|||||||
port-input-min-width = { size = 60.0 }
|
port-input-min-width = { size = 60.0 }
|
||||||
port-input-width-ratio = { size = 0.4 }
|
port-input-width-ratio = { size = 0.4 }
|
||||||
idle-combo-width = { size = 64.0 }
|
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]
|
[components.main-layout]
|
||||||
app-bar-height = { size = 64.0 }
|
app-bar-height = { size = 64.0 }
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ struct SettingsPageState {
|
|||||||
std::set<std::string> debug_categories;
|
std::set<std::string> debug_categories;
|
||||||
bool debug_cats_dirty = false;
|
bool debug_cats_dirty = false;
|
||||||
bool debug_expanded = false;
|
bool debug_expanded = false;
|
||||||
|
bool node_advanced_expanded = false; // Node & Security: rare/destructive daemon actions
|
||||||
bool effects_expanded = false;
|
bool effects_expanded = false;
|
||||||
bool tools_expanded = false;
|
bool tools_expanded = false;
|
||||||
bool confirm_clear_ztx = false;
|
bool confirm_clear_ztx = false;
|
||||||
@@ -1521,7 +1522,10 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImGui::PushClipRect(ImVec2(leftX, sectionOrigin.y),
|
ImGui::PushClipRect(ImVec2(leftX, sectionOrigin.y),
|
||||||
ImVec2(leftX + leftColW, sectionOrigin.y + 9999), true);
|
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()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -2029,7 +2033,10 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
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 isEncrypted = app->state().isEncrypted();
|
||||||
bool isLocked = app->state().isLocked();
|
bool isLocked = app->state().isLocked();
|
||||||
|
|
||||||
@@ -2072,6 +2079,7 @@ void RenderSettingsPage(App* app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
|
} // !isLiteBuild — RPC-backed encrypt/change/lock/remove block
|
||||||
|
|
||||||
// Auto-Lock timeout
|
// Auto-Lock timeout
|
||||||
{
|
{
|
||||||
@@ -2223,17 +2231,8 @@ 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::Dummy(ImVec2(0, Layout::spacingXs()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
// All node actions on one full-width toolbar row: daemon-binary actions first
|
// Common actions, always visible: Refresh | Test connection.
|
||||||
// (Install bundled | Refresh), then maintenance (Test connection | Rescan |
|
|
||||||
// Delete blockchain | Repair wallet).
|
|
||||||
ImGui::SetCursorScreenPos(ImVec2(dbLeftX, ImGui::GetCursorScreenPos().y));
|
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)) {
|
if (TactileButton(TR("refresh"), ImVec2(0, 0), dbBtnFont)) {
|
||||||
s_settingsState.daemon_info_loaded = false;
|
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"));
|
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::EndDisabled();
|
||||||
ImGui::SameLine(0, Layout::spacingMd());
|
|
||||||
ImGui::BeginDisabled(!app->isUsingEmbeddedDaemon());
|
// Advanced — rare + destructive daemon actions behind a collapsible header
|
||||||
if (TactileButton(TR("delete_blockchain"), ImVec2(0, 0), dbBtnFont)) {
|
// (reuses the DEBUG LOGGING expander idiom).
|
||||||
s_settingsState.confirm_delete_blockchain = true;
|
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"));
|
if (s_settingsState.node_advanced_expanded) {
|
||||||
ImGui::SameLine(0, Layout::spacingMd());
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
if (TactileButton(TR("repair_wallet"), ImVec2(0, 0), dbBtnFont)) {
|
ImGui::SetCursorScreenPos(ImVec2(dbLeftX, ImGui::GetCursorScreenPos().y));
|
||||||
s_settingsState.confirm_repair_wallet = true;
|
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::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
|
||||||
ImGui::Indent(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();
|
ImTextureID logoTex = app->getLogoTexture();
|
||||||
float logoAreaW = 0;
|
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) {
|
if (logoTex != 0) {
|
||||||
float logoMaxH = schema::UI().drawElement("components.settings-page", "about-logo-size").sizeOr(64.0f);
|
logoAreaW = logoReserveH * logoAspect + Layout::spacingLg();
|
||||||
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();
|
|
||||||
ImGui::Indent(logoAreaW);
|
ImGui::Indent(logoAreaW);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2471,6 +2502,18 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImGui::Unindent(pad);
|
ImGui::Unindent(pad);
|
||||||
|
|
||||||
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
|
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);
|
dl->ChannelsSetCurrent(0);
|
||||||
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
||||||
dl->ChannelsMerge();
|
dl->ChannelsMerge();
|
||||||
@@ -2482,9 +2525,10 @@ void RenderSettingsPage(App* app) {
|
|||||||
ImGui::Dummy(ImVec2(0, gap));
|
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
|
// Clickable header row
|
||||||
ImVec2 headerPos = ImGui::GetCursorScreenPos();
|
ImVec2 headerPos = ImGui::GetCursorScreenPos();
|
||||||
const char* arrow = s_settingsState.debug_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
const char* arrow = s_settingsState.debug_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
||||||
|
|||||||
Reference in New Issue
Block a user