sort of working dictionary
This commit is contained in:
261
src/activities/dictionary/DictionarySearchActivity.cpp
Normal file
261
src/activities/dictionary/DictionarySearchActivity.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
#include "DictionarySearchActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <StarDict.h>
|
||||
|
||||
#include "DictionaryMargins.h"
|
||||
#include "DictionaryResultActivity.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "activities/util/KeyboardEntryActivity.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
// Dictionary path on SD card
|
||||
constexpr const char* DICT_BASE_PATH = "/dictionaries/dict-data";
|
||||
|
||||
// Global dictionary instance (lazy initialized)
|
||||
StarDict* g_dictionary = nullptr;
|
||||
|
||||
StarDict& getDictionary() {
|
||||
if (!g_dictionary) {
|
||||
g_dictionary = new StarDict(DICT_BASE_PATH);
|
||||
if (!g_dictionary->begin()) {
|
||||
Serial.printf("[%lu] [DICT] Failed to initialize dictionary\n", millis());
|
||||
}
|
||||
}
|
||||
return *g_dictionary;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void DictionarySearchActivity::taskTrampoline(void* param) {
|
||||
auto* self = static_cast<DictionarySearchActivity*>(param);
|
||||
self->displayTaskLoop();
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::onEnter() {
|
||||
ActivityWithSubactivity::onEnter();
|
||||
|
||||
renderingMutex = xSemaphoreCreateMutex();
|
||||
isSearching = false;
|
||||
keyboardShown = false;
|
||||
searchStatus = "";
|
||||
updateRequired = true;
|
||||
|
||||
xTaskCreate(&DictionarySearchActivity::taskTrampoline, "DictSearchTask",
|
||||
4096, // Stack size (needs more for dictionary operations)
|
||||
this, // Parameters
|
||||
1, // Priority
|
||||
&displayTaskHandle // Task handle
|
||||
);
|
||||
|
||||
// If no initial word provided, show keyboard
|
||||
if (searchWord.empty()) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
keyboardShown = true;
|
||||
enterNewActivity(new KeyboardEntryActivity(
|
||||
renderer, mappedInput, "Enter Word", "", 10,
|
||||
64, // maxLength
|
||||
false, // not password
|
||||
[this](const std::string& word) {
|
||||
// User entered a word
|
||||
exitActivity();
|
||||
searchWord = word;
|
||||
keyboardShown = false;
|
||||
if (!word.empty()) {
|
||||
performSearch(word);
|
||||
} else {
|
||||
onBack();
|
||||
}
|
||||
},
|
||||
[this]() {
|
||||
// User cancelled keyboard
|
||||
exitActivity();
|
||||
keyboardShown = false;
|
||||
onBack();
|
||||
}));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
} else {
|
||||
// Perform search with provided word
|
||||
performSearch(searchWord);
|
||||
}
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::onExit() {
|
||||
ActivityWithSubactivity::onExit();
|
||||
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
if (displayTaskHandle) {
|
||||
vTaskDelete(displayTaskHandle);
|
||||
displayTaskHandle = nullptr;
|
||||
}
|
||||
vSemaphoreDelete(renderingMutex);
|
||||
renderingMutex = nullptr;
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::loop() {
|
||||
if (subActivity) {
|
||||
subActivity->loop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle back button - use wasReleased to consume the full button event
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
onBack();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::performSearch(const std::string& word) {
|
||||
isSearching = true;
|
||||
searchStatus = "Searching...";
|
||||
updateRequired = true;
|
||||
|
||||
// Small delay to allow render
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// Initialize dictionary if needed
|
||||
StarDict& dict = getDictionary();
|
||||
|
||||
if (!dict.isReady()) {
|
||||
searchStatus = "Dictionary not found";
|
||||
isSearching = false;
|
||||
updateRequired = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform lookup
|
||||
const auto result = dict.lookup(word);
|
||||
|
||||
if (result.found) {
|
||||
showResult(result.word, result.definition);
|
||||
} else {
|
||||
showNotFound(word);
|
||||
}
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::showResult(const std::string& word, const std::string& definition) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
isSearching = false;
|
||||
exitActivity();
|
||||
enterNewActivity(new DictionaryResultActivity(
|
||||
renderer, mappedInput, word, definition,
|
||||
[this]() {
|
||||
// Back from result
|
||||
exitActivity();
|
||||
onBack();
|
||||
},
|
||||
[this]() {
|
||||
// Search another word
|
||||
exitActivity();
|
||||
searchWord = "";
|
||||
keyboardShown = true;
|
||||
enterNewActivity(new KeyboardEntryActivity(
|
||||
renderer, mappedInput, "Enter Word", "", 10, 64, false,
|
||||
[this](const std::string& newWord) {
|
||||
exitActivity();
|
||||
keyboardShown = false;
|
||||
if (!newWord.empty()) {
|
||||
performSearch(newWord);
|
||||
} else {
|
||||
onBack();
|
||||
}
|
||||
},
|
||||
[this]() {
|
||||
exitActivity();
|
||||
keyboardShown = false;
|
||||
onBack();
|
||||
}));
|
||||
}));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::showNotFound(const std::string& word) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
isSearching = false;
|
||||
exitActivity();
|
||||
enterNewActivity(new DictionaryResultActivity(
|
||||
renderer, mappedInput, word, "", // Empty definition = not found
|
||||
[this]() {
|
||||
// Back from result
|
||||
exitActivity();
|
||||
onBack();
|
||||
},
|
||||
[this]() {
|
||||
// Search another word
|
||||
exitActivity();
|
||||
searchWord = "";
|
||||
keyboardShown = true;
|
||||
enterNewActivity(new KeyboardEntryActivity(
|
||||
renderer, mappedInput, "Enter Word", "", 10, 64, false,
|
||||
[this](const std::string& newWord) {
|
||||
exitActivity();
|
||||
keyboardShown = false;
|
||||
if (!newWord.empty()) {
|
||||
performSearch(newWord);
|
||||
} else {
|
||||
onBack();
|
||||
}
|
||||
},
|
||||
[this]() {
|
||||
exitActivity();
|
||||
keyboardShown = false;
|
||||
onBack();
|
||||
}));
|
||||
}));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::displayTaskLoop() {
|
||||
int animationCounter = 0;
|
||||
constexpr int ANIMATION_INTERVAL = 30; // ~300ms at 10ms per tick
|
||||
|
||||
while (true) {
|
||||
// Handle animation updates when searching
|
||||
if (isSearching && !subActivity) {
|
||||
animationCounter++;
|
||||
if (animationCounter >= ANIMATION_INTERVAL) {
|
||||
animationCounter = 0;
|
||||
animationFrame = (animationFrame + 1) % 3;
|
||||
updateRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateRequired && !subActivity) {
|
||||
updateRequired = false;
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
render();
|
||||
xSemaphoreGive(renderingMutex);
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void DictionarySearchActivity::render() const {
|
||||
renderer.clearScreen();
|
||||
|
||||
// Get margins using same pattern as reader + button hint space
|
||||
int marginTop, marginRight, marginBottom, marginLeft;
|
||||
getDictionaryContentMargins(renderer, &marginTop, &marginRight, &marginBottom, &marginLeft);
|
||||
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
|
||||
// Draw header with top margin
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, marginTop + 15, "Dictionary", true, EpdFontFamily::BOLD);
|
||||
|
||||
if (isSearching) {
|
||||
// Show searching status with word and animated ellipsis
|
||||
// Center in content area (accounting for margins)
|
||||
const int centerY = marginTop + (pageHeight - marginTop - marginBottom) / 2;
|
||||
|
||||
// Build animated ellipsis
|
||||
const char* dots = (animationFrame == 0) ? "." : (animationFrame == 1) ? ".." : "...";
|
||||
|
||||
// Show "Searching for 'word'..."
|
||||
char statusText[128];
|
||||
snprintf(statusText, sizeof(statusText), "Searching for '%s'%s", searchWord.c_str(), dots);
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, centerY, statusText);
|
||||
}
|
||||
|
||||
renderer.displayBuffer();
|
||||
}
|
||||
Reference in New Issue
Block a user