mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 17:27:19 +02:00
Show each mod's size in the mod manager
Show each mod's size in the mod manager
A mod's size was only visible in the package overview and the install
prompt, so there was no telling from either list what a mod costs to
download or how much room the installed ones take.
- MY MODS: each row carries the size of the mod's folder on disk after
the version ("v1.0.1 / 410 MB / by anton / 1 map"), and so does the
line under the title in a mod's overview. The folders are measured
when the Mods folder is scanned, so the figure follows an install, an
update or Refresh. The game's merged patch hard links a mod's archives
rather than copying them, so this is also what removing the mod gives
back.
- GET MODS: each row carries the download size Thunderstore reports for
the latest version, in the same place ("v1.0.0 / 101 MB / 0 downloads
/ 2026-10-04"), as the package overview already did.
- size_text moves out of gui_mods_browse.cpp's file-local namespace so
both pages format a size the same way.
The two figures are not the same thing: one is the unpacked folder, the
other the zip, so a mod reads a little larger once installed.
This commit is contained in:
1 parent
04e50f7dee
commit
9c03edda34
3 files changed
+38
-11
No files matched your search
@@ -379,6 +379,9 @@ struct ModsPanel {
|
||||
bool scanned{};
|
||||
fs::path root;
|
||||
mods::ModList list;
|
||||
// What each installed mod's folder holds on disk, by folder name; measured
|
||||
// when the folder is scanned.
|
||||
std::map<std::string, std::uint64_t, std::less<>> sizes;
|
||||
int selected{-1}; // the mod whose overview is open
|
||||
bool overview{}; // the overview popup is showing
|
||||
int tab{}; // 0 MY MODS, 1 GET MODS
|
||||
@@ -414,6 +417,8 @@ void start_install(ModsPanel& panel, const fs::path& source, bool replace);
|
||||
|
||||
// ---------------------------------------------------------------- Thunderstore (gui_mods_browse.cpp)
|
||||
|
||||
// "1.27 GB", "263 MB", "12 KB": a mod's size, on both pages.
|
||||
std::string size_text(std::uint64_t bytes);
|
||||
// Starts a listing fetch when none ran yet, or when `force`.
|
||||
void refresh_listing(ModsPanel& panel, double time, bool force = false);
|
||||
// Takes over a fetch the worker finished; call once a frame.
|
||||
|
||||
+24
-3
@@ -80,9 +80,28 @@ fs::path pick(HWND owner, bool folder) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string summary(const mods::Mod& mod) {
|
||||
// Everything in the mod's folder, as Explorer would count it. The game's merged
|
||||
// patch hard links a mod's archives rather than copying them, so this is also
|
||||
// what removing the mod gives back.
|
||||
std::uint64_t folder_size(const fs::path& directory) {
|
||||
std::uint64_t total{};
|
||||
std::error_code error;
|
||||
for (fs::recursive_directory_iterator it(directory, fs::directory_options::skip_permission_denied, error), end;
|
||||
it != end && !error; it.increment(error)) {
|
||||
std::error_code ignored;
|
||||
if (it->is_regular_file(ignored) && !ignored) {
|
||||
const auto size = it->file_size(ignored);
|
||||
if (!ignored) total += size;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
std::string summary(const ModsPanel& panel, const mods::Mod& mod) {
|
||||
std::vector<std::string> parts;
|
||||
if (!mod.version.empty()) parts.push_back("v" + mod.version);
|
||||
if (const auto size = panel.sizes.find(mod.name); size != panel.sizes.end() && size->second)
|
||||
parts.push_back(size_text(size->second));
|
||||
if (!mod.author.empty()) parts.push_back("by " + mod.author);
|
||||
if (!mod.levels.empty()) parts.push_back(mod.levels.size() == 1 ? "1 map" : std::to_string(mod.levels.size()) + " maps");
|
||||
else if (mod.provides_layout) parts.push_back("game data");
|
||||
@@ -169,7 +188,7 @@ void installed_row(const Fonts& fonts, ModsPanel& panel, const thunderstore::Ins
|
||||
const auto title = std::to_string(index + 1) + ". " + mod.title;
|
||||
draw->AddText(fonts.bold, fonts.bold->FontSize, ImVec2(text_x, start.y + S(16)),
|
||||
entry.enabled ? color::text : color::muted, title.c_str());
|
||||
if (const auto detail = summary(mod); !detail.empty())
|
||||
if (const auto detail = summary(panel, mod); !detail.empty())
|
||||
draw->AddText(fonts.body, fonts.body->FontSize, ImVec2(text_x, start.y + S(40)), color::muted, detail.c_str());
|
||||
|
||||
// The widgets that sit on the row, over its Selectable.
|
||||
@@ -316,7 +335,7 @@ void mod_overview(const Fonts& fonts, ModsPanel& panel, const thunderstore::Inst
|
||||
ImGui::TextUnformatted(mod.title.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::PopFont();
|
||||
ImGui::TextDisabled("%s", summary(mod).c_str());
|
||||
ImGui::TextDisabled("%s", summary(panel, mod).c_str());
|
||||
ImGui::Spacing();
|
||||
if (update) {
|
||||
ImGui::BeginDisabled(installing);
|
||||
@@ -463,6 +482,8 @@ void refresh_mods(Launcher& launcher, ModsPanel& panel) {
|
||||
void scan(ModsPanel& panel, const launcher_app::Session& session) {
|
||||
panel.root = launcher_mods::mods_root(session.paths.directory);
|
||||
panel.list = mods::scan_mods(panel.root.parent_path());
|
||||
panel.sizes.clear();
|
||||
for (const auto& entry : panel.list.entries) panel.sizes[entry.mod.name] = folder_size(entry.mod.directory);
|
||||
panel.scanned = true;
|
||||
if (panel.selected >= static_cast<int>(panel.list.entries.size())) panel.selected = -1;
|
||||
}
|
||||
|
||||
@@ -137,12 +137,6 @@ std::string count_text(std::uint64_t value) {
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
std::string size_text(std::uint64_t bytes) {
|
||||
if (bytes >= 1024ull * 1024 * 1024) return std::format("{:.2f} GB", static_cast<double>(bytes) / (1024.0 * 1024 * 1024));
|
||||
if (bytes >= 1024ull * 1024) return std::format("{:.0f} MB", static_cast<double>(bytes) / (1024.0 * 1024));
|
||||
return std::format("{} KB", std::max<std::uint64_t>(1, bytes / 1024));
|
||||
}
|
||||
|
||||
std::string date_text(const std::string& iso) { return iso.substr(0, std::min<std::size_t>(iso.size(), 10)); }
|
||||
|
||||
void set_activity(ModsPanel& panel, std::string text) {
|
||||
@@ -252,6 +246,12 @@ void mod_icon(ModsPanel& panel, const thunderstore::Package* package, ImVec2 pos
|
||||
draw->AddRectFilled(position, end, rgba(255, 255, 255, 0.06f), S(4));
|
||||
}
|
||||
|
||||
std::string size_text(std::uint64_t bytes) {
|
||||
if (bytes >= 1024ull * 1024 * 1024) return std::format("{:.2f} GB", static_cast<double>(bytes) / (1024.0 * 1024 * 1024));
|
||||
if (bytes >= 1024ull * 1024) return std::format("{:.0f} MB", static_cast<double>(bytes) / (1024.0 * 1024));
|
||||
return std::format("{} KB", std::max<std::uint64_t>(1, bytes / 1024));
|
||||
}
|
||||
|
||||
void refresh_listing(ModsPanel& panel, double time, bool force) {
|
||||
auto& store = panel.store;
|
||||
// Once per launch, unless asked; a failed fetch retries after a minute.
|
||||
@@ -569,8 +569,9 @@ void browse_page(Launcher& launcher, const Fonts& fonts, ModsPanel& panel, float
|
||||
draw->AddText(fonts.body, fonts.body->FontSize, ImVec2(text_x, start.y + S(32)), color::text, line.c_str(),
|
||||
nullptr, 0, &clip);
|
||||
draw->AddText(fonts.caption, fonts.caption->FontSize, ImVec2(text_x, start.y + S(55)), color::muted,
|
||||
std::format("v{} / {} downloads / {}", version.number, count_text(package.downloads),
|
||||
date_text(package.date_updated)).c_str());
|
||||
std::format("v{}{} / {} downloads / {}", version.number,
|
||||
version.file_size ? " / " + size_text(version.file_size) : std::string(),
|
||||
count_text(package.downloads), date_text(package.date_updated)).c_str());
|
||||
ImGui::PopID();
|
||||
});
|
||||
ImGui::EndDisabled();
|
||||
|
||||
Reference in new issue
Block a user