Refactor ui content rect
This commit is contained in:
@@ -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<size_t>(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<size_t>(selectorIndex));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]); });
|
||||
}
|
||||
|
||||
@@ -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<int>(menuItems.size()), selectorIndex - recentBooks.size(),
|
||||
[&menuItems](int index) { return std::string(menuItems[index]); },
|
||||
[&menuIcons](int index) { return menuIcons[index]; });
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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), "", "", "");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<int>(MENU_ITEM_COUNT), selectedIndex,
|
||||
[](int index) { return std::string(I18N.get(menuItems[index])); },
|
||||
renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast<int>(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
|
||||
|
||||
@@ -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<int>(networks.size()),
|
||||
renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast<int>(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) : "";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<int>(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), "", "");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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), "", "", "");
|
||||
|
||||
@@ -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<int>(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();
|
||||
}
|
||||
|
||||
@@ -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<uint8_t>(index)); }, nullptr, nullptr,
|
||||
[&](int index) {
|
||||
uint8_t assignedButton = tempMapping[static_cast<uint8_t>(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.
|
||||
|
||||
@@ -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<int>(MENU_ITEMS),
|
||||
renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast<int>(MENU_ITEMS),
|
||||
static_cast<int>(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr,
|
||||
nullptr,
|
||||
[this](int index) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<int>(MENU_ITEMS),
|
||||
renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast<int>(MENU_ITEMS),
|
||||
static_cast<int>(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr,
|
||||
nullptr,
|
||||
[this](int index) {
|
||||
|
||||
@@ -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<uint8_t>(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<Language>(SORTED_LANGUAGE_INDICES[index])); },
|
||||
nullptr, nullptr,
|
||||
[this, currentLang](int index) { return SORTED_LANGUAGE_INDICES[index] == currentLang ? tr(STR_SELECTED) : ""; },
|
||||
|
||||
@@ -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<int>(updaterProgress * 100), 100);
|
||||
GUI.drawProgressBar(renderer,
|
||||
Rect{contentRect.x + metrics.contentSidePadding, y,
|
||||
contentRect.width - metrics.contentSidePadding * 2, metrics.progressBarHeight},
|
||||
static_cast<int>(updaterProgress * 100), 100);
|
||||
|
||||
y += metrics.progressBarHeight + metrics.verticalSpacing;
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, y,
|
||||
|
||||
@@ -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<TabInfo> 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) {
|
||||
|
||||
@@ -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<int>(MENU_ITEMS),
|
||||
renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast<int>(MENU_ITEMS),
|
||||
static_cast<int>(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr,
|
||||
nullptr,
|
||||
[this](int index) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user