Merge 4824247137ec0c41b57ac90d2a835f0dc1798b0b into 3ce11f14ce7bc3ce1f2f040bfb09a9b3d9f87f72
This commit is contained in:
commit
c6c2d5dfb6
@ -166,10 +166,10 @@ The role of the volume (side) buttons can be swapped in **[Settings](#35-setting
|
|||||||
If the **Short Power Button Click** setting is set to "Page Turn", you can also turn to the next page by briefly pressing the Power button.
|
If the **Short Power Button Click** setting is set to "Page Turn", you can also turn to the next page by briefly pressing the Power button.
|
||||||
|
|
||||||
### Chapter Navigation
|
### Chapter Navigation
|
||||||
* **Next Chapter:** Press and **hold** the **Right** (or **Volume Down**) button briefly, then release.
|
* **Next Chapter:** Press and **hold** the **Right** (or **Volume Down**) button for 2 seconds, then release.
|
||||||
* **Previous Chapter:** Press and **hold** the **Left** (or **Volume Up**) button briefly, then release.
|
* **Previous Chapter:** Press and **hold** the **Left** (or **Volume Up**) button for 2 seconds, then release.
|
||||||
|
|
||||||
This feature can be disabled in **[Settings](#35-settings)** to help avoid changing chapters by mistake.
|
This feature can be disabled in **[Settings](#35-settings)** where the long-press hold time can also be configured.
|
||||||
|
|
||||||
|
|
||||||
### System Navigation
|
### System Navigation
|
||||||
|
|||||||
@ -14,7 +14,7 @@ CrossPointSettings CrossPointSettings::instance;
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||||
// Increment this when adding new persisted settings fields
|
// Increment this when adding new persisted settings fields
|
||||||
constexpr uint8_t SETTINGS_COUNT = 20;
|
constexpr uint8_t SETTINGS_COUNT = 21;
|
||||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -49,6 +49,7 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writePod(outputFile, hideBatteryPercentage);
|
serialization::writePod(outputFile, hideBatteryPercentage);
|
||||||
serialization::writePod(outputFile, longPressChapterSkip);
|
serialization::writePod(outputFile, longPressChapterSkip);
|
||||||
serialization::writePod(outputFile, hyphenationEnabled);
|
serialization::writePod(outputFile, hyphenationEnabled);
|
||||||
|
serialization::writePod(outputFile, longPressDuration);
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||||
@ -120,6 +121,8 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
serialization::readPod(inputFile, hyphenationEnabled);
|
serialization::readPod(inputFile, hyphenationEnabled);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, longPressDuration);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
@ -195,6 +198,22 @@ int CrossPointSettings::getRefreshFrequency() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long CrossPointSettings::getLongPressDurationMs() const {
|
||||||
|
switch (longPressDuration) {
|
||||||
|
case LONG_PRESS_DURATION::LP_1S:
|
||||||
|
return 1UL * 1000;
|
||||||
|
case LONG_PRESS_DURATION::LP_2S:
|
||||||
|
default:
|
||||||
|
return 2UL * 1000;
|
||||||
|
case LONG_PRESS_DURATION::LP_3S:
|
||||||
|
return 3UL * 1000;
|
||||||
|
case LONG_PRESS_DURATION::LP_5S:
|
||||||
|
return 5UL * 1000;
|
||||||
|
case LONG_PRESS_DURATION::LP_10S:
|
||||||
|
return 10UL * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int CrossPointSettings::getReaderFontId() const {
|
int CrossPointSettings::getReaderFontId() const {
|
||||||
switch (fontFamily) {
|
switch (fontFamily) {
|
||||||
case BOOKERLY:
|
case BOOKERLY:
|
||||||
|
|||||||
@ -55,6 +55,9 @@ class CrossPointSettings {
|
|||||||
// Short power button press actions
|
// Short power button press actions
|
||||||
enum SHORT_PWRBTN { IGNORE = 0, SLEEP = 1, PAGE_TURN = 2 };
|
enum SHORT_PWRBTN { IGNORE = 0, SLEEP = 1, PAGE_TURN = 2 };
|
||||||
|
|
||||||
|
// Long-press duration options
|
||||||
|
enum LONG_PRESS_DURATION { LP_1S = 0, LP_2S = 1, LP_3S = 2, LP_5S = 3, LP_10S = 4 };
|
||||||
|
|
||||||
// Hide battery percentage
|
// Hide battery percentage
|
||||||
enum HIDE_BATTERY_PERCENTAGE { HIDE_NEVER = 0, HIDE_READER = 1, HIDE_ALWAYS = 2 };
|
enum HIDE_BATTERY_PERCENTAGE { HIDE_NEVER = 0, HIDE_READER = 1, HIDE_ALWAYS = 2 };
|
||||||
|
|
||||||
@ -94,6 +97,7 @@ class CrossPointSettings {
|
|||||||
uint8_t hideBatteryPercentage = HIDE_NEVER;
|
uint8_t hideBatteryPercentage = HIDE_NEVER;
|
||||||
// Long-press chapter skip on side buttons
|
// Long-press chapter skip on side buttons
|
||||||
uint8_t longPressChapterSkip = 1;
|
uint8_t longPressChapterSkip = 1;
|
||||||
|
uint8_t longPressDuration = LP_2S;
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
@ -111,6 +115,7 @@ class CrossPointSettings {
|
|||||||
float getReaderLineCompression() const;
|
float getReaderLineCompression() const;
|
||||||
unsigned long getSleepTimeoutMs() const;
|
unsigned long getSleepTimeoutMs() const;
|
||||||
int getRefreshFrequency() const;
|
int getRefreshFrequency() const;
|
||||||
|
unsigned long getLongPressDurationMs() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper macro to access settings
|
// Helper macro to access settings
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int PAGE_ITEMS = 23;
|
constexpr int PAGE_ITEMS = 23;
|
||||||
constexpr int SKIP_PAGE_MS = 700;
|
|
||||||
constexpr char OPDS_ROOT_PATH[] = "opds"; // No leading slash - relative to server URL
|
constexpr char OPDS_ROOT_PATH[] = "opds"; // No leading slash - relative to server URL
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -123,7 +122,7 @@ void OpdsBookBrowserActivity::loop() {
|
|||||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
const bool skipPage = mappedInput.getHeldTime() > SETTINGS.getLongPressDurationMs();
|
||||||
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
if (!entries.empty()) {
|
if (!entries.empty()) {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
#include "RecentBooksStore.h"
|
#include "RecentBooksStore.h"
|
||||||
#include "ScreenComponents.h"
|
#include "ScreenComponents.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
@ -20,7 +21,6 @@ constexpr int LEFT_MARGIN = 20;
|
|||||||
constexpr int RIGHT_MARGIN = 40; // Extra space for scroll indicator
|
constexpr int RIGHT_MARGIN = 40; // Extra space for scroll indicator
|
||||||
|
|
||||||
// Timing thresholds
|
// Timing thresholds
|
||||||
constexpr int SKIP_PAGE_MS = 700;
|
|
||||||
constexpr unsigned long GO_HOME_MS = 1000;
|
constexpr unsigned long GO_HOME_MS = 1000;
|
||||||
|
|
||||||
void sortFileList(std::vector<std::string>& strs) {
|
void sortFileList(std::vector<std::string>& strs) {
|
||||||
@ -201,7 +201,7 @@ void MyLibraryActivity::loop() {
|
|||||||
const bool leftReleased = mappedInput.wasReleased(MappedInputManager::Button::Left);
|
const bool leftReleased = mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||||
const bool rightReleased = mappedInput.wasReleased(MappedInputManager::Button::Right);
|
const bool rightReleased = mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||||
|
|
||||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
const bool skipPage = mappedInput.getHeldTime() > SETTINGS.getLongPressDurationMs();
|
||||||
|
|
||||||
// Confirm button - open selected item
|
// Confirm button - open selected item
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// pagesPerRefresh now comes from SETTINGS.getRefreshFrequency()
|
// pagesPerRefresh now comes from SETTINGS.getRefreshFrequency()
|
||||||
constexpr unsigned long skipChapterMs = 700;
|
|
||||||
constexpr unsigned long goHomeMs = 1000;
|
constexpr unsigned long goHomeMs = 1000;
|
||||||
constexpr int statusBarMargin = 19;
|
constexpr int statusBarMargin = 19;
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -182,16 +181,18 @@ void EpubReaderActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool skipChapter = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipChapterMs;
|
const bool skipChapter = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > SETTINGS.getLongPressDurationMs();
|
||||||
|
|
||||||
if (skipChapter) {
|
if (skipChapter) {
|
||||||
// We don't want to delete the section mid-render, so grab the semaphore
|
// We don't want to delete the section mid-render, so grab the semaphore
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
nextPageNumber = 0;
|
// Show immediate feedback for long-press skip, then schedule delayed action (500ms)
|
||||||
currentSpineIndex = nextReleased ? currentSpineIndex + 1 : currentSpineIndex - 1;
|
showSkipPopup("Skipping");
|
||||||
section.reset();
|
delayedSkipPending = true;
|
||||||
|
delayedSkipDir = nextReleased ? +1 : -1;
|
||||||
|
delayedSkipExecuteAtMs = millis() + 500;
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
updateRequired = true;
|
// Do not perform the skip immediately; it will be executed in display loop after delay
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,11 +231,21 @@ void EpubReaderActivity::loop() {
|
|||||||
|
|
||||||
void EpubReaderActivity::displayTaskLoop() {
|
void EpubReaderActivity::displayTaskLoop() {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
const uint32_t now = millis();
|
||||||
if (updateRequired) {
|
if (updateRequired) {
|
||||||
updateRequired = false;
|
updateRequired = false;
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
renderScreen();
|
renderScreen();
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
|
} else if (delayedSkipPending && now >= delayedSkipExecuteAtMs) {
|
||||||
|
// Execute the delayed chapter skip now
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
nextPageNumber = 0;
|
||||||
|
currentSpineIndex += delayedSkipDir;
|
||||||
|
section.reset();
|
||||||
|
delayedSkipPending = false;
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
@ -386,6 +397,19 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EpubReaderActivity::showSkipPopup(const char* text) {
|
||||||
|
constexpr int boxMargin = 20;
|
||||||
|
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, text);
|
||||||
|
const int boxWidth = textWidth + boxMargin * 2;
|
||||||
|
const int boxHeight = renderer.getLineHeight(UI_12_FONT_ID) + boxMargin * 2;
|
||||||
|
const int boxX = (renderer.getScreenWidth() - boxWidth) / 2;
|
||||||
|
constexpr int boxY = 50;
|
||||||
|
renderer.fillRect(boxX, boxY, boxWidth, boxHeight, false);
|
||||||
|
renderer.drawText(UI_12_FONT_ID, boxX + boxMargin, boxY + boxMargin, text);
|
||||||
|
renderer.drawRect(boxX + 5, boxY + 5, boxWidth - 10, boxHeight - 10);
|
||||||
|
renderer.displayBuffer(EInkDisplay::FAST_REFRESH);
|
||||||
|
}
|
||||||
|
|
||||||
void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int orientedMarginTop,
|
void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int orientedMarginTop,
|
||||||
const int orientedMarginRight, const int orientedMarginBottom,
|
const int orientedMarginRight, const int orientedMarginBottom,
|
||||||
const int orientedMarginLeft) {
|
const int orientedMarginLeft) {
|
||||||
|
|||||||
@ -16,6 +16,9 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
|
|||||||
int nextPageNumber = 0;
|
int nextPageNumber = 0;
|
||||||
int pagesUntilFullRefresh = 0;
|
int pagesUntilFullRefresh = 0;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
|
bool delayedSkipPending = false;
|
||||||
|
int delayedSkipDir = 0;
|
||||||
|
uint32_t delayedSkipExecuteAtMs = 0;
|
||||||
const std::function<void()> onGoBack;
|
const std::function<void()> onGoBack;
|
||||||
const std::function<void()> onGoHome;
|
const std::function<void()> onGoHome;
|
||||||
|
|
||||||
@ -25,6 +28,7 @@ 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;
|
||||||
|
void showSkipPopup(const char* text);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EpubReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Epub> epub,
|
explicit EpubReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Epub> epub,
|
||||||
|
|||||||
@ -4,12 +4,11 @@
|
|||||||
|
|
||||||
#include "KOReaderCredentialStore.h"
|
#include "KOReaderCredentialStore.h"
|
||||||
#include "KOReaderSyncActivity.h"
|
#include "KOReaderSyncActivity.h"
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Time threshold for treating a long press as a page-up/page-down
|
|
||||||
constexpr int SKIP_PAGE_MS = 700;
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool EpubReaderChapterSelectionActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); }
|
bool EpubReaderChapterSelectionActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); }
|
||||||
@ -124,7 +123,7 @@ void EpubReaderChapterSelectionActivity::loop() {
|
|||||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||||
|
|
||||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
const bool skipPage = mappedInput.getHeldTime() > SETTINGS.getLongPressDurationMs();
|
||||||
const int pageItems = getPageItems();
|
const int pageItems = getPageItems();
|
||||||
const int totalItems = getTotalItems();
|
const int totalItems = getTotalItems();
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@
|
|||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr unsigned long skipPageMs = 700;
|
|
||||||
constexpr unsigned long goHomeMs = 1000;
|
constexpr unsigned long goHomeMs = 1000;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -129,10 +128,20 @@ void XtcReaderActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool skipPages = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipPageMs;
|
const bool skipPages = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > SETTINGS.getLongPressDurationMs();
|
||||||
const int skipAmount = skipPages ? 10 : 1;
|
const int skipAmount = skipPages ? 10 : 1;
|
||||||
|
|
||||||
if (prevReleased) {
|
if (prevReleased) {
|
||||||
|
if (skipPages) {
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
showSkipPopup("Skipping");
|
||||||
|
delayedSkipPending = true;
|
||||||
|
delayedSkipDir = -1;
|
||||||
|
delayedSkipAmount = skipAmount;
|
||||||
|
delayedSkipExecuteAtMs = millis() + 500;
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (currentPage >= static_cast<uint32_t>(skipAmount)) {
|
if (currentPage >= static_cast<uint32_t>(skipAmount)) {
|
||||||
currentPage -= skipAmount;
|
currentPage -= skipAmount;
|
||||||
} else {
|
} else {
|
||||||
@ -140,6 +149,16 @@ void XtcReaderActivity::loop() {
|
|||||||
}
|
}
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else if (nextReleased) {
|
} else if (nextReleased) {
|
||||||
|
if (skipPages) {
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
showSkipPopup("Skipping");
|
||||||
|
delayedSkipPending = true;
|
||||||
|
delayedSkipDir = +1;
|
||||||
|
delayedSkipAmount = skipAmount;
|
||||||
|
delayedSkipExecuteAtMs = millis() + 500;
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
currentPage += skipAmount;
|
currentPage += skipAmount;
|
||||||
if (currentPage >= xtc->getPageCount()) {
|
if (currentPage >= xtc->getPageCount()) {
|
||||||
currentPage = xtc->getPageCount(); // Allow showing "End of book"
|
currentPage = xtc->getPageCount(); // Allow showing "End of book"
|
||||||
@ -150,11 +169,29 @@ void XtcReaderActivity::loop() {
|
|||||||
|
|
||||||
void XtcReaderActivity::displayTaskLoop() {
|
void XtcReaderActivity::displayTaskLoop() {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
const uint32_t now = millis();
|
||||||
if (updateRequired) {
|
if (updateRequired) {
|
||||||
updateRequired = false;
|
updateRequired = false;
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
renderScreen();
|
renderScreen();
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
|
} else if (delayedSkipPending && now >= delayedSkipExecuteAtMs) {
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
if (delayedSkipDir < 0) {
|
||||||
|
if (currentPage >= delayedSkipAmount) {
|
||||||
|
currentPage -= delayedSkipAmount;
|
||||||
|
} else {
|
||||||
|
currentPage = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentPage += delayedSkipAmount;
|
||||||
|
if (currentPage >= xtc->getPageCount()) {
|
||||||
|
currentPage = xtc->getPageCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delayedSkipPending = false;
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
@ -178,6 +215,19 @@ void XtcReaderActivity::renderScreen() {
|
|||||||
saveProgress();
|
saveProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XtcReaderActivity::showSkipPopup(const char* text) {
|
||||||
|
constexpr int boxMargin = 20;
|
||||||
|
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, text);
|
||||||
|
const int boxWidth = textWidth + boxMargin * 2;
|
||||||
|
const int boxHeight = renderer.getLineHeight(UI_12_FONT_ID) + boxMargin * 2;
|
||||||
|
const int boxX = (renderer.getScreenWidth() - boxWidth) / 2;
|
||||||
|
constexpr int boxY = 50;
|
||||||
|
renderer.fillRect(boxX, boxY, boxWidth, boxHeight, false);
|
||||||
|
renderer.drawText(UI_12_FONT_ID, boxX + boxMargin, boxY + boxMargin, text);
|
||||||
|
renderer.drawRect(boxX + 5, boxY + 5, boxWidth - 10, boxHeight - 10);
|
||||||
|
renderer.displayBuffer(EInkDisplay::FAST_REFRESH);
|
||||||
|
}
|
||||||
|
|
||||||
void XtcReaderActivity::renderPage() {
|
void XtcReaderActivity::renderPage() {
|
||||||
const uint16_t pageWidth = xtc->getPageWidth();
|
const uint16_t pageWidth = xtc->getPageWidth();
|
||||||
const uint16_t pageHeight = xtc->getPageHeight();
|
const uint16_t pageHeight = xtc->getPageHeight();
|
||||||
@ -360,6 +410,8 @@ void XtcReaderActivity::renderPage() {
|
|||||||
bitDepth);
|
bitDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scheduleSkipMessage removed: delayed skip now handled via delayedSkip* fields
|
||||||
|
|
||||||
void XtcReaderActivity::saveProgress() const {
|
void XtcReaderActivity::saveProgress() const {
|
||||||
FsFile f;
|
FsFile f;
|
||||||
if (SdMan.openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) {
|
if (SdMan.openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) {
|
||||||
|
|||||||
@ -21,6 +21,10 @@ class XtcReaderActivity final : public ActivityWithSubactivity {
|
|||||||
uint32_t currentPage = 0;
|
uint32_t currentPage = 0;
|
||||||
int pagesUntilFullRefresh = 0;
|
int pagesUntilFullRefresh = 0;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
|
bool delayedSkipPending = false;
|
||||||
|
int delayedSkipDir = 0;
|
||||||
|
uint32_t delayedSkipExecuteAtMs = 0;
|
||||||
|
uint32_t delayedSkipAmount = 0;
|
||||||
const std::function<void()> onGoBack;
|
const std::function<void()> onGoBack;
|
||||||
const std::function<void()> onGoHome;
|
const std::function<void()> onGoHome;
|
||||||
|
|
||||||
@ -30,6 +34,7 @@ class XtcReaderActivity final : public ActivityWithSubactivity {
|
|||||||
void renderPage();
|
void renderPage();
|
||||||
void saveProgress() const;
|
void saveProgress() const;
|
||||||
void loadProgress();
|
void loadProgress();
|
||||||
|
void showSkipPopup(const char* text);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit XtcReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Xtc> xtc,
|
explicit XtcReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Xtc> xtc,
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int SKIP_PAGE_MS = 700;
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int XtcReaderChapterSelectionActivity::getPageItems() const {
|
int XtcReaderChapterSelectionActivity::getPageItems() const {
|
||||||
@ -80,7 +80,7 @@ void XtcReaderChapterSelectionActivity::loop() {
|
|||||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||||
|
|
||||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
const bool skipPage = mappedInput.getHeldTime() > SETTINGS.getLongPressDurationMs();
|
||||||
const int pageItems = getPageItems();
|
const int pageItems = getPageItems();
|
||||||
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
|
|||||||
@ -35,13 +35,14 @@ const SettingInfo readerSettings[readerSettingsCount] = {
|
|||||||
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
||||||
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing)};
|
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing)};
|
||||||
|
|
||||||
constexpr int controlsSettingsCount = 4;
|
constexpr int controlsSettingsCount = 5;
|
||||||
const SettingInfo controlsSettings[controlsSettingsCount] = {
|
const SettingInfo controlsSettings[controlsSettingsCount] = {
|
||||||
SettingInfo::Enum("Front Button Layout", &CrossPointSettings::frontButtonLayout,
|
SettingInfo::Enum("Front Button Layout", &CrossPointSettings::frontButtonLayout,
|
||||||
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght"}),
|
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght"}),
|
||||||
SettingInfo::Enum("Side Button Layout (reader)", &CrossPointSettings::sideButtonLayout,
|
SettingInfo::Enum("Side Button Layout (reader)", &CrossPointSettings::sideButtonLayout,
|
||||||
{"Prev, Next", "Next, Prev"}),
|
{"Prev, Next", "Next, Prev"}),
|
||||||
SettingInfo::Toggle("Long-press Chapter Skip", &CrossPointSettings::longPressChapterSkip),
|
SettingInfo::Toggle("Long-press Chapter Skip", &CrossPointSettings::longPressChapterSkip),
|
||||||
|
SettingInfo::Enum("Long-press Duration", &CrossPointSettings::longPressDuration, {"1s", "2s", "3s", "5s", "10s"}),
|
||||||
SettingInfo::Enum("Short Power Button Click", &CrossPointSettings::shortPwrBtn, {"Ignore", "Sleep", "Page Turn"})};
|
SettingInfo::Enum("Short Power Button Click", &CrossPointSettings::shortPwrBtn, {"Ignore", "Sleep", "Page Turn"})};
|
||||||
|
|
||||||
constexpr int systemSettingsCount = 5;
|
constexpr int systemSettingsCount = 5;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user