mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 22:57:50 +03:00
feat: Apply orientation to UI too, move setting to Display
This commit is contained in:
parent
e5c0ddc9fa
commit
ba700eef84
@ -494,11 +494,11 @@ void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char
|
|||||||
setOrientation(Orientation::Portrait);
|
setOrientation(Orientation::Portrait);
|
||||||
|
|
||||||
const int pageHeight = getScreenHeight();
|
const int pageHeight = getScreenHeight();
|
||||||
constexpr int buttonWidth = 106;
|
constexpr int buttonWidth = 94;
|
||||||
constexpr int buttonHeight = 40;
|
constexpr int buttonHeight = 40;
|
||||||
constexpr int buttonY = 40; // Distance from bottom
|
constexpr int buttonY = 40; // Distance from bottom
|
||||||
constexpr int textYOffset = 7; // Distance from top of button to text baseline
|
constexpr int textYOffset = 7; // Distance from top of button to text baseline
|
||||||
constexpr int buttonPositions[] = {25, 130, 245, 350};
|
constexpr int buttonPositions[] = {50, 143, 247, 340};
|
||||||
const char* labels[] = {btn1, btn2, btn3, btn4};
|
const char* labels[] = {btn1, btn2, btn3, btn4};
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
@ -516,7 +516,10 @@ void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char
|
|||||||
setOrientation(orig_orientation);
|
setOrientation(orig_orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, const char* bottomBtn) const {
|
void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, const char* bottomBtn) {
|
||||||
|
const Orientation orig_orientation = getOrientation();
|
||||||
|
setOrientation(Orientation::Portrait);
|
||||||
|
|
||||||
const int screenWidth = getScreenWidth();
|
const int screenWidth = getScreenWidth();
|
||||||
constexpr int buttonWidth = 40; // Width on screen (height when rotated)
|
constexpr int buttonWidth = 40; // Width on screen (height when rotated)
|
||||||
constexpr int buttonHeight = 80; // Height on screen (width when rotated)
|
constexpr int buttonHeight = 80; // Height on screen (width when rotated)
|
||||||
@ -565,6 +568,8 @@ void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, cons
|
|||||||
drawTextRotated90CW(fontId, textX, textY, labels[i]);
|
drawTextRotated90CW(fontId, textX, textY, labels[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOrientation(orig_orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getTextHeight(const int fontId) const {
|
int GfxRenderer::getTextHeight(const int fontId) const {
|
||||||
|
|||||||
@ -85,7 +85,7 @@ class GfxRenderer {
|
|||||||
|
|
||||||
// UI Components
|
// UI Components
|
||||||
void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4);
|
void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4);
|
||||||
void drawSideButtonHints(int fontId, const char* topBtn, const char* bottomBtn) const;
|
void drawSideButtonHints(int fontId, const char* topBtn, const char* bottomBtn);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Helper for drawing rotated text (90 degrees clockwise, for side buttons)
|
// Helper for drawing rotated text (90 degrees clockwise, for side buttons)
|
||||||
|
|||||||
78
src/activities/Activity.cpp
Normal file
78
src/activities/Activity.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include "Activity.h"
|
||||||
|
|
||||||
|
#include <GfxRenderer.h>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
|
|
||||||
|
void Activity::onEnter() {
|
||||||
|
Serial.printf("[%lu] [ACT] Entering activity: %s\n", millis(), name.c_str());
|
||||||
|
updateMargins();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Activity::onExit() { Serial.printf("[%lu] [ACT] Exiting activity: %s\n", millis(), name.c_str()); }
|
||||||
|
|
||||||
|
void Activity::updateMargins() {
|
||||||
|
switch (SETTINGS.orientation) {
|
||||||
|
case CrossPointSettings::ORIENTATION::PORTRAIT:
|
||||||
|
marginTop = 15;
|
||||||
|
marginBottom = 50;
|
||||||
|
marginLeft = marginRight = 20;
|
||||||
|
break;
|
||||||
|
case CrossPointSettings::ORIENTATION::LANDSCAPE_CW:
|
||||||
|
marginTop = 15;
|
||||||
|
marginLeft = 50;
|
||||||
|
marginRight = marginBottom = 20;
|
||||||
|
break;
|
||||||
|
case CrossPointSettings::ORIENTATION::INVERTED:
|
||||||
|
marginTop = 50;
|
||||||
|
marginBottom = 15;
|
||||||
|
marginLeft = marginRight = 20;
|
||||||
|
break;
|
||||||
|
case CrossPointSettings::ORIENTATION::LANDSCAPE_CCW:
|
||||||
|
marginTop = 15;
|
||||||
|
marginRight = 50;
|
||||||
|
marginLeft = marginBottom = 20;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
contentStartY = marginTop + 45;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Activity::updateRendererOrientation() {
|
||||||
|
switch (SETTINGS.orientation) {
|
||||||
|
case CrossPointSettings::ORIENTATION::PORTRAIT:
|
||||||
|
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||||
|
break;
|
||||||
|
case CrossPointSettings::ORIENTATION::LANDSCAPE_CW:
|
||||||
|
renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise);
|
||||||
|
break;
|
||||||
|
case CrossPointSettings::ORIENTATION::INVERTED:
|
||||||
|
renderer.setOrientation(GfxRenderer::Orientation::PortraitInverted);
|
||||||
|
break;
|
||||||
|
case CrossPointSettings::ORIENTATION::LANDSCAPE_CCW:
|
||||||
|
renderer.setOrientation(GfxRenderer::Orientation::LandscapeCounterClockwise);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updateMargins();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of items that fit on a page, derived from logical screen height.
|
||||||
|
// This adapts automatically when switching between portrait and landscape.
|
||||||
|
int Activity::getPageItems() const {
|
||||||
|
// Layout constants used in renderScreen
|
||||||
|
const int startY = contentStartY;
|
||||||
|
constexpr int lineHeight = LINE_HEIGHT;
|
||||||
|
|
||||||
|
const int screenHeight = renderer.getScreenHeight();
|
||||||
|
const int endY = screenHeight - lineHeight;
|
||||||
|
|
||||||
|
const int availableHeight = endY - startY;
|
||||||
|
int items = availableHeight / lineHeight;
|
||||||
|
|
||||||
|
// Ensure we always have at least one item per page to avoid division by zero
|
||||||
|
if (items < 1) {
|
||||||
|
items = 1;
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
@ -8,19 +8,31 @@
|
|||||||
class MappedInputManager;
|
class MappedInputManager;
|
||||||
class GfxRenderer;
|
class GfxRenderer;
|
||||||
|
|
||||||
|
constexpr int LINE_HEIGHT = 30;
|
||||||
|
|
||||||
class Activity {
|
class Activity {
|
||||||
protected:
|
protected:
|
||||||
std::string name;
|
std::string name;
|
||||||
GfxRenderer& renderer;
|
GfxRenderer& renderer;
|
||||||
MappedInputManager& mappedInput;
|
MappedInputManager& mappedInput;
|
||||||
|
int marginTop = 15;
|
||||||
|
int marginBottom = 50;
|
||||||
|
int marginLeft = 20;
|
||||||
|
int marginRight = 20;
|
||||||
|
int contentStartY = 60;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Activity(std::string name, GfxRenderer& renderer, MappedInputManager& mappedInput)
|
explicit Activity(std::string name, GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||||
: name(std::move(name)), renderer(renderer), mappedInput(mappedInput) {}
|
: name(std::move(name)), renderer(renderer), mappedInput(mappedInput) {}
|
||||||
virtual ~Activity() = default;
|
virtual ~Activity() = default;
|
||||||
virtual void onEnter() { Serial.printf("[%lu] [ACT] Entering activity: %s\n", millis(), name.c_str()); }
|
virtual void onEnter();
|
||||||
virtual void onExit() { Serial.printf("[%lu] [ACT] Exiting activity: %s\n", millis(), name.c_str()); }
|
virtual void onExit();
|
||||||
virtual void loop() {}
|
virtual void loop() {}
|
||||||
virtual bool skipLoopDelay() { return false; }
|
virtual bool skipLoopDelay() { return false; }
|
||||||
virtual bool preventAutoSleep() { return false; }
|
virtual bool preventAutoSleep() { return false; }
|
||||||
|
|
||||||
|
void updateMargins();
|
||||||
|
void updateRendererOrientation();
|
||||||
|
|
||||||
|
int getPageItems() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
void BootActivity::onEnter() {
|
void BootActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
|
updateRendererOrientation();
|
||||||
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|||||||
@ -126,6 +126,7 @@ void SleepActivity::renderDefaultSleepScreen() const {
|
|||||||
|
|
||||||
void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
|
void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
|
||||||
int x, y;
|
int x, y;
|
||||||
|
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
float cropX = 0, cropY = 0;
|
float cropX = 0, cropY = 0;
|
||||||
|
|||||||
@ -228,7 +228,7 @@ void OpdsBookBrowserActivity::render() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto pageStartIndex = selectorIndex / PAGE_ITEMS * PAGE_ITEMS;
|
const auto pageStartIndex = selectorIndex / PAGE_ITEMS * PAGE_ITEMS;
|
||||||
renderer.fillRect(0, 60 + (selectorIndex % PAGE_ITEMS) * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, contentStartY + (selectorIndex % PAGE_ITEMS) * LINE_HEIGHT - 2, pageWidth - 1, LINE_HEIGHT);
|
||||||
|
|
||||||
for (size_t i = pageStartIndex; i < entries.size() && i < static_cast<size_t>(pageStartIndex + PAGE_ITEMS); i++) {
|
for (size_t i = pageStartIndex; i < entries.size() && i < static_cast<size_t>(pageStartIndex + PAGE_ITEMS); i++) {
|
||||||
const auto& entry = entries[i];
|
const auto& entry = entries[i];
|
||||||
@ -246,7 +246,7 @@ void OpdsBookBrowserActivity::render() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto item = renderer.truncatedText(UI_10_FONT_ID, displayText.c_str(), renderer.getScreenWidth() - 40);
|
auto item = renderer.truncatedText(UI_10_FONT_ID, displayText.c_str(), renderer.getScreenWidth() - 40);
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % PAGE_ITEMS) * 30, item.c_str(),
|
renderer.drawText(UI_10_FONT_ID, 20, contentStartY + (i % PAGE_ITEMS) * LINE_HEIGHT, item.c_str(),
|
||||||
i != static_cast<size_t>(selectorIndex));
|
i != static_cast<size_t>(selectorIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -220,14 +220,29 @@ void HomeActivity::render() {
|
|||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
constexpr int margin = 20;
|
// --- Bottom menu tiles ---
|
||||||
constexpr int bottomMargin = 60;
|
// Build menu items dynamically
|
||||||
|
std::vector<const char*> menuItems = {"My Library", "File Transfer", "Settings"};
|
||||||
|
if (hasOpdsUrl) {
|
||||||
|
// Insert OPDS Browser after My Library
|
||||||
|
menuItems.insert(menuItems.begin() + 1, "OPDS Browser");
|
||||||
|
}
|
||||||
|
|
||||||
|
const int menuTileWidth = pageWidth - marginLeft - marginRight;
|
||||||
|
constexpr int menuTileHeight = 45;
|
||||||
|
constexpr int menuSpacing = 8;
|
||||||
|
const int totalMenuHeight =
|
||||||
|
static_cast<int>(menuItems.size()) * menuTileHeight + (static_cast<int>(menuItems.size()) - 1) * menuSpacing;
|
||||||
|
|
||||||
// --- Top "book" card for the current title (selectorIndex == 0) ---
|
// --- Top "book" card for the current title (selectorIndex == 0) ---
|
||||||
|
// if we're in inverted portrait, battery and hints can overlap
|
||||||
|
GfxRenderer::Orientation orientation = renderer.getOrientation();
|
||||||
|
const int batteryHeight = (orientation == GfxRenderer::Orientation::PortraitInverted ? 0 : 20);
|
||||||
const int bookWidth = pageWidth / 2;
|
const int bookWidth = pageWidth / 2;
|
||||||
const int bookHeight = pageHeight / 2;
|
const int maxBookHeight = pageHeight - marginTop - marginBottom - totalMenuHeight - batteryHeight;
|
||||||
const int bookX = (pageWidth - bookWidth) / 2;
|
const int bookHeight = std::min(pageHeight / 2, maxBookHeight);
|
||||||
constexpr int bookY = 30;
|
const int bookX = marginLeft + (pageWidth - marginLeft - marginRight - bookWidth) / 2;
|
||||||
|
const int bookY = marginTop + batteryHeight;
|
||||||
const bool bookSelected = hasContinueReading && selectorIndex == 0;
|
const bool bookSelected = hasContinueReading && selectorIndex == 0;
|
||||||
|
|
||||||
// Bookmark dimensions (used in multiple places)
|
// Bookmark dimensions (used in multiple places)
|
||||||
@ -443,7 +458,7 @@ void HomeActivity::render() {
|
|||||||
|
|
||||||
const int boxWidth = maxTextWidth + boxPadding * 2;
|
const int boxWidth = maxTextWidth + boxPadding * 2;
|
||||||
const int boxHeight = totalTextHeight + boxPadding * 2;
|
const int boxHeight = totalTextHeight + boxPadding * 2;
|
||||||
const int boxX = (pageWidth - boxWidth) / 2;
|
const int boxX = bookX + (bookWidth - boxWidth) / 2;
|
||||||
const int boxY = titleYStart - boxPadding;
|
const int boxY = titleYStart - boxPadding;
|
||||||
|
|
||||||
// Draw box (inverted when selected: black box instead of white)
|
// Draw box (inverted when selected: black box instead of white)
|
||||||
@ -453,7 +468,8 @@ void HomeActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& line : lines) {
|
for (const auto& line : lines) {
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, titleYStart, line.c_str(), !bookSelected);
|
renderer.drawText(UI_12_FONT_ID, bookX + (bookWidth - renderer.getTextWidth(UI_12_FONT_ID, line.c_str())) / 2,
|
||||||
|
titleYStart, line.c_str(), !bookSelected);
|
||||||
titleYStart += renderer.getLineHeight(UI_12_FONT_ID);
|
titleYStart += renderer.getLineHeight(UI_12_FONT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,26 +490,27 @@ void HomeActivity::render() {
|
|||||||
}
|
}
|
||||||
trimmedAuthor.append("...");
|
trimmedAuthor.append("...");
|
||||||
}
|
}
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, trimmedAuthor.c_str(), !bookSelected);
|
renderer.drawText(UI_10_FONT_ID,
|
||||||
|
bookX + (bookWidth - renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str())) / 2,
|
||||||
|
titleYStart, trimmedAuthor.c_str(), !bookSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Continue Reading" label at the bottom
|
// "Continue Reading" label at the bottom
|
||||||
const int continueY = bookY + bookHeight - renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2;
|
const int continueY = bookY + bookHeight - renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2;
|
||||||
|
const char* continueText = "Continue Reading";
|
||||||
|
const int continueTextWidth = renderer.getTextWidth(UI_10_FONT_ID, continueText);
|
||||||
if (coverRendered) {
|
if (coverRendered) {
|
||||||
// Draw box behind "Continue Reading" text (inverted when selected: black box instead of white)
|
// Draw box behind "Continue Reading" text (inverted when selected: black box instead of white)
|
||||||
const char* continueText = "Continue Reading";
|
|
||||||
const int continueTextWidth = renderer.getTextWidth(UI_10_FONT_ID, continueText);
|
|
||||||
constexpr int continuePadding = 6;
|
constexpr int continuePadding = 6;
|
||||||
const int continueBoxWidth = continueTextWidth + continuePadding * 2;
|
const int continueBoxWidth = continueTextWidth + continuePadding * 2;
|
||||||
const int continueBoxHeight = renderer.getLineHeight(UI_10_FONT_ID) + continuePadding;
|
const int continueBoxHeight = renderer.getLineHeight(UI_10_FONT_ID) + continuePadding;
|
||||||
const int continueBoxX = (pageWidth - continueBoxWidth) / 2;
|
const int continueBoxX = bookX + (bookWidth - continueBoxWidth) / 2;
|
||||||
const int continueBoxY = continueY - continuePadding / 2;
|
const int continueBoxY = continueY - continuePadding / 2;
|
||||||
renderer.fillRect(continueBoxX, continueBoxY, continueBoxWidth, continueBoxHeight, bookSelected);
|
renderer.fillRect(continueBoxX, continueBoxY, continueBoxWidth, continueBoxHeight, bookSelected);
|
||||||
renderer.drawRect(continueBoxX, continueBoxY, continueBoxWidth, continueBoxHeight, !bookSelected);
|
renderer.drawRect(continueBoxX, continueBoxY, continueBoxWidth, continueBoxHeight, !bookSelected);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, continueY, continueText, !bookSelected);
|
|
||||||
} else {
|
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, continueY, "Continue Reading", !bookSelected);
|
|
||||||
}
|
}
|
||||||
|
renderer.drawText(UI_10_FONT_ID, bookX + (bookWidth - continueTextWidth) / 2, continueY, continueText,
|
||||||
|
!bookSelected);
|
||||||
} else {
|
} else {
|
||||||
// No book to continue reading
|
// No book to continue reading
|
||||||
const int y =
|
const int y =
|
||||||
@ -502,30 +519,11 @@ void HomeActivity::render() {
|
|||||||
renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Start reading below");
|
renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Start reading below");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Bottom menu tiles ---
|
int menuStartY = bookY + bookHeight + menuSpacing;
|
||||||
// Build menu items dynamically
|
|
||||||
std::vector<const char*> menuItems = {"My Library", "File Transfer", "Settings"};
|
|
||||||
if (hasOpdsUrl) {
|
|
||||||
// Insert OPDS Browser after My Library
|
|
||||||
menuItems.insert(menuItems.begin() + 1, "OPDS Browser");
|
|
||||||
}
|
|
||||||
|
|
||||||
const int menuTileWidth = pageWidth - 2 * margin;
|
|
||||||
constexpr int menuTileHeight = 45;
|
|
||||||
constexpr int menuSpacing = 8;
|
|
||||||
const int totalMenuHeight =
|
|
||||||
static_cast<int>(menuItems.size()) * menuTileHeight + (static_cast<int>(menuItems.size()) - 1) * menuSpacing;
|
|
||||||
|
|
||||||
int menuStartY = bookY + bookHeight + 15;
|
|
||||||
// Ensure we don't collide with the bottom button legend
|
|
||||||
const int maxMenuStartY = pageHeight - bottomMargin - totalMenuHeight - margin;
|
|
||||||
if (menuStartY > maxMenuStartY) {
|
|
||||||
menuStartY = maxMenuStartY;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < menuItems.size(); ++i) {
|
for (size_t i = 0; i < menuItems.size(); ++i) {
|
||||||
const int overallIndex = static_cast<int>(i) + (hasContinueReading ? 1 : 0);
|
const int overallIndex = static_cast<int>(i) + (hasContinueReading ? 1 : 0);
|
||||||
constexpr int tileX = margin;
|
const int tileX = marginLeft;
|
||||||
const int tileY = menuStartY + static_cast<int>(i) * (menuTileHeight + menuSpacing);
|
const int tileY = menuStartY + static_cast<int>(i) * (menuTileHeight + menuSpacing);
|
||||||
const bool selected = selectorIndex == overallIndex;
|
const bool selected = selectorIndex == overallIndex;
|
||||||
|
|
||||||
@ -553,7 +551,7 @@ void HomeActivity::render() {
|
|||||||
// get percentage so we can align text properly
|
// get percentage so we can align text properly
|
||||||
const uint16_t percentage = battery.readPercentage();
|
const uint16_t percentage = battery.readPercentage();
|
||||||
const auto percentageText = showBatteryPercentage ? std::to_string(percentage) + "%" : "";
|
const auto percentageText = showBatteryPercentage ? std::to_string(percentage) + "%" : "";
|
||||||
const auto batteryX = pageWidth - 25 - renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str());
|
const auto batteryX = pageWidth - 30 - renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str());
|
||||||
ScreenComponents::drawBattery(renderer, batteryX, 10, showBatteryPercentage);
|
ScreenComponents::drawBattery(renderer, batteryX, 10, showBatteryPercentage);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@ -13,12 +13,8 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Layout constants
|
// Layout constants
|
||||||
constexpr int TAB_BAR_Y = 15;
|
|
||||||
constexpr int CONTENT_START_Y = 60;
|
|
||||||
constexpr int LINE_HEIGHT = 30;
|
|
||||||
constexpr int RECENTS_LINE_HEIGHT = 65; // Increased for two-line items
|
constexpr int RECENTS_LINE_HEIGHT = 65; // Increased for two-line items
|
||||||
constexpr int LEFT_MARGIN = 20;
|
constexpr int scrollIndicatorWidth = 20;
|
||||||
constexpr int RIGHT_MARGIN = 40; // Extra space for scroll indicator
|
|
||||||
|
|
||||||
// Timing thresholds
|
// Timing thresholds
|
||||||
constexpr int SKIP_PAGE_MS = 700;
|
constexpr int SKIP_PAGE_MS = 700;
|
||||||
@ -35,17 +31,6 @@ void sortFileList(std::vector<std::string>& strs) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int MyLibraryActivity::getPageItems() const {
|
|
||||||
const int screenHeight = renderer.getScreenHeight();
|
|
||||||
const int bottomBarHeight = 60; // Space for button hints
|
|
||||||
const int availableHeight = screenHeight - CONTENT_START_Y - bottomBarHeight;
|
|
||||||
int items = availableHeight / LINE_HEIGHT;
|
|
||||||
if (items < 1) {
|
|
||||||
items = 1;
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MyLibraryActivity::getCurrentItemCount() const {
|
int MyLibraryActivity::getCurrentItemCount() const {
|
||||||
if (currentTab == Tab::Recent) {
|
if (currentTab == Tab::Recent) {
|
||||||
return static_cast<int>(recentBooks.size());
|
return static_cast<int>(recentBooks.size());
|
||||||
@ -286,7 +271,7 @@ void MyLibraryActivity::render() const {
|
|||||||
|
|
||||||
// Draw tab bar
|
// Draw tab bar
|
||||||
std::vector<TabInfo> tabs = {{"Recent", currentTab == Tab::Recent}, {"Files", currentTab == Tab::Files}};
|
std::vector<TabInfo> tabs = {{"Recent", currentTab == Tab::Recent}, {"Files", currentTab == Tab::Files}};
|
||||||
ScreenComponents::drawTabBar(renderer, TAB_BAR_Y, tabs);
|
ScreenComponents::drawTabBar(renderer, marginTop, tabs);
|
||||||
|
|
||||||
// Draw content based on current tab
|
// Draw content based on current tab
|
||||||
if (currentTab == Tab::Recent) {
|
if (currentTab == Tab::Recent) {
|
||||||
@ -297,8 +282,8 @@ void MyLibraryActivity::render() const {
|
|||||||
|
|
||||||
// Draw scroll indicator
|
// Draw scroll indicator
|
||||||
const int screenHeight = renderer.getScreenHeight();
|
const int screenHeight = renderer.getScreenHeight();
|
||||||
const int contentHeight = screenHeight - CONTENT_START_Y - 60; // 60 for bottom bar
|
const int contentHeight = screenHeight - contentStartY - marginBottom;
|
||||||
ScreenComponents::drawScrollIndicator(renderer, getCurrentPage(), getTotalPages(), CONTENT_START_Y, contentHeight);
|
ScreenComponents::drawScrollIndicator(renderer, getCurrentPage(), getTotalPages(), contentStartY, contentHeight);
|
||||||
|
|
||||||
// Draw side button hints (up/down navigation on right side)
|
// Draw side button hints (up/down navigation on right side)
|
||||||
// Note: text is rotated 90° CW, so ">" appears as "^" and "<" appears as "v"
|
// Note: text is rotated 90° CW, so ">" appears as "^" and "<" appears as "v"
|
||||||
@ -317,20 +302,20 @@ void MyLibraryActivity::renderRecentTab() const {
|
|||||||
const int bookCount = static_cast<int>(recentBooks.size());
|
const int bookCount = static_cast<int>(recentBooks.size());
|
||||||
|
|
||||||
if (bookCount == 0) {
|
if (bookCount == 0) {
|
||||||
renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, CONTENT_START_Y, "No recent books");
|
renderer.drawText(UI_10_FONT_ID, marginLeft, contentStartY, "No recent books");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
||||||
|
|
||||||
// Draw selection highlight
|
// Draw selection highlight
|
||||||
renderer.fillRect(0, CONTENT_START_Y + (selectorIndex % pageItems) * RECENTS_LINE_HEIGHT - 2,
|
renderer.fillRect(0, contentStartY + (selectorIndex % pageItems) * RECENTS_LINE_HEIGHT - 2,
|
||||||
pageWidth - RIGHT_MARGIN, RECENTS_LINE_HEIGHT);
|
pageWidth - marginRight - scrollIndicatorWidth, RECENTS_LINE_HEIGHT);
|
||||||
|
|
||||||
// Draw items
|
// Draw items
|
||||||
for (int i = pageStartIndex; i < bookCount && i < pageStartIndex + pageItems; i++) {
|
for (int i = pageStartIndex; i < bookCount && i < pageStartIndex + pageItems; i++) {
|
||||||
const auto& book = recentBooks[i];
|
const auto& book = recentBooks[i];
|
||||||
const int y = CONTENT_START_Y + (i % pageItems) * RECENTS_LINE_HEIGHT;
|
const int y = contentStartY + (i % pageItems) * RECENTS_LINE_HEIGHT;
|
||||||
|
|
||||||
// Line 1: Title
|
// Line 1: Title
|
||||||
std::string title = book.title;
|
std::string title = book.title;
|
||||||
@ -346,14 +331,15 @@ void MyLibraryActivity::renderRecentTab() const {
|
|||||||
title.resize(dot);
|
title.resize(dot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto truncatedTitle = renderer.truncatedText(UI_12_FONT_ID, title.c_str(), pageWidth - LEFT_MARGIN - RIGHT_MARGIN);
|
auto truncatedTitle = renderer.truncatedText(UI_12_FONT_ID, title.c_str(),
|
||||||
renderer.drawText(UI_12_FONT_ID, LEFT_MARGIN, y + 2, truncatedTitle.c_str(), i != selectorIndex);
|
pageWidth - marginLeft - marginRight - scrollIndicatorWidth);
|
||||||
|
renderer.drawText(UI_12_FONT_ID, marginLeft, y + 2, truncatedTitle.c_str(), i != selectorIndex);
|
||||||
|
|
||||||
// Line 2: Author
|
// Line 2: Author
|
||||||
if (!book.author.empty()) {
|
if (!book.author.empty()) {
|
||||||
auto truncatedAuthor =
|
auto truncatedAuthor = renderer.truncatedText(UI_10_FONT_ID, book.author.c_str(),
|
||||||
renderer.truncatedText(UI_10_FONT_ID, book.author.c_str(), pageWidth - LEFT_MARGIN - RIGHT_MARGIN);
|
pageWidth - marginLeft - marginRight - scrollIndicatorWidth);
|
||||||
renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, y + 32, truncatedAuthor.c_str(), i != selectorIndex);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, y + 32, truncatedAuthor.c_str(), i != selectorIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,20 +350,21 @@ void MyLibraryActivity::renderFilesTab() const {
|
|||||||
const int fileCount = static_cast<int>(files.size());
|
const int fileCount = static_cast<int>(files.size());
|
||||||
|
|
||||||
if (fileCount == 0) {
|
if (fileCount == 0) {
|
||||||
renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, CONTENT_START_Y, "No books found");
|
renderer.drawText(UI_10_FONT_ID, marginLeft, contentStartY, "No books found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
||||||
|
|
||||||
// Draw selection highlight
|
// Draw selection highlight
|
||||||
renderer.fillRect(0, CONTENT_START_Y + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - RIGHT_MARGIN,
|
renderer.fillRect(0, contentStartY + (selectorIndex % pageItems) * LINE_HEIGHT - 2,
|
||||||
LINE_HEIGHT);
|
pageWidth - marginRight - scrollIndicatorWidth, LINE_HEIGHT);
|
||||||
|
|
||||||
// Draw items
|
// Draw items
|
||||||
for (int i = pageStartIndex; i < fileCount && i < pageStartIndex + pageItems; i++) {
|
for (int i = pageStartIndex; i < fileCount && i < pageStartIndex + pageItems; i++) {
|
||||||
auto item = renderer.truncatedText(UI_10_FONT_ID, files[i].c_str(), pageWidth - LEFT_MARGIN - RIGHT_MARGIN);
|
auto item = renderer.truncatedText(UI_10_FONT_ID, files[i].c_str(),
|
||||||
renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, CONTENT_START_Y + (i % pageItems) * LINE_HEIGHT, item.c_str(),
|
pageWidth - marginLeft - marginRight - scrollIndicatorWidth);
|
||||||
|
renderer.drawText(UI_10_FONT_ID, marginLeft, contentStartY + (i % pageItems) * LINE_HEIGHT, item.c_str(),
|
||||||
i != selectorIndex);
|
i != selectorIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,6 @@ class MyLibraryActivity final : public Activity {
|
|||||||
const std::function<void(const std::string& path, Tab fromTab)> onSelectBook;
|
const std::function<void(const std::string& path, Tab fromTab)> onSelectBook;
|
||||||
|
|
||||||
// Number of items that fit on a page
|
// Number of items that fit on a page
|
||||||
int getPageItems() const;
|
|
||||||
int getCurrentItemCount() const;
|
int getCurrentItemCount() const;
|
||||||
int getTotalPages() const;
|
int getTotalPages() const;
|
||||||
int getCurrentPage() const;
|
int getCurrentPage() const;
|
||||||
|
|||||||
@ -417,12 +417,13 @@ void drawQRCode(const GfxRenderer& renderer, const int x, const int y, const std
|
|||||||
void CrossPointWebServerActivity::renderServerRunning() const {
|
void CrossPointWebServerActivity::renderServerRunning() const {
|
||||||
// Use consistent line spacing
|
// Use consistent line spacing
|
||||||
constexpr int LINE_SPACING = 28; // Space between lines
|
constexpr int LINE_SPACING = 28; // Space between lines
|
||||||
|
const int screenWidth = renderer.getScreenWidth();
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "File Transfer", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (isApMode) {
|
if (isApMode) {
|
||||||
// AP mode display - center the content block
|
// AP mode display - center the content block
|
||||||
int startY = 55;
|
int startY = marginTop + 35;
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, startY, "Hotspot Mode", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, startY, "Hotspot Mode", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
@ -435,7 +436,7 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
|||||||
"or scan QR code with your phone to connect to Wifi.");
|
"or scan QR code with your phone to connect to Wifi.");
|
||||||
// Show QR code for URL
|
// Show QR code for URL
|
||||||
const std::string wifiConfig = std::string("WIFI:S:") + connectedSSID + ";;";
|
const std::string wifiConfig = std::string("WIFI:S:") + connectedSSID + ";;";
|
||||||
drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 4, wifiConfig);
|
drawQRCode(renderer, (screenWidth - 6 * 33) / 2, startY + LINE_SPACING * 4, wifiConfig);
|
||||||
|
|
||||||
startY += 6 * 29 + 3 * LINE_SPACING;
|
startY += 6 * 29 + 3 * LINE_SPACING;
|
||||||
// Show primary URL (hostname)
|
// Show primary URL (hostname)
|
||||||
@ -449,10 +450,10 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
|||||||
|
|
||||||
// Show QR code for URL
|
// Show QR code for URL
|
||||||
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 6, "or scan QR code with your phone:");
|
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 6, "or scan QR code with your phone:");
|
||||||
drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 7, hostnameUrl);
|
drawQRCode(renderer, (screenWidth - 6 * 33) / 2, startY + LINE_SPACING * 7, hostnameUrl);
|
||||||
} else {
|
} else {
|
||||||
// STA mode display (original behavior)
|
// STA mode display (original behavior)
|
||||||
const int startY = 65;
|
const int startY = marginTop + 50;
|
||||||
|
|
||||||
std::string ssidInfo = "Network: " + connectedSSID;
|
std::string ssidInfo = "Network: " + connectedSSID;
|
||||||
if (ssidInfo.length() > 28) {
|
if (ssidInfo.length() > 28) {
|
||||||
@ -474,7 +475,7 @@ void CrossPointWebServerActivity::renderServerRunning() const {
|
|||||||
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 4, "Open this URL in your browser");
|
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 4, "Open this URL in your browser");
|
||||||
|
|
||||||
// Show QR code for URL
|
// Show QR code for URL
|
||||||
drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 6, webInfo);
|
drawQRCode(renderer, (screenWidth - 6 * 33) / 2, startY + LINE_SPACING * 6, webInfo);
|
||||||
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "or scan QR code with your phone:");
|
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "or scan QR code with your phone:");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,10 +105,10 @@ void NetworkModeSelectionActivity::render() const {
|
|||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "File Transfer", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Draw subtitle
|
// Draw subtitle
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 50, "How would you like to connect?");
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 35, "How would you like to connect?");
|
||||||
|
|
||||||
// Draw menu items centered on screen
|
// Draw menu items centered on screen
|
||||||
constexpr int itemHeight = 50; // Height for each menu item (including description)
|
constexpr int itemHeight = 50; // Height for each menu item (including description)
|
||||||
@ -120,13 +120,13 @@ void NetworkModeSelectionActivity::render() const {
|
|||||||
|
|
||||||
// Draw selection highlight (black fill) for selected item
|
// Draw selection highlight (black fill) for selected item
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
renderer.fillRect(20, itemY - 2, pageWidth - 40, itemHeight - 6);
|
renderer.fillRect(marginLeft, itemY - 2, pageWidth - marginLeft - marginRight, itemHeight - 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw text: black=false (white text) when selected (on black background)
|
// Draw text: black=false (white text) when selected (on black background)
|
||||||
// black=true (black text) when not selected (on white background)
|
// black=true (black text) when not selected (on white background)
|
||||||
renderer.drawText(UI_10_FONT_ID, 30, itemY, MENU_ITEMS[i], /*black=*/!isSelected);
|
renderer.drawText(UI_10_FONT_ID, marginLeft + 10, itemY, MENU_ITEMS[i], /*black=*/!isSelected);
|
||||||
renderer.drawText(SMALL_FONT_ID, 30, itemY + 22, MENU_DESCRIPTIONS[i], /*black=*/!isSelected);
|
renderer.drawText(SMALL_FONT_ID, marginLeft + 10, itemY + 22, MENU_DESCRIPTIONS[i], /*black=*/!isSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw help text at bottom
|
// Draw help text at bottom
|
||||||
|
|||||||
@ -513,7 +513,7 @@ void WifiSelectionActivity::renderNetworkList() const {
|
|||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "WiFi Networks", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "WiFi Networks", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (networks.empty()) {
|
if (networks.empty()) {
|
||||||
// No networks found or scan failed
|
// No networks found or scan failed
|
||||||
@ -523,8 +523,8 @@ void WifiSelectionActivity::renderNetworkList() const {
|
|||||||
renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press OK to scan again");
|
renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press OK to scan again");
|
||||||
} else {
|
} else {
|
||||||
// Calculate how many networks we can display
|
// Calculate how many networks we can display
|
||||||
constexpr int startY = 60;
|
const int startY = contentStartY;
|
||||||
constexpr int lineHeight = 25;
|
constexpr int lineHeight = 25; // tighter spacing than normal menus
|
||||||
const int maxVisibleNetworks = (pageHeight - startY - 40) / lineHeight;
|
const int maxVisibleNetworks = (pageHeight - startY - 40) / lineHeight;
|
||||||
|
|
||||||
// Calculate scroll offset to keep selected item visible
|
// Calculate scroll offset to keep selected item visible
|
||||||
@ -541,7 +541,7 @@ void WifiSelectionActivity::renderNetworkList() const {
|
|||||||
|
|
||||||
// Draw selection indicator
|
// Draw selection indicator
|
||||||
if (static_cast<int>(i) == selectedNetworkIndex) {
|
if (static_cast<int>(i) == selectedNetworkIndex) {
|
||||||
renderer.drawText(UI_10_FONT_ID, 5, networkY, ">");
|
renderer.drawText(UI_10_FONT_ID, marginLeft - 15, networkY, ">");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw network name (truncate if too long)
|
// Draw network name (truncate if too long)
|
||||||
@ -549,42 +549,47 @@ void WifiSelectionActivity::renderNetworkList() const {
|
|||||||
if (displayName.length() > 16) {
|
if (displayName.length() > 16) {
|
||||||
displayName.replace(13, displayName.length() - 13, "...");
|
displayName.replace(13, displayName.length() - 13, "...");
|
||||||
}
|
}
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, networkY, displayName.c_str());
|
renderer.drawText(UI_10_FONT_ID, marginLeft, networkY, displayName.c_str());
|
||||||
|
|
||||||
// Draw signal strength indicator
|
// Draw signal strength indicator
|
||||||
std::string signalStr = getSignalStrengthIndicator(network.rssi);
|
std::string signalStr = getSignalStrengthIndicator(network.rssi);
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 90, networkY, signalStr.c_str());
|
renderer.drawText(UI_10_FONT_ID, pageWidth - marginRight - 90, networkY, signalStr.c_str());
|
||||||
|
|
||||||
// Draw saved indicator (checkmark) for networks with saved passwords
|
// Draw saved indicator (checkmark) for networks with saved passwords
|
||||||
if (network.hasSavedPassword) {
|
if (network.hasSavedPassword) {
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 50, networkY, "+");
|
renderer.drawText(UI_10_FONT_ID, pageWidth - marginRight - 50, networkY, "+");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw lock icon for encrypted networks
|
// Draw lock icon for encrypted networks
|
||||||
if (network.isEncrypted) {
|
if (network.isEncrypted) {
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 30, networkY, "*");
|
renderer.drawText(UI_10_FONT_ID, pageWidth - marginRight - 30, networkY, "*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw scroll indicators if needed
|
// Draw scroll indicators if needed
|
||||||
if (scrollOffset > 0) {
|
if (scrollOffset > 0) {
|
||||||
renderer.drawText(SMALL_FONT_ID, pageWidth - 15, startY - 10, "^");
|
renderer.drawText(SMALL_FONT_ID, pageWidth - marginRight - 15, startY - 10, "^");
|
||||||
}
|
}
|
||||||
if (scrollOffset + maxVisibleNetworks < static_cast<int>(networks.size())) {
|
if (scrollOffset + maxVisibleNetworks < static_cast<int>(networks.size())) {
|
||||||
renderer.drawText(SMALL_FONT_ID, pageWidth - 15, startY + maxVisibleNetworks * lineHeight, "v");
|
renderer.drawText(SMALL_FONT_ID, pageWidth - marginRight - 15, startY + maxVisibleNetworks * lineHeight, "v");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show network count
|
// Show network count
|
||||||
char countStr[32];
|
char countStr[32];
|
||||||
snprintf(countStr, sizeof(countStr), "%zu networks found", networks.size());
|
snprintf(countStr, sizeof(countStr), "%zu networks found", networks.size());
|
||||||
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 90, countStr);
|
renderer.drawText(SMALL_FONT_ID, pageWidth - marginRight - renderer.getTextWidth(SMALL_FONT_ID, countStr) - 95,
|
||||||
|
pageHeight - 90, countStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show MAC address above the network count and legend
|
// Show MAC address above the network count and legend
|
||||||
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 105, cachedMacAddress.c_str());
|
renderer.drawText(SMALL_FONT_ID,
|
||||||
|
pageWidth - marginRight - renderer.getTextWidth(SMALL_FONT_ID, cachedMacAddress.c_str()) - 95,
|
||||||
|
pageHeight - 105, cachedMacAddress.c_str());
|
||||||
|
|
||||||
// Draw help text
|
// Draw help text
|
||||||
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved");
|
const char helpLabel[] = "* = Encrypted | + = Saved";
|
||||||
|
renderer.drawText(SMALL_FONT_ID, pageWidth - marginRight - renderer.getTextWidth(SMALL_FONT_ID, helpLabel) - 95,
|
||||||
|
pageHeight - 75, helpLabel);
|
||||||
const auto labels = mappedInput.mapLabels("« Back", "Connect", "", "");
|
const auto labels = mappedInput.mapLabels("« Back", "Connect", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,24 +34,6 @@ void EpubReaderActivity::onEnter() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure screen orientation based on settings
|
|
||||||
switch (SETTINGS.orientation) {
|
|
||||||
case CrossPointSettings::ORIENTATION::PORTRAIT:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
|
||||||
break;
|
|
||||||
case CrossPointSettings::ORIENTATION::LANDSCAPE_CW:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise);
|
|
||||||
break;
|
|
||||||
case CrossPointSettings::ORIENTATION::INVERTED:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::PortraitInverted);
|
|
||||||
break;
|
|
||||||
case CrossPointSettings::ORIENTATION::LANDSCAPE_CCW:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::LandscapeCounterClockwise);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderingMutex = xSemaphoreCreateMutex();
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
epub->setupCacheDir();
|
epub->setupCacheDir();
|
||||||
@ -101,9 +83,6 @@ void EpubReaderActivity::onEnter() {
|
|||||||
void EpubReaderActivity::onExit() {
|
void EpubReaderActivity::onExit() {
|
||||||
ActivityWithSubactivity::onExit();
|
ActivityWithSubactivity::onExit();
|
||||||
|
|
||||||
// Reset orientation back to portrait for the rest of the UI
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
|
||||||
|
|
||||||
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
if (displayTaskHandle) {
|
if (displayTaskHandle) {
|
||||||
|
|||||||
@ -32,24 +32,6 @@ int EpubReaderChapterSelectionActivity::tocIndexFromItemIndex(int itemIndex) con
|
|||||||
return itemIndex - offset;
|
return itemIndex - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EpubReaderChapterSelectionActivity::getPageItems() const {
|
|
||||||
// Layout constants used in renderScreen
|
|
||||||
constexpr int startY = 60;
|
|
||||||
constexpr int lineHeight = 30;
|
|
||||||
|
|
||||||
const int screenHeight = renderer.getScreenHeight();
|
|
||||||
const int endY = screenHeight - lineHeight;
|
|
||||||
|
|
||||||
const int availableHeight = endY - startY;
|
|
||||||
int items = availableHeight / lineHeight;
|
|
||||||
|
|
||||||
// Ensure we always have at least one item per page to avoid division by zero
|
|
||||||
if (items < 1) {
|
|
||||||
items = 1;
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EpubReaderChapterSelectionActivity::taskTrampoline(void* param) {
|
void EpubReaderChapterSelectionActivity::taskTrampoline(void* param) {
|
||||||
auto* self = static_cast<EpubReaderChapterSelectionActivity*>(param);
|
auto* self = static_cast<EpubReaderChapterSelectionActivity*>(param);
|
||||||
self->displayTaskLoop();
|
self->displayTaskLoop();
|
||||||
@ -184,21 +166,21 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
|||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Go to Chapter", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Go to Chapter", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
||||||
renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, contentStartY + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - 1, LINE_HEIGHT);
|
||||||
|
|
||||||
for (int i = 0; i < pageItems; i++) {
|
for (int i = 0; i < pageItems; i++) {
|
||||||
int itemIndex = pageStartIndex + i;
|
int itemIndex = pageStartIndex + i;
|
||||||
if (itemIndex >= totalItems) break;
|
if (itemIndex >= totalItems) break;
|
||||||
const int displayY = 60 + i * 30;
|
const int displayY = contentStartY + (itemIndex % pageItems) * LINE_HEIGHT;
|
||||||
const bool isSelected = (itemIndex == selectorIndex);
|
const bool isSelected = (itemIndex == selectorIndex);
|
||||||
|
|
||||||
if (isSyncItem(itemIndex)) {
|
if (isSyncItem(itemIndex)) {
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, displayY, ">> Sync Progress", !isSelected);
|
||||||
} else {
|
} else {
|
||||||
const int tocIndex = tocIndexFromItemIndex(itemIndex);
|
const int tocIndex = tocIndexFromItemIndex(itemIndex);
|
||||||
auto item = epub->getTocItem(tocIndex);
|
auto item = epub->getTocItem(tocIndex);
|
||||||
|
|
||||||
const int indentSize = 20 + (item.level - 1) * 15;
|
const int indentSize = marginLeft + (item.level - 1) * 15;
|
||||||
const std::string chapterName =
|
const std::string chapterName =
|
||||||
renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize);
|
renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize);
|
||||||
|
|
||||||
@ -206,11 +188,8 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip button hints in landscape CW mode (they overlap content)
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
|
||||||
if (renderer.getOrientation() != GfxRenderer::LandscapeClockwise) {
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
|
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,10 +22,6 @@ class EpubReaderChapterSelectionActivity final : public ActivityWithSubactivity
|
|||||||
const std::function<void(int newSpineIndex)> onSelectSpineIndex;
|
const std::function<void(int newSpineIndex)> onSelectSpineIndex;
|
||||||
const std::function<void(int newSpineIndex, int newPage)> onSyncPosition;
|
const std::function<void(int newSpineIndex, int newPage)> onSyncPosition;
|
||||||
|
|
||||||
// Number of items that fit on a page, derived from logical screen height.
|
|
||||||
// This adapts automatically when switching between portrait and landscape.
|
|
||||||
int getPageItems() const;
|
|
||||||
|
|
||||||
// Total items including sync options (top and bottom)
|
// Total items including sync options (top and bottom)
|
||||||
int getTotalItems() const;
|
int getTotalItems() const;
|
||||||
|
|
||||||
|
|||||||
@ -259,11 +259,11 @@ void KOReaderSyncActivity::render() {
|
|||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "KOReader Sync", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "KOReader Sync", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (state == NO_CREDENTIALS) {
|
if (state == NO_CREDENTIALS) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 280, "No credentials configured", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 265, "No credentials configured", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 320, "Set up KOReader account in Settings");
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 305, "Set up KOReader account in Settings");
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
@ -272,14 +272,14 @@ void KOReaderSyncActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == SYNCING || state == UPLOADING) {
|
if (state == SYNCING || state == UPLOADING) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, statusMessage.c_str(), true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 285, statusMessage.c_str(), true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == SHOWING_RESULT) {
|
if (state == SHOWING_RESULT) {
|
||||||
// Show comparison
|
// Show comparison
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 120, "Progress found!", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 105, "Progress found!", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Get chapter names from TOC
|
// Get chapter names from TOC
|
||||||
const int remoteTocIndex = epub->getTocIndexForSpineIndex(remotePosition.spineIndex);
|
const int remoteTocIndex = epub->getTocIndexForSpineIndex(remotePosition.spineIndex);
|
||||||
@ -291,52 +291,52 @@ void KOReaderSyncActivity::render() {
|
|||||||
: ("Section " + std::to_string(currentSpineIndex + 1));
|
: ("Section " + std::to_string(currentSpineIndex + 1));
|
||||||
|
|
||||||
// Remote progress - chapter and page
|
// Remote progress - chapter and page
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 160, "Remote:", true);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 145, "Remote:", true);
|
||||||
char remoteChapterStr[128];
|
char remoteChapterStr[128];
|
||||||
snprintf(remoteChapterStr, sizeof(remoteChapterStr), " %s", remoteChapter.c_str());
|
snprintf(remoteChapterStr, sizeof(remoteChapterStr), " %s", remoteChapter.c_str());
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 185, remoteChapterStr);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 170, remoteChapterStr);
|
||||||
char remotePageStr[64];
|
char remotePageStr[64];
|
||||||
snprintf(remotePageStr, sizeof(remotePageStr), " Page %d, %.2f%% overall", remotePosition.pageNumber + 1,
|
snprintf(remotePageStr, sizeof(remotePageStr), " Page %d, %.2f%% overall", remotePosition.pageNumber + 1,
|
||||||
remoteProgress.percentage * 100);
|
remoteProgress.percentage * 100);
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 210, remotePageStr);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 195, remotePageStr);
|
||||||
|
|
||||||
if (!remoteProgress.device.empty()) {
|
if (!remoteProgress.device.empty()) {
|
||||||
char deviceStr[64];
|
char deviceStr[64];
|
||||||
snprintf(deviceStr, sizeof(deviceStr), " From: %s", remoteProgress.device.c_str());
|
snprintf(deviceStr, sizeof(deviceStr), " From: %s", remoteProgress.device.c_str());
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 235, deviceStr);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 220, deviceStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local progress - chapter and page
|
// Local progress - chapter and page
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 270, "Local:", true);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 255, "Local:", true);
|
||||||
char localChapterStr[128];
|
char localChapterStr[128];
|
||||||
snprintf(localChapterStr, sizeof(localChapterStr), " %s", localChapter.c_str());
|
snprintf(localChapterStr, sizeof(localChapterStr), " %s", localChapter.c_str());
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 295, localChapterStr);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 280, localChapterStr);
|
||||||
char localPageStr[64];
|
char localPageStr[64];
|
||||||
snprintf(localPageStr, sizeof(localPageStr), " Page %d/%d, %.2f%% overall", currentPage + 1, totalPagesInSpine,
|
snprintf(localPageStr, sizeof(localPageStr), " Page %d/%d, %.2f%% overall", currentPage + 1, totalPagesInSpine,
|
||||||
localProgress.percentage * 100);
|
localProgress.percentage * 100);
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 320, localPageStr);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 305, localPageStr);
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
const int optionY = 350;
|
const int optionY = marginTop + 335;
|
||||||
const int optionHeight = 30;
|
const int optionHeight = 30;
|
||||||
|
|
||||||
// Apply option
|
// Apply option
|
||||||
if (selectedOption == 0) {
|
if (selectedOption == 0) {
|
||||||
renderer.fillRect(0, optionY - 2, pageWidth - 1, optionHeight);
|
renderer.fillRect(0, optionY - 2, pageWidth - 1, optionHeight);
|
||||||
}
|
}
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, optionY, "Apply remote progress", selectedOption != 0);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, optionY, "Apply remote progress", selectedOption != 0);
|
||||||
|
|
||||||
// Upload option
|
// Upload option
|
||||||
if (selectedOption == 1) {
|
if (selectedOption == 1) {
|
||||||
renderer.fillRect(0, optionY + optionHeight - 2, pageWidth - 1, optionHeight);
|
renderer.fillRect(0, optionY + optionHeight - 2, pageWidth - 1, optionHeight);
|
||||||
}
|
}
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, optionY + optionHeight, "Upload local progress", selectedOption != 1);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, optionY + optionHeight, "Upload local progress", selectedOption != 1);
|
||||||
|
|
||||||
// Cancel option
|
// Cancel option
|
||||||
if (selectedOption == 2) {
|
if (selectedOption == 2) {
|
||||||
renderer.fillRect(0, optionY + optionHeight * 2 - 2, pageWidth - 1, optionHeight);
|
renderer.fillRect(0, optionY + optionHeight * 2 - 2, pageWidth - 1, optionHeight);
|
||||||
}
|
}
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, optionY + optionHeight * 2, "Cancel", selectedOption != 2);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, optionY + optionHeight * 2, "Cancel", selectedOption != 2);
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("", "Select", "", "");
|
const auto labels = mappedInput.mapLabels("", "Select", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
@ -345,8 +345,8 @@ void KOReaderSyncActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == NO_REMOTE_PROGRESS) {
|
if (state == NO_REMOTE_PROGRESS) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 280, "No remote progress found", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 265, "No remote progress found", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 320, "Upload current position?");
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 305, "Upload current position?");
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("Cancel", "Upload", "", "");
|
const auto labels = mappedInput.mapLabels("Cancel", "Upload", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
@ -355,7 +355,7 @@ void KOReaderSyncActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == UPLOAD_COMPLETE) {
|
if (state == UPLOAD_COMPLETE) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Progress uploaded!", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 285, "Progress uploaded!", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
@ -364,8 +364,8 @@ void KOReaderSyncActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == SYNC_FAILED) {
|
if (state == SYNC_FAILED) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 280, "Sync failed", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 265, "Sync failed", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 320, statusMessage.c_str());
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 305, statusMessage.c_str());
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|||||||
@ -35,24 +35,6 @@ void TxtReaderActivity::onEnter() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure screen orientation based on settings
|
|
||||||
switch (SETTINGS.orientation) {
|
|
||||||
case CrossPointSettings::ORIENTATION::PORTRAIT:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
|
||||||
break;
|
|
||||||
case CrossPointSettings::ORIENTATION::LANDSCAPE_CW:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise);
|
|
||||||
break;
|
|
||||||
case CrossPointSettings::ORIENTATION::INVERTED:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::PortraitInverted);
|
|
||||||
break;
|
|
||||||
case CrossPointSettings::ORIENTATION::LANDSCAPE_CCW:
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::LandscapeCounterClockwise);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderingMutex = xSemaphoreCreateMutex();
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
txt->setupCacheDir();
|
txt->setupCacheDir();
|
||||||
@ -76,9 +58,6 @@ void TxtReaderActivity::onEnter() {
|
|||||||
void TxtReaderActivity::onExit() {
|
void TxtReaderActivity::onExit() {
|
||||||
ActivityWithSubactivity::onExit();
|
ActivityWithSubactivity::onExit();
|
||||||
|
|
||||||
// Reset orientation back to portrait for the rest of the UI
|
|
||||||
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
|
||||||
|
|
||||||
// Wait until not rendering to delete task
|
// Wait until not rendering to delete task
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
if (displayTaskHandle) {
|
if (displayTaskHandle) {
|
||||||
|
|||||||
@ -30,6 +30,7 @@ void XtcReaderActivity::taskTrampoline(void* param) {
|
|||||||
|
|
||||||
void XtcReaderActivity::onEnter() {
|
void XtcReaderActivity::onEnter() {
|
||||||
ActivityWithSubactivity::onEnter();
|
ActivityWithSubactivity::onEnter();
|
||||||
|
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||||
|
|
||||||
if (!xtc) {
|
if (!xtc) {
|
||||||
return;
|
return;
|
||||||
@ -60,6 +61,7 @@ void XtcReaderActivity::onEnter() {
|
|||||||
|
|
||||||
void XtcReaderActivity::onExit() {
|
void XtcReaderActivity::onExit() {
|
||||||
ActivityWithSubactivity::onExit();
|
ActivityWithSubactivity::onExit();
|
||||||
|
updateRendererOrientation();
|
||||||
|
|
||||||
// Wait until not rendering to delete task
|
// Wait until not rendering to delete task
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
@ -84,6 +86,7 @@ void XtcReaderActivity::loop() {
|
|||||||
if (xtc && xtc->hasChapters() && !xtc->getChapters().empty()) {
|
if (xtc && xtc->hasChapters() && !xtc->getChapters().empty()) {
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
exitActivity();
|
exitActivity();
|
||||||
|
updateRendererOrientation();
|
||||||
enterNewActivity(new XtcReaderChapterSelectionActivity(
|
enterNewActivity(new XtcReaderChapterSelectionActivity(
|
||||||
this->renderer, this->mappedInput, xtc, currentPage,
|
this->renderer, this->mappedInput, xtc, currentPage,
|
||||||
[this] {
|
[this] {
|
||||||
@ -93,6 +96,7 @@ void XtcReaderActivity::loop() {
|
|||||||
[this](const uint32_t newPage) {
|
[this](const uint32_t newPage) {
|
||||||
currentPage = newPage;
|
currentPage = newPage;
|
||||||
exitActivity();
|
exitActivity();
|
||||||
|
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}));
|
}));
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
|
|||||||
@ -9,21 +9,6 @@ namespace {
|
|||||||
constexpr int SKIP_PAGE_MS = 700;
|
constexpr int SKIP_PAGE_MS = 700;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int XtcReaderChapterSelectionActivity::getPageItems() const {
|
|
||||||
constexpr int startY = 60;
|
|
||||||
constexpr int lineHeight = 30;
|
|
||||||
|
|
||||||
const int screenHeight = renderer.getScreenHeight();
|
|
||||||
const int endY = screenHeight - lineHeight;
|
|
||||||
|
|
||||||
const int availableHeight = endY - startY;
|
|
||||||
int items = availableHeight / lineHeight;
|
|
||||||
if (items < 1) {
|
|
||||||
items = 1;
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
int XtcReaderChapterSelectionActivity::findChapterIndexForPage(uint32_t page) const {
|
int XtcReaderChapterSelectionActivity::findChapterIndexForPage(uint32_t page) const {
|
||||||
if (!xtc) {
|
if (!xtc) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -132,28 +117,26 @@ void XtcReaderChapterSelectionActivity::renderScreen() {
|
|||||||
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const int pageItems = getPageItems();
|
const int pageItems = getPageItems();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Select Chapter", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "Select Chapter", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
const auto& chapters = xtc->getChapters();
|
const auto& chapters = xtc->getChapters();
|
||||||
if (chapters.empty()) {
|
if (chapters.empty()) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 120, "No chapters");
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 105, "No chapters");
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
||||||
renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, contentStartY + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - 1, LINE_HEIGHT);
|
||||||
for (int i = pageStartIndex; i < static_cast<int>(chapters.size()) && i < pageStartIndex + pageItems; i++) {
|
for (int i = pageStartIndex; i < static_cast<int>(chapters.size()) && i < pageStartIndex + pageItems; i++) {
|
||||||
const auto& chapter = chapters[i];
|
const auto& chapter = chapters[i];
|
||||||
const char* title = chapter.name.empty() ? "Unnamed" : chapter.name.c_str();
|
const char* title = chapter.name.empty() ? "Unnamed" : chapter.name.c_str();
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % pageItems) * 30, title, i != selectorIndex);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, contentStartY + (i % pageItems) * LINE_HEIGHT, title,
|
||||||
|
i != selectorIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip button hints in landscape CW mode (they overlap content)
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
|
||||||
if (renderer.getOrientation() != GfxRenderer::LandscapeClockwise) {
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
|
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,6 @@ class XtcReaderChapterSelectionActivity final : public Activity {
|
|||||||
const std::function<void()> onGoBack;
|
const std::function<void()> onGoBack;
|
||||||
const std::function<void(uint32_t newPage)> onSelectPage;
|
const std::function<void(uint32_t newPage)> onSelectPage;
|
||||||
|
|
||||||
int getPageItems() const;
|
|
||||||
int findChapterIndexForPage(uint32_t page) const;
|
int findChapterIndexForPage(uint32_t page) const;
|
||||||
|
|
||||||
static void taskTrampoline(void* param);
|
static void taskTrampoline(void* param);
|
||||||
|
|||||||
@ -80,7 +80,7 @@ void CalibreSettingsActivity::handleSelection() {
|
|||||||
// OPDS Server URL
|
// OPDS Server URL
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new KeyboardEntryActivity(
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
renderer, mappedInput, "OPDS Server URL", SETTINGS.opdsServerUrl, 10,
|
renderer, mappedInput, "OPDS Server URL", SETTINGS.opdsServerUrl, 50,
|
||||||
127, // maxLength
|
127, // maxLength
|
||||||
false, // not password
|
false, // not password
|
||||||
[this](const std::string& url) {
|
[this](const std::string& url) {
|
||||||
@ -98,7 +98,7 @@ void CalibreSettingsActivity::handleSelection() {
|
|||||||
// Username
|
// Username
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new KeyboardEntryActivity(
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
renderer, mappedInput, "Username", SETTINGS.opdsUsername, 10,
|
renderer, mappedInput, "Username", SETTINGS.opdsUsername, 50,
|
||||||
63, // maxLength
|
63, // maxLength
|
||||||
false, // not password
|
false, // not password
|
||||||
[this](const std::string& username) {
|
[this](const std::string& username) {
|
||||||
@ -116,7 +116,7 @@ void CalibreSettingsActivity::handleSelection() {
|
|||||||
// Password
|
// Password
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new KeyboardEntryActivity(
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
renderer, mappedInput, "Password", SETTINGS.opdsPassword, 10,
|
renderer, mappedInput, "Password", SETTINGS.opdsPassword, 50,
|
||||||
63, // maxLength
|
63, // maxLength
|
||||||
false, // not password mode
|
false, // not password mode
|
||||||
[this](const std::string& password) {
|
[this](const std::string& password) {
|
||||||
@ -153,20 +153,20 @@ void CalibreSettingsActivity::render() {
|
|||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "OPDS Browser", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "OPDS Browser", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Draw info text about Calibre
|
// Draw info text about Calibre
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 40, "For Calibre, add /opds to your URL");
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 25, "For Calibre, add /opds to your URL");
|
||||||
|
|
||||||
// Draw selection highlight
|
// Draw selection highlight
|
||||||
renderer.fillRect(0, 70 + selectedIndex * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, contentStartY + 10 + selectedIndex * LINE_HEIGHT - 2, pageWidth - 1, LINE_HEIGHT);
|
||||||
|
|
||||||
// Draw menu items
|
// Draw menu items
|
||||||
for (int i = 0; i < MENU_ITEMS; i++) {
|
for (int i = 0; i < MENU_ITEMS; i++) {
|
||||||
const int settingY = 70 + i * 30;
|
const int settingY = contentStartY + 10 + i * LINE_HEIGHT;
|
||||||
const bool isSelected = (i == selectedIndex);
|
const bool isSelected = (i == selectedIndex);
|
||||||
|
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, menuNames[i], !isSelected);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, settingY, menuNames[i], !isSelected);
|
||||||
|
|
||||||
// Draw status for each setting
|
// Draw status for each setting
|
||||||
const char* status = "[Not Set]";
|
const char* status = "[Not Set]";
|
||||||
@ -178,7 +178,7 @@ void CalibreSettingsActivity::render() {
|
|||||||
status = (strlen(SETTINGS.opdsPassword) > 0) ? "[Set]" : "[Not Set]";
|
status = (strlen(SETTINGS.opdsPassword) > 0) ? "[Set]" : "[Not Set]";
|
||||||
}
|
}
|
||||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, status, !isSelected);
|
renderer.drawText(UI_10_FONT_ID, pageWidth - marginRight - width, settingY, status, !isSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw button hints
|
// Draw button hints
|
||||||
|
|||||||
@ -87,6 +87,9 @@ void CategorySettingsActivity::toggleCurrentSetting() {
|
|||||||
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
||||||
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
|
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
|
||||||
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
||||||
|
if (strcmp(setting.name, "Orientation") == 0) {
|
||||||
|
updateRendererOrientation();
|
||||||
|
}
|
||||||
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
||||||
const int8_t currentValue = SETTINGS.*(setting.valuePtr);
|
const int8_t currentValue = SETTINGS.*(setting.valuePtr);
|
||||||
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
|
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
|
||||||
@ -151,20 +154,22 @@ void CategorySettingsActivity::render() const {
|
|||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const int pageItems = getPageItems();
|
||||||
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, categoryName, true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, categoryName, true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Draw selection highlight
|
// Draw selection highlight
|
||||||
renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, contentStartY + (selectedSettingIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - 1,
|
||||||
|
LINE_HEIGHT);
|
||||||
|
|
||||||
// Draw all settings
|
// Draw all settings
|
||||||
for (int i = 0; i < settingsCount; i++) {
|
const auto pageStartIndex = selectedSettingIndex / pageItems * pageItems;
|
||||||
const int settingY = 60 + i * 30; // 30 pixels between settings
|
for (int i = pageStartIndex; i < settingsCount && i < pageStartIndex + pageItems; i++) {
|
||||||
|
const int settingY = contentStartY + (i % pageItems) * LINE_HEIGHT;
|
||||||
const bool isSelected = (i == selectedSettingIndex);
|
const bool isSelected = (i == selectedSettingIndex);
|
||||||
|
|
||||||
// Draw setting name
|
// Draw setting name
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, !isSelected);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, settingY, settingsList[i].name, !isSelected);
|
||||||
|
|
||||||
// Draw value based on setting type
|
// Draw value based on setting type
|
||||||
std::string valueText;
|
std::string valueText;
|
||||||
@ -179,12 +184,12 @@ void CategorySettingsActivity::render() const {
|
|||||||
}
|
}
|
||||||
if (!valueText.empty()) {
|
if (!valueText.empty()) {
|
||||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str());
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str());
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, valueText.c_str(), !isSelected);
|
renderer.drawText(UI_10_FONT_ID, pageWidth - marginRight - width, settingY, valueText.c_str(), !isSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
|
renderer.drawText(SMALL_FONT_ID, pageWidth - marginRight - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
|
||||||
pageHeight - 60, CROSSPOINT_VERSION);
|
marginTop + 5, CROSSPOINT_VERSION);
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("« Back", "Toggle", "", "");
|
const auto labels = mappedInput.mapLabels("« Back", "Toggle", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|||||||
@ -56,7 +56,7 @@ void ClearCacheActivity::render() {
|
|||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Clear Cache", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "Clear Cache", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (state == WARNING) {
|
if (state == WARNING) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 60, "This will clear all cached book data.", true);
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 60, "This will clear all cached book data.", true);
|
||||||
|
|||||||
@ -123,17 +123,17 @@ void KOReaderAuthActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "KOReader Auth", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "KOReader Auth", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (state == AUTHENTICATING) {
|
if (state == AUTHENTICATING) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, statusMessage.c_str(), true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 285, statusMessage.c_str(), true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == SUCCESS) {
|
if (state == SUCCESS) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 280, "Success!", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 265, "Success!", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 320, "KOReader sync is ready to use");
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 305, "KOReader sync is ready to use");
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("Done", "", "", "");
|
const auto labels = mappedInput.mapLabels("Done", "", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
@ -142,8 +142,8 @@ void KOReaderAuthActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == FAILED) {
|
if (state == FAILED) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 280, "Authentication Failed", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 265, "Authentication Failed", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 320, errorMessage.c_str());
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 305, errorMessage.c_str());
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
const auto labels = mappedInput.mapLabels("Back", "", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|||||||
@ -81,7 +81,7 @@ void KOReaderSettingsActivity::handleSelection() {
|
|||||||
// Username
|
// Username
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new KeyboardEntryActivity(
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
renderer, mappedInput, "KOReader Username", KOREADER_STORE.getUsername(), 10,
|
renderer, mappedInput, "KOReader Username", KOREADER_STORE.getUsername(), 50,
|
||||||
64, // maxLength
|
64, // maxLength
|
||||||
false, // not password
|
false, // not password
|
||||||
[this](const std::string& username) {
|
[this](const std::string& username) {
|
||||||
@ -98,7 +98,7 @@ void KOReaderSettingsActivity::handleSelection() {
|
|||||||
// Password
|
// Password
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new KeyboardEntryActivity(
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
renderer, mappedInput, "KOReader Password", KOREADER_STORE.getPassword(), 10,
|
renderer, mappedInput, "KOReader Password", KOREADER_STORE.getPassword(), 50,
|
||||||
64, // maxLength
|
64, // maxLength
|
||||||
false, // show characters
|
false, // show characters
|
||||||
[this](const std::string& password) {
|
[this](const std::string& password) {
|
||||||
@ -117,7 +117,7 @@ void KOReaderSettingsActivity::handleSelection() {
|
|||||||
const std::string prefillUrl = currentUrl.empty() ? "https://" : currentUrl;
|
const std::string prefillUrl = currentUrl.empty() ? "https://" : currentUrl;
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new KeyboardEntryActivity(
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
renderer, mappedInput, "Sync Server URL", prefillUrl, 10,
|
renderer, mappedInput, "Sync Server URL", prefillUrl, 50,
|
||||||
128, // maxLength - URLs can be long
|
128, // maxLength - URLs can be long
|
||||||
false, // not password
|
false, // not password
|
||||||
[this](const std::string& url) {
|
[this](const std::string& url) {
|
||||||
@ -175,17 +175,17 @@ void KOReaderSettingsActivity::render() {
|
|||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "KOReader Sync", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "KOReader Sync", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Draw selection highlight
|
// Draw selection highlight
|
||||||
renderer.fillRect(0, 60 + selectedIndex * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, contentStartY + selectedIndex * LINE_HEIGHT - 2, pageWidth - 1, LINE_HEIGHT);
|
||||||
|
|
||||||
// Draw menu items
|
// Draw menu items
|
||||||
for (int i = 0; i < MENU_ITEMS; i++) {
|
for (int i = 0; i < MENU_ITEMS; i++) {
|
||||||
const int settingY = 60 + i * 30;
|
const int settingY = contentStartY + i * LINE_HEIGHT;
|
||||||
const bool isSelected = (i == selectedIndex);
|
const bool isSelected = (i == selectedIndex);
|
||||||
|
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, menuNames[i], !isSelected);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, settingY, menuNames[i], !isSelected);
|
||||||
|
|
||||||
// Draw status for each item
|
// Draw status for each item
|
||||||
const char* status = "";
|
const char* status = "";
|
||||||
@ -202,7 +202,7 @@ void KOReaderSettingsActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, status, !isSelected);
|
renderer.drawText(UI_10_FONT_ID, pageWidth - marginRight - width, settingY, status, !isSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw button hints
|
// Draw button hints
|
||||||
|
|||||||
@ -128,18 +128,19 @@ void OtaUpdateActivity::render() {
|
|||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
|
||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Update", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "Update", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
if (state == CHECKING_FOR_UPDATE) {
|
if (state == CHECKING_FOR_UPDATE) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 285, "Checking for update...", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == WAITING_CONFIRMATION) {
|
if (state == WAITING_CONFIRMATION) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 200, "New update available!", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 185, "New update available!", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 250, "Current Version: " CROSSPOINT_VERSION);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 235, "Current Version: " CROSSPOINT_VERSION);
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, 270, ("New Version: " + updater.getLatestVersion()).c_str());
|
renderer.drawText(UI_10_FONT_ID, marginLeft, marginTop + 255,
|
||||||
|
("New Version: " + updater.getLatestVersion()).c_str());
|
||||||
|
|
||||||
const auto labels = mappedInput.mapLabels("Cancel", "Update", "", "");
|
const auto labels = mappedInput.mapLabels("Cancel", "Update", "", "");
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
@ -148,33 +149,34 @@ void OtaUpdateActivity::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == UPDATE_IN_PROGRESS) {
|
if (state == UPDATE_IN_PROGRESS) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 310, "Updating...", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 295, "Updating...", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawRect(20, 350, pageWidth - 40, 50);
|
renderer.drawRect(marginLeft, marginTop + 335, pageWidth - 40, 50);
|
||||||
renderer.fillRect(24, 354, static_cast<int>(updaterProgress * static_cast<float>(pageWidth - 44)), 42);
|
renderer.fillRect(marginLeft + 4, marginTop + 339,
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 420,
|
static_cast<int>(updaterProgress * static_cast<float>(pageWidth - 44)), 42);
|
||||||
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 405,
|
||||||
(std::to_string(static_cast<int>(updaterProgress * 100)) + "%").c_str());
|
(std::to_string(static_cast<int>(updaterProgress * 100)) + "%").c_str());
|
||||||
renderer.drawCenteredText(
|
renderer.drawCenteredText(
|
||||||
UI_10_FONT_ID, 440,
|
UI_10_FONT_ID, marginTop + 425,
|
||||||
(std::to_string(updater.getProcessedSize()) + " / " + std::to_string(updater.getTotalSize())).c_str());
|
(std::to_string(updater.getProcessedSize()) + " / " + std::to_string(updater.getTotalSize())).c_str());
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == NO_UPDATE) {
|
if (state == NO_UPDATE) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "No update available", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 285, "No update available", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == FAILED) {
|
if (state == FAILED) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update failed", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 285, "Update failed", true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == FINISHED) {
|
if (state == FINISHED) {
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update complete", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 285, "Update complete", true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, 350, "Press and hold power button to turn back on");
|
renderer.drawCenteredText(UI_10_FONT_ID, marginTop + 335, "Press and hold power button to turn back on");
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
state = SHUTTING_DOWN;
|
state = SHUTTING_DOWN;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -11,9 +11,11 @@
|
|||||||
const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"};
|
const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int displaySettingsCount = 6;
|
constexpr int displaySettingsCount = 7;
|
||||||
const SettingInfo displaySettings[displaySettingsCount] = {
|
const SettingInfo displaySettings[displaySettingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
|
SettingInfo::Enum("Orientation", &CrossPointSettings::orientation,
|
||||||
|
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}),
|
||||||
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
||||||
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
|
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
|
||||||
SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
|
SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
|
||||||
@ -24,7 +26,7 @@ const SettingInfo displaySettings[displaySettingsCount] = {
|
|||||||
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
||||||
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})};
|
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})};
|
||||||
|
|
||||||
constexpr int readerSettingsCount = 9;
|
constexpr int readerSettingsCount = 8;
|
||||||
const SettingInfo readerSettings[readerSettingsCount] = {
|
const SettingInfo readerSettings[readerSettingsCount] = {
|
||||||
SettingInfo::Enum("Font Family", &CrossPointSettings::fontFamily, {"Bookerly", "Noto Sans", "Open Dyslexic"}),
|
SettingInfo::Enum("Font Family", &CrossPointSettings::fontFamily, {"Bookerly", "Noto Sans", "Open Dyslexic"}),
|
||||||
SettingInfo::Enum("Font Size", &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}),
|
SettingInfo::Enum("Font Size", &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}),
|
||||||
@ -33,8 +35,6 @@ const SettingInfo readerSettings[readerSettingsCount] = {
|
|||||||
SettingInfo::Enum("Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
|
SettingInfo::Enum("Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
|
||||||
{"Justify", "Left", "Center", "Right"}),
|
{"Justify", "Left", "Center", "Right"}),
|
||||||
SettingInfo::Toggle("Hyphenation", &CrossPointSettings::hyphenationEnabled),
|
SettingInfo::Toggle("Hyphenation", &CrossPointSettings::hyphenationEnabled),
|
||||||
SettingInfo::Enum("Reading Orientation", &CrossPointSettings::orientation,
|
|
||||||
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}),
|
|
||||||
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
||||||
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing)};
|
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing)};
|
||||||
|
|
||||||
@ -157,6 +157,7 @@ void SettingsActivity::enterCategory(int categoryIndex) {
|
|||||||
enterNewActivity(new CategorySettingsActivity(renderer, mappedInput, categoryNames[categoryIndex], settingsList,
|
enterNewActivity(new CategorySettingsActivity(renderer, mappedInput, categoryNames[categoryIndex], settingsList,
|
||||||
settingsCount, [this] {
|
settingsCount, [this] {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
|
updateMargins(); // we may have changed orientation
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}));
|
}));
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
@ -178,25 +179,24 @@ void SettingsActivity::render() const {
|
|||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
|
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
|
||||||
|
|
||||||
// Draw header
|
// Draw header
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, marginTop, "Settings", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Draw selection
|
// Draw selection
|
||||||
renderer.fillRect(0, 60 + selectedCategoryIndex * 30 - 2, pageWidth - 1, 30);
|
renderer.fillRect(0, contentStartY + selectedCategoryIndex * LINE_HEIGHT - 2, pageWidth - 1, LINE_HEIGHT);
|
||||||
|
|
||||||
// Draw all categories
|
// Draw all categories
|
||||||
for (int i = 0; i < categoryCount; i++) {
|
for (int i = 0; i < categoryCount; i++) {
|
||||||
const int categoryY = 60 + i * 30; // 30 pixels between categories
|
const int categoryY = contentStartY + i * LINE_HEIGHT;
|
||||||
|
|
||||||
// Draw category name
|
// Draw category name
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, categoryY, categoryNames[i], i != selectedCategoryIndex);
|
renderer.drawText(UI_10_FONT_ID, marginLeft, categoryY, categoryNames[i], i != selectedCategoryIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw version text above button hints
|
// Draw version text in corner of header
|
||||||
renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
|
renderer.drawText(SMALL_FONT_ID, pageWidth - marginRight - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
|
||||||
pageHeight - 60, CROSSPOINT_VERSION);
|
marginTop + 5, CROSSPOINT_VERSION);
|
||||||
|
|
||||||
// Draw help text
|
// Draw help text
|
||||||
const auto labels = mappedInput.mapLabels("« Back", "Select", "", "");
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "", "");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user