Merge staging: ef-1.0.1 and ef-1.0.2
ef-1.0.2 - Quick Menu Enhancements - Screen rotation toggle (Portrait/Landscape CCW) - Customizable menu order with pick-and-place reordering - Navigation button hints on quick menu ef-1.0.1 - Dictionary Stability & UX - Fixed dictionary crashes from heap fragmentation - Refactored TextBlock/ParsedText to std::vector (~12x fewer allocations) - Uncompressed .dict support, chunked HTML parsing - Orientation-aware button hints on dictionary screens
This commit is contained in:
commit
d5e42b9e40
105
ef-CHANGELOG.md
Normal file
105
ef-CHANGELOG.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# crosspoint-ef Changelog
|
||||||
|
|
||||||
|
All notable changes to the crosspoint-ef fork are documented here.
|
||||||
|
|
||||||
|
Base: CrossPoint Reader 0.15.0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ef-1.0.2
|
||||||
|
|
||||||
|
**Quick Menu Enhancements**
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
- **Screen Rotation Toggle**: Quick toggle between Portrait and Landscape CCW directly from the quick menu
|
||||||
|
- Automatically reindexes content for new screen dimensions
|
||||||
|
- Preserves reading position via content offset restoration
|
||||||
|
- **Customizable Menu Order**: Reorder quick menu items to your preference
|
||||||
|
- New "Edit List Order" option at bottom of menu
|
||||||
|
- Pick-and-place reordering: select item, navigate to destination, place
|
||||||
|
- Order persists across sessions
|
||||||
|
|
||||||
|
### UI Improvements
|
||||||
|
|
||||||
|
- Added navigation button hints to quick menu (prev/next on front buttons, up/down on side buttons)
|
||||||
|
- Fixed orientation-aware margins for button hint areas in landscape modes
|
||||||
|
- New default menu order: Bookmark, Dictionary, Rotate Screen, Settings, Clear Cache
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ef-1.0.1
|
||||||
|
|
||||||
|
**Dictionary Stability & UX Improvements**
|
||||||
|
|
||||||
|
### Bug Fixes - Stability
|
||||||
|
|
||||||
|
- Fixed dictionary crashes caused by heap fragmentation from repeated page navigation
|
||||||
|
- Refactored TextBlock/ParsedText from `std::list` to `std::vector`, reducing heap allocations by ~12x per TextBlock
|
||||||
|
- Affects EPUB reader page rendering, dictionary definition display, and word selection
|
||||||
|
- Contiguous memory improves cache locality during text layout and reduces heap fragmentation on the memory-constrained ESP32
|
||||||
|
- Added uncompressed dictionary (`.dict`) support to avoid decompression memory issues with large dictzip chunks (58KB chunks -> direct read)
|
||||||
|
- Implemented chunked on-demand HTML parsing for large definitions, parsing pages as user navigates rather than all at once
|
||||||
|
- Limited cached pages to 4 with re-parse capability for backward navigation beyond cache window
|
||||||
|
- Fixed double-button press bug when loading new dictionary chunks
|
||||||
|
|
||||||
|
### Bug Fixes - UI/Layout
|
||||||
|
|
||||||
|
- Restored proper orientation-aware button hint spacing (front: 45px, side: 50px)
|
||||||
|
- Added side button hints to definition screen with "<" / ">" labels for page navigation
|
||||||
|
- Added side button hints to word selection screen ("UP"/"DOWN" labels, borderless, small font)
|
||||||
|
- Added side button hints to dictionary menu ("< Prev", "Next >")
|
||||||
|
- Moved page indicator up to avoid bezel cutoff in landscape orientations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ef-1.0.0
|
||||||
|
|
||||||
|
**First Official Release** (previously ef-0.15.99)
|
||||||
|
|
||||||
|
First milestone release of the crosspoint-ef fork, building on CrossPoint Reader 0.15.0 with 14+ major new features and enhancements.
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
- **Dictionary Support**: Offline StarDict dictionary with word selection from reader, fast prefix-indexed search, rich HTML formatting, and multi-page pagination
|
||||||
|
- **Bookmark System**: Per-book bookmarks with visual folded-corner indicators, dedicated management interface, and auto-generated bookmark names
|
||||||
|
- **Quick Menu**: In-reader quick access menu for common actions (Dictionary, Bookmark, Clear Cache, Settings) via short power button press
|
||||||
|
- **Library Search**: Search across all books by title, author, or filename with dynamic character picker and weighted relevance scoring
|
||||||
|
- **CSS Support**: Parse and apply CSS styles from EPUB stylesheets (text-align, font-style, font-weight, text-decoration, margins, padding)
|
||||||
|
- **Inline Image Support**: PNG and Baseline JPEG rendering within EPUB content with 2-bit grayscale dithering and caching
|
||||||
|
- **Custom Fonts**: Atkinson Hyperlegible Next (low-vision readers) and Fern Micro (small screens)
|
||||||
|
- **Enhanced Web Server**: File management (upload, download, delete, rename, copy, move, mkdir), companion app API, WebSocket uploads, mDNS discovery at `crosspoint.local`
|
||||||
|
- **Reading Lists**: Create, manage, and pin custom book lists with web API support (CSV format)
|
||||||
|
- **Enhanced Tab Bar**: Unified tab bar with horizontal scrolling and overflow indicators (Recent, Lists, Bookmarks, Search, Files)
|
||||||
|
- **Progress Bar Status**: Additional status bar option showing visual reading progress
|
||||||
|
- **OPDS Browser Enhancements**: Navigation history, page skipping (hold Up/Down), error retry, HTTP Basic Auth support
|
||||||
|
|
||||||
|
### Display Enhancements
|
||||||
|
|
||||||
|
- **High Contrast Mode**: System-wide contrast adjustment
|
||||||
|
- **Bezel Compensation**: Configurable margin (0-10px) for physical screen edge defects
|
||||||
|
- **Sleep Screen Improvements**: Edge-aware color filling for seamless letterbox appearance
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fixed device hanging when booted without USB connected (Serial.available()/Serial.read() called without Serial.begin())
|
||||||
|
- Fixed grayscale state corruption causing ghosting artifacts when anti-aliasing enabled under memory pressure
|
||||||
|
- Memory optimization with graceful degradation when memory is low
|
||||||
|
|
||||||
|
### Development Tools
|
||||||
|
|
||||||
|
- `pre_flash.py`: Displays "Flashing firmware..." screen during upload
|
||||||
|
- `debugging_monitor.py`: Enhanced serial monitor with memory graphs
|
||||||
|
- `pio_helper.py`: Interactive PlatformIO workflow helper
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Differences from Upstream 0.16.0
|
||||||
|
|
||||||
|
This fork is based on upstream 0.15.0. The following 0.16.0 features are not included:
|
||||||
|
|
||||||
|
- KOReader sync support
|
||||||
|
- Non-English hyphenation patterns (Spanish, German, French, Russian)
|
||||||
|
- XTC/XTCH file format support
|
||||||
|
|
||||||
|
See [crosspoint-ef-features.md](docs/crosspoint-ef-features.md) for complete feature documentation.
|
||||||
@ -2,7 +2,8 @@
|
|||||||
default_envs = default
|
default_envs = default
|
||||||
|
|
||||||
[crosspoint]
|
[crosspoint]
|
||||||
version = ef-0.15.99
|
# 0.15.0 CrossPoint base, ef-1.0.0 is the first release of the ef branch
|
||||||
|
version = 0.15.ef-1.0.2
|
||||||
|
|
||||||
[base]
|
[base]
|
||||||
platform = espressif32 @ 6.12.0
|
platform = espressif32 @ 6.12.0
|
||||||
|
|||||||
@ -155,6 +155,11 @@ class CrossPointSettings {
|
|||||||
// Pinned list name (empty = none pinned)
|
// Pinned list name (empty = none pinned)
|
||||||
char pinnedListName[64] = "";
|
char pinnedListName[64] = "";
|
||||||
|
|
||||||
|
// Quick menu item order (indices 0-4 representing the 5 menu items)
|
||||||
|
// Maps to QuickMenuAction enum: 0=Dictionary, 1=Bookmark, 2=ClearCache, 3=Orientation, 4=Settings
|
||||||
|
// Default order: Bookmark(1), Dictionary(0), Orientation(3), Settings(4), ClearCache(2)
|
||||||
|
uint8_t quickMenuOrder[5] = {1, 0, 3, 4, 2};
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
// Get singleton instance
|
// Get singleton instance
|
||||||
|
|||||||
@ -496,6 +496,28 @@ void EpubReaderActivity::loop() {
|
|||||||
self->onGoToClearCache();
|
self->onGoToClearCache();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self->updateRequired = true;
|
||||||
|
} else if (action == QuickMenuAction::TOGGLE_ORIENTATION) {
|
||||||
|
// Toggle between Portrait and Landscape CCW
|
||||||
|
if (SETTINGS.orientation == CrossPointSettings::ORIENTATION::PORTRAIT) {
|
||||||
|
SETTINGS.orientation = CrossPointSettings::ORIENTATION::LANDSCAPE_CCW;
|
||||||
|
} else {
|
||||||
|
SETTINGS.orientation = CrossPointSettings::ORIENTATION::PORTRAIT;
|
||||||
|
}
|
||||||
|
SETTINGS.saveToFile();
|
||||||
|
|
||||||
|
// Apply new orientation to renderer
|
||||||
|
if (SETTINGS.orientation == CrossPointSettings::ORIENTATION::PORTRAIT) {
|
||||||
|
self->renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||||
|
} else {
|
||||||
|
self->renderer.setOrientation(GfxRenderer::Orientation::LandscapeCounterClockwise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force section reload with new orientation's viewport dimensions
|
||||||
|
xSemaphoreTake(cachedMutex, portMAX_DELAY);
|
||||||
|
self->section.reset();
|
||||||
|
xSemaphoreGive(cachedMutex);
|
||||||
|
|
||||||
self->updateRequired = true;
|
self->updateRequired = true;
|
||||||
} else if (action == QuickMenuAction::GO_TO_SETTINGS) {
|
} else if (action == QuickMenuAction::GO_TO_SETTINGS) {
|
||||||
// Navigate to Settings activity
|
// Navigate to Settings activity
|
||||||
|
|||||||
@ -2,16 +2,25 @@
|
|||||||
|
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int MENU_ITEM_COUNT = 4;
|
// Base menu item count (reorderable items)
|
||||||
const char* MENU_ITEMS[MENU_ITEM_COUNT] = {"Dictionary", "Bookmark", "Clear Cache", "Settings"};
|
constexpr int BASE_MENU_ITEM_COUNT = 5;
|
||||||
const char* MENU_DESCRIPTIONS_ADD[MENU_ITEM_COUNT] = {"Look up a word", "Add bookmark to this page",
|
// Total display count including "Edit List Order"
|
||||||
"Free up storage space", "Open settings menu"};
|
constexpr int DISPLAY_ITEM_COUNT = 6;
|
||||||
const char* MENU_DESCRIPTIONS_REMOVE[MENU_ITEM_COUNT] = {"Look up a word", "Remove bookmark from this page",
|
|
||||||
"Free up storage space", "Open settings menu"};
|
// Menu items indexed by QuickMenuAction enum value
|
||||||
|
// 0=Dictionary, 1=Bookmark, 2=ClearCache, 3=Orientation, 4=Settings
|
||||||
|
const char* MENU_ITEMS[BASE_MENU_ITEM_COUNT] = {"Dictionary", "Bookmark", "Clear Cache", "Rotate Screen", "Settings"};
|
||||||
|
const char* MENU_DESCRIPTIONS_ADD[BASE_MENU_ITEM_COUNT] = {"Look up a word", "Add bookmark to this page",
|
||||||
|
"Free up storage space", "Toggle screen orientation",
|
||||||
|
"Open settings menu"};
|
||||||
|
const char* MENU_DESCRIPTIONS_REMOVE[BASE_MENU_ITEM_COUNT] = {"Look up a word", "Remove bookmark from this page",
|
||||||
|
"Free up storage space", "Toggle screen orientation",
|
||||||
|
"Open settings menu"};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void QuickMenuActivity::taskTrampoline(void* param) {
|
void QuickMenuActivity::taskTrampoline(void* param) {
|
||||||
@ -53,6 +62,16 @@ void QuickMenuActivity::onExit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void QuickMenuActivity::loop() {
|
void QuickMenuActivity::loop() {
|
||||||
|
if (editMode) {
|
||||||
|
// Edit mode logic
|
||||||
|
handleEditMode();
|
||||||
|
} else {
|
||||||
|
// Normal mode logic
|
||||||
|
handleNormalMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickMenuActivity::handleNormalMode() {
|
||||||
// Handle back button - cancel
|
// Handle back button - cancel
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||||
onCancel();
|
onCancel();
|
||||||
@ -61,8 +80,22 @@ void QuickMenuActivity::loop() {
|
|||||||
|
|
||||||
// Handle confirm button - select current option
|
// Handle confirm button - select current option
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
|
// Last item is "Edit List Order"
|
||||||
|
if (selectedIndex == DISPLAY_ITEM_COUNT - 1) {
|
||||||
|
// Enter edit mode - copy current order to local buffer
|
||||||
|
for (int i = 0; i < BASE_MENU_ITEM_COUNT; i++) {
|
||||||
|
localOrder[i] = SETTINGS.quickMenuOrder[i];
|
||||||
|
}
|
||||||
|
editMode = true;
|
||||||
|
selectedIndex = 0; // Start at first item in edit mode
|
||||||
|
updateRequired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the action from the order array
|
||||||
|
const int actionIndex = SETTINGS.quickMenuOrder[selectedIndex];
|
||||||
QuickMenuAction action;
|
QuickMenuAction action;
|
||||||
switch (selectedIndex) {
|
switch (actionIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
action = QuickMenuAction::DICTIONARY;
|
action = QuickMenuAction::DICTIONARY;
|
||||||
break;
|
break;
|
||||||
@ -73,6 +106,9 @@ void QuickMenuActivity::loop() {
|
|||||||
action = QuickMenuAction::CLEAR_CACHE;
|
action = QuickMenuAction::CLEAR_CACHE;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
action = QuickMenuAction::TOGGLE_ORIENTATION;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
default:
|
default:
|
||||||
action = QuickMenuAction::GO_TO_SETTINGS;
|
action = QuickMenuAction::GO_TO_SETTINGS;
|
||||||
break;
|
break;
|
||||||
@ -88,10 +124,69 @@ void QuickMenuActivity::loop() {
|
|||||||
mappedInput.wasPressed(MappedInputManager::Button::Right);
|
mappedInput.wasPressed(MappedInputManager::Button::Right);
|
||||||
|
|
||||||
if (prevPressed) {
|
if (prevPressed) {
|
||||||
selectedIndex = (selectedIndex + MENU_ITEM_COUNT - 1) % MENU_ITEM_COUNT;
|
selectedIndex = (selectedIndex + DISPLAY_ITEM_COUNT - 1) % DISPLAY_ITEM_COUNT;
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else if (nextPressed) {
|
} else if (nextPressed) {
|
||||||
selectedIndex = (selectedIndex + 1) % MENU_ITEM_COUNT;
|
selectedIndex = (selectedIndex + 1) % DISPLAY_ITEM_COUNT;
|
||||||
|
updateRequired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickMenuActivity::handleEditMode() {
|
||||||
|
// Handle back button - save and exit edit mode
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||||
|
// Save the local order to settings
|
||||||
|
for (int i = 0; i < BASE_MENU_ITEM_COUNT; i++) {
|
||||||
|
SETTINGS.quickMenuOrder[i] = localOrder[i];
|
||||||
|
}
|
||||||
|
SETTINGS.saveToFile();
|
||||||
|
editMode = false;
|
||||||
|
movingIndex = -1;
|
||||||
|
selectedIndex = DISPLAY_ITEM_COUNT - 1; // Select "Edit List Order" when exiting
|
||||||
|
updateRequired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle confirm button - pick or place item
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
|
if (movingIndex < 0) {
|
||||||
|
// No item selected yet - pick up the current item
|
||||||
|
movingIndex = selectedIndex;
|
||||||
|
} else {
|
||||||
|
// Item is being moved - place it at the current position
|
||||||
|
if (movingIndex != selectedIndex) {
|
||||||
|
// Remove item from old position and insert at new position
|
||||||
|
const uint8_t movingItem = localOrder[movingIndex];
|
||||||
|
if (movingIndex < selectedIndex) {
|
||||||
|
// Moving down - shift items up
|
||||||
|
for (int i = movingIndex; i < selectedIndex; i++) {
|
||||||
|
localOrder[i] = localOrder[i + 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Moving up - shift items down
|
||||||
|
for (int i = movingIndex; i > selectedIndex; i--) {
|
||||||
|
localOrder[i] = localOrder[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
localOrder[selectedIndex] = movingItem;
|
||||||
|
}
|
||||||
|
movingIndex = -1; // Deselect
|
||||||
|
}
|
||||||
|
updateRequired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle navigation - just move cursor
|
||||||
|
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 > 0) {
|
||||||
|
selectedIndex--;
|
||||||
|
updateRequired = true;
|
||||||
|
} else if (nextPressed && selectedIndex < BASE_MENU_ITEM_COUNT - 1) {
|
||||||
|
selectedIndex++;
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,46 +215,110 @@ void QuickMenuActivity::render() const {
|
|||||||
const int bezelRight = renderer.getBezelOffsetRight();
|
const int bezelRight = renderer.getBezelOffsetRight();
|
||||||
const int bezelBottom = renderer.getBezelOffsetBottom();
|
const int bezelBottom = renderer.getBezelOffsetBottom();
|
||||||
|
|
||||||
// Calculate usable content area
|
// Button hint space constants
|
||||||
const int marginLeft = 20 + bezelLeft;
|
constexpr int FRONT_BUTTON_SPACE = 45; // 40px button height + 5px padding
|
||||||
const int marginRight = 20 + bezelRight;
|
constexpr int SIDE_BUTTON_SPACE = 50; // 45px button area + 5px padding
|
||||||
const int marginTop = 15 + bezelTop;
|
|
||||||
const int contentWidth = pageWidth - marginLeft - marginRight;
|
|
||||||
const int contentHeight = pageHeight - marginTop - 60 - bezelBottom; // 60 for button hints
|
|
||||||
|
|
||||||
// Draw header
|
// Calculate button hint margins based on orientation
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "Quick Menu", true, EpdFontFamily::BOLD);
|
// Physical button locations (fixed on device):
|
||||||
|
// - Front buttons: physical bottom in portrait
|
||||||
|
// - Side buttons: physical right in portrait
|
||||||
|
// These map to different logical edges depending on orientation
|
||||||
|
int frontBtnMarginTop = 0, frontBtnMarginBottom = 0, frontBtnMarginLeft = 0, frontBtnMarginRight = 0;
|
||||||
|
int sideBtnMarginTop = 0, sideBtnMarginBottom = 0, sideBtnMarginLeft = 0, sideBtnMarginRight = 0;
|
||||||
|
|
||||||
|
switch (renderer.getOrientation()) {
|
||||||
|
case GfxRenderer::Portrait:
|
||||||
|
// Front buttons at logical BOTTOM, Side buttons at logical RIGHT
|
||||||
|
frontBtnMarginBottom = FRONT_BUTTON_SPACE;
|
||||||
|
sideBtnMarginRight = SIDE_BUTTON_SPACE;
|
||||||
|
break;
|
||||||
|
case GfxRenderer::LandscapeClockwise:
|
||||||
|
// Front buttons at logical LEFT, Side buttons at logical BOTTOM
|
||||||
|
frontBtnMarginLeft = FRONT_BUTTON_SPACE;
|
||||||
|
sideBtnMarginBottom = SIDE_BUTTON_SPACE;
|
||||||
|
break;
|
||||||
|
case GfxRenderer::PortraitInverted:
|
||||||
|
// Front buttons at logical TOP, Side buttons at logical LEFT
|
||||||
|
frontBtnMarginTop = FRONT_BUTTON_SPACE;
|
||||||
|
sideBtnMarginLeft = SIDE_BUTTON_SPACE;
|
||||||
|
break;
|
||||||
|
case GfxRenderer::LandscapeCounterClockwise:
|
||||||
|
// Front buttons at logical RIGHT, Side buttons at logical TOP
|
||||||
|
frontBtnMarginRight = FRONT_BUTTON_SPACE;
|
||||||
|
sideBtnMarginTop = SIDE_BUTTON_SPACE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate usable content area with bezel and button hint margins
|
||||||
|
const int marginLeft = 20 + bezelLeft + frontBtnMarginLeft + sideBtnMarginLeft;
|
||||||
|
const int marginRight = 20 + bezelRight + frontBtnMarginRight + sideBtnMarginRight;
|
||||||
|
const int marginTop = 15 + bezelTop + frontBtnMarginTop + sideBtnMarginTop;
|
||||||
|
const int marginBottom = 15 + bezelBottom + frontBtnMarginBottom + sideBtnMarginBottom;
|
||||||
|
const int contentWidth = pageWidth - marginLeft - marginRight;
|
||||||
|
const int contentHeight = pageHeight - marginTop - marginBottom;
|
||||||
|
|
||||||
|
// Draw header - different text in edit mode
|
||||||
|
const char* headerText = editMode ? "Edit Menu Order" : "Quick Menu";
|
||||||
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, headerText, true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Select descriptions based on bookmark state
|
// Select descriptions based on bookmark state
|
||||||
const char* const* descriptions = isPageBookmarked ? MENU_DESCRIPTIONS_REMOVE : MENU_DESCRIPTIONS_ADD;
|
const char* const* descriptions = isPageBookmarked ? MENU_DESCRIPTIONS_REMOVE : MENU_DESCRIPTIONS_ADD;
|
||||||
|
|
||||||
|
// Get the order array to use (local copy in edit mode, settings otherwise)
|
||||||
|
const uint8_t* order = editMode ? localOrder : SETTINGS.quickMenuOrder;
|
||||||
|
|
||||||
// Draw menu items centered in content area
|
// Draw menu items centered in content area
|
||||||
constexpr int itemHeight = 50; // Height for each menu item (including description)
|
constexpr int itemHeight = 50; // Height for each menu item (including description)
|
||||||
const int startY = marginTop + (contentHeight - (MENU_ITEM_COUNT * itemHeight)) / 2;
|
const int startY = marginTop + (contentHeight - (DISPLAY_ITEM_COUNT * itemHeight)) / 2;
|
||||||
|
|
||||||
for (int i = 0; i < MENU_ITEM_COUNT; i++) {
|
for (int i = 0; i < DISPLAY_ITEM_COUNT; i++) {
|
||||||
const int itemY = startY + i * itemHeight;
|
const int itemY = startY + i * itemHeight;
|
||||||
const bool isSelected = (i == selectedIndex);
|
const bool isSelected = (i == selectedIndex);
|
||||||
|
const bool isBeingMoved = (editMode && i == movingIndex);
|
||||||
|
|
||||||
// Draw selection highlight (black fill) for selected item
|
// Draw selection highlight (black fill) for selected item
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
renderer.fillRect(marginLeft + 10, itemY - 2, contentWidth - 20, itemHeight - 6);
|
renderer.fillRect(marginLeft + 10, itemY - 2, contentWidth - 20, itemHeight - 6);
|
||||||
}
|
}
|
||||||
|
// Draw outline for item being moved (when cursor is elsewhere)
|
||||||
// Draw menu item text
|
if (isBeingMoved && !isSelected) {
|
||||||
const char* itemText = MENU_ITEMS[i];
|
renderer.drawRect(marginLeft + 10, itemY - 2, contentWidth - 20, itemHeight - 6);
|
||||||
// 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);
|
// Last item is always "Edit List Order" (fixed, not in the order array)
|
||||||
renderer.drawText(SMALL_FONT_ID, marginLeft + 20, itemY + 22, descriptions[i], !isSelected);
|
if (i == DISPLAY_ITEM_COUNT - 1) {
|
||||||
|
renderer.drawText(UI_10_FONT_ID, marginLeft + 20, itemY, "- Edit List Order -", !isSelected);
|
||||||
|
renderer.drawText(SMALL_FONT_ID, marginLeft + 20, itemY + 22, "Customize menu order", !isSelected);
|
||||||
|
} else {
|
||||||
|
// Get the action index from the order array
|
||||||
|
const int actionIndex = order[i];
|
||||||
|
|
||||||
|
// Draw menu item text - add indicator for item being moved
|
||||||
|
const char* itemText = MENU_ITEMS[actionIndex];
|
||||||
|
// For bookmark item (action index 1), show different text based on state
|
||||||
|
if (actionIndex == 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[actionIndex], !isSelected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw help text at bottom
|
// Draw help text at bottom - different hints for edit mode
|
||||||
const auto labels = mappedInput.mapLabels("\xc2\xab Back", "Select", "", "");
|
if (editMode) {
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
const char* confirmLabel = (movingIndex < 0) ? "Pick" : "Place";
|
||||||
|
const auto labels = mappedInput.mapLabels("\xc2\xab Done", confirmLabel, "", "");
|
||||||
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
// Side button hints for navigation
|
||||||
|
renderer.drawSideButtonHints(UI_10_FONT_ID, "<", ">");
|
||||||
|
} else {
|
||||||
|
const auto labels = mappedInput.mapLabels("\xc2\xab Back", "Select", "< Prev", "Next >");
|
||||||
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
// Side button hints for up/down navigation
|
||||||
|
renderer.drawSideButtonHints(UI_10_FONT_ID, "<", ">");
|
||||||
|
}
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
#include "../Activity.h"
|
#include "../Activity.h"
|
||||||
|
|
||||||
// Enum for quick menu selection
|
// Enum for quick menu selection
|
||||||
enum class QuickMenuAction { DICTIONARY, ADD_BOOKMARK, CLEAR_CACHE, GO_TO_SETTINGS };
|
enum class QuickMenuAction { DICTIONARY, ADD_BOOKMARK, CLEAR_CACHE, TOGGLE_ORIENTATION, GO_TO_SETTINGS };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QuickMenuActivity presents a quick access menu triggered by short power button press.
|
* QuickMenuActivity presents a quick access menu triggered by short power button press.
|
||||||
@ -28,9 +28,16 @@ class QuickMenuActivity final : public Activity {
|
|||||||
const std::function<void()> onCancel;
|
const std::function<void()> onCancel;
|
||||||
const bool isPageBookmarked; // True if current page already has a bookmark
|
const bool isPageBookmarked; // True if current page already has a bookmark
|
||||||
|
|
||||||
|
// Edit mode state
|
||||||
|
bool editMode = false; // True when in edit mode
|
||||||
|
int movingIndex = -1; // Index of item being moved (-1 if none)
|
||||||
|
uint8_t localOrder[5] = {0}; // Local copy of order for editing
|
||||||
|
|
||||||
static void taskTrampoline(void* param);
|
static void taskTrampoline(void* param);
|
||||||
[[noreturn]] void displayTaskLoop();
|
[[noreturn]] void displayTaskLoop();
|
||||||
void render() const;
|
void render() const;
|
||||||
|
void handleNormalMode();
|
||||||
|
void handleEditMode();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QuickMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
explicit QuickMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user