Refactor duplicate/stale button handler
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -141,110 +141,109 @@ void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) {
|
|||||||
void FileBrowserActivity::loop() {
|
void FileBrowserActivity::loop() {
|
||||||
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false);
|
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false);
|
||||||
|
|
||||||
// Long press BACK always navigates to home
|
ButtonEventManager::ButtonEvent ev;
|
||||||
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= GO_HOME_MS) {
|
while (buttonEvents.consumeEvent(ev)) {
|
||||||
onGoHome();
|
if (ev.button == MappedInputManager::Button::Back) {
|
||||||
return;
|
if (ev.type == ButtonEventManager::PressType::Long) {
|
||||||
}
|
onGoHome();
|
||||||
|
return;
|
||||||
// Short press BACK goes up one directory (if not root) or home (at root)
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back) && mappedInput.getHeldTime() < GO_HOME_MS) {
|
|
||||||
if (basepath != "/") {
|
|
||||||
const std::string oldPath = basepath;
|
|
||||||
basepath.replace(basepath.find_last_of('/'), std::string::npos, "");
|
|
||||||
if (basepath.empty()) basepath = "/";
|
|
||||||
loadFiles();
|
|
||||||
const auto pos = oldPath.find_last_of('/');
|
|
||||||
const std::string dirName = oldPath.substr(pos + 1) + "/";
|
|
||||||
const size_t idx = findEntry(dirName);
|
|
||||||
selectorIndex = (idx < files.size()) ? idx : 0;
|
|
||||||
requestUpdate();
|
|
||||||
} else {
|
|
||||||
onGoHome();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm short press opens selected entry; long press does nothing
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() < GO_HOME_MS) {
|
|
||||||
if (files.empty()) return;
|
|
||||||
|
|
||||||
const std::string& entry = files[selectorIndex];
|
|
||||||
const bool isDirectory = (entry.back() == '/');
|
|
||||||
|
|
||||||
if (isDirectory) {
|
|
||||||
if (basepath.back() != '/') basepath += "/";
|
|
||||||
basepath += entry.substr(0, entry.length() - 1);
|
|
||||||
loadFiles();
|
|
||||||
selectorIndex = 0;
|
|
||||||
requestUpdate();
|
|
||||||
} else {
|
|
||||||
std::string fullPath = basepath;
|
|
||||||
if (fullPath.back() != '/') fullPath += "/";
|
|
||||||
fullPath += entry;
|
|
||||||
ReturnHint hint;
|
|
||||||
hint.target = ReturnTo::FileBrowser;
|
|
||||||
hint.path = basepath;
|
|
||||||
hint.selectName = entry;
|
|
||||||
activityManager.replaceWithReader(std::move(fullPath), std::move(hint));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left short press does nothing; long press deletes selected file after confirmation
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Left)) {
|
|
||||||
if (mappedInput.getHeldTime() < GO_HOME_MS || files.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& entry = files[selectorIndex];
|
|
||||||
const bool isDirectory = (entry.back() == '/');
|
|
||||||
if (isDirectory) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string cleanBase = basepath;
|
|
||||||
if (cleanBase.back() != '/') cleanBase += "/";
|
|
||||||
const std::string fullPath = cleanBase + entry;
|
|
||||||
|
|
||||||
auto handler = [this, fullPath](const ActivityResult& res) {
|
|
||||||
if (!res.isCancelled) {
|
|
||||||
LOG_DBG("FileBrowser", "Attempting to delete: %s", fullPath.c_str());
|
|
||||||
clearFileMetadata(fullPath);
|
|
||||||
if (Storage.remove(fullPath.c_str())) {
|
|
||||||
LOG_DBG("FileBrowser", "Deleted successfully");
|
|
||||||
loadFiles();
|
|
||||||
if (files.empty()) {
|
|
||||||
selectorIndex = 0;
|
|
||||||
} else if (selectorIndex >= files.size()) {
|
|
||||||
selectorIndex = files.size() - 1;
|
|
||||||
}
|
|
||||||
requestUpdate(true);
|
|
||||||
} else {
|
|
||||||
LOG_ERR("FileBrowser", "Failed to delete file: %s", fullPath.c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_DBG("FileBrowser", "Delete cancelled by user");
|
|
||||||
}
|
}
|
||||||
};
|
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||||
|
if (basepath != "/") {
|
||||||
|
const std::string oldPath = basepath;
|
||||||
|
basepath.replace(basepath.find_last_of('/'), std::string::npos, "");
|
||||||
|
if (basepath.empty()) basepath = "/";
|
||||||
|
loadFiles();
|
||||||
|
const auto pos = oldPath.find_last_of('/');
|
||||||
|
const std::string dirName = oldPath.substr(pos + 1) + "/";
|
||||||
|
const size_t idx = findEntry(dirName);
|
||||||
|
selectorIndex = (idx < files.size()) ? idx : 0;
|
||||||
|
requestUpdate();
|
||||||
|
} else {
|
||||||
|
onGoHome();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
startActivityForResult(
|
if (ev.button == MappedInputManager::Button::Confirm && ev.type == ButtonEventManager::PressType::Short) {
|
||||||
std::make_unique<ConfirmationActivity>(renderer, mappedInput, tr(STR_DELETE) + std::string("? "), entry),
|
if (files.empty()) return;
|
||||||
handler);
|
|
||||||
return;
|
const std::string& entry = files[selectorIndex];
|
||||||
}
|
const bool isDirectory = (entry.back() == '/');
|
||||||
|
|
||||||
|
if (isDirectory) {
|
||||||
|
if (basepath.back() != '/') basepath += "/";
|
||||||
|
basepath += entry.substr(0, entry.length() - 1);
|
||||||
|
loadFiles();
|
||||||
|
selectorIndex = 0;
|
||||||
|
requestUpdate();
|
||||||
|
} else {
|
||||||
|
std::string fullPath = basepath;
|
||||||
|
if (fullPath.back() != '/') fullPath += "/";
|
||||||
|
fullPath += entry;
|
||||||
|
ReturnHint hint;
|
||||||
|
hint.target = ReturnTo::FileBrowser;
|
||||||
|
hint.path = basepath;
|
||||||
|
hint.selectName = entry;
|
||||||
|
activityManager.replaceWithReader(std::move(fullPath), std::move(hint));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.button == MappedInputManager::Button::Left && ev.type == ButtonEventManager::PressType::Long) {
|
||||||
|
if (files.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& entry = files[selectorIndex];
|
||||||
|
const bool isDirectory = (entry.back() == '/');
|
||||||
|
if (isDirectory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Right opens the info page for epub files
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Right)) {
|
|
||||||
if (files.empty()) return;
|
|
||||||
const std::string& entry = files[selectorIndex];
|
|
||||||
if (entry.back() != '/' && (FsHelpers::hasEpubExtension(entry) || FsHelpers::hasXtcExtension(entry))) {
|
|
||||||
std::string cleanBase = basepath;
|
std::string cleanBase = basepath;
|
||||||
if (cleanBase.back() != '/') cleanBase += "/";
|
if (cleanBase.back() != '/') cleanBase += "/";
|
||||||
startActivityForResult(std::make_unique<BookInfoActivity>(renderer, mappedInput, cleanBase + entry),
|
const std::string fullPath = cleanBase + entry;
|
||||||
[this](const ActivityResult&) { requestUpdate(); });
|
|
||||||
|
auto handler = [this, fullPath](const ActivityResult& res) {
|
||||||
|
if (!res.isCancelled) {
|
||||||
|
LOG_DBG("FileBrowser", "Attempting to delete: %s", fullPath.c_str());
|
||||||
|
clearFileMetadata(fullPath);
|
||||||
|
if (Storage.remove(fullPath.c_str())) {
|
||||||
|
LOG_DBG("FileBrowser", "Deleted successfully");
|
||||||
|
loadFiles();
|
||||||
|
if (files.empty()) {
|
||||||
|
selectorIndex = 0;
|
||||||
|
} else if (selectorIndex >= files.size()) {
|
||||||
|
selectorIndex = files.size() - 1;
|
||||||
|
}
|
||||||
|
requestUpdate(true);
|
||||||
|
} else {
|
||||||
|
LOG_ERR("FileBrowser", "Failed to delete file: %s", fullPath.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_DBG("FileBrowser", "Delete cancelled by user");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
startActivityForResult(
|
||||||
|
std::make_unique<ConfirmationActivity>(renderer, mappedInput, tr(STR_DELETE) + std::string("? "), entry),
|
||||||
|
handler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.button == MappedInputManager::Button::Right && ev.type == ButtonEventManager::PressType::Short) {
|
||||||
|
if (files.empty()) return;
|
||||||
|
const std::string& entry = files[selectorIndex];
|
||||||
|
if (entry.back() != '/' && (FsHelpers::hasEpubExtension(entry) || FsHelpers::hasXtcExtension(entry))) {
|
||||||
|
std::string cleanBase = basepath;
|
||||||
|
if (cleanBase.back() != '/') cleanBase += "/";
|
||||||
|
startActivityForResult(std::make_unique<BookInfoActivity>(renderer, mappedInput, cleanBase + entry),
|
||||||
|
[this](const ActivityResult&) { requestUpdate(); });
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up/Down side buttons navigate the list
|
// Up/Down side buttons navigate the list
|
||||||
|
|||||||
@@ -248,66 +248,64 @@ void EpubReaderActivity::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Long press CONFIRM (1s+) goes directly to KOReader sync when credentials are configured.
|
ButtonEventManager::ButtonEvent ev;
|
||||||
// We intentionally keep long-press on the richer compare flow so advanced
|
while (buttonEvents.consumeEvent(ev)) {
|
||||||
// conflict-resolution behavior stays available even after simplifying menu UX.
|
if (ev.button == MappedInputManager::Button::Confirm) {
|
||||||
// Without credentials, fall through to the regular menu on release.
|
if (ev.type == ButtonEventManager::PressType::Long && KOREADER_STORE.hasCredentials()) {
|
||||||
if (mappedInput.isPressed(MappedInputManager::Button::Confirm) &&
|
launchKOReaderSync(SyncLaunchMode::COMPARE);
|
||||||
mappedInput.getHeldTime() >= ReaderUtils::GO_HOME_MS && KOREADER_STORE.hasCredentials()) {
|
return;
|
||||||
launchKOReaderSync(SyncLaunchMode::COMPARE);
|
}
|
||||||
return;
|
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||||
}
|
const int currentPage = section ? section->currentPage + 1 : 0;
|
||||||
|
const int totalPages = section ? section->pageCount : 0;
|
||||||
// Short press CONFIRM enters reader menu activity.
|
float bookProgress = 0.0f;
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) &&
|
if (epub->getBookSize() > 0 && section && section->pageCount > 0) {
|
||||||
mappedInput.getHeldTime() < ReaderUtils::GO_HOME_MS) {
|
const float chapterProgress =
|
||||||
const int currentPage = section ? section->currentPage + 1 : 0;
|
static_cast<float>(section->currentPage) / static_cast<float>(section->pageCount);
|
||||||
const int totalPages = section ? section->pageCount : 0;
|
bookProgress = epub->calculateProgress(currentSpineIndex, chapterProgress) * 100.0f;
|
||||||
float bookProgress = 0.0f;
|
}
|
||||||
if (epub->getBookSize() > 0 && section && section->pageCount > 0) {
|
const int bookProgressPercent = clampPercent(static_cast<int>(bookProgress + 0.5f));
|
||||||
const float chapterProgress = static_cast<float>(section->currentPage) / static_cast<float>(section->pageCount);
|
const bool isCurrentPageStarred = section && bookmarkStore.has(static_cast<uint16_t>(currentSpineIndex),
|
||||||
bookProgress = epub->calculateProgress(currentSpineIndex, chapterProgress) * 100.0f;
|
static_cast<uint16_t>(section->currentPage));
|
||||||
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
|
startActivityForResult(
|
||||||
|
std::make_unique<EpubReaderMenuActivity>(
|
||||||
|
renderer, mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent,
|
||||||
|
SETTINGS.orientation, !currentPageFootnotes.empty(), bookEmbeddedStyleOverride,
|
||||||
|
bookImageRenderingOverride, bookFontFamilyOverride, bookFontSizeOverride, SETTINGS.textDarkness,
|
||||||
|
!bookmarkStore.isEmpty(), isCurrentPageStarred),
|
||||||
|
[this](const ActivityResult& result) {
|
||||||
|
// Always apply orientation/darkness change even if the menu was cancelled
|
||||||
|
const auto& menu = std::get<MenuResult>(result.data);
|
||||||
|
applyOrientation(menu.orientation);
|
||||||
|
applyTextDarkness(menu.textDarkness);
|
||||||
|
toggleAutoPageTurn(menu.pageTurnOption);
|
||||||
|
applyBookReaderOverrides(menu.embeddedStyleOverride, menu.imageRenderingOverride, menu.fontFamilyOverride,
|
||||||
|
menu.fontSizeOverride);
|
||||||
|
if (!result.isCancelled) {
|
||||||
|
onReaderMenuConfirm(static_cast<EpubReaderMenuActivity::MenuAction>(menu.action));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const int bookProgressPercent = clampPercent(static_cast<int>(bookProgress + 0.5f));
|
|
||||||
const bool isCurrentPageStarred = section && bookmarkStore.has(static_cast<uint16_t>(currentSpineIndex),
|
|
||||||
static_cast<uint16_t>(section->currentPage));
|
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
|
||||||
startActivityForResult(std::make_unique<EpubReaderMenuActivity>(
|
|
||||||
renderer, mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent,
|
|
||||||
SETTINGS.orientation, !currentPageFootnotes.empty(), bookEmbeddedStyleOverride,
|
|
||||||
bookImageRenderingOverride, bookFontFamilyOverride, bookFontSizeOverride,
|
|
||||||
SETTINGS.textDarkness, !bookmarkStore.isEmpty(), isCurrentPageStarred),
|
|
||||||
[this](const ActivityResult& result) {
|
|
||||||
// Always apply orientation/darkness change even if the menu was cancelled
|
|
||||||
const auto& menu = std::get<MenuResult>(result.data);
|
|
||||||
applyOrientation(menu.orientation);
|
|
||||||
applyTextDarkness(menu.textDarkness);
|
|
||||||
toggleAutoPageTurn(menu.pageTurnOption);
|
|
||||||
applyBookReaderOverrides(menu.embeddedStyleOverride, menu.imageRenderingOverride,
|
|
||||||
menu.fontFamilyOverride, menu.fontSizeOverride);
|
|
||||||
if (!result.isCancelled) {
|
|
||||||
onReaderMenuConfirm(static_cast<EpubReaderMenuActivity::MenuAction>(menu.action));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Long press BACK (1s+) goes to home screen
|
if (ev.button == MappedInputManager::Button::Back) {
|
||||||
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= ReaderUtils::GO_HOME_MS) {
|
if (ev.type == ButtonEventManager::PressType::Long) {
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
onGoHome();
|
onGoHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||||
// Short press BACK returns to the calling activity (or restores position if viewing footnote)
|
if (footnoteDepth > 0) {
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back) &&
|
restoreSavedPosition();
|
||||||
mappedInput.getHeldTime() < ReaderUtils::GO_HOME_MS) {
|
return;
|
||||||
if (footnoteDepth > 0) {
|
}
|
||||||
restoreSavedPosition();
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
return;
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
|
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
|
||||||
@@ -327,65 +325,6 @@ void EpubReaderActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool skipChapter = mappedInput.getHeldTime() > skipChapterMs;
|
|
||||||
|
|
||||||
// Chapter skip navigates by TOC entries, not spine boundaries.
|
|
||||||
// Spine items without their own TOC entry inherit the previous spine's tocIndex
|
|
||||||
// (see BookMetadataCache), so they're treated as continuations of the last chapter.
|
|
||||||
// At the boundaries: skipping forward past the last TOC entry jumps to end-of-book
|
|
||||||
// (clamped in render()); skipping backward before the first TOC entry jumps to the
|
|
||||||
// spine before the current chapter's first spine (clamped to 0 in render()).
|
|
||||||
if (skipChapter) {
|
|
||||||
lastPageTurnTime = millis();
|
|
||||||
{
|
|
||||||
RenderLock lock(*this);
|
|
||||||
|
|
||||||
if (section && section->pageCount > 0) {
|
|
||||||
const int curTocIndex = section->getTocIndexForPage(section->currentPage);
|
|
||||||
const int nextTocIndex = nextTriggered ? curTocIndex + 1 : curTocIndex - 1;
|
|
||||||
|
|
||||||
if (curTocIndex < 0) {
|
|
||||||
// No TOC entry for this spine, fall back to spine-level skip
|
|
||||||
nextPageNumber = 0;
|
|
||||||
currentSpineIndex = nextTriggered ? currentSpineIndex + 1 : currentSpineIndex - 1;
|
|
||||||
section.reset();
|
|
||||||
} else if (nextTocIndex >= 0 && nextTocIndex < epub->getTocItemsCount()) {
|
|
||||||
const int newSpineIndex = epub->getSpineIndexForTocIndex(nextTocIndex);
|
|
||||||
|
|
||||||
if (newSpineIndex == currentSpineIndex) {
|
|
||||||
if (const auto resolvedPage = section->getPageForTocIndex(nextTocIndex)) {
|
|
||||||
section->currentPage = *resolvedPage;
|
|
||||||
} else {
|
|
||||||
LOG_DBG("ERS", "No page boundary for TOC %d in spine %d, staying on current page", nextTocIndex,
|
|
||||||
currentSpineIndex);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pendingTocIndex = nextTocIndex;
|
|
||||||
nextPageNumber = 0;
|
|
||||||
currentSpineIndex = newSpineIndex;
|
|
||||||
section.reset();
|
|
||||||
}
|
|
||||||
} else if (nextTriggered) {
|
|
||||||
// Beyond last TOC entry, go to end of book
|
|
||||||
nextPageNumber = 0;
|
|
||||||
currentSpineIndex = epub->getSpineItemsCount();
|
|
||||||
section.reset();
|
|
||||||
} else {
|
|
||||||
// Before first TOC entry, skip to spine before the current chapter
|
|
||||||
nextPageNumber = 0;
|
|
||||||
currentSpineIndex = epub->getTocItem(curTocIndex).spineIndex - 1;
|
|
||||||
section.reset();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nextPageNumber = 0;
|
|
||||||
currentSpineIndex = nextTriggered ? currentSpineIndex + 1 : currentSpineIndex - 1;
|
|
||||||
section.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requestUpdate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No current section, attempt to rerender the book
|
// No current section, attempt to rerender the book
|
||||||
if (!section) {
|
if (!section) {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr size_t CHUNK_SIZE = 8 * 1024;
|
constexpr size_t CHUNK_SIZE = 8 * 1024;
|
||||||
constexpr size_t MAX_LINE_LENGTH = 64 * 1024;
|
constexpr size_t MAX_LINE_LENGTH = 64 * 1024;
|
||||||
constexpr unsigned long HEADING_SKIP_MS = 700;
|
|
||||||
constexpr uint32_t CACHE_MAGIC = 0x4D4B4449; // "MKDI"
|
constexpr uint32_t CACHE_MAGIC = 0x4D4B4449; // "MKDI"
|
||||||
constexpr uint8_t CACHE_VERSION = 3; // Bumped: nested list indent + task checkboxes
|
constexpr uint8_t CACHE_VERSION = 3; // Bumped: nested list indent + task checkboxes
|
||||||
|
|
||||||
@@ -222,54 +221,55 @@ void MdReaderActivity::onExit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MdReaderActivity::loop() {
|
void MdReaderActivity::loop() {
|
||||||
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= ReaderUtils::GO_HOME_MS) {
|
ButtonEventManager::ButtonEvent ev;
|
||||||
activityManager.goToFileBrowser(txt ? txt->getPath() : "");
|
while (buttonEvents.consumeEvent(ev)) {
|
||||||
return;
|
if (ev.button == MappedInputManager::Button::Back) {
|
||||||
}
|
if (ev.type == ButtonEventManager::PressType::Long) {
|
||||||
|
activityManager.goToFileBrowser(txt ? txt->getPath() : "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||||
|
onGoHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back) &&
|
if (!headings.empty() && ev.button == MappedInputManager::Button::Confirm &&
|
||||||
mappedInput.getHeldTime() < ReaderUtils::GO_HOME_MS) {
|
ev.type == ButtonEventManager::PressType::Short) {
|
||||||
onGoHome();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) && !headings.empty()) {
|
|
||||||
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
|
||||||
startActivityForResult(
|
|
||||||
std::make_unique<MdReaderTocSelectionActivity>(renderer, mappedInput, headings, currentHeadingIndex),
|
|
||||||
[this](const ActivityResult& result) {
|
|
||||||
if (!result.isCancelled) {
|
|
||||||
currentPage = std::get<PageResult>(result.data).page;
|
|
||||||
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
|
||||||
requestUpdate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
|
|
||||||
if (!prevTriggered && !nextTriggered) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool headingSkip = mappedInput.getHeldTime() > HEADING_SKIP_MS;
|
|
||||||
if (headingSkip && !headings.empty()) {
|
|
||||||
jumpToHeading(nextTriggered);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevTriggered && currentPage > 0) {
|
|
||||||
currentPage--;
|
|
||||||
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
|
||||||
requestUpdate();
|
|
||||||
} else if (nextTriggered) {
|
|
||||||
if (currentPage < totalPages - 1) {
|
|
||||||
currentPage++;
|
|
||||||
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
||||||
requestUpdate();
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
} else {
|
startActivityForResult(
|
||||||
onGoHome();
|
std::make_unique<MdReaderTocSelectionActivity>(renderer, mappedInput, headings, currentHeadingIndex),
|
||||||
|
[this](const ActivityResult& result) {
|
||||||
|
if (!result.isCancelled) {
|
||||||
|
currentPage = std::get<PageResult>(result.data).page;
|
||||||
|
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ev.button == MappedInputManager::Button::PageBack || ev.button == MappedInputManager::Button::Left) &&
|
||||||
|
ev.type == ButtonEventManager::PressType::Short) {
|
||||||
|
if (currentPage > 0) {
|
||||||
|
currentPage--;
|
||||||
|
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ev.button == MappedInputManager::Button::PageForward || ev.button == MappedInputManager::Button::Right) &&
|
||||||
|
ev.type == ButtonEventManager::PressType::Short) {
|
||||||
|
if (currentPage < totalPages - 1) {
|
||||||
|
currentPage++;
|
||||||
|
currentHeadingIndex = getHeadingIndexForOffset(pageOffsets[currentPage]);
|
||||||
|
requestUpdate();
|
||||||
|
} else {
|
||||||
|
onGoHome();
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,51 +140,55 @@ void TxtReaderActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Long press BACK (1s+) goes to home screen
|
ButtonEventManager::ButtonEvent ev;
|
||||||
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= ReaderUtils::GO_HOME_MS) {
|
while (buttonEvents.consumeEvent(ev)) {
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
if (ev.button == MappedInputManager::Button::Back) {
|
||||||
onGoHome();
|
if (ev.type == ButtonEventManager::PressType::Long) {
|
||||||
return;
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
}
|
onGoHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||||
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Short press BACK returns to the calling activity
|
if (!bookmarkStore.isEmpty() && ev.button == MappedInputManager::Button::Confirm &&
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back) &&
|
ev.type == ButtonEventManager::PressType::Short) {
|
||||||
mappedInput.getHeldTime() < ReaderUtils::GO_HOME_MS) {
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
startActivityForResult(std::make_unique<StarredPagesActivity>(renderer, mappedInput, bookmarkStore),
|
||||||
finish();
|
[this](const ActivityResult& result) {
|
||||||
return;
|
if (!result.isCancelled) {
|
||||||
}
|
const auto& starred = std::get<StarredPageResult>(result.data);
|
||||||
|
currentPage = starred.pageNumber;
|
||||||
|
if (currentPage >= totalPages) currentPage = totalPages - 1;
|
||||||
|
if (currentPage < 0) currentPage = 0;
|
||||||
|
}
|
||||||
|
requestUpdate();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Open starred pages list via Confirm button
|
if ((ev.button == MappedInputManager::Button::PageBack || ev.button == MappedInputManager::Button::Left) &&
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) && !bookmarkStore.isEmpty()) {
|
ev.type == ButtonEventManager::PressType::Short) {
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
if (currentPage > 0) {
|
||||||
startActivityForResult(std::make_unique<StarredPagesActivity>(renderer, mappedInput, bookmarkStore),
|
currentPage--;
|
||||||
[this](const ActivityResult& result) {
|
requestUpdate();
|
||||||
if (!result.isCancelled) {
|
}
|
||||||
const auto& starred = std::get<StarredPageResult>(result.data);
|
return;
|
||||||
currentPage = starred.pageNumber;
|
}
|
||||||
if (currentPage >= totalPages) currentPage = totalPages - 1;
|
|
||||||
if (currentPage < 0) currentPage = 0;
|
|
||||||
}
|
|
||||||
requestUpdate();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
|
if ((ev.button == MappedInputManager::Button::PageForward || ev.button == MappedInputManager::Button::Right) &&
|
||||||
if (!prevTriggered && !nextTriggered) {
|
ev.type == ButtonEventManager::PressType::Short) {
|
||||||
return;
|
if (currentPage < totalPages - 1) {
|
||||||
}
|
currentPage++;
|
||||||
|
requestUpdate();
|
||||||
if (prevTriggered && currentPage > 0) {
|
} else {
|
||||||
currentPage--;
|
finish();
|
||||||
requestUpdate();
|
}
|
||||||
} else if (nextTriggered) {
|
return;
|
||||||
if (currentPage < totalPages - 1) {
|
|
||||||
currentPage++;
|
|
||||||
requestUpdate();
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,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
|
||||||
|
|
||||||
@@ -65,38 +64,37 @@ void XtcReaderActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter chapter selection activity
|
ButtonEventManager::ButtonEvent ev;
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
while (buttonEvents.consumeEvent(ev)) {
|
||||||
if (xtc && xtc->hasChapters() && !xtc->getChapters().empty()) {
|
if (ev.button == MappedInputManager::Button::Confirm && ev.type == ButtonEventManager::PressType::Short) {
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
if (xtc && xtc->hasChapters() && !xtc->getChapters().empty()) {
|
||||||
startActivityForResult(
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
std::make_unique<XtcReaderChapterSelectionActivity>(renderer, mappedInput, xtc, currentPage),
|
startActivityForResult(
|
||||||
[this](const ActivityResult& result) {
|
std::make_unique<XtcReaderChapterSelectionActivity>(renderer, mappedInput, xtc, currentPage),
|
||||||
if (!result.isCancelled) {
|
[this](const ActivityResult& result) {
|
||||||
currentPage = std::get<PageResult>(result.data).page;
|
if (!result.isCancelled) {
|
||||||
}
|
currentPage = std::get<PageResult>(result.data).page;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.button == MappedInputManager::Button::Back) {
|
||||||
|
if (ev.type == ButtonEventManager::PressType::Long) {
|
||||||
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
|
onGoHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||||
|
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Long press BACK (1s+) goes to home screen
|
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
|
||||||
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
|
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
|
||||||
onGoHome();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short press BACK returns to the calling activity
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back) && mappedInput.getHeldTime() < goHomeMs) {
|
|
||||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool prevTriggered = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
|
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
|
||||||
const bool nextTriggered = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
|
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
|
||||||
|
|
||||||
if (!prevTriggered && !nextTriggered) {
|
if (!prevTriggered && !nextTriggered) {
|
||||||
return;
|
return;
|
||||||
@@ -113,18 +111,13 @@ void XtcReaderActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool skipPages = mappedInput.getHeldTime() > skipPageMs;
|
|
||||||
const int skipAmount = skipPages ? 10 : 1;
|
|
||||||
|
|
||||||
if (prevTriggered) {
|
if (prevTriggered) {
|
||||||
if (currentPage >= static_cast<uint32_t>(skipAmount)) {
|
if (currentPage > 0) {
|
||||||
currentPage -= skipAmount;
|
currentPage--;
|
||||||
} else {
|
requestUpdate();
|
||||||
currentPage = 0;
|
|
||||||
}
|
}
|
||||||
requestUpdate();
|
|
||||||
} else if (nextTriggered) {
|
} else if (nextTriggered) {
|
||||||
currentPage += skipAmount;
|
currentPage++;
|
||||||
if (currentPage >= xtc->getPageCount()) {
|
if (currentPage >= xtc->getPageCount()) {
|
||||||
currentPage = xtc->getPageCount(); // Allow showing "End of book"
|
currentPage = xtc->getPageCount(); // Allow showing "End of book"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -389,16 +389,18 @@ void BmpViewerActivity::loop() {
|
|||||||
Activity::loop();
|
Activity::loop();
|
||||||
|
|
||||||
// Long press BACK (1s+) goes to home screen
|
// Long press BACK (1s+) goes to home screen
|
||||||
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= ReaderUtils::GO_HOME_MS) {
|
ButtonEventManager::ButtonEvent ev;
|
||||||
onGoHome();
|
while (buttonEvents.consumeEvent(ev)) {
|
||||||
return;
|
if (ev.button == MappedInputManager::Button::Back) {
|
||||||
}
|
if (ev.type == ButtonEventManager::PressType::Long) {
|
||||||
|
onGoHome();
|
||||||
// Short press BACK returns to the calling activity
|
return;
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back) &&
|
}
|
||||||
mappedInput.getHeldTime() < ReaderUtils::GO_HOME_MS) {
|
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm: toggle between 1-bit B&W and 4-level grayscale display.
|
// Confirm: toggle between 1-bit B&W and 4-level grayscale display.
|
||||||
|
|||||||
Reference in New Issue
Block a user