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:
@@ -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