2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-10-19 04:55:19 +00:00

feat: show loading progress in modman

This commit is contained in:
Jan Laupetin
2025-10-14 17:59:34 +01:00
parent 5b3664ad8c
commit 9fa41ca0d3
9 changed files with 134 additions and 12 deletions

View File

@@ -5,5 +5,8 @@
class ProgressCallback
{
public:
ProgressCallback() = default;
virtual ~ProgressCallback() = default;
virtual void OnProgress(size_t current, size_t total) = 0;
};

View File

@@ -4,6 +4,40 @@
#include "Web/UiCommunication.h"
#include "ZoneLoading.h"
#include <filesystem>
namespace fs = std::filesystem;
namespace
{
constexpr double MIN_PROGRESS_TO_REPORT = 0.005;
class EventProgressReporter : public ProgressCallback
{
public:
explicit EventProgressReporter(std::string zoneName)
: m_zone_name(std::move(zoneName)),
m_last_progress(0)
{
}
void OnProgress(const size_t current, const size_t total) override
{
const double percentage = static_cast<double>(current) / static_cast<double>(total);
if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT)
{
m_last_progress = percentage;
ui::NotifyZoneLoadProgress(m_zone_name, percentage);
}
}
private:
std::string m_zone_name;
double m_last_progress;
};
} // namespace
void FastFileContext::Destroy()
{
// Unload all zones
@@ -12,7 +46,7 @@ void FastFileContext::Destroy()
result::Expected<Zone*, std::string> FastFileContext::LoadFastFile(const std::string& path)
{
auto zone = ZoneLoading::LoadZone(path, std::nullopt);
auto zone = ZoneLoading::LoadZone(path, std::make_unique<EventProgressReporter>(fs::path(path).filename().replace_extension().string()));
if (!zone)
return result::Unexpected(std::move(zone.error()));

View File

@@ -7,6 +7,15 @@
namespace
{
class ZoneLoadProgressDto
{
public:
std::string zoneName;
double percentage;
};
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadProgressDto, zoneName, percentage);
class ZoneLoadedDto
{
public:
@@ -71,6 +80,15 @@ namespace
namespace ui
{
void NotifyZoneLoadProgress(std::string zoneName, const double percentage)
{
const ZoneLoadProgressDto dto{
.zoneName = std::move(zoneName),
.percentage = percentage,
};
Notify(*ModManContext::Get().m_main_webview, "zoneLoadProgress", dto);
}
void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath)
{
const ZoneLoadedDto dto{

View File

@@ -4,6 +4,7 @@
namespace ui
{
void NotifyZoneLoadProgress(std::string zoneName, double percentage);
void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath);
void NotifyZoneUnloaded(std::string zoneName);

View File

@@ -1,14 +1,16 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { webviewBinds } from "@/native";
import { webviewAddEventListener, webviewBinds } from "@/native";
import { useZoneStore } from "@/stores/ZoneStore";
import SpinningLoader from "@/components/SpinningLoader.vue";
const zoneStore = useZoneStore();
const loadingFastFile = ref(false);
const unlinkingFastFile = ref(false);
const lastPercentage = ref<number>(0);
const performingAction = computed<boolean>(() => loadingFastFile.value || unlinkingFastFile.value);
const progressBarWidth = computed<string>(() => `${lastPercentage.value * 100}%`);
async function openFastFileSelect() {
return await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] });
@@ -21,6 +23,7 @@ async function onOpenFastFileClick() {
if (!fastFilePath) return;
loadingFastFile.value = true;
lastPercentage.value = 0;
webviewBinds
.loadFastFile(fastFilePath)
@@ -29,6 +32,7 @@ async function onOpenFastFileClick() {
})
.finally(() => {
loadingFastFile.value = false;
lastPercentage.value = 1;
});
}
@@ -43,6 +47,7 @@ async function onUnlinkFastFileClick() {
let loadedZoneName: string;
try {
lastPercentage.value = 0;
loadedZoneName = (await webviewBinds.loadFastFile(fastFilePath)).zoneName;
} catch (e: unknown) {
console.error("Failed to load fastfile:", e as string);
@@ -50,6 +55,7 @@ async function onUnlinkFastFileClick() {
}
try {
lastPercentage.value = 0;
await webviewBinds.unlinkZone(loadedZoneName);
} catch (e: unknown) {
console.error("Failed to unlink fastfile:", e as string);
@@ -59,6 +65,7 @@ async function onUnlinkFastFileClick() {
}
} finally {
unlinkingFastFile.value = false;
lastPercentage.value = 1;
}
}
@@ -67,6 +74,11 @@ function onUnloadClicked(zoneName: string) {
console.error("Failed to unload zone:", e);
});
}
webviewAddEventListener("zoneLoadProgress", (dto) => {
console.log(dto);
lastPercentage.value = dto.percentage;
});
</script>
<template>
@@ -94,10 +106,18 @@ function onUnloadClicked(zoneName: string) {
</div>
</div>
</div>
<div class="progressbar-wrapper">
<div
class="progressbar"
:class="{ visible: performingAction }"
:style="{ width: progressBarWidth }"
></div>
</div>
</main>
</template>
<style scoped>
<style scoped lang="scss">
.actions {
display: flex;
justify-content: center;
@@ -117,4 +137,24 @@ function onUnloadClicked(zoneName: string) {
.zone > button {
margin-left: 0.5em;
}
.progressbar-wrapper {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 0.35rem 0.4rem;
}
.progressbar {
opacity: 0;
height: 0.4rem;
border-radius: 2.5rem;
background-color: #b9772c;
transition: opacity 0.2s ease-in-out;
&.visible {
opacity: 1;
}
}
</style>

View File

@@ -14,13 +14,24 @@
-webkit-text-size-adjust: 100%;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.container {
margin: 0;
padding-top: 10vh;
display: flex;
position: relative;
flex-direction: column;
justify-content: center;
justify-content: start;
text-align: center;
height: 100vh;
}
.logo {

View File

@@ -1,3 +1,8 @@
export interface ZoneLoadProgressDto {
zoneName: string;
percentage: number;
}
export interface ZoneLoadedDto {
zoneName: string;
filePath: string;
@@ -13,6 +18,7 @@ export interface ZoneBinds {
}
export interface ZoneEventMap {
zoneLoadProgress: ZoneLoadProgressDto;
zoneLoaded: ZoneLoadedDto;
zoneUnloaded: ZoneUnloadedDto;
}

View File

@@ -60,8 +60,6 @@ namespace
m_block_shift(pointerBitCount - blockBitCount),
m_offset_mask(std::numeric_limits<uintptr_t>::max() >> (sizeof(uintptr_t) * 8 - (pointerBitCount - blockBitCount))),
m_last_fill_size(0),
m_has_progress_callback(progressCallback.has_value()),
m_progress_callback(std::move(progressCallback).value_or(nullptr)),
m_progress_current_size(0uz),
m_progress_total_size(0uz)
{
@@ -74,7 +72,12 @@ namespace
m_insert_block = blocks[insertBlock];
m_progress_total_size = CalculateTotalSize();
if (progressCallback)
{
m_has_progress_callback = true;
m_progress_callback = *std::move(progressCallback);
m_progress_total_size = CalculateTotalSize();
}
}
[[nodiscard]] unsigned GetPointerBitCount() const override
@@ -452,7 +455,8 @@ namespace
{
m_block_offsets[block.m_index] += size;
if (m_has_progress_callback)
// We cannot know the full size of the temp block
if (m_has_progress_callback && block.m_type != XBlockType::BLOCK_TYPE_TEMP)
{
m_progress_current_size += size;
m_progress_callback->OnProgress(m_progress_current_size, m_progress_total_size);
@@ -473,7 +477,11 @@ namespace
size_t result = 0uz;
for (const auto& block : m_blocks)
result += block->m_buffer_size;
{
// We cannot know the full size of the temp block
if (block->m_type != XBlockType::BLOCK_TYPE_TEMP)
result += block->m_buffer_size;
}
return result;
}

View File

@@ -28,10 +28,11 @@ result::Expected<std::unique_ptr<Zone>, std::string> ZoneLoading::LoadZone(const
for (auto game = 0u; game < static_cast<unsigned>(GameId::COUNT); game++)
{
const auto* factory = IZoneLoaderFactory::GetZoneLoaderFactoryForGame(static_cast<GameId>(game));
zoneLoader = factory->CreateLoaderForHeader(header, zoneName, std::move(progressCallback));
if (zoneLoader)
if (factory->InspectZoneHeader(header))
{
zoneLoader = factory->CreateLoaderForHeader(header, zoneName, std::move(progressCallback));
break;
}
}
if (!zoneLoader)