[ci skip] Add EtaProgressDisplay class.

Also adds a nxdt::views::EtaProgressInfo struct that's used by EtaProgressDisplay's setProgress() method. It may also be used by child classes of AsyncTask.

Other changes include:

* DownloadTask: remove DownloadTaskProgress struct, update logic to use the new nxdt::views::EtaProgressInfo struct.

* DumpOptionsFrame, GameCardTab: minor formatting fixes.

* OptionsTab: remove OptionsTabUpdateProgress class, update logic to use the new nxdt::views::EtaProgressInfo struct.
This commit is contained in:
Pablo Curiel 2024-04-12 23:08:54 +02:00
parent 789a6d6eb8
commit 8f8fc6af37
7 changed files with 226 additions and 162 deletions

View File

@ -28,23 +28,12 @@
#include "core/nxdt_utils.h"
#include "async_task.hpp"
#include "eta_progress_display.hpp"
namespace nxdt::tasks
{
/* Used to hold download progress info. */
typedef struct {
/// Fields set by DownloadTask::HttpProgressCallback().
size_t size; ///< Total download size.
size_t current; ///< Number of bytes downloaded thus far.
int percentage; ///< Progress percentage.
/// Fields set by DownloadTask::onProgressUpdate().
double speed; ///< Download speed expressed in bytes per second.
std::string eta; ///< Formatted ETA string.
} DownloadTaskProgress;
/* Custom event type used to push download progress updates. */
typedef brls::Event<const DownloadTaskProgress&> DownloadProgressEvent;
typedef brls::Event<const nxdt::views::EtaProgressInfo&> DownloadProgressEvent;
/* Used to hold a buffer + size pair with downloaded data. */
typedef std::pair<char*, size_t> DownloadDataResult;
@ -54,7 +43,7 @@ namespace nxdt::tasks
/* This internal RepeatingTask is guaranteed to work on the UI thread, and it is also automatically unregistered on object destruction. */
/* Progress updates are pushed through a DownloadProgressEvent. Make sure to register all event listeners before executing the task. */
template<typename Result, typename... Params>
class DownloadTask: public AsyncTask<DownloadTaskProgress, Result, Params...>
class DownloadTask: public AsyncTask<nxdt::views::EtaProgressInfo, Result, Params...>
{
public:
/* Handles task progress updates on the calling thread. */
@ -90,7 +79,7 @@ namespace nxdt::tasks
void onCancelled(const Result& result) override final;
void onPostExecute(const Result& result) override final;
void onPreExecute(void) override final;
void onProgressUpdate(const DownloadTaskProgress& progress) override final;
void onProgressUpdate(const nxdt::views::EtaProgressInfo& progress) override final;
public:
DownloadTask(void);
@ -153,7 +142,7 @@ namespace nxdt::tasks
NX_IGNORE_ARG(ultotal);
NX_IGNORE_ARG(ulnow);
DownloadTaskProgress progress = {0};
nxdt::views::EtaProgressInfo progress = {0};
DownloadTask<Result, Params...>* task = static_cast<DownloadTask<Result, Params...>*>(clientp);
/* Don't proceed if we're dealing with an invalid task pointer, or if the task has been cancelled. */
@ -212,7 +201,7 @@ namespace nxdt::tasks
}
template<typename Result, typename... Params>
void DownloadTask<Result, Params...>::onProgressUpdate(const DownloadTaskProgress& progress)
void DownloadTask<Result, Params...>::onProgressUpdate(const nxdt::views::EtaProgressInfo& progress)
{
AsyncTaskStatus status = this->getStatus();
@ -235,7 +224,7 @@ namespace nxdt::tasks
double speed = (diff_current / diff_time_conv);
/* Fill struct. */
DownloadTaskProgress new_progress = progress;
nxdt::views::EtaProgressInfo new_progress = progress;
new_progress.speed = speed;
if (progress.size)

View File

@ -73,7 +73,7 @@ namespace nxdt::views
void UpdateRawFileName(void)
{
if (raw_filename) free(this->raw_filename);
if (this->raw_filename) free(this->raw_filename);
this->raw_filename = strdup(this->filename_input->getValue().c_str());
}
@ -92,7 +92,7 @@ namespace nxdt::views
std::string device_str = (std::string(cur_ums_device.name) + ", ");
if (cur_ums_device.product_name[0]) device_str += (std::string(cur_ums_device.product_name) + ", ");
device_str += fmt::format("LUN {}, FS #{}, {}", cur_ums_device.lun, cur_ums_device.fs_idx, LIBUSBHSFS_FS_TYPE_STR(cur_ums_device.fs_type));
storages->push_back(brls::i18n::getStr("dump_options/output_storage/value_02"_i18n, device_str));
storages->push_back(brls::i18n::getStr("dump_options/output_storage/value_02", device_str));
}
if (this->output_storage->getSelectedValue() > ConfigOutputStorage_UsbHost)
@ -152,7 +152,7 @@ namespace nxdt::views
"dump_options/output_storage/value_00"_i18n,
"dump_options/output_storage/value_01"_i18n
}, configGetInteger("output_storage"),
brls::i18n::getStr("dump_options/output_storage/description"_i18n, GITHUB_REPOSITORY_URL));
brls::i18n::getStr("dump_options/output_storage/description", GITHUB_REPOSITORY_URL));
/* Subscribe to SelectListItem's value selected event. */
this->output_storage->getValueSelectedEvent()->subscribe([this](int selected) {
@ -292,7 +292,7 @@ namespace nxdt::views
"NSWDB",
"No-Intro"
}, configGetInteger("gamecard/checksum_lookup_method"),
brls::i18n::getStr("dump_options/checksum_lookup_method/description"_i18n,
brls::i18n::getStr("dump_options/checksum_lookup_method/description",
"dump_options/calculate_checksum/label"_i18n, "NSWDB", NSWDB_XML_NAME, "No-Intro"));
/* Subscribe to SelectListItem's value selected event. */

View File

@ -0,0 +1,68 @@
/*
* eta_progress_display.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ETA_PROGRESS_DISPLAY_HPP__
#define __ETA_PROGRESS_DISPLAY_HPP__
#include <borealis.hpp>
namespace nxdt::views
{
/* Used to hold progress info. */
typedef struct {
size_t size; ///< Total process size.
size_t current; ///< Number of bytes processed thus far.
int percentage; ///< Progress percentage.
double speed; ///< Current speed expressed in bytes per second.
std::string eta; ///< Formatted ETA string.
} EtaProgressInfo;
/* Used to display the progress of a running task. Shows a progress bar, a spinner, a percentage value, the process speed and an ETA value. */
class EtaProgressDisplay: public brls::View
{
private:
brls::ProgressDisplay *progress_display = nullptr;
brls::Label *size_lbl = nullptr, *speed_eta_lbl = nullptr;
std::string GetFormattedSizeString(double size);
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(EtaProgressDisplay);
NON_MOVEABLE(EtaProgressDisplay);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
public:
EtaProgressDisplay(void);
~EtaProgressDisplay(void);
void setProgress(const EtaProgressInfo& progress);
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
};
}
#endif /* __ETA_PROGRESS_DISPLAY_HPP__ */

View File

@ -27,32 +27,10 @@
#include <borealis.hpp>
#include "root_view.hpp"
#include "eta_progress_display.hpp"
namespace nxdt::views
{
/* Used in OptionsTabUpdateFileDialog and OptionsTabUpdateApplicationFrame to display the update progress. */
class OptionsTabUpdateProgress: public brls::View
{
private:
brls::ProgressDisplay *progress_display = nullptr;
brls::Label *size_lbl = nullptr, *speed_eta_lbl = nullptr;
std::string GetFormattedSizeString(double size);
protected:
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
public:
OptionsTabUpdateProgress(void);
~OptionsTabUpdateProgress(void);
void SetProgress(const nxdt::tasks::DownloadTaskProgress& progress);
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
};
/* Update file dialog. */
class OptionsTabUpdateFileDialog: public brls::Dialog
{
@ -73,9 +51,9 @@ namespace nxdt::views
size_t json_buf_size = 0;
UtilsGitHubReleaseJsonData json_data = {0};
brls::Label *wait_lbl = nullptr; /// First stage.
brls::List *changelog_list = nullptr; /// Second stage.
OptionsTabUpdateProgress *update_progress = nullptr; /// Third stage.
brls::Label *wait_lbl = nullptr; /// First stage.
brls::List *changelog_list = nullptr; /// Second stage.
EtaProgressDisplay *update_progress = nullptr; /// Third stage.
nxdt::tasks::DownloadFileTask nro_task;

View File

@ -0,0 +1,133 @@
/*
* eta_progress_display.cpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <nxdt_utils.h>
#include <eta_progress_display.hpp>
namespace i18n = brls::i18n; /* For getStr(). */
using namespace i18n::literals; /* For _i18n. */
namespace nxdt::views
{
EtaProgressDisplay::EtaProgressDisplay(void)
{
this->progress_display = new brls::ProgressDisplay();
this->progress_display->setParent(this);
this->size_lbl = new brls::Label(brls::LabelStyle::MEDIUM, "", false);
this->size_lbl->setVerticalAlign(NVG_ALIGN_BOTTOM);
this->size_lbl->setParent(this);
this->speed_eta_lbl = new brls::Label(brls::LabelStyle::MEDIUM, "", false);
this->speed_eta_lbl->setVerticalAlign(NVG_ALIGN_TOP);
this->speed_eta_lbl->setParent(this);
}
EtaProgressDisplay::~EtaProgressDisplay(void)
{
delete this->progress_display;
delete this->size_lbl;
delete this->speed_eta_lbl;
}
void EtaProgressDisplay::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{
/* Progress display. */
this->progress_display->frame(ctx);
/* Size label. */
this->size_lbl->frame(ctx);
/* Speed / ETA label. */
this->speed_eta_lbl->frame(ctx);
}
void EtaProgressDisplay::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
{
unsigned elem_width = roundf(static_cast<float>(this->width) * 0.90f);
/* Progress display. */
this->progress_display->setBoundaries(
this->x + (this->width - elem_width) / 2,
this->y + (this->height - style->CrashFrame.buttonHeight) / 2,
elem_width,
style->CrashFrame.buttonHeight);
this->progress_display->invalidate(true);
/* Size label. */
this->size_lbl->setWidth(elem_width);
this->size_lbl->invalidate(true);
this->size_lbl->setBoundaries(
this->x + (this->width - this->size_lbl->getWidth()) / 2,
this->progress_display->getY() - this->progress_display->getHeight() / 8,
this->size_lbl->getWidth(),
this->size_lbl->getHeight());
/* Speed / ETA label. */
this->speed_eta_lbl->setWidth(elem_width);
this->speed_eta_lbl->invalidate(true);
this->speed_eta_lbl->setBoundaries(
this->x + (this->width - this->speed_eta_lbl->getWidth()) / 2,
this->progress_display->getY() + this->progress_display->getHeight() + this->progress_display->getHeight() / 8,
this->speed_eta_lbl->getWidth(),
this->speed_eta_lbl->getHeight());
}
void EtaProgressDisplay::setProgress(const EtaProgressInfo& progress)
{
/* Update progress percentage. */
this->progress_display->setProgress(progress.percentage, 100);
/* Update size string. */
this->size_lbl->setText(fmt::format("{} / {}", this->GetFormattedSizeString(static_cast<double>(progress.current)), \
progress.size ? this->GetFormattedSizeString(static_cast<double>(progress.size)) : "?"));
/* Update speed / ETA string. */
if (progress.eta.length())
{
this->speed_eta_lbl->setText(fmt::format("{}/s - ETA: {}", this->GetFormattedSizeString(progress.speed), progress.eta));
} else {
this->speed_eta_lbl->setText(fmt::format("{}/s", this->GetFormattedSizeString(progress.speed)));
}
this->invalidate();
}
void EtaProgressDisplay::willAppear(bool resetState)
{
this->progress_display->willAppear(resetState);
}
void EtaProgressDisplay::willDisappear(bool resetState)
{
this->progress_display->willDisappear(resetState);
}
std::string EtaProgressDisplay::GetFormattedSizeString(double size)
{
char strbuf[0x40] = {0};
utilsGenerateFormattedSizeString(size, strbuf, sizeof(strbuf));
return std::string(strbuf);
}
}

View File

@ -268,7 +268,7 @@ namespace nxdt::views
brls::ListItem *dump_initial_data = new brls::ListItem("gamecard_tab/list/dump_initial_data/label"_i18n, "gamecard_tab/list/dump_initial_data/description"_i18n);
this->list->addView(dump_initial_data);
brls::ListItem *dump_certificate = new brls::ListItem("gamecard_tab/list/dump_certificate/label"_i18n, fmt::format("gamecard_tab/list/dump_certificate/description"_i18n, GAMECARD_CERTIFICATE_OFFSET / GAMECARD_PAGE_SIZE));
brls::ListItem *dump_certificate = new brls::ListItem("gamecard_tab/list/dump_certificate/label"_i18n, brls::i18n::getStr("gamecard_tab/list/dump_certificate/description", GAMECARD_CERTIFICATE_OFFSET / GAMECARD_PAGE_SIZE));
this->list->addView(dump_certificate);
brls::ListItem *dump_card_id_set = new brls::ListItem("gamecard_tab/list/dump_card_id_set/label"_i18n, "gamecard_tab/list/dump_card_id_set/description"_i18n);
@ -277,7 +277,7 @@ namespace nxdt::views
brls::ListItem *dump_card_uid = new brls::ListItem("gamecard_tab/list/dump_card_uid/label"_i18n, "gamecard_tab/list/dump_card_uid/description"_i18n);
this->list->addView(dump_card_uid);
brls::ListItem *dump_header = new brls::ListItem("gamecard_tab/list/dump_header/label"_i18n, fmt::format("gamecard_tab/list/dump_header/description"_i18n, 0));
brls::ListItem *dump_header = new brls::ListItem("gamecard_tab/list/dump_header/label"_i18n, brls::i18n::getStr("gamecard_tab/list/dump_header/description", 0));
this->list->addView(dump_header);
brls::ListItem *dump_plaintext_cardinfo = new brls::ListItem("gamecard_tab/list/dump_plaintext_cardinfo/label"_i18n, "gamecard_tab/list/dump_plaintext_cardinfo/description"_i18n);

View File

@ -31,114 +31,10 @@ using namespace i18n::literals; /* For _i18n. */
namespace nxdt::views
{
OptionsTabUpdateProgress::OptionsTabUpdateProgress(void)
{
this->progress_display = new brls::ProgressDisplay();
this->progress_display->setParent(this);
this->size_lbl = new brls::Label(brls::LabelStyle::MEDIUM, "", false);
this->size_lbl->setVerticalAlign(NVG_ALIGN_BOTTOM);
this->size_lbl->setParent(this);
this->speed_eta_lbl = new brls::Label(brls::LabelStyle::MEDIUM, "", false);
this->speed_eta_lbl->setVerticalAlign(NVG_ALIGN_TOP);
this->speed_eta_lbl->setParent(this);
}
OptionsTabUpdateProgress::~OptionsTabUpdateProgress(void)
{
delete this->progress_display;
delete this->size_lbl;
delete this->speed_eta_lbl;
}
void OptionsTabUpdateProgress::SetProgress(const nxdt::tasks::DownloadTaskProgress& progress)
{
/* Update progress percentage. */
this->progress_display->setProgress(progress.percentage, 100);
/* Update size string. */
this->size_lbl->setText(fmt::format("{} / {}", this->GetFormattedSizeString(static_cast<double>(progress.current)), \
progress.size ? this->GetFormattedSizeString(static_cast<double>(progress.size)) : "?"));
/* Update speed / ETA string. */
if (progress.eta.length())
{
this->speed_eta_lbl->setText(fmt::format("{}/s - ETA: {}", this->GetFormattedSizeString(progress.speed), progress.eta));
} else {
this->speed_eta_lbl->setText(fmt::format("{}/s", this->GetFormattedSizeString(progress.speed)));
}
this->invalidate();
}
void OptionsTabUpdateProgress::willAppear(bool resetState)
{
this->progress_display->willAppear(resetState);
}
void OptionsTabUpdateProgress::willDisappear(bool resetState)
{
this->progress_display->willDisappear(resetState);
}
void OptionsTabUpdateProgress::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{
/* Progress display. */
this->progress_display->frame(ctx);
/* Size label. */
this->size_lbl->frame(ctx);
/* Speed / ETA label. */
this->speed_eta_lbl->frame(ctx);
}
void OptionsTabUpdateProgress::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
{
unsigned elem_width = roundf(static_cast<float>(this->width) * 0.90f);
/* Progress display. */
this->progress_display->setBoundaries(
this->x + (this->width - elem_width) / 2,
this->y + (this->height - style->CrashFrame.buttonHeight) / 2,
elem_width,
style->CrashFrame.buttonHeight);
this->progress_display->invalidate(true);
/* Size label. */
this->size_lbl->setWidth(elem_width);
this->size_lbl->invalidate(true);
this->size_lbl->setBoundaries(
this->x + (this->width - this->size_lbl->getWidth()) / 2,
this->progress_display->getY() - this->progress_display->getHeight() / 8,
this->size_lbl->getWidth(),
this->size_lbl->getHeight());
/* Speed / ETA label. */
this->speed_eta_lbl->setWidth(elem_width);
this->speed_eta_lbl->invalidate(true);
this->speed_eta_lbl->setBoundaries(
this->x + (this->width - this->speed_eta_lbl->getWidth()) / 2,
this->progress_display->getY() + this->progress_display->getHeight() + this->progress_display->getHeight() / 8,
this->speed_eta_lbl->getWidth(),
this->speed_eta_lbl->getHeight());
}
std::string OptionsTabUpdateProgress::GetFormattedSizeString(double size)
{
char strbuf[0x40] = {0};
utilsGenerateFormattedSizeString(size, strbuf, sizeof(strbuf));
return std::string(strbuf);
}
OptionsTabUpdateFileDialog::OptionsTabUpdateFileDialog(std::string path, std::string url, bool force_https, std::string success_str) : brls::Dialog(), success_str(success_str)
{
/* Set content view. */
OptionsTabUpdateProgress *update_progress = new OptionsTabUpdateProgress();
EtaProgressDisplay *update_progress = new EtaProgressDisplay();
this->setContentView(update_progress);
/* Add cancel button. */
@ -154,9 +50,9 @@ namespace nxdt::views
this->setCancelable(false);
/* Subscribe to the download task. */
this->download_task.RegisterListener([this, update_progress](const nxdt::tasks::DownloadTaskProgress& progress) {
this->download_task.RegisterListener([this, update_progress](const EtaProgressInfo& progress) {
/* Update progress. */
update_progress->SetProgress(progress);
update_progress->setProgress(progress);
/* Check if the download task has finished. */
if (this->download_task.isFinished())
@ -194,11 +90,11 @@ namespace nxdt::views
this->addStage(this->changelog_list);
/* Add third stage. */
this->update_progress = new OptionsTabUpdateProgress();
this->update_progress = new EtaProgressDisplay();
this->addStage(this->update_progress);
/* Subscribe to the JSON task. */
this->json_task.RegisterListener([this](const nxdt::tasks::DownloadTaskProgress& progress) {
this->json_task.RegisterListener([this](const EtaProgressInfo& progress) {
/* Return immediately if the JSON task hasn't finished. */
if (!this->json_task.isFinished()) return;
@ -293,7 +189,7 @@ namespace nxdt::views
std::stringstream ss(std::string(this->json_data.changelog));
/* Display version string at the top. */
FocusableLabel *version_lbl = new FocusableLabel(true, false, brls::LabelStyle::CRASH, std::string(this->json_data.version), true);
FocusableLabel *version_lbl = new FocusableLabel(false, false, brls::LabelStyle::CRASH, std::string(this->json_data.version), true);
version_lbl->setHorizontalAlign(NVG_ALIGN_CENTER);
this->changelog_list->addView(version_lbl);
@ -356,9 +252,9 @@ namespace nxdt::views
this->updateActionHint(brls::Key::B, "options_tab/update_dialog/cancel"_i18n);
/* Subscribe to the NRO task. */
this->nro_task.RegisterListener([this](const nxdt::tasks::DownloadTaskProgress& progress) {
this->nro_task.RegisterListener([this](const EtaProgressInfo& progress) {
/* Update progress. */
this->update_progress->SetProgress(progress);
this->update_progress->setProgress(progress);
/* Check if the download task has finished. */
if (this->nro_task.isFinished())