refactor(audit): batch 4 — UI design-system helpers + i18n
Mechanical, behavior-preserving consolidations from the audit. Rendering extractions were kept byte-equivalent; sites that couldn't be made identical were left as-is (noted below). Shared helpers: - util::truncateMiddle(s,maxLen) / (s,front,back) in text_format.h — replaces 6 file-local middle-ellipsis truncators + several inline substr sites (send/receive/transactions/balance_recent_tx/explorer + app.cpp + 3 dialogs). Carries the maxLen<=3 guard, fixing the latent unsigned-underflow copies. - material::LoadingDots() in draw_helpers.h — one animated-ellipsis source for 8 copy-pasted spinner sites (identical GetTime()*3 phase preserved). - Reuse the existing FormatHashrate() in explorer_tab + peers_tab (dropped two inline hashrate ladders). - material::DrawButtonGlassOverlay() — the glass-fill/rim/tactile overlay block shared by TactileButton / TactileSmallButton / schema TactileButton. - material::CollapsibleHeader() — the invisible-button + label + chevron idiom (3 of 5 settings_page sites; RPC/Debug headers skipped — non-identical). - material::GlassCardScope (RAII) — the ChannelsSplit/Indent/DrawGlassPanel/ ChannelsMerge card scaffold (5 settings_page cards; About/send/receive skipped — different padding / logo interleaving). - material::DialogWarningHeader()/DialogConfirmFooter() — warning header + 50/50 Cancel/danger footer across the confirm dialogs; button height moved from a hardcoded 40px into ui.toml (components.overlay-dialog.confirm-btn-height). - File-local enterLowSpec()/exitLowSpec() collapse the 3 low-spec snapshot copies. i18n: wrapped hardcoded English in the shared balance render paths and the whole Lite lifecycle/security section in TR(), with English defaults added to loadBuiltinEnglish() (res/lang/*.json left for the translation tooling — TR falls back to English, so output is unchanged). Full-node + Lite build clean; ctest 1/1; hygiene clean. These are rendering changes — the Settings page (cards/headers/confirm dialogs), all tactile buttons, and the Lite settings section warrant a screenshot check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -170,6 +170,51 @@ struct SettingsPageState {
|
||||
|
||||
static SettingsPageState s_settingsState;
|
||||
|
||||
// Low-spec mode enter: snapshot the current effects prefs, then force the
|
||||
// low-cost overrides. When applyEffects is true, also push the overrides to
|
||||
// the live effects subsystems (the runtime-sync path passes false since it
|
||||
// only mirrors an already-applied hotkey toggle into the checkbox state).
|
||||
static void enterLowSpec(bool applyEffects) {
|
||||
s_settingsState.low_spec_snapshot.valid = true;
|
||||
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
|
||||
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
|
||||
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
|
||||
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
|
||||
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
|
||||
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
|
||||
s_settingsState.acrylic_enabled = false;
|
||||
s_settingsState.blur_amount = 0.0f;
|
||||
s_settingsState.ui_opacity = 1.0f;
|
||||
s_settingsState.window_opacity = 1.0f;
|
||||
s_settingsState.theme_effects_enabled = false;
|
||||
s_settingsState.scanline_enabled = false;
|
||||
if (applyEffects) {
|
||||
effects::ImGuiAcrylic::ApplyBlurAmount(0.0f);
|
||||
effects::ImGuiAcrylic::SetUIOpacity(1.0f);
|
||||
effects::ThemeEffects::instance().setEnabled(false);
|
||||
ConsoleTab::s_scanline_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Low-spec mode exit: restore the previously snapshotted effects prefs and
|
||||
// clear the snapshot. When applyEffects is true, also push them to the live
|
||||
// effects subsystems. Callers guard this on low_spec_snapshot.valid.
|
||||
static void exitLowSpec(bool applyEffects) {
|
||||
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
|
||||
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
|
||||
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
|
||||
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
|
||||
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
|
||||
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
|
||||
if (applyEffects) {
|
||||
effects::ImGuiAcrylic::ApplyBlurAmount(s_settingsState.blur_amount);
|
||||
effects::ImGuiAcrylic::SetUIOpacity(s_settingsState.ui_opacity);
|
||||
effects::ThemeEffects::instance().setEnabled(s_settingsState.theme_effects_enabled);
|
||||
ConsoleTab::s_scanline_enabled = s_settingsState.scanline_enabled;
|
||||
}
|
||||
s_settingsState.low_spec_snapshot.valid = false;
|
||||
}
|
||||
|
||||
// Count whitespace-separated words in a (seed) buffer — used to validate/guide restore input.
|
||||
static int liteSeedWordCount(const char* s) {
|
||||
int words = 0;
|
||||
@@ -274,7 +319,7 @@ static void evaluateLiteLifecycleRequestFromPageState(App* app) {
|
||||
}
|
||||
if (started) {
|
||||
s_settingsState.lite_lifecycle_pending = true;
|
||||
s_settingsState.lite_lifecycle_status = "Working…";
|
||||
s_settingsState.lite_lifecycle_status = TR("lite_working");
|
||||
s_settingsState.lite_lifecycle_summary.clear();
|
||||
} else {
|
||||
// Rejected before any thread launched (wallet already open, an attempt in flight, or no
|
||||
@@ -428,28 +473,10 @@ void RenderSettingsPage(App* app) {
|
||||
if (s_settingsState.low_spec_mode != runtimeLowSpec) {
|
||||
if (runtimeLowSpec) {
|
||||
// Hotkey turned low-spec ON — save snapshot, override statics
|
||||
s_settingsState.low_spec_snapshot.valid = true;
|
||||
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
|
||||
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
|
||||
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
|
||||
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
|
||||
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
|
||||
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
|
||||
s_settingsState.acrylic_enabled = false;
|
||||
s_settingsState.blur_amount = 0.0f;
|
||||
s_settingsState.ui_opacity = 1.0f;
|
||||
s_settingsState.window_opacity = 1.0f;
|
||||
s_settingsState.theme_effects_enabled = false;
|
||||
s_settingsState.scanline_enabled = false;
|
||||
enterLowSpec(false);
|
||||
} else if (s_settingsState.low_spec_snapshot.valid) {
|
||||
// Hotkey turned low-spec OFF — restore snapshot
|
||||
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
|
||||
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
|
||||
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
|
||||
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
|
||||
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
|
||||
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
|
||||
s_settingsState.low_spec_snapshot.valid = false;
|
||||
exitLowSpec(false);
|
||||
} else if (app->settings()) {
|
||||
// No snapshot — read prefs from settings file
|
||||
s_settingsState.blur_amount = app->settings()->getBlurMultiplier();
|
||||
@@ -561,12 +588,7 @@ void RenderSettingsPage(App* app) {
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("theme_language"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
||||
dl->ChannelsSplit(2);
|
||||
dl->ChannelsSetCurrent(1);
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
|
||||
ImGui::Indent(pad);
|
||||
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
|
||||
|
||||
float contentW = availWidth - pad * 2;
|
||||
float comboGap = S.drawElement("components.settings-page", "combo-row-gap").size;
|
||||
@@ -767,27 +789,9 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// --- Collapsible: Advanced Effects... ---
|
||||
{
|
||||
const char* arrow = s_settingsState.effects_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
||||
ImGui::PushFont(body2);
|
||||
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));
|
||||
{
|
||||
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
|
||||
if (ImGui::Button("##EffectsToggle", ImVec2(contentW, ImGui::GetFrameHeight()))) {
|
||||
s_settingsState.effects_expanded = !s_settingsState.effects_expanded;
|
||||
}
|
||||
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
|
||||
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("advanced_effects"));
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
if (!iconFont) iconFont = body2;
|
||||
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + contentW - arrowW, textY), OnSurfaceMedium(), arrow);
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
material::CollapsibleHeader(dl, "##EffectsToggle", TR("advanced_effects"),
|
||||
s_settingsState.effects_expanded, contentW,
|
||||
body2, OnSurfaceMedium());
|
||||
|
||||
if (s_settingsState.effects_expanded) {
|
||||
ImGui::PushFont(body2);
|
||||
@@ -796,35 +800,9 @@ void RenderSettingsPage(App* app) {
|
||||
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &s_settingsState.low_spec_mode)) {
|
||||
effects::setLowSpecMode(s_settingsState.low_spec_mode);
|
||||
if (s_settingsState.low_spec_mode) {
|
||||
s_settingsState.low_spec_snapshot.valid = true;
|
||||
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
|
||||
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
|
||||
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
|
||||
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
|
||||
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
|
||||
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
|
||||
s_settingsState.acrylic_enabled = false;
|
||||
s_settingsState.blur_amount = 0.0f;
|
||||
s_settingsState.ui_opacity = 1.0f;
|
||||
s_settingsState.window_opacity = 1.0f;
|
||||
s_settingsState.theme_effects_enabled = false;
|
||||
s_settingsState.scanline_enabled = false;
|
||||
effects::ImGuiAcrylic::ApplyBlurAmount(0.0f);
|
||||
effects::ImGuiAcrylic::SetUIOpacity(1.0f);
|
||||
effects::ThemeEffects::instance().setEnabled(false);
|
||||
ConsoleTab::s_scanline_enabled = false;
|
||||
enterLowSpec(true);
|
||||
} else if (s_settingsState.low_spec_snapshot.valid) {
|
||||
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
|
||||
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
|
||||
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
|
||||
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
|
||||
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
|
||||
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
|
||||
effects::ImGuiAcrylic::ApplyBlurAmount(s_settingsState.blur_amount);
|
||||
effects::ImGuiAcrylic::SetUIOpacity(s_settingsState.ui_opacity);
|
||||
effects::ThemeEffects::instance().setEnabled(s_settingsState.theme_effects_enabled);
|
||||
ConsoleTab::s_scanline_enabled = s_settingsState.scanline_enabled;
|
||||
s_settingsState.low_spec_snapshot.valid = false;
|
||||
exitLowSpec(true);
|
||||
}
|
||||
saveSettingsPageState(app->settings());
|
||||
}
|
||||
@@ -1065,26 +1043,10 @@ void RenderSettingsPage(App* app) {
|
||||
|
||||
// --- Collapsible: Advanced Effects... ---
|
||||
{
|
||||
const char* arrow = s_settingsState.effects_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
||||
ImGui::PushFont(body2);
|
||||
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));
|
||||
{
|
||||
float narrowContentW = availWidth - pad * 2;
|
||||
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
|
||||
if (ImGui::Button("##EffectsToggleN", ImVec2(narrowContentW, ImGui::GetFrameHeight()))) {
|
||||
s_settingsState.effects_expanded = !s_settingsState.effects_expanded;
|
||||
}
|
||||
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
|
||||
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("advanced_effects"));
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
if (!iconFont) iconFont = body2;
|
||||
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + narrowContentW - arrowW, textY), OnSurfaceMedium(), arrow);
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopFont();
|
||||
float narrowContentW = availWidth - pad * 2;
|
||||
material::CollapsibleHeader(dl, "##EffectsToggleN", TR("advanced_effects"),
|
||||
s_settingsState.effects_expanded, narrowContentW,
|
||||
body2, OnSurfaceMedium());
|
||||
}
|
||||
|
||||
if (s_settingsState.effects_expanded) {
|
||||
@@ -1093,35 +1055,9 @@ void RenderSettingsPage(App* app) {
|
||||
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &s_settingsState.low_spec_mode)) {
|
||||
effects::setLowSpecMode(s_settingsState.low_spec_mode);
|
||||
if (s_settingsState.low_spec_mode) {
|
||||
s_settingsState.low_spec_snapshot.valid = true;
|
||||
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
|
||||
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
|
||||
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
|
||||
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
|
||||
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
|
||||
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
|
||||
s_settingsState.acrylic_enabled = false;
|
||||
s_settingsState.blur_amount = 0.0f;
|
||||
s_settingsState.ui_opacity = 1.0f;
|
||||
s_settingsState.window_opacity = 1.0f;
|
||||
s_settingsState.theme_effects_enabled = false;
|
||||
s_settingsState.scanline_enabled = false;
|
||||
effects::ImGuiAcrylic::ApplyBlurAmount(0.0f);
|
||||
effects::ImGuiAcrylic::SetUIOpacity(1.0f);
|
||||
effects::ThemeEffects::instance().setEnabled(false);
|
||||
ConsoleTab::s_scanline_enabled = false;
|
||||
enterLowSpec(true);
|
||||
} else if (s_settingsState.low_spec_snapshot.valid) {
|
||||
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
|
||||
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
|
||||
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
|
||||
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
|
||||
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
|
||||
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
|
||||
effects::ImGuiAcrylic::ApplyBlurAmount(s_settingsState.blur_amount);
|
||||
effects::ImGuiAcrylic::SetUIOpacity(s_settingsState.ui_opacity);
|
||||
effects::ThemeEffects::instance().setEnabled(s_settingsState.theme_effects_enabled);
|
||||
ConsoleTab::s_scanline_enabled = s_settingsState.scanline_enabled;
|
||||
s_settingsState.low_spec_snapshot.valid = false;
|
||||
exitLowSpec(true);
|
||||
}
|
||||
saveSettingsPageState(app->settings());
|
||||
}
|
||||
@@ -1219,19 +1155,6 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::PopFont();
|
||||
} // s_settingsState.effects_expanded
|
||||
}
|
||||
|
||||
// Bottom padding
|
||||
ImGui::Dummy(ImVec2(0, bottomPad));
|
||||
ImGui::Unindent(pad);
|
||||
|
||||
// Draw glass panel behind content (auto-sized)
|
||||
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
|
||||
dl->ChannelsSetCurrent(0);
|
||||
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
||||
dl->ChannelsMerge();
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
||||
ImGui::Dummy(ImVec2(availWidth, 0));
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, gap));
|
||||
@@ -1243,11 +1166,8 @@ void RenderSettingsPage(App* app) {
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("wallet"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
||||
dl->ChannelsSplit(2);
|
||||
dl->ChannelsSetCurrent(1);
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
|
||||
ImGui::Indent(pad);
|
||||
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
|
||||
const ImVec2& cardMin = card.cardMin;
|
||||
|
||||
float contentW = availWidth - pad * 2;
|
||||
|
||||
@@ -1307,27 +1227,9 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// --- Collapsible: Tools & Actions... ---
|
||||
{
|
||||
const char* arrow = s_settingsState.tools_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
|
||||
ImGui::PushFont(body2);
|
||||
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));
|
||||
{
|
||||
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
|
||||
if (ImGui::Button("##ToolsToggle", ImVec2(contentW, ImGui::GetFrameHeight()))) {
|
||||
s_settingsState.tools_expanded = !s_settingsState.tools_expanded;
|
||||
}
|
||||
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
|
||||
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("tools_actions"));
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
if (!iconFont) iconFont = body2;
|
||||
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + contentW - arrowW, textY), OnSurfaceMedium(), arrow);
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
material::CollapsibleHeader(dl, "##ToolsToggle", TR("tools_actions"),
|
||||
s_settingsState.tools_expanded, contentW,
|
||||
body2, OnSurfaceMedium());
|
||||
|
||||
if (s_settingsState.tools_expanded) {
|
||||
float btnSpacing = Layout::spacingMd();
|
||||
@@ -1435,17 +1337,6 @@ void RenderSettingsPage(App* app) {
|
||||
|
||||
if (scale < 1.0f) ImGui::SetWindowFontScale(1.0f);
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, bottomPad));
|
||||
ImGui::Unindent(pad);
|
||||
|
||||
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
|
||||
dl->ChannelsSetCurrent(0);
|
||||
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
||||
dl->ChannelsMerge();
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
||||
ImGui::Dummy(ImVec2(availWidth, 0));
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, gap));
|
||||
@@ -1458,11 +1349,7 @@ void RenderSettingsPage(App* app) {
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("node_security"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
||||
dl->ChannelsSplit(2);
|
||||
dl->ChannelsSetCurrent(1);
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
|
||||
ImGui::Indent(pad);
|
||||
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
|
||||
|
||||
float contentW = availWidth - pad * 2;
|
||||
float minBtnW = S.drawElement("components.settings-page", "wallet-btn-min-width").sizeOr(130.0f);
|
||||
@@ -1618,18 +1505,18 @@ void RenderSettingsPage(App* app) {
|
||||
|
||||
// Lite-server selection lives in the dedicated Network tab now.
|
||||
Type().textColored(TypeStyle::Body2, OnSurfaceMedium(),
|
||||
"Lite servers are managed in the Network tab.");
|
||||
TR("lite_servers_network_tab"));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
if (ImGui::Button("Lite wallet request##LiteLifecycleToggle", ImVec2(liteInputW, 0))) {
|
||||
if (ImGui::Button(TrId("lite_wallet_request", "LiteLifecycleToggle").c_str(), ImVec2(liteInputW, 0))) {
|
||||
s_settingsState.lite_lifecycle_expanded = !s_settingsState.lite_lifecycle_expanded;
|
||||
}
|
||||
|
||||
if (s_settingsState.lite_lifecycle_expanded) {
|
||||
const char* lifecycleLabels[] = {"Create", "Open", "Restore"};
|
||||
const char* lifecycleLabels[] = {TR("lite_op_create"), TR("lite_op_open"), TR("lite_op_restore")};
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Action");
|
||||
ImGui::TextUnformatted(TR("lite_action"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
if (ImGui::BeginCombo("##LiteLifecycleOperation",
|
||||
@@ -1650,7 +1537,7 @@ void RenderSettingsPage(App* app) {
|
||||
s_settingsState.lite_lifecycle_operation == 2) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Wallet");
|
||||
ImGui::TextUnformatted(TR("lite_wallet_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
ImGui::InputText("##LiteWalletPath", s_settingsState.lite_wallet_path,
|
||||
@@ -1660,7 +1547,7 @@ void RenderSettingsPage(App* app) {
|
||||
if (s_settingsState.lite_lifecycle_operation == 2) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Seed");
|
||||
ImGui::TextUnformatted(TR("lite_seed_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
ImGui::InputText("##LiteRestoreSeed", s_settingsState.lite_restore_seed,
|
||||
@@ -1670,38 +1557,38 @@ void RenderSettingsPage(App* app) {
|
||||
{
|
||||
const int wc = liteSeedWordCount(s_settingsState.lite_restore_seed);
|
||||
char wbuf[32];
|
||||
snprintf(wbuf, sizeof(wbuf), "%d / 24 words", wc);
|
||||
snprintf(wbuf, sizeof(wbuf), TR("lite_word_count"), wc);
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
Type().textColored(TypeStyle::Caption, wc == 24 ? Success() : Warning(), wbuf);
|
||||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Birthday");
|
||||
ImGui::TextUnformatted(TR("lite_birthday_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(std::min(160.0f, liteInputW));
|
||||
ImGui::InputInt("##LiteRestoreBirthday", &s_settingsState.lite_restore_birthday);
|
||||
if (s_settingsState.lite_restore_birthday < 0) s_settingsState.lite_restore_birthday = 0;
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(),
|
||||
"Block height to start scanning from. Leave 0 if unknown (slower full scan).");
|
||||
TR("lite_birthday_hint"));
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Account");
|
||||
ImGui::TextUnformatted(TR("lite_account_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(std::min(160.0f, liteInputW));
|
||||
ImGui::InputInt("##LiteRestoreAccount", &s_settingsState.lite_restore_account);
|
||||
if (s_settingsState.lite_restore_account < 0) s_settingsState.lite_restore_account = 0;
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::Checkbox("Overwrite##LiteRestoreOverwrite",
|
||||
ImGui::Checkbox(TrId("lite_overwrite", "LiteRestoreOverwrite").c_str(),
|
||||
&s_settingsState.lite_restore_overwrite);
|
||||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Passphrase");
|
||||
ImGui::TextUnformatted(TR("lite_passphrase_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
ImGui::InputText("##LiteLifecyclePassphrase",
|
||||
@@ -1719,7 +1606,7 @@ void RenderSettingsPage(App* app) {
|
||||
const auto& result = lite->lastLifecycleResult();
|
||||
s_settingsState.lite_lifecycle_summary = result.bridgeResponseRedacted;
|
||||
if (result.walletReady) {
|
||||
s_settingsState.lite_lifecycle_status = "Wallet ready";
|
||||
s_settingsState.lite_lifecycle_status = TR("lite_wallet_ready");
|
||||
} else {
|
||||
s_settingsState.lite_lifecycle_status = result.error.empty()
|
||||
? result.status.message
|
||||
@@ -1735,7 +1622,7 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
const bool liteLifecycleBusy = s_settingsState.lite_lifecycle_pending;
|
||||
if (liteLifecycleBusy) ImGui::BeginDisabled();
|
||||
if (TactileButton("Validate##LiteLifecycleValidate", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_validate", "LiteLifecycleValidate").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
evaluateLiteLifecycleRequestFromPageState(app);
|
||||
}
|
||||
if (liteLifecycleBusy) ImGui::EndDisabled();
|
||||
@@ -1765,17 +1652,15 @@ void RenderSettingsPage(App* app) {
|
||||
if (app->liteWallet() && app->liteWallet()->walletOpen()) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
Type().text(TypeStyle::Body2, "Backup & keys");
|
||||
Type().text(TypeStyle::Body2, TR("lite_backup_keys"));
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton("Show seed##LiteExportSeed", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_show_seed", "LiteExportSeed").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
auto r = app->liteWallet()->exportSeed();
|
||||
wallet::secureWipeLiteSecret(s_settingsState.lite_export_secret);
|
||||
if (r.ok) {
|
||||
s_settingsState.lite_export_secret = r.seedPhrase;
|
||||
s_settingsState.lite_export_label =
|
||||
"Seed phrase — the ONLY way to restore your wallet. "
|
||||
"Write it down, store it offline, never share it.";
|
||||
s_settingsState.lite_export_label = TR("lite_seed_warning");
|
||||
s_settingsState.lite_export_is_seed = true;
|
||||
s_settingsState.lite_export_birthday = r.birthday;
|
||||
s_settingsState.lite_backup_status.clear();
|
||||
@@ -1787,13 +1672,13 @@ void RenderSettingsPage(App* app) {
|
||||
wallet::secureWipeLiteSecret(r.seedPhrase); // wipe the result copy
|
||||
}
|
||||
ImGui::SameLine(0, Layout::spacingSm());
|
||||
if (TactileButton("Show private keys##LiteExportKeys", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_show_private_keys", "LiteExportKeys").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
auto r = app->liteWallet()->exportPrivateKeys();
|
||||
wallet::secureWipeLiteSecret(s_settingsState.lite_export_secret);
|
||||
s_settingsState.lite_export_is_seed = false; // keys, not a seed
|
||||
if (r.ok) {
|
||||
s_settingsState.lite_export_secret = r.privateKeysJson;
|
||||
s_settingsState.lite_export_label = "Private keys — anyone with these can spend your funds";
|
||||
s_settingsState.lite_export_label = TR("lite_private_keys_warning");
|
||||
s_settingsState.lite_backup_status.clear();
|
||||
} else {
|
||||
s_settingsState.lite_export_label.clear();
|
||||
@@ -1814,19 +1699,19 @@ void RenderSettingsPage(App* app) {
|
||||
// The seed's birthday is needed to restore quickly — show + back it up too.
|
||||
if (s_settingsState.lite_export_is_seed) {
|
||||
char bday[64];
|
||||
snprintf(bday, sizeof(bday), "Birthday: %llu (back this up too)",
|
||||
snprintf(bday, sizeof(bday), TR("lite_birthday_backup"),
|
||||
s_settingsState.lite_export_birthday);
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), bday);
|
||||
}
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton("Copy##LiteExportCopy", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_copy", "LiteExportCopy").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
app->copySecretToClipboard(s_settingsState.lite_export_secret);
|
||||
}
|
||||
ImGui::SameLine(0, Layout::spacingSm());
|
||||
// Save the seed (+ birthday) to an owner-only (0600) file in the config dir.
|
||||
if (s_settingsState.lite_export_is_seed &&
|
||||
TactileButton("Save to file##LiteExportSave", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
TactileButton(TrId("lite_save_to_file", "LiteExportSave").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
std::string path = util::Platform::getConfigDir() + "/lite-seed-backup.txt";
|
||||
std::string content = s_settingsState.lite_export_secret + "\nBirthday: " +
|
||||
std::to_string(s_settingsState.lite_export_birthday) + "\n";
|
||||
@@ -1834,11 +1719,11 @@ void RenderSettingsPage(App* app) {
|
||||
path, content, /*restrictPermissions=*/true);
|
||||
wallet::secureWipeLiteSecret(content);
|
||||
s_settingsState.lite_backup_status = ok
|
||||
? ("Saved (plaintext, owner-only) to " + path)
|
||||
: ("Could not write " + path);
|
||||
? (std::string(TR("lite_saved_to")) + path)
|
||||
: (std::string(TR("lite_could_not_write")) + path);
|
||||
}
|
||||
if (s_settingsState.lite_export_is_seed) ImGui::SameLine(0, Layout::spacingSm());
|
||||
if (TactileButton("Hide & wipe##LiteExportHide", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_hide_wipe", "LiteExportHide").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
wallet::secureWipeLiteSecret(s_settingsState.lite_export_secret);
|
||||
s_settingsState.lite_export_label.clear();
|
||||
s_settingsState.lite_export_is_seed = false;
|
||||
@@ -1848,18 +1733,18 @@ void RenderSettingsPage(App* app) {
|
||||
// Import a spending/viewing key (history appears after the next sync).
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Import key");
|
||||
ImGui::TextUnformatted(TR("lite_import_key_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
ImGui::InputText("##LiteImportKey", s_settingsState.lite_import_key,
|
||||
sizeof(s_settingsState.lite_import_key),
|
||||
ImGuiInputTextFlags_Password);
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton("Import##LiteImportKeyBtn", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_import", "LiteImportKeyBtn").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
const auto r = app->liteWallet()->importKey(s_settingsState.lite_import_key);
|
||||
sodium_memzero(s_settingsState.lite_import_key, sizeof(s_settingsState.lite_import_key));
|
||||
s_settingsState.lite_backup_status =
|
||||
r.ok ? "Key imported — run a sync to scan its history" : r.error;
|
||||
r.ok ? TR("lite_key_imported") : r.error;
|
||||
}
|
||||
|
||||
if (!s_settingsState.lite_backup_status.empty()) {
|
||||
@@ -1871,57 +1756,57 @@ void RenderSettingsPage(App* app) {
|
||||
// ---- Security: passphrase encryption (encrypt / unlock / lock / decrypt) ----
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
Type().text(TypeStyle::Body2, "Security");
|
||||
Type().text(TypeStyle::Body2, TR("lite_security"));
|
||||
const auto& wstate = app->getWalletState();
|
||||
const float encLabelX = leftX - sectionOrigin.x + liteLabelW;
|
||||
if (!wstate.isEncrypted()) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Passphrase");
|
||||
ImGui::TextUnformatted(TR("lite_passphrase_label"));
|
||||
ImGui::SameLine(encLabelX);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
ImGui::InputText("##LiteEncryptPass", s_settingsState.lite_enc_pass,
|
||||
sizeof(s_settingsState.lite_enc_pass), ImGuiInputTextFlags_Password);
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton("Encrypt wallet##LiteEncrypt", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_encrypt_wallet", "LiteEncrypt").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
const auto r = app->liteWallet()->encryptWallet(s_settingsState.lite_enc_pass);
|
||||
sodium_memzero(s_settingsState.lite_enc_pass, sizeof(s_settingsState.lite_enc_pass));
|
||||
s_settingsState.lite_encryption_status = r.ok ? "Wallet encrypted" : r.error;
|
||||
s_settingsState.lite_encryption_status = r.ok ? TR("lite_wallet_encrypted") : r.error;
|
||||
}
|
||||
} else {
|
||||
if (wstate.isLocked()) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Unlock");
|
||||
ImGui::TextUnformatted(TR("lite_unlock"));
|
||||
ImGui::SameLine(encLabelX);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
ImGui::InputText("##LiteUnlockPass", s_settingsState.lite_enc_pass,
|
||||
sizeof(s_settingsState.lite_enc_pass), ImGuiInputTextFlags_Password);
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton("Unlock##LiteUnlock", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_unlock", "LiteUnlock").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
const bool ok = app->liteWallet()->unlockWallet(s_settingsState.lite_enc_pass);
|
||||
sodium_memzero(s_settingsState.lite_enc_pass, sizeof(s_settingsState.lite_enc_pass));
|
||||
s_settingsState.lite_encryption_status = ok ? "Wallet unlocked" : "Unlock failed";
|
||||
s_settingsState.lite_encryption_status = ok ? TR("lite_wallet_unlocked") : TR("lite_unlock_failed");
|
||||
}
|
||||
} else {
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton("Lock now##LiteLock", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_lock_now", "LiteLock").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
s_settingsState.lite_encryption_status =
|
||||
app->liteWallet()->lockWallet() ? "Wallet locked" : "Lock failed";
|
||||
app->liteWallet()->lockWallet() ? TR("lite_wallet_locked") : TR("lite_lock_failed");
|
||||
}
|
||||
}
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("Passphrase");
|
||||
ImGui::TextUnformatted(TR("lite_passphrase_label"));
|
||||
ImGui::SameLine(encLabelX);
|
||||
ImGui::SetNextItemWidth(liteInputW);
|
||||
ImGui::InputText("##LiteDecryptPass", s_settingsState.lite_dec_pass,
|
||||
sizeof(s_settingsState.lite_dec_pass), ImGuiInputTextFlags_Password);
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton("Remove encryption##LiteDecrypt", ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TrId("lite_remove_encryption", "LiteDecrypt").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
const auto r = app->liteWallet()->decryptWallet(s_settingsState.lite_dec_pass);
|
||||
sodium_memzero(s_settingsState.lite_dec_pass, sizeof(s_settingsState.lite_dec_pass));
|
||||
s_settingsState.lite_encryption_status = r.ok ? "Encryption removed" : r.error;
|
||||
s_settingsState.lite_encryption_status = r.ok ? TR("lite_encryption_removed") : r.error;
|
||||
}
|
||||
}
|
||||
if (!s_settingsState.lite_encryption_status.empty()) {
|
||||
@@ -2347,17 +2232,6 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(sectionOrigin.x, ImGui::GetCursorScreenPos().y));
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, bottomPad));
|
||||
ImGui::Unindent(pad);
|
||||
|
||||
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
|
||||
dl->ChannelsSetCurrent(0);
|
||||
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
||||
dl->ChannelsMerge();
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
||||
ImGui::Dummy(ImVec2(availWidth, 0));
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, gap));
|
||||
@@ -2369,11 +2243,7 @@ void RenderSettingsPage(App* app) {
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("explorer_section"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
||||
dl->ChannelsSplit(2);
|
||||
dl->ChannelsSetCurrent(1);
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
|
||||
ImGui::Indent(pad);
|
||||
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
|
||||
|
||||
float contentW = availWidth - pad * 2;
|
||||
ImGui::PushFont(body2);
|
||||
@@ -2413,17 +2283,6 @@ void RenderSettingsPage(App* app) {
|
||||
util::Platform::openUrl("https://explorer.dragonx.is");
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_block_explorer"));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, bottomPad));
|
||||
ImGui::Unindent(pad);
|
||||
|
||||
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
|
||||
dl->ChannelsSetCurrent(0);
|
||||
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
||||
dl->ChannelsMerge();
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
||||
ImGui::Dummy(ImVec2(availWidth, 0));
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, gap));
|
||||
@@ -2597,11 +2456,7 @@ void RenderSettingsPage(App* app) {
|
||||
if (s_settingsState.debug_expanded) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
ImVec2 cardMin = ImGui::GetCursorScreenPos();
|
||||
dl->ChannelsSplit(2);
|
||||
dl->ChannelsSetCurrent(1);
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
|
||||
ImGui::Indent(pad);
|
||||
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
|
||||
|
||||
ImGui::TextColored(ImVec4(1,1,1,0.5f), "%s", TR("settings_debug_select"));
|
||||
ImGui::TextColored(ImVec4(1,1,1,0.35f), "%s", TR("settings_debug_restart_note"));
|
||||
@@ -2684,17 +2539,6 @@ void RenderSettingsPage(App* app) {
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_restart_daemon"));
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, bottomPad));
|
||||
ImGui::Unindent(pad);
|
||||
|
||||
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
|
||||
dl->ChannelsSetCurrent(0);
|
||||
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
|
||||
dl->ChannelsMerge();
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
||||
ImGui::Dummy(ImVec2(availWidth, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2739,12 +2583,8 @@ void RenderSettingsPage(App* app) {
|
||||
// Confirmation dialog for clearing z-tx history
|
||||
if (s_settingsState.confirm_clear_ztx) {
|
||||
if (BeginOverlayDialog(TR("confirm_clear_ztx_title"), &s_settingsState.confirm_clear_ztx, 480.0f, 0.94f)) {
|
||||
ImGui::PushFont(Type().iconLarge());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), ICON_MD_WARNING);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "%s", TR("warning"));
|
||||
|
||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.6f, 0.0f, 1.0f));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", TR("confirm_clear_ztx_warning1"));
|
||||
ImGui::Spacing();
|
||||
@@ -2752,15 +2592,14 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (ImGui::Button(TR("cancel"), ImVec2(btnW, 40))) {
|
||||
|
||||
bool doCancel = false, doConfirm = false;
|
||||
material::DialogConfirmFooter(TR("cancel"), TrId("clear_anyway", "clear_ztx_btn").c_str(),
|
||||
true, doCancel, doConfirm);
|
||||
if (doCancel) {
|
||||
s_settingsState.confirm_clear_ztx = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.3f, 0.3f, 1.0f));
|
||||
if (ImGui::Button(TrId("clear_anyway", "clear_ztx_btn").c_str(), ImVec2(btnW, 40))) {
|
||||
if (doConfirm) {
|
||||
std::string ztx_file = util::Platform::getDragonXDataDir() + "ztx_history.json";
|
||||
if (util::Platform::deleteFile(ztx_file)) {
|
||||
Notifications::instance().success("Z-transaction history cleared");
|
||||
@@ -2769,7 +2608,6 @@ void RenderSettingsPage(App* app) {
|
||||
}
|
||||
s_settingsState.confirm_clear_ztx = false;
|
||||
}
|
||||
ImGui::PopStyleColor(2);
|
||||
EndOverlayDialog();
|
||||
}
|
||||
}
|
||||
@@ -2777,12 +2615,8 @@ void RenderSettingsPage(App* app) {
|
||||
// Confirmation dialog for deleting blockchain data
|
||||
if (s_settingsState.confirm_delete_blockchain) {
|
||||
if (BeginOverlayDialog(TR("confirm_delete_blockchain_title"), &s_settingsState.confirm_delete_blockchain, 500.0f, 0.94f)) {
|
||||
ImGui::PushFont(Type().iconLarge());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), ICON_MD_WARNING);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", TR("warning"));
|
||||
|
||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", TR("confirm_delete_blockchain_msg"));
|
||||
ImGui::Spacing();
|
||||
@@ -2790,20 +2624,18 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (ImGui::Button(TR("cancel"), ImVec2(btnW, 40))) {
|
||||
|
||||
bool doCancel = false, doConfirm = false;
|
||||
material::DialogConfirmFooter(TR("cancel"), TrId("delete_blockchain_confirm", "del_bc_btn").c_str(),
|
||||
true, doCancel, doConfirm);
|
||||
if (doCancel) {
|
||||
s_settingsState.confirm_delete_blockchain = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.3f, 0.3f, 1.0f));
|
||||
if (ImGui::Button(TrId("delete_blockchain_confirm", "del_bc_btn").c_str(), ImVec2(btnW, 40))) {
|
||||
if (doConfirm) {
|
||||
if (app->supportsFullNodeLifecycleActions())
|
||||
app->deleteBlockchainData();
|
||||
s_settingsState.confirm_delete_blockchain = false;
|
||||
}
|
||||
ImGui::PopStyleColor(2);
|
||||
EndOverlayDialog();
|
||||
}
|
||||
}
|
||||
@@ -2825,11 +2657,7 @@ void RenderSettingsPage(App* app) {
|
||||
}
|
||||
|
||||
if (BeginOverlayDialog(TR("confirm_rescan_title"), &s_settingsState.confirm_rescan, 500.0f, 0.94f)) {
|
||||
ImGui::PushFont(Type().iconLarge());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
|
||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
@@ -2877,11 +2705,7 @@ void RenderSettingsPage(App* app) {
|
||||
// Confirm: repair wallet (-zapwallettxes=2 — wipe & rebuild wallet tx records, then rescan)
|
||||
if (s_settingsState.confirm_repair_wallet) {
|
||||
if (BeginOverlayDialog(TR("confirm_repair_wallet_title"), &s_settingsState.confirm_repair_wallet, 500.0f, 0.94f)) {
|
||||
ImGui::PushFont(Type().iconLarge());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
|
||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", TR("confirm_repair_wallet_msg"));
|
||||
@@ -2891,12 +2715,14 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (ImGui::Button(TrId("cancel", "repair_wallet_cancel").c_str(), ImVec2(btnW, 40))) {
|
||||
bool doCancel = false, doConfirm = false;
|
||||
material::DialogConfirmFooter(TrId("cancel", "repair_wallet_cancel").c_str(),
|
||||
TrId("repair_wallet", "repair_wallet_confirm").c_str(),
|
||||
false, doCancel, doConfirm);
|
||||
if (doCancel) {
|
||||
s_settingsState.confirm_repair_wallet = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(TrId("repair_wallet", "repair_wallet_confirm").c_str(), ImVec2(btnW, 40))) {
|
||||
if (doConfirm) {
|
||||
app->repairWallet();
|
||||
s_settingsState.confirm_repair_wallet = false;
|
||||
}
|
||||
@@ -2907,11 +2733,7 @@ void RenderSettingsPage(App* app) {
|
||||
// Confirm: reinstall the bundled daemon binary (stop → overwrite → restart)
|
||||
if (s_settingsState.confirm_reinstall_daemon) {
|
||||
if (BeginOverlayDialog(TR("confirm_reinstall_daemon_title"), &s_settingsState.confirm_reinstall_daemon, 500.0f, 0.94f)) {
|
||||
ImGui::PushFont(Type().iconLarge());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
|
||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", TR("confirm_reinstall_daemon_msg"));
|
||||
@@ -2921,12 +2743,14 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (ImGui::Button(TrId("cancel", "reinstall_daemon_cancel").c_str(), ImVec2(btnW, 40))) {
|
||||
bool doCancel = false, doConfirm = false;
|
||||
material::DialogConfirmFooter(TrId("cancel", "reinstall_daemon_cancel").c_str(),
|
||||
TrId("daemon_install_bundled", "reinstall_daemon_confirm").c_str(),
|
||||
false, doCancel, doConfirm);
|
||||
if (doCancel) {
|
||||
s_settingsState.confirm_reinstall_daemon = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(TrId("daemon_install_bundled", "reinstall_daemon_confirm").c_str(), ImVec2(btnW, 40))) {
|
||||
if (doConfirm) {
|
||||
app->reinstallBundledDaemon();
|
||||
s_settingsState.daemon_info_loaded = false; // refresh the panel after the swap
|
||||
s_settingsState.confirm_reinstall_daemon = false;
|
||||
@@ -2938,11 +2762,7 @@ void RenderSettingsPage(App* app) {
|
||||
// Confirm: restart daemon (briefly drops the connection to apply changed options)
|
||||
if (s_settingsState.confirm_restart_daemon) {
|
||||
if (BeginOverlayDialog(TR("confirm_restart_daemon_title"), &s_settingsState.confirm_restart_daemon, 500.0f, 0.94f)) {
|
||||
ImGui::PushFont(Type().iconLarge());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
|
||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", TR("confirm_restart_daemon_msg"));
|
||||
@@ -2950,12 +2770,14 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (ImGui::Button(TrId("cancel", "restartd_cancel").c_str(), ImVec2(btnW, 40))) {
|
||||
bool doCancel = false, doConfirm = false;
|
||||
material::DialogConfirmFooter(TrId("cancel", "restartd_cancel").c_str(),
|
||||
TrId("settings_restart_daemon", "restartd_confirm").c_str(),
|
||||
false, doCancel, doConfirm);
|
||||
if (doCancel) {
|
||||
s_settingsState.confirm_restart_daemon = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(TrId("settings_restart_daemon", "restartd_confirm").c_str(), ImVec2(btnW, 40))) {
|
||||
if (doConfirm) {
|
||||
s_settingsState.debug_cats_dirty = false;
|
||||
app->restartDaemon();
|
||||
s_settingsState.confirm_restart_daemon = false;
|
||||
@@ -2967,11 +2789,7 @@ void RenderSettingsPage(App* app) {
|
||||
// Confirm: lite wallet re-download blocks (rescan from the lite server — long but safe)
|
||||
if (s_settingsState.confirm_lite_redownload) {
|
||||
if (BeginOverlayDialog(TR("confirm_lite_redownload_title"), &s_settingsState.confirm_lite_redownload, 500.0f, 0.94f)) {
|
||||
ImGui::PushFont(Type().iconLarge());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
|
||||
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", TR("confirm_lite_redownload_msg"));
|
||||
@@ -2981,12 +2799,14 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (ImGui::Button(TrId("cancel", "lite_redl_cancel").c_str(), ImVec2(btnW, 40))) {
|
||||
bool doCancel = false, doConfirm = false;
|
||||
material::DialogConfirmFooter(TrId("cancel", "lite_redl_cancel").c_str(),
|
||||
TrId("lite_redownload_blocks", "lite_redl_confirm").c_str(),
|
||||
false, doCancel, doConfirm);
|
||||
if (doCancel) {
|
||||
s_settingsState.confirm_lite_redownload = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(TrId("lite_redownload_blocks", "lite_redl_confirm").c_str(), ImVec2(btnW, 40))) {
|
||||
if (doConfirm) {
|
||||
if (auto* lite = app->liteWallet()) lite->startRescan();
|
||||
s_settingsState.confirm_lite_redownload = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user