mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 06:37:38 +03:00
Merge 10cd348f17 into 78d6e5931c
This commit is contained in:
commit
24885b25c8
@ -8,6 +8,7 @@
|
|||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
#include "CrossPointState.h"
|
#include "CrossPointState.h"
|
||||||
#include "EpubReaderChapterSelectionActivity.h"
|
#include "EpubReaderChapterSelectionActivity.h"
|
||||||
|
#include "EpubReaderPercentSelectionActivity.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "RecentBooksStore.h"
|
#include "RecentBooksStore.h"
|
||||||
#include "ScreenComponents.h"
|
#include "ScreenComponents.h"
|
||||||
@ -22,6 +23,17 @@ constexpr int progressBarMarginTop = 1;
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// Clamp any percent-like value into the valid 0-100 range.
|
||||||
|
static int clampPercent(const int value) {
|
||||||
|
if (value < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (value > 100) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
void EpubReaderActivity::taskTrampoline(void* param) {
|
void EpubReaderActivity::taskTrampoline(void* param) {
|
||||||
auto* self = static_cast<EpubReaderActivity*>(param);
|
auto* self = static_cast<EpubReaderActivity*>(param);
|
||||||
self->displayTaskLoop();
|
self->displayTaskLoop();
|
||||||
@ -123,7 +135,7 @@ void EpubReaderActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter chapter selection activity
|
// Enter reader menu activity.
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
// Don't start activity transition while rendering
|
// Don't start activity transition while rendering
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
@ -225,6 +237,81 @@ void EpubReaderActivity::onReaderMenuBack() {
|
|||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Translate an absolute percent into a spine index plus a normalized position
|
||||||
|
// within that spine so we can jump after the section is loaded.
|
||||||
|
void EpubReaderActivity::jumpToPercent(int percent) {
|
||||||
|
if (!epub) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t bookSize = epub->getBookSize();
|
||||||
|
if (bookSize == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize input to 0-100 to avoid invalid jumps.
|
||||||
|
percent = clampPercent(percent);
|
||||||
|
|
||||||
|
// Convert percent into a byte-like absolute position across the spine sizes.
|
||||||
|
// Use an overflow-safe computation: (bookSize / 100) * percent + (bookSize % 100) * percent / 100
|
||||||
|
size_t targetSize =
|
||||||
|
(bookSize / 100) * static_cast<size_t>(percent) + (bookSize % 100) * static_cast<size_t>(percent) / 100;
|
||||||
|
if (percent >= 100) {
|
||||||
|
// Ensure the final percent lands inside the last spine item.
|
||||||
|
targetSize = bookSize - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int spineCount = epub->getSpineItemsCount();
|
||||||
|
if (spineCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int targetSpineIndex = spineCount - 1;
|
||||||
|
size_t prevCumulative = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < spineCount; i++) {
|
||||||
|
const size_t cumulative = epub->getCumulativeSpineItemSize(i);
|
||||||
|
if (targetSize <= cumulative) {
|
||||||
|
// Found the spine item containing the absolute position.
|
||||||
|
targetSpineIndex = i;
|
||||||
|
prevCumulative = (i > 0) ? epub->getCumulativeSpineItemSize(i - 1) : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t cumulative = epub->getCumulativeSpineItemSize(targetSpineIndex);
|
||||||
|
const size_t spineSize = (cumulative > prevCumulative) ? (cumulative - prevCumulative) : 0;
|
||||||
|
// Store a normalized position within the spine so it can be applied once loaded.
|
||||||
|
pendingSpineProgress =
|
||||||
|
(spineSize == 0) ? 0.0f : static_cast<float>(targetSize - prevCumulative) / static_cast<float>(spineSize);
|
||||||
|
if (pendingSpineProgress < 0.0f) {
|
||||||
|
pendingSpineProgress = 0.0f;
|
||||||
|
} else if (pendingSpineProgress > 1.0f) {
|
||||||
|
pendingSpineProgress = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset state so renderScreen() reloads and repositions on the target spine.
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
currentSpineIndex = targetSpineIndex;
|
||||||
|
nextPageNumber = 0;
|
||||||
|
pendingPercentJump = true;
|
||||||
|
section.reset();
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the overall reading position as a percent of the book.
|
||||||
|
float EpubReaderActivity::getBookProgressPercent() const {
|
||||||
|
if (!epub || epub->getBookSize() == 0 || !section || section->pageCount == 0) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimate within-spine progress based on the current page.
|
||||||
|
const float chapterProgress = static_cast<float>(section->currentPage) / static_cast<float>(section->pageCount);
|
||||||
|
|
||||||
|
// Convert to overall progress using cumulative spine sizes.
|
||||||
|
return epub->calculateProgress(currentSpineIndex, chapterProgress) * 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action) {
|
void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case EpubReaderMenuActivity::MenuAction::SELECT_CHAPTER: {
|
case EpubReaderMenuActivity::MenuAction::SELECT_CHAPTER: {
|
||||||
@ -268,6 +355,27 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
|||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case EpubReaderMenuActivity::MenuAction::GO_TO_PERCENT: {
|
||||||
|
// Launch the slider-based percent selector and return here on confirm/cancel.
|
||||||
|
const int initialPercent = clampPercent(static_cast<int>(getBookProgressPercent() + 0.5f));
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new EpubReaderPercentSelectionActivity(
|
||||||
|
renderer, mappedInput, initialPercent,
|
||||||
|
[this](const int percent) {
|
||||||
|
// Apply the new position and exit back to the reader.
|
||||||
|
jumpToPercent(percent);
|
||||||
|
exitActivity();
|
||||||
|
updateRequired = true;
|
||||||
|
},
|
||||||
|
[this]() {
|
||||||
|
// Cancel selection and return to the reader.
|
||||||
|
exitActivity();
|
||||||
|
updateRequired = true;
|
||||||
|
}));
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EpubReaderMenuActivity::MenuAction::GO_HOME: {
|
case EpubReaderMenuActivity::MenuAction::GO_HOME: {
|
||||||
// 2. Trigger the reader's "Go Home" callback
|
// 2. Trigger the reader's "Go Home" callback
|
||||||
if (onGoHome) {
|
if (onGoHome) {
|
||||||
@ -398,6 +506,16 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
}
|
}
|
||||||
cachedChapterTotalPageCount = 0; // resets to 0 to prevent reading cached progress again
|
cachedChapterTotalPageCount = 0; // resets to 0 to prevent reading cached progress again
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pendingPercentJump && section->pageCount > 0) {
|
||||||
|
// Apply the pending percent jump now that we know the new section's page count.
|
||||||
|
int newPage = static_cast<int>(pendingSpineProgress * static_cast<float>(section->pageCount));
|
||||||
|
if (newPage >= section->pageCount) {
|
||||||
|
newPage = section->pageCount - 1;
|
||||||
|
}
|
||||||
|
section->currentPage = newPage;
|
||||||
|
pendingPercentJump = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
@ -512,8 +630,7 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
|
|||||||
int progressTextWidth = 0;
|
int progressTextWidth = 0;
|
||||||
|
|
||||||
// Calculate progress in book
|
// Calculate progress in book
|
||||||
const float sectionChapterProg = static_cast<float>(section->currentPage) / section->pageCount;
|
const float bookProgress = getBookProgressPercent();
|
||||||
const float bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg) * 100;
|
|
||||||
|
|
||||||
if (showProgressText || showProgressPercentage) {
|
if (showProgressText || showProgressPercentage) {
|
||||||
// Right aligned text for progress counter
|
// Right aligned text for progress counter
|
||||||
|
|||||||
@ -18,6 +18,11 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
|
|||||||
int pagesUntilFullRefresh = 0;
|
int pagesUntilFullRefresh = 0;
|
||||||
int cachedSpineIndex = 0;
|
int cachedSpineIndex = 0;
|
||||||
int cachedChapterTotalPageCount = 0;
|
int cachedChapterTotalPageCount = 0;
|
||||||
|
// Signals that the next render should reposition within the newly loaded section
|
||||||
|
// based on a cross-book percentage jump.
|
||||||
|
bool pendingPercentJump = false;
|
||||||
|
// Normalized 0.0-1.0 progress within the target spine item, computed from book percentage.
|
||||||
|
float pendingSpineProgress = 0.0f;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
const std::function<void()> onGoBack;
|
const std::function<void()> onGoBack;
|
||||||
const std::function<void()> onGoHome;
|
const std::function<void()> onGoHome;
|
||||||
@ -28,7 +33,11 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
|
|||||||
void renderContents(std::unique_ptr<Page> page, int orientedMarginTop, int orientedMarginRight,
|
void renderContents(std::unique_ptr<Page> page, int orientedMarginTop, int orientedMarginRight,
|
||||||
int orientedMarginBottom, int orientedMarginLeft);
|
int orientedMarginBottom, int orientedMarginLeft);
|
||||||
void renderStatusBar(int orientedMarginRight, int orientedMarginBottom, int orientedMarginLeft) const;
|
void renderStatusBar(int orientedMarginRight, int orientedMarginBottom, int orientedMarginLeft) const;
|
||||||
|
// Compute overall book progress as a percentage in the range 0-100.
|
||||||
|
float getBookProgressPercent() const;
|
||||||
void saveProgress(int spineIndex, int currentPage, int pageCount);
|
void saveProgress(int spineIndex, int currentPage, int pageCount);
|
||||||
|
// Jump to a percentage of the book (0-100), mapping it to spine and page.
|
||||||
|
void jumpToPercent(int percent);
|
||||||
void onReaderMenuBack();
|
void onReaderMenuBack();
|
||||||
void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action);
|
void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action);
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,8 @@
|
|||||||
|
|
||||||
class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
||||||
public:
|
public:
|
||||||
enum class MenuAction { SELECT_CHAPTER, GO_HOME, DELETE_CACHE };
|
// Menu actions available from the reader menu.
|
||||||
|
enum class MenuAction { SELECT_CHAPTER, GO_TO_PERCENT, GO_HOME, DELETE_CACHE };
|
||||||
|
|
||||||
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
||||||
const std::function<void()>& onBack, const std::function<void(MenuAction)>& onAction)
|
const std::function<void()>& onBack, const std::function<void(MenuAction)>& onAction)
|
||||||
@ -32,7 +33,9 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
|||||||
std::string label;
|
std::string label;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fixed menu layout (order matters for up/down navigation).
|
||||||
const std::vector<MenuItem> menuItems = {{MenuAction::SELECT_CHAPTER, "Go to Chapter"},
|
const std::vector<MenuItem> menuItems = {{MenuAction::SELECT_CHAPTER, "Go to Chapter"},
|
||||||
|
{MenuAction::GO_TO_PERCENT, "Go to %"},
|
||||||
{MenuAction::GO_HOME, "Go Home"},
|
{MenuAction::GO_HOME, "Go Home"},
|
||||||
{MenuAction::DELETE_CACHE, "Delete Book Cache"}};
|
{MenuAction::DELETE_CACHE, "Delete Book Cache"}};
|
||||||
|
|
||||||
|
|||||||
138
src/activities/reader/EpubReaderPercentSelectionActivity.cpp
Normal file
138
src/activities/reader/EpubReaderPercentSelectionActivity.cpp
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#include "EpubReaderPercentSelectionActivity.h"
|
||||||
|
|
||||||
|
#include <GfxRenderer.h>
|
||||||
|
|
||||||
|
#include "MappedInputManager.h"
|
||||||
|
#include "fontIds.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Fine/coarse slider step sizes for percent adjustments.
|
||||||
|
constexpr int kSmallStep = 1;
|
||||||
|
constexpr int kLargeStep = 10;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void EpubReaderPercentSelectionActivity::onEnter() {
|
||||||
|
ActivityWithSubactivity::onEnter();
|
||||||
|
// Set up rendering task and mark first frame dirty.
|
||||||
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
updateRequired = true;
|
||||||
|
xTaskCreate(&EpubReaderPercentSelectionActivity::taskTrampoline, "EpubPercentSlider", 4096, this, 1,
|
||||||
|
&displayTaskHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubReaderPercentSelectionActivity::onExit() {
|
||||||
|
ActivityWithSubactivity::onExit();
|
||||||
|
// Ensure the render task is stopped before freeing the mutex.
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
if (displayTaskHandle) {
|
||||||
|
vTaskDelete(displayTaskHandle);
|
||||||
|
displayTaskHandle = nullptr;
|
||||||
|
}
|
||||||
|
vSemaphoreDelete(renderingMutex);
|
||||||
|
renderingMutex = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubReaderPercentSelectionActivity::taskTrampoline(void* param) {
|
||||||
|
auto* self = static_cast<EpubReaderPercentSelectionActivity*>(param);
|
||||||
|
self->displayTaskLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubReaderPercentSelectionActivity::displayTaskLoop() {
|
||||||
|
while (true) {
|
||||||
|
// Render only when the view is dirty and no subactivity is running.
|
||||||
|
if (updateRequired && !subActivity) {
|
||||||
|
updateRequired = false;
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
renderScreen();
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
}
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubReaderPercentSelectionActivity::adjustPercent(const int delta) {
|
||||||
|
// Apply delta and clamp within 0-100.
|
||||||
|
percent += delta;
|
||||||
|
if (percent < 0) {
|
||||||
|
percent = 0;
|
||||||
|
} else if (percent > 100) {
|
||||||
|
percent = 100;
|
||||||
|
}
|
||||||
|
updateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubReaderPercentSelectionActivity::loop() {
|
||||||
|
if (subActivity) {
|
||||||
|
subActivity->loop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back cancels, confirm selects, arrows adjust the percent.
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||||
|
onCancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
|
onSelect(percent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Left)) {
|
||||||
|
adjustPercent(-kSmallStep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Right)) {
|
||||||
|
adjustPercent(kSmallStep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Up)) {
|
||||||
|
adjustPercent(kLargeStep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Down)) {
|
||||||
|
adjustPercent(-kLargeStep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubReaderPercentSelectionActivity::renderScreen() {
|
||||||
|
renderer.clearScreen();
|
||||||
|
|
||||||
|
// Title and numeric percent value.
|
||||||
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Go to Position", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
|
const std::string percentText = std::to_string(percent) + "%";
|
||||||
|
renderer.drawCenteredText(UI_12_FONT_ID, 90, percentText.c_str(), true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
|
// Draw slider track.
|
||||||
|
const int screenWidth = renderer.getScreenWidth();
|
||||||
|
constexpr int barWidth = 360;
|
||||||
|
constexpr int barHeight = 16;
|
||||||
|
const int barX = (screenWidth - barWidth) / 2;
|
||||||
|
const int barY = 140;
|
||||||
|
|
||||||
|
renderer.drawRect(barX, barY, barWidth, barHeight);
|
||||||
|
|
||||||
|
// Fill slider based on percent.
|
||||||
|
const int fillWidth = (barWidth - 4) * percent / 100;
|
||||||
|
if (fillWidth > 0) {
|
||||||
|
renderer.fillRect(barX + 2, barY + 2, fillWidth, barHeight - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a simple knob centered at the current percent.
|
||||||
|
const int knobX = barX + 2 + fillWidth - 2;
|
||||||
|
renderer.fillRect(knobX, barY - 4, 4, barHeight + 8, true);
|
||||||
|
|
||||||
|
// Hint text for step sizes.
|
||||||
|
renderer.drawCenteredText(SMALL_FONT_ID, barY + 30, "Left/Right: 1% Up/Down: 10%", true);
|
||||||
|
|
||||||
|
// Button hints follow the current front button layout.
|
||||||
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "-", "+");
|
||||||
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
|
renderer.displayBuffer();
|
||||||
|
}
|
||||||
46
src/activities/reader/EpubReaderPercentSelectionActivity.h
Normal file
46
src/activities/reader/EpubReaderPercentSelectionActivity.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "MappedInputManager.h"
|
||||||
|
#include "activities/ActivityWithSubactivity.h"
|
||||||
|
|
||||||
|
class EpubReaderPercentSelectionActivity final : public ActivityWithSubactivity {
|
||||||
|
public:
|
||||||
|
// Slider-style percent selector for jumping within a book.
|
||||||
|
explicit EpubReaderPercentSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
|
const int initialPercent, const std::function<void(int)>& onSelect,
|
||||||
|
const std::function<void()>& onCancel)
|
||||||
|
: ActivityWithSubactivity("EpubReaderPercentSelection", renderer, mappedInput),
|
||||||
|
percent(initialPercent),
|
||||||
|
onSelect(onSelect),
|
||||||
|
onCancel(onCancel) {}
|
||||||
|
|
||||||
|
void onEnter() override;
|
||||||
|
void onExit() override;
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Current percent value (0-100) shown on the slider.
|
||||||
|
int percent = 0;
|
||||||
|
// Render dirty flag for the task loop.
|
||||||
|
bool updateRequired = false;
|
||||||
|
// FreeRTOS task and mutex for rendering.
|
||||||
|
TaskHandle_t displayTaskHandle = nullptr;
|
||||||
|
SemaphoreHandle_t renderingMutex = nullptr;
|
||||||
|
|
||||||
|
// Callback invoked when the user confirms a percent.
|
||||||
|
const std::function<void(int)> onSelect;
|
||||||
|
// Callback invoked when the user cancels the slider.
|
||||||
|
const std::function<void()> onCancel;
|
||||||
|
|
||||||
|
static void taskTrampoline(void* param);
|
||||||
|
[[noreturn]] void displayTaskLoop();
|
||||||
|
// Render the slider UI.
|
||||||
|
void renderScreen();
|
||||||
|
// Change the current percent by a delta and clamp within bounds.
|
||||||
|
void adjustPercent(int delta);
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user