feat(wallets): redesign the modal layout — content-sized, framed, aligned
Act on the design review of the Wallets modal (all six points): 1. Content-sized card — size the card to the wallet count (up to 8 rows, then the list scrolls) instead of a fixed 620px, killing the huge empty void a short list floated in. Computed from the KNOWN row count, so it stays a fixed-height dialog (no auto-height feedback — that's what slid content off-screen on a monitor move). 2. Framed wallet list — BordersOuter + PadOuterX so the list reads as a contained panel with room to grow, not loose rows on the backdrop. 3. Right-aligned numeric columns (Size / Addresses / Balance) and their headers, via a custom header row + a right-aligned cell helper, so the numbers line up. 4. Consistent action column — the active wallet's row gets a subtle green tint and an "Active" chip (badge), distinct from the Open/Import buttons. 5. De-cluttered bottom — "Create wallet" stays the primary action; the folder-scan is tucked behind a "+ Scan another folder…" toggle. 6. Polish — the external-file marker is now a real info icon + tooltip (was ICON_MD_FOLDER rendering as a "?" tofu, since that glyph isn't in the subset); Refresh is an icon-only button, de-emphasized next to Close. New i18n keys (wallets_add_folder_toggle, wallets_external_tt) + 8 langs; CJK subset rebuilt. Verified via the headless sweep at 100% and font_scale 1.5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "Aktiv",
|
||||
"wallets_add": "Ordner hinzufügen",
|
||||
"wallets_add_folder": "Auch einen weiteren Ordner nach Wallet-Dateien durchsuchen:",
|
||||
"wallets_add_folder_toggle": "+ Weiteren Ordner nach Wallets durchsuchen…",
|
||||
"wallets_button": "Wallets…",
|
||||
"wallets_col_addresses": "Adressen",
|
||||
"wallets_col_balance": "Guthaben",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "Wallet wird erstellt — der Node wird neu gestartet…",
|
||||
"wallets_current": "aktuell",
|
||||
"wallets_exists": "Eine Wallet mit diesem Namen existiert bereits.",
|
||||
"wallets_external_tt": "Außerhalb deines Datenverzeichnisses — mit Import hineinkopieren.",
|
||||
"wallets_folder_hint": "/pfad/zum/ordner mit .dat-Wallet-Dateien",
|
||||
"wallets_folder_invalid": "Dieser Ordner existiert nicht.",
|
||||
"wallets_import": "Importieren",
|
||||
|
||||
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "Activa",
|
||||
"wallets_add": "Agregar carpeta",
|
||||
"wallets_add_folder": "Escanear también otra carpeta en busca de archivos de cartera:",
|
||||
"wallets_add_folder_toggle": "+ Buscar wallets en otra carpeta…",
|
||||
"wallets_button": "Carteras…",
|
||||
"wallets_col_addresses": "Direcciones",
|
||||
"wallets_col_balance": "Saldo",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "Creando cartera — el nodo se reiniciará…",
|
||||
"wallets_current": "actual",
|
||||
"wallets_exists": "Ya existe una cartera con ese nombre.",
|
||||
"wallets_external_tt": "Fuera de tu directorio de datos — usa Importar para copiarlo.",
|
||||
"wallets_folder_hint": "/ruta/a/carpeta con archivos .dat de cartera",
|
||||
"wallets_folder_invalid": "Esa carpeta no existe.",
|
||||
"wallets_import": "Importar",
|
||||
|
||||
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "Actif",
|
||||
"wallets_add": "Ajouter le dossier",
|
||||
"wallets_add_folder": "Analyser aussi un autre dossier à la recherche de fichiers de portefeuille :",
|
||||
"wallets_add_folder_toggle": "+ Analyser un autre dossier pour les portefeuilles…",
|
||||
"wallets_button": "Portefeuilles…",
|
||||
"wallets_col_addresses": "Adresses",
|
||||
"wallets_col_balance": "Solde",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "Création du portefeuille — le nœud va redémarrer…",
|
||||
"wallets_current": "actuel",
|
||||
"wallets_exists": "Un portefeuille portant ce nom existe déjà.",
|
||||
"wallets_external_tt": "Hors de votre répertoire de données — Importer pour le copier.",
|
||||
"wallets_folder_hint": "/chemin/vers/le/dossier contenant les fichiers .dat",
|
||||
"wallets_folder_invalid": "Ce dossier n'existe pas.",
|
||||
"wallets_import": "Importer",
|
||||
|
||||
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "アクティブ",
|
||||
"wallets_add": "フォルダを追加",
|
||||
"wallets_add_folder": "ウォレットファイルを検索する別のフォルダを追加:",
|
||||
"wallets_add_folder_toggle": "+ 別のフォルダーをウォレット検索…",
|
||||
"wallets_button": "ウォレット…",
|
||||
"wallets_col_addresses": "アドレス",
|
||||
"wallets_col_balance": "残高",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "ウォレットを作成中 — ノードが再起動します…",
|
||||
"wallets_current": "現在",
|
||||
"wallets_exists": "その名前のウォレットは既に存在します。",
|
||||
"wallets_external_tt": "データディレクトリの外にあります — インポートでコピーします。",
|
||||
"wallets_folder_hint": "wallet .dat ファイルのあるフォルダのパス",
|
||||
"wallets_folder_invalid": "そのフォルダは存在しません。",
|
||||
"wallets_import": "インポート",
|
||||
|
||||
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "활성",
|
||||
"wallets_add": "폴더 추가",
|
||||
"wallets_add_folder": "지갑 파일을 검색할 폴더를 추가로 지정:",
|
||||
"wallets_add_folder_toggle": "+ 다른 폴더에서 지갑 검색…",
|
||||
"wallets_button": "지갑…",
|
||||
"wallets_col_addresses": "주소",
|
||||
"wallets_col_balance": "잔액",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "지갑을 만드는 중 — 노드가 재시작됩니다…",
|
||||
"wallets_current": "현재",
|
||||
"wallets_exists": "같은 이름의 지갑이 이미 존재합니다.",
|
||||
"wallets_external_tt": "데이터 디렉터리 외부에 있음 — 가져오기로 복사합니다.",
|
||||
"wallets_folder_hint": "wallet-*.dat 파일이 있는 폴더 경로",
|
||||
"wallets_folder_invalid": "해당 폴더가 존재하지 않습니다.",
|
||||
"wallets_import": "가져오기",
|
||||
|
||||
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "Ativa",
|
||||
"wallets_add": "Adicionar pasta",
|
||||
"wallets_add_folder": "Verificar também outra pasta em busca de arquivos de carteira:",
|
||||
"wallets_add_folder_toggle": "+ Procurar carteiras noutra pasta…",
|
||||
"wallets_button": "Carteiras…",
|
||||
"wallets_col_addresses": "Endereços",
|
||||
"wallets_col_balance": "Saldo",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "Criando carteira — o nó será reiniciado…",
|
||||
"wallets_current": "atual",
|
||||
"wallets_exists": "Já existe uma carteira com esse nome.",
|
||||
"wallets_external_tt": "Fora do seu diretório de dados — use Importar para copiá-lo.",
|
||||
"wallets_folder_hint": "/caminho/para/pasta com arquivos .dat de carteira",
|
||||
"wallets_folder_invalid": "Essa pasta não existe.",
|
||||
"wallets_import": "Importar",
|
||||
|
||||
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "Активен",
|
||||
"wallets_add": "Добавить папку",
|
||||
"wallets_add_folder": "Также сканировать другую папку на наличие файлов кошельков:",
|
||||
"wallets_add_folder_toggle": "+ Искать кошельки в другой папке…",
|
||||
"wallets_button": "Кошельки…",
|
||||
"wallets_col_addresses": "Адреса",
|
||||
"wallets_col_balance": "Баланс",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "Создание кошелька — узел будет перезапущен…",
|
||||
"wallets_current": "текущий",
|
||||
"wallets_exists": "Кошелёк с таким именем уже существует.",
|
||||
"wallets_external_tt": "Вне вашего каталога данных — «Импорт» скопирует его.",
|
||||
"wallets_folder_hint": "/путь/к/папке с файлами кошельков .dat",
|
||||
"wallets_folder_invalid": "Такой папки не существует.",
|
||||
"wallets_import": "Импорт",
|
||||
|
||||
@@ -1322,6 +1322,7 @@
|
||||
"wallets_active": "活跃",
|
||||
"wallets_add": "添加文件夹",
|
||||
"wallets_add_folder": "同时扫描另一个文件夹中的钱包文件:",
|
||||
"wallets_add_folder_toggle": "+ 扫描其他文件夹中的钱包…",
|
||||
"wallets_button": "钱包…",
|
||||
"wallets_col_addresses": "地址",
|
||||
"wallets_col_balance": "余额",
|
||||
@@ -1332,6 +1333,7 @@
|
||||
"wallets_creating": "正在创建钱包——节点将重启…",
|
||||
"wallets_current": "当前",
|
||||
"wallets_exists": "已存在同名钱包。",
|
||||
"wallets_external_tt": "位于数据目录之外 — 导入以将其复制进来。",
|
||||
"wallets_folder_hint": "/含 .dat 钱包文件的文件夹路径",
|
||||
"wallets_folder_invalid": "该文件夹不存在。",
|
||||
"wallets_import": "导入",
|
||||
|
||||
@@ -48,20 +48,39 @@ public:
|
||||
using namespace material;
|
||||
App* app = s_app;
|
||||
const float dp = Layout::dpiScale();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
if (s_needScan) { scan(); s_needScan = false; }
|
||||
|
||||
// FIXED cardHeight (not the auto-height overload): the table below sizes itself from
|
||||
// GetContentRegionAvail().y, which is only stable when the dialog's content child is
|
||||
// fixed-height. Auto-height makes that read self-referential and it diverges across a
|
||||
// monitor move (dpiScale jump), sliding the create/footer rows off-screen. See Manage
|
||||
// Portfolio (market_tab.cpp) for the same fixed-height pattern.
|
||||
// ---- #1 Content-sized card (bounded) -----------------------------------------------------
|
||||
// Size the card to the wallet count (up to kMaxVisibleRows, then the list scrolls) so a short
|
||||
// list doesn't float in a sea of empty space. Computed from the KNOWN row count, so it stays a
|
||||
// fixed-height dialog (no auto-height feedback — that slid content off-screen on a monitor move).
|
||||
const int kMaxVisibleRows = 8;
|
||||
const int visRows = std::min(std::max(1, (int)s_rows.size()), kMaxVisibleRows);
|
||||
const float tableRowH = ImGui::GetFrameHeight() + style.CellPadding.y * 2.0f;
|
||||
const float tableHdrH = ImGui::GetTextLineHeight() + style.CellPadding.y * 2.0f;
|
||||
const float tableH = tableHdrH + visRows * tableRowH + 4.0f * dp;
|
||||
|
||||
const float ctrlRow = ImGui::GetFrameHeightWithSpacing();
|
||||
const float capRow = Type().caption()->LegacySize + style.ItemSpacing.y;
|
||||
const float belowH = 3.0f * Layout::spacingSm()
|
||||
+ (capRow + ctrlRow) // Create label + input row
|
||||
+ (s_showAddFolder ? (capRow + ctrlRow) : capRow) // folder row OR toggle link
|
||||
+ (style.ItemSpacing.y + 1.0f) // separator
|
||||
+ ctrlRow // footer buttons
|
||||
+ 6.0f * style.ItemSpacing.y; // uncounted inter-item gaps
|
||||
const float headH = Type().h6()->LegacySize + Layout::spacingXs() // framework h6 title
|
||||
+ Type().caption()->LegacySize + Layout::spacingSm(); // intro + gap
|
||||
const float padV = 48.0f; // content-child padding (top+bottom) + margin
|
||||
const float cardH = (headH + tableH + belowH + padV) / dp;
|
||||
|
||||
OverlayDialogSpec ov;
|
||||
ov.title = TR("wallets_title");
|
||||
ov.p_open = &s_open;
|
||||
ov.style = OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 780.0f;
|
||||
ov.cardHeight = 620.0f;
|
||||
ov.cardHeight = cardH;
|
||||
ov.idSuffix = "wallets";
|
||||
if (BeginOverlayDialog(ov)) {
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("wallets_intro"));
|
||||
@@ -69,36 +88,52 @@ public:
|
||||
|
||||
const std::string active = app->settings() ? app->settings()->getActiveWalletFile() : "wallet.dat";
|
||||
|
||||
// Reserve room below the table for the create-wallet + add-folder rows (each a label +
|
||||
// an input row), the separator, and the footer buttons — computed from real widget
|
||||
// metrics so nothing clips at any DPI (a fixed px reserve was too small at 150%).
|
||||
const float rowH = ImGui::GetFrameHeightWithSpacing();
|
||||
const float lblH = Type().caption()->LegacySize + ImGui::GetStyle().ItemSpacing.y;
|
||||
const float belowH = 3.0f * Layout::spacingSm() + 2.0f * lblH + 3.0f * rowH + Layout::spacingMd();
|
||||
float tableH = ImGui::GetContentRegionAvail().y - belowH;
|
||||
if (tableH < 120.0f * dp) tableH = 120.0f * dp;
|
||||
|
||||
// ---- #2 Framed wallet list ------------------------------------------------------------
|
||||
const ImGuiTableFlags tflags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_BordersInnerH;
|
||||
ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_PadOuterX;
|
||||
if (ImGui::BeginTable("##wallets", 6, tflags, ImVec2(0, tableH))) {
|
||||
ImGui::TableSetupColumn(TR("wallets_col_name"), ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn(TR("wallets_col_size"), ImGuiTableColumnFlags_WidthFixed, 90.0f * dp);
|
||||
ImGui::TableSetupColumn(TR("wallets_col_addresses"), ImGuiTableColumnFlags_WidthFixed, 80.0f * dp);
|
||||
ImGui::TableSetupColumn(TR("wallets_col_addresses"), ImGuiTableColumnFlags_WidthFixed, 90.0f * dp);
|
||||
ImGui::TableSetupColumn(TR("wallets_col_balance"), ImGuiTableColumnFlags_WidthFixed, 130.0f * dp);
|
||||
ImGui::TableSetupColumn(TR("wallets_col_opened"), ImGuiTableColumnFlags_WidthFixed, 120.0f * dp);
|
||||
ImGui::TableSetupColumn("##action", ImGuiTableColumnFlags_WidthFixed, 110.0f * dp);
|
||||
ImGui::TableSetupColumn("##action", ImGuiTableColumnFlags_WidthFixed, 100.0f * dp);
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// ---- #3 Right-aligned headers for the numeric columns (Size / Addresses / Balance).
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
for (int c = 0; c < 6; ++c) {
|
||||
ImGui::TableSetColumnIndex(c);
|
||||
const char* cn = ImGui::TableGetColumnName(c);
|
||||
if (c == 5 || !cn || !cn[0]) continue;
|
||||
if (c >= 1 && c <= 3) {
|
||||
float w = ImGui::CalcTextSize(cn).x;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - w);
|
||||
ImGui::TextUnformatted(cn);
|
||||
} else {
|
||||
ImGui::TableHeader(cn);
|
||||
}
|
||||
}
|
||||
|
||||
auto rcell = [&](const std::string& s, bool muted) {
|
||||
float w = ImGui::CalcTextSize(s.c_str()).x;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - w);
|
||||
if (muted) ImGui::TextDisabled("%s", s.c_str()); else ImGui::TextUnformatted(s.c_str());
|
||||
};
|
||||
const char* kDash = "\xE2\x80\x94";
|
||||
|
||||
char nb[32];
|
||||
for (std::size_t i = 0; i < s_rows.size(); ++i) {
|
||||
const WalletRow& r = s_rows[i];
|
||||
const data::WalletIndexEntry* meta = app->walletIndex().find(r.fileName);
|
||||
const bool isCurrent = r.inDatadir && r.fileName == active;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
if (isCurrent) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, WithAlpha(Success(), 26));
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
// Name — + "current" badge, or a folder marker for out-of-datadir files.
|
||||
// Name (+ #4 current badge / #6 external-file info icon)
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(r.fileName.c_str());
|
||||
if (isCurrent) {
|
||||
@@ -106,38 +141,45 @@ public:
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(Success()), "%s", TR("wallets_current"));
|
||||
} else if (!r.inDatadir) {
|
||||
ImGui::SameLine(0, 8.0f * dp);
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()), "%s", ICON_MD_FOLDER);
|
||||
if (ImGui::IsItemHovered()) Tooltip("%s", r.dir.c_str());
|
||||
ImGui::PushFont(Type().iconSmall());
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()), "%s", ICON_MD_INFO);
|
||||
ImGui::PopFont();
|
||||
if (ImGui::IsItemHovered()) Tooltip("%s\n%s", TR("wallets_external_tt"), r.dir.c_str());
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(util::Platform::formatFileSize(static_cast<uint64_t>(r.sizeBytes)).c_str());
|
||||
rcell(util::Platform::formatFileSize(static_cast<uint64_t>(r.sizeBytes)), false);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (meta && meta->cachedAddressCount >= 0) ImGui::Text("%lld", meta->cachedAddressCount);
|
||||
else ImGui::TextDisabled("—");
|
||||
if (meta && meta->cachedAddressCount >= 0) { snprintf(nb, sizeof(nb), "%lld", (long long)meta->cachedAddressCount); rcell(nb, false); }
|
||||
else rcell(kDash, true);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (meta && meta->cachedBalance >= 0.0) ImGui::Text("%.4f", meta->cachedBalance);
|
||||
else ImGui::TextDisabled("—");
|
||||
if (meta && meta->cachedBalance >= 0.0) { snprintf(nb, sizeof(nb), "%.4f", meta->cachedBalance); rcell(nb, false); }
|
||||
else rcell(kDash, true);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (meta && meta->lastOpenedEpoch > 0)
|
||||
ImGui::TextUnformatted(util::formatTimeAgo(meta->lastOpenedEpoch).c_str());
|
||||
else ImGui::TextDisabled("%s", TR("wallets_never"));
|
||||
|
||||
// Action
|
||||
// ---- #4 Action column (Active as a chip; Open/Import as buttons) ---------------
|
||||
ImGui::TableNextColumn();
|
||||
if (isCurrent) {
|
||||
ImGui::TextDisabled("%s", TR("wallets_active"));
|
||||
const char* al = TR("wallets_active");
|
||||
ImVec2 ts = ImGui::CalcTextSize(al);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
float cx = 8.0f * dp, cy = 3.0f * dp;
|
||||
ImDrawList* cdl = ImGui::GetWindowDrawList();
|
||||
cdl->AddRectFilled(p, ImVec2(p.x + ts.x + cx * 2, p.y + ts.y + cy * 2), WithAlpha(Success(), 34), 4.0f * dp);
|
||||
cdl->AddText(ImVec2(p.x + cx, p.y + cy), Success(), al);
|
||||
ImGui::Dummy(ImVec2(ts.x + cx * 2, ts.y + cy * 2));
|
||||
} else if (r.inDatadir) {
|
||||
if (StyledButton(TR("wallets_open"), ImVec2(90.0f * dp, 0))) {
|
||||
app->switchToWallet(r.fileName);
|
||||
s_open = false; // node restarts; the main UI shows the reconnect overlay
|
||||
s_open = false;
|
||||
}
|
||||
} else {
|
||||
// Out-of-datadir wallets can't be loaded directly (the daemon rejects paths).
|
||||
// Import = copy into the datadir under a wallet-*.dat name, then switch to it.
|
||||
if (StyledButton(TR("wallets_import"), ImVec2(90.0f * dp, 0)))
|
||||
importAndOpen(app, r);
|
||||
if (ImGui::IsItemHovered()) Tooltip("%s", TR("wallets_import_tt"));
|
||||
@@ -149,7 +191,7 @@ public:
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// Create a fresh named wallet — the daemon mints it (with a seed phrase) on first load.
|
||||
// ---- #5 Create a new wallet (primary) -------------------------------------------------
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("wallets_new_label"));
|
||||
ImGui::SetNextItemWidth(-150.0f * dp);
|
||||
ImGui::InputTextWithHint("##newWalletName", TR("wallets_new_hint"), s_newName, sizeof(s_newName));
|
||||
@@ -164,33 +206,51 @@ public:
|
||||
} else {
|
||||
s_newName[0] = '\0';
|
||||
Notifications::instance().info(TR("wallets_creating"));
|
||||
app->switchToWallet(name); // daemon creates it on start (-usemnemonic=1 -> seed-backed)
|
||||
app->switchToWallet(name);
|
||||
s_open = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// Add a folder to also scan for wallet files (no native picker — a validated path input).
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("wallets_add_folder"));
|
||||
ImGui::SetNextItemWidth(-150.0f * dp);
|
||||
ImGui::InputTextWithHint("##walletFolder", TR("wallets_folder_hint"), s_newFolder, sizeof(s_newFolder));
|
||||
ImGui::SameLine();
|
||||
if (StyledButton(TR("wallets_add"), ImVec2(120.0f * dp, 0))) {
|
||||
std::error_code ec;
|
||||
std::string dir = s_newFolder;
|
||||
if (!dir.empty() && std::filesystem::is_directory(dir, ec)) {
|
||||
if (app->walletIndex().addExtraFolder(dir)) app->walletIndex().save();
|
||||
s_newFolder[0] = '\0';
|
||||
s_needScan = true;
|
||||
} else {
|
||||
Notifications::instance().warning(TR("wallets_folder_invalid"));
|
||||
// ---- #5 Add-folder tucked behind a toggle so it doesn't compete with Create -----------
|
||||
if (!s_showAddFolder) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, OnSurfaceMedium());
|
||||
ImGui::TextUnformatted(TR("wallets_add_folder_toggle"));
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemHovered()) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (ImGui::IsItemClicked()) s_showAddFolder = true;
|
||||
} else {
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("wallets_add_folder"));
|
||||
ImGui::SetNextItemWidth(-150.0f * dp);
|
||||
ImGui::InputTextWithHint("##walletFolder", TR("wallets_folder_hint"), s_newFolder, sizeof(s_newFolder));
|
||||
ImGui::SameLine();
|
||||
if (StyledButton(TR("wallets_add"), ImVec2(120.0f * dp, 0))) {
|
||||
std::error_code ec;
|
||||
std::string dir = s_newFolder;
|
||||
if (!dir.empty() && std::filesystem::is_directory(dir, ec)) {
|
||||
if (app->walletIndex().addExtraFolder(dir)) app->walletIndex().save();
|
||||
s_newFolder[0] = '\0';
|
||||
s_needScan = true;
|
||||
} else {
|
||||
Notifications::instance().warning(TR("wallets_folder_invalid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::Separator();
|
||||
if (StyledButton(TR("refresh"), ImVec2(110.0f * dp, 0))) s_needScan = true;
|
||||
// ---- #6 Refresh: icon-only, de-emphasized next to the text actions --------------------
|
||||
{
|
||||
float bh = ImGui::GetFrameHeight();
|
||||
IconButtonStyle rst;
|
||||
rst.color = OnSurfaceMedium();
|
||||
rst.hoverBg = StateHover();
|
||||
rst.bgRounding = 4.0f * dp;
|
||||
rst.tooltip = TR("refresh");
|
||||
if (IconButton("##walletsRefresh", ICON_MD_REFRESH, Type().iconSmall(), ImVec2(bh, bh), rst))
|
||||
s_needScan = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (StyledButton(TR("wallets_reveal"), ImVec2(150.0f * dp, 0)))
|
||||
util::Platform::openFolder(util::Platform::getDragonXDataDir());
|
||||
@@ -279,6 +339,7 @@ private:
|
||||
static inline bool s_needScan = false;
|
||||
static inline char s_newFolder[512] = "";
|
||||
static inline char s_newName[128] = "";
|
||||
static inline bool s_showAddFolder = false;
|
||||
static inline std::vector<WalletRow> s_rows;
|
||||
};
|
||||
|
||||
|
||||
@@ -267,6 +267,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["wallets_import_hint"] = "Import to open";
|
||||
strings_["wallets_import_tt"] = "This wallet is outside the data directory. Opening it will copy it in first (coming soon).";
|
||||
strings_["wallets_add_folder"] = "Also scan another folder for wallet files:";
|
||||
strings_["wallets_add_folder_toggle"] = "+ Scan another folder for wallets\xE2\x80\xA6";
|
||||
strings_["wallets_external_tt"] = "Outside your data directory \xE2\x80\x94 Import to copy it in.";
|
||||
strings_["wallets_folder_hint"] = "/path/to/folder with wallet .dat files";
|
||||
strings_["wallets_add"] = "Add folder";
|
||||
strings_["wallets_folder_invalid"] = "That folder doesn't exist.";
|
||||
|
||||
Reference in New Issue
Block a user