Merge branch 'master' of https://github.com/jpirnay/crosspoint-reader into feat-page-overlay

This commit is contained in:
jpirnay
2026-03-05 18:28:38 +01:00
33 changed files with 268 additions and 172 deletions
+7
View File
@@ -135,6 +135,9 @@ class CrossPointSettings {
// UI Theme
enum UI_THEME { CLASSIC = 0, LYRA = 1, LYRA_3_COVERS = 2 };
// Image rendering in EPUB reader
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
// Sleep screen settings
uint8_t sleepScreen = DARK;
// Sleep screen cover mode settings
@@ -193,6 +196,10 @@ class CrossPointSettings {
uint8_t fadingFix = 0;
// Use book's embedded CSS styles for EPUB rendering (1 = enabled, 0 = disabled)
uint8_t embeddedStyle = 1;
// Show hidden files/directories (starting with '.') in the file browser (0 = hidden, 1 = show)
uint8_t showHiddenFiles = 0;
// Image rendering mode in EPUB reader
uint8_t imageRendering = IMAGES_DISPLAY;
~CrossPointSettings() = default;
+3
View File
@@ -62,6 +62,9 @@ inline const std::vector<SettingInfo>& getSettingsList() {
StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering,
{StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
"imageRendering", StrId::STR_CAT_READER),
// --- Controls ---
SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout,
{StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS),
+3 -3
View File
@@ -5,8 +5,8 @@
#include "boot_sleep/BootActivity.h"
#include "boot_sleep/SleepActivity.h"
#include "browser/OpdsBookBrowserActivity.h"
#include "home/FileBrowserActivity.h"
#include "home/HomeActivity.h"
#include "home/MyLibraryActivity.h"
#include "home/RecentBooksActivity.h"
#include "network/CrossPointWebServerActivity.h"
#include "reader/ReaderActivity.h"
@@ -169,8 +169,8 @@ void ActivityManager::goToFileTransfer() {
void ActivityManager::goToSettings() { replaceActivity(std::make_unique<SettingsActivity>(renderer, mappedInput)); }
void ActivityManager::goToMyLibrary(std::string path) {
replaceActivity(std::make_unique<MyLibraryActivity>(renderer, mappedInput, std::move(path)));
void ActivityManager::goToFileBrowser(std::string path) {
replaceActivity(std::make_unique<FileBrowserActivity>(renderer, mappedInput, std::move(path)));
}
void ActivityManager::goToRecentBooks() {
+1 -1
View File
@@ -79,7 +79,7 @@ class ActivityManager {
// goTo... functions are convenient wrapper for replaceActivity()
void goToFileTransfer();
void goToSettings();
void goToMyLibrary(std::string path = {});
void goToFileBrowser(std::string path = {});
void goToRecentBooks();
void goToBrowser();
void goToReader(std::string path);
+15 -4
View File
@@ -182,9 +182,20 @@ void SleepActivity::onEnter() {
}
void SleepActivity::renderCustomSleepScreen() const {
// Check if we have a /sleep directory
auto dir = Storage.open("/sleep");
// Check if we have a /.sleep (preferred) or /sleep directory
const char* sleepDir = nullptr;
auto dir = Storage.open("/.sleep");
if (dir && dir.isDirectory()) {
sleepDir = "/.sleep";
} else {
if (dir) dir.close();
dir = Storage.open("/sleep");
if (dir && dir.isDirectory()) {
sleepDir = "/sleep";
}
}
if (sleepDir) {
std::vector<std::string> files;
char name[500];
// collect all valid BMP files
@@ -224,10 +235,10 @@ void SleepActivity::renderCustomSleepScreen() const {
}
APP_STATE.lastSleepImage = randomFileIndex;
APP_STATE.saveToFile();
const auto filename = "/sleep/" + files[randomFileIndex];
const auto filename = std::string(sleepDir) + "/" + files[randomFileIndex];
FsFile file;
if (Storage.openFileForRead("SLP", filename, file)) {
LOG_DBG("SLP", "Randomly loading: /sleep/%s", files[randomFileIndex].c_str());
LOG_DBG("SLP", "Randomly loading: %s/%s", sleepDir, files[randomFileIndex].c_str());
delay(100);
Bitmap bitmap(file, true);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
@@ -1,4 +1,4 @@
#include "MyLibraryActivity.h"
#include "FileBrowserActivity.h"
#include <Epub.h>
#include <GfxRenderer.h>
@@ -69,7 +69,7 @@ void sortFileList(std::vector<std::string>& strs) {
});
}
void MyLibraryActivity::loadFiles() {
void FileBrowserActivity::loadFiles() {
files.clear();
auto root = Storage.open(basepath.c_str());
@@ -104,7 +104,7 @@ void MyLibraryActivity::loadFiles() {
sortFileList(files);
}
void MyLibraryActivity::onEnter() {
void FileBrowserActivity::onEnter() {
Activity::onEnter();
loadFiles();
@@ -113,20 +113,20 @@ void MyLibraryActivity::onEnter() {
requestUpdate();
}
void MyLibraryActivity::onExit() {
void FileBrowserActivity::onExit() {
Activity::onExit();
files.clear();
}
void MyLibraryActivity::clearFileMetadata(const std::string& fullPath) {
void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) {
// Only clear cache for .epub files
if (StringUtils::checkFileExtension(fullPath, ".epub")) {
Epub(fullPath, "/.crosspoint").clearCache();
LOG_DBG("MyLibrary", "Cleared metadata cache for: %s", fullPath.c_str());
LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str());
}
}
void MyLibraryActivity::loop() {
void FileBrowserActivity::loop() {
// Long press BACK (1s+) goes to root folder
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= GO_HOME_MS &&
basepath != "/") {
@@ -152,10 +152,10 @@ void MyLibraryActivity::loop() {
auto handler = [this, fullPath](const ActivityResult& res) {
if (!res.isCancelled) {
LOG_DBG("MyLibrary", "Attempting to delete: %s", fullPath.c_str());
LOG_DBG("FileBrowser", "Attempting to delete: %s", fullPath.c_str());
clearFileMetadata(fullPath);
if (Storage.remove(fullPath.c_str())) {
LOG_DBG("MyLibrary", "Deleted successfully");
LOG_DBG("FileBrowser", "Deleted successfully");
loadFiles();
if (files.empty()) {
selectorIndex = 0;
@@ -166,10 +166,10 @@ void MyLibraryActivity::loop() {
requestUpdate(true);
} else {
LOG_ERR("MyLibrary", "Failed to delete file: %s", fullPath.c_str());
LOG_ERR("FileBrowser", "Failed to delete file: %s", fullPath.c_str());
}
} else {
LOG_DBG("MyLibrary", "Delete cancelled by user");
LOG_DBG("FileBrowser", "Delete cancelled by user");
}
};
@@ -244,7 +244,7 @@ std::string getFileName(std::string filename) {
return filename.substr(0, pos);
}
void MyLibraryActivity::render(RenderLock&&) {
void FileBrowserActivity::render(RenderLock&&) {
renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth();
@@ -274,7 +274,7 @@ void MyLibraryActivity::render(RenderLock&&) {
renderer.displayBuffer();
}
size_t MyLibraryActivity::findEntry(const std::string& name) const {
size_t FileBrowserActivity::findEntry(const std::string& name) const {
for (size_t i = 0; i < files.size(); i++)
if (files[i] == name) return i;
return 0;
@@ -8,7 +8,7 @@
#include "RecentBooksStore.h"
#include "util/ButtonNavigator.h"
class MyLibraryActivity final : public Activity {
class FileBrowserActivity final : public Activity {
private:
// Deletion
void clearFileMetadata(const std::string& fullPath);
@@ -26,8 +26,8 @@ class MyLibraryActivity final : public Activity {
size_t findEntry(const std::string& name) const;
public:
explicit MyLibraryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialPath = "/")
: Activity("MyLibrary", renderer, mappedInput), basepath(initialPath.empty() ? "/" : std::move(initialPath)) {}
explicit FileBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialPath = "/")
: Activity("FileBrowser", renderer, mappedInput), basepath(initialPath.empty() ? "/" : std::move(initialPath)) {}
void onEnter() override;
void onExit() override;
void loop() override;
+6 -6
View File
@@ -20,7 +20,7 @@
#include "util/StringUtils.h"
int HomeActivity::getMenuItemCount() const {
int count = 4; // My Library, Recents, File transfer, Settings
int count = 4; // File Browser, Recents, File transfer, Settings
if (!recentBooks.empty()) {
count += recentBooks.size();
}
@@ -189,7 +189,7 @@ void HomeActivity::loop() {
// Calculate dynamic indices based on which options are available
int idx = 0;
int menuSelectedIndex = selectorIndex - static_cast<int>(recentBooks.size());
const int myLibraryIdx = idx++;
const int fileBrowserIdx = idx++;
const int recentsIdx = idx++;
const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1;
const int fileTransferIdx = idx++;
@@ -197,8 +197,8 @@ void HomeActivity::loop() {
if (selectorIndex < recentBooks.size()) {
onSelectBook(recentBooks[selectorIndex].path);
} else if (menuSelectedIndex == myLibraryIdx) {
onMyLibraryOpen();
} else if (menuSelectedIndex == fileBrowserIdx) {
onFileBrowserOpen();
} else if (menuSelectedIndex == recentsIdx) {
onRecentsOpen();
} else if (menuSelectedIndex == opdsLibraryIdx) {
@@ -231,7 +231,7 @@ void HomeActivity::render(RenderLock&&) {
std::vector<UIIcon> menuIcons = {Folder, Recent, Transfer, Settings};
if (hasOpdsUrl) {
// Insert OPDS Browser after My Library
// Insert OPDS Browser after File Browser
menuItems.insert(menuItems.begin() + 2, tr(STR_OPDS_BROWSER));
menuIcons.insert(menuIcons.begin() + 2, Library);
}
@@ -261,7 +261,7 @@ void HomeActivity::render(RenderLock&&) {
void HomeActivity::onSelectBook(const std::string& path) { activityManager.goToReader(path); }
void HomeActivity::onMyLibraryOpen() { activityManager.goToMyLibrary(); }
void HomeActivity::onFileBrowserOpen() { activityManager.goToFileBrowser(); }
void HomeActivity::onRecentsOpen() { activityManager.goToRecentBooks(); }
+2 -2
View File
@@ -3,7 +3,7 @@
#include <vector>
#include "../Activity.h"
#include "./MyLibraryActivity.h"
#include "./FileBrowserActivity.h"
#include "util/ButtonNavigator.h"
struct RecentBook;
@@ -21,7 +21,7 @@ class HomeActivity final : public Activity {
uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image
std::vector<RecentBook> recentBooks;
void onSelectBook(const std::string& path);
void onMyLibraryOpen();
void onFileBrowserOpen();
void onRecentsOpen();
void onSettingsOpen();
void onFileTransferOpen();
+5 -3
View File
@@ -182,7 +182,7 @@ void EpubReaderActivity::loop() {
// Long press BACK (1s+) goes to file selection
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
activityManager.goToMyLibrary(epub ? epub->getPath() : "");
activityManager.goToFileBrowser(epub ? epub->getPath() : "");
return;
}
@@ -573,14 +573,16 @@ void EpubReaderActivity::render(RenderLock&& lock) {
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle)) {
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle,
SETTINGS.imageRendering)) {
LOG_DBG("ERS", "Cache not found, building...");
const auto popupFn = [this]() { GUI.drawPopup(renderer, tr(STR_INDEXING)); };
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, popupFn)) {
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle,
SETTINGS.imageRendering, popupFn)) {
LOG_ERR("ERS", "Failed to persist page data to SD");
section.reset();
return;
+1 -19
View File
@@ -204,25 +204,7 @@ void KOReaderSyncActivity::onEnter() {
// Check if already connected (e.g. from settings page auth)
if (WiFi.status() == WL_CONNECTED) {
LOG_DBG("KOSync", "Already connected to WiFi");
state = SYNCING;
statusMessage = tr(STR_SYNCING_TIME);
requestUpdate(true);
// Perform sync directly (will be handled in loop)
xTaskCreate(
[](void* param) {
auto* self = static_cast<KOReaderSyncActivity*>(param);
// Sync time first
syncTimeWithNTP();
{
RenderLock lock(*self);
self->statusMessage = tr(STR_CALC_HASH);
}
self->requestUpdate(true);
self->performSync();
vTaskDelete(nullptr);
},
"SyncTask", 4096, this, 1, nullptr);
onWifiSelectionComplete(true);
return;
}
+1 -1
View File
@@ -80,7 +80,7 @@ std::unique_ptr<Txt> ReaderActivity::loadTxt(const std::string& path) {
void ReaderActivity::goToLibrary(const std::string& fromBookPath) {
// If coming from a book, start in that book's folder; otherwise start from root
auto initialPath = fromBookPath.empty() ? "/" : extractFolderPath(fromBookPath);
activityManager.goToMyLibrary(std::move(initialPath));
activityManager.goToFileBrowser(std::move(initialPath));
}
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
+1 -1
View File
@@ -2,7 +2,7 @@
#include <memory>
#include "../Activity.h"
#include "activities/home/MyLibraryActivity.h"
#include "activities/home/FileBrowserActivity.h"
class Epub;
class Xtc;
+1 -1
View File
@@ -135,7 +135,7 @@ void TxtReaderActivity::onExit() {
void TxtReaderActivity::loop() {
// Long press BACK (1s+) goes to file selection
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
activityManager.goToMyLibrary(txt ? txt->getPath() : "");
activityManager.goToFileBrowser(txt ? txt->getPath() : "");
return;
}
+1 -1
View File
@@ -70,7 +70,7 @@ void XtcReaderActivity::loop() {
// Long press BACK (1s+) goes to file selection
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
activityManager.goToMyLibrary(xtc ? xtc->getPath() : "");
activityManager.goToFileBrowser(xtc ? xtc->getPath() : "");
return;
}
@@ -51,23 +51,9 @@ void KOReaderAuthActivity::performAuthentication() {
void KOReaderAuthActivity::onEnter() {
Activity::onEnter();
// Turn on WiFi
WiFi.mode(WIFI_STA);
// Check if already connected
if (WiFi.status() == WL_CONNECTED) {
state = AUTHENTICATING;
statusMessage = tr(STR_AUTHENTICATING);
requestUpdate();
// Perform authentication in a separate task
xTaskCreate(
[](void* param) {
auto* self = static_cast<KOReaderAuthActivity*>(param);
self->performAuthentication();
vTaskDelete(nullptr);
},
"AuthTask", 4096, this, 1, nullptr);
onWifiSelectionComplete(true);
return;
}