mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 22:57:50 +03:00
refactor: Update popup dimensions and styles; add rounded rectangle drawing functions
This commit is contained in:
parent
d418948a76
commit
1facf55fbd
@ -10,9 +10,6 @@
|
|||||||
const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"};
|
const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"};
|
||||||
constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]);
|
constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]);
|
||||||
|
|
||||||
// Minimum file size (in bytes) to show progress bar - smaller chapters don't benefit from it
|
|
||||||
constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB
|
|
||||||
|
|
||||||
const char* BLOCK_TAGS[] = {"p", "li", "div", "br", "blockquote"};
|
const char* BLOCK_TAGS[] = {"p", "li", "div", "br", "blockquote"};
|
||||||
constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]);
|
constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]);
|
||||||
|
|
||||||
@ -305,7 +302,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
|||||||
// Update progress (call every 10% change to avoid too frequent updates)
|
// Update progress (call every 10% change to avoid too frequent updates)
|
||||||
// Only show progress for larger chapters where rendering overhead is worth it
|
// Only show progress for larger chapters where rendering overhead is worth it
|
||||||
bytesRead += len;
|
bytesRead += len;
|
||||||
if (progressFn && totalSize >= MIN_SIZE_FOR_PROGRESS) {
|
if (progressFn && totalSize >= ChapterHtmlSlimParser::MIN_SIZE_FOR_PROGRESS) {
|
||||||
const int progress = static_cast<int>((bytesRead * 100) / totalSize);
|
const int progress = static_cast<int>((bytesRead * 100) / totalSize);
|
||||||
if (lastProgress / 10 != progress / 10) {
|
if (lastProgress / 10 != progress / 10) {
|
||||||
lastProgress = progress;
|
lastProgress = progress;
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <expat.h>
|
#include <expat.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -15,6 +16,11 @@ class GfxRenderer;
|
|||||||
#define MAX_WORD_SIZE 200
|
#define MAX_WORD_SIZE 200
|
||||||
|
|
||||||
class ChapterHtmlSlimParser {
|
class ChapterHtmlSlimParser {
|
||||||
|
public:
|
||||||
|
// Minimum file size (in bytes) to show progress bar - smaller chapters don't benefit from it.
|
||||||
|
static constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB
|
||||||
|
|
||||||
|
private:
|
||||||
const std::string& filepath;
|
const std::string& filepath;
|
||||||
GfxRenderer& renderer;
|
GfxRenderer& renderer;
|
||||||
std::function<void(std::unique_ptr<Page>)> completePageFn;
|
std::function<void(std::unique_ptr<Page>)> completePageFn;
|
||||||
|
|||||||
@ -2,6 +2,19 @@
|
|||||||
|
|
||||||
#include <Utf8.h>
|
#include <Utf8.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int clampRoundedRadius(const int width, const int height, int radius) {
|
||||||
|
if (radius <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const int maxRadius = (std::min(width, height) - 1) / 2;
|
||||||
|
if (maxRadius <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return radius > maxRadius ? maxRadius : radius;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); }
|
void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); }
|
||||||
|
|
||||||
void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int* rotatedY) const {
|
void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int* rotatedY) const {
|
||||||
@ -138,6 +151,158 @@ void GfxRenderer::drawRect(const int x, const int y, const int width, const int
|
|||||||
drawLine(x, y, x, y + height - 1, state);
|
drawLine(x, y, x, y + height - 1, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GfxRenderer::drawRoundedRect(const int x, const int y, const int width, const int height, int radius,
|
||||||
|
const bool state) const {
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
radius = clampRoundedRadius(width, height, radius);
|
||||||
|
if (radius <= 0) {
|
||||||
|
drawRect(x, y, width, height, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int right = x + width - 1;
|
||||||
|
const int bottom = y + height - 1;
|
||||||
|
const int x0 = x + radius;
|
||||||
|
const int x1 = right - radius;
|
||||||
|
const int y0 = y + radius;
|
||||||
|
const int y1 = bottom - radius;
|
||||||
|
|
||||||
|
if (x0 <= x1) {
|
||||||
|
drawLine(x0, y, x1, y, state);
|
||||||
|
drawLine(x0, bottom, x1, bottom, state);
|
||||||
|
}
|
||||||
|
if (y0 <= y1) {
|
||||||
|
drawLine(x, y0, x, y1, state);
|
||||||
|
drawLine(right, y0, right, y1, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int cxLeft = x + radius;
|
||||||
|
const int cxRight = right - radius;
|
||||||
|
const int cyTop = y + radius;
|
||||||
|
const int cyBottom = bottom - radius;
|
||||||
|
|
||||||
|
auto plotCornerPoints = [&](const int offsetX, const int offsetY) {
|
||||||
|
drawPixel(cxLeft - offsetX, cyTop - offsetY, state);
|
||||||
|
drawPixel(cxRight + offsetX, cyTop - offsetY, state);
|
||||||
|
drawPixel(cxRight + offsetX, cyBottom + offsetY, state);
|
||||||
|
drawPixel(cxLeft - offsetX, cyBottom + offsetY, state);
|
||||||
|
|
||||||
|
if (offsetX == offsetY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawPixel(cxLeft - offsetY, cyTop - offsetX, state);
|
||||||
|
drawPixel(cxRight + offsetY, cyTop - offsetX, state);
|
||||||
|
drawPixel(cxRight + offsetY, cyBottom + offsetX, state);
|
||||||
|
drawPixel(cxLeft - offsetY, cyBottom + offsetX, state);
|
||||||
|
};
|
||||||
|
|
||||||
|
int f = 1 - radius;
|
||||||
|
int ddF_x = 1;
|
||||||
|
int ddF_y = -2 * radius;
|
||||||
|
int offsetX = 0;
|
||||||
|
int offsetY = radius;
|
||||||
|
|
||||||
|
while (offsetX <= offsetY) {
|
||||||
|
plotCornerPoints(offsetX, offsetY);
|
||||||
|
if (f >= 0) {
|
||||||
|
offsetY--;
|
||||||
|
ddF_y += 2;
|
||||||
|
f += ddF_y;
|
||||||
|
}
|
||||||
|
offsetX++;
|
||||||
|
ddF_x += 2;
|
||||||
|
f += ddF_x;
|
||||||
|
plotCornerPoints(offsetX, offsetY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GfxRenderer::fillRoundedRect(const int x, const int y, const int width, const int height, int radius,
|
||||||
|
const bool state) const {
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
radius = clampRoundedRadius(width, height, radius);
|
||||||
|
if (radius <= 0) {
|
||||||
|
fillRect(x, y, width, height, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int right = x + width - 1;
|
||||||
|
const int bottom = y + height - 1;
|
||||||
|
const int innerTop = y + radius;
|
||||||
|
const int innerBottom = bottom - radius;
|
||||||
|
|
||||||
|
if (innerBottom >= innerTop) {
|
||||||
|
fillRect(x, innerTop, width, innerBottom - innerTop + 1, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
int f = 1 - radius;
|
||||||
|
int ddF_x = 1;
|
||||||
|
int ddF_y = -2 * radius;
|
||||||
|
int offsetX = 0;
|
||||||
|
int offsetY = radius;
|
||||||
|
|
||||||
|
while (offsetX <= offsetY) {
|
||||||
|
int left = x + radius - offsetX;
|
||||||
|
int rightSpan = right - radius + offsetX;
|
||||||
|
int topY = innerTop - offsetY;
|
||||||
|
int bottomY = innerBottom + offsetY;
|
||||||
|
drawLine(left, topY, rightSpan, topY, state);
|
||||||
|
drawLine(left, bottomY, rightSpan, bottomY, state);
|
||||||
|
|
||||||
|
if (offsetX != offsetY) {
|
||||||
|
left = x + radius - offsetY;
|
||||||
|
rightSpan = right - radius + offsetY;
|
||||||
|
topY = innerTop - offsetX;
|
||||||
|
bottomY = innerBottom + offsetX;
|
||||||
|
drawLine(left, topY, rightSpan, topY, state);
|
||||||
|
drawLine(left, bottomY, rightSpan, bottomY, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f >= 0) {
|
||||||
|
offsetY--;
|
||||||
|
ddF_y += 2;
|
||||||
|
f += ddF_y;
|
||||||
|
}
|
||||||
|
offsetX++;
|
||||||
|
ddF_x += 2;
|
||||||
|
f += ddF_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GfxRenderer::drawRoundedRectFrame(const int x, const int y, const int width, const int height, int radius,
|
||||||
|
int thickness, const bool frameState, const bool fillState) const {
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int maxThickness = (std::min(width, height) - 1) / 2;
|
||||||
|
thickness = std::min(thickness, maxThickness);
|
||||||
|
if (thickness <= 0) {
|
||||||
|
fillRoundedRect(x, y, width, height, radius, fillState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillRoundedRect(x, y, width, height, radius, frameState);
|
||||||
|
|
||||||
|
const int inset = thickness * 2;
|
||||||
|
const int innerX = x + thickness;
|
||||||
|
const int innerY = y + thickness;
|
||||||
|
const int innerW = width - inset;
|
||||||
|
const int innerH = height - inset;
|
||||||
|
if (innerW <= 0 || innerH <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int innerRadius = radius - thickness;
|
||||||
|
fillRoundedRect(innerX, innerY, innerW, innerH, innerRadius, fillState);
|
||||||
|
}
|
||||||
|
|
||||||
void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const {
|
void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const {
|
||||||
for (int fillY = y; fillY < y + height; fillY++) {
|
for (int fillY = y; fillY < y + height; fillY++) {
|
||||||
drawLine(x, fillY, x + width - 1, fillY, state);
|
drawLine(x, fillY, x + width - 1, fillY, state);
|
||||||
|
|||||||
@ -64,6 +64,10 @@ class GfxRenderer {
|
|||||||
void drawPixel(int x, int y, bool state = true) const;
|
void drawPixel(int x, int y, bool state = true) const;
|
||||||
void drawLine(int x1, int y1, int x2, int y2, bool state = true) const;
|
void drawLine(int x1, int y1, int x2, int y2, bool state = true) const;
|
||||||
void drawRect(int x, int y, int width, int height, bool state = true) const;
|
void drawRect(int x, int y, int width, int height, bool state = true) const;
|
||||||
|
void drawRoundedRect(int x, int y, int width, int height, int radius, bool state = true) const;
|
||||||
|
void fillRoundedRect(int x, int y, int width, int height, int radius, bool state = true) const;
|
||||||
|
void drawRoundedRectFrame(int x, int y, int width, int height, int radius, int thickness = 1,
|
||||||
|
bool frameState = true, bool fillState = false) const;
|
||||||
void fillRect(int x, int y, int width, int height, bool state = true) const;
|
void fillRect(int x, int y, int width, int height, bool state = true) const;
|
||||||
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const;
|
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const;
|
||||||
void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, float cropX = 0,
|
void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, float cropX = 0,
|
||||||
|
|||||||
@ -42,22 +42,23 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left,
|
|||||||
renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4);
|
renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message, const int y,
|
ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message) {
|
||||||
const int minWidth, const int minHeight) {
|
constexpr int margin = 12;
|
||||||
constexpr int margin = 15;
|
constexpr int frameThickness = 4;
|
||||||
|
constexpr int frameInset = frameThickness / 2;
|
||||||
|
constexpr int frameRadius = 8;
|
||||||
|
constexpr int bottomOffset = 20;
|
||||||
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD);
|
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD);
|
||||||
const int contentWidth = std::max(textWidth, minWidth);
|
const int w = std::max(textWidth, POPUP_DEFAULT_MIN_WIDTH) + margin * 2;
|
||||||
const int contentHeight = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2;
|
const int h = std::max(renderer.getLineHeight(UI_12_FONT_ID) + margin * 2, POPUP_DEFAULT_MIN_HEIGHT);
|
||||||
const int w = contentWidth + margin * 2 + 50;
|
const int x = std::max(0, (renderer.getScreenWidth() - w) / 2);
|
||||||
// const int x = (renderer.getScreenWidth() - w) / 2;
|
const int y = std::max(0, renderer.getScreenHeight() - h - margin - bottomOffset);
|
||||||
const int x = renderer.getScreenWidth() - w - margin;
|
|
||||||
const int h = std::max(contentHeight, minHeight);
|
|
||||||
renderer.fillRect(x - 2, y - 2, w + 4, h + 4, true);
|
|
||||||
renderer.fillRect(x + 2, y + 2, w - 4, h - 4, false);
|
|
||||||
|
|
||||||
const int textX = x + margin + (contentWidth - textWidth) / 2;
|
renderer.drawRoundedRectFrame(x - frameInset, y - frameInset, w + frameInset * 2, h + frameInset * 2,
|
||||||
// renderer.drawText(UI_12_FONT_ID, textX, y + margin + 4, message, true, EpdFontFamily::BOLD);
|
frameRadius, frameThickness, true, false);
|
||||||
renderer.drawText(NOTOSANS_18_FONT_ID, textX, y + margin + 4, message, true, EpdFontFamily::BOLD);
|
|
||||||
|
const int textX = x + (w - textWidth) / 2;
|
||||||
|
renderer.drawText(UI_12_FONT_ID, textX, y + margin - 2, message, true, EpdFontFamily::BOLD);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return {x, y, w, h};
|
return {x, y, w, h};
|
||||||
}
|
}
|
||||||
@ -66,7 +67,7 @@ void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const Popu
|
|||||||
constexpr int barWidth = POPUP_DEFAULT_MIN_WIDTH;
|
constexpr int barWidth = POPUP_DEFAULT_MIN_WIDTH;
|
||||||
constexpr int barHeight = POPUP_DEFAULT_BAR_HEIGHT;
|
constexpr int barHeight = POPUP_DEFAULT_BAR_HEIGHT;
|
||||||
const int barX = layout.x + (layout.width - barWidth) / 2;
|
const int barX = layout.x + (layout.width - barWidth) / 2;
|
||||||
const int barY = layout.y + layout.height - 15;
|
const int barY = layout.y + layout.height - 13;
|
||||||
|
|
||||||
int fillWidth = barWidth * progress / 100;
|
int fillWidth = barWidth * progress / 100;
|
||||||
if (fillWidth < 0) {
|
if (fillWidth < 0) {
|
||||||
|
|||||||
@ -13,8 +13,8 @@ struct TabInfo {
|
|||||||
|
|
||||||
class ScreenComponents {
|
class ScreenComponents {
|
||||||
public:
|
public:
|
||||||
static constexpr int POPUP_DEFAULT_MIN_WIDTH = 200;
|
static constexpr int POPUP_DEFAULT_MIN_WIDTH = 130;
|
||||||
static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 72;
|
static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 50;
|
||||||
static constexpr int POPUP_DEFAULT_BAR_HEIGHT = 4;
|
static constexpr int POPUP_DEFAULT_BAR_HEIGHT = 4;
|
||||||
|
|
||||||
struct PopupLayout {
|
struct PopupLayout {
|
||||||
@ -26,8 +26,7 @@ class ScreenComponents {
|
|||||||
|
|
||||||
static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true);
|
static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true);
|
||||||
|
|
||||||
static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message, int y = 125,
|
static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message);
|
||||||
int minWidth = POPUP_DEFAULT_MIN_WIDTH, int minHeight = POPUP_DEFAULT_MIN_HEIGHT);
|
|
||||||
|
|
||||||
static void fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, int progress);
|
static void fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, int progress);
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,10 @@
|
|||||||
void SleepActivity::onEnter() {
|
void SleepActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
|
|
||||||
ScreenComponents::drawPopup(renderer, "Entering Sleep...");
|
ScreenComponents::drawPopup(renderer, "Sleeping");
|
||||||
|
|
||||||
|
// debug delay to see sleep screen
|
||||||
|
delay(5000);
|
||||||
|
|
||||||
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) {
|
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) {
|
||||||
return renderBlankSleepScreen();
|
return renderBlankSleepScreen();
|
||||||
|
|||||||
@ -286,7 +286,7 @@ void EpubReaderActivity::renderScreen() {
|
|||||||
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
||||||
|
|
||||||
pagesUntilFullRefresh = 0;
|
pagesUntilFullRefresh = 0;
|
||||||
const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing...");
|
const auto popupLayout = ScreenComponents::drawPopup(renderer, "Rendering");
|
||||||
const auto progressCallback = [this, popupLayout](int progress) {
|
const auto progressCallback = [this, popupLayout](int progress) {
|
||||||
ScreenComponents::fillPopupProgress(renderer, popupLayout, progress);
|
ScreenComponents::fillPopupProgress(renderer, popupLayout, progress);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user