impr: Add auto backups to crash restore popup

This commit is contained in:
WerWolv 2025-11-29 09:06:49 +01:00
parent 95ff52b025
commit 2c7da65242
No known key found for this signature in database
GPG Key ID: B5B84C2ACF708377
4 changed files with 106 additions and 49 deletions

View File

@ -5,6 +5,7 @@
#include <nlohmann/json.hpp>
#include <wolv/io/fs.hpp>
#include <hex/ui/popup.hpp>
namespace hex::plugin::builtin::recent {
@ -53,6 +54,31 @@ namespace hex::plugin::builtin::recent {
};
class PopupAutoBackups : public Popup<PopupAutoBackups> {
private:
struct BackupEntry {
std::string displayName;
std::fs::path path;
std::tm time;
bool operator<(const BackupEntry& other) const {
auto a = this->time;
auto b = other.time;
return std::mktime(&a) < std::mktime(&b);
}
};
public:
PopupAutoBackups();
void drawContent() override;
[[nodiscard]] ImGuiWindowFlags getFlags() const override;
static std::vector<BackupEntry> getAutoBackups();
private:
std::vector<BackupEntry> m_backups;
};
void registerEventHandlers();
/**

View File

@ -170,12 +170,12 @@ namespace hex::plugin::builtin {
// Request, as this puts us into a project state
RequestUpdateWindowTitle::post();
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.save_project.name");
EventProjectSaved::post();
}
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.save_project.name");
EventProjectSaved::post();
return result;
}

View File

@ -1,3 +1,5 @@
#include <content/recent.hpp>
#include <imgui.h>
#include <imgui_internal.h>
@ -17,7 +19,6 @@
#include <wolv/utils/guards.hpp>
#include <wolv/utils/string.hpp>
#include <content/recent.hpp>
#include <toasts/toast_notification.hpp>
#include <fonts/vscode_icons.hpp>
@ -36,55 +37,68 @@ namespace hex::plugin::builtin::recent {
std::list<RecentEntry> s_recentEntries;
std::atomic_bool s_autoBackupsFound = false;
}
class PopupAutoBackups : public Popup<PopupAutoBackups> {
private:
struct BackupEntry {
std::string displayName;
std::fs::path path;
};
public:
PopupAutoBackups() : Popup("hex.builtin.welcome.start.recent.auto_backups", true, true) {
for (const auto &backupPath : paths::Backups.read()) {
for (const auto &entry : std::fs::directory_iterator(backupPath)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexproj") {
wolv::io::File backupFile(entry.path(), wolv::io::File::Mode::Read);
std::vector<PopupAutoBackups::BackupEntry> PopupAutoBackups::getAutoBackups() {
std::set<BackupEntry> result;
m_backups.emplace_back(
fmt::format("hex.builtin.welcome.start.recent.auto_backups.backup"_lang, fmt::gmtime(backupFile.getFileInfo()->st_ctime)),
entry.path()
);
}
}
for (const auto &backupPath : paths::Backups.read()) {
for (const auto &entry : std::fs::directory_iterator(backupPath)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexproj") {
// auto_backup.{:%y%m%d_%H%M%S}.hexproj
auto fileName = wolv::util::toUTF8String(entry.path().stem());
if (!fileName.starts_with("auto_backup."))
continue;
wolv::io::File backupFile(entry.path(), wolv::io::File::Mode::Read);
auto creationTimeString = fileName.substr(12);
std::chrono::sys_seconds utcSeconds;
std::istringstream ss(creationTimeString);
ss >> std::chrono::parse("%y%m%d_%H%M%S", utcSeconds);
if (ss.fail())
continue;
auto utcTime = std::chrono::system_clock::to_time_t(utcSeconds);
std::tm localTm = *std::localtime(&utcTime);
result.emplace(
fmt::format("hex.builtin.welcome.start.recent.auto_backups.backup"_lang, localTm),
entry.path(),
localTm
);
}
}
}
return { result.begin(), result.end() };
}
PopupAutoBackups::PopupAutoBackups() : Popup("hex.builtin.welcome.start.recent.auto_backups", true, true) {
m_backups = getAutoBackups();
}
void PopupAutoBackups::drawContent() {
if (ImGui::BeginTable("AutoBackups", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5))) {
for (const auto &backup : m_backups | std::views::reverse | std::views::take(10)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(backup.displayName.c_str())) {
ProjectFile::load(backup.path);
Popup::close();
}
}
void drawContent() override {
if (ImGui::BeginTable("AutoBackups", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5))) {
for (const auto &backup : m_backups | std::views::reverse | std::views::take(10)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(backup.displayName.c_str())) {
ProjectFile::load(backup.path);
Popup::close();
}
}
ImGui::EndTable();
}
ImGui::EndTable();
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
this->close();
}
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
return ImGuiWindowFlags_AlwaysAutoResize;
}
private:
std::vector<BackupEntry> m_backups;
};
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
this->close();
}
[[nodiscard]] ImGuiWindowFlags PopupAutoBackups::getFlags() const {
return ImGuiWindowFlags_AlwaysAutoResize;
}
void saveCurrentProjectAsRecent() {

View File

@ -56,13 +56,15 @@ namespace hex::plugin::builtin {
class PopupRestoreBackup : public Popup<PopupRestoreBackup> {
private:
std::fs::path m_logFilePath;
bool m_hasAutoBackups;
std::function<void()> m_restoreCallback;
std::function<void()> m_deleteCallback;
bool m_reportError = true;
public:
PopupRestoreBackup(std::fs::path logFilePath, const std::function<void()> &restoreCallback, const std::function<void()> &deleteCallback)
PopupRestoreBackup(std::fs::path logFilePath, bool hasAutoBackups, const std::function<void()> &restoreCallback, const std::function<void()> &deleteCallback)
: Popup("hex.builtin.popup.safety_backup.title"),
m_logFilePath(std::move(logFilePath)),
m_hasAutoBackups(hasAutoBackups),
m_restoreCallback(restoreCallback),
m_deleteCallback(deleteCallback) {
@ -123,6 +125,12 @@ namespace hex::plugin::builtin {
this->close();
}
if (m_hasAutoBackups) {
if (ImGui::Button("Show Automatic Backups", ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
recent::PopupAutoBackups::open();
}
}
}
};
@ -739,6 +747,10 @@ namespace hex::plugin::builtin {
auto backupFilePathOld = path / BackupFileName;
backupFilePathOld.replace_extension(".hexproj.old");
bool autoBackupsEnabled = ContentRegistry::Settings::read<int>("hex.builtin.setting.general", "hex.builtin.setting.general.auto_backup_time", 0) > 0;
auto autoBackups = recent::PopupAutoBackups::getAutoBackups();
bool hasAutoBackups = autoBackupsEnabled && !autoBackups.empty();
bool hasBackupFile = wolv::io::fs::exists(backupFilePath);
if (!hasProject && !hasBackupFile) {
@ -762,13 +774,16 @@ namespace hex::plugin::builtin {
PopupRestoreBackup::open(
// Path of log file
crashFileData.value("logFile", ""),
hasAutoBackups,
// Restore callback
[crashFileData, backupFilePath, hasProject, hasBackupFile] {
[crashFileData, backupFilePath, hasProject, hasBackupFile, hasAutoBackups, autoBackups = std::move(autoBackups)] {
if (hasBackupFile) {
if (ProjectFile::load(backupFilePath)) {
if (hasProject) {
ProjectFile::setPath(crashFileData["project"].get<std::string>());
} else if (hasAutoBackups) {
ProjectFile::setPath(autoBackups.front().path);
} else {
ProjectFile::setPath("");
}
@ -779,6 +794,8 @@ namespace hex::plugin::builtin {
} else {
if (hasProject) {
ProjectFile::setPath(crashFileData["project"].get<std::string>());
} else if (hasAutoBackups) {
ProjectFile::setPath(autoBackups.front().path);
}
}
},