mod: overhaul reader menu with long-press actions and quick toggles
Consolidate dictionary items: remove "Lookup Word History" and "Delete Dictionary Cache" from the menu. Long-press on "Lookup Word" opens history; delete-dict-cache is now a sentinel entry at the bottom of the history list. Replace "Reading Orientation" with "Toggle Portrait/Landscape" that toggles between two configurable preferred orientations (new settings: Preferred Portrait, Preferred Landscape). Long-press opens a manual 4-option orientation sub-menu. Add "Toggle Font Size" menu item that cycles through font sizes and applies on menu exit (with section re-layout). Rename "Letterbox Fill" to "Override Letterbox Fill" and "Sync Progress" to "Sync Reading Progress" in reader menu. All long-press flows use ignoreNextConfirmRelease to prevent the button release from triggering actions on the subsequent screen. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -250,8 +250,8 @@ void EpubReaderActivity::loop() {
|
||||
exitActivity();
|
||||
enterNewActivity(new EpubReaderMenuActivity(
|
||||
this->renderer, this->mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent,
|
||||
SETTINGS.orientation, hasDictionary, isBookmarked, epub->getCachePath(),
|
||||
[this](const uint8_t orientation) { onReaderMenuBack(orientation); },
|
||||
SETTINGS.orientation, SETTINGS.fontSize, hasDictionary, isBookmarked, epub->getCachePath(),
|
||||
[this](const uint8_t orientation, const uint8_t fontSize) { onReaderMenuBack(orientation, fontSize); },
|
||||
[this](EpubReaderMenuActivity::MenuAction action) { onReaderMenuConfirm(action); }));
|
||||
}
|
||||
|
||||
@@ -342,11 +342,13 @@ void EpubReaderActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
void EpubReaderActivity::onReaderMenuBack(const uint8_t orientation) {
|
||||
void EpubReaderActivity::onReaderMenuBack(const uint8_t orientation, const uint8_t fontSize) {
|
||||
exitActivity();
|
||||
// Apply the user-selected orientation when the menu is dismissed.
|
||||
// This ensures the menu can be navigated without immediately rotating the screen.
|
||||
applyOrientation(orientation);
|
||||
// Apply font size change (no-op if unchanged).
|
||||
applyFontSize(fontSize);
|
||||
// Force a half refresh on the next render to clear menu/popup artifacts
|
||||
pagesUntilFullRefresh = 1;
|
||||
requestUpdate();
|
||||
@@ -563,24 +565,6 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::DELETE_DICT_CACHE: {
|
||||
if (Dictionary::cacheExists()) {
|
||||
Dictionary::deleteCache();
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
GUI.drawPopup(renderer, tr(STR_DICT_CACHE_DELETED));
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
}
|
||||
} else {
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
GUI.drawPopup(renderer, tr(STR_NO_CACHE_TO_DELETE));
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
}
|
||||
}
|
||||
vTaskDelay(1500 / portTICK_PERIOD_MS);
|
||||
break;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::SELECT_CHAPTER: {
|
||||
// Calculate values BEFORE we start destroying things
|
||||
const int currentP = section ? section->currentPage : 0;
|
||||
@@ -706,7 +690,8 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
||||
exitActivity();
|
||||
enterNewActivity(new LookedUpWordsActivity(
|
||||
renderer, mappedInput, epub->getCachePath(), SETTINGS.getReaderFontId(), SETTINGS.orientation,
|
||||
[this]() { pendingSubactivityExit = true; }, [this]() { pendingSubactivityExit = true; }));
|
||||
[this]() { pendingSubactivityExit = true; }, [this]() { pendingSubactivityExit = true; },
|
||||
true)); // initialSkipRelease: consumed the long-press that triggered this
|
||||
break;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::GO_HOME: {
|
||||
@@ -766,6 +751,7 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
||||
}
|
||||
// Handled locally in the menu activity (cycle on Confirm, never dispatched here)
|
||||
case EpubReaderMenuActivity::MenuAction::ROTATE_SCREEN:
|
||||
case EpubReaderMenuActivity::MenuAction::TOGGLE_FONT_SIZE:
|
||||
case EpubReaderMenuActivity::MenuAction::LETTERBOX_FILL:
|
||||
break;
|
||||
}
|
||||
@@ -798,6 +784,28 @@ void EpubReaderActivity::applyOrientation(const uint8_t orientation) {
|
||||
}
|
||||
}
|
||||
|
||||
void EpubReaderActivity::applyFontSize(const uint8_t fontSize) {
|
||||
if (SETTINGS.fontSize == fontSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Preserve current reading position so we can restore after reflow.
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
if (section) {
|
||||
cachedSpineIndex = currentSpineIndex;
|
||||
cachedChapterTotalPageCount = section->pageCount;
|
||||
nextPageNumber = section->currentPage;
|
||||
}
|
||||
|
||||
SETTINGS.fontSize = fontSize;
|
||||
SETTINGS.saveToFile();
|
||||
|
||||
// Reset section to force re-layout with the new font size.
|
||||
section.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Failure handling
|
||||
void EpubReaderActivity::render(Activity::RenderLock&& lock) {
|
||||
if (!epub) {
|
||||
|
||||
@@ -33,9 +33,10 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
|
||||
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(uint8_t orientation);
|
||||
void onReaderMenuBack(uint8_t orientation, uint8_t fontSize);
|
||||
void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action);
|
||||
void applyOrientation(uint8_t orientation);
|
||||
void applyFontSize(uint8_t fontSize);
|
||||
|
||||
public:
|
||||
explicit EpubReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Epub> epub,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <GfxRenderer.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
@@ -20,6 +21,55 @@ void EpubReaderMenuActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Orientation sub-menu mode ---
|
||||
if (orientationSelectMode) {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
if (ignoreNextConfirmRelease) {
|
||||
ignoreNextConfirmRelease = false;
|
||||
} else {
|
||||
pendingOrientation = static_cast<uint8_t>(orientationSelectIndex);
|
||||
orientationSelectMode = false;
|
||||
requestUpdate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
orientationSelectMode = false;
|
||||
ignoreNextConfirmRelease = false;
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
buttonNavigator.onNext([this] {
|
||||
orientationSelectIndex = ButtonNavigator::nextIndex(orientationSelectIndex,
|
||||
static_cast<int>(orientationLabels.size()));
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onPrevious([this] {
|
||||
orientationSelectIndex = ButtonNavigator::previousIndex(orientationSelectIndex,
|
||||
static_cast<int>(orientationLabels.size()));
|
||||
requestUpdate();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Long-press detection (before release checks) ---
|
||||
if (mappedInput.isPressed(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() >= LONG_PRESS_MS) {
|
||||
const auto selectedAction = menuItems[selectedIndex].action;
|
||||
if (selectedAction == MenuAction::LOOKUP) {
|
||||
ignoreNextConfirmRelease = true;
|
||||
auto cb = onAction;
|
||||
cb(MenuAction::LOOKED_UP_WORDS);
|
||||
return;
|
||||
}
|
||||
if (selectedAction == MenuAction::ROTATE_SCREEN) {
|
||||
orientationSelectMode = true;
|
||||
ignoreNextConfirmRelease = true;
|
||||
orientationSelectIndex = pendingOrientation;
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, static_cast<int>(menuItems.size()));
|
||||
@@ -31,12 +81,29 @@ void EpubReaderMenuActivity::loop() {
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
// Use local variables for items we need to check after potential deletion
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
if (ignoreNextConfirmRelease) {
|
||||
ignoreNextConfirmRelease = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto selectedAction = menuItems[selectedIndex].action;
|
||||
if (selectedAction == MenuAction::ROTATE_SCREEN) {
|
||||
// Cycle orientation preview locally; actual rotation happens on menu exit.
|
||||
pendingOrientation = (pendingOrientation + 1) % orientationLabels.size();
|
||||
// Toggle between the two preferred orientations.
|
||||
// If currently in a portrait-category orientation (Portrait/Inverted), switch to preferredLandscape.
|
||||
// If currently in a landscape-category orientation (CW/CCW), switch to preferredPortrait.
|
||||
const bool isCurrentlyPortrait = (pendingOrientation == CrossPointSettings::PORTRAIT ||
|
||||
pendingOrientation == CrossPointSettings::INVERTED);
|
||||
if (isCurrentlyPortrait) {
|
||||
pendingOrientation = SETTINGS.preferredLandscape;
|
||||
} else {
|
||||
pendingOrientation = SETTINGS.preferredPortrait;
|
||||
}
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
if (selectedAction == MenuAction::TOGGLE_FONT_SIZE) {
|
||||
pendingFontSize = (pendingFontSize + 1) % CrossPointSettings::FONT_SIZE_COUNT;
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
@@ -58,9 +125,9 @@ void EpubReaderMenuActivity::loop() {
|
||||
// 3. CRITICAL: Return immediately. 'this' is likely deleted now.
|
||||
return;
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
// Return the pending orientation to the parent so it can apply on exit.
|
||||
onBack(pendingOrientation);
|
||||
return; // Also return here just in case
|
||||
// Return the pending orientation and font size to the parent so it can apply on exit.
|
||||
onBack(pendingOrientation, pendingFontSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +187,11 @@ void EpubReaderMenuActivity::render(Activity::RenderLock&&) {
|
||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
|
||||
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
|
||||
}
|
||||
if (menuItems[i].action == MenuAction::TOGGLE_FONT_SIZE) {
|
||||
const char* value = I18N.get(fontSizeLabels[pendingFontSize]);
|
||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
|
||||
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
|
||||
}
|
||||
if (menuItems[i].action == MenuAction::LETTERBOX_FILL) {
|
||||
// Render current letterbox fill value on the right edge of the content area.
|
||||
const char* value = I18N.get(letterboxFillLabels[letterboxFillToIndex()]);
|
||||
@@ -128,6 +200,30 @@ void EpubReaderMenuActivity::render(Activity::RenderLock&&) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Orientation sub-menu overlay ---
|
||||
if (orientationSelectMode) {
|
||||
constexpr int popupMargin = 15;
|
||||
constexpr int popupLineHeight = 28;
|
||||
const int optionCount = static_cast<int>(orientationLabels.size());
|
||||
const int popupH = popupMargin * 2 + popupLineHeight * optionCount;
|
||||
const int popupW = contentWidth - 60;
|
||||
const int popupX = contentX + (contentWidth - popupW) / 2;
|
||||
const int popupY = 180 + hintGutterHeight;
|
||||
|
||||
renderer.fillRect(popupX - 2, popupY - 2, popupW + 4, popupH + 4, true);
|
||||
renderer.fillRect(popupX, popupY, popupW, popupH, false);
|
||||
|
||||
for (int i = 0; i < optionCount; ++i) {
|
||||
const int optY = popupY + popupMargin + i * popupLineHeight;
|
||||
const bool isSel = (i == orientationSelectIndex);
|
||||
if (isSel) {
|
||||
renderer.fillRect(popupX + 2, optY, popupW - 4, popupLineHeight, true);
|
||||
}
|
||||
const char* label = I18N.get(orientationLabels[i]);
|
||||
renderer.drawText(UI_10_FONT_ID, popupX + popupMargin, optY, label, !isSel);
|
||||
}
|
||||
}
|
||||
|
||||
// Footer / Hints
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../ActivityWithSubactivity.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "util/BookSettings.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
@@ -19,6 +20,7 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
||||
LOOKUP,
|
||||
LOOKED_UP_WORDS,
|
||||
ROTATE_SCREEN,
|
||||
TOGGLE_FONT_SIZE,
|
||||
LETTERBOX_FILL,
|
||||
SELECT_CHAPTER,
|
||||
GO_TO_BOOKMARK,
|
||||
@@ -26,19 +28,20 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
||||
GO_HOME,
|
||||
SYNC,
|
||||
DELETE_CACHE,
|
||||
DELETE_DICT_CACHE
|
||||
};
|
||||
|
||||
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
||||
const int currentPage, const int totalPages, const int bookProgressPercent,
|
||||
const uint8_t currentOrientation, const bool hasDictionary,
|
||||
const bool isBookmarked, const std::string& bookCachePath,
|
||||
const std::function<void(uint8_t)>& onBack,
|
||||
const uint8_t currentOrientation, const uint8_t currentFontSize,
|
||||
const bool hasDictionary, const bool isBookmarked,
|
||||
const std::string& bookCachePath,
|
||||
const std::function<void(uint8_t, uint8_t)>& onBack,
|
||||
const std::function<void(MenuAction)>& onAction)
|
||||
: ActivityWithSubactivity("EpubReaderMenu", renderer, mappedInput),
|
||||
menuItems(buildMenuItems(hasDictionary, isBookmarked)),
|
||||
title(title),
|
||||
pendingOrientation(currentOrientation),
|
||||
pendingFontSize(currentFontSize),
|
||||
bookCachePath(bookCachePath),
|
||||
currentPage(currentPage),
|
||||
totalPages(totalPages),
|
||||
@@ -68,8 +71,11 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
||||
ButtonNavigator buttonNavigator;
|
||||
std::string title = "Reader Menu";
|
||||
uint8_t pendingOrientation = 0;
|
||||
uint8_t pendingFontSize = 0;
|
||||
const std::vector<StrId> orientationLabels = {StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED,
|
||||
StrId::STR_LANDSCAPE_CCW};
|
||||
const std::vector<StrId> fontSizeLabels = {StrId::STR_SMALL, StrId::STR_MEDIUM, StrId::STR_LARGE,
|
||||
StrId::STR_X_LARGE};
|
||||
std::string bookCachePath;
|
||||
// Letterbox fill override: 0xFF = Default (use global), 0 = Dithered, 1 = Solid, 2 = None
|
||||
uint8_t pendingLetterboxFill = BookSettings::USE_GLOBAL;
|
||||
@@ -80,7 +86,15 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
||||
int totalPages = 0;
|
||||
int bookProgressPercent = 0;
|
||||
|
||||
const std::function<void(uint8_t)> onBack;
|
||||
// Long-press state
|
||||
bool ignoreNextConfirmRelease = false;
|
||||
static constexpr unsigned long LONG_PRESS_MS = 700;
|
||||
|
||||
// Orientation sub-menu state (entered via long-press on Toggle Portrait/Landscape)
|
||||
bool orientationSelectMode = false;
|
||||
int orientationSelectIndex = 0;
|
||||
|
||||
const std::function<void(uint8_t, uint8_t)> onBack;
|
||||
const std::function<void(MenuAction)> onAction;
|
||||
|
||||
// Map the internal override value to an index into letterboxFillLabels.
|
||||
@@ -111,19 +125,16 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
||||
}
|
||||
if (hasDictionary) {
|
||||
items.push_back({MenuAction::LOOKUP, StrId::STR_LOOKUP_WORD});
|
||||
items.push_back({MenuAction::LOOKED_UP_WORDS, StrId::STR_LOOKUP_HISTORY});
|
||||
}
|
||||
items.push_back({MenuAction::ROTATE_SCREEN, StrId::STR_ORIENTATION});
|
||||
items.push_back({MenuAction::LETTERBOX_FILL, StrId::STR_LETTERBOX_FILL});
|
||||
items.push_back({MenuAction::ROTATE_SCREEN, StrId::STR_TOGGLE_ORIENTATION});
|
||||
items.push_back({MenuAction::TOGGLE_FONT_SIZE, StrId::STR_TOGGLE_FONT_SIZE});
|
||||
items.push_back({MenuAction::LETTERBOX_FILL, StrId::STR_OVERRIDE_LETTERBOX_FILL});
|
||||
items.push_back({MenuAction::SELECT_CHAPTER, StrId::STR_TABLE_OF_CONTENTS});
|
||||
items.push_back({MenuAction::GO_TO_BOOKMARK, StrId::STR_GO_TO_BOOKMARK});
|
||||
items.push_back({MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT});
|
||||
items.push_back({MenuAction::GO_HOME, StrId::STR_CLOSE_BOOK});
|
||||
items.push_back({MenuAction::SYNC, StrId::STR_SYNC_PROGRESS});
|
||||
items.push_back({MenuAction::DELETE_CACHE, StrId::STR_DELETE_CACHE});
|
||||
if (hasDictionary) {
|
||||
items.push_back({MenuAction::DELETE_DICT_CACHE, StrId::STR_DELETE_DICT_CACHE});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "LookedUpWordsActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -16,6 +17,9 @@ void LookedUpWordsActivity::onEnter() {
|
||||
ActivityWithSubactivity::onEnter();
|
||||
words = LookupHistory::load(cachePath);
|
||||
std::reverse(words.begin(), words.end());
|
||||
// Append the "Delete Dictionary Cache" sentinel entry
|
||||
words.push_back("\xE2\x80\x94 " + std::string(tr(STR_DELETE_DICT_CACHE)));
|
||||
deleteDictCacheIndex = static_cast<int>(words.size()) - 1;
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
@@ -39,6 +43,7 @@ void LookedUpWordsActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Empty list has only the sentinel entry; if even that's gone, just go back.
|
||||
if (words.empty()) {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
@@ -57,6 +62,10 @@ void LookedUpWordsActivity::loop() {
|
||||
// Confirm delete
|
||||
LookupHistory::removeWord(cachePath, words[pendingDeleteIndex]);
|
||||
words.erase(words.begin() + pendingDeleteIndex);
|
||||
// Adjust sentinel index since we removed an item before it
|
||||
if (deleteDictCacheIndex > pendingDeleteIndex) {
|
||||
deleteDictCacheIndex--;
|
||||
}
|
||||
if (selectedIndex >= static_cast<int>(words.size())) {
|
||||
selectedIndex = std::max(0, static_cast<int>(words.size()) - 1);
|
||||
}
|
||||
@@ -72,9 +81,10 @@ void LookedUpWordsActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect long press on Confirm to trigger delete
|
||||
// Detect long press on Confirm to trigger delete (only for real word entries, not sentinel)
|
||||
constexpr unsigned long DELETE_HOLD_MS = 700;
|
||||
if (mappedInput.isPressed(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() >= DELETE_HOLD_MS) {
|
||||
if (selectedIndex != deleteDictCacheIndex &&
|
||||
mappedInput.isPressed(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() >= DELETE_HOLD_MS) {
|
||||
deleteConfirmMode = true;
|
||||
ignoreNextConfirmRelease = true;
|
||||
pendingDeleteIndex = selectedIndex;
|
||||
@@ -106,6 +116,33 @@ void LookedUpWordsActivity::loop() {
|
||||
});
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
// Consume stale release from long-press navigation into this activity
|
||||
if (ignoreNextConfirmRelease) {
|
||||
ignoreNextConfirmRelease = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the "Delete Dictionary Cache" sentinel entry
|
||||
if (selectedIndex == deleteDictCacheIndex) {
|
||||
if (Dictionary::cacheExists()) {
|
||||
Dictionary::deleteCache();
|
||||
{
|
||||
Activity::RenderLock lock(*this);
|
||||
GUI.drawPopup(renderer, tr(STR_DICT_CACHE_DELETED));
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
}
|
||||
} else {
|
||||
{
|
||||
Activity::RenderLock lock(*this);
|
||||
GUI.drawPopup(renderer, tr(STR_NO_CACHE_TO_DELETE));
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
}
|
||||
}
|
||||
vTaskDelay(1500 / portTICK_PERIOD_MS);
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& headword = words[selectedIndex];
|
||||
|
||||
Rect popupLayout;
|
||||
@@ -197,6 +234,9 @@ void LookedUpWordsActivity::render(Activity::RenderLock&&) {
|
||||
const int contentTop = hintGutterHeight + metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
|
||||
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing;
|
||||
|
||||
// The list always has at least the sentinel entry
|
||||
const bool hasRealWords = (deleteDictCacheIndex > 0);
|
||||
|
||||
if (words.empty()) {
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, contentTop + 20, "No words looked up yet");
|
||||
} else {
|
||||
@@ -234,8 +274,8 @@ void LookedUpWordsActivity::render(Activity::RenderLock&&) {
|
||||
const auto labels = mappedInput.mapLabels("Cancel", "Delete", "", "");
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
} else {
|
||||
// "Hold select to delete" hint above button hints
|
||||
if (!words.empty()) {
|
||||
// "Hold select to delete" hint above button hints (only when real words exist)
|
||||
if (hasRealWords) {
|
||||
const char* deleteHint = "Hold select to delete";
|
||||
const int hintWidth = renderer.getTextWidth(SMALL_FONT_ID, deleteHint);
|
||||
const int hintX = contentX + (renderer.getScreenWidth() - hintGutterWidth - hintWidth) / 2;
|
||||
|
||||
@@ -10,13 +10,14 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity {
|
||||
public:
|
||||
explicit LookedUpWordsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& cachePath,
|
||||
int readerFontId, uint8_t orientation, const std::function<void()>& onBack,
|
||||
const std::function<void()>& onDone)
|
||||
const std::function<void()>& onDone, bool initialSkipRelease = false)
|
||||
: ActivityWithSubactivity("LookedUpWords", renderer, mappedInput),
|
||||
cachePath(cachePath),
|
||||
readerFontId(readerFontId),
|
||||
orientation(orientation),
|
||||
onBack(onBack),
|
||||
onDone(onDone) {}
|
||||
onDone(onDone),
|
||||
ignoreNextConfirmRelease(initialSkipRelease) {}
|
||||
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
@@ -41,5 +42,9 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity {
|
||||
bool ignoreNextConfirmRelease = false;
|
||||
int pendingDeleteIndex = 0;
|
||||
|
||||
// Sentinel index: the "Delete Dictionary Cache" entry at the end of the list.
|
||||
// -1 if not present (shouldn't happen when dictionary exists).
|
||||
int deleteDictCacheIndex = -1;
|
||||
|
||||
int getPageItems() const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user