From c14dc4c1095275d122b7f45b40cd5a05218990dd Mon Sep 17 00:00:00 2001 From: Justin Mitchell Date: Sun, 15 Feb 2026 21:14:52 -0500 Subject: [PATCH] Fix scanning text in calibre and customize buttons for x3 --- .../network/CalibreConnectActivity.cpp | 5 + src/components/themes/BaseTheme.cpp | 111 ++++++++++++------ src/components/themes/lyra/LyraTheme.cpp | 70 +++++++---- 3 files changed, 124 insertions(+), 62 deletions(-) diff --git a/src/activities/network/CalibreConnectActivity.cpp b/src/activities/network/CalibreConnectActivity.cpp index dd5e14bbd..192ffa925 100644 --- a/src/activities/network/CalibreConnectActivity.cpp +++ b/src/activities/network/CalibreConnectActivity.cpp @@ -201,6 +201,11 @@ void CalibreConnectActivity::displayTaskLoop() { } void CalibreConnectActivity::render() const { + // Don't render when WifiSelectionActivity subactivity is active + if (state == CalibreConnectState::WIFI_SELECTION) { + return; + } + if (state == CalibreConnectState::SERVER_RUNNING) { renderer.clearScreen(); renderServerRunning(); diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index cacee60b4..7264f1f5a 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -88,7 +88,12 @@ void BaseTheme::drawButtonHints(GfxRenderer& renderer, const char* btn1, const c constexpr int buttonHeight = BaseMetrics::values.buttonHintsHeight; constexpr int buttonY = BaseMetrics::values.buttonHintsHeight; // Distance from bottom constexpr int textYOffset = 7; // Distance from top of button to text baseline - constexpr int buttonPositions[] = {25, 130, 245, 350}; + + // X3 has wider screen in portrait (528 vs 480), use more spacing + // X3: buttons at 38, 154, 268, 384 (10px between pairs, 8px between groups) + constexpr int x4ButtonPositions[] = {25, 130, 245, 350}; + constexpr int x3ButtonPositions[] = {38, 154, 268, 384}; + const int* buttonPositions = gpio.deviceIsX3() ? x3ButtonPositions : x4ButtonPositions; const char* labels[] = {btn1, btn2, btn3, btn4}; for (int i = 0; i < 4; i++) { @@ -110,50 +115,78 @@ void BaseTheme::drawSideButtonHints(const GfxRenderer& renderer, const char* top const int screenWidth = renderer.getScreenWidth(); constexpr int buttonWidth = BaseMetrics::values.sideButtonHintsWidth; // Width on screen (height when rotated) constexpr int buttonHeight = 80; // Height on screen (width when rotated) - constexpr int buttonX = 4; // Distance from right edge - // Position for the button group - buttons share a border so they're adjacent - constexpr int topButtonY = 345; // Top button position + constexpr int buttonMargin = 4; // Distance from edge - const char* labels[] = {topBtn, bottomBtn}; + if (gpio.deviceIsX3()) { + // X3 layout: Up on left side, Down on right side, positioned higher + constexpr int x3ButtonY = 155; // Higher position for X3 - // Draw the shared border for both buttons as one unit - const int x = screenWidth - buttonX - buttonWidth; + // Draw Up button on left side (topBtn) + if (topBtn != nullptr && topBtn[0] != '\0') { + const int leftX = buttonMargin; + renderer.drawRect(leftX, x3ButtonY, buttonWidth, buttonHeight); - // Draw top button outline (3 sides, bottom open) - if (topBtn != nullptr && topBtn[0] != '\0') { - renderer.drawLine(x, topButtonY, x + buttonWidth - 1, topButtonY); // Top - renderer.drawLine(x, topButtonY, x, topButtonY + buttonHeight - 1); // Left - renderer.drawLine(x + buttonWidth - 1, topButtonY, x + buttonWidth - 1, topButtonY + buttonHeight - 1); // Right - } - - // Draw shared middle border - if ((topBtn != nullptr && topBtn[0] != '\0') || (bottomBtn != nullptr && bottomBtn[0] != '\0')) { - renderer.drawLine(x, topButtonY + buttonHeight, x + buttonWidth - 1, topButtonY + buttonHeight); // Shared border - } - - // Draw bottom button outline (3 sides, top is shared) - if (bottomBtn != nullptr && bottomBtn[0] != '\0') { - renderer.drawLine(x, topButtonY + buttonHeight, x, topButtonY + 2 * buttonHeight - 1); // Left - renderer.drawLine(x + buttonWidth - 1, topButtonY + buttonHeight, x + buttonWidth - 1, - topButtonY + 2 * buttonHeight - 1); // Right - renderer.drawLine(x, topButtonY + 2 * buttonHeight - 1, x + buttonWidth - 1, - topButtonY + 2 * buttonHeight - 1); // Bottom - } - - // Draw text for each button - for (int i = 0; i < 2; i++) { - if (labels[i] != nullptr && labels[i][0] != '\0') { - const int y = topButtonY + i * buttonHeight; - - // Draw rotated text centered in the button - const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, labels[i]); + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, topBtn); const int textHeight = renderer.getTextHeight(SMALL_FONT_ID); + const int textX = leftX + (buttonWidth - textHeight) / 2; + const int textY = x3ButtonY + (buttonHeight + textWidth) / 2; + renderer.drawTextRotated90CW(SMALL_FONT_ID, textX, textY, topBtn); + } - // Center the rotated text in the button - const int textX = x + (buttonWidth - textHeight) / 2; - const int textY = y + (buttonHeight + textWidth) / 2; + // Draw Down button on right side (bottomBtn) + if (bottomBtn != nullptr && bottomBtn[0] != '\0') { + const int rightX = screenWidth - buttonMargin - buttonWidth; + renderer.drawRect(rightX, x3ButtonY, buttonWidth, buttonHeight); - renderer.drawTextRotated90CW(SMALL_FONT_ID, textX, textY, labels[i]); + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, bottomBtn); + const int textHeight = renderer.getTextHeight(SMALL_FONT_ID); + const int textX = rightX + (buttonWidth - textHeight) / 2; + const int textY = x3ButtonY + (buttonHeight + textWidth) / 2; + renderer.drawTextRotated90CW(SMALL_FONT_ID, textX, textY, bottomBtn); + } + } else { + // X4 layout: Both buttons stacked on right side + constexpr int topButtonY = 345; // Top button position + const int x = screenWidth - buttonMargin - buttonWidth; + + const char* labels[] = {topBtn, bottomBtn}; + + // Draw top button outline (3 sides, bottom open) + if (topBtn != nullptr && topBtn[0] != '\0') { + renderer.drawLine(x, topButtonY, x + buttonWidth - 1, topButtonY); // Top + renderer.drawLine(x, topButtonY, x, topButtonY + buttonHeight - 1); // Left + renderer.drawLine(x + buttonWidth - 1, topButtonY, x + buttonWidth - 1, topButtonY + buttonHeight - 1); // Right + } + + // Draw shared middle border + if ((topBtn != nullptr && topBtn[0] != '\0') || (bottomBtn != nullptr && bottomBtn[0] != '\0')) { + renderer.drawLine(x, topButtonY + buttonHeight, x + buttonWidth - 1, topButtonY + buttonHeight); // Shared border + } + + // Draw bottom button outline (3 sides, top is shared) + if (bottomBtn != nullptr && bottomBtn[0] != '\0') { + renderer.drawLine(x, topButtonY + buttonHeight, x, topButtonY + 2 * buttonHeight - 1); // Left + renderer.drawLine(x + buttonWidth - 1, topButtonY + buttonHeight, x + buttonWidth - 1, + topButtonY + 2 * buttonHeight - 1); // Right + renderer.drawLine(x, topButtonY + 2 * buttonHeight - 1, x + buttonWidth - 1, + topButtonY + 2 * buttonHeight - 1); // Bottom + } + + // Draw text for each button + for (int i = 0; i < 2; i++) { + if (labels[i] != nullptr && labels[i][0] != '\0') { + const int y = topButtonY + i * buttonHeight; + + // Draw rotated text centered in the button + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, labels[i]); + const int textHeight = renderer.getTextHeight(SMALL_FONT_ID); + + // Center the rotated text in the button + const int textX = x + (buttonWidth - textHeight) / 2; + const int textY = y + (buttonHeight + textWidth) / 2; + + renderer.drawTextRotated90CW(SMALL_FONT_ID, textX, textY, labels[i]); + } } } } diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index 5e6edad2b..ed66f6697 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -199,7 +199,13 @@ void LyraTheme::drawButtonHints(GfxRenderer& renderer, const char* btn1, const c constexpr int buttonHeight = LyraMetrics::values.buttonHintsHeight; constexpr int buttonY = LyraMetrics::values.buttonHintsHeight; // Distance from bottom constexpr int textYOffset = 7; // Distance from top of button to text baseline - constexpr int buttonPositions[] = {58, 146, 254, 342}; + + // X3 has wider screen in portrait (528 vs 480), use more spacing + // X4: buttons at 58, 146, 254, 342 (8px between pairs, 28px between groups) + // X3: buttons at 65, 157, 291, 383 (12px between pairs, 54px between groups) + constexpr int x4ButtonPositions[] = {58, 146, 254, 342}; + constexpr int x3ButtonPositions[] = {65, 157, 291, 383}; + const int* buttonPositions = gpio.deviceIsX3() ? x3ButtonPositions : x4ButtonPositions; const char* labels[] = {btn1, btn2, btn3, btn4}; for (int i = 0; i < 4; i++) { @@ -225,34 +231,52 @@ void LyraTheme::drawSideButtonHints(const GfxRenderer& renderer, const char* top const int screenWidth = renderer.getScreenWidth(); constexpr int buttonWidth = LyraMetrics::values.sideButtonHintsWidth; // Width on screen (height when rotated) constexpr int buttonHeight = 78; // Height on screen (width when rotated) - // Position for the button group - buttons share a border so they're adjacent + constexpr int buttonMargin = 0; - const char* labels[] = {topBtn, bottomBtn}; + if (gpio.deviceIsX3()) { + // X3 layout: Up on left side, Down on right side, positioned higher + constexpr int x3ButtonY = 155; - // Draw the shared border for both buttons as one unit - const int x = screenWidth - buttonWidth; + // Draw Up button on left side (topBtn) - rounded corners on right (facing inward) + if (topBtn != nullptr && topBtn[0] != '\0') { + renderer.drawRoundedRect(buttonMargin, x3ButtonY, buttonWidth, buttonHeight, 1, cornerRadius, false, true, false, + true, true); + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, topBtn); + renderer.drawTextRotated90CW(SMALL_FONT_ID, buttonMargin, x3ButtonY + (buttonHeight + textWidth) / 2, topBtn); + } - // Draw top button outline - if (topBtn != nullptr && topBtn[0] != '\0') { - renderer.drawRoundedRect(x, topHintButtonY, buttonWidth, buttonHeight, 1, cornerRadius, true, false, true, false, - true); - } + // Draw Down button on right side (bottomBtn) + if (bottomBtn != nullptr && bottomBtn[0] != '\0') { + const int rightX = screenWidth - buttonWidth; + renderer.drawRoundedRect(rightX, x3ButtonY, buttonWidth, buttonHeight, 1, cornerRadius, true, false, true, false, + true); + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, bottomBtn); + renderer.drawTextRotated90CW(SMALL_FONT_ID, rightX, x3ButtonY + (buttonHeight + textWidth) / 2, bottomBtn); + } + } else { + // X4 layout: Both buttons stacked on right side + const char* labels[] = {topBtn, bottomBtn}; + const int x = screenWidth - buttonWidth; - // Draw bottom button outline - if (bottomBtn != nullptr && bottomBtn[0] != '\0') { - renderer.drawRoundedRect(x, topHintButtonY + buttonHeight + 5, buttonWidth, buttonHeight, 1, cornerRadius, true, - false, true, false, true); - } + // Draw top button outline + if (topBtn != nullptr && topBtn[0] != '\0') { + renderer.drawRoundedRect(x, topHintButtonY, buttonWidth, buttonHeight, 1, cornerRadius, true, false, true, false, + true); + } - // Draw text for each button - for (int i = 0; i < 2; i++) { - if (labels[i] != nullptr && labels[i][0] != '\0') { - const int y = topHintButtonY + (i * buttonHeight + 5); + // Draw bottom button outline + if (bottomBtn != nullptr && bottomBtn[0] != '\0') { + renderer.drawRoundedRect(x, topHintButtonY + buttonHeight + 5, buttonWidth, buttonHeight, 1, cornerRadius, true, + false, true, false, true); + } - // Draw rotated text centered in the button - const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, labels[i]); - - renderer.drawTextRotated90CW(SMALL_FONT_ID, x, y + (buttonHeight + textWidth) / 2, labels[i]); + // Draw text for each button + for (int i = 0; i < 2; i++) { + if (labels[i] != nullptr && labels[i][0] != '\0') { + const int y = topHintButtonY + (i * buttonHeight + 5); + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, labels[i]); + renderer.drawTextRotated90CW(SMALL_FONT_ID, x, y + (buttonHeight + textWidth) / 2, labels[i]); + } } } }