mod: adapt mod activities to #774 render() pattern

Migrate 5 mod Activity subclasses from old polling-based
display task pattern to the upstream render() super-class
pattern with freeRTOS notification:

- EpubReaderBookmarkSelectionActivity
- DictionaryWordSelectActivity
- DictionarySuggestionsActivity
- DictionaryDefinitionActivity
- LookedUpWordsActivity

Changes: remove own TaskHandle/SemaphoreHandle/updateRequired,
use requestUpdate() + render(RenderLock&&) override, fix
potential deadlocks around enterNewActivity() calls.

Also fix stale conflict marker in EpubReaderMenuActivity.h.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-02-16 13:22:40 -05:00
parent f06e3a0a82
commit 02f2474e3b
11 changed files with 77 additions and 260 deletions

View File

@@ -11,40 +11,14 @@
#include "components/UITheme.h"
#include "fontIds.h"
void DictionaryDefinitionActivity::taskTrampoline(void* param) {
auto* self = static_cast<DictionaryDefinitionActivity*>(param);
self->displayTaskLoop();
}
void DictionaryDefinitionActivity::displayTaskLoop() {
while (true) {
if (updateRequired) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
renderScreen();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void DictionaryDefinitionActivity::onEnter() {
Activity::onEnter();
renderingMutex = xSemaphoreCreateMutex();
wrapText();
updateRequired = true;
xTaskCreate(&DictionaryDefinitionActivity::taskTrampoline, "DictDefTask", 4096, this, 1, &displayTaskHandle);
requestUpdate();
}
void DictionaryDefinitionActivity::onExit() {
Activity::onExit();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
}
// ---------------------------------------------------------------------------
@@ -442,12 +416,12 @@ void DictionaryDefinitionActivity::loop() {
if (prevPage && currentPage > 0) {
currentPage--;
updateRequired = true;
requestUpdate();
}
if (nextPage && currentPage < totalPages - 1) {
currentPage++;
updateRequired = true;
requestUpdate();
}
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
@@ -465,7 +439,7 @@ void DictionaryDefinitionActivity::loop() {
}
}
void DictionaryDefinitionActivity::renderScreen() {
void DictionaryDefinitionActivity::render(Activity::RenderLock&&) {
renderer.clearScreen();
const bool landscape = orientation == CrossPointSettings::ORIENTATION::LANDSCAPE_CW ||

View File

@@ -1,8 +1,5 @@
#pragma once
#include <EpdFontFamily.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <string>
@@ -27,6 +24,7 @@ class DictionaryDefinitionActivity final : public Activity {
void onEnter() override;
void onExit() override;
void loop() override;
void render(Activity::RenderLock&&) override;
private:
// A positioned text segment within a wrapped line (pre-calculated x offset and style).
@@ -61,17 +59,10 @@ class DictionaryDefinitionActivity final : public Activity {
int currentPage = 0;
int linesPerPage = 0;
int totalPages = 0;
bool updateRequired = false;
bool firstRender = true;
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
std::vector<TextAtom> parseHtml(const std::string& html);
static std::string decodeEntity(const std::string& entity);
static bool isRenderableCodepoint(uint32_t cp);
void wrapText();
void renderScreen();
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
};

View File

@@ -8,39 +8,13 @@
#include "fontIds.h"
#include "util/Dictionary.h"
void DictionarySuggestionsActivity::taskTrampoline(void* param) {
auto* self = static_cast<DictionarySuggestionsActivity*>(param);
self->displayTaskLoop();
}
void DictionarySuggestionsActivity::displayTaskLoop() {
while (true) {
if (updateRequired && !subActivity) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
renderScreen();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void DictionarySuggestionsActivity::onEnter() {
ActivityWithSubactivity::onEnter();
renderingMutex = xSemaphoreCreateMutex();
updateRequired = true;
xTaskCreate(&DictionarySuggestionsActivity::taskTrampoline, "DictSugTask", 4096, this, 1, &displayTaskHandle);
requestUpdate();
}
void DictionarySuggestionsActivity::onExit() {
ActivityWithSubactivity::onExit();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
}
void DictionarySuggestionsActivity::loop() {
@@ -49,7 +23,7 @@ void DictionarySuggestionsActivity::loop() {
if (pendingBackFromDef) {
pendingBackFromDef = false;
exitActivity();
updateRequired = true;
requestUpdate();
}
if (pendingExitToReader) {
pendingExitToReader = false;
@@ -68,12 +42,12 @@ void DictionarySuggestionsActivity::loop() {
buttonNavigator.onNext([this] {
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, static_cast<int>(suggestions.size()));
updateRequired = true;
requestUpdate();
});
buttonNavigator.onPrevious([this] {
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, static_cast<int>(suggestions.size()));
updateRequired = true;
requestUpdate();
});
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
@@ -81,10 +55,13 @@ void DictionarySuggestionsActivity::loop() {
std::string definition = Dictionary::lookup(selected);
if (definition.empty()) {
GUI.drawPopup(renderer, "Not found");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
{
Activity::RenderLock lock(*this);
GUI.drawPopup(renderer, "Not found");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
updateRequired = true;
requestUpdate();
return;
}
@@ -100,7 +77,7 @@ void DictionarySuggestionsActivity::loop() {
}
}
void DictionarySuggestionsActivity::renderScreen() {
void DictionarySuggestionsActivity::render(Activity::RenderLock&&) {
renderer.clearScreen();
const auto orient = renderer.getOrientation();

View File

@@ -1,8 +1,4 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <string>
#include <vector>
@@ -28,6 +24,7 @@ class DictionarySuggestionsActivity final : public ActivityWithSubactivity {
void onEnter() override;
void onExit() override;
void loop() override;
void render(Activity::RenderLock&&) override;
private:
std::string originalWord;
@@ -39,15 +36,7 @@ class DictionarySuggestionsActivity final : public ActivityWithSubactivity {
const std::function<void()> onDone;
int selectedIndex = 0;
bool updateRequired = false;
bool pendingBackFromDef = false;
bool pendingExitToReader = false;
ButtonNavigator buttonNavigator;
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
void renderScreen();
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
};

View File

@@ -14,45 +14,19 @@
#include "util/Dictionary.h"
#include "util/LookupHistory.h"
void DictionaryWordSelectActivity::taskTrampoline(void* param) {
auto* self = static_cast<DictionaryWordSelectActivity*>(param);
self->displayTaskLoop();
}
void DictionaryWordSelectActivity::displayTaskLoop() {
while (true) {
if (updateRequired && !subActivity) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
renderScreen();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void DictionaryWordSelectActivity::onEnter() {
ActivityWithSubactivity::onEnter();
renderingMutex = xSemaphoreCreateMutex();
extractWords();
mergeHyphenatedWords();
if (!rows.empty()) {
currentRow = static_cast<int>(rows.size()) / 3;
currentWordInRow = 0;
}
updateRequired = true;
xTaskCreate(&DictionaryWordSelectActivity::taskTrampoline, "DictWordSelTask", 4096, this, 1, &displayTaskHandle);
requestUpdate();
}
void DictionaryWordSelectActivity::onExit() {
ActivityWithSubactivity::onExit();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
}
bool DictionaryWordSelectActivity::isLandscape() const {
@@ -231,7 +205,7 @@ void DictionaryWordSelectActivity::loop() {
if (pendingBackFromDef) {
pendingBackFromDef = false;
exitActivity();
updateRequired = true;
requestUpdate();
}
if (pendingExitToReader) {
pendingExitToReader = false;
@@ -353,25 +327,28 @@ void DictionaryWordSelectActivity::loop() {
std::string cleaned = Dictionary::cleanWord(rawWord);
if (cleaned.empty()) {
GUI.drawPopup(renderer, "No word");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
{
Activity::RenderLock lock(*this);
GUI.drawPopup(renderer, "No word");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
updateRequired = true;
requestUpdate();
return;
}
// Show looking up popup, then release mutex so display task can run
xSemaphoreTake(renderingMutex, portMAX_DELAY);
Rect popupLayout = GUI.drawPopup(renderer, "Looking up...");
xSemaphoreGive(renderingMutex);
Rect popupLayout;
{
Activity::RenderLock lock(*this);
popupLayout = GUI.drawPopup(renderer, "Looking up...");
}
bool cancelled = false;
std::string definition = Dictionary::lookup(
cleaned,
[this, &popupLayout](int percent) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
Activity::RenderLock lock(*this);
GUI.fillPopupProgress(renderer, popupLayout, percent);
xSemaphoreGive(renderingMutex);
},
[this, &cancelled]() -> bool {
mappedInput.update();
@@ -383,7 +360,7 @@ void DictionaryWordSelectActivity::loop() {
});
if (cancelled) {
updateRequired = true;
requestUpdate();
return;
}
@@ -417,10 +394,13 @@ void DictionaryWordSelectActivity::loop() {
return;
}
GUI.drawPopup(renderer, "Not found");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
{
Activity::RenderLock lock(*this);
GUI.drawPopup(renderer, "Not found");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
}
vTaskDelay(1500 / portTICK_PERIOD_MS);
updateRequired = true;
requestUpdate();
return;
}
@@ -430,11 +410,11 @@ void DictionaryWordSelectActivity::loop() {
}
if (changed) {
updateRequired = true;
requestUpdate();
}
}
void DictionaryWordSelectActivity::renderScreen() {
void DictionaryWordSelectActivity::render(Activity::RenderLock&&) {
renderer.clearScreen();
// Render the page content

View File

@@ -1,8 +1,5 @@
#pragma once
#include <Epub/Page.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <memory>
@@ -31,6 +28,7 @@ class DictionaryWordSelectActivity final : public ActivityWithSubactivity {
void onEnter() override;
void onExit() override;
void loop() override;
void render(Activity::RenderLock&&) override;
private:
struct WordInfo {
@@ -64,19 +62,12 @@ class DictionaryWordSelectActivity final : public ActivityWithSubactivity {
std::vector<Row> rows;
int currentRow = 0;
int currentWordInRow = 0;
bool updateRequired = false;
bool pendingBackFromDef = false;
bool pendingExitToReader = false;
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
bool isLandscape() const;
bool isInverted() const;
void extractWords();
void mergeHyphenatedWords();
void renderScreen();
void drawHints();
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
};

View File

@@ -42,36 +42,13 @@ std::string EpubReaderBookmarkSelectionActivity::getPageSuffix(const Bookmark& b
return " - Page " + std::to_string(bookmark.pageNumber + 1);
}
void EpubReaderBookmarkSelectionActivity::taskTrampoline(void* param) {
auto* self = static_cast<EpubReaderBookmarkSelectionActivity*>(param);
self->displayTaskLoop();
}
void EpubReaderBookmarkSelectionActivity::onEnter() {
ActivityWithSubactivity::onEnter();
renderingMutex = xSemaphoreCreateMutex();
// Trigger first update
updateRequired = true;
xTaskCreate(&EpubReaderBookmarkSelectionActivity::taskTrampoline, "BookmarkSelTask",
4096, // Stack size
this, // Parameters
1, // Priority
&displayTaskHandle // Task handle
);
requestUpdate();
}
void EpubReaderBookmarkSelectionActivity::onExit() {
ActivityWithSubactivity::onExit();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
}
void EpubReaderBookmarkSelectionActivity::loop() {
@@ -83,7 +60,6 @@ void EpubReaderBookmarkSelectionActivity::loop() {
const int totalItems = getTotalItems();
if (totalItems == 0) {
// All bookmarks deleted, go back
if (mappedInput.wasReleased(MappedInputManager::Button::Back) ||
mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
onGoBack();
@@ -91,14 +67,11 @@ void EpubReaderBookmarkSelectionActivity::loop() {
return;
}
// Delete confirmation mode: wait for confirm (delete) or back (cancel)
if (deleteConfirmMode) {
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (ignoreNextConfirmRelease) {
// Ignore the release from the initial long press
ignoreNextConfirmRelease = false;
} else {
// Confirm delete
BookmarkStore::removeBookmark(cachePath, bookmarks[pendingDeleteIndex].spineIndex,
bookmarks[pendingDeleteIndex].pageNumber);
bookmarks.erase(bookmarks.begin() + pendingDeleteIndex);
@@ -106,25 +79,24 @@ void EpubReaderBookmarkSelectionActivity::loop() {
selectorIndex = std::max(0, static_cast<int>(bookmarks.size()) - 1);
}
deleteConfirmMode = false;
updateRequired = true;
requestUpdate();
}
}
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
deleteConfirmMode = false;
ignoreNextConfirmRelease = false;
updateRequired = true;
requestUpdate();
}
return;
}
// Detect long press on Confirm to trigger delete
constexpr unsigned long DELETE_HOLD_MS = 700;
if (mappedInput.isPressed(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() >= DELETE_HOLD_MS) {
if (totalItems > 0 && selectorIndex >= 0 && selectorIndex < totalItems) {
deleteConfirmMode = true;
ignoreNextConfirmRelease = true;
pendingDeleteIndex = selectorIndex;
updateRequired = true;
requestUpdate();
}
return;
}
@@ -144,38 +116,26 @@ void EpubReaderBookmarkSelectionActivity::loop() {
buttonNavigator.onNextRelease([this, totalItems] {
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
updateRequired = true;
requestUpdate();
});
buttonNavigator.onPreviousRelease([this, totalItems] {
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
updateRequired = true;
requestUpdate();
});
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
updateRequired = true;
requestUpdate();
});
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
updateRequired = true;
requestUpdate();
});
}
void EpubReaderBookmarkSelectionActivity::displayTaskLoop() {
while (true) {
if (updateRequired && !subActivity) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
renderScreen();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void EpubReaderBookmarkSelectionActivity::renderScreen() {
void EpubReaderBookmarkSelectionActivity::render(Activity::RenderLock&&) {
renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth();
@@ -191,7 +151,6 @@ void EpubReaderBookmarkSelectionActivity::renderScreen() {
const int pageItems = getPageItems();
const int totalItems = getTotalItems();
// Title
const int titleX =
contentX + (contentWidth - renderer.getTextWidth(UI_12_FONT_ID, "Go to Bookmark", EpdFontFamily::BOLD)) / 2;
renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentY, "Go to Bookmark", true, EpdFontFamily::BOLD);
@@ -213,7 +172,6 @@ void EpubReaderBookmarkSelectionActivity::renderScreen() {
const std::string suffix = getPageSuffix(bookmarks[itemIndex]);
const int suffixWidth = renderer.getTextWidth(UI_10_FONT_ID, suffix.c_str());
// Truncate the prefix (chapter + snippet) to leave room for the page suffix
const std::string prefix = getBookmarkPrefix(bookmarks[itemIndex]);
const std::string truncatedPrefix =
renderer.truncatedText(UI_10_FONT_ID, prefix.c_str(), maxLabelWidth - suffixWidth);
@@ -225,7 +183,6 @@ void EpubReaderBookmarkSelectionActivity::renderScreen() {
}
if (deleteConfirmMode && pendingDeleteIndex < static_cast<int>(bookmarks.size())) {
// Draw delete confirmation overlay
const std::string suffix = getPageSuffix(bookmarks[pendingDeleteIndex]);
std::string msg = "Delete bookmark" + suffix + "?";

View File

@@ -1,8 +1,5 @@
#pragma once
#include <Epub.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <memory>
#include <vector>
@@ -15,32 +12,19 @@ class EpubReaderBookmarkSelectionActivity final : public ActivityWithSubactivity
std::shared_ptr<Epub> epub;
std::vector<Bookmark> bookmarks;
std::string cachePath;
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
ButtonNavigator buttonNavigator;
int selectorIndex = 0;
bool updateRequired = false;
bool deleteConfirmMode = false;
bool ignoreNextConfirmRelease = false;
int pendingDeleteIndex = 0;
const std::function<void()> onGoBack;
const std::function<void(int newSpineIndex, int newPage)> onSelectBookmark;
// Number of items that fit on a page, derived from logical screen height.
int getPageItems() const;
int getTotalItems() const;
// Build the prefix portion of a bookmark label (chapter + snippet, without page suffix)
std::string getBookmarkPrefix(const Bookmark& bookmark) const;
// Build the page suffix (e.g. " - Page 5")
static std::string getPageSuffix(const Bookmark& bookmark);
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void renderScreen();
public:
explicit EpubReaderBookmarkSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::shared_ptr<Epub>& epub,
@@ -57,4 +41,5 @@ class EpubReaderBookmarkSelectionActivity final : public ActivityWithSubactivity
void onEnter() override;
void onExit() override;
void loop() override;
void render(Activity::RenderLock&&) override;
};

View File

@@ -82,7 +82,6 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
const std::function<void(uint8_t)> onBack;
const std::function<void(MenuAction)> onAction;
<<<<<<< HEAD
// Map the internal override value to an index into letterboxFillLabels.
int letterboxFillToIndex() const {

View File

@@ -12,41 +12,15 @@
#include "util/Dictionary.h"
#include "util/LookupHistory.h"
void LookedUpWordsActivity::taskTrampoline(void* param) {
auto* self = static_cast<LookedUpWordsActivity*>(param);
self->displayTaskLoop();
}
void LookedUpWordsActivity::displayTaskLoop() {
while (true) {
if (updateRequired && !subActivity) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
renderScreen();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void LookedUpWordsActivity::onEnter() {
ActivityWithSubactivity::onEnter();
renderingMutex = xSemaphoreCreateMutex();
words = LookupHistory::load(cachePath);
std::reverse(words.begin(), words.end());
updateRequired = true;
xTaskCreate(&LookedUpWordsActivity::taskTrampoline, "LookedUpTask", 4096, this, 1, &displayTaskHandle);
requestUpdate();
}
void LookedUpWordsActivity::onExit() {
ActivityWithSubactivity::onExit();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
}
void LookedUpWordsActivity::loop() {
@@ -55,7 +29,7 @@ void LookedUpWordsActivity::loop() {
if (pendingBackFromDef) {
pendingBackFromDef = false;
exitActivity();
updateRequired = true;
requestUpdate();
}
if (pendingExitToReader) {
pendingExitToReader = false;
@@ -87,13 +61,13 @@ void LookedUpWordsActivity::loop() {
selectedIndex = std::max(0, static_cast<int>(words.size()) - 1);
}
deleteConfirmMode = false;
updateRequired = true;
requestUpdate();
}
}
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
deleteConfirmMode = false;
ignoreNextConfirmRelease = false;
updateRequired = true;
requestUpdate();
}
return;
}
@@ -104,7 +78,7 @@ void LookedUpWordsActivity::loop() {
deleteConfirmMode = true;
ignoreNextConfirmRelease = true;
pendingDeleteIndex = selectedIndex;
updateRequired = true;
requestUpdate();
return;
}
@@ -113,30 +87,37 @@ void LookedUpWordsActivity::loop() {
buttonNavigator.onNextRelease([this, totalItems] {
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, totalItems);
updateRequired = true;
requestUpdate();
});
buttonNavigator.onPreviousRelease([this, totalItems] {
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, totalItems);
updateRequired = true;
requestUpdate();
});
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
selectedIndex = ButtonNavigator::nextPageIndex(selectedIndex, totalItems, pageItems);
updateRequired = true;
requestUpdate();
});
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
selectedIndex = ButtonNavigator::previousPageIndex(selectedIndex, totalItems, pageItems);
updateRequired = true;
requestUpdate();
});
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
const std::string& headword = words[selectedIndex];
Rect popupLayout = GUI.drawPopup(renderer, "Looking up...");
Rect popupLayout;
{
Activity::RenderLock lock(*this);
popupLayout = GUI.drawPopup(renderer, "Looking up...");
}
std::string definition = Dictionary::lookup(
headword, [this, &popupLayout](int percent) { GUI.fillPopupProgress(renderer, popupLayout, percent); });
headword, [this, &popupLayout](int percent) {
Activity::RenderLock lock(*this);
GUI.fillPopupProgress(renderer, popupLayout, percent);
});
if (!definition.empty()) {
enterNewActivity(new DictionaryDefinitionActivity(
@@ -166,10 +147,13 @@ void LookedUpWordsActivity::loop() {
return;
}
GUI.drawPopup(renderer, "Not found");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
{
Activity::RenderLock lock(*this);
GUI.drawPopup(renderer, "Not found");
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
}
vTaskDelay(1500 / portTICK_PERIOD_MS);
updateRequired = true;
requestUpdate();
return;
}
@@ -190,7 +174,7 @@ int LookedUpWordsActivity::getPageItems() const {
return std::max(1, contentHeight / metrics.listRowHeight);
}
void LookedUpWordsActivity::renderScreen() {
void LookedUpWordsActivity::render(Activity::RenderLock&&) {
renderer.clearScreen();
const auto orient = renderer.getOrientation();

View File

@@ -1,8 +1,4 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <string>
#include <vector>
@@ -25,6 +21,7 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity {
void onEnter() override;
void onExit() override;
void loop() override;
void render(Activity::RenderLock&&) override;
private:
std::string cachePath;
@@ -35,7 +32,6 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity {
std::vector<std::string> words;
int selectedIndex = 0;
bool updateRequired = false;
bool pendingBackFromDef = false;
bool pendingExitToReader = false;
ButtonNavigator buttonNavigator;
@@ -45,11 +41,5 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity {
bool ignoreNextConfirmRelease = false;
int pendingDeleteIndex = 0;
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
int getPageItems() const;
void renderScreen();
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
};