#include "QuickMenuActivity.h" #include #include "MappedInputManager.h" #include "fontIds.h" namespace { constexpr int MENU_ITEM_COUNT = 4; const char* MENU_ITEMS[MENU_ITEM_COUNT] = {"Dictionary", "Bookmark", "Clear Cache", "Settings"}; const char* MENU_DESCRIPTIONS_ADD[MENU_ITEM_COUNT] = {"Look up a word", "Add bookmark to this page", "Free up storage space", "Open settings menu"}; const char* MENU_DESCRIPTIONS_REMOVE[MENU_ITEM_COUNT] = {"Look up a word", "Remove bookmark from this page", "Free up storage space", "Open settings menu"}; } // namespace void QuickMenuActivity::taskTrampoline(void* param) { auto* self = static_cast(param); self->displayTaskLoop(); } void QuickMenuActivity::onEnter() { Activity::onEnter(); renderingMutex = xSemaphoreCreateMutex(); // Reset selection selectedIndex = 0; // Trigger first update updateRequired = true; xTaskCreate(&QuickMenuActivity::taskTrampoline, "QuickMenuTask", 2048, // Stack size this, // Parameters 1, // Priority &displayTaskHandle // Task handle ); } void QuickMenuActivity::onExit() { Activity::onExit(); // Wait until not rendering to delete task xSemaphoreTake(renderingMutex, portMAX_DELAY); if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; } void QuickMenuActivity::loop() { // Handle back button - cancel if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { onCancel(); return; } // Handle confirm button - select current option if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { QuickMenuAction action; switch (selectedIndex) { case 0: action = QuickMenuAction::DICTIONARY; break; case 1: action = QuickMenuAction::ADD_BOOKMARK; break; case 2: action = QuickMenuAction::CLEAR_CACHE; break; case 3: default: action = QuickMenuAction::GO_TO_SETTINGS; break; } onActionSelected(action); return; } // Handle navigation const bool prevPressed = mappedInput.wasPressed(MappedInputManager::Button::Up) || mappedInput.wasPressed(MappedInputManager::Button::Left); const bool nextPressed = mappedInput.wasPressed(MappedInputManager::Button::Down) || mappedInput.wasPressed(MappedInputManager::Button::Right); if (prevPressed) { selectedIndex = (selectedIndex + MENU_ITEM_COUNT - 1) % MENU_ITEM_COUNT; updateRequired = true; } else if (nextPressed) { selectedIndex = (selectedIndex + 1) % MENU_ITEM_COUNT; updateRequired = true; } } void QuickMenuActivity::displayTaskLoop() { while (true) { if (updateRequired) { updateRequired = false; xSemaphoreTake(renderingMutex, portMAX_DELAY); render(); xSemaphoreGive(renderingMutex); } vTaskDelay(10 / portTICK_PERIOD_MS); } } void QuickMenuActivity::render() const { renderer.clearScreen(); const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); // Get bezel offsets const int bezelTop = renderer.getBezelOffsetTop(); const int bezelLeft = renderer.getBezelOffsetLeft(); const int bezelRight = renderer.getBezelOffsetRight(); const int bezelBottom = renderer.getBezelOffsetBottom(); // Calculate usable content area const int marginLeft = 20 + bezelLeft; const int marginRight = 20 + bezelRight; const int marginTop = 15 + bezelTop; const int contentWidth = pageWidth - marginLeft - marginRight; const int contentHeight = pageHeight - marginTop - 60 - bezelBottom; // 60 for button hints // Draw header renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "Quick Menu", true, EpdFontFamily::BOLD); // Select descriptions based on bookmark state const char* const* descriptions = isPageBookmarked ? MENU_DESCRIPTIONS_REMOVE : MENU_DESCRIPTIONS_ADD; // Draw menu items centered in content area constexpr int itemHeight = 50; // Height for each menu item (including description) const int startY = marginTop + (contentHeight - (MENU_ITEM_COUNT * itemHeight)) / 2; for (int i = 0; i < MENU_ITEM_COUNT; i++) { const int itemY = startY + i * itemHeight; const bool isSelected = (i == selectedIndex); // Draw selection highlight (black fill) for selected item if (isSelected) { renderer.fillRect(marginLeft + 10, itemY - 2, contentWidth - 20, itemHeight - 6); } // Draw menu item text const char* itemText = MENU_ITEMS[i]; // For bookmark item, show different text based on state if (i == 1) { itemText = isPageBookmarked ? "Remove Bookmark" : "Add Bookmark"; } renderer.drawText(UI_10_FONT_ID, marginLeft + 20, itemY, itemText, !isSelected); renderer.drawText(SMALL_FONT_ID, marginLeft + 20, itemY + 22, descriptions[i], !isSelected); } // Draw help text at bottom const auto labels = mappedInput.mapLabels("\xc2\xab Back", "Select", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); }