Files
ObsidianDragon/src/ui/windows/seed_display.h
DanS 6c19fac6f5 fix(ui): DPI-scale hand-drawn overlays + freeze sweep demo state
Hand-drawn absolute geometry (dl->AddText offsets, SetNextWindowSize,
explicit ImVec2 button widths, SameLine strides) is immune to the app's
DPI machinery (font-atlas rebuild + ScaleAllSizes), so several overlays
rendered native-size (tiny) on HiDPI / Windows 150% displays. Multiply the
raw logical-px geometry by Layout::dpiScale():

- seed_display: content-aware, DPI-scaled seed-grid column stride (a raw
  130px stride let words collide with the next column's index at 150%)
- lock screen: card/logo/input/unlock-button + all vertical offsets
- migrate-to-seed + seed-backup dialogs: button widths
- send-confirm: confirm/cancel button widths
- about dialog: value-column x, edition inset, link/close button widths
- chat conversation list: row height + padding (fixes the preview clip)

Also freeze demo state during the full UI sweep: guard App::update() on
capture_mode_ so a running daemon's refresh can't clobber the injected
demo state (it blanked Send/Receive/History with a CDB error mid-sweep).

Verified on Windows 4K@150% and locally at font_scale=1.5 (same
dpiScale()==1.5 code path). Adds a DPI-scaling convention note to CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:58:54 -05:00

60 lines
2.2 KiB
C++

#pragma once
// Shared helpers for rendering a BIP39 seed phrase as a numbered word grid and for
// splitting a whitespace-separated phrase into words. Used by the full-node seed-phrase
// backup dialog and the "migrate to a seed wallet" flow so both present the phrase the
// same way the lite welcome wizard does.
#include <cstdio>
#include <string>
#include <vector>
#include "imgui.h"
#include "../layout.h" // Layout::dpiScale()
namespace dragonx {
namespace ui {
// Split a whitespace-separated seed phrase into its individual words.
inline std::vector<std::string> SplitSeedWords(const std::string& phrase)
{
std::vector<std::string> words;
std::string cur;
for (char c : phrase) {
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
if (!cur.empty()) { words.push_back(cur); cur.clear(); }
} else {
cur.push_back(c);
}
}
if (!cur.empty()) words.push_back(cur);
return words;
}
// Render the words as a numbered 4-column grid (matches the lite welcome wizard layout).
// The column stride is sized to the widest "NN. word" cell and scaled by dpiScale(): a raw px
// stride collided with the next column's index once the font grew at higher DPI / font scale
// (e.g. at 150% "13. elevator" ran straight into "14."). Sizing to content keeps a gap at any
// scale and for any word length.
inline void RenderSeedWordGrid(const std::vector<std::string>& words, float colSpacingPx = 130.0f)
{
const float dp = Layout::dpiScale();
float colStride = colSpacingPx * dp;
for (size_t i = 0; i < words.size(); ++i) {
char cell[96];
std::snprintf(cell, sizeof(cell), "%2zu. %s", i + 1, words[i].c_str());
const float need = ImGui::CalcTextSize(cell).x + 20.0f * dp; // cell width + inter-column gap
if (need > colStride) colStride = need;
}
for (size_t i = 0; i < words.size(); ++i) {
char cell[96];
std::snprintf(cell, sizeof(cell), "%2zu. %s", i + 1, words[i].c_str());
ImGui::TextUnformatted(cell);
if ((i % 4) != 3 && i + 1 < words.size())
ImGui::SameLine(((i % 4) + 1) * colStride);
}
}
} // namespace ui
} // namespace dragonx