mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-08 08:37:38 +03:00
Compare commits
2 Commits
3ea0878189
...
e9469b7475
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9469b7475 | ||
|
|
db2a7f4f8d |
@ -24,7 +24,7 @@ void HomeActivity::taskTrampoline(void* param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int HomeActivity::getMenuItemCount() const {
|
int HomeActivity::getMenuItemCount() const {
|
||||||
int count = 3; // My Library, File transfer, Settings
|
int count = 4; // My Library, Recents, File transfer, Settings
|
||||||
if (!recentBooks.empty()) {
|
if (!recentBooks.empty()) {
|
||||||
count += recentBooks.size();
|
count += recentBooks.size();
|
||||||
}
|
}
|
||||||
@ -69,12 +69,7 @@ void HomeActivity::loadRecentBooks(int maxBooks, int coverHeight, PopupCallbacks
|
|||||||
if (StringUtils::checkFileExtension(lastBookFileName, ".epub")) {
|
if (StringUtils::checkFileExtension(lastBookFileName, ".epub")) {
|
||||||
Epub epub(path, "/.crosspoint");
|
Epub epub(path, "/.crosspoint");
|
||||||
epub.load(false);
|
epub.load(false);
|
||||||
// if (!epub.getTitle().empty()) {
|
|
||||||
// lastBookTitle = std::string(epub.getTitle());
|
|
||||||
// }
|
|
||||||
// if (!epub.getAuthor().empty()) {
|
|
||||||
// lastBookAuthor = std::string(epub.getAuthor());
|
|
||||||
// }
|
|
||||||
// Try to generate thumbnail image for Continue Reading card
|
// Try to generate thumbnail image for Continue Reading card
|
||||||
coverBmpPath = epub.getThumbBmpPath(coverHeight);
|
coverBmpPath = epub.getThumbBmpPath(coverHeight);
|
||||||
if (!SdMan.exists(coverBmpPath.c_str())) {
|
if (!SdMan.exists(coverBmpPath.c_str())) {
|
||||||
@ -93,9 +88,6 @@ void HomeActivity::loadRecentBooks(int maxBooks, int coverHeight, PopupCallbacks
|
|||||||
// Handle XTC file
|
// Handle XTC file
|
||||||
Xtc xtc(path, "/.crosspoint");
|
Xtc xtc(path, "/.crosspoint");
|
||||||
if (xtc.load()) {
|
if (xtc.load()) {
|
||||||
// if (!xtc.getTitle().empty()) {
|
|
||||||
// lastBookTitle = std::string(xtc.getTitle());
|
|
||||||
// }
|
|
||||||
// Try to generate thumbnail image for Continue Reading card
|
// Try to generate thumbnail image for Continue Reading card
|
||||||
coverBmpPath = xtc.getThumbBmpPath(coverHeight);
|
coverBmpPath = xtc.getThumbBmpPath(coverHeight);
|
||||||
if (!SdMan.exists(coverBmpPath.c_str())) {
|
if (!SdMan.exists(coverBmpPath.c_str())) {
|
||||||
@ -110,16 +102,6 @@ void HomeActivity::loadRecentBooks(int maxBooks, int coverHeight, PopupCallbacks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (lastBookTitle.empty()) {
|
|
||||||
// // Remove extension from title if we don't have metadata
|
|
||||||
// if (StringUtils::checkFileExtension(lastBookFileName, ".xtch")) {
|
|
||||||
// lastBookFileName.resize(lastBookFileName.length() - 5);
|
|
||||||
// } else if (StringUtils::checkFileExtension(lastBookFileName, ".xtc")) {
|
|
||||||
// lastBookFileName.resize(lastBookFileName.length() - 4);
|
|
||||||
// }
|
|
||||||
// lastBookTitle = lastBookFileName;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recentBooks.push_back(RecentBookWithCover{book, coverBmpPath});
|
recentBooks.push_back(RecentBookWithCover{book, coverBmpPath});
|
||||||
@ -227,14 +209,17 @@ void HomeActivity::loop() {
|
|||||||
int idx = 0;
|
int idx = 0;
|
||||||
int menuSelectedIndex = selectorIndex - static_cast<int>(recentBooks.size());
|
int menuSelectedIndex = selectorIndex - static_cast<int>(recentBooks.size());
|
||||||
const int myLibraryIdx = idx++;
|
const int myLibraryIdx = idx++;
|
||||||
|
const int recentsIdx = idx++;
|
||||||
const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1;
|
const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1;
|
||||||
const int fileTransferIdx = idx++;
|
const int fileTransferIdx = idx++;
|
||||||
const int settingsIdx = idx;
|
const int settingsIdx = idx;
|
||||||
|
|
||||||
if (selectorIndex < recentBooks.size()) {
|
if (selectorIndex < recentBooks.size()) {
|
||||||
onSelectBook(recentBooks[selectorIndex].book.path, MyLibraryActivity::Tab::Recent);
|
onSelectBook(recentBooks[selectorIndex].book.path);
|
||||||
} else if (menuSelectedIndex == myLibraryIdx) {
|
} else if (menuSelectedIndex == myLibraryIdx) {
|
||||||
onMyLibraryOpen();
|
onMyLibraryOpen();
|
||||||
|
} else if (menuSelectedIndex == recentsIdx) {
|
||||||
|
onRecentsOpen();
|
||||||
} else if (menuSelectedIndex == opdsLibraryIdx) {
|
} else if (menuSelectedIndex == opdsLibraryIdx) {
|
||||||
onOpdsBrowserOpen();
|
onOpdsBrowserOpen();
|
||||||
} else if (menuSelectedIndex == fileTransferIdx) {
|
} else if (menuSelectedIndex == fileTransferIdx) {
|
||||||
@ -289,7 +274,7 @@ void HomeActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build menu items dynamically
|
// Build menu items dynamically
|
||||||
std::vector<const char*> menuItems = {"Browse Files", "File Transfer", "Settings"};
|
std::vector<const char*> menuItems = {"Browse Files", "Recents", "File Transfer", "Settings"};
|
||||||
if (hasOpdsUrl) {
|
if (hasOpdsUrl) {
|
||||||
// Insert OPDS Browser after My Library
|
// Insert OPDS Browser after My Library
|
||||||
menuItems.insert(menuItems.begin() + 1, "OPDS Browser");
|
menuItems.insert(menuItems.begin() + 1, "OPDS Browser");
|
||||||
|
|||||||
@ -27,8 +27,9 @@ class HomeActivity final : public Activity {
|
|||||||
bool coverBufferStored = false; // Track if cover buffer is stored
|
bool coverBufferStored = false; // Track if cover buffer is stored
|
||||||
uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image
|
uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image
|
||||||
std::vector<RecentBookWithCover> recentBooks;
|
std::vector<RecentBookWithCover> recentBooks;
|
||||||
const std::function<void(const std::string& path, MyLibraryActivity::Tab fromTab)> onSelectBook;
|
const std::function<void(const std::string& path)> onSelectBook;
|
||||||
const std::function<void()> onMyLibraryOpen;
|
const std::function<void()> onMyLibraryOpen;
|
||||||
|
const std::function<void()> onRecentsOpen;
|
||||||
const std::function<void()> onSettingsOpen;
|
const std::function<void()> onSettingsOpen;
|
||||||
const std::function<void()> onFileTransferOpen;
|
const std::function<void()> onFileTransferOpen;
|
||||||
const std::function<void()> onOpdsBrowserOpen;
|
const std::function<void()> onOpdsBrowserOpen;
|
||||||
@ -43,14 +44,15 @@ class HomeActivity final : public Activity {
|
|||||||
void loadRecentBooks(int maxBooks, int coverHeight, PopupCallbacks& popupCallbacks);
|
void loadRecentBooks(int maxBooks, int coverHeight, PopupCallbacks& popupCallbacks);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HomeActivity(
|
explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
GfxRenderer& renderer, MappedInputManager& mappedInput,
|
const std::function<void(const std::string& path)>& onSelectBook,
|
||||||
const std::function<void(const std::string& path, MyLibraryActivity::Tab fromTab)>& onSelectBook,
|
const std::function<void()>& onMyLibraryOpen, const std::function<void()>& onRecentsOpen,
|
||||||
const std::function<void()>& onMyLibraryOpen, const std::function<void()>& onSettingsOpen,
|
const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen,
|
||||||
const std::function<void()>& onFileTransferOpen, const std::function<void()>& onOpdsBrowserOpen)
|
const std::function<void()>& onOpdsBrowserOpen)
|
||||||
: Activity("Home", renderer, mappedInput),
|
: Activity("Home", renderer, mappedInput),
|
||||||
onSelectBook(onSelectBook),
|
onSelectBook(onSelectBook),
|
||||||
onMyLibraryOpen(onMyLibraryOpen),
|
onMyLibraryOpen(onMyLibraryOpen),
|
||||||
|
onRecentsOpen(onRecentsOpen),
|
||||||
onSettingsOpen(onSettingsOpen),
|
onSettingsOpen(onSettingsOpen),
|
||||||
onFileTransferOpen(onFileTransferOpen),
|
onFileTransferOpen(onFileTransferOpen),
|
||||||
onOpdsBrowserOpen(onOpdsBrowserOpen) {}
|
onOpdsBrowserOpen(onOpdsBrowserOpen) {}
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#include <SDCardManager.h>
|
#include <SDCardManager.h>
|
||||||
|
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "RecentBooksStore.h"
|
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "util/StringUtils.h"
|
#include "util/StringUtils.h"
|
||||||
@ -29,20 +28,6 @@ void MyLibraryActivity::taskTrampoline(void* param) {
|
|||||||
self->displayTaskLoop();
|
self->displayTaskLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyLibraryActivity::loadRecentBooks() {
|
|
||||||
recentBooks.clear();
|
|
||||||
const auto& books = RECENT_BOOKS.getBooks();
|
|
||||||
recentBooks.reserve(books.size());
|
|
||||||
|
|
||||||
for (const auto& book : books) {
|
|
||||||
// Skip if file no longer exists
|
|
||||||
if (!SdMan.exists(book.path.c_str())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
recentBooks.push_back(book);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MyLibraryActivity::loadFiles() {
|
void MyLibraryActivity::loadFiles() {
|
||||||
files.clear();
|
files.clear();
|
||||||
|
|
||||||
@ -83,8 +68,6 @@ void MyLibraryActivity::onEnter() {
|
|||||||
|
|
||||||
renderingMutex = xSemaphoreCreateMutex();
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
// Load data for both tabs
|
|
||||||
loadRecentBooks();
|
|
||||||
loadFiles();
|
loadFiles();
|
||||||
|
|
||||||
selectorIndex = 0;
|
selectorIndex = 0;
|
||||||
@ -115,54 +98,45 @@ void MyLibraryActivity::onExit() {
|
|||||||
|
|
||||||
void MyLibraryActivity::loop() {
|
void MyLibraryActivity::loop() {
|
||||||
// Long press BACK (1s+) goes to root folder
|
// Long press BACK (1s+) goes to root folder
|
||||||
if (currentTab == Tab::Files && mappedInput.isPressed(MappedInputManager::Button::Back) &&
|
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= GO_HOME_MS &&
|
||||||
mappedInput.getHeldTime() >= GO_HOME_MS) {
|
basepath != "/") {
|
||||||
if (basepath != "/") {
|
basepath = "/";
|
||||||
basepath = "/";
|
loadFiles();
|
||||||
loadFiles();
|
selectorIndex = 0;
|
||||||
selectorIndex = 0;
|
updateRequired = true;
|
||||||
updateRequired = true;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Left);
|
const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Left) ||
|
||||||
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Right);
|
mappedInput.wasReleased(MappedInputManager::Button::Up);
|
||||||
const bool leftReleased = mappedInput.wasReleased(MappedInputManager::Button::Up);
|
;
|
||||||
const bool rightReleased = mappedInput.wasReleased(MappedInputManager::Button::Down);
|
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Right) ||
|
||||||
|
mappedInput.wasReleased(MappedInputManager::Button::Down);
|
||||||
|
|
||||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||||
const int pageItems = UITheme::getNumberOfItemsPerPage(renderer, true, true, true);
|
const int pageItems = UITheme::getNumberOfItemsPerPage(renderer, true, true, true);
|
||||||
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
if (currentTab == Tab::Recent) {
|
if (files.empty()) {
|
||||||
if (!recentBooks.empty() && selectorIndex < static_cast<int>(recentBooks.size())) {
|
return;
|
||||||
Serial.printf("Selected recent book: %s\n", recentBooks[selectorIndex].path.c_str());
|
}
|
||||||
onSelectBook(recentBooks[selectorIndex].path, currentTab);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (files.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (basepath.back() != '/') basepath += "/";
|
if (basepath.back() != '/') basepath += "/";
|
||||||
if (files[selectorIndex].back() == '/') {
|
if (files[selectorIndex].back() == '/') {
|
||||||
basepath += files[selectorIndex].substr(0, files[selectorIndex].length() - 1);
|
basepath += files[selectorIndex].substr(0, files[selectorIndex].length() - 1);
|
||||||
loadFiles();
|
loadFiles();
|
||||||
selectorIndex = 0;
|
selectorIndex = 0;
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else {
|
} else {
|
||||||
onSelectBook(basepath + files[selectorIndex], currentTab);
|
onSelectBook(basepath + files[selectorIndex]);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||||
// Short press: go up one directory, or go home if at root
|
// Short press: go up one directory, or go home if at root
|
||||||
if (mappedInput.getHeldTime() < GO_HOME_MS) {
|
if (mappedInput.getHeldTime() < GO_HOME_MS) {
|
||||||
if (currentTab == Tab::Files && basepath != "/") {
|
if (basepath != "/") {
|
||||||
const std::string oldPath = basepath;
|
const std::string oldPath = basepath;
|
||||||
|
|
||||||
basepath.replace(basepath.find_last_of('/'), std::string::npos, "");
|
basepath.replace(basepath.find_last_of('/'), std::string::npos, "");
|
||||||
@ -180,19 +154,7 @@ void MyLibraryActivity::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab switching: Left/Right always control tabs
|
int listSize = static_cast<int>(files.size());
|
||||||
if (leftReleased || rightReleased) {
|
|
||||||
if (currentTab == Tab::Files) {
|
|
||||||
currentTab = Tab::Recent;
|
|
||||||
} else {
|
|
||||||
currentTab = Tab::Files;
|
|
||||||
}
|
|
||||||
selectorIndex = 0;
|
|
||||||
updateRequired = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int listSize = (currentTab == Tab::Recent) ? static_cast<int>(recentBooks.size()) : static_cast<int>(files.size());
|
|
||||||
if (upReleased) {
|
if (upReleased) {
|
||||||
if (skipPage) {
|
if (skipPage) {
|
||||||
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + listSize) % listSize;
|
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + listSize) % listSize;
|
||||||
@ -232,34 +194,19 @@ void MyLibraryActivity::render() const {
|
|||||||
auto folderName = basepath == "/" ? "SD card" : basepath.substr(basepath.rfind('/') + 1).c_str();
|
auto folderName = basepath == "/" ? "SD card" : basepath.substr(basepath.rfind('/') + 1).c_str();
|
||||||
UITheme::drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, folderName);
|
UITheme::drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, folderName);
|
||||||
|
|
||||||
UITheme::drawTabBar(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight},
|
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
|
||||||
{{"Recent", currentTab == Tab::Recent}, {"Files", currentTab == Tab::Files}}, false);
|
|
||||||
|
|
||||||
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing;
|
|
||||||
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2;
|
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2;
|
||||||
if (currentTab == Tab::Recent) {
|
if (files.empty()) {
|
||||||
// Recent tab
|
renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, contentTop + 20, "No books found");
|
||||||
if (recentBooks.empty()) {
|
|
||||||
renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, contentTop + 20, "No recent books");
|
|
||||||
} else {
|
|
||||||
UITheme::drawList(
|
|
||||||
renderer, Rect{0, contentTop, pageWidth, contentHeight}, recentBooks.size(), selectorIndex,
|
|
||||||
[this](int index) { return recentBooks[index].title; }, false, nullptr, false, nullptr);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (files.empty()) {
|
UITheme::drawList(
|
||||||
renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, contentTop + 20, "No books found");
|
renderer, Rect{0, contentTop, pageWidth, contentHeight}, files.size(), selectorIndex,
|
||||||
} else {
|
[this](int index) { return files[index]; }, false, nullptr, false, nullptr);
|
||||||
UITheme::drawList(
|
|
||||||
renderer, Rect{0, contentTop, pageWidth, contentHeight}, files.size(), selectorIndex,
|
|
||||||
[this](int index) { return files[index]; }, false, nullptr, false, nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Help text
|
// Help text
|
||||||
const auto labels = mappedInput.mapLabels("« Home", "Open", "Up", "Down");
|
const auto labels = mappedInput.mapLabels("« Home", "Open", "Up", "Down");
|
||||||
UITheme::drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
UITheme::drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
UITheme::drawSideButtonHints(renderer, "^", "v");
|
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,29 +8,21 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "../Activity.h"
|
#include "../Activity.h"
|
||||||
#include "RecentBooksStore.h"
|
|
||||||
|
|
||||||
class MyLibraryActivity final : public Activity {
|
class MyLibraryActivity final : public Activity {
|
||||||
public:
|
|
||||||
enum class Tab { Recent, Files };
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TaskHandle_t displayTaskHandle = nullptr;
|
TaskHandle_t displayTaskHandle = nullptr;
|
||||||
SemaphoreHandle_t renderingMutex = nullptr;
|
SemaphoreHandle_t renderingMutex = nullptr;
|
||||||
|
|
||||||
Tab currentTab = Tab::Recent;
|
|
||||||
size_t selectorIndex = 0;
|
size_t selectorIndex = 0;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
|
|
||||||
// Recent tab state
|
// Files state
|
||||||
std::vector<RecentBook> recentBooks;
|
|
||||||
|
|
||||||
// Files tab state (from FileSelectionActivity)
|
|
||||||
std::string basepath = "/";
|
std::string basepath = "/";
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
const std::function<void(const std::string& path, Tab fromTab)> onSelectBook;
|
const std::function<void(const std::string& path)> onSelectBook;
|
||||||
const std::function<void()> onGoHome;
|
const std::function<void()> onGoHome;
|
||||||
|
|
||||||
static void taskTrampoline(void* param);
|
static void taskTrampoline(void* param);
|
||||||
@ -38,18 +30,16 @@ class MyLibraryActivity final : public Activity {
|
|||||||
void render() const;
|
void render() const;
|
||||||
|
|
||||||
// Data loading
|
// Data loading
|
||||||
void loadRecentBooks();
|
|
||||||
void loadFiles();
|
void loadFiles();
|
||||||
size_t findEntry(const std::string& name) const;
|
size_t findEntry(const std::string& name) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MyLibraryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
explicit MyLibraryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
const std::function<void()>& onGoHome,
|
const std::function<void()>& onGoHome,
|
||||||
const std::function<void(const std::string& path, Tab fromTab)>& onSelectBook,
|
const std::function<void(const std::string& path)>& onSelectBook,
|
||||||
Tab initialTab = Tab::Recent, std::string initialPath = "/")
|
std::string initialPath = "/")
|
||||||
: Activity("MyLibrary", renderer, mappedInput),
|
: Activity("MyLibrary", renderer, mappedInput),
|
||||||
basepath(initialPath.empty() ? "/" : std::move(initialPath)),
|
basepath(initialPath.empty() ? "/" : std::move(initialPath)),
|
||||||
currentTab(initialTab),
|
|
||||||
onSelectBook(onSelectBook),
|
onSelectBook(onSelectBook),
|
||||||
onGoHome(onGoHome) {}
|
onGoHome(onGoHome) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
|||||||
148
src/activities/home/RecentBooksActivity.cpp
Normal file
148
src/activities/home/RecentBooksActivity.cpp
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#include "RecentBooksActivity.h"
|
||||||
|
|
||||||
|
#include <GfxRenderer.h>
|
||||||
|
#include <SDCardManager.h>
|
||||||
|
|
||||||
|
#include "MappedInputManager.h"
|
||||||
|
#include "RecentBooksStore.h"
|
||||||
|
#include "components/UITheme.h"
|
||||||
|
#include "fontIds.h"
|
||||||
|
#include "util/StringUtils.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr int SKIP_PAGE_MS = 700;
|
||||||
|
constexpr unsigned long GO_HOME_MS = 1000;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void RecentBooksActivity::taskTrampoline(void* param) {
|
||||||
|
auto* self = static_cast<RecentBooksActivity*>(param);
|
||||||
|
self->displayTaskLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentBooksActivity::loadRecentBooks() {
|
||||||
|
recentBooks.clear();
|
||||||
|
const auto& books = RECENT_BOOKS.getBooks();
|
||||||
|
recentBooks.reserve(books.size());
|
||||||
|
|
||||||
|
for (const auto& book : books) {
|
||||||
|
// Skip if file no longer exists
|
||||||
|
if (!SdMan.exists(book.path.c_str())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
recentBooks.push_back(book);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentBooksActivity::onEnter() {
|
||||||
|
Activity::onEnter();
|
||||||
|
|
||||||
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
// Load data
|
||||||
|
loadRecentBooks();
|
||||||
|
|
||||||
|
selectorIndex = 0;
|
||||||
|
updateRequired = true;
|
||||||
|
|
||||||
|
xTaskCreate(&RecentBooksActivity::taskTrampoline, "RecentBooksActivityTask",
|
||||||
|
4096, // Stack size
|
||||||
|
this, // Parameters
|
||||||
|
1, // Priority
|
||||||
|
&displayTaskHandle // Task handle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentBooksActivity::onExit() {
|
||||||
|
Activity::onExit();
|
||||||
|
|
||||||
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
if (displayTaskHandle) {
|
||||||
|
vTaskDelete(displayTaskHandle);
|
||||||
|
displayTaskHandle = nullptr;
|
||||||
|
}
|
||||||
|
vSemaphoreDelete(renderingMutex);
|
||||||
|
renderingMutex = nullptr;
|
||||||
|
|
||||||
|
recentBooks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentBooksActivity::loop() {
|
||||||
|
const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Left) ||
|
||||||
|
mappedInput.wasReleased(MappedInputManager::Button::Up);
|
||||||
|
;
|
||||||
|
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Right) ||
|
||||||
|
mappedInput.wasReleased(MappedInputManager::Button::Down);
|
||||||
|
|
||||||
|
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||||
|
const int pageItems = UITheme::getNumberOfItemsPerPage(renderer, true, true, true);
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
|
if (!recentBooks.empty() && selectorIndex < static_cast<int>(recentBooks.size())) {
|
||||||
|
Serial.printf("Selected recent book: %s\n", recentBooks[selectorIndex].path.c_str());
|
||||||
|
onSelectBook(recentBooks[selectorIndex].path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||||
|
onGoHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
int listSize = static_cast<int>(recentBooks.size());
|
||||||
|
if (upReleased) {
|
||||||
|
if (skipPage) {
|
||||||
|
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + listSize) % listSize;
|
||||||
|
} else {
|
||||||
|
selectorIndex = (selectorIndex + listSize - 1) % listSize;
|
||||||
|
}
|
||||||
|
updateRequired = true;
|
||||||
|
} else if (downReleased) {
|
||||||
|
if (skipPage) {
|
||||||
|
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % listSize;
|
||||||
|
} else {
|
||||||
|
selectorIndex = (selectorIndex + 1) % listSize;
|
||||||
|
}
|
||||||
|
updateRequired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentBooksActivity::displayTaskLoop() {
|
||||||
|
while (true) {
|
||||||
|
if (updateRequired) {
|
||||||
|
updateRequired = false;
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
render();
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
}
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentBooksActivity::render() const {
|
||||||
|
renderer.clearScreen();
|
||||||
|
|
||||||
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
auto metrics = UITheme::getMetrics();
|
||||||
|
|
||||||
|
UITheme::drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, "Recent Books");
|
||||||
|
|
||||||
|
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
|
||||||
|
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2;
|
||||||
|
|
||||||
|
// Recent tab
|
||||||
|
if (recentBooks.empty()) {
|
||||||
|
renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, contentTop + 20, "No recent books");
|
||||||
|
} else {
|
||||||
|
UITheme::drawList(
|
||||||
|
renderer, Rect{0, contentTop, pageWidth, contentHeight}, recentBooks.size(), selectorIndex,
|
||||||
|
[this](int index) { return recentBooks[index].title; }, false, nullptr, false, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help text
|
||||||
|
const auto labels = mappedInput.mapLabels("« Home", "Open", "Up", "Down");
|
||||||
|
UITheme::drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
|
renderer.displayBuffer();
|
||||||
|
}
|
||||||
43
src/activities/home/RecentBooksActivity.h
Normal file
43
src/activities/home/RecentBooksActivity.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../Activity.h"
|
||||||
|
#include "RecentBooksStore.h"
|
||||||
|
|
||||||
|
class RecentBooksActivity final : public Activity {
|
||||||
|
private:
|
||||||
|
TaskHandle_t displayTaskHandle = nullptr;
|
||||||
|
SemaphoreHandle_t renderingMutex = nullptr;
|
||||||
|
|
||||||
|
size_t selectorIndex = 0;
|
||||||
|
bool updateRequired = false;
|
||||||
|
|
||||||
|
// Recent tab state
|
||||||
|
std::vector<RecentBook> recentBooks;
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
const std::function<void(const std::string& path)> onSelectBook;
|
||||||
|
const std::function<void()> onGoHome;
|
||||||
|
|
||||||
|
static void taskTrampoline(void* param);
|
||||||
|
[[noreturn]] void displayTaskLoop();
|
||||||
|
void render() const;
|
||||||
|
|
||||||
|
// Data loading
|
||||||
|
void loadRecentBooks();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RecentBooksActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
|
const std::function<void()>& onGoHome,
|
||||||
|
const std::function<void(const std::string& path)>& onSelectBook)
|
||||||
|
: Activity("RecentBooks", renderer, mappedInput), onSelectBook(onSelectBook), onGoHome(onGoHome) {}
|
||||||
|
void onEnter() override;
|
||||||
|
void onExit() override;
|
||||||
|
void loop() override;
|
||||||
|
};
|
||||||
@ -74,7 +74,7 @@ std::unique_ptr<Txt> ReaderActivity::loadTxt(const std::string& path) {
|
|||||||
void ReaderActivity::goToLibrary(const std::string& fromBookPath) {
|
void ReaderActivity::goToLibrary(const std::string& fromBookPath) {
|
||||||
// If coming from a book, start in that book's folder; otherwise start from root
|
// If coming from a book, start in that book's folder; otherwise start from root
|
||||||
const auto initialPath = fromBookPath.empty() ? "/" : extractFolderPath(fromBookPath);
|
const auto initialPath = fromBookPath.empty() ? "/" : extractFolderPath(fromBookPath);
|
||||||
onGoToLibrary(initialPath, libraryTab);
|
onGoToLibrary(initialPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
|
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
|
||||||
|
|||||||
@ -10,10 +10,9 @@ class Txt;
|
|||||||
|
|
||||||
class ReaderActivity final : public ActivityWithSubactivity {
|
class ReaderActivity final : public ActivityWithSubactivity {
|
||||||
std::string initialBookPath;
|
std::string initialBookPath;
|
||||||
std::string currentBookPath; // Track current book path for navigation
|
std::string currentBookPath; // Track current book path for navigation
|
||||||
MyLibraryActivity::Tab libraryTab; // Track which tab to return to
|
|
||||||
const std::function<void()> onGoBack;
|
const std::function<void()> onGoBack;
|
||||||
const std::function<void(const std::string&, MyLibraryActivity::Tab)> onGoToLibrary;
|
const std::function<void(const std::string&)> onGoToLibrary;
|
||||||
static std::unique_ptr<Epub> loadEpub(const std::string& path);
|
static std::unique_ptr<Epub> loadEpub(const std::string& path);
|
||||||
static std::unique_ptr<Xtc> loadXtc(const std::string& path);
|
static std::unique_ptr<Xtc> loadXtc(const std::string& path);
|
||||||
static std::unique_ptr<Txt> loadTxt(const std::string& path);
|
static std::unique_ptr<Txt> loadTxt(const std::string& path);
|
||||||
@ -28,11 +27,10 @@ class ReaderActivity final : public ActivityWithSubactivity {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialBookPath,
|
explicit ReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialBookPath,
|
||||||
MyLibraryActivity::Tab libraryTab, const std::function<void()>& onGoBack,
|
const std::function<void()>& onGoBack,
|
||||||
const std::function<void(const std::string&, MyLibraryActivity::Tab)>& onGoToLibrary)
|
const std::function<void(const std::string&)>& onGoToLibrary)
|
||||||
: ActivityWithSubactivity("Reader", renderer, mappedInput),
|
: ActivityWithSubactivity("Reader", renderer, mappedInput),
|
||||||
initialBookPath(std::move(initialBookPath)),
|
initialBookPath(std::move(initialBookPath)),
|
||||||
libraryTab(libraryTab),
|
|
||||||
onGoBack(onGoBack),
|
onGoBack(onGoBack),
|
||||||
onGoToLibrary(onGoToLibrary) {}
|
onGoToLibrary(onGoToLibrary) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"};
|
const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
constexpr int changeTabsMs = 700;
|
||||||
constexpr int displaySettingsCount = 7;
|
constexpr int displaySettingsCount = 7;
|
||||||
const SettingInfo displaySettings[displaySettingsCount] = {
|
const SettingInfo displaySettings[displaySettingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
@ -111,7 +112,7 @@ void SettingsActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool hasChangedCategory = false;
|
bool hasChangedCategory = false;
|
||||||
|
|
||||||
// Handle actions with early return
|
// Handle actions with early return
|
||||||
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
||||||
if (selectedSettingIndex == 0) {
|
if (selectedSettingIndex == 0) {
|
||||||
@ -131,22 +132,27 @@ void SettingsActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Up);
|
||||||
|
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Down);
|
||||||
|
const bool leftReleased = mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||||
|
const bool rightReleased = mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||||
|
const bool changeTab = mappedInput.getHeldTime() > changeTabsMs;
|
||||||
|
|
||||||
// Handle navigation
|
// Handle navigation
|
||||||
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
if (upReleased && changeTab) {
|
||||||
selectedSettingIndex = (selectedSettingIndex > 0) ? (selectedSettingIndex - 1) : (settingsCount);
|
|
||||||
updateRequired = true;
|
|
||||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
|
||||||
selectedSettingIndex = (selectedSettingIndex < settingsCount) ? (selectedSettingIndex + 1) : 0;
|
|
||||||
updateRequired = true;
|
|
||||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Up)) {
|
|
||||||
hasChangedCategory = true;
|
hasChangedCategory = true;
|
||||||
selectedCategoryIndex = (selectedCategoryIndex > 0) ? (selectedCategoryIndex - 1) : (categoryCount - 1);
|
selectedCategoryIndex = (selectedCategoryIndex > 0) ? (selectedCategoryIndex - 1) : (categoryCount - 1);
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down)) {
|
} else if (downReleased && changeTab) {
|
||||||
hasChangedCategory = true;
|
hasChangedCategory = true;
|
||||||
selectedCategoryIndex = (selectedCategoryIndex < categoryCount - 1) ? (selectedCategoryIndex + 1) : 0;
|
selectedCategoryIndex = (selectedCategoryIndex < categoryCount - 1) ? (selectedCategoryIndex + 1) : 0;
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
|
} else if (upReleased || leftReleased) {
|
||||||
|
selectedSettingIndex = (selectedSettingIndex > 0) ? (selectedSettingIndex - 1) : (settingsCount);
|
||||||
|
updateRequired = true;
|
||||||
|
} else if (rightReleased || downReleased) {
|
||||||
|
selectedSettingIndex = (selectedSettingIndex < settingsCount) ? (selectedSettingIndex + 1) : 0;
|
||||||
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasChangedCategory) {
|
if (hasChangedCategory) {
|
||||||
@ -270,8 +276,8 @@ void SettingsActivity::render() const {
|
|||||||
Rect{0, metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing, pageWidth,
|
Rect{0, metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing, pageWidth,
|
||||||
pageHeight - (metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.buttonHintsHeight +
|
pageHeight - (metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.buttonHintsHeight +
|
||||||
metrics.verticalSpacing * 2)},
|
metrics.verticalSpacing * 2)},
|
||||||
settingsCount, selectedSettingIndex - 1, [this](int index) { return std::string(settingsList[index].name); }, false,
|
settingsCount, selectedSettingIndex - 1, [this](int index) { return std::string(settingsList[index].name); },
|
||||||
nullptr, true,
|
false, nullptr, true,
|
||||||
[this](int i) {
|
[this](int i) {
|
||||||
const auto& setting = settingsList[i];
|
const auto& setting = settingsList[i];
|
||||||
std::string valueText = "";
|
std::string valueText = "";
|
||||||
@ -293,9 +299,8 @@ void SettingsActivity::render() const {
|
|||||||
metrics.versionTextY, CROSSPOINT_VERSION);
|
metrics.versionTextY, CROSSPOINT_VERSION);
|
||||||
|
|
||||||
// Draw help text
|
// Draw help text
|
||||||
const auto labels = mappedInput.mapLabels("« Back", selectedSettingIndex == 0 ? "Tab >" : "Toggle", "Up", "Down");
|
const auto labels = mappedInput.mapLabels("« Back", "Toggle", "Up", "Down");
|
||||||
UITheme::drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
UITheme::drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
UITheme::drawSideButtonHints(renderer, "^", "v");
|
|
||||||
|
|
||||||
// Always use standard refresh for settings screen
|
// Always use standard refresh for settings screen
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@ -89,7 +89,8 @@ void UITheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* tit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITheme::drawTabBar(const GfxRenderer& renderer, const Rect rect, const std::vector<TabInfo>& tabs, bool selected) {
|
void UITheme::drawTabBar(const GfxRenderer& renderer, const Rect rect, const std::vector<TabInfo>& tabs,
|
||||||
|
bool selected) {
|
||||||
if (currentTheme != nullptr) {
|
if (currentTheme != nullptr) {
|
||||||
currentTheme->drawTabBar(renderer, rect, tabs, selected);
|
currentTheme->drawTabBar(renderer, rect, tabs, selected);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -170,7 +170,7 @@ void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
|
|||||||
constexpr int margin = 15; // Offset from right edge
|
constexpr int margin = 15; // Offset from right edge
|
||||||
|
|
||||||
const int centerX = rect.x + rect.width - indicatorWidth / 2 - margin;
|
const int centerX = rect.x + rect.width - indicatorWidth / 2 - margin;
|
||||||
const int indicatorTop = rect.y + 60; // Offset to avoid overlapping side button hints
|
const int indicatorTop = rect.y; // Offset to avoid overlapping side button hints
|
||||||
const int indicatorBottom = rect.y + rect.height - 30;
|
const int indicatorBottom = rect.y + rect.height - 30;
|
||||||
|
|
||||||
// Draw up arrow at top (^) - narrow point at top, wide base at bottom
|
// Draw up arrow at top (^) - narrow point at top, wide base at bottom
|
||||||
@ -234,7 +234,8 @@ void BaseTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseTheme::drawTabBar(const GfxRenderer& renderer, const Rect rect, const std::vector<TabInfo>& tabs, bool selected) {
|
void BaseTheme::drawTabBar(const GfxRenderer& renderer, const Rect rect, const std::vector<TabInfo>& tabs,
|
||||||
|
bool selected) {
|
||||||
constexpr int underlineHeight = 2; // Height of selection underline
|
constexpr int underlineHeight = 2; // Height of selection underline
|
||||||
constexpr int underlineGap = 4; // Gap between text and underline
|
constexpr int underlineGap = 4; // Gap between text and underline
|
||||||
|
|
||||||
|
|||||||
@ -93,13 +93,15 @@ void LyraTheme::drawTabBar(const GfxRenderer& renderer, Rect rect, const std::ve
|
|||||||
renderer.fillRoundedRect(currentX, rect.y + 1, textWidth + 2 * hPaddingInSelection, rect.height - 4,
|
renderer.fillRoundedRect(currentX, rect.y + 1, textWidth + 2 * hPaddingInSelection, rect.height - 4,
|
||||||
cornerRadius, COLOR_BLACK);
|
cornerRadius, COLOR_BLACK);
|
||||||
} else {
|
} else {
|
||||||
renderer.fillRectDither(currentX, rect.y, textWidth + 2 * hPaddingInSelection, rect.height - 3, COLOR_LIGHT_GRAY);
|
renderer.fillRectDither(currentX, rect.y, textWidth + 2 * hPaddingInSelection, rect.height - 3,
|
||||||
renderer.drawLine(currentX, rect.y + rect.height - 3, currentX + textWidth + 2 * hPaddingInSelection, rect.y + rect.height - 3, 2, true);
|
COLOR_LIGHT_GRAY);
|
||||||
|
renderer.drawLine(currentX, rect.y + rect.height - 3, currentX + textWidth + 2 * hPaddingInSelection,
|
||||||
|
rect.y + rect.height - 3, 2, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.drawText(UI_10_FONT_ID, currentX + hPaddingInSelection, rect.y + 6, tab.label, !(tab.selected && selected), EpdFontFamily::REGULAR);
|
renderer.drawText(UI_10_FONT_ID, currentX + hPaddingInSelection, rect.y + 6, tab.label, !(tab.selected && selected),
|
||||||
|
EpdFontFamily::REGULAR);
|
||||||
|
|
||||||
currentX += textWidth + LyraMetrics::values.tabSpacing + 2 * hPaddingInSelection;
|
currentX += textWidth + LyraMetrics::values.tabSpacing + 2 * hPaddingInSelection;
|
||||||
}
|
}
|
||||||
@ -116,7 +118,7 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
|
|||||||
|
|
||||||
const int totalPages = (itemCount + pageItems - 1) / pageItems;
|
const int totalPages = (itemCount + pageItems - 1) / pageItems;
|
||||||
if (totalPages > 1) {
|
if (totalPages > 1) {
|
||||||
const int scrollAreaHeight = topHintButtonY - rect.y - LyraMetrics::values.verticalSpacing;
|
const int scrollAreaHeight = rect.height;
|
||||||
|
|
||||||
// Draw scroll bar
|
// Draw scroll bar
|
||||||
const int scrollBarHeight = (scrollAreaHeight * pageItems) / itemCount;
|
const int scrollBarHeight = (scrollAreaHeight * pageItems) / itemCount;
|
||||||
@ -134,8 +136,8 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
|
|||||||
(totalPages > 1 ? (LyraMetrics::values.scrollBarWidth + LyraMetrics::values.scrollBarRightOffset) : 1);
|
(totalPages > 1 ? (LyraMetrics::values.scrollBarWidth + LyraMetrics::values.scrollBarRightOffset) : 1);
|
||||||
if (selectedIndex >= 0) {
|
if (selectedIndex >= 0) {
|
||||||
renderer.fillRoundedRect(LyraMetrics::values.contentSidePadding, rect.y + selectedIndex % pageItems * rowHeight,
|
renderer.fillRoundedRect(LyraMetrics::values.contentSidePadding, rect.y + selectedIndex % pageItems * rowHeight,
|
||||||
contentWidth - LyraMetrics::values.contentSidePadding * 2, rowHeight, cornerRadius,
|
contentWidth - LyraMetrics::values.contentSidePadding * 2, rowHeight, cornerRadius,
|
||||||
COLOR_LIGHT_GRAY);
|
COLOR_LIGHT_GRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw all items
|
// Draw all items
|
||||||
@ -198,8 +200,8 @@ void LyraTheme::drawButtonHints(const GfxRenderer& renderer, const char* btn1, c
|
|||||||
const int textX = x + (buttonWidth - 1 - textWidth) / 2;
|
const int textX = x + (buttonWidth - 1 - textWidth) / 2;
|
||||||
renderer.drawText(SMALL_FONT_ID, textX, pageHeight - buttonY + textYOffset, labels[i]);
|
renderer.drawText(SMALL_FONT_ID, textX, pageHeight - buttonY + textYOffset, labels[i]);
|
||||||
} else {
|
} else {
|
||||||
renderer.drawRoundedRect(x, pageHeight - smallButtonHeight, buttonWidth, smallButtonHeight, 1, cornerRadius, true, true, false,
|
renderer.drawRoundedRect(x, pageHeight - smallButtonHeight, buttonWidth, smallButtonHeight, 1, cornerRadius, true,
|
||||||
false, true);
|
true, false, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
src/main.cpp
23
src/main.cpp
@ -20,6 +20,7 @@
|
|||||||
#include "activities/browser/OpdsBookBrowserActivity.h"
|
#include "activities/browser/OpdsBookBrowserActivity.h"
|
||||||
#include "activities/home/HomeActivity.h"
|
#include "activities/home/HomeActivity.h"
|
||||||
#include "activities/home/MyLibraryActivity.h"
|
#include "activities/home/MyLibraryActivity.h"
|
||||||
|
#include "activities/home/RecentBooksActivity.h"
|
||||||
#include "activities/network/CrossPointWebServerActivity.h"
|
#include "activities/network/CrossPointWebServerActivity.h"
|
||||||
#include "activities/reader/ReaderActivity.h"
|
#include "activities/reader/ReaderActivity.h"
|
||||||
#include "activities/settings/SettingsActivity.h"
|
#include "activities/settings/SettingsActivity.h"
|
||||||
@ -204,11 +205,12 @@ void enterDeepSleep() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onGoHome();
|
void onGoHome();
|
||||||
void onGoToMyLibraryWithTab(const std::string& path, MyLibraryActivity::Tab tab);
|
void onGoToMyLibraryWithPath(const std::string& path);
|
||||||
void onGoToReader(const std::string& initialEpubPath, MyLibraryActivity::Tab fromTab) {
|
void onGoToRecentBooks();
|
||||||
|
void onGoToReader(const std::string& initialEpubPath) {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(
|
enterNewActivity(
|
||||||
new ReaderActivity(renderer, mappedInputManager, initialEpubPath, fromTab, onGoHome, onGoToMyLibraryWithTab));
|
new ReaderActivity(renderer, mappedInputManager, initialEpubPath, onGoHome, onGoToMyLibraryWithPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoToFileTransfer() {
|
void onGoToFileTransfer() {
|
||||||
@ -226,9 +228,14 @@ void onGoToMyLibrary() {
|
|||||||
enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader));
|
enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoToMyLibraryWithTab(const std::string& path, MyLibraryActivity::Tab tab) {
|
void onGoToRecentBooks() {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader, tab, path));
|
enterNewActivity(new RecentBooksActivity(renderer, mappedInputManager, onGoHome, onGoToReader));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onGoToMyLibraryWithPath(const std::string& path) {
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoToBrowser() {
|
void onGoToBrowser() {
|
||||||
@ -238,8 +245,8 @@ void onGoToBrowser() {
|
|||||||
|
|
||||||
void onGoHome() {
|
void onGoHome() {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new HomeActivity(renderer, mappedInputManager, onGoToReader, onGoToMyLibrary, onGoToSettings,
|
enterNewActivity(new HomeActivity(renderer, mappedInputManager, onGoToReader, onGoToMyLibrary, onGoToRecentBooks,
|
||||||
onGoToFileTransfer, onGoToBrowser));
|
onGoToSettings, onGoToFileTransfer, onGoToBrowser));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDisplayAndFonts() {
|
void setupDisplayAndFonts() {
|
||||||
@ -320,7 +327,7 @@ void setup() {
|
|||||||
APP_STATE.openEpubPath = "";
|
APP_STATE.openEpubPath = "";
|
||||||
APP_STATE.lastSleepImage = 0;
|
APP_STATE.lastSleepImage = 0;
|
||||||
APP_STATE.saveToFile();
|
APP_STATE.saveToFile();
|
||||||
onGoToReader(path, MyLibraryActivity::Tab::Recent);
|
onGoToReader(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we're not still holding the power button before leaving setup
|
// Ensure we're not still holding the power button before leaving setup
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user