fix: make home navigation work for all themes

This commit is contained in:
Brackyt 2026-01-25 16:42:00 +01:00
parent 1f5a436abb
commit 29daf3f866
4 changed files with 80 additions and 96 deletions

View File

@ -38,6 +38,7 @@ class ThemeManager {
private: private:
std::map<std::string, UIElement *> elements; // All elements by ID std::map<std::string, UIElement *> elements; // All elements by ID
std::string currentThemeName; std::string currentThemeName;
int navBookCount = 1; // Number of navigable book slots (from theme [Global] section)
std::map<std::string, int> fontMap; std::map<std::string, int> fontMap;
// Screen-level caching for fast redraw // Screen-level caching for fast redraw
@ -71,6 +72,9 @@ public:
// Get current theme name // Get current theme name
const std::string &getCurrentTheme() const { return currentThemeName; } const std::string &getCurrentTheme() const { return currentThemeName; }
// Get number of navigable book slots (from theme config, default 1)
int getNavBookCount() const { return navBookCount; }
// Render a screen // Render a screen
void renderScreen(const std::string &screenName, const GfxRenderer &renderer, void renderScreen(const std::string &screenName, const GfxRenderer &renderer,
const ThemeContext &context); const ThemeContext &context);

View File

@ -515,6 +515,17 @@ void ThemeManager::loadTheme(const std::string &themeName) {
} }
} }
// Read theme configuration from [Global] section
navBookCount = 1; // Default
if (sections.count("Global")) {
const auto &global = sections.at("Global");
if (global.count("NavBookCount")) {
navBookCount = std::stoi(global.at("NavBookCount"));
if (navBookCount < 1) navBookCount = 1;
if (navBookCount > 10) navBookCount = 10; // Reasonable max
}
}
// Pass 1: Creation // Pass 1: Creation
for (const auto &sec : sections) { for (const auto &sec : sections) {
std::string id = sec.first; std::string id = sec.first;

View File

@ -25,7 +25,7 @@ void HomeActivity::taskTrampoline(void *param) {
} }
int HomeActivity::getMenuItemCount() const { int HomeActivity::getMenuItemCount() const {
int count = 4; // Books, Files, Transfer, Settings int count = 3; // Browse Files, File Transfer, Settings
if (hasOpdsUrl) if (hasOpdsUrl)
count++; // + Calibre Library count++; // + Calibre Library
return count; return count;
@ -36,6 +36,12 @@ void HomeActivity::onEnter() {
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
// Reset render and selection state
coverRendered = false;
coverBufferStored = false;
freeCoverBuffer();
selectorIndex = 0; // Start at first item (first book if any, else first menu)
// Check if we have a book to continue reading // Check if we have a book to continue reading
hasContinueReading = !APP_STATE.openEpubPath.empty() && hasContinueReading = !APP_STATE.openEpubPath.empty() &&
SdMan.exists(APP_STATE.openEpubPath.c_str()); SdMan.exists(APP_STATE.openEpubPath.c_str());
@ -249,86 +255,54 @@ void HomeActivity::freeCoverBuffer() {
} }
void HomeActivity::loop() { void HomeActivity::loop() {
const bool upPressed = mappedInput.wasPressed(MappedInputManager::Button::Up); const bool prevPressed =
const bool downPressed = mappedInput.wasPressed(MappedInputManager::Button::Down); mappedInput.wasPressed(MappedInputManager::Button::Up) ||
const bool leftPressed = mappedInput.wasPressed(MappedInputManager::Button::Left); mappedInput.wasPressed(MappedInputManager::Button::Left);
const bool rightPressed = mappedInput.wasPressed(MappedInputManager::Button::Right); const bool nextPressed =
mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right);
const bool confirmPressed = mappedInput.wasReleased(MappedInputManager::Button::Confirm); const bool confirmPressed = mappedInput.wasReleased(MappedInputManager::Button::Confirm);
const int bookCount = static_cast<int>(cachedRecentBooks.size()); // Navigation uses theme-configured book slots (limited by actual books available)
const int maxBooks = static_cast<int>(cachedRecentBooks.size());
const int themeBookCount = ThemeEngine::ThemeManager::get().getNavBookCount();
const int navBookCount = std::min(themeBookCount, maxBooks);
const int menuCount = getMenuItemCount(); const int menuCount = getMenuItemCount();
const bool hasBooks = bookCount > 0; const int totalCount = navBookCount + menuCount;
if (confirmPressed) { if (confirmPressed) {
if (inBookSelection && hasBooks && bookSelectorIndex < bookCount) { if (selectorIndex < navBookCount && selectorIndex < maxBooks) {
// Open selected book - set the path and trigger continue reading // Book selected - open the selected book
APP_STATE.openEpubPath = cachedRecentBooks[bookSelectorIndex].path; APP_STATE.openEpubPath = cachedRecentBooks[selectorIndex].path;
onContinueReading(); onContinueReading();
return; } else {
} else if (!inBookSelection) { // Menu item selected
// Menu selection - calculate which action const int menuIdx = selectorIndex - navBookCount;
int idx = 0; int idx = 0;
const int myLibraryIdx = idx++; const int myLibraryIdx = idx++;
const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1; const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1;
const int filesIdx = idx++; const int fileTransferIdx = idx++;
const int transferIdx = idx++;
const int settingsIdx = idx; const int settingsIdx = idx;
if (selectorIndex == myLibraryIdx) { if (menuIdx == myLibraryIdx) {
onMyLibraryOpen(); onMyLibraryOpen();
} else if (selectorIndex == opdsLibraryIdx) { } else if (menuIdx == opdsLibraryIdx) {
onOpdsBrowserOpen(); onOpdsBrowserOpen();
} else if (selectorIndex == filesIdx) { } else if (menuIdx == fileTransferIdx) {
onMyLibraryOpen(); // Files = file browser onFileTransferOpen();
} else if (selectorIndex == transferIdx) { } else if (menuIdx == settingsIdx) {
onFileTransferOpen(); // Transfer = web transfer
} else if (selectorIndex == settingsIdx) {
onSettingsOpen(); onSettingsOpen();
} }
} }
return; return;
} }
if (inBookSelection && hasBooks) { if (prevPressed) {
// Book selection mode selectorIndex = (selectorIndex + totalCount - 1) % totalCount;
if (leftPressed) { updateRequired = true;
bookSelectorIndex = (bookSelectorIndex + bookCount - 1) % bookCount; } else if (nextPressed) {
updateRequired = true; selectorIndex = (selectorIndex + 1) % totalCount;
} else if (rightPressed) { updateRequired = true;
bookSelectorIndex = (bookSelectorIndex + 1) % bookCount;
updateRequired = true;
} else if (downPressed) {
// Move to menu selection
inBookSelection = false;
selectorIndex = 0;
updateRequired = true;
}
} else {
// Menu selection mode
if (upPressed) {
if (selectorIndex == 0 && hasBooks) {
// Move back to book selection
inBookSelection = true;
updateRequired = true;
} else if (selectorIndex > 0) {
selectorIndex--;
updateRequired = true;
}
} else if (downPressed) {
if (selectorIndex < menuCount - 1) {
selectorIndex++;
updateRequired = true;
}
} else if (leftPressed || rightPressed) {
// In menu, left/right can also navigate (for 2-column layout)
if (leftPressed && selectorIndex > 0) {
selectorIndex--;
updateRequired = true;
} else if (rightPressed && selectorIndex < menuCount - 1) {
selectorIndex++;
updateRequired = true;
}
}
} }
} }
@ -365,11 +339,16 @@ void HomeActivity::render() {
SETTINGS.hideBatteryPercentage != SETTINGS.hideBatteryPercentage !=
CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_ALWAYS); CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_ALWAYS);
// --- Recent Books Data (use cached data for performance) --- // --- Navigation counts (must match loop()) ---
int recentCount = static_cast<int>(cachedRecentBooks.size()); const int recentCount = static_cast<int>(cachedRecentBooks.size());
const int themeBookCount = ThemeEngine::ThemeManager::get().getNavBookCount();
const int navBookCount = std::min(themeBookCount, recentCount);
const bool isBookSelected = selectorIndex < navBookCount;
// --- Recent Books Data ---
context.setBool("HasRecentBooks", recentCount > 0); context.setBool("HasRecentBooks", recentCount > 0);
context.setInt("RecentBooks.Count", recentCount); context.setInt("RecentBooks.Count", recentCount);
context.setInt("SelectedBookIndex", isBookSelected ? selectorIndex : -1);
for (int i = 0; i < recentCount; i++) { for (int i = 0; i < recentCount; i++) {
const auto &book = cachedRecentBooks[i]; const auto &book = cachedRecentBooks[i];
@ -378,11 +357,12 @@ void HomeActivity::render() {
context.setString(prefix + "Title", book.title); context.setString(prefix + "Title", book.title);
context.setString(prefix + "Image", book.coverPath); context.setString(prefix + "Image", book.coverPath);
context.setString(prefix + "Progress", std::to_string(book.progressPercent)); context.setString(prefix + "Progress", std::to_string(book.progressPercent));
// Book is selected if we're in book selection mode and this is the selected index // Book is selected if selectorIndex matches
context.setBool(prefix + "Selected", inBookSelection && i == bookSelectorIndex); context.setBool(prefix + "Selected", selectorIndex == i);
} }
// --- Book Card Data (for legacy theme) --- // --- Book Card Data (for themes with single book) ---
context.setBool("IsBookSelected", isBookSelected);
context.setBool("HasBook", hasContinueReading); context.setBool("HasBook", hasContinueReading);
context.setString("BookTitle", lastBookTitle); context.setString("BookTitle", lastBookTitle);
context.setString("BookAuthor", lastBookAuthor); context.setString("BookAuthor", lastBookAuthor);
@ -391,46 +371,37 @@ void HomeActivity::render() {
hasContinueReading && hasCoverImage && !coverBmpPath.empty()); hasContinueReading && hasCoverImage && !coverBmpPath.empty());
context.setBool("ShowInfoBox", true); context.setBool("ShowInfoBox", true);
// --- Selection Logic (for menu items, books handled separately) ---
int idx = 0;
const int myLibraryIdx = idx++;
const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1;
const int filesIdx = idx++;
const int transferIdx = idx++;
const int settingsIdx = idx;
// IsBookSelected is true when we're in book selection mode
context.setBool("IsBookSelected", inBookSelection);
// --- Main Menu Data --- // --- Main Menu Data ---
// Menu items start after the book slot
const int menuStartIdx = navBookCount;
int idx = 0;
const int myLibraryIdx = menuStartIdx + idx++;
const int opdsLibraryIdx = hasOpdsUrl ? menuStartIdx + idx++ : -1;
const int fileTransferIdx = menuStartIdx + idx++;
const int settingsIdx = menuStartIdx + idx;
std::vector<std::string> menuLabels; std::vector<std::string> menuLabels;
std::vector<std::string> menuIcons; std::vector<std::string> menuIcons;
std::vector<bool> menuSelected; std::vector<bool> menuSelected;
// Menu items are only selected when NOT in book selection mode menuLabels.push_back("Browse Files");
const bool menuActive = !inBookSelection; menuIcons.push_back("folder");
menuSelected.push_back(selectorIndex == myLibraryIdx);
menuLabels.push_back("Books");
menuIcons.push_back("book");
menuSelected.push_back(menuActive && selectorIndex == myLibraryIdx);
if (hasOpdsUrl) { if (hasOpdsUrl) {
menuLabels.push_back("Calibre Library"); menuLabels.push_back("Calibre Library");
menuIcons.push_back("library"); menuIcons.push_back("library");
menuSelected.push_back(menuActive && selectorIndex == opdsLibraryIdx); menuSelected.push_back(selectorIndex == opdsLibraryIdx);
} }
menuLabels.push_back("Files"); menuLabels.push_back("File Transfer");
menuIcons.push_back("folder");
menuSelected.push_back(menuActive && selectorIndex == filesIdx);
menuLabels.push_back("Transfer");
menuIcons.push_back("transfer"); menuIcons.push_back("transfer");
menuSelected.push_back(menuActive && selectorIndex == transferIdx); menuSelected.push_back(selectorIndex == fileTransferIdx);
menuLabels.push_back("Settings"); menuLabels.push_back("Settings");
menuIcons.push_back("settings"); menuIcons.push_back("settings");
menuSelected.push_back(menuActive && selectorIndex == settingsIdx); menuSelected.push_back(selectorIndex == settingsIdx);
context.setInt("MainMenu.Count", menuLabels.size()); context.setInt("MainMenu.Count", menuLabels.size());
for (size_t i = 0; i < menuLabels.size(); ++i) { for (size_t i = 0; i < menuLabels.size(); ++i) {

View File

@ -20,9 +20,7 @@ struct CachedBookInfo {
class HomeActivity final : public Activity { class HomeActivity final : public Activity {
TaskHandle_t displayTaskHandle = nullptr; TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr; SemaphoreHandle_t renderingMutex = nullptr;
int selectorIndex = 0; // Menu item index int selectorIndex = 0; // Unified index: 0..bookCount-1 = books, bookCount+ = menu
int bookSelectorIndex = 0; // Book selection index (0-2 for recent books)
bool inBookSelection = true; // True = selecting books, False = selecting menu
bool updateRequired = false; bool updateRequired = false;
bool hasContinueReading = false; bool hasContinueReading = false;
bool hasOpdsUrl = false; bool hasOpdsUrl = false;