From 2008060a817eef45df45fed7c7603501930fa796 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Tue, 10 Mar 2026 19:11:30 +0100 Subject: [PATCH] Refactor ui content rect --- .../browser/OpdsBookBrowserActivity.cpp | 35 ++++++------ src/activities/home/BookInfoActivity.cpp | 21 ++++---- src/activities/home/FileBrowserActivity.cpp | 13 ++--- src/activities/home/HomeActivity.cpp | 14 ++--- src/activities/home/RecentBooksActivity.cpp | 13 ++--- .../network/CalibreConnectActivity.cpp | 42 ++++++++------- .../network/CrossPointWebServerActivity.cpp | 24 +++++---- .../network/NetworkModeSelectionActivity.cpp | 12 ++--- .../network/WifiSelectionActivity.cpp | 28 +++++----- .../EpubReaderChapterSelectionActivity.cpp | 41 +++++--------- .../reader/EpubReaderFootnotesActivity.cpp | 10 ++-- .../reader/EpubReaderMenuActivity.cpp | 33 ++++-------- .../EpubReaderPercentSelectionActivity.cpp | 4 +- .../reader/KOReaderSyncActivity.cpp | 27 +++++----- src/activities/reader/QrDisplayActivity.cpp | 14 ++--- .../XtcReaderChapterSelectionActivity.cpp | 53 ++++++------------- .../settings/ButtonRemapActivity.cpp | 28 +++++----- .../settings/CalibreSettingsActivity.cpp | 15 +++--- .../settings/ClearCacheActivity.cpp | 27 +++++----- .../settings/KOReaderAuthActivity.cpp | 8 +-- .../settings/KOReaderSettingsActivity.cpp | 10 ++-- .../settings/LanguageSelectActivity.cpp | 10 ++-- src/activities/settings/OtaUpdateActivity.cpp | 21 ++++---- src/activities/settings/SettingsActivity.cpp | 20 +++---- .../settings/StatusBarSettingsActivity.cpp | 10 ++-- src/activities/util/ConfirmationActivity.cpp | 5 +- src/activities/util/KeyboardEntryActivity.cpp | 22 ++++---- src/components/UITheme.cpp | 35 +++++++++++- src/components/UITheme.h | 4 ++ src/components/themes/lyra/LyraTheme.cpp | 8 +-- 30 files changed, 304 insertions(+), 303 deletions(-) diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index f6a58385..c1a129b6 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -139,13 +139,13 @@ void OpdsBookBrowserActivity::loop() { void OpdsBookBrowserActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); + const int midY = contentRect.y + contentRect.height / 2; renderer.drawCenteredText(UI_12_FONT_ID, 15, tr(STR_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, midY, statusMessage.c_str()); const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); @@ -153,7 +153,7 @@ void OpdsBookBrowserActivity::render(RenderLock&&) { } if (state == BrowserState::LOADING) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, statusMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, midY, statusMessage.c_str()); const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); @@ -161,8 +161,8 @@ void OpdsBookBrowserActivity::render(RenderLock&&) { } if (state == BrowserState::ERROR) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, tr(STR_ERROR_MSG)); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, errorMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, midY - 20, tr(STR_ERROR_MSG)); + renderer.drawCenteredText(UI_10_FONT_ID, midY + 10, errorMessage.c_str()); const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_RETRY), "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); @@ -170,16 +170,15 @@ void OpdsBookBrowserActivity::render(RenderLock&&) { } if (state == BrowserState::DOWNLOADING) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 40, tr(STR_DOWNLOADING)); - const auto maxWidth = pageWidth - 40; - // Trim long titles to keep them within the screen bounds. - auto title = renderer.truncatedText(UI_10_FONT_ID, statusMessage.c_str(), maxWidth); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 10, title.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, midY - 40, tr(STR_DOWNLOADING)); + // Trim long titles to keep them within the content bounds. + auto title = renderer.truncatedText(UI_10_FONT_ID, statusMessage.c_str(), contentRect.width - 40); + renderer.drawCenteredText(UI_10_FONT_ID, midY - 10, title.c_str()); if (downloadTotal > 0) { - const int barWidth = pageWidth - 100; + const int barWidth = contentRect.width - 100; constexpr int barHeight = 20; - constexpr int barX = 50; - const int barY = pageHeight / 2 + 20; + const int barX = contentRect.x + 50; + const int barY = midY + 20; GUI.drawProgressBar(renderer, Rect{barX, barY, barWidth, barHeight}, downloadProgress, downloadTotal); } renderer.displayBuffer(); @@ -196,13 +195,13 @@ void OpdsBookBrowserActivity::render(RenderLock&&) { GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); if (entries.empty()) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, tr(STR_NO_ENTRIES)); + renderer.drawCenteredText(UI_10_FONT_ID, midY, tr(STR_NO_ENTRIES)); 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(contentRect.x, 60 + (selectorIndex % PAGE_ITEMS) * 30 - 2, contentRect.width - 1, 30); for (size_t i = pageStartIndex; i < entries.size() && i < static_cast(pageStartIndex + PAGE_ITEMS); i++) { const auto& entry = entries[i]; @@ -219,8 +218,8 @@ void OpdsBookBrowserActivity::render(RenderLock&&) { } } - 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(), contentRect.width - 40); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 60 + (i % PAGE_ITEMS) * 30, item.c_str(), i != static_cast(selectorIndex)); } diff --git a/src/activities/home/BookInfoActivity.cpp b/src/activities/home/BookInfoActivity.cpp index 4d9a6359..1c82ec8b 100644 --- a/src/activities/home/BookInfoActivity.cpp +++ b/src/activities/home/BookInfoActivity.cpp @@ -27,13 +27,14 @@ std::string BookInfoActivity::formatFileSize(const size_t bytes) { void BookInfoActivity::renderLoading() { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_INFO)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_INFO)); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - renderer.drawText(UI_12_FONT_ID, metrics.contentSidePadding, contentTop, tr(STR_LOADING)); + renderer.drawText(UI_12_FONT_ID, contentRect.x + metrics.contentSidePadding, contentTop, tr(STR_LOADING)); renderer.displayBuffer(); } @@ -111,17 +112,17 @@ void BookInfoActivity::loop() { void BookInfoActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); // Header - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_INFO)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_INFO)); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentBottom = pageHeight - metrics.buttonHintsHeight - metrics.verticalSpacing; - const int textX = metrics.contentSidePadding; - const int textWidth = pageWidth - metrics.contentSidePadding * 2; + const int contentBottom = contentRect.y + contentRect.height - metrics.verticalSpacing; + const int textX = contentRect.x + metrics.contentSidePadding; + const int textWidth = contentRect.width - metrics.contentSidePadding * 2; const int lineHeightSmall = renderer.getLineHeight(UI_10_FONT_ID); const int lineHeightLarge = renderer.getLineHeight(UI_12_FONT_ID); @@ -240,7 +241,7 @@ void BookInfoActivity::render(RenderLock&&) { if (!description.empty()) { int y = topSectionBottom + metrics.verticalSpacing; if (y + lineHeightSmall + 4 < contentBottom) { - renderer.drawLine(textX, y, pageWidth - metrics.contentSidePadding, y); + renderer.drawLine(textX, y, contentRect.x + contentRect.width - metrics.contentSidePadding, y); y += 4; const int descMaxLines = (contentBottom - y) / lineHeightSmall; diff --git a/src/activities/home/FileBrowserActivity.cpp b/src/activities/home/FileBrowserActivity.cpp index 99f31522..06f46ed0 100644 --- a/src/activities/home/FileBrowserActivity.cpp +++ b/src/activities/home/FileBrowserActivity.cpp @@ -269,20 +269,21 @@ std::string getFileName(std::string filename) { void FileBrowserActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, true); std::string folderName = (basepath == "/") ? tr(STR_SD_CARD) : basepath.substr(basepath.rfind('/') + 1); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, folderName.c_str()); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + folderName.c_str()); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing; if (files.empty()) { - renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, contentTop + 20, tr(STR_NO_FILES_FOUND)); + renderer.drawText(UI_10_FONT_ID, contentRect.x + metrics.contentSidePadding, contentTop + 20, + tr(STR_NO_FILES_FOUND)); } else { GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, files.size(), selectorIndex, + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, files.size(), selectorIndex, [this](int index) { return getFileName(files[index]); }, nullptr, [this](int index) { return UITheme::getFileIcon(files[index]); }); } diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index bc2180fa..ab6a2199 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -212,15 +212,15 @@ void HomeActivity::loop() { void HomeActivity::render(RenderLock&&) { const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); renderer.clearScreen(); bool bufferRestored = coverBufferStored && restoreCoverBuffer(); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.homeTopPadding}, nullptr); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.homeTopPadding}, nullptr); - GUI.drawRecentBookCover(renderer, Rect{0, metrics.homeTopPadding, pageWidth, metrics.homeCoverTileHeight}, + GUI.drawRecentBookCover(renderer, + Rect{contentRect.x, metrics.homeTopPadding, contentRect.width, metrics.homeCoverTileHeight}, recentBooks, selectorIndex, coverRendered, coverBufferStored, bufferRestored, std::bind(&HomeActivity::storeCoverBuffer, this)); @@ -237,9 +237,9 @@ void HomeActivity::render(RenderLock&&) { GUI.drawButtonMenu( renderer, - Rect{0, metrics.homeTopPadding + metrics.homeCoverTileHeight + metrics.verticalSpacing, pageWidth, - pageHeight - (metrics.headerHeight + metrics.homeTopPadding + metrics.verticalSpacing * 2 + - metrics.buttonHintsHeight)}, + Rect{contentRect.x, metrics.homeTopPadding + metrics.homeCoverTileHeight + metrics.verticalSpacing, + contentRect.width, + contentRect.height - (metrics.headerHeight + metrics.homeTopPadding + metrics.verticalSpacing * 2)}, static_cast(menuItems.size()), selectorIndex - recentBooks.size(), [&menuItems](int index) { return std::string(menuItems[index]); }, [&menuIcons](int index) { return menuIcons[index]; }); diff --git a/src/activities/home/RecentBooksActivity.cpp b/src/activities/home/RecentBooksActivity.cpp index 55f41ad0..ddf8d468 100644 --- a/src/activities/home/RecentBooksActivity.cpp +++ b/src/activities/home/RecentBooksActivity.cpp @@ -98,21 +98,22 @@ void RecentBooksActivity::loop() { void RecentBooksActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, true); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_MENU_RECENT_BOOKS)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_MENU_RECENT_BOOKS)); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing; // Recent tab if (recentBooks.empty()) { - renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, contentTop + 20, tr(STR_NO_RECENT_BOOKS)); + renderer.drawText(UI_10_FONT_ID, contentRect.x + metrics.contentSidePadding, contentTop + 20, + tr(STR_NO_RECENT_BOOKS)); } else { GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, recentBooks.size(), selectorIndex, + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, recentBooks.size(), selectorIndex, [this](int index) { return recentBooks[index].title; }, [this](int index) { const auto& book = recentBooks[index]; diff --git a/src/activities/network/CalibreConnectActivity.cpp b/src/activities/network/CalibreConnectActivity.cpp index a42ab16e..4d9f307d 100644 --- a/src/activities/network/CalibreConnectActivity.cpp +++ b/src/activities/network/CalibreConnectActivity.cpp @@ -169,57 +169,59 @@ void CalibreConnectActivity::loop() { void CalibreConnectActivity::render(RenderLock&&) { const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); renderer.clearScreen(); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_CALIBRE_WIRELESS)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_CALIBRE_WIRELESS)); const auto height = renderer.getLineHeight(UI_10_FONT_ID); - const auto top = (pageHeight - height) / 2; + const auto top = contentRect.y + (contentRect.height - height) / 2; if (state == CalibreConnectState::SERVER_STARTING) { renderer.drawCenteredText(UI_12_FONT_ID, top, tr(STR_CALIBRE_STARTING)); } else if (state == CalibreConnectState::ERROR) { renderer.drawCenteredText(UI_12_FONT_ID, top, tr(STR_CONNECTION_FAILED), true, EpdFontFamily::BOLD); } else if (state == CalibreConnectState::SERVER_RUNNING) { - GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, - connectedSSID.c_str(), (std::string(tr(STR_IP_ADDRESS_PREFIX)) + connectedIP).c_str()); + GUI.drawSubHeader( + renderer, + Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight}, + connectedSSID.c_str(), (std::string(tr(STR_IP_ADDRESS_PREFIX)) + connectedIP).c_str()); + const int textX = contentRect.x + metrics.contentSidePadding; + const int textWidth = contentRect.width - metrics.contentSidePadding * 2; int y = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 4; const auto heightText12 = renderer.getTextHeight(UI_12_FONT_ID); - renderer.drawText(UI_12_FONT_ID, metrics.contentSidePadding, y, tr(STR_CALIBRE_SETUP), true, EpdFontFamily::BOLD); + renderer.drawText(UI_12_FONT_ID, textX, y, tr(STR_CALIBRE_SETUP), true, EpdFontFamily::BOLD); y += heightText12 + metrics.verticalSpacing * 2; - renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y, tr(STR_CALIBRE_INSTRUCTION_1)); - renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y + height, tr(STR_CALIBRE_INSTRUCTION_2)); - renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y + height * 2, tr(STR_CALIBRE_INSTRUCTION_3)); - renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y + height * 3, tr(STR_CALIBRE_INSTRUCTION_4)); + renderer.drawText(SMALL_FONT_ID, textX, y, tr(STR_CALIBRE_INSTRUCTION_1)); + renderer.drawText(SMALL_FONT_ID, textX, y + height, tr(STR_CALIBRE_INSTRUCTION_2)); + renderer.drawText(SMALL_FONT_ID, textX, y + height * 2, tr(STR_CALIBRE_INSTRUCTION_3)); + renderer.drawText(SMALL_FONT_ID, textX, y + height * 3, tr(STR_CALIBRE_INSTRUCTION_4)); y += height * 3 + metrics.verticalSpacing * 4; - renderer.drawText(UI_12_FONT_ID, metrics.contentSidePadding, y, tr(STR_CALIBRE_STATUS), true, EpdFontFamily::BOLD); + renderer.drawText(UI_12_FONT_ID, textX, y, tr(STR_CALIBRE_STATUS), true, EpdFontFamily::BOLD); y += heightText12 + metrics.verticalSpacing * 2; if (lastProgressTotal > 0 && lastProgressReceived <= lastProgressTotal) { std::string label = tr(STR_CALIBRE_RECEIVING); if (!currentUploadName.empty()) { label += ": " + currentUploadName; - label = renderer.truncatedText(SMALL_FONT_ID, label.c_str(), pageWidth - metrics.contentSidePadding * 2, - EpdFontFamily::REGULAR); + label = renderer.truncatedText(SMALL_FONT_ID, label.c_str(), textWidth, EpdFontFamily::REGULAR); } - renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y, label.c_str()); + renderer.drawText(SMALL_FONT_ID, textX, y, label.c_str()); GUI.drawProgressBar(renderer, - Rect{metrics.contentSidePadding, y + height + metrics.verticalSpacing, - pageWidth - metrics.contentSidePadding * 2, metrics.progressBarHeight}, + Rect{contentRect.x + metrics.contentSidePadding, y + height + metrics.verticalSpacing, + textWidth, metrics.progressBarHeight}, lastProgressReceived, lastProgressTotal); y += height + metrics.verticalSpacing * 2 + metrics.progressBarHeight; } if (lastCompleteAt > 0 && (millis() - lastCompleteAt) < 6000) { std::string msg = std::string(tr(STR_CALIBRE_RECEIVED)) + lastCompleteName; - msg = renderer.truncatedText(SMALL_FONT_ID, msg.c_str(), pageWidth - metrics.contentSidePadding * 2, - EpdFontFamily::REGULAR); - renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y, msg.c_str()); + msg = renderer.truncatedText(SMALL_FONT_ID, msg.c_str(), textWidth, EpdFontFamily::REGULAR); + renderer.drawText(SMALL_FONT_ID, textX, y, msg.c_str()); } const auto labels = mappedInput.mapLabels(tr(STR_EXIT), "", "", ""); diff --git a/src/activities/network/CrossPointWebServerActivity.cpp b/src/activities/network/CrossPointWebServerActivity.cpp index b3cc8642..f2eaf501 100644 --- a/src/activities/network/CrossPointWebServerActivity.cpp +++ b/src/activities/network/CrossPointWebServerActivity.cpp @@ -348,19 +348,20 @@ void CrossPointWebServerActivity::render(RenderLock&&) { if (state == WebServerActivityState::SERVER_RUNNING || state == WebServerActivityState::AP_STARTING) { renderer.clearScreen(); const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, isApMode ? tr(STR_HOTSPOT_MODE) : tr(STR_FILE_TRANSFER), nullptr); if (state == WebServerActivityState::SERVER_RUNNING) { - GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, - connectedSSID.c_str()); + GUI.drawSubHeader( + renderer, + Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight}, + connectedSSID.c_str()); renderServerRunning(); } else { const auto height = renderer.getLineHeight(UI_10_FONT_ID); - const auto top = (pageHeight - height) / 2; + const auto top = contentRect.y + (contentRect.height - height) / 2; renderer.drawCenteredText(UI_10_FONT_ID, top, tr(STR_STARTING_HOTSPOT)); } renderer.displayBuffer(); @@ -369,12 +370,13 @@ void CrossPointWebServerActivity::render(RenderLock&&) { void CrossPointWebServerActivity::renderServerRunning() const { const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, isApMode ? tr(STR_HOTSPOT_MODE) : tr(STR_FILE_TRANSFER), nullptr); - GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, - connectedSSID.c_str()); + GUI.drawSubHeader( + renderer, Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight}, + connectedSSID.c_str()); int startY = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 2; int height10 = renderer.getLineHeight(UI_10_FONT_ID); @@ -424,7 +426,7 @@ void CrossPointWebServerActivity::renderServerRunning() const { // Show QR code for URL std::string webInfo = "http://" + connectedIP + "/"; - const Rect qrBounds((pageWidth - QR_CODE_WIDTH) / 2, startY, QR_CODE_WIDTH, QR_CODE_HEIGHT); + const Rect qrBounds(contentRect.x + (contentRect.width - QR_CODE_WIDTH) / 2, startY, QR_CODE_WIDTH, QR_CODE_HEIGHT); QrUtils::drawQrCode(renderer, qrBounds, webInfo); startY += QR_CODE_HEIGHT + metrics.verticalSpacing * 2; diff --git a/src/activities/network/NetworkModeSelectionActivity.cpp b/src/activities/network/NetworkModeSelectionActivity.cpp index f8e45307..4acc5df1 100644 --- a/src/activities/network/NetworkModeSelectionActivity.cpp +++ b/src/activities/network/NetworkModeSelectionActivity.cpp @@ -58,13 +58,13 @@ void NetworkModeSelectionActivity::render(RenderLock&&) { renderer.clearScreen(); const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_FILE_TRANSFER)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_FILE_TRANSFER)); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing * 2; // Menu items and descriptions static constexpr StrId menuItems[MENU_ITEM_COUNT] = {StrId::STR_JOIN_NETWORK, StrId::STR_CALIBRE_WIRELESS, StrId::STR_CREATE_HOTSPOT}; @@ -73,8 +73,8 @@ void NetworkModeSelectionActivity::render(RenderLock&&) { static constexpr UIIcon menuIcons[MENU_ITEM_COUNT] = {UIIcon::Wifi, UIIcon::Library, UIIcon::Hotspot}; GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast(MENU_ITEM_COUNT), selectedIndex, - [](int index) { return std::string(I18N.get(menuItems[index])); }, + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast(MENU_ITEM_COUNT), + selectedIndex, [](int index) { return std::string(I18N.get(menuItems[index])); }, [](int index) { return std::string(I18N.get(menuDescs[index])); }, [](int index) { return menuIcons[index]; }); // Draw help text at bottom diff --git a/src/activities/network/WifiSelectionActivity.cpp b/src/activities/network/WifiSelectionActivity.cpp index 85650df9..2db2623c 100644 --- a/src/activities/network/WifiSelectionActivity.cpp +++ b/src/activities/network/WifiSelectionActivity.cpp @@ -472,16 +472,16 @@ void WifiSelectionActivity::render(RenderLock&&) { renderer.clearScreen(); const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); // Draw header char countStr[32]; snprintf(countStr, sizeof(countStr), tr(STR_NETWORKS_FOUND), networks.size()); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_WIFI_NETWORKS), - countStr); - GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, - cachedMacAddress.c_str()); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_WIFI_NETWORKS), countStr); + GUI.drawSubHeader( + renderer, Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight}, + cachedMacAddress.c_str()); switch (state) { case WifiSelectionState::AUTO_CONNECTING: @@ -515,20 +515,19 @@ void WifiSelectionActivity::render(RenderLock&&) { void WifiSelectionActivity::renderNetworkList() const { const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); if (networks.empty()) { // No networks found or scan failed const auto height = renderer.getLineHeight(UI_10_FONT_ID); - const auto top = (pageHeight - height) / 2; + const auto top = (contentRect.y + contentRect.height - height) / 2; renderer.drawCenteredText(UI_10_FONT_ID, top, tr(STR_NO_NETWORKS)); renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, tr(STR_PRESS_OK_SCAN)); } else { int contentTop = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing; - int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2; + int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing * 2; GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast(networks.size()), + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast(networks.size()), selectedNetworkIndex, [this](int index) { return networks[index].ssid; }, nullptr, nullptr, [this](int index) { auto network = networks[index]; @@ -537,9 +536,10 @@ void WifiSelectionActivity::renderNetworkList() const { }); } - GUI.drawHelpText(renderer, - Rect{0, pageHeight - metrics.buttonHintsHeight - metrics.contentSidePadding - 15, pageWidth, 20}, - tr(STR_NETWORK_LEGEND)); + GUI.drawHelpText( + renderer, + Rect{contentRect.x, contentRect.y + contentRect.height - metrics.contentSidePadding - 15, contentRect.width, 20}, + tr(STR_NETWORK_LEGEND)); const bool hasSavedPassword = !networks.empty() && networks[selectedNetworkIndex].hasSavedPassword; const char* forgetLabel = hasSavedPassword ? tr(STR_FORGET_BUTTON) : ""; diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp index ea68bd8f..821db372 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp @@ -10,17 +10,10 @@ int EpubReaderChapterSelectionActivity::getTotalItems() const { return epub->getTocItemsCount(); } int EpubReaderChapterSelectionActivity::getPageItems() const { - // Layout constants used in renderScreen constexpr int lineHeight = 30; - - const int screenHeight = renderer.getScreenHeight(); - const auto orientation = renderer.getOrientation(); - // In inverted portrait, the button hints are drawn near the logical top. - // Reserve vertical space so list items do not collide with the hints. - const bool isPortraitInverted = orientation == GfxRenderer::Orientation::PortraitInverted; - const int hintGutterHeight = isPortraitInverted ? 50 : 0; - const int startY = 60 + hintGutterHeight; - const int availableHeight = screenHeight - startY - lineHeight; + const Rect contentRect = UITheme::getContentRect(renderer, true, false); + const int startY = 60 + contentRect.y; + const int availableHeight = contentRect.y + contentRect.height - startY - lineHeight; // Clamp to at least one item to avoid division by zero and empty paging. return std::max(1, availableHeight / lineHeight); } @@ -89,43 +82,33 @@ void EpubReaderChapterSelectionActivity::loop() { void EpubReaderChapterSelectionActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto orientation = renderer.getOrientation(); - // Landscape orientation: reserve a horizontal gutter for button hints. - const bool isLandscapeCw = orientation == GfxRenderer::Orientation::LandscapeClockwise; - const bool isLandscapeCcw = orientation == GfxRenderer::Orientation::LandscapeCounterClockwise; - // Inverted portrait: reserve vertical space for hints at the top. - const bool isPortraitInverted = orientation == GfxRenderer::Orientation::PortraitInverted; - const int hintGutterWidth = (isLandscapeCw || isLandscapeCcw) ? 30 : 0; - // Landscape CW places hints on the left edge; CCW keeps them on the right. - const int contentX = isLandscapeCw ? hintGutterWidth : 0; - const int contentWidth = pageWidth - hintGutterWidth; - const int hintGutterHeight = isPortraitInverted ? 50 : 0; - const int contentY = hintGutterHeight; + const Rect contentRect = UITheme::getContentRect(renderer, true, false); const int pageItems = getPageItems(); const int totalItems = getTotalItems(); // Manual centering to honor content gutters. const int titleX = - contentX + (contentWidth - renderer.getTextWidth(UI_12_FONT_ID, tr(STR_SELECT_CHAPTER), EpdFontFamily::BOLD)) / 2; - renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentY, tr(STR_SELECT_CHAPTER), true, EpdFontFamily::BOLD); + contentRect.x + + (contentRect.width - renderer.getTextWidth(UI_12_FONT_ID, tr(STR_SELECT_CHAPTER), EpdFontFamily::BOLD)) / 2; + renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentRect.y, tr(STR_SELECT_CHAPTER), true, EpdFontFamily::BOLD); const auto pageStartIndex = selectorIndex / pageItems * pageItems; // Highlight only the content area, not the hint gutters. - renderer.fillRect(contentX, 60 + contentY + (selectorIndex % pageItems) * 30 - 2, contentWidth - 1, 30); + renderer.fillRect(contentRect.x, 60 + contentRect.y + (selectorIndex % pageItems) * 30 - 2, contentRect.width - 1, + 30); for (int i = 0; i < pageItems; i++) { int itemIndex = pageStartIndex + i; if (itemIndex >= totalItems) break; - const int displayY = 60 + contentY + i * 30; + const int displayY = 60 + contentRect.y + i * 30; const bool isSelected = (itemIndex == selectorIndex); auto item = epub->getTocItem(itemIndex); // Indent per TOC level while keeping content within the gutter-safe region. - const int indentSize = contentX + 20 + (item.level - 1) * 15; + const int indentSize = contentRect.x + 20 + (item.level - 1) * 15; const std::string chapterName = - renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), contentWidth - 40 - indentSize); + renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), contentRect.width - 40 - indentSize); renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected); } diff --git a/src/activities/reader/EpubReaderFootnotesActivity.cpp b/src/activities/reader/EpubReaderFootnotesActivity.cpp index ef077a07..25ed5ca1 100644 --- a/src/activities/reader/EpubReaderFootnotesActivity.cpp +++ b/src/activities/reader/EpubReaderFootnotesActivity.cpp @@ -64,19 +64,19 @@ void EpubReaderFootnotesActivity::render(RenderLock&&) { constexpr int startY = 50; constexpr int lineHeight = 36; - const int screenWidth = renderer.getScreenWidth(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); constexpr int marginLeft = 20; - const int visibleCount = std::max(1, (renderer.getScreenHeight() - startY) / lineHeight); + const int visibleCount = std::max(1, (contentRect.height - startY) / lineHeight); if (selectedIndex < scrollOffset) scrollOffset = selectedIndex; if (selectedIndex >= scrollOffset + visibleCount) scrollOffset = selectedIndex - visibleCount + 1; for (int i = scrollOffset; i < static_cast(footnotes.size()) && i < scrollOffset + visibleCount; i++) { - const int y = startY + (i - scrollOffset) * lineHeight; + const int y = contentRect.y + startY + (i - scrollOffset) * lineHeight; const bool isSelected = (i == selectedIndex); if (isSelected) { - renderer.fillRect(0, y, screenWidth, lineHeight, true); + renderer.fillRect(contentRect.x, y, contentRect.width, lineHeight, true); } // Show footnote number and abbreviated href @@ -84,7 +84,7 @@ void EpubReaderFootnotesActivity::render(RenderLock&&) { if (label.empty()) { label = tr(STR_LINK); } - renderer.drawText(UI_10_FONT_ID, marginLeft, y + 4, label.c_str(), !isSelected); + renderer.drawText(UI_10_FONT_ID, contentRect.x + marginLeft, y + 4, label.c_str(), !isSelected); } const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", ""); diff --git a/src/activities/reader/EpubReaderMenuActivity.cpp b/src/activities/reader/EpubReaderMenuActivity.cpp index 1d95d9b7..b8e2555f 100644 --- a/src/activities/reader/EpubReaderMenuActivity.cpp +++ b/src/activities/reader/EpubReaderMenuActivity.cpp @@ -86,29 +86,16 @@ void EpubReaderMenuActivity::loop() { void EpubReaderMenuActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto orientation = renderer.getOrientation(); - // Landscape orientation: button hints are drawn along a vertical edge, so we - // reserve a horizontal gutter to prevent overlap with menu content. - const bool isLandscapeCw = orientation == GfxRenderer::Orientation::LandscapeClockwise; - const bool isLandscapeCcw = orientation == GfxRenderer::Orientation::LandscapeCounterClockwise; - // Inverted portrait: button hints appear near the logical top, so we reserve - // vertical space to keep the header and list clear. - const bool isPortraitInverted = orientation == GfxRenderer::Orientation::PortraitInverted; - const int hintGutterWidth = (isLandscapeCw || isLandscapeCcw) ? 30 : 0; - // Landscape CW places hints on the left edge; CCW keeps them on the right. - const int contentX = isLandscapeCw ? hintGutterWidth : 0; - const int contentWidth = pageWidth - hintGutterWidth; - const int hintGutterHeight = isPortraitInverted ? 50 : 0; - const int contentY = hintGutterHeight; + const Rect contentRect = UITheme::getContentRect(renderer, true, false); // Title const std::string truncTitle = - renderer.truncatedText(UI_12_FONT_ID, title.c_str(), contentWidth - 40, EpdFontFamily::BOLD); + renderer.truncatedText(UI_12_FONT_ID, title.c_str(), contentRect.width - 40, EpdFontFamily::BOLD); // Manual centering so we can respect the content gutter. const int titleX = - contentX + (contentWidth - renderer.getTextWidth(UI_12_FONT_ID, truncTitle.c_str(), EpdFontFamily::BOLD)) / 2; - renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentY, truncTitle.c_str(), true, EpdFontFamily::BOLD); + contentRect.x + + (contentRect.width - renderer.getTextWidth(UI_12_FONT_ID, truncTitle.c_str(), EpdFontFamily::BOLD)) / 2; + renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentRect.y, truncTitle.c_str(), true, EpdFontFamily::BOLD); // Progress summary std::string progressLine; @@ -120,7 +107,7 @@ void EpubReaderMenuActivity::render(RenderLock&&) { renderer.drawCenteredText(UI_10_FONT_ID, 45, progressLine.c_str()); // Menu Items - const int startY = 75 + contentY; + const int startY = 75 + contentRect.y; constexpr int lineHeight = 30; for (size_t i = 0; i < menuItems.size(); ++i) { @@ -129,23 +116,23 @@ void EpubReaderMenuActivity::render(RenderLock&&) { if (isSelected) { // Highlight only the content area so we don't paint over hint gutters. - renderer.fillRect(contentX, displayY, contentWidth - 1, lineHeight, true); + renderer.fillRect(contentRect.x, displayY, contentRect.width - 1, lineHeight, true); } - renderer.drawText(UI_10_FONT_ID, contentX + 20, displayY, I18N.get(menuItems[i].labelId), !isSelected); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, displayY, I18N.get(menuItems[i].labelId), !isSelected); if (menuItems[i].action == MenuAction::ROTATE_SCREEN) { // Render current orientation value on the right edge of the content area. const char* value = I18N.get(orientationLabels[pendingOrientation]); const auto width = renderer.getTextWidth(UI_10_FONT_ID, value); - renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected); + renderer.drawText(UI_10_FONT_ID, contentRect.x + contentRect.width - 20 - width, displayY, value, !isSelected); } if (menuItems[i].action == MenuAction::AUTO_PAGE_TURN) { // Render current page turn value on the right edge of the content area. const auto value = pageTurnLabels[selectedPageTurnOption]; const auto width = renderer.getTextWidth(UI_10_FONT_ID, value); - renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected); + renderer.drawText(UI_10_FONT_ID, contentRect.x + contentRect.width - 20 - width, displayY, value, !isSelected); } } diff --git a/src/activities/reader/EpubReaderPercentSelectionActivity.cpp b/src/activities/reader/EpubReaderPercentSelectionActivity.cpp index db4109f0..7e122dff 100644 --- a/src/activities/reader/EpubReaderPercentSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderPercentSelectionActivity.cpp @@ -65,10 +65,10 @@ void EpubReaderPercentSelectionActivity::render(RenderLock&&) { renderer.drawCenteredText(UI_12_FONT_ID, 90, percentText.c_str(), true, EpdFontFamily::BOLD); // Draw slider track. - const int screenWidth = renderer.getScreenWidth(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); constexpr int barWidth = 360; constexpr int barHeight = 16; - const int barX = (screenWidth - barWidth) / 2; + const int barX = contentRect.x + (contentRect.width - barWidth) / 2; const int barY = 140; renderer.drawRect(barX, barY, barWidth, barHeight); diff --git a/src/activities/reader/KOReaderSyncActivity.cpp b/src/activities/reader/KOReaderSyncActivity.cpp index 4df71e93..3cec3f79 100644 --- a/src/activities/reader/KOReaderSyncActivity.cpp +++ b/src/activities/reader/KOReaderSyncActivity.cpp @@ -221,10 +221,10 @@ void KOReaderSyncActivity::onExit() { } void KOReaderSyncActivity::render(RenderLock&&) { - const auto pageWidth = renderer.getScreenWidth(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); renderer.clearScreen(); - renderer.drawCenteredText(UI_12_FONT_ID, 15, tr(STR_KOREADER_SYNC), true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15 + contentRect.y, tr(STR_KOREADER_SYNC), true, EpdFontFamily::BOLD); if (state == NO_CREDENTIALS) { renderer.drawCenteredText(UI_10_FONT_ID, 280, tr(STR_NO_CREDENTIALS_MSG), true, EpdFontFamily::BOLD); @@ -257,45 +257,46 @@ void KOReaderSyncActivity::render(RenderLock&&) { : (std::string(tr(STR_SECTION_PREFIX)) + std::to_string(currentSpineIndex + 1)); // Remote progress - chapter and page - renderer.drawText(UI_10_FONT_ID, 20, 160, tr(STR_REMOTE_LABEL), true); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 160, tr(STR_REMOTE_LABEL), true); char remoteChapterStr[128]; snprintf(remoteChapterStr, sizeof(remoteChapterStr), " %s", remoteChapter.c_str()); - renderer.drawText(UI_10_FONT_ID, 20, 185, remoteChapterStr); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 185, remoteChapterStr); char remotePageStr[64]; snprintf(remotePageStr, sizeof(remotePageStr), tr(STR_PAGE_OVERALL_FORMAT), remotePosition.pageNumber + 1, remoteProgress.percentage * 100); - renderer.drawText(UI_10_FONT_ID, 20, 210, remotePageStr); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 210, remotePageStr); if (!remoteProgress.device.empty()) { char deviceStr[64]; snprintf(deviceStr, sizeof(deviceStr), tr(STR_DEVICE_FROM_FORMAT), remoteProgress.device.c_str()); - renderer.drawText(UI_10_FONT_ID, 20, 235, deviceStr); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 235, deviceStr); } // Local progress - chapter and page - renderer.drawText(UI_10_FONT_ID, 20, 270, tr(STR_LOCAL_LABEL), true); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 270, tr(STR_LOCAL_LABEL), true); char localChapterStr[128]; snprintf(localChapterStr, sizeof(localChapterStr), " %s", localChapter.c_str()); - renderer.drawText(UI_10_FONT_ID, 20, 295, localChapterStr); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 295, localChapterStr); char localPageStr[64]; snprintf(localPageStr, sizeof(localPageStr), tr(STR_PAGE_TOTAL_OVERALL_FORMAT), currentPage + 1, totalPagesInSpine, localProgress.percentage * 100); - renderer.drawText(UI_10_FONT_ID, 20, 320, localPageStr); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, 320, localPageStr); const int optionY = 350; const int optionHeight = 30; // Apply option if (selectedOption == 0) { - renderer.fillRect(0, optionY - 2, pageWidth - 1, optionHeight); + renderer.fillRect(contentRect.x, optionY - 2, contentRect.width - 1, optionHeight); } - renderer.drawText(UI_10_FONT_ID, 20, optionY, tr(STR_APPLY_REMOTE), selectedOption != 0); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, optionY, tr(STR_APPLY_REMOTE), selectedOption != 0); // Upload option if (selectedOption == 1) { - renderer.fillRect(0, optionY + optionHeight - 2, pageWidth - 1, optionHeight); + renderer.fillRect(contentRect.x, optionY + optionHeight - 2, contentRect.width - 1, optionHeight); } - renderer.drawText(UI_10_FONT_ID, 20, optionY + optionHeight, tr(STR_UPLOAD_LOCAL), selectedOption != 1); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, optionY + optionHeight, tr(STR_UPLOAD_LOCAL), + selectedOption != 1); // Bottom button hints const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); diff --git a/src/activities/reader/QrDisplayActivity.cpp b/src/activities/reader/QrDisplayActivity.cpp index 2bcf330a..98cba2e4 100644 --- a/src/activities/reader/QrDisplayActivity.cpp +++ b/src/activities/reader/QrDisplayActivity.cpp @@ -25,17 +25,17 @@ void QrDisplayActivity::loop() { void QrDisplayActivity::render(RenderLock&&) { renderer.clearScreen(); - auto metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_DISPLAY_QR), nullptr); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_DISPLAY_QR), nullptr); - const int availableWidth = pageWidth - 40; - const int availableHeight = pageHeight - metrics.topPadding - metrics.headerHeight - metrics.verticalSpacing * 2 - 40; const int startY = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; + const int availableHeight = contentRect.height - startY - metrics.verticalSpacing; - const Rect qrBounds(20, startY, availableWidth, availableHeight); + const Rect qrBounds(contentRect.x + metrics.contentSidePadding, startY, + contentRect.width - metrics.contentSidePadding * 2, availableHeight); QrUtils::drawQrCode(renderer, qrBounds, textPayload); const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); diff --git a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp index 99d6b28d..61d283de 100644 --- a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp @@ -11,15 +11,9 @@ int XtcReaderChapterSelectionActivity::getPageItems() const { constexpr int lineHeight = 30; - - const int screenHeight = renderer.getScreenHeight(); - const auto orientation = renderer.getOrientation(); - // In inverted portrait, the hint row is drawn near the logical top. - // Reserve vertical space so the list starts below the hints. - const bool isPortraitInverted = orientation == GfxRenderer::Orientation::PortraitInverted; - const int hintGutterHeight = isPortraitInverted ? 50 : 0; - const int startY = 60 + hintGutterHeight; - const int availableHeight = screenHeight - startY - lineHeight; + const Rect contentRect = UITheme::getContentRect(renderer, true, false); + constexpr int startY = 60; + const int availableHeight = contentRect.height - startY - lineHeight; // Clamp to at least one item to prevent empty page math. return std::max(1, availableHeight / lineHeight); } @@ -93,48 +87,35 @@ void XtcReaderChapterSelectionActivity::loop() { void XtcReaderChapterSelectionActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto orientation = renderer.getOrientation(); - // Landscape orientation: reserve a horizontal gutter for button hints. - const bool isLandscapeCw = orientation == GfxRenderer::Orientation::LandscapeClockwise; - const bool isLandscapeCcw = orientation == GfxRenderer::Orientation::LandscapeCounterClockwise; - // Inverted portrait: reserve vertical space for hints at the top. - const bool isPortraitInverted = orientation == GfxRenderer::Orientation::PortraitInverted; - const int hintGutterWidth = (isLandscapeCw || isLandscapeCcw) ? 30 : 0; - // Landscape CW places hints on the left edge; CCW keeps them on the right. - const int contentX = isLandscapeCw ? hintGutterWidth : 0; - const int contentWidth = pageWidth - hintGutterWidth; - const int hintGutterHeight = isPortraitInverted ? 50 : 0; - const int contentY = hintGutterHeight; + const Rect contentRect = UITheme::getContentRect(renderer, true, false); const int pageItems = getPageItems(); - // Manual centering to honor content gutters. + const int titleX = - contentX + (contentWidth - renderer.getTextWidth(UI_12_FONT_ID, tr(STR_SELECT_CHAPTER), EpdFontFamily::BOLD)) / 2; - renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentY, tr(STR_SELECT_CHAPTER), true, EpdFontFamily::BOLD); + contentRect.x + + (contentRect.width - renderer.getTextWidth(UI_12_FONT_ID, tr(STR_SELECT_CHAPTER), EpdFontFamily::BOLD)) / 2; + renderer.drawText(UI_12_FONT_ID, titleX, contentRect.y + 15, tr(STR_SELECT_CHAPTER), true, EpdFontFamily::BOLD); const auto& chapters = xtc->getChapters(); if (chapters.empty()) { - // Center the empty state within the gutter-safe content region. - const int emptyX = contentX + (contentWidth - renderer.getTextWidth(UI_10_FONT_ID, tr(STR_NO_CHAPTERS))) / 2; - renderer.drawText(UI_10_FONT_ID, emptyX, 120 + contentY, tr(STR_NO_CHAPTERS)); + const int emptyX = + contentRect.x + (contentRect.width - renderer.getTextWidth(UI_10_FONT_ID, tr(STR_NO_CHAPTERS))) / 2; + renderer.drawText(UI_10_FONT_ID, emptyX, contentRect.y + 120, tr(STR_NO_CHAPTERS)); renderer.displayBuffer(); return; } const auto pageStartIndex = selectorIndex / pageItems * pageItems; - // Highlight only the content area, not the hint gutters. - renderer.fillRect(contentX, 60 + contentY + (selectorIndex % pageItems) * 30 - 2, contentWidth - 1, 30); + renderer.fillRect(contentRect.x, contentRect.y + 60 + (selectorIndex % pageItems) * 30 - 2, contentRect.width - 1, + 30); for (int i = pageStartIndex; i < static_cast(chapters.size()) && i < pageStartIndex + pageItems; i++) { const auto& chapter = chapters[i]; const char* title = chapter.name.empty() ? tr(STR_UNNAMED) : chapter.name.c_str(); - renderer.drawText(UI_10_FONT_ID, contentX + 20, 60 + contentY + (i % pageItems) * 30, title, i != selectorIndex); + renderer.drawText(UI_10_FONT_ID, contentRect.x + 20, contentRect.y + 60 + (i % pageItems) * 30, title, + i != selectorIndex); } - // Skip button hints in landscape CW mode (they overlap content) - if (renderer.getOrientation() != GfxRenderer::LandscapeClockwise) { - const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); - GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); - } + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); } diff --git a/src/activities/settings/ButtonRemapActivity.cpp b/src/activities/settings/ButtonRemapActivity.cpp index 8e2e8d7a..aa32f9a6 100644 --- a/src/activities/settings/ButtonRemapActivity.cpp +++ b/src/activities/settings/ButtonRemapActivity.cpp @@ -105,19 +105,20 @@ void ButtonRemapActivity::render(RenderLock&&) { }; const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); renderer.clearScreen(); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_REMAP_FRONT_BUTTONS)); - GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, - tr(STR_REMAP_PROMPT)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_REMAP_FRONT_BUTTONS)); + GUI.drawSubHeader( + renderer, Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight}, + tr(STR_REMAP_PROMPT)); int topOffset = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing; - int contentHeight = pageHeight - topOffset - metrics.buttonHintsHeight - metrics.verticalSpacing; + int contentHeight = contentRect.height - topOffset - metrics.verticalSpacing; GUI.drawList( - renderer, Rect{0, topOffset, pageWidth, contentHeight}, kRoleCount, currentStep, + renderer, Rect{contentRect.x, topOffset, contentRect.width, contentHeight}, kRoleCount, currentStep, [&](int index) { return getRoleName(static_cast(index)); }, nullptr, nullptr, [&](int index) { uint8_t assignedButton = tempMapping[static_cast(index)]; @@ -128,16 +129,19 @@ void ButtonRemapActivity::render(RenderLock&&) { // Temporary warning banner for duplicates. if (!errorMessage.empty()) { GUI.drawHelpText(renderer, - Rect{0, pageHeight - metrics.buttonHintsHeight - metrics.contentSidePadding - 15, pageWidth, 20}, + Rect{contentRect.x, contentRect.y + contentRect.height - metrics.contentSidePadding - 15, + contentRect.width, 20}, errorMessage.c_str()); } // Provide side button actions at the bottom of the screen (split across two lines). + GUI.drawHelpText( + renderer, + Rect{contentRect.x, topOffset + 4 * metrics.listRowHeight + 4 * metrics.verticalSpacing, contentRect.width, 20}, + tr(STR_REMAP_RESET_HINT)); GUI.drawHelpText(renderer, - Rect{0, topOffset + 4 * metrics.listRowHeight + 4 * metrics.verticalSpacing, pageWidth, 20}, - tr(STR_REMAP_RESET_HINT)); - GUI.drawHelpText(renderer, - Rect{0, topOffset + 4 * metrics.listRowHeight + 5 * metrics.verticalSpacing + 20, pageWidth, 20}, + Rect{contentRect.x, topOffset + 4 * metrics.listRowHeight + 5 * metrics.verticalSpacing + 20, + contentRect.width, 20}, tr(STR_REMAP_CANCEL_HINT)); // Live preview of logical labels under front buttons. diff --git a/src/activities/settings/CalibreSettingsActivity.cpp b/src/activities/settings/CalibreSettingsActivity.cpp index 8fcf9aba..ec635861 100644 --- a/src/activities/settings/CalibreSettingsActivity.cpp +++ b/src/activities/settings/CalibreSettingsActivity.cpp @@ -92,16 +92,17 @@ void CalibreSettingsActivity::render(RenderLock&&) { renderer.clearScreen(); const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_OPDS_BROWSER)); - GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, - tr(STR_CALIBRE_URL_HINT)); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_OPDS_BROWSER)); + GUI.drawSubHeader( + renderer, Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight}, + tr(STR_CALIBRE_URL_HINT)); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing + metrics.tabBarHeight; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing * 2; GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast(MENU_ITEMS), + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast(MENU_ITEMS), static_cast(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr, nullptr, [this](int index) { diff --git a/src/activities/settings/ClearCacheActivity.cpp b/src/activities/settings/ClearCacheActivity.cpp index c4fc4347..34216aba 100644 --- a/src/activities/settings/ClearCacheActivity.cpp +++ b/src/activities/settings/ClearCacheActivity.cpp @@ -20,19 +20,19 @@ void ClearCacheActivity::onExit() { Activity::onExit(); } void ClearCacheActivity::render(RenderLock&&) { const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); renderer.clearScreen(); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_CLEAR_READING_CACHE)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_CLEAR_READING_CACHE)); + const int midY = contentRect.y + contentRect.height / 2; if (state == WARNING) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 60, tr(STR_CLEAR_CACHE_WARNING_1), true); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 30, tr(STR_CLEAR_CACHE_WARNING_2), true, - EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, tr(STR_CLEAR_CACHE_WARNING_3), true); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 30, tr(STR_CLEAR_CACHE_WARNING_4), true); + renderer.drawCenteredText(UI_10_FONT_ID, midY - 60, tr(STR_CLEAR_CACHE_WARNING_1), true); + renderer.drawCenteredText(UI_10_FONT_ID, midY - 30, tr(STR_CLEAR_CACHE_WARNING_2), true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, midY + 10, tr(STR_CLEAR_CACHE_WARNING_3), true); + renderer.drawCenteredText(UI_10_FONT_ID, midY + 30, tr(STR_CLEAR_CACHE_WARNING_4), true); const auto labels = mappedInput.mapLabels(tr(STR_CANCEL), tr(STR_CLEAR_BUTTON), "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); @@ -41,18 +41,18 @@ void ClearCacheActivity::render(RenderLock&&) { } if (state == CLEARING) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, tr(STR_CLEARING_CACHE)); + renderer.drawCenteredText(UI_10_FONT_ID, midY, tr(STR_CLEARING_CACHE)); renderer.displayBuffer(); return; } if (state == SUCCESS) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, tr(STR_CACHE_CLEARED), true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, midY - 20, tr(STR_CACHE_CLEARED), true, EpdFontFamily::BOLD); std::string resultText = std::to_string(clearedCount) + " " + std::string(tr(STR_ITEMS_REMOVED)); if (failedCount > 0) { resultText += ", " + std::to_string(failedCount) + " " + std::string(tr(STR_FAILED_LOWER)); } - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, resultText.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, midY + 10, resultText.c_str()); const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); @@ -61,9 +61,8 @@ void ClearCacheActivity::render(RenderLock&&) { } if (state == FAILED) { - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, tr(STR_CLEAR_CACHE_FAILED), true, - EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, tr(STR_CHECK_SERIAL_OUTPUT)); + renderer.drawCenteredText(UI_10_FONT_ID, midY - 20, tr(STR_CLEAR_CACHE_FAILED), true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_10_FONT_ID, midY + 10, tr(STR_CHECK_SERIAL_OUTPUT)); const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); diff --git a/src/activities/settings/KOReaderAuthActivity.cpp b/src/activities/settings/KOReaderAuthActivity.cpp index 2240ac6e..47c7188f 100644 --- a/src/activities/settings/KOReaderAuthActivity.cpp +++ b/src/activities/settings/KOReaderAuthActivity.cpp @@ -76,12 +76,12 @@ void KOReaderAuthActivity::render(RenderLock&&) { renderer.clearScreen(); const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_KOREADER_AUTH)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_KOREADER_AUTH)); const auto height = renderer.getLineHeight(UI_10_FONT_ID); - const auto top = (pageHeight - height) / 2; + const auto top = contentRect.y + (contentRect.height - height) / 2; if (state == AUTHENTICATING) { renderer.drawCenteredText(UI_10_FONT_ID, top, statusMessage.c_str()); diff --git a/src/activities/settings/KOReaderSettingsActivity.cpp b/src/activities/settings/KOReaderSettingsActivity.cpp index 7291c8c1..cbfefbd2 100644 --- a/src/activities/settings/KOReaderSettingsActivity.cpp +++ b/src/activities/settings/KOReaderSettingsActivity.cpp @@ -115,15 +115,15 @@ void KOReaderSettingsActivity::render(RenderLock&&) { renderer.clearScreen(); const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_KOREADER_SYNC)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_KOREADER_SYNC)); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing * 2; GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast(MENU_ITEMS), + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast(MENU_ITEMS), static_cast(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr, nullptr, [this](int index) { diff --git a/src/activities/settings/LanguageSelectActivity.cpp b/src/activities/settings/LanguageSelectActivity.cpp index 5f21da26..2f09ae35 100644 --- a/src/activities/settings/LanguageSelectActivity.cpp +++ b/src/activities/settings/LanguageSelectActivity.cpp @@ -61,18 +61,18 @@ void LanguageSelectActivity::handleSelection() { void LanguageSelectActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); auto metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_LANGUAGE)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_LANGUAGE)); // Current language marker const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing; const auto currentLang = static_cast(I18N.getLanguage()); GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, totalItems, selectedIndex, + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, totalItems, selectedIndex, [this](int index) { return I18N.getLanguageName(static_cast(SORTED_LANGUAGE_INDICES[index])); }, nullptr, nullptr, [this, currentLang](int index) { return SORTED_LANGUAGE_INDICES[index] == currentLang ? tr(STR_SELECTED) : ""; }, diff --git a/src/activities/settings/OtaUpdateActivity.cpp b/src/activities/settings/OtaUpdateActivity.cpp index c661a618..ebbb20f9 100644 --- a/src/activities/settings/OtaUpdateActivity.cpp +++ b/src/activities/settings/OtaUpdateActivity.cpp @@ -75,14 +75,14 @@ void OtaUpdateActivity::onExit() { void OtaUpdateActivity::render(RenderLock&&) { const auto& metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); renderer.clearScreen(); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_UPDATE)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_UPDATE)); const auto height = renderer.getLineHeight(UI_10_FONT_ID); - const auto top = (pageHeight - height) / 2; + const auto top = contentRect.y + (contentRect.height - height) / 2; float updaterProgress = 0; if (state == UPDATE_IN_PROGRESS) { @@ -99,9 +99,10 @@ void OtaUpdateActivity::render(RenderLock&&) { renderer.drawCenteredText(UI_10_FONT_ID, top, tr(STR_CHECKING_UPDATE)); } else if (state == WAITING_CONFIRMATION) { renderer.drawCenteredText(UI_10_FONT_ID, top, tr(STR_NEW_UPDATE), true, EpdFontFamily::BOLD); - renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, top + height + metrics.verticalSpacing, + renderer.drawText(UI_10_FONT_ID, contentRect.x + metrics.contentSidePadding, top + height + metrics.verticalSpacing, (std::string(tr(STR_CURRENT_VERSION)) + CROSSPOINT_VERSION).c_str()); - renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, top + height * 2 + metrics.verticalSpacing * 2, + renderer.drawText(UI_10_FONT_ID, contentRect.x + metrics.contentSidePadding, + top + height * 2 + metrics.verticalSpacing * 2, (std::string(tr(STR_NEW_VERSION)) + updater.getLatestVersion()).c_str()); const auto labels = mappedInput.mapLabels(tr(STR_CANCEL), tr(STR_UPDATE), "", ""); @@ -110,10 +111,10 @@ void OtaUpdateActivity::render(RenderLock&&) { renderer.drawCenteredText(UI_10_FONT_ID, top, tr(STR_UPDATING)); int y = top + height + metrics.verticalSpacing; - GUI.drawProgressBar( - renderer, - Rect{metrics.contentSidePadding, y, pageWidth - metrics.contentSidePadding * 2, metrics.progressBarHeight}, - static_cast(updaterProgress * 100), 100); + GUI.drawProgressBar(renderer, + Rect{contentRect.x + metrics.contentSidePadding, y, + contentRect.width - metrics.contentSidePadding * 2, metrics.progressBarHeight}, + static_cast(updaterProgress * 100), 100); y += metrics.progressBarHeight + metrics.verticalSpacing; renderer.drawCenteredText(UI_10_FONT_ID, y, diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index d692352c..bee6315b 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -202,28 +202,28 @@ void SettingsActivity::toggleCurrentSetting() { void SettingsActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); - const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_SETTINGS_TITLE), - CROSSPOINT_VERSION); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_SETTINGS_TITLE), CROSSPOINT_VERSION); std::vector tabs; tabs.reserve(categoryCount); for (int i = 0; i < categoryCount; i++) { tabs.push_back({I18N.get(categoryNames[i]), selectedCategoryIndex == i}); } - GUI.drawTabBar(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, tabs, - selectedSettingIndex == 0); + GUI.drawTabBar( + renderer, Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight}, + tabs, selectedSettingIndex == 0); + const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing; const auto& settings = *currentSettings; GUI.drawList( renderer, - Rect{0, metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing, pageWidth, - pageHeight - (metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.buttonHintsHeight + - metrics.verticalSpacing * 2)}, + Rect{contentRect.x, contentTop, contentRect.width, + contentRect.height - + (metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 2)}, settingsCount, selectedSettingIndex - 1, [&settings](int index) { return std::string(I18N.get(settings[index].nameId)); }, nullptr, nullptr, [&settings](int i) { diff --git a/src/activities/settings/StatusBarSettingsActivity.cpp b/src/activities/settings/StatusBarSettingsActivity.cpp index 6ff0b34d..0928a57d 100644 --- a/src/activities/settings/StatusBarSettingsActivity.cpp +++ b/src/activities/settings/StatusBarSettingsActivity.cpp @@ -118,15 +118,15 @@ void StatusBarSettingsActivity::render(RenderLock&&) { renderer.clearScreen(); auto metrics = UITheme::getInstance().getMetrics(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_CUSTOMISE_STATUS_BAR)); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_CUSTOMISE_STATUS_BAR)); const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; - const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2; + const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing * 2; GUI.drawList( - renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast(MENU_ITEMS), + renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast(MENU_ITEMS), static_cast(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr, nullptr, [this](int index) { diff --git a/src/activities/util/ConfirmationActivity.cpp b/src/activities/util/ConfirmationActivity.cpp index 093e096a..353c5474 100644 --- a/src/activities/util/ConfirmationActivity.cpp +++ b/src/activities/util/ConfirmationActivity.cpp @@ -14,7 +14,8 @@ void ConfirmationActivity::onEnter() { inputArmed = false; lineHeight = renderer.getLineHeight(fontId); - const int maxWidth = renderer.getScreenWidth() - (margin * 2); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); + const int maxWidth = contentRect.width - (margin * 2); if (!heading.empty()) { safeHeading = renderer.truncatedText(fontId, heading.c_str(), maxWidth, EpdFontFamily::BOLD); @@ -28,7 +29,7 @@ void ConfirmationActivity::onEnter() { if (!safeBody.empty()) totalHeight += lineHeight; if (!safeHeading.empty() && !safeBody.empty()) totalHeight += spacing; - startY = (renderer.getScreenHeight() - totalHeight) / 2; + startY = contentRect.y + (contentRect.height - totalHeight) / 2; requestUpdate(true); } diff --git a/src/activities/util/KeyboardEntryActivity.cpp b/src/activities/util/KeyboardEntryActivity.cpp index b763a2b6..c112537e 100644 --- a/src/activities/util/KeyboardEntryActivity.cpp +++ b/src/activities/util/KeyboardEntryActivity.cpp @@ -192,11 +192,11 @@ void KeyboardEntryActivity::loop() { void KeyboardEntryActivity::render(RenderLock&&) { renderer.clearScreen(); - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, true); - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, title.c_str()); + GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight}, + title.c_str()); // Draw input field const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); @@ -221,11 +221,13 @@ void KeyboardEntryActivity::render(RenderLock&&) { while (true) { std::string lineText = displayText.substr(lineStartIdx, lineEndIdx - lineStartIdx); textWidth = renderer.getTextWidth(UI_12_FONT_ID, lineText.c_str()); - if (textWidth <= pageWidth - 2 * metrics.contentSidePadding) { + if (textWidth <= contentRect.width - 2 * metrics.contentSidePadding) { if (metrics.keyboardCenteredText) { - renderer.drawCenteredText(UI_12_FONT_ID, inputStartY + inputHeight, lineText.c_str()); + const int centeredX = contentRect.x + (contentRect.width - textWidth) / 2; + renderer.drawText(UI_12_FONT_ID, centeredX, inputStartY + inputHeight, lineText.c_str()); } else { - renderer.drawText(UI_12_FONT_ID, metrics.contentSidePadding, inputStartY + inputHeight, lineText.c_str()); + renderer.drawText(UI_12_FONT_ID, contentRect.x + metrics.contentSidePadding, inputStartY + inputHeight, + lineText.c_str()); } if (lineEndIdx == displayText.length()) { break; @@ -239,11 +241,11 @@ void KeyboardEntryActivity::render(RenderLock&&) { } } - GUI.drawTextField(renderer, Rect{0, inputStartY, pageWidth, inputHeight}, textWidth); + GUI.drawTextField(renderer, Rect{contentRect.x, inputStartY, contentRect.width, inputHeight}, textWidth); // Draw keyboard - use compact spacing to fit 5 rows on screen const int keyboardStartY = metrics.keyboardBottomAligned - ? pageHeight - metrics.buttonHintsHeight - metrics.verticalSpacing - + ? contentRect.y + contentRect.height - metrics.verticalSpacing - (metrics.keyboardKeyHeight + metrics.keyboardKeySpacing) * NUM_ROWS : inputStartY + inputHeight + metrics.verticalSpacing * 4; const int keyWidth = metrics.keyboardKeyWidth; @@ -252,9 +254,9 @@ void KeyboardEntryActivity::render(RenderLock&&) { const char* const* layout = shiftState ? keyboardShift : keyboard; - // Calculate left margin to center the longest row (13 keys) + // Calculate left margin to center the longest row (13 keys) within the content area const int maxRowWidth = KEYS_PER_ROW * (keyWidth + keySpacing); - const int leftMargin = (pageWidth - maxRowWidth) / 2; + const int leftMargin = contentRect.x + (contentRect.width - maxRowWidth) / 2; for (int row = 0; row < NUM_ROWS; row++) { const int rowY = keyboardStartY + row * (keyHeight + keySpacing); diff --git a/src/components/UITheme.cpp b/src/components/UITheme.cpp index 57e49484..212e5f0d 100644 --- a/src/components/UITheme.cpp +++ b/src/components/UITheme.cpp @@ -51,6 +51,7 @@ void UITheme::setTheme(CrossPointSettings::UI_THEME type) { int UITheme::getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints, bool hasSubtitle) { const ThemeMetrics& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = getContentRect(renderer, hasButtonHints, /*hasSideHints=*/false); int reservedHeight = metrics.topPadding; if (hasHeader) { reservedHeight += metrics.headerHeight + metrics.verticalSpacing; @@ -59,13 +60,43 @@ int UITheme::getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader reservedHeight += metrics.tabBarHeight; } if (hasButtonHints) { - reservedHeight += metrics.verticalSpacing + metrics.buttonHintsHeight; + reservedHeight += metrics.verticalSpacing; } - const int availableHeight = renderer.getScreenHeight() - reservedHeight; + const int availableHeight = contentRect.height - reservedHeight; int rowHeight = hasSubtitle ? metrics.listWithSubtitleRowHeight : metrics.listRowHeight; return availableHeight / rowHeight; } +Rect UITheme::getContentRect(const GfxRenderer& renderer, bool hasBottomHints, bool hasSideHints) { + const ThemeMetrics& metrics = UITheme::getInstance().getMetrics(); + const int bh = hasBottomHints ? metrics.buttonHintsHeight : 0; + const int sw = hasSideHints ? metrics.sideButtonHintsWidth : 0; + + int top = 0, right = 0, bottom = 0, left = 0; + switch (renderer.getOrientation()) { + case GfxRenderer::Portrait: + bottom = bh; + right = sw; + break; + case GfxRenderer::PortraitInverted: + top = bh; + left = sw; + break; + case GfxRenderer::LandscapeClockwise: + left = bh; + bottom = sw; + break; + case GfxRenderer::LandscapeCounterClockwise: + right = bh; + top = sw; + break; + } + + const int w = renderer.getScreenWidth(); + const int h = renderer.getScreenHeight(); + return Rect{left, top, w - left - right, h - top - bottom}; +} + std::string UITheme::getCoverThumbPath(std::string coverBmpPath, int coverHeight) { size_t pos = coverBmpPath.find("[HEIGHT]", 0); if (pos != std::string::npos) { diff --git a/src/components/UITheme.h b/src/components/UITheme.h index daa1ec45..e45c743d 100644 --- a/src/components/UITheme.h +++ b/src/components/UITheme.h @@ -20,6 +20,10 @@ class UITheme { void setTheme(CrossPointSettings::UI_THEME type); static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints, bool hasSubtitle); + // Returns the drawable content Rect accounting for screen orientation and visible button hints. + // Bottom hints occupy the physical bottom edge; side hints occupy the physical right edge. + // The mapping to logical edges is orientation-dependent. + static Rect getContentRect(const GfxRenderer& renderer, bool hasBottomHints, bool hasSideHints); static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight); static UIIcon getFileIcon(const std::string& filename); static int getStatusBarHeight(); diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index ad97f6b1..84de1454 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -273,9 +273,9 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, rect.width - (totalPages > 1 ? (LyraMetrics::values.scrollBarWidth + LyraMetrics::values.scrollBarRightOffset) : 1); if (selectedIndex >= 0) { - renderer.fillRoundedRect(LyraMetrics::values.contentSidePadding, rect.y + selectedIndex % pageItems * rowHeight, - contentWidth - LyraMetrics::values.contentSidePadding * 2, rowHeight, cornerRadius, - Color::LightGray); + renderer.fillRoundedRect( + rect.x + LyraMetrics::values.contentSidePadding, rect.y + selectedIndex % pageItems * rowHeight, + contentWidth - LyraMetrics::values.contentSidePadding * 2, rowHeight, cornerRadius, Color::LightGray); } int textX = rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection; @@ -336,7 +336,7 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, if (!valueText.empty()) { if (i == selectedIndex && highlightValue) { renderer.fillRoundedRect( - contentWidth - LyraMetrics::values.contentSidePadding - hPaddingInSelection - valueWidth, itemY, + rect.x + contentWidth - LyraMetrics::values.contentSidePadding - hPaddingInSelection - valueWidth, itemY, valueWidth + hPaddingInSelection, rowHeight, cornerRadius, Color::Black); }