From 80c9e7a1d6545cacd9c218ed8587400686c34e09 Mon Sep 17 00:00:00 2001 From: cottongin Date: Tue, 27 Jan 2026 21:40:52 -0500 Subject: [PATCH] adds bezel compensation settings --- CrossPoint-ef.md | 1 + lib/GfxRenderer/GfxRenderer.cpp | 130 +++++++++++++++--- lib/GfxRenderer/GfxRenderer.h | 30 +++- src/CrossPointSettings.cpp | 12 +- src/CrossPointSettings.h | 9 ++ src/ScreenComponents.cpp | 1 + src/activities/boot_sleep/BootActivity.cpp | 13 +- src/activities/boot_sleep/SleepActivity.cpp | 11 +- .../browser/OpdsBookBrowserActivity.cpp | 35 +++-- src/activities/home/HomeActivity.cpp | 17 ++- src/activities/home/ListViewActivity.cpp | 28 ++-- src/activities/home/MyLibraryActivity.cpp | 68 +++++++-- .../network/CalibreConnectActivity.cpp | 10 +- .../network/CrossPointWebServerActivity.cpp | 5 +- .../network/NetworkModeSelectionActivity.cpp | 18 ++- .../network/WifiSelectionActivity.cpp | 32 +++-- .../EpubReaderChapterSelectionActivity.cpp | 21 +-- .../settings/CalibreSettingsActivity.cpp | 17 ++- .../settings/CategorySettingsActivity.cpp | 30 +++- .../settings/ClearCacheActivity.cpp | 25 ++-- src/activities/settings/OtaUpdateActivity.cpp | 36 +++-- src/activities/settings/SettingsActivity.cpp | 28 ++-- .../util/FullScreenMessageActivity.cpp | 4 +- src/main.cpp | 2 + 24 files changed, 428 insertions(+), 155 deletions(-) diff --git a/CrossPoint-ef.md b/CrossPoint-ef.md index b155a53..864871e 100644 --- a/CrossPoint-ef.md +++ b/CrossPoint-ef.md @@ -2,6 +2,7 @@ 1) Ability to clear all books and clear individual books from Recents. 2) Bookmarks +2a) crosspoint logo on firmware flashing screen 3) ability to add/remove books from lists on device. 4) quick menu 5) hide "system folders" from files view diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 2ce2162..9683ba8 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -621,15 +621,20 @@ void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char const int pageHeight = getScreenHeight(); constexpr int buttonWidth = 106; constexpr int buttonHeight = 40; - constexpr int buttonY = 40; // Distance from bottom - constexpr int textYOffset = 7; // Distance from top of button to text baseline - constexpr int buttonPositions[] = {25, 130, 245, 350}; + constexpr int baseButtonY = 40; // Base distance from bottom + constexpr int textYOffset = 7; // Distance from top of button to text baseline + constexpr int baseButtonPositions[] = {25, 130, 245, 350}; const char* labels[] = {btn1, btn2, btn3, btn4}; + // Apply bezel compensation (in portrait mode, bottom bezel affects Y position) + const int bezelBottom = getBezelOffsetBottom(); + const int bezelLeft = getBezelOffsetLeft(); + const int buttonY = baseButtonY + bezelBottom; + for (int i = 0; i < 4; i++) { // Only draw if the label is non-empty if (labels[i] != nullptr && labels[i][0] != '\0') { - const int x = buttonPositions[i]; + const int x = baseButtonPositions[i] + bezelLeft; fillRect(x, pageHeight - buttonY, buttonWidth, buttonHeight, false); drawRect(x, pageHeight - buttonY, buttonWidth, buttonHeight); const int textWidth = getTextWidth(fontId, labels[i]); @@ -648,9 +653,14 @@ void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, cons const int screenWidth = getScreenWidth(); constexpr int buttonWidth = 40; // Width on screen (height when rotated) constexpr int buttonHeight = 80; // Height on screen (width when rotated) - constexpr int buttonX = 5; // Distance from right edge + constexpr int baseButtonX = 5; // Base distance from right edge // Position for the button group - buttons share a border so they're adjacent - constexpr int topButtonY = 345; // Top button position + constexpr int baseTopButtonY = 345; // Base top button position + + // Apply bezel compensation (in portrait mode) + const int bezelRight = getBezelOffsetRight(); + const int buttonX = baseButtonX + bezelRight; + const int topButtonY = baseTopButtonY; // Y position doesn't need adjustment for side buttons const char* labels[] = {topBtn, bottomBtn}; @@ -979,31 +989,107 @@ void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, *x += glyph->advanceX; } +// Helper to map physical bezel edge to logical edge based on orientation +// bezelEdge: 0=physical bottom, 1=physical top, 2=physical left, 3=physical right (in portrait) +// Returns: 0=logical bottom, 1=logical top, 2=logical left, 3=logical right +int mapPhysicalToLogicalEdge(int bezelEdge, GfxRenderer::Orientation orientation) { + switch (orientation) { + case GfxRenderer::Portrait: + return bezelEdge; + case GfxRenderer::LandscapeClockwise: + switch (bezelEdge) { + case 0: return 2; // Physical bottom -> logical left + case 1: return 3; // Physical top -> logical right + case 2: return 1; // Physical left -> logical top + case 3: return 0; // Physical right -> logical bottom + } + break; + case GfxRenderer::PortraitInverted: + switch (bezelEdge) { + case 0: return 1; // Physical bottom -> logical top + case 1: return 0; // Physical top -> logical bottom + case 2: return 3; // Physical left -> logical right + case 3: return 2; // Physical right -> logical left + } + break; + case GfxRenderer::LandscapeCounterClockwise: + switch (bezelEdge) { + case 0: return 3; // Physical bottom -> logical right + case 1: return 2; // Physical top -> logical left + case 2: return 0; // Physical left -> logical bottom + case 3: return 1; // Physical right -> logical top + } + break; + } + return bezelEdge; +} + +int GfxRenderer::getViewableMarginTop() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return BASE_VIEWABLE_MARGIN_TOP + (logicalEdge == 1 ? bezelCompensation : 0); +} + +int GfxRenderer::getViewableMarginRight() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return BASE_VIEWABLE_MARGIN_RIGHT + (logicalEdge == 3 ? bezelCompensation : 0); +} + +int GfxRenderer::getViewableMarginBottom() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return BASE_VIEWABLE_MARGIN_BOTTOM + (logicalEdge == 0 ? bezelCompensation : 0); +} + +int GfxRenderer::getViewableMarginLeft() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return BASE_VIEWABLE_MARGIN_LEFT + (logicalEdge == 2 ? bezelCompensation : 0); +} + +int GfxRenderer::getBezelOffsetTop() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return (logicalEdge == 1) ? bezelCompensation : 0; +} + +int GfxRenderer::getBezelOffsetRight() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return (logicalEdge == 3) ? bezelCompensation : 0; +} + +int GfxRenderer::getBezelOffsetBottom() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return (logicalEdge == 0) ? bezelCompensation : 0; +} + +int GfxRenderer::getBezelOffsetLeft() const { + int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation); + return (logicalEdge == 2) ? bezelCompensation : 0; +} + void GfxRenderer::getOrientedViewableTRBL(int* outTop, int* outRight, int* outBottom, int* outLeft) const { + // Get base margins rotated for current orientation, with bezel compensation applied switch (orientation) { case Portrait: - *outTop = VIEWABLE_MARGIN_TOP; - *outRight = VIEWABLE_MARGIN_RIGHT; - *outBottom = VIEWABLE_MARGIN_BOTTOM; - *outLeft = VIEWABLE_MARGIN_LEFT; + *outTop = getViewableMarginTop(); + *outRight = getViewableMarginRight(); + *outBottom = getViewableMarginBottom(); + *outLeft = getViewableMarginLeft(); break; case LandscapeClockwise: - *outTop = VIEWABLE_MARGIN_LEFT; - *outRight = VIEWABLE_MARGIN_TOP; - *outBottom = VIEWABLE_MARGIN_RIGHT; - *outLeft = VIEWABLE_MARGIN_BOTTOM; + *outTop = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0); + *outRight = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0); + *outBottom = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0); + *outLeft = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0); break; case PortraitInverted: - *outTop = VIEWABLE_MARGIN_BOTTOM; - *outRight = VIEWABLE_MARGIN_LEFT; - *outBottom = VIEWABLE_MARGIN_TOP; - *outLeft = VIEWABLE_MARGIN_RIGHT; + *outTop = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0); + *outRight = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0); + *outBottom = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0); + *outLeft = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0); break; case LandscapeCounterClockwise: - *outTop = VIEWABLE_MARGIN_RIGHT; - *outRight = VIEWABLE_MARGIN_BOTTOM; - *outBottom = VIEWABLE_MARGIN_LEFT; - *outLeft = VIEWABLE_MARGIN_TOP; + *outTop = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0); + *outRight = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0); + *outBottom = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0); + *outLeft = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0); break; } } diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index 6d3a579..4f37531 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -28,9 +28,17 @@ class GfxRenderer { static_assert(BW_BUFFER_CHUNK_SIZE * BW_BUFFER_NUM_CHUNKS == EInkDisplay::BUFFER_SIZE, "BW buffer chunking does not line up with display buffer size"); + // Base viewable margins (hardware-specific, before bezel compensation) + static constexpr int BASE_VIEWABLE_MARGIN_TOP = 9; + static constexpr int BASE_VIEWABLE_MARGIN_RIGHT = 3; + static constexpr int BASE_VIEWABLE_MARGIN_BOTTOM = 3; + static constexpr int BASE_VIEWABLE_MARGIN_LEFT = 3; + EInkDisplay& einkDisplay; RenderMode renderMode; Orientation orientation; + int bezelCompensation = 0; // Pixels to add for bezel defect compensation + int bezelEdge = 0; // Which physical edge (0=bottom, 1=top, 2=left, 3=right in portrait) uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr}; std::map fontMap; void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState, @@ -42,10 +50,24 @@ class GfxRenderer { explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW), orientation(Portrait) {} ~GfxRenderer() { freeBwBufferChunks(); } - static constexpr int VIEWABLE_MARGIN_TOP = 9; - static constexpr int VIEWABLE_MARGIN_RIGHT = 3; - static constexpr int VIEWABLE_MARGIN_BOTTOM = 3; - static constexpr int VIEWABLE_MARGIN_LEFT = 3; + // Viewable margins (includes bezel compensation applied to the configured edge) + int getViewableMarginTop() const; + int getViewableMarginRight() const; + int getViewableMarginBottom() const; + int getViewableMarginLeft() const; + + // Bezel compensation configuration + void setBezelCompensation(int amount, int edge) { + bezelCompensation = amount; + bezelEdge = edge; + } + + // Get bezel offset for each logical edge (0 if not the compensated edge) + // Use these to add bezel compensation to hardcoded margins in UI screens + int getBezelOffsetTop() const; + int getBezelOffsetRight() const; + int getBezelOffsetBottom() const; + int getBezelOffsetLeft() const; // Setup void insertFont(int fontId, EpdFontFamily font); diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index de1f03b..dd26c82 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -23,7 +23,7 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) { namespace { constexpr uint8_t SETTINGS_FILE_VERSION = 1; // Increment this when adding new persisted settings fields -constexpr uint8_t SETTINGS_COUNT = 27; // 26 + displayContrast +constexpr uint8_t SETTINGS_COUNT = 29; // 28 + bezelCompensationEdge constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin"; } // namespace @@ -68,6 +68,10 @@ bool CrossPointSettings::saveToFile() const { serialization::writePod(outputFile, sleepScreenCoverFilter); // System-wide display contrast serialization::writePod(outputFile, displayContrast); + // Bezel compensation for physical screen edge defects + serialization::writePod(outputFile, bezelCompensation); + // Which physical edge needs bezel compensation + serialization::writePod(outputFile, bezelCompensationEdge); // New fields added at end for backward compatibility outputFile.close(); @@ -172,6 +176,12 @@ bool CrossPointSettings::loadFromFile() { // System-wide display contrast (0 = normal, 1 = high) serialization::readPod(inputFile, displayContrast); if (++settingsRead >= fileSettingsCount) break; + // Bezel compensation for physical screen edge defects (0-10px) + serialization::readPod(inputFile, bezelCompensation); + if (++settingsRead >= fileSettingsCount) break; + // Which physical edge needs bezel compensation + readAndValidate(inputFile, bezelCompensationEdge, BEZEL_EDGE_COUNT); + if (++settingsRead >= fileSettingsCount) break; // New fields added at end for backward compatibility } while (false); diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index a26e9a8..21ece46 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -91,6 +91,10 @@ class CrossPointSettings { // Hide battery percentage enum HIDE_BATTERY_PERCENTAGE { HIDE_NEVER = 0, HIDE_READER = 1, HIDE_ALWAYS = 2, HIDE_BATTERY_PERCENTAGE_COUNT }; + // Bezel compensation edge (which physical edge has the defect) + // These refer to physical edges in portrait orientation + enum BEZEL_EDGE { BEZEL_BOTTOM = 0, BEZEL_TOP = 1, BEZEL_LEFT = 2, BEZEL_RIGHT = 3, BEZEL_EDGE_COUNT }; + // Sleep screen settings uint8_t sleepScreen = DARK; // Sleep screen cover mode settings @@ -135,6 +139,11 @@ class CrossPointSettings { uint8_t longPressChapterSkip = 1; // System-wide display contrast (0 = normal, 1 = high) uint8_t displayContrast = 0; + // Bezel compensation - extra margin for physical screen edge defects (0-10px) + // Applied to the physical edge specified by bezelCompensationEdge, rotates with orientation + uint8_t bezelCompensation = 0; + // Which physical edge needs compensation (in portrait orientation) + uint8_t bezelCompensationEdge = BEZEL_BOTTOM; // Pinned list name (empty = none pinned) char pinnedListName[64] = ""; diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index bfa0973..deb5140 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -7,6 +7,7 @@ #include #include "Battery.h" +#include "CrossPointSettings.h" #include "fontIds.h" void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, const int top, diff --git a/src/activities/boot_sleep/BootActivity.cpp b/src/activities/boot_sleep/BootActivity.cpp index b741c3e..dc76425 100644 --- a/src/activities/boot_sleep/BootActivity.cpp +++ b/src/activities/boot_sleep/BootActivity.cpp @@ -11,10 +11,15 @@ void BootActivity::onEnter() { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int centerY = (pageHeight - bezelTop - bezelBottom) / 2 + bezelTop; + renderer.clearScreen(); - renderer.drawImage(CrossLarge, (pageWidth - 128) / 2, (pageHeight - 128) / 2, 128, 128); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD); - renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "BOOTING"); - renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, CROSSPOINT_VERSION); + renderer.drawImage(CrossLarge, (pageWidth - 128) / 2, centerY - 64, 128, 128); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 70, "CrossPoint", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(SMALL_FONT_ID, centerY + 95, "BOOTING"); + renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30 - bezelBottom, CROSSPOINT_VERSION); renderer.displayBuffer(); } diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index b71b9bb..e69e801 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -139,10 +139,15 @@ void SleepActivity::renderDefaultSleepScreen() const { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int centerY = (pageHeight - bezelTop - bezelBottom) / 2 + bezelTop; + renderer.clearScreen(); - renderer.drawImage(CrossLarge, (pageWidth - 128) / 2, (pageHeight - 128) / 2, 128, 128); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD); - renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "SLEEPING"); + renderer.drawImage(CrossLarge, (pageWidth - 128) / 2, centerY - 64, 128, 128); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 70, "CrossPoint", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(SMALL_FONT_ID, centerY + 95, "SLEEPING"); // Make sleep screen dark unless light is selected in settings if (SETTINGS.sleepScreen != CrossPointSettings::SLEEP_SCREEN_MODE::LIGHT) { diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index 6598e49..1dfbc1c 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -172,10 +172,17 @@ void OpdsBookBrowserActivity::render() const { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); - renderer.drawCenteredText(UI_12_FONT_ID, 15, "OPDS Browser", true, EpdFontFamily::BOLD); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int centerY = (pageHeight - bezelTop - bezelBottom) / 2 + bezelTop; + + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "OPDS Browser", true, EpdFontFamily::BOLD); if (state == BrowserState::CHECK_WIFI) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, statusMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, centerY, statusMessage.c_str()); const auto labels = mappedInput.mapLabels("« Back", "", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); @@ -183,7 +190,7 @@ void OpdsBookBrowserActivity::render() const { } if (state == BrowserState::LOADING) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, statusMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, centerY, statusMessage.c_str()); const auto labels = mappedInput.mapLabels("« Back", "", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); @@ -191,8 +198,8 @@ void OpdsBookBrowserActivity::render() const { } if (state == BrowserState::ERROR) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, "Error:"); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, errorMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 20, "Error:"); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 10, errorMessage.c_str()); const auto labels = mappedInput.mapLabels("« Back", "Retry", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); @@ -200,13 +207,13 @@ void OpdsBookBrowserActivity::render() const { } if (state == BrowserState::DOWNLOADING) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 40, "Downloading..."); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 10, statusMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 40, "Downloading..."); + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 10, statusMessage.c_str()); if (downloadTotal > 0) { - const int barWidth = pageWidth - 100; + const int barWidth = pageWidth - 100 - bezelLeft - bezelRight; constexpr int barHeight = 20; - constexpr int barX = 50; - const int barY = pageHeight / 2 + 20; + const int barX = 50 + bezelLeft; + const int barY = centerY + 20; ScreenComponents::drawProgressBar(renderer, barX, barY, barWidth, barHeight, downloadProgress, downloadTotal); } renderer.displayBuffer(); @@ -223,13 +230,13 @@ void OpdsBookBrowserActivity::render() const { renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); if (entries.empty()) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "No entries found"); + renderer.drawCenteredText(UI_10_FONT_ID, centerY, "No entries found"); renderer.displayBuffer(); return; } const auto pageStartIndex = selectorIndex / PAGE_ITEMS * PAGE_ITEMS; - renderer.fillRect(0, 60 + (selectorIndex % PAGE_ITEMS) * 30 - 2, pageWidth - 1, 30); + renderer.fillRect(bezelLeft, 60 + bezelTop + (selectorIndex % PAGE_ITEMS) * 30 - 2, pageWidth - 1 - bezelLeft - bezelRight, 30); for (size_t i = pageStartIndex; i < entries.size() && i < static_cast(pageStartIndex + PAGE_ITEMS); i++) { const auto& entry = entries[i]; @@ -246,8 +253,8 @@ void OpdsBookBrowserActivity::render() const { } } - 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(), + auto item = renderer.truncatedText(UI_10_FONT_ID, displayText.c_str(), renderer.getScreenWidth() - 40 - bezelLeft - bezelRight); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, 60 + bezelTop + (i % PAGE_ITEMS) * 30, item.c_str(), i != static_cast(selectorIndex)); } diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 9abf31f..bf00dcf 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -343,9 +343,12 @@ void HomeActivity::render() { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); - constexpr int margin = 20; - constexpr int bottomMargin = 60; - constexpr int bookY = 30; + // Base margins plus bezel compensation + const int margin = 20 + renderer.getBezelOffsetLeft(); + const int rightMargin = 20 + renderer.getBezelOffsetRight(); + const int bottomMargin = 60 + renderer.getBezelOffsetBottom(); + const int topMargin = renderer.getBezelOffsetTop(); + const int bookY = 30 + topMargin; constexpr int elementSpacing = 15; // --- Calculate layout from bottom up --- @@ -366,7 +369,7 @@ void HomeActivity::render() { fullWidthItems.insert(fullWidthItems.begin(), "OPDS Browser"); } - const int menuTileWidth = pageWidth - 2 * margin; + const int menuTileWidth = pageWidth - margin - rightMargin; constexpr int menuTileHeight = 45; constexpr int menuSpacing = 8; const int halfTileWidth = (menuTileWidth - menuSpacing) / 2; // Account for spacing between halves @@ -375,10 +378,10 @@ void HomeActivity::render() { menuTileHeight + static_cast(fullWidthItems.size()) * (menuTileHeight + menuSpacing); // Anchor menu to bottom of screen - const int menuStartY = pageHeight - bottomMargin - totalMenuHeight - margin; + const int menuStartY = pageHeight - bottomMargin - totalMenuHeight; // Calculate book card dimensions - larger, filling available space - const int bookWidth = pageWidth - 2 * margin; + const int bookWidth = pageWidth - margin - rightMargin; // Card extends to just above menu const int bookCardBottomY = menuStartY - elementSpacing; const int bookHeight = bookCardBottomY - bookY; @@ -721,7 +724,7 @@ void HomeActivity::render() { for (size_t i = 0; i < fullWidthItems.size(); ++i) { // Index offset: base + 2 (for Lists and My Library) + i const int overallIndex = baseMenuIndex + 2 + static_cast(i); - constexpr int tileX = margin; + const int tileX = margin; const int tileY = firstRowY + menuTileHeight + menuSpacing + static_cast(i) * (menuTileHeight + menuSpacing); const bool selected = selectorIndex == overallIndex; diff --git a/src/activities/home/ListViewActivity.cpp b/src/activities/home/ListViewActivity.cpp index aa94e62..f5261c3 100644 --- a/src/activities/home/ListViewActivity.cpp +++ b/src/activities/home/ListViewActivity.cpp @@ -12,15 +12,15 @@ #include "util/StringUtils.h" namespace { -// Layout constants (matching MyLibraryActivity's Recent tab) -constexpr int HEADER_Y = 15; -constexpr int CONTENT_START_Y = 60; +// Base layout constants (bezel offsets added at render time) +constexpr int BASE_HEADER_Y = 15; +constexpr int BASE_CONTENT_START_Y = 60; constexpr int LINE_HEIGHT = 65; // Two-line items (title + author) -constexpr int LEFT_MARGIN = 20; -constexpr int RIGHT_MARGIN = 40; +constexpr int BASE_LEFT_MARGIN = 20; +constexpr int BASE_RIGHT_MARGIN = 40; constexpr int MICRO_THUMB_WIDTH = 45; constexpr int MICRO_THUMB_HEIGHT = 60; -constexpr int THUMB_RIGHT_MARGIN = 50; +constexpr int BASE_THUMB_RIGHT_MARGIN = 50; // Timing thresholds constexpr int SKIP_PAGE_MS = 700; @@ -41,7 +41,9 @@ std::string getMicroThumbPathForBook(const std::string& bookPath) { int ListViewActivity::getPageItems() const { const int screenHeight = renderer.getScreenHeight(); const int bottomBarHeight = 60; - const int availableHeight = screenHeight - CONTENT_START_Y - bottomBarHeight; + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int availableHeight = screenHeight - (BASE_CONTENT_START_Y + bezelTop) - bottomBarHeight - bezelBottom; int items = availableHeight / LINE_HEIGHT; if (items < 1) { items = 1; @@ -172,6 +174,16 @@ void ListViewActivity::render() const { const int pageItems = getPageItems(); const int bookCount = static_cast(bookList.books.size()); + // Calculate bezel-adjusted margins + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int HEADER_Y = BASE_HEADER_Y + bezelTop; + const int CONTENT_START_Y = BASE_CONTENT_START_Y + bezelTop; + const int LEFT_MARGIN = BASE_LEFT_MARGIN + bezelLeft; + const int RIGHT_MARGIN = BASE_RIGHT_MARGIN + bezelRight; + const int THUMB_RIGHT_MARGIN = BASE_THUMB_RIGHT_MARGIN + bezelRight; + // Draw header with list name auto truncatedTitle = renderer.truncatedText(UI_12_FONT_ID, listName.c_str(), pageWidth - LEFT_MARGIN - RIGHT_MARGIN); renderer.drawText(UI_12_FONT_ID, LEFT_MARGIN, HEADER_Y, truncatedTitle.c_str(), true, EpdFontFamily::BOLD); @@ -190,7 +202,7 @@ void ListViewActivity::render() const { const auto pageStartIndex = selectorIndex / pageItems * pageItems; // Draw selection highlight - renderer.fillRect(0, CONTENT_START_Y + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - RIGHT_MARGIN, + renderer.fillRect(bezelLeft, CONTENT_START_Y + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - RIGHT_MARGIN - bezelLeft, LINE_HEIGHT); // Calculate available text width diff --git a/src/activities/home/MyLibraryActivity.cpp b/src/activities/home/MyLibraryActivity.cpp index 83a5ab9..f4cf039 100644 --- a/src/activities/home/MyLibraryActivity.cpp +++ b/src/activities/home/MyLibraryActivity.cpp @@ -31,16 +31,16 @@ void MyLibraryActivity::clearThumbExistsCache() { } namespace { -// Layout constants -constexpr int TAB_BAR_Y = 15; -constexpr int CONTENT_START_Y = 60; +// Base layout constants (bezel offsets added at render time) +constexpr int BASE_TAB_BAR_Y = 15; +constexpr int BASE_CONTENT_START_Y = 60; constexpr int LINE_HEIGHT = 30; constexpr int RECENTS_LINE_HEIGHT = 65; // Increased for two-line items -constexpr int LEFT_MARGIN = 20; -constexpr int RIGHT_MARGIN = 40; // Extra space for scroll indicator +constexpr int BASE_LEFT_MARGIN = 20; +constexpr int BASE_RIGHT_MARGIN = 40; // Extra space for scroll indicator constexpr int MICRO_THUMB_WIDTH = 45; constexpr int MICRO_THUMB_HEIGHT = 60; -constexpr int THUMB_RIGHT_MARGIN = 50; // Space from right edge for thumbnail +constexpr int BASE_THUMB_RIGHT_MARGIN = 50; // Space from right edge for thumbnail // Helper function to get the micro-thumb path for a book based on its file path std::string getMicroThumbPathForBook(const std::string& bookPath) { @@ -74,7 +74,9 @@ void sortFileList(std::vector& strs) { 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; + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int availableHeight = screenHeight - (BASE_CONTENT_START_Y + bezelTop) - bottomBarHeight - bezelBottom; // Recent tab uses taller items (title + author), Lists and Files use single-line items const int lineHeight = (currentTab == Tab::Recent) ? RECENTS_LINE_HEIGHT : LINE_HEIGHT; int items = availableHeight / lineHeight; @@ -700,6 +702,12 @@ void MyLibraryActivity::render() const { return; } + // Calculate bezel-adjusted margins + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int TAB_BAR_Y = BASE_TAB_BAR_Y + bezelTop; + const int CONTENT_START_Y = BASE_CONTENT_START_Y + bezelTop; + // Normal state - draw library view // Draw tab bar std::vector tabs = {{"Recent", currentTab == Tab::Recent}, @@ -718,7 +726,7 @@ void MyLibraryActivity::render() const { // Draw scroll indicator const int screenHeight = renderer.getScreenHeight(); - const int contentHeight = screenHeight - CONTENT_START_Y - 60; // 60 for bottom bar + const int contentHeight = screenHeight - CONTENT_START_Y - 60 - bezelBottom; // 60 for bottom bar ScreenComponents::drawScrollIndicator(renderer, getCurrentPage(), getTotalPages(), CONTENT_START_Y, contentHeight); // Draw side button hints (up/down navigation on right side) @@ -737,6 +745,15 @@ void MyLibraryActivity::renderRecentTab() const { const int pageItems = getPageItems(); const int bookCount = static_cast(recentBooks.size()); + // Calculate bezel-adjusted margins + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int CONTENT_START_Y = BASE_CONTENT_START_Y + bezelTop; + const int LEFT_MARGIN = BASE_LEFT_MARGIN + bezelLeft; + const int RIGHT_MARGIN = BASE_RIGHT_MARGIN + bezelRight; + const int THUMB_RIGHT_MARGIN = BASE_THUMB_RIGHT_MARGIN + bezelRight; + if (bookCount == 0) { renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, CONTENT_START_Y, "No recent books"); return; @@ -745,8 +762,8 @@ void MyLibraryActivity::renderRecentTab() const { const auto pageStartIndex = selectorIndex / pageItems * pageItems; // Draw selection highlight - renderer.fillRect(0, CONTENT_START_Y + (selectorIndex % pageItems) * RECENTS_LINE_HEIGHT - 2, - pageWidth - RIGHT_MARGIN, RECENTS_LINE_HEIGHT); + renderer.fillRect(bezelLeft, CONTENT_START_Y + (selectorIndex % pageItems) * RECENTS_LINE_HEIGHT - 2, + pageWidth - RIGHT_MARGIN - bezelLeft, RECENTS_LINE_HEIGHT); // Calculate available text width (leaving space for thumbnail on the right) const int textMaxWidth = pageWidth - LEFT_MARGIN - RIGHT_MARGIN - MICRO_THUMB_WIDTH - 10; @@ -897,6 +914,14 @@ void MyLibraryActivity::renderListsTab() const { const int pageItems = getPageItems(); const int listCount = static_cast(lists.size()); + // Calculate bezel-adjusted margins + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int CONTENT_START_Y = BASE_CONTENT_START_Y + bezelTop; + const int LEFT_MARGIN = BASE_LEFT_MARGIN + bezelLeft; + const int RIGHT_MARGIN = BASE_RIGHT_MARGIN + bezelRight; + if (listCount == 0) { renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, CONTENT_START_Y, "No lists found"); renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, CONTENT_START_Y + LINE_HEIGHT, "Create lists in Companion App"); @@ -906,7 +931,7 @@ void MyLibraryActivity::renderListsTab() const { const auto pageStartIndex = selectorIndex / pageItems * pageItems; // Draw selection highlight - renderer.fillRect(0, CONTENT_START_Y + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - RIGHT_MARGIN, + renderer.fillRect(bezelLeft, CONTENT_START_Y + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - RIGHT_MARGIN - bezelLeft, LINE_HEIGHT); // Draw items @@ -927,6 +952,14 @@ void MyLibraryActivity::renderFilesTab() const { const int pageItems = getPageItems(); const int fileCount = static_cast(files.size()); + // Calculate bezel-adjusted margins + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int CONTENT_START_Y = BASE_CONTENT_START_Y + bezelTop; + const int LEFT_MARGIN = BASE_LEFT_MARGIN + bezelLeft; + const int RIGHT_MARGIN = BASE_RIGHT_MARGIN + bezelRight; + if (fileCount == 0) { renderer.drawText(UI_10_FONT_ID, LEFT_MARGIN, CONTENT_START_Y, "No books found"); return; @@ -935,7 +968,7 @@ void MyLibraryActivity::renderFilesTab() const { const auto pageStartIndex = selectorIndex / pageItems * pageItems; // Draw selection highlight - renderer.fillRect(0, CONTENT_START_Y + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - RIGHT_MARGIN, + renderer.fillRect(bezelLeft, CONTENT_START_Y + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - RIGHT_MARGIN - bezelLeft, LINE_HEIGHT); // Draw items @@ -950,12 +983,17 @@ void MyLibraryActivity::renderActionMenu() const { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + // Title - renderer.drawCenteredText(UI_12_FONT_ID, 20, "Book Actions", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 20 + bezelTop, "Book Actions", true, EpdFontFamily::BOLD); // Show filename - const int filenameY = 70; - auto truncatedName = renderer.truncatedText(UI_10_FONT_ID, actionTargetName.c_str(), pageWidth - 40); + const int filenameY = 70 + bezelTop; + auto truncatedName = renderer.truncatedText(UI_10_FONT_ID, actionTargetName.c_str(), pageWidth - 40 - bezelLeft - bezelRight); renderer.drawCenteredText(UI_10_FONT_ID, filenameY, truncatedName.c_str()); // Menu options - 4 for Recent tab, 2 for Files tab diff --git a/src/activities/network/CalibreConnectActivity.cpp b/src/activities/network/CalibreConnectActivity.cpp index 5ceb5be..e069385 100644 --- a/src/activities/network/CalibreConnectActivity.cpp +++ b/src/activities/network/CalibreConnectActivity.cpp @@ -211,10 +211,13 @@ void CalibreConnectActivity::render() const { renderer.clearScreen(); const auto pageHeight = renderer.getScreenHeight(); + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int centerY = (pageHeight - bezelTop - bezelBottom) / 2 + bezelTop; if (state == CalibreConnectState::SERVER_STARTING) { - renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, "Starting Calibre...", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, centerY - 20, "Starting Calibre...", true, EpdFontFamily::BOLD); } else if (state == CalibreConnectState::ERROR) { - renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, "Calibre setup failed", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, centerY - 20, "Calibre setup failed", true, EpdFontFamily::BOLD); } renderer.displayBuffer(); } @@ -224,7 +227,8 @@ void CalibreConnectActivity::renderServerRunning() const { constexpr int SMALL_SPACING = 20; constexpr int SECTION_SPACING = 40; constexpr int TOP_PADDING = 14; - renderer.drawCenteredText(UI_12_FONT_ID, 15, "Connect to Calibre", true, EpdFontFamily::BOLD); + const int bezelTop = renderer.getBezelOffsetTop(); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "Connect to Calibre", true, EpdFontFamily::BOLD); int y = 55 + TOP_PADDING; renderer.drawCenteredText(UI_10_FONT_ID, y, "Network", true, EpdFontFamily::BOLD); diff --git a/src/activities/network/CrossPointWebServerActivity.cpp b/src/activities/network/CrossPointWebServerActivity.cpp index 93a8534..1872ee9 100644 --- a/src/activities/network/CrossPointWebServerActivity.cpp +++ b/src/activities/network/CrossPointWebServerActivity.cpp @@ -460,7 +460,10 @@ void CrossPointWebServerActivity::render() const { } else if (state == WebServerActivityState::AP_STARTING) { renderer.clearScreen(); const auto pageHeight = renderer.getScreenHeight(); - renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, "Starting Hotspot...", true, EpdFontFamily::BOLD); + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int centerY = (pageHeight - bezelTop - bezelBottom) / 2 + bezelTop; + renderer.drawCenteredText(UI_12_FONT_ID, centerY - 20, "Starting Hotspot...", true, EpdFontFamily::BOLD); renderer.displayBuffer(); } } diff --git a/src/activities/network/NetworkModeSelectionActivity.cpp b/src/activities/network/NetworkModeSelectionActivity.cpp index 67d6480..f68d0af 100644 --- a/src/activities/network/NetworkModeSelectionActivity.cpp +++ b/src/activities/network/NetworkModeSelectionActivity.cpp @@ -105,15 +105,21 @@ void NetworkModeSelectionActivity::render() const { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + // Draw header - renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "File Transfer", true, EpdFontFamily::BOLD); // Draw subtitle - renderer.drawCenteredText(UI_10_FONT_ID, 50, "How would you like to connect?"); + renderer.drawCenteredText(UI_10_FONT_ID, 50 + bezelTop, "How would you like to connect?"); // Draw menu items centered on screen constexpr int itemHeight = 50; // Height for each menu item (including description) - const int startY = (pageHeight - (MENU_ITEM_COUNT * itemHeight)) / 2 + 10; + const int startY = (pageHeight - bezelTop - bezelBottom - (MENU_ITEM_COUNT * itemHeight)) / 2 + bezelTop + 10; for (int i = 0; i < MENU_ITEM_COUNT; i++) { const int itemY = startY + i * itemHeight; @@ -121,13 +127,13 @@ void NetworkModeSelectionActivity::render() const { // Draw selection highlight (black fill) for selected item if (isSelected) { - renderer.fillRect(20, itemY - 2, pageWidth - 40, itemHeight - 6); + renderer.fillRect(20 + bezelLeft, itemY - 2, pageWidth - 40 - bezelLeft - bezelRight, itemHeight - 6); } // Draw text: black=false (white text) when selected (on black 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(SMALL_FONT_ID, 30, itemY + 22, MENU_DESCRIPTIONS[i], /*black=*/!isSelected); + renderer.drawText(UI_10_FONT_ID, 30 + bezelLeft, itemY, MENU_ITEMS[i], /*black=*/!isSelected); + renderer.drawText(SMALL_FONT_ID, 30 + bezelLeft, itemY + 22, MENU_DESCRIPTIONS[i], /*black=*/!isSelected); } // Draw help text at bottom diff --git a/src/activities/network/WifiSelectionActivity.cpp b/src/activities/network/WifiSelectionActivity.cpp index c58e0b8..5d378e1 100644 --- a/src/activities/network/WifiSelectionActivity.cpp +++ b/src/activities/network/WifiSelectionActivity.cpp @@ -513,8 +513,14 @@ void WifiSelectionActivity::renderNetworkList() const { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + // Draw header - renderer.drawCenteredText(UI_12_FONT_ID, 15, "WiFi Networks", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "WiFi Networks", true, EpdFontFamily::BOLD); if (networks.empty()) { // No networks found or scan failed @@ -524,9 +530,9 @@ void WifiSelectionActivity::renderNetworkList() const { renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press OK to scan again"); } else { // Calculate how many networks we can display - constexpr int startY = 60; + const int startY = 60 + bezelTop; constexpr int lineHeight = 25; - const int maxVisibleNetworks = (pageHeight - startY - 40) / lineHeight; + const int maxVisibleNetworks = (pageHeight - startY - 40 - bezelBottom) / lineHeight; // Calculate scroll offset to keep selected item visible int scrollOffset = 0; @@ -542,7 +548,7 @@ void WifiSelectionActivity::renderNetworkList() const { // Draw selection indicator if (static_cast(i) == selectedNetworkIndex) { - renderer.drawText(UI_10_FONT_ID, 5, networkY, ">"); + renderer.drawText(UI_10_FONT_ID, 5 + bezelLeft, networkY, ">"); } // Draw network name (truncate if too long) @@ -550,42 +556,42 @@ void WifiSelectionActivity::renderNetworkList() const { if (displayName.length() > 16) { displayName.replace(13, displayName.length() - 13, "..."); } - renderer.drawText(UI_10_FONT_ID, 20, networkY, displayName.c_str()); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, networkY, displayName.c_str()); // Draw signal strength indicator 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 - 90 - bezelRight, networkY, signalStr.c_str()); // Draw saved indicator (checkmark) for networks with saved passwords if (network.hasSavedPassword) { - renderer.drawText(UI_10_FONT_ID, pageWidth - 50, networkY, "+"); + renderer.drawText(UI_10_FONT_ID, pageWidth - 50 - bezelRight, networkY, "+"); } // Draw lock icon for encrypted networks if (network.isEncrypted) { - renderer.drawText(UI_10_FONT_ID, pageWidth - 30, networkY, "*"); + renderer.drawText(UI_10_FONT_ID, pageWidth - 30 - bezelRight, networkY, "*"); } } // Draw scroll indicators if needed if (scrollOffset > 0) { - renderer.drawText(SMALL_FONT_ID, pageWidth - 15, startY - 10, "^"); + renderer.drawText(SMALL_FONT_ID, pageWidth - 15 - bezelRight, startY - 10, "^"); } if (scrollOffset + maxVisibleNetworks < static_cast(networks.size())) { - renderer.drawText(SMALL_FONT_ID, pageWidth - 15, startY + maxVisibleNetworks * lineHeight, "v"); + renderer.drawText(SMALL_FONT_ID, pageWidth - 15 - bezelRight, startY + maxVisibleNetworks * lineHeight, "v"); } // Show network count char countStr[32]; snprintf(countStr, sizeof(countStr), "%zu networks found", networks.size()); - renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 90, countStr); + renderer.drawText(SMALL_FONT_ID, 20 + bezelLeft, pageHeight - 90 - bezelBottom, countStr); } // 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, 20 + bezelLeft, pageHeight - 105 - bezelBottom, cachedMacAddress.c_str()); // Draw help text - renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved"); + renderer.drawText(SMALL_FONT_ID, 20 + bezelLeft, pageHeight - 75 - bezelBottom, "* = Encrypted | + = Saved"); const auto labels = mappedInput.mapLabels("« Back", "Connect", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); } diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp index a1b68d4..c8a17d5 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp @@ -166,28 +166,33 @@ void EpubReaderChapterSelectionActivity::renderScreen() { const int pageItems = getPageItems(); const int totalItems = getTotalItems(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const std::string title = - renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(), pageWidth - 40, EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_12_FONT_ID, 15, title.c_str(), true, EpdFontFamily::BOLD); + renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(), pageWidth - 40 - bezelLeft - bezelRight, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, title.c_str(), true, EpdFontFamily::BOLD); const auto pageStartIndex = selectorIndex / pageItems * pageItems; - renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30); + renderer.fillRect(bezelLeft, 60 + bezelTop + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1 - bezelLeft - bezelRight, 30); for (int itemIndex = pageStartIndex; itemIndex < totalItems && itemIndex < pageStartIndex + pageItems; itemIndex++) { - const int displayY = 60 + (itemIndex % pageItems) * 30; + const int displayY = 60 + bezelTop + (itemIndex % pageItems) * 30; const bool isSelected = (itemIndex == selectorIndex); if (isSyncItem(itemIndex)) { // Draw sync option (at top or bottom) - renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, displayY, ">> Sync Progress", !isSelected); } else { // Draw TOC item (account for top sync offset) const int tocIndex = tocIndexFromItemIndex(itemIndex); auto item = epub->getTocItem(tocIndex); - const int indentSize = 20 + (item.level - 1) * 15; + const int indentSize = 20 + bezelLeft + (item.level - 1) * 15; const std::string chapterName = - renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize); - renderer.drawText(UI_10_FONT_ID, indentSize, 60 + (tocIndex % pageItems) * 30, chapterName.c_str(), + renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - bezelLeft - bezelRight - indentSize + 20 + bezelLeft); + renderer.drawText(UI_10_FONT_ID, indentSize, 60 + bezelTop + (tocIndex % pageItems) * 30, chapterName.c_str(), tocIndex != selectorIndex); } } diff --git a/src/activities/settings/CalibreSettingsActivity.cpp b/src/activities/settings/CalibreSettingsActivity.cpp index c86ea81..ed8005b 100644 --- a/src/activities/settings/CalibreSettingsActivity.cpp +++ b/src/activities/settings/CalibreSettingsActivity.cpp @@ -153,21 +153,26 @@ void CalibreSettingsActivity::render() { const auto pageWidth = renderer.getScreenWidth(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + // Draw header - renderer.drawCenteredText(UI_12_FONT_ID, 15, "OPDS Browser", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "OPDS Browser", true, EpdFontFamily::BOLD); // 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, 40 + bezelTop, "For Calibre, add /opds to your URL"); // Draw selection highlight - renderer.fillRect(0, 70 + selectedIndex * 30 - 2, pageWidth - 1, 30); + renderer.fillRect(bezelLeft, 70 + bezelTop + selectedIndex * 30 - 2, pageWidth - 1 - bezelLeft - bezelRight, 30); // Draw menu items for (int i = 0; i < MENU_ITEMS; i++) { - const int settingY = 70 + i * 30; + const int settingY = 70 + bezelTop + i * 30; const bool isSelected = (i == selectedIndex); - renderer.drawText(UI_10_FONT_ID, 20, settingY, menuNames[i], !isSelected); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, settingY, menuNames[i], !isSelected); // Draw status for each setting const char* status = "[Not Set]"; @@ -179,7 +184,7 @@ void CalibreSettingsActivity::render() { status = (strlen(SETTINGS.opdsPassword) > 0) ? "[Set]" : "[Not Set]"; } 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 - 20 - bezelRight - width, settingY, status, !isSelected); } // Draw button hints diff --git a/src/activities/settings/CategorySettingsActivity.cpp b/src/activities/settings/CategorySettingsActivity.cpp index b95dbc2..4d059ef 100644 --- a/src/activities/settings/CategorySettingsActivity.cpp +++ b/src/activities/settings/CategorySettingsActivity.cpp @@ -124,6 +124,11 @@ void CategorySettingsActivity::toggleCurrentSetting() { } else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) { const uint8_t currentValue = SETTINGS.*(setting.valuePtr); SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast(setting.enumValues.size()); + + // Handle side effects for specific settings + if (setting.valuePtr == &CrossPointSettings::bezelCompensationEdge) { + renderer.setBezelCompensation(SETTINGS.bezelCompensation, SETTINGS.bezelCompensationEdge); + } } else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) { const int8_t currentValue = SETTINGS.*(setting.valuePtr); if (currentValue + setting.valueRange.step > setting.valueRange.max) { @@ -131,6 +136,11 @@ void CategorySettingsActivity::toggleCurrentSetting() { } else { SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step; } + + // Handle side effects for specific settings + if (setting.valuePtr == &CrossPointSettings::bezelCompensation) { + renderer.setBezelCompensation(SETTINGS.bezelCompensation, SETTINGS.bezelCompensationEdge); + } } else if (setting.type == SettingType::ACTION) { if (strcmp(setting.name, "Calibre Settings") == 0) { xSemaphoreTake(renderingMutex, portMAX_DELAY); @@ -182,10 +192,16 @@ void CategorySettingsActivity::render() const { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); - renderer.drawCenteredText(UI_12_FONT_ID, 15, categoryName, true, EpdFontFamily::BOLD); + // Bezel compensation offsets + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, categoryName, true, EpdFontFamily::BOLD); // Draw selection highlight - renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30); + renderer.fillRect(bezelLeft, 60 + bezelTop + selectedSettingIndex * 30 - 2, pageWidth - 1 - bezelLeft - bezelRight, 30); // Draw only visible settings int visibleIndex = 0; @@ -194,11 +210,11 @@ void CategorySettingsActivity::render() const { continue; // Skip hidden settings } - const int settingY = 60 + visibleIndex * 30; // 30 pixels between settings + const int settingY = 60 + bezelTop + visibleIndex * 30; // 30 pixels between settings const bool isSelected = (visibleIndex == selectedSettingIndex); // Draw setting name - renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, !isSelected); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, settingY, settingsList[i].name, !isSelected); // Draw value based on setting type std::string valueText; @@ -215,14 +231,14 @@ void CategorySettingsActivity::render() const { } if (!valueText.empty()) { 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 - 20 - bezelRight - width, settingY, valueText.c_str(), !isSelected); } visibleIndex++; } - renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION), - pageHeight - 60, CROSSPOINT_VERSION); + renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - bezelRight - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION), + pageHeight - 60 - bezelBottom, CROSSPOINT_VERSION); const auto labels = mappedInput.mapLabels("« Back", "Toggle", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); diff --git a/src/activities/settings/ClearCacheActivity.cpp b/src/activities/settings/ClearCacheActivity.cpp index 3d2abee..4aeff73 100644 --- a/src/activities/settings/ClearCacheActivity.cpp +++ b/src/activities/settings/ClearCacheActivity.cpp @@ -58,15 +58,20 @@ void ClearCacheActivity::displayTaskLoop() { void ClearCacheActivity::render() { const auto pageHeight = renderer.getScreenHeight(); + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int centerY = (pageHeight - bezelTop - bezelBottom) / 2 + bezelTop; + renderer.clearScreen(); - renderer.drawCenteredText(UI_12_FONT_ID, 15, "Clear Cache", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "Clear Cache", true, EpdFontFamily::BOLD); 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 - 30, "All reading progress will be lost!", true, + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 60, "This will clear all cached book data.", true); + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 30, "All reading progress will be lost!", true, EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, "Books will need to be re-indexed", true); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 30, "when opened again.", true); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 10, "Books will need to be re-indexed", true); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 30, "when opened again.", true); const auto labels = mappedInput.mapLabels("« Cancel", "Clear", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); @@ -75,18 +80,18 @@ void ClearCacheActivity::render() { } if (state == CLEARING) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "Clearing cache...", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, centerY, "Clearing cache...", true, EpdFontFamily::BOLD); renderer.displayBuffer(); return; } if (state == SUCCESS) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, "Cache Cleared", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 20, "Cache Cleared", true, EpdFontFamily::BOLD); String resultText = String(clearedCount) + " items removed"; if (failedCount > 0) { resultText += ", " + String(failedCount) + " failed"; } - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, resultText.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 10, resultText.c_str()); const auto labels = mappedInput.mapLabels("« Back", "", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); @@ -95,8 +100,8 @@ void ClearCacheActivity::render() { } if (state == FAILED) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, "Failed to clear cache", true, EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, "Check serial output for details"); + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 20, "Failed to clear cache", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 10, "Check serial output for details"); const auto labels = mappedInput.mapLabels("« Back", "", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); diff --git a/src/activities/settings/OtaUpdateActivity.cpp b/src/activities/settings/OtaUpdateActivity.cpp index cb1d57b..fee931b 100644 --- a/src/activities/settings/OtaUpdateActivity.cpp +++ b/src/activities/settings/OtaUpdateActivity.cpp @@ -126,20 +126,28 @@ void OtaUpdateActivity::render() { } const auto pageWidth = renderer.getScreenWidth(); + const auto pageHeight = renderer.getScreenHeight(); + + // Bezel compensation + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int centerY = (pageHeight - bezelTop - bezelBottom) / 2 + bezelTop; renderer.clearScreen(); - renderer.drawCenteredText(UI_12_FONT_ID, 15, "Update", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "Update", true, EpdFontFamily::BOLD); if (state == CHECKING_FOR_UPDATE) { - renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, centerY, "Checking for update...", true, EpdFontFamily::BOLD); renderer.displayBuffer(); return; } if (state == WAITING_CONFIRMATION) { - renderer.drawCenteredText(UI_10_FONT_ID, 200, "New update available!", true, EpdFontFamily::BOLD); - renderer.drawText(UI_10_FONT_ID, 20, 250, "Current Version: " CROSSPOINT_VERSION); - renderer.drawText(UI_10_FONT_ID, 20, 270, ("New Version: " + updater.getLatestVersion()).c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 100, "New update available!", true, EpdFontFamily::BOLD); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, centerY - 50, "Current Version: " CROSSPOINT_VERSION); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, centerY - 30, ("New Version: " + updater.getLatestVersion()).c_str()); const auto labels = mappedInput.mapLabels("Cancel", "Update", "", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); @@ -148,33 +156,33 @@ void OtaUpdateActivity::render() { } if (state == UPDATE_IN_PROGRESS) { - renderer.drawCenteredText(UI_10_FONT_ID, 310, "Updating...", true, EpdFontFamily::BOLD); - renderer.drawRect(20, 350, pageWidth - 40, 50); - renderer.fillRect(24, 354, static_cast(updaterProgress * static_cast(pageWidth - 44)), 42); - renderer.drawCenteredText(UI_10_FONT_ID, 420, + renderer.drawCenteredText(UI_10_FONT_ID, centerY - 40, "Updating...", true, EpdFontFamily::BOLD); + renderer.drawRect(20 + bezelLeft, centerY, pageWidth - 40 - bezelLeft - bezelRight, 50); + renderer.fillRect(24 + bezelLeft, centerY + 4, static_cast(updaterProgress * static_cast(pageWidth - 44 - bezelLeft - bezelRight)), 42); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 70, (std::to_string(static_cast(updaterProgress * 100)) + "%").c_str()); renderer.drawCenteredText( - UI_10_FONT_ID, 440, + UI_10_FONT_ID, centerY + 90, (std::to_string(updater.processedSize) + " / " + std::to_string(updater.totalSize)).c_str()); renderer.displayBuffer(); return; } if (state == NO_UPDATE) { - renderer.drawCenteredText(UI_10_FONT_ID, 300, "No update available", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, centerY, "No update available", true, EpdFontFamily::BOLD); renderer.displayBuffer(); return; } if (state == FAILED) { - renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update failed", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, centerY, "Update failed", true, EpdFontFamily::BOLD); renderer.displayBuffer(); return; } if (state == FINISHED) { - renderer.drawCenteredText(UI_10_FONT_ID, 300, "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, centerY, "Update complete", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, centerY + 50, "Press and hold power button to turn back on"); renderer.displayBuffer(); state = SHUTTING_DOWN; return; diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 3ae4d8f..e5d2817 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -12,7 +12,10 @@ const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"}; namespace { -constexpr int displaySettingsCount = 7; +// Visibility condition for bezel edge setting (only show when compensation > 0) +bool isBezelCompensationEnabled() { return SETTINGS.bezelCompensation > 0; } + +constexpr int displaySettingsCount = 9; const SettingInfo displaySettings[displaySettingsCount] = { // Should match with SLEEP_SCREEN_MODE SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}), @@ -24,7 +27,10 @@ const SettingInfo displaySettings[displaySettingsCount] = { {"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}), SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}), 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"}), + SettingInfo::Value("Bezel Compensation", &CrossPointSettings::bezelCompensation, {0, 10, 1}), + SettingInfo::Enum("Bezel Edge", &CrossPointSettings::bezelCompensationEdge, + {"Bottom", "Top", "Left", "Right"}, isBezelCompensationEnabled)}; // Helper to get custom font names as a vector std::vector getCustomFontNamesVector() { @@ -213,23 +219,29 @@ void SettingsActivity::render() const { const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); + // Bezel compensation offsets + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelLeft = renderer.getBezelOffsetLeft(); + const int bezelRight = renderer.getBezelOffsetRight(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + // Draw header - renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, "Settings", true, EpdFontFamily::BOLD); // Draw selection - renderer.fillRect(0, 60 + selectedCategoryIndex * 30 - 2, pageWidth - 1, 30); + renderer.fillRect(bezelLeft, 60 + bezelTop + selectedCategoryIndex * 30 - 2, pageWidth - 1 - bezelLeft - bezelRight, 30); // Draw all categories for (int i = 0; i < categoryCount; i++) { - const int categoryY = 60 + i * 30; // 30 pixels between categories + const int categoryY = 60 + bezelTop + i * 30; // 30 pixels between categories // Draw category name - renderer.drawText(UI_10_FONT_ID, 20, categoryY, categoryNames[i], i != selectedCategoryIndex); + renderer.drawText(UI_10_FONT_ID, 20 + bezelLeft, categoryY, categoryNames[i], i != selectedCategoryIndex); } // Draw version text above button hints - renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION), - pageHeight - 60, CROSSPOINT_VERSION); + renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - bezelRight - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION), + pageHeight - 60 - bezelBottom, CROSSPOINT_VERSION); // Draw help text const auto labels = mappedInput.mapLabels("« Back", "Select", "", ""); diff --git a/src/activities/util/FullScreenMessageActivity.cpp b/src/activities/util/FullScreenMessageActivity.cpp index 3b7428a..594329e 100644 --- a/src/activities/util/FullScreenMessageActivity.cpp +++ b/src/activities/util/FullScreenMessageActivity.cpp @@ -8,7 +8,9 @@ void FullScreenMessageActivity::onEnter() { Activity::onEnter(); const auto height = renderer.getLineHeight(UI_10_FONT_ID); - const auto top = (renderer.getScreenHeight() - height) / 2; + const int bezelTop = renderer.getBezelOffsetTop(); + const int bezelBottom = renderer.getBezelOffsetBottom(); + const auto top = (renderer.getScreenHeight() - bezelTop - bezelBottom - height) / 2 + bezelTop; renderer.clearScreen(); renderer.drawCenteredText(UI_10_FONT_ID, top, text.c_str(), true, style); diff --git a/src/main.cpp b/src/main.cpp index b16d0b7..47f12fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -475,6 +475,8 @@ void setup() { SETTINGS.loadFromFile(); // Apply high contrast mode from settings setHighContrastMode(SETTINGS.displayContrast == 1); + // Apply bezel compensation from settings + renderer.setBezelCompensation(SETTINGS.bezelCompensation, SETTINGS.bezelCompensationEdge); if (isWakeupByPowerButton()) { // For normal wakeups, verify power button press duration