mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 22:57:50 +03:00
## Summary * **What is the goal of this PR?** This PR introduces a new "Status Bar" mode that displays a visual progress bar at the bottom of the screen, providing readers with a graphical indication of their position within the book. * **What changes are included?** * **Settings**: Updated SettingsActivity to expand the "Status Bar" configuration with a new option: Full w/ Progress Bar. * **EPUB Reader**: Modified EpubReaderActivity to calculate the global book progress and render a progress bar at the bottom of the viewable area when the new setting is active. * **TXT Reader**: Modified TxtReaderActivity to implement similar progress bar rendering logic based on the current page and total page count. ## Additional Context * The progress bar is rendered with a height of 4 pixels at the very bottom of the screen (adjusted for margins). * The feature reuses the existing renderStatusBar logic but conditionally draws the bar instead of (or in addition to) other elements depending on the specific implementation details in each reader. * Renamed existing 'Full' mode to 'Full w/ Percentage' * Added new 'Full w/ Progress Bar' option <img src="https://github.com/user-attachments/assets/08c0dd49-c64c-4d4d-9fbb-f576c02d05d9" width="500"> --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**NO**_
147 lines
6.0 KiB
C++
147 lines
6.0 KiB
C++
#include "ScreenComponents.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
#include "Battery.h"
|
|
#include "fontIds.h"
|
|
|
|
void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, const int top,
|
|
const bool showPercentage) {
|
|
// Left aligned battery icon and percentage
|
|
const uint16_t percentage = battery.readPercentage();
|
|
const auto percentageText = showPercentage ? std::to_string(percentage) + "%" : "";
|
|
renderer.drawText(SMALL_FONT_ID, left + 20, top, percentageText.c_str());
|
|
|
|
// 1 column on left, 2 columns on right, 5 columns of battery body
|
|
constexpr int batteryWidth = 15;
|
|
constexpr int batteryHeight = 12;
|
|
const int x = left;
|
|
const int y = top + 6;
|
|
|
|
// Top line
|
|
renderer.drawLine(x + 1, y, x + batteryWidth - 3, y);
|
|
// Bottom line
|
|
renderer.drawLine(x + 1, y + batteryHeight - 1, x + batteryWidth - 3, y + batteryHeight - 1);
|
|
// Left line
|
|
renderer.drawLine(x, y + 1, x, y + batteryHeight - 2);
|
|
// Battery end
|
|
renderer.drawLine(x + batteryWidth - 2, y + 1, x + batteryWidth - 2, y + batteryHeight - 2);
|
|
renderer.drawPixel(x + batteryWidth - 1, y + 3);
|
|
renderer.drawPixel(x + batteryWidth - 1, y + batteryHeight - 4);
|
|
renderer.drawLine(x + batteryWidth - 0, y + 4, x + batteryWidth - 0, y + batteryHeight - 5);
|
|
|
|
// The +1 is to round up, so that we always fill at least one pixel
|
|
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
|
|
if (filledWidth > batteryWidth - 5) {
|
|
filledWidth = batteryWidth - 5; // Ensure we don't overflow
|
|
}
|
|
|
|
renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4);
|
|
}
|
|
|
|
void ScreenComponents::drawBookProgressBar(const GfxRenderer& renderer, const size_t bookProgress) {
|
|
int vieweableMarginTop, vieweableMarginRight, vieweableMarginBottom, vieweableMarginLeft;
|
|
renderer.getOrientedViewableTRBL(&vieweableMarginTop, &vieweableMarginRight, &vieweableMarginBottom,
|
|
&vieweableMarginLeft);
|
|
|
|
const int progressBarMaxWidth = renderer.getScreenWidth() - vieweableMarginLeft - vieweableMarginRight;
|
|
const int progressBarY = renderer.getScreenHeight() - vieweableMarginBottom - BOOK_PROGRESS_BAR_HEIGHT;
|
|
const int barWidth = progressBarMaxWidth * bookProgress / 100;
|
|
renderer.fillRect(vieweableMarginLeft, progressBarY, barWidth, BOOK_PROGRESS_BAR_HEIGHT, true);
|
|
}
|
|
|
|
int ScreenComponents::drawTabBar(const GfxRenderer& renderer, const int y, const std::vector<TabInfo>& tabs) {
|
|
constexpr int tabPadding = 20; // Horizontal padding between tabs
|
|
constexpr int leftMargin = 20; // Left margin for first tab
|
|
constexpr int underlineHeight = 2; // Height of selection underline
|
|
constexpr int underlineGap = 4; // Gap between text and underline
|
|
|
|
const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID);
|
|
const int tabBarHeight = lineHeight + underlineGap + underlineHeight;
|
|
|
|
int currentX = leftMargin;
|
|
|
|
for (const auto& tab : tabs) {
|
|
const int textWidth =
|
|
renderer.getTextWidth(UI_12_FONT_ID, tab.label, tab.selected ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR);
|
|
|
|
// Draw tab label
|
|
renderer.drawText(UI_12_FONT_ID, currentX, y, tab.label, true,
|
|
tab.selected ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR);
|
|
|
|
// Draw underline for selected tab
|
|
if (tab.selected) {
|
|
renderer.fillRect(currentX, y + lineHeight + underlineGap, textWidth, underlineHeight);
|
|
}
|
|
|
|
currentX += textWidth + tabPadding;
|
|
}
|
|
|
|
return tabBarHeight;
|
|
}
|
|
|
|
void ScreenComponents::drawScrollIndicator(const GfxRenderer& renderer, const int currentPage, const int totalPages,
|
|
const int contentTop, const int contentHeight) {
|
|
if (totalPages <= 1) {
|
|
return; // No need for indicator if only one page
|
|
}
|
|
|
|
const int screenWidth = renderer.getScreenWidth();
|
|
constexpr int indicatorWidth = 20;
|
|
constexpr int arrowSize = 6;
|
|
constexpr int margin = 15; // Offset from right edge
|
|
|
|
const int centerX = screenWidth - indicatorWidth / 2 - margin;
|
|
const int indicatorTop = contentTop + 60; // Offset to avoid overlapping side button hints
|
|
const int indicatorBottom = contentTop + contentHeight - 30;
|
|
|
|
// Draw up arrow at top (^) - narrow point at top, wide base at bottom
|
|
for (int i = 0; i < arrowSize; ++i) {
|
|
const int lineWidth = 1 + i * 2;
|
|
const int startX = centerX - i;
|
|
renderer.drawLine(startX, indicatorTop + i, startX + lineWidth - 1, indicatorTop + i);
|
|
}
|
|
|
|
// Draw down arrow at bottom (v) - wide base at top, narrow point at bottom
|
|
for (int i = 0; i < arrowSize; ++i) {
|
|
const int lineWidth = 1 + (arrowSize - 1 - i) * 2;
|
|
const int startX = centerX - (arrowSize - 1 - i);
|
|
renderer.drawLine(startX, indicatorBottom - arrowSize + 1 + i, startX + lineWidth - 1,
|
|
indicatorBottom - arrowSize + 1 + i);
|
|
}
|
|
|
|
// Draw page fraction in the middle (e.g., "1/3")
|
|
const std::string pageText = std::to_string(currentPage) + "/" + std::to_string(totalPages);
|
|
const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, pageText.c_str());
|
|
const int textX = centerX - textWidth / 2;
|
|
const int textY = (indicatorTop + indicatorBottom) / 2 - renderer.getLineHeight(SMALL_FONT_ID) / 2;
|
|
|
|
renderer.drawText(SMALL_FONT_ID, textX, textY, pageText.c_str());
|
|
}
|
|
|
|
void ScreenComponents::drawProgressBar(const GfxRenderer& renderer, const int x, const int y, const int width,
|
|
const int height, const size_t current, const size_t total) {
|
|
if (total == 0) {
|
|
return;
|
|
}
|
|
|
|
// Use 64-bit arithmetic to avoid overflow for large files
|
|
const int percent = static_cast<int>((static_cast<uint64_t>(current) * 100) / total);
|
|
|
|
// Draw outline
|
|
renderer.drawRect(x, y, width, height);
|
|
|
|
// Draw filled portion
|
|
const int fillWidth = (width - 4) * percent / 100;
|
|
if (fillWidth > 0) {
|
|
renderer.fillRect(x + 2, y + 2, fillWidth, height - 4);
|
|
}
|
|
|
|
// Draw percentage text centered below bar
|
|
const std::string percentText = std::to_string(percent) + "%";
|
|
renderer.drawCenteredText(UI_10_FONT_ID, y + height + 15, percentText.c_str());
|
|
}
|