diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index b6623899..bcc1a20b 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -229,6 +229,9 @@ STR_EXAMPLE_BOOK: "Book Title" STR_PREVIEW: "Preview" STR_TITLE: "Title" STR_BATTERY: "Battery" +STR_XTC_STATUS_BAR: "XTC Status Bar" +STR_BOTTOM: "Bottom" +STR_TOP: "Top" STR_UI_THEME: "UI Theme" STR_THEME_CLASSIC: "Classic" STR_THEME_LYRA: "Lyra" diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 01d7cc56..cf5c695d 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -57,6 +57,12 @@ class CrossPointSettings { STATUS_BAR_PROGRESS_BAR_THICKNESS_COUNT }; enum STATUS_BAR_TITLE { BOOK_TITLE = 0, CHAPTER_TITLE = 1, HIDE_TITLE = 2, STATUS_BAR_TITLE_COUNT }; + enum XTC_STATUS_BAR_MODE { + XTC_STATUS_BAR_HIDE = 0, + XTC_STATUS_BAR_BOTTOM = 1, + XTC_STATUS_BAR_TOP = 2, + XTC_STATUS_BAR_MODE_COUNT + }; enum ORIENTATION { PORTRAIT = 0, // 480x800 logical coordinates (current default) @@ -161,6 +167,7 @@ class CrossPointSettings { uint8_t statusBarProgressBarThickness = PROGRESS_BAR_NORMAL; uint8_t statusBarTitle = CHAPTER_TITLE; uint8_t statusBarBattery = 1; + uint8_t xtcStatusBarMode = XTC_STATUS_BAR_HIDE; // Text rendering settings uint8_t extraParagraphSpacing = 1; uint8_t textAntiAliasing = 1; diff --git a/src/SettingsList.h b/src/SettingsList.h index bbe9a774..cf2d02ed 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -131,6 +131,9 @@ inline const std::vector& getSettingsList() { StrId::STR_CUSTOMISE_STATUS_BAR), SettingInfo::Toggle(StrId::STR_BATTERY, &CrossPointSettings::statusBarBattery, "statusBarBattery", StrId::STR_CUSTOMISE_STATUS_BAR), + SettingInfo::Enum(StrId::STR_XTC_STATUS_BAR, &CrossPointSettings::xtcStatusBarMode, + {StrId::STR_HIDE, StrId::STR_BOTTOM, StrId::STR_TOP}, "xtcStatusBarMode", + StrId::STR_CUSTOMISE_STATUS_BAR), }; // Only show tilt page turn setting when the QMI8658 IMU is present (X3) if (halTiltSensor.isAvailable()) { diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index 8e904fb3..897d3f52 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include "CrossPointSettings.h" #include "CrossPointState.h" #include "MappedInputManager.h" @@ -131,6 +133,76 @@ void XtcReaderActivity::render(RenderLock&&) { saveProgress(); } +XtcReaderActivity::StatusBarInfo XtcReaderActivity::getStatusBarInfo() const { + const int bookPageCount = static_cast(xtc->getPageCount()); + const int bookPage = static_cast(currentPage) + 1; + std::string title = + SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::BOOK_TITLE ? xtc->getTitle() : ""; + + if (!xtc->hasChapters()) { + return StatusBarInfo{bookPage, bookPageCount, std::move(title)}; + } + + const auto& chapters = xtc->getChapters(); + const auto chapterIt = std::find_if(chapters.begin(), chapters.end(), [this](const xtc::ChapterInfo& chapter) { + return currentPage >= chapter.startPage && currentPage <= chapter.endPage; + }); + + if (chapterIt == chapters.end() || chapterIt->endPage < chapterIt->startPage) { + return StatusBarInfo{bookPage, bookPageCount, std::move(title)}; + } + + if (SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::CHAPTER_TITLE) { + title = chapterIt->name.empty() ? tr(STR_UNNAMED) : chapterIt->name; + } + + return StatusBarInfo{static_cast(currentPage - chapterIt->startPage) + 1, + static_cast(chapterIt->endPage - chapterIt->startPage) + 1, std::move(title)}; +} + +void XtcReaderActivity::renderStatusBarOverlay(const StatusBarOverlayPosition position) const { + const bool drawBottom = SETTINGS.xtcStatusBarMode == CrossPointSettings::XTC_STATUS_BAR_MODE::XTC_STATUS_BAR_BOTTOM && + position == StatusBarOverlayPosition::Bottom; + const bool drawTop = SETTINGS.xtcStatusBarMode == CrossPointSettings::XTC_STATUS_BAR_MODE::XTC_STATUS_BAR_TOP && + position == StatusBarOverlayPosition::Top; + if (!drawBottom && !drawTop) { + return; + } + + const int statusBarHeight = UITheme::getInstance().getStatusBarHeight(); + if (statusBarHeight <= 0) { + return; + } + + int orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft; + renderer.getOrientedViewableTRBL(&orientedMarginTop, &orientedMarginRight, &orientedMarginBottom, + &orientedMarginLeft); + + int clearY; + int paddingBottom = 0; + if (position == StatusBarOverlayPosition::Bottom) { + clearY = renderer.getScreenHeight() - orientedMarginBottom - statusBarHeight - 4; + if (clearY < 0) { + clearY = 0; + } + } else { + clearY = orientedMarginTop; + paddingBottom = renderer.getScreenHeight() - statusBarHeight - orientedMarginBottom - orientedMarginTop - 4; + } + const int clearHeight = position == StatusBarOverlayPosition::Bottom + ? renderer.getScreenHeight() - orientedMarginBottom - clearY + : statusBarHeight + 4; + if (clearHeight > 0) { + renderer.fillRect(0, clearY, renderer.getScreenWidth(), clearHeight, false); + } + + const int pageCount = static_cast(xtc->getPageCount()); + const int displayPage = static_cast(currentPage) + 1; + const float progress = pageCount > 0 ? (static_cast(displayPage) * 100.0f) / pageCount : 0.0f; + const auto pageInfo = getStatusBarInfo(); + GUI.drawStatusBar(renderer, progress, pageInfo.currentPage, pageInfo.pageCount, pageInfo.title, paddingBottom); +} + void XtcReaderActivity::renderPage() { const uint16_t pageWidth = xtc->getPageWidth(); const uint16_t pageHeight = xtc->getPageHeight(); @@ -291,7 +363,11 @@ void XtcReaderActivity::renderPage() { free(pageBuffer); - // XTC pages already have status bar pre-rendered, no need to add our own + if (SETTINGS.xtcStatusBarMode == CrossPointSettings::XTC_STATUS_BAR_MODE::XTC_STATUS_BAR_TOP) { + renderStatusBarOverlay(StatusBarOverlayPosition::Top); + } else { + renderStatusBarOverlay(StatusBarOverlayPosition::Bottom); + } ReaderUtils::displayWithRefreshCycle(renderer, pagesUntilFullRefresh); diff --git a/src/activities/reader/XtcReaderActivity.h b/src/activities/reader/XtcReaderActivity.h index 282e8d2c..f020b0b0 100644 --- a/src/activities/reader/XtcReaderActivity.h +++ b/src/activities/reader/XtcReaderActivity.h @@ -9,6 +9,9 @@ #include +#include +#include + #include "activities/Activity.h" class XtcReaderActivity final : public Activity { @@ -17,7 +20,16 @@ class XtcReaderActivity final : public Activity { uint32_t currentPage = 0; int pagesUntilFullRefresh = 0; + enum class StatusBarOverlayPosition { Bottom, Top }; + struct StatusBarInfo { + int currentPage; + int pageCount; + std::string title; + }; + void renderPage(); + void renderStatusBarOverlay(StatusBarOverlayPosition position) const; + StatusBarInfo getStatusBarInfo() const; void saveProgress() const; void loadProgress(); diff --git a/src/activities/settings/StatusBarSettingsActivity.cpp b/src/activities/settings/StatusBarSettingsActivity.cpp index 6ff0b34d..f62ce4d4 100644 --- a/src/activities/settings/StatusBarSettingsActivity.cpp +++ b/src/activities/settings/StatusBarSettingsActivity.cpp @@ -11,13 +11,14 @@ #include "fontIds.h" namespace { -constexpr int MENU_ITEMS = 6; +constexpr int MENU_ITEMS = 7; const StrId menuNames[MENU_ITEMS] = {StrId::STR_CHAPTER_PAGE_COUNT, StrId::STR_BOOK_PROGRESS_PERCENTAGE, StrId::STR_PROGRESS_BAR, StrId::STR_PROGRESS_BAR_THICKNESS, StrId::STR_TITLE, - StrId::STR_BATTERY}; + StrId::STR_BATTERY, + StrId::STR_XTC_STATUS_BAR}; constexpr int PROGRESS_BAR_ITEMS = 3; const StrId progressBarNames[PROGRESS_BAR_ITEMS] = {StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE}; @@ -28,6 +29,9 @@ const StrId progressBarThicknessNames[PROGRESS_BAR_THICKNESS_ITEMS] = { constexpr int TITLE_ITEMS = 3; const StrId titleNames[TITLE_ITEMS] = {StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE}; +constexpr int XTC_STATUS_BAR_ITEMS = 3; +const StrId xtcStatusBarNames[XTC_STATUS_BAR_ITEMS] = {StrId::STR_HIDE, StrId::STR_BOTTOM, StrId::STR_TOP}; + const int widthMargin = 10; const int verticalPreviewPadding = 50; const int verticalPreviewTextPadding = 40; @@ -51,6 +55,10 @@ void StatusBarSettingsActivity::onEnter() { SETTINGS.statusBarTitle = CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE; } + if (SETTINGS.xtcStatusBarMode >= XTC_STATUS_BAR_ITEMS) { + SETTINGS.xtcStatusBarMode = CrossPointSettings::XTC_STATUS_BAR_MODE::XTC_STATUS_BAR_HIDE; + } + requestUpdate(); } @@ -110,6 +118,9 @@ void StatusBarSettingsActivity::handleSelection() { } else if (selectedIndex == 5) { // Show Battery SETTINGS.statusBarBattery = (SETTINGS.statusBarBattery + 1) % 2; + } else if (selectedIndex == 6) { + // XTC Status Bar + SETTINGS.xtcStatusBarMode = (SETTINGS.xtcStatusBarMode + 1) % XTC_STATUS_BAR_ITEMS; } SETTINGS.saveToFile(); } @@ -143,6 +154,8 @@ void StatusBarSettingsActivity::render(RenderLock&&) { return I18N.get(titleNames[SETTINGS.statusBarTitle]); } else if (index == 5) { return SETTINGS.statusBarBattery ? tr(STR_SHOW) : tr(STR_HIDE); + } else if (index == 6) { + return I18N.get(xtcStatusBarNames[SETTINGS.xtcStatusBarMode]); } else { return tr(STR_HIDE); }