From 7271c00d355ba8b9a4dd6a6f4fff1a23901d5333 Mon Sep 17 00:00:00 2001 From: Husam Younis Date: Thu, 25 Jun 2026 00:23:23 +0300 Subject: [PATCH] feat: Allow statusbar clock to be on the left (#2359) --- src/CrossPointSettings.h | 4 +- src/SettingsList.h | 5 +- .../settings/StatusBarSettingsActivity.cpp | 11 +++- src/components/UITheme.cpp | 7 +-- src/components/themes/BaseTheme.cpp | 54 ++++++++++++------- 5 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index af274e3c..8a2cf217 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -65,6 +65,8 @@ class CrossPointSettings { XTC_STATUS_BAR_MODE_COUNT }; + enum STATUS_BAR_CLOCK_MODE { STATUS_BAR_CLOCK_HIDE = 0, STATUS_BAR_CLOCK_RIGHT = 1, STATUS_BAR_CLOCK_LEFT = 2 }; + enum ORIENTATION { PORTRAIT = 0, // 480x800 logical coordinates (current default) LANDSCAPE_CW = 1, // 800x480 logical coordinates, rotated 180° (swap top/bottom) @@ -188,7 +190,7 @@ class CrossPointSettings { uint8_t statusBarBattery = 1; uint8_t xtcStatusBarMode = XTC_STATUS_BAR_HIDE; // Clock display in status bar (X3 only, requires DS3231 RTC) - uint8_t statusBarClock = 0; + uint8_t statusBarClock = STATUS_BAR_CLOCK_HIDE; // Clock UTC offset in quarter-hour steps, biased by 48 so it fits in uint8_t. // Value 48 = UTC+0, 0 = UTC-12:00, 104 = UTC+14:00. // Quarter-hour granularity supports oddball zones like Nepal (+5:45) and Chatham (+12:45). diff --git a/src/SettingsList.h b/src/SettingsList.h index a60303bc..695e8eec 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -244,8 +244,9 @@ inline std::vector getSettingsList(const SdCardFontRegistry* regist StrId::STR_CUSTOMISE_STATUS_BAR), // Clock entries (web settings only; device UI uses ClockOffsetActivity for the offset). // Range 0..104 = quarter-hour steps from UTC-12:00 to UTC+14:00, biased by 48. - SettingInfo::Toggle(StrId::STR_CLOCK, &CrossPointSettings::statusBarClock, "statusBarClock", - StrId::STR_CUSTOMISE_STATUS_BAR), + SettingInfo::Enum(StrId::STR_CLOCK, &CrossPointSettings::statusBarClock, + {StrId::STR_HIDE, StrId::STR_DIR_LEFT, StrId::STR_DIR_RIGHT}, "statusBarClock", + StrId::STR_CUSTOMISE_STATUS_BAR), SettingInfo::Value(StrId::STR_CLOCK_UTC_OFFSET, &CrossPointSettings::clockUtcOffsetQ, {0, 104, 1}, "clockUtcOffsetQ", StrId::STR_CUSTOMISE_STATUS_BAR), SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat, diff --git a/src/activities/settings/StatusBarSettingsActivity.cpp b/src/activities/settings/StatusBarSettingsActivity.cpp index 2df09254..4046fcb1 100644 --- a/src/activities/settings/StatusBarSettingsActivity.cpp +++ b/src/activities/settings/StatusBarSettingsActivity.cpp @@ -77,6 +77,9 @@ const StrId titleNames[TITLE_ITEMS] = {StrId::STR_BOOK, StrId::STR_CHAPTER, StrI constexpr int XTC_STATUS_BAR_ITEMS = 3; const StrId xtcStatusBarNames[XTC_STATUS_BAR_ITEMS] = {StrId::STR_HIDE, StrId::STR_BOTTOM, StrId::STR_TOP}; +constexpr int STATUS_BAR_CLOCK_ITEMS = 3; +const StrId statusBarClockNames[STATUS_BAR_CLOCK_ITEMS] = {StrId::STR_HIDE, StrId::STR_DIR_RIGHT, StrId::STR_DIR_LEFT}; + const int verticalPreviewPadding = 50; const int verticalPreviewTextPadding = 40; } // namespace @@ -112,6 +115,10 @@ void StatusBarSettingsActivity::onEnter() { SETTINGS.clockFormat = 0; } + if (SETTINGS.statusBarClock >= STATUS_BAR_CLOCK_ITEMS) { + SETTINGS.statusBarClock = CrossPointSettings::STATUS_BAR_CLOCK_MODE::STATUS_BAR_CLOCK_HIDE; + } + requestUpdate(); } @@ -176,7 +183,7 @@ void StatusBarSettingsActivity::handleSelection() { SETTINGS.xtcStatusBarMode = (SETTINGS.xtcStatusBarMode + 1) % XTC_STATUS_BAR_ITEMS; break; case ITEM_CLOCK: - SETTINGS.statusBarClock = (SETTINGS.statusBarClock + 1) % 2; + SETTINGS.statusBarClock = (SETTINGS.statusBarClock + 1) % STATUS_BAR_CLOCK_ITEMS; break; case ITEM_CLOCK_FORMAT: SETTINGS.clockFormat = (SETTINGS.clockFormat + 1) % CLOCK_FORMAT_ITEMS; @@ -225,7 +232,7 @@ void StatusBarSettingsActivity::render(RenderLock&&) { case ITEM_XTC_STATUS_BAR: return I18N.get(xtcStatusBarNames[SETTINGS.xtcStatusBarMode]); case ITEM_CLOCK: - return SETTINGS.statusBarClock ? tr(STR_SHOW) : tr(STR_HIDE); + return I18N.get(statusBarClockNames[SETTINGS.statusBarClock]); case ITEM_CLOCK_FORMAT: { const uint8_t fmt = SETTINGS.clockFormat < CLOCK_FORMAT_ITEMS ? SETTINGS.clockFormat : 0; return std::string(I18N.get(clockFormatNames[fmt])); diff --git a/src/components/UITheme.cpp b/src/components/UITheme.cpp index 7de7f960..9220d43c 100644 --- a/src/components/UITheme.cpp +++ b/src/components/UITheme.cpp @@ -131,9 +131,10 @@ int UITheme::getStatusBarHeight() { const ThemeMetrics& metrics = UITheme::getInstance().getMetrics(); // Add status bar margin - const bool showStatusBar = SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage || - SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE || - SETTINGS.statusBarBattery; + const bool showStatusBar = + SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage || + SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE || SETTINGS.statusBarBattery || + SETTINGS.statusBarClock != CrossPointSettings::STATUS_BAR_CLOCK_MODE::STATUS_BAR_CLOCK_HIDE; const bool showProgressBar = SETTINGS.statusBarProgressBar != CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS; return (showStatusBar ? (metrics.statusBarVerticalMargin) : 0) + diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index ff7e927d..15b2d569 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -759,7 +759,11 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c // Draw Progress Text const auto screenHeight = renderer.getScreenHeight(); auto textY = screenHeight - UITheme::getInstance().getStatusBarHeight() - orientedMarginBottom - paddingBottom - 4; - int progressTextWidth = 0; + + const int leftClusterX = metrics.statusBarHorizontalMargin + orientedMarginLeft + 1; + const int rightClusterX = renderer.getScreenWidth() - metrics.statusBarHorizontalMargin - orientedMarginRight; + int leftClusterWidth = 0; + int rightClusterWidth = 0; if (SETTINGS.statusBarBookProgressPercentage || SETTINGS.statusBarChapterPageCount) { // Right aligned text for progress counter @@ -773,11 +777,10 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c snprintf(progressStr, sizeof(progressStr), "%d/%d", currentPage, pageCount); } - progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progressStr); - renderer.drawText( - SMALL_FONT_ID, - renderer.getScreenWidth() - metrics.statusBarHorizontalMargin - orientedMarginRight - progressTextWidth, textY, - progressStr); + int progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progressStr); + renderer.drawText(SMALL_FONT_ID, rightClusterX - progressTextWidth, textY, progressStr); + + rightClusterWidth += progressTextWidth; } // Draw Progress Bar @@ -801,34 +804,46 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c } // Draw Bookmark - const int leftClusterX = metrics.statusBarHorizontalMargin + orientedMarginLeft + 1; - const bool showBookmarkIcon = showStatusBarTextLane && isPageBookmarked; - const int bookmarkReserveWidth = showBookmarkIcon ? (bookmarkStatusIconWidth + bookmarkStatusIconGap) : 0; - if (showBookmarkIcon) { + if (showStatusBarTextLane && isPageBookmarked) { const int bookmarkY = textY + 5; drawBookmarkStatusIcon(renderer, leftClusterX, bookmarkY); + leftClusterWidth += bookmarkStatusIconWidth + bookmarkStatusIconGap; } // Draw Battery const bool showBatteryPercentage = SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER; - int leftClusterWidth = bookmarkReserveWidth; + if (SETTINGS.statusBarBattery) { GUI.drawBatteryLeft(renderer, - Rect{leftClusterX + bookmarkReserveWidth, textY, metrics.batteryWidth, metrics.batteryHeight}, + Rect{leftClusterX + leftClusterWidth, textY, metrics.batteryWidth, metrics.batteryHeight}, showBatteryPercentage); - leftClusterWidth += showBatteryPercentage ? 50 : 20; + int batteryWidth = metrics.batteryWidth; + + if (showBatteryPercentage) { + const uint16_t percentage = powerManager.getBatteryPercentage(); + // width of icon + spacing + text for layout purposes + batteryWidth += + batteryPercentSpacing + renderer.getTextWidth(SMALL_FONT_ID, (std::to_string(percentage) + "%").c_str()); + } + + leftClusterWidth += batteryWidth; } // Draw Clock (X3 only — DS3231 RTC) - int clockTextWidth = 0; if (SETTINGS.statusBarClock && halClock.isAvailable()) { char timeBuf[9]; if (halClock.formatTime(timeBuf, sizeof(timeBuf), SETTINGS.clockUtcOffsetQ, SETTINGS.clockFormat == 1)) { - clockTextWidth = renderer.getTextWidth(SMALL_FONT_ID, timeBuf); - // Position to the left of the progress text (with a small gap) - const int clockX = renderer.getScreenWidth() - metrics.statusBarHorizontalMargin - orientedMarginRight - - progressTextWidth - (progressTextWidth > 0 ? 10 : 0) - clockTextWidth; + int clockTextWidth = renderer.getTextWidth(SMALL_FONT_ID, timeBuf); + int clockX = 0; + // Position to the left or right of the progress text (with a small gap) + if (SETTINGS.statusBarClock == CrossPointSettings::STATUS_BAR_CLOCK_LEFT) { + clockX = leftClusterX + leftClusterWidth + (leftClusterWidth > 0 ? 10 : 0); + leftClusterWidth += clockTextWidth + 10; + } else if (SETTINGS.statusBarClock == CrossPointSettings::STATUS_BAR_CLOCK_RIGHT) { + clockX = rightClusterX - rightClusterWidth - (rightClusterWidth > 0 ? 10 : 0) - clockTextWidth; + rightClusterWidth += clockTextWidth + 10; + } renderer.drawText(SMALL_FONT_ID, clockX, textY, timeBuf); } } @@ -842,8 +857,7 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c renderer.getScreenWidth() - (metrics.statusBarHorizontalMargin * 2) - orientedMarginLeft - orientedMarginRight; const int titleMarginLeft = leftClusterWidth + 30; - const int clockReserve = clockTextWidth > 0 ? (clockTextWidth + 10) : 0; - const int titleMarginRight = progressTextWidth + clockReserve + 30; + const int titleMarginRight = rightClusterWidth + 30; // Attempt to center title on the screen, but if title is too wide then later we will center it within the // available space.