feat(wallets): fixed max-height list + manage scanned folders
Two wallets-modal changes: - The wallet list now always renders at its max height (kMaxVisibleRows) instead of shrinking to the wallet count, so the modal is a consistent size whether you have one wallet or many — fewer rows leave empty space, more than 7 scroll. - Add a "Scanned folders" manager: each user-added scan folder is listed with a control to stop scanning it (removeExtraFolder + save + re-scan so its wallets drop out). Paths front-elide to keep the identifying leaf visible, with a full-path tooltip. The card-height math reserves the manager's rows so nothing clips. Two new i18n keys (wallets_scanned_folders / wallets_remove_folder), translated across all 8 languages; CJK subset rebuilt. Verified on the sweep at 1.0 and 1.5x DPI: max-height list on both the few- and many-wallet surfaces, the folder manager renders and stays clear of the footer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "Diese Wallet konnte nicht an ihrem Ort geöffnet werden. Sie liegt vermutlich auf einem anderen Laufwerk als dein Datenverzeichnis — verschiebe sie auf dasselbe Laufwerk (unter Windows erlaubt auch der aktivierte Entwicklermodus laufwerkübergreifende Verknüpfungen).",
|
||||
"wallets_open_folder": "Ordnerpfad öffnen",
|
||||
"wallets_open_inplace_tt": "Öffnet diese Wallet an ihrem Ort — im Datenverzeichnis verlinkt (keine Kopie)",
|
||||
"wallets_remove_folder": "Diesen Ordner nicht mehr durchsuchen",
|
||||
"wallets_reveal": "Ordner anzeigen",
|
||||
"wallets_scanned_folders": "Durchsuchte Ordner:",
|
||||
"wallets_sort_addresses": "Adressen",
|
||||
"wallets_sort_asc": "Aufsteigend (ältestes / wenigste / kleinstes zuerst)",
|
||||
"wallets_sort_by": "Sortieren:",
|
||||
|
||||
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "No se pudo abrir esta cartera en su ubicación. Probablemente está en una unidad distinta a tu directorio de datos: muévela a la misma unidad (en Windows, activar el Modo de desarrollador también permite enlazar entre unidades).",
|
||||
"wallets_open_folder": "Abrir ubicación de la carpeta",
|
||||
"wallets_open_inplace_tt": "Abre esta cartera donde está, enlazándola al directorio de datos (sin copiar)",
|
||||
"wallets_remove_folder": "Dejar de escanear esta carpeta",
|
||||
"wallets_reveal": "Mostrar carpeta",
|
||||
"wallets_scanned_folders": "Carpetas escaneadas:",
|
||||
"wallets_sort_addresses": "Direcciones",
|
||||
"wallets_sort_asc": "Ascendente (más antiguo / menos / más pequeño primero)",
|
||||
"wallets_sort_by": "Ordenar:",
|
||||
|
||||
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "Impossible d'ouvrir ce portefeuille à son emplacement. Il se trouve probablement sur un lecteur différent de votre répertoire de données — déplacez-le sur le même lecteur (sous Windows, activer le mode développeur permet aussi de créer des liens entre lecteurs).",
|
||||
"wallets_open_folder": "Ouvrir l'emplacement du dossier",
|
||||
"wallets_open_inplace_tt": "Ouvre ce portefeuille à son emplacement — lié au répertoire de données (sans copie)",
|
||||
"wallets_remove_folder": "Ne plus analyser ce dossier",
|
||||
"wallets_reveal": "Afficher le dossier",
|
||||
"wallets_scanned_folders": "Dossiers analysés :",
|
||||
"wallets_sort_addresses": "Adresses",
|
||||
"wallets_sort_asc": "Croissant (plus ancien / moins / plus petit d'abord)",
|
||||
"wallets_sort_by": "Trier :",
|
||||
|
||||
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "このウォレットをその場で開けませんでした。データディレクトリとは別のドライブにある可能性があります — 同じドライブに移動してください(Windows では開発者モードを有効にするとドライブ間のリンクも可能になります)。",
|
||||
"wallets_open_folder": "フォルダーの場所を開く",
|
||||
"wallets_open_inplace_tt": "このウォレットをその場で開きます — データディレクトリにリンク(コピーなし)",
|
||||
"wallets_remove_folder": "このフォルダーのスキャンを停止",
|
||||
"wallets_reveal": "フォルダを開く",
|
||||
"wallets_scanned_folders": "スキャン対象フォルダー:",
|
||||
"wallets_sort_addresses": "アドレス",
|
||||
"wallets_sort_asc": "昇順(古い/少ない/小さい順)",
|
||||
"wallets_sort_by": "並べ替え:",
|
||||
|
||||
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "이 지갑을 제자리에서 열 수 없습니다. 데이터 디렉터리와 다른 드라이브에 있을 가능성이 높습니다 — 같은 드라이브로 옮기세요 (Windows에서는 개발자 모드를 켜면 드라이브 간 링크도 가능합니다).",
|
||||
"wallets_open_folder": "폴더 위치 열기",
|
||||
"wallets_open_inplace_tt": "이 지갑을 있는 자리에서 엽니다 — 데이터 디렉터리에 링크 (복사 없음)",
|
||||
"wallets_remove_folder": "이 폴더 스캔 중지",
|
||||
"wallets_reveal": "폴더 열기",
|
||||
"wallets_scanned_folders": "스캔한 폴더:",
|
||||
"wallets_sort_addresses": "주소",
|
||||
"wallets_sort_asc": "오름차순 (오래된/적은/작은 순)",
|
||||
"wallets_sort_by": "정렬:",
|
||||
|
||||
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "Não foi possível abrir esta carteira no lugar. Provavelmente está em uma unidade diferente do seu diretório de dados — mova-a para a mesma unidade (no Windows, ativar o Modo de Desenvolvedor também permite vincular entre unidades).",
|
||||
"wallets_open_folder": "Abrir local da pasta",
|
||||
"wallets_open_inplace_tt": "Abre esta carteira onde está — vinculada ao diretório de dados (sem cópia)",
|
||||
"wallets_remove_folder": "Parar de verificar esta pasta",
|
||||
"wallets_reveal": "Mostrar pasta",
|
||||
"wallets_scanned_folders": "Pastas verificadas:",
|
||||
"wallets_sort_addresses": "Endereços",
|
||||
"wallets_sort_asc": "Crescente (mais antigo / menos / menor primeiro)",
|
||||
"wallets_sort_by": "Ordenar:",
|
||||
|
||||
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "Не удалось открыть этот кошелёк на месте. Вероятно, он на другом диске, чем каталог данных — переместите его на тот же диск (в Windows включённый режим разработчика также позволяет создавать ссылки между дисками).",
|
||||
"wallets_open_folder": "Открыть расположение папки",
|
||||
"wallets_open_inplace_tt": "Открывает этот кошелёк на месте — по ссылке в каталоге данных (без копирования)",
|
||||
"wallets_remove_folder": "Больше не сканировать эту папку",
|
||||
"wallets_reveal": "Открыть папку",
|
||||
"wallets_scanned_folders": "Просканированные папки:",
|
||||
"wallets_sort_addresses": "Адреса",
|
||||
"wallets_sort_asc": "По возрастанию (сначала старые / меньше / меньший)",
|
||||
"wallets_sort_by": "Сортировка:",
|
||||
|
||||
@@ -1356,7 +1356,9 @@
|
||||
"wallets_open_failed": "无法在原位置打开此钱包。它可能与数据目录位于不同的驱动器上 — 请将其移动到同一驱动器(在 Windows 上,启用开发者模式也可跨驱动器链接)。",
|
||||
"wallets_open_folder": "打开文件夹位置",
|
||||
"wallets_open_inplace_tt": "在原位置打开此钱包 — 链接到数据目录(不复制)",
|
||||
"wallets_remove_folder": "停止扫描此文件夹",
|
||||
"wallets_reveal": "打开文件夹",
|
||||
"wallets_scanned_folders": "已扫描的文件夹:",
|
||||
"wallets_sort_addresses": "地址",
|
||||
"wallets_sort_asc": "升序(最早/最少/最小优先)",
|
||||
"wallets_sort_by": "排序:",
|
||||
|
||||
@@ -2232,6 +2232,18 @@ TRANSLATIONS = {
|
||||
"ja": "データディレクトリの外 —「開く」はその場でリンクします(コピーなし)。",
|
||||
"ko": "데이터 디렉터리 밖 — '열기'는 제자리에 링크합니다 (복사 없음)."
|
||||
},
|
||||
"wallets_scanned_folders": {
|
||||
"es": "Carpetas escaneadas:", "de": "Durchsuchte Ordner:",
|
||||
"fr": "Dossiers analysés :", "pt": "Pastas verificadas:",
|
||||
"ru": "Просканированные папки:", "zh": "已扫描的文件夹:",
|
||||
"ja": "スキャン対象フォルダー:", "ko": "스캔한 폴더:"
|
||||
},
|
||||
"wallets_remove_folder": {
|
||||
"es": "Dejar de escanear esta carpeta", "de": "Diesen Ordner nicht mehr durchsuchen",
|
||||
"fr": "Ne plus analyser ce dossier", "pt": "Parar de verificar esta pasta",
|
||||
"ru": "Больше не сканировать эту папку", "zh": "停止扫描此文件夹",
|
||||
"ja": "このフォルダーのスキャンを停止", "ko": "이 폴더 스캔 중지"
|
||||
},
|
||||
}
|
||||
|
||||
def main():
|
||||
|
||||
@@ -71,7 +71,9 @@ public:
|
||||
const int kMaxVisibleRows = 7;
|
||||
ImFont* nameFont = Type().subtitle1();
|
||||
ImFont* metaFont = Type().caption();
|
||||
const int visRows = std::min(std::max(1, (int)s_rows.size()), kMaxVisibleRows);
|
||||
// The list is always sized to its MAX height (kMaxVisibleRows) for a consistent modal size — it
|
||||
// does not shrink to fit a few wallets; fewer rows leave empty space, more than 7 scroll.
|
||||
const int visRows = kMaxVisibleRows;
|
||||
const float cardPadY = Layout::spacingMd(); // roomier cards (was spacingSm)
|
||||
const float walRowH = cardPadY * 2.0f + nameFont->LegacySize + Layout::spacingSm() + metaFont->LegacySize;
|
||||
const float cardGap = Layout::spacingMd(); // more breathing room between wallet cards
|
||||
@@ -81,9 +83,14 @@ public:
|
||||
|
||||
const float ctrlRow = ImGui::GetFrameHeightWithSpacing();
|
||||
const float capRow = Type().caption()->LegacySize + style.ItemSpacing.y;
|
||||
const int numScanFolders = app ? (int)app->walletIndex().extraFolders().size() : 0;
|
||||
const float foldersH = numScanFolders > 0
|
||||
? (capRow + (float)numScanFolders * ctrlRow + Layout::spacingXs()) // "Scanned folders:" + one row each
|
||||
: 0.0f;
|
||||
const float belowH = 4.0f * Layout::spacingSm()
|
||||
+ (capRow + ctrlRow) // Create label + input row
|
||||
+ ctrlRow // full-width "scan folder" button
|
||||
+ foldersH // scanned-folders manager
|
||||
+ (style.ItemSpacing.y + 1.0f) // separator
|
||||
+ ctrlRow // footer buttons
|
||||
+ 6.0f * style.ItemSpacing.y; // uncounted inter-item gaps
|
||||
@@ -422,6 +429,51 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Manage scanned folders: list each user-added folder with a control to stop scanning it.
|
||||
// (The datadir is always scanned and isn't listed here — only the folders the user added.) -----
|
||||
{
|
||||
const auto& scanFolders = app->walletIndex().extraFolders();
|
||||
if (!scanFolders.empty()) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("wallets_scanned_folders"));
|
||||
ImFont* pf = Type().caption();
|
||||
const float bh2 = ImGui::GetFrameHeight();
|
||||
// Front-elide a long path so its identifying tail (the leaf folder) stays visible.
|
||||
auto elideFront = [&](std::string s, float maxW) {
|
||||
auto w = [&](const std::string& t){ return pf->CalcTextSizeA(pf->LegacySize, FLT_MAX, 0, t.c_str()).x; };
|
||||
if (w(s) <= maxW) return s;
|
||||
while (s.size() > 1 && w("\xE2\x80\xA6" + s) > maxW) {
|
||||
s.erase(s.begin());
|
||||
while (!s.empty() && (static_cast<unsigned char>(s.front()) & 0xC0) == 0x80) s.erase(s.begin());
|
||||
}
|
||||
return "\xE2\x80\xA6" + s;
|
||||
};
|
||||
std::string toRemove;
|
||||
for (const auto& folder : scanFolders) {
|
||||
ImGui::PushID(folder.c_str());
|
||||
IconButtonStyle rm;
|
||||
rm.color = OnSurfaceMedium();
|
||||
rm.hoverColor = Error();
|
||||
rm.hoverBg = StateHover();
|
||||
rm.bgRounding = 4.0f * dp;
|
||||
rm.tooltip = TR("wallets_remove_folder");
|
||||
if (IconButton("##rmScanFolder", ICON_MD_CLOSE, Type().iconSmall(), ImVec2(bh2, bh2), rm))
|
||||
toRemove = folder;
|
||||
ImGui::SameLine(0, Layout::spacingSm());
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const float textW = listW - bh2 - 2.0f * Layout::spacingSm();
|
||||
Type().textColored(TypeStyle::Caption, OnSurface(), elideFront(folder, textW).c_str());
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", folder.c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (!toRemove.empty()) {
|
||||
app->walletIndex().removeExtraFolder(toRemove);
|
||||
app->walletIndex().save();
|
||||
s_needScan = true; // re-scan so wallets from the dropped folder disappear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::Separator();
|
||||
// ---- Refresh: icon-only, de-emphasized next to the text actions -----------------------
|
||||
|
||||
@@ -287,6 +287,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["wallets_never"] = "Never opened";
|
||||
strings_["wallets_external_tt"] = "Outside your data directory \xE2\x80\x94 Open links it in place (no copy).";
|
||||
strings_["wallets_scan_folder"] = "Scan another folder for wallets\xE2\x80\xA6";
|
||||
strings_["wallets_scanned_folders"] = "Scanned folders:";
|
||||
strings_["wallets_remove_folder"] = "Stop scanning this folder";
|
||||
strings_["wallets_folder_invalid"] = "That folder doesn't exist.";
|
||||
strings_["wallets_new_label"] = "Create a new wallet:";
|
||||
strings_["wallets_new_hint"] = "Name (e.g. savings)";
|
||||
|
||||
Reference in New Issue
Block a user