add save backup/restore, fix filebrowser touch screen, optimise all zip/unzip file code by using bigger stdio buffers.
This commit is contained in:
210
sphaira/source/minizip_helper.cpp
Normal file
210
sphaira/source/minizip_helper.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
#include "minizip_helper.hpp"
|
||||
#include <minizip/unzip.h>
|
||||
#include <minizip/zip.h>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
namespace sphaira::mz {
|
||||
namespace {
|
||||
|
||||
voidpf minizip_open_file_func_mem(voidpf opaque, const void* filename, int mode) {
|
||||
return opaque;
|
||||
}
|
||||
|
||||
ZPOS64_T minizip_tell_file_func_mem(voidpf opaque, voidpf stream) {
|
||||
auto mem = static_cast<const MzMem*>(opaque);
|
||||
return mem->offset;
|
||||
}
|
||||
|
||||
long minizip_seek_file_func_mem(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
|
||||
auto mem = static_cast<MzMem*>(opaque);
|
||||
size_t new_offset = 0;
|
||||
|
||||
switch (origin) {
|
||||
case ZLIB_FILEFUNC_SEEK_SET: new_offset = offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_CUR: new_offset = mem->offset + offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_END: new_offset = (mem->buf.size() - 1) + offset; break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
if (new_offset > mem->buf.size()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mem->offset = new_offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uLong minizip_read_file_func_mem(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||
auto mem = static_cast<MzMem*>(opaque);
|
||||
|
||||
size = std::min(size, mem->buf.size() - mem->offset);
|
||||
std::memcpy(buf, mem->buf.data() + mem->offset, size);
|
||||
mem->offset += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
uLong minizip_write_file_func_mem(voidpf opaque, voidpf stream, const void* buf, uLong size) {
|
||||
auto mem = static_cast<MzMem*>(opaque);
|
||||
|
||||
// give it more memory
|
||||
if (mem->buf.capacity() < mem->offset + size) {
|
||||
mem->buf.reserve(mem->buf.capacity() + 1024*1024*64);
|
||||
}
|
||||
|
||||
if (mem->buf.size() < mem->offset + size) {
|
||||
mem->buf.resize(mem->offset + size);
|
||||
}
|
||||
|
||||
std::memcpy(mem->buf.data() + mem->offset, buf, size);
|
||||
mem->offset += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int minizip_close_file_func_mem(voidpf opaque, voidpf stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr zlib_filefunc64_def zlib_filefunc_mem = {
|
||||
.zopen64_file = minizip_open_file_func_mem,
|
||||
.zread_file = minizip_read_file_func_mem,
|
||||
.zwrite_file = minizip_write_file_func_mem,
|
||||
.ztell64_file = minizip_tell_file_func_mem,
|
||||
.zseek64_file = minizip_seek_file_func_mem,
|
||||
.zclose_file = minizip_close_file_func_mem,
|
||||
};
|
||||
|
||||
voidpf minizip_open_file_func_span(voidpf opaque, const void* filename, int mode) {
|
||||
return opaque;
|
||||
}
|
||||
|
||||
ZPOS64_T minizip_tell_file_func_span(voidpf opaque, voidpf stream) {
|
||||
auto mem = static_cast<const MzSpan*>(opaque);
|
||||
return mem->offset;
|
||||
}
|
||||
|
||||
long minizip_seek_file_func_span(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
|
||||
auto mem = static_cast<MzSpan*>(opaque);
|
||||
size_t new_offset = 0;
|
||||
|
||||
switch (origin) {
|
||||
case ZLIB_FILEFUNC_SEEK_SET: new_offset = offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_CUR: new_offset = mem->offset + offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_END: new_offset = (mem->buf.size() - 1) + offset; break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
if (new_offset > mem->buf.size()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mem->offset = new_offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uLong minizip_read_file_func_span(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||
auto mem = static_cast<MzSpan*>(opaque);
|
||||
|
||||
size = std::min(size, mem->buf.size() - mem->offset);
|
||||
std::memcpy(buf, mem->buf.data() + mem->offset, size);
|
||||
mem->offset += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int minizip_close_file_func_span(voidpf opaque, voidpf stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr zlib_filefunc64_def zlib_filefunc_span = {
|
||||
.zopen64_file = minizip_open_file_func_span,
|
||||
.zread_file = minizip_read_file_func_span,
|
||||
.ztell64_file = minizip_tell_file_func_span,
|
||||
.zseek64_file = minizip_seek_file_func_span,
|
||||
.zclose_file = minizip_close_file_func_span,
|
||||
};
|
||||
|
||||
voidpf minizip_open_file_func_stdio(voidpf opaque, const void* filename, int mode) {
|
||||
const char* mode_fopen = NULL;
|
||||
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
|
||||
mode_fopen = "rb";
|
||||
} else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
|
||||
mode_fopen = "r+b";
|
||||
} else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
|
||||
mode_fopen = "wb";
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto f = std::fopen((const char*)filename, mode_fopen);
|
||||
if (f) {
|
||||
std::setvbuf(f, nullptr, _IOFBF, 1024 * 512);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
ZPOS64_T minizip_tell_file_func_stdio(voidpf opaque, voidpf stream) {
|
||||
auto file = static_cast<std::FILE*>(stream);
|
||||
return std::ftell(file);
|
||||
}
|
||||
|
||||
long minizip_seek_file_func_stdio(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
|
||||
auto file = static_cast<std::FILE*>(stream);
|
||||
return std::fseek(file, offset, origin);
|
||||
}
|
||||
|
||||
uLong minizip_read_file_func_stdio(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||
auto file = static_cast<std::FILE*>(stream);
|
||||
return std::fread(buf, 1, size, file);
|
||||
}
|
||||
|
||||
uLong minizip_write_file_func_stdio(voidpf opaque, voidpf stream, const void* buf, uLong size) {
|
||||
auto file = static_cast<std::FILE*>(stream);
|
||||
return std::fwrite(buf, 1, size, file);
|
||||
}
|
||||
|
||||
int minizip_close_file_func_stdio(voidpf opaque, voidpf stream) {
|
||||
auto file = static_cast<std::FILE*>(stream);
|
||||
if (file) {
|
||||
return std::fclose(file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int minizip_error_file_func_stdio(voidpf opaque, voidpf stream) {
|
||||
auto file = static_cast<std::FILE*>(stream);
|
||||
if (file) {
|
||||
return std::ferror(file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr zlib_filefunc64_def zlib_filefunc_stdio = {
|
||||
.zopen64_file = minizip_open_file_func_stdio,
|
||||
.zread_file = minizip_read_file_func_stdio,
|
||||
.zwrite_file = minizip_write_file_func_stdio,
|
||||
.ztell64_file = minizip_tell_file_func_stdio,
|
||||
.zseek64_file = minizip_seek_file_func_stdio,
|
||||
.zclose_file = minizip_close_file_func_stdio,
|
||||
.zerror_file = minizip_error_file_func_stdio,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs) {
|
||||
*funcs = zlib_filefunc_mem;
|
||||
funcs->opaque = mem;
|
||||
}
|
||||
|
||||
void FileFuncSpan(MzSpan* span, zlib_filefunc64_def* funcs) {
|
||||
*funcs = zlib_filefunc_span;
|
||||
funcs->opaque = span;
|
||||
}
|
||||
|
||||
void FileFuncStdio(zlib_filefunc64_def* funcs) {
|
||||
*funcs = zlib_filefunc_stdio;
|
||||
}
|
||||
|
||||
} // namespace sphaira::mz
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "log.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "app.hpp"
|
||||
#include "minizip_helper.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
@@ -456,7 +457,7 @@ Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::F
|
||||
[&](void* data, s64 off, s64 size, u64* bytes_read) -> Result {
|
||||
const auto result = unzReadCurrentFile(zfile, data, size);
|
||||
if (result <= 0) {
|
||||
// log_write("failed to read zip file: %s\n", inzip.c_str());
|
||||
log_write("failed to read zip file: %s %d\n", path.s, result);
|
||||
R_THROW(0x1);
|
||||
}
|
||||
|
||||
@@ -474,21 +475,29 @@ Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::F
|
||||
));
|
||||
|
||||
// validate crc32 (if set in the info).
|
||||
R_UNLESS(!crc32 || crc32 == crc32_out, 0x1);
|
||||
R_UNLESS(!crc32 || crc32 == crc32_out, 0x8);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path) {
|
||||
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32) {
|
||||
fs::File f;
|
||||
R_TRY(fs->OpenFile(path, FsOpenMode_Read, &f));
|
||||
|
||||
s64 file_size;
|
||||
R_TRY(f.GetSize(&file_size));
|
||||
|
||||
if (crc32) {
|
||||
*crc32 = 0;
|
||||
}
|
||||
|
||||
return thread::TransferInternal(pbox, file_size,
|
||||
[&](void* data, s64 off, s64 size, u64* bytes_read) -> Result {
|
||||
return f.Read(off, data, size, FsReadOption_None, bytes_read);
|
||||
const auto rc = f.Read(off, data, size, FsReadOption_None, bytes_read);
|
||||
if (R_SUCCEEDED(rc) && crc32) {
|
||||
*crc32 = crc32CalculateWithSeed(*crc32, data, *bytes_read);
|
||||
}
|
||||
return rc;
|
||||
},
|
||||
[&](const void* data, s64 off, s64 size) -> Result {
|
||||
if (ZIP_OK != zipWriteInFileInZip(zfile, data, size)) {
|
||||
@@ -559,7 +568,10 @@ Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs
|
||||
}
|
||||
|
||||
Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter) {
|
||||
auto zfile = unzOpen64(zip_out);
|
||||
zlib_filefunc64_def file_func;
|
||||
mz::FileFuncStdio(&file_func);
|
||||
|
||||
auto zfile = unzOpen2_64(zip_out, &file_func);
|
||||
R_UNLESS(zfile, 0x1);
|
||||
ON_SCOPE_EXIT(unzClose(zfile));
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "threaded_file_transfer.hpp"
|
||||
#include "nro.hpp"
|
||||
#include "web.hpp"
|
||||
#include "minizip_helper.hpp"
|
||||
|
||||
#include <minIni.h>
|
||||
#include <string>
|
||||
@@ -77,62 +78,6 @@ constexpr const char* ORDER_STR[] = {
|
||||
"Asc",
|
||||
};
|
||||
|
||||
struct MzMem {
|
||||
const void* buf;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
ZPOS64_T minizip_tell_file_func(voidpf opaque, voidpf stream) {
|
||||
auto mem = static_cast<const MzMem*>(opaque);
|
||||
return mem->offset;
|
||||
}
|
||||
|
||||
long minizip_seek_file_func(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
|
||||
auto mem = static_cast<MzMem*>(opaque);
|
||||
size_t new_offset = 0;
|
||||
|
||||
switch (origin) {
|
||||
case ZLIB_FILEFUNC_SEEK_SET: new_offset = offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_CUR: new_offset = mem->offset + offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_END: new_offset = (mem->size - 1) + offset; break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
if (new_offset > mem->size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mem->offset = new_offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
voidpf minizip_open_file_func(voidpf opaque, const void* filename, int mode) {
|
||||
return opaque;
|
||||
}
|
||||
|
||||
uLong minizip_read_file_func(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||
auto mem = static_cast<MzMem*>(opaque);
|
||||
|
||||
size = std::min(size, mem->size - mem->offset);
|
||||
std::memcpy(buf, (const u8*)mem->buf + mem->offset, size);
|
||||
mem->offset += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int minizip_close_file_func(voidpf opaque, voidpf stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr zlib_filefunc64_def zlib_filefunc = {
|
||||
.zopen64_file = minizip_open_file_func,
|
||||
.zread_file = minizip_read_file_func,
|
||||
.ztell64_file = minizip_tell_file_func,
|
||||
.zseek64_file = minizip_seek_file_func,
|
||||
.zclose_file = minizip_close_file_func,
|
||||
};
|
||||
|
||||
auto BuildIconUrl(const Entry& e) -> std::string {
|
||||
char out[0x100];
|
||||
std::snprintf(out, sizeof(out), "%s/packages/%s/icon.png", URL_BASE, e.name.c_str());
|
||||
@@ -474,20 +419,17 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
|
||||
}
|
||||
}
|
||||
|
||||
struct MzMem mem{};
|
||||
mem.buf = api_result.data.data();
|
||||
mem.size = api_result.data.size();
|
||||
auto file_func = zlib_filefunc;
|
||||
file_func.opaque = &mem;
|
||||
|
||||
zlib_filefunc64_def* file_func_ptr{};
|
||||
mz::MzSpan mz_span{api_result.data};
|
||||
zlib_filefunc64_def file_func;
|
||||
if (!file_download) {
|
||||
file_func_ptr = &file_func;
|
||||
mz::FileFuncSpan(&mz_span, &file_func);
|
||||
} else {
|
||||
mz::FileFuncStdio(&file_func);
|
||||
}
|
||||
|
||||
// 3. extract the zip
|
||||
if (!pbox->ShouldExit()) {
|
||||
auto zfile = unzOpen2_64(zip_out, file_func_ptr);
|
||||
auto zfile = unzOpen2_64(zip_out, &file_func);
|
||||
R_UNLESS(zfile, 0x1);
|
||||
ON_SCOPE_EXIT(unzClose(zfile));
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "hasher.hpp"
|
||||
#include "location.hpp"
|
||||
#include "threaded_file_transfer.hpp"
|
||||
#include "minizip_helper.hpp"
|
||||
|
||||
#include "yati/yati.hpp"
|
||||
#include "yati/source/file.hpp"
|
||||
@@ -435,7 +436,6 @@ FsView::~FsView() {
|
||||
}
|
||||
|
||||
void FsView::Update(Controller* controller, TouchInfo* touch) {
|
||||
Widget::Update(controller, touch);
|
||||
m_list->OnUpdate(controller, touch, m_index, m_entries_current.size(), [this](bool touch, auto i) {
|
||||
if (touch && m_index == i) {
|
||||
FireAction(Button::A);
|
||||
@@ -790,7 +790,10 @@ void FsView::ZipFiles(fs::FsPath zip_out) {
|
||||
zip_info.tmz_date.tm_mon = tm->tm_mon;
|
||||
zip_info.tmz_date.tm_year = tm->tm_year;
|
||||
|
||||
auto zfile = zipOpen(zip_out, APPEND_STATUS_CREATE);
|
||||
zlib_filefunc64_def file_func;
|
||||
mz::FileFuncStdio(&file_func);
|
||||
|
||||
auto zfile = zipOpen2_64(zip_out, APPEND_STATUS_CREATE, nullptr, &file_func);
|
||||
R_UNLESS(zfile, 0x1);
|
||||
ON_SCOPE_EXIT(zipClose(zfile, "sphaira v" APP_VERSION_HASH));
|
||||
|
||||
@@ -1854,6 +1857,13 @@ Menu::~Menu() {
|
||||
}
|
||||
|
||||
void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||
// workaround the buttons not being display properly.
|
||||
// basically, inherit all actions from the view, draw them,
|
||||
// then restore state after.
|
||||
const auto actions_copy = GetActions();
|
||||
ON_SCOPE_EXIT(m_actions = actions_copy);
|
||||
m_actions.insert_range(view->GetActions());
|
||||
|
||||
MenuBase::Update(controller, touch);
|
||||
view->Update(controller, touch);
|
||||
}
|
||||
|
||||
@@ -998,6 +998,11 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||
void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
MenuBase::Draw(vg, theme);
|
||||
|
||||
if (m_entries.empty()) {
|
||||
gfx::drawTextArgs(vg, GetX() + GetW() / 2.f, GetY() + GetH() / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT_INFO), "Empty..."_i18n.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// max images per frame, in order to not hit io / gpu too hard.
|
||||
const int image_load_max = 2;
|
||||
int image_load_count = 0;
|
||||
@@ -1096,7 +1101,7 @@ void Menu::ScanHomebrew() {
|
||||
u64 unk_x11;// = e.unk_x11;
|
||||
memcpy(&unk_x0a, e.unk_x0a, sizeof(e.unk_x0a));
|
||||
memcpy(&unk_x11, e.unk_x11, sizeof(e.unk_x11));
|
||||
log_write("ID: %016lx got type: %u unk_x09: %u unk_x0a: %zu unk_x10: %u unk_x11: %zu\n", e.application_id, e.type,
|
||||
log_write("ID: %016lx got type: %u unk_x09: %u unk_x0a: %zu unk_x10: %u unk_x11: %zu\n", e.app_id, e.type,
|
||||
unk_x09,
|
||||
unk_x0a,
|
||||
unk_x10,
|
||||
|
||||
@@ -529,7 +529,7 @@ Result Menu::GcMount() {
|
||||
// the fs, same as mounting storage.
|
||||
for (u32 i = 0; i < REMOUNT_ATTEMPT_MAX; i++) {
|
||||
R_TRY(fsDeviceOperatorGetGameCardHandle(std::addressof(m_dev_op), std::addressof(m_handle)));
|
||||
m_fs = std::make_unique<fs::FsNativeGameCard>(std::addressof(m_handle), FsGameCardPartition_Secure, false);
|
||||
m_fs = std::make_unique<fs::FsNativeGameCard>(std::addressof(m_handle), FsGameCardPartition_Secure);
|
||||
if (R_SUCCEEDED(m_fs->GetFsOpenResult())) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "ui/menus/ftp_menu.hpp"
|
||||
#include "ui/menus/gc_menu.hpp"
|
||||
#include "ui/menus/game_menu.hpp"
|
||||
#include "ui/menus/save_menu.hpp"
|
||||
#include "ui/menus/appstore.hpp"
|
||||
|
||||
#include "app.hpp"
|
||||
@@ -49,6 +50,7 @@ const MiscMenuEntry MISC_MENU_ENTRIES[] = {
|
||||
{ .name = "Appstore", .title = "Appstore", .func = MiscMenuFuncGenerator<ui::menu::appstore::Menu>, .flag = MiscMenuFlag_Shortcut },
|
||||
{ .name = "Games", .title = "Games", .func = MiscMenuFuncGenerator<ui::menu::game::Menu>, .flag = MiscMenuFlag_Shortcut },
|
||||
{ .name = "FileBrowser", .title = "FileBrowser", .func = MiscMenuFuncGenerator<ui::menu::filebrowser::Menu>, .flag = MiscMenuFlag_Shortcut },
|
||||
{ .name = "Saves", .title = "Saves", .func = MiscMenuFuncGenerator<ui::menu::save::Menu>, .flag = MiscMenuFlag_Shortcut },
|
||||
{ .name = "Themezer", .title = "Themezer", .func = MiscMenuFuncGenerator<ui::menu::themezer::Menu>, .flag = MiscMenuFlag_Shortcut },
|
||||
{ .name = "GitHub", .title = "GitHub", .func = MiscMenuFuncGenerator<ui::menu::gh::Menu>, .flag = MiscMenuFlag_Shortcut },
|
||||
{ .name = "FTP", .title = "FTP Install", .func = MiscMenuFuncGenerator<ui::menu::ftp::Menu>, .flag = MiscMenuFlag_Install },
|
||||
|
||||
1274
sphaira/source/ui/menus/save_menu.cpp
Normal file
1274
sphaira/source/ui/menus/save_menu.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -88,6 +88,7 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
m_last_offset = m_offset;
|
||||
}
|
||||
|
||||
const auto action = m_action;
|
||||
const auto title = m_title;
|
||||
const auto transfer = m_transfer;
|
||||
const auto size = m_size;
|
||||
@@ -166,7 +167,7 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
gfx::drawTextArgs(vg, center_x, prog_bar.y + prog_bar.h + 30, 18, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), "%s (%s)", time_str, speed_str);
|
||||
}
|
||||
|
||||
gfx::drawTextArgs(vg, center_x, m_pos.y + 40, 24, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), m_action.c_str());
|
||||
gfx::drawTextArgs(vg, center_x, m_pos.y + 40, 24, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), action.c_str());
|
||||
|
||||
const auto draw_text = [&](ScrollingText& scroll, const std::string& txt, float y, float size, float pad, ThemeEntryID id){
|
||||
float bounds[4];
|
||||
@@ -187,6 +188,14 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
nvgRestore(vg);
|
||||
}
|
||||
|
||||
auto ProgressBox::SetActionName(const std::string& action) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
m_action = action;
|
||||
mutexUnlock(&m_mutex);
|
||||
Yield();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::SetTitle(const std::string& title) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
m_title = title;
|
||||
|
||||
Reference in New Issue
Block a user