Merge pull request #239 from jpirnay/refactor-thumbs-pending
refactor: thumbs pending indicator
This commit is contained in:
@@ -377,6 +377,7 @@ void RecentBooksActivity::renderGridCell(int index, bool selected, int cellX, in
|
||||
if (!book.coverBmpPath.empty()) {
|
||||
const std::string thumbPath = gridThumbPath(book.coverBmpPath, tw, th);
|
||||
FsFile file;
|
||||
bool thumbDrawn = false;
|
||||
if (Storage.openFileForRead("RBA", thumbPath, file)) {
|
||||
Bitmap bmp(file);
|
||||
if (bmp.parseHeaders() == BmpReaderError::Ok) {
|
||||
@@ -399,10 +400,19 @@ void RecentBooksActivity::renderGridCell(int index, bool selected, int cellX, in
|
||||
// shows through in the surrounding space.
|
||||
renderer.fillRect(cellX + offsetX, cellY + offsetY, rendW, rendH, false);
|
||||
renderer.drawBitmap1Bit(bmp, cellX + offsetX, cellY + offsetY, rendW, rendH);
|
||||
thumbDrawn = true;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
if (!thumbDrawn) {
|
||||
// Thumbnail not yet generated — clear interior and show loading label
|
||||
renderer.fillRect(cellX + 1, cellY + 1, tw - 2, th - 2, false);
|
||||
const char* loadingText = tr(STR_LOADING);
|
||||
const int textW = renderer.getTextWidth(SMALL_FONT_ID, loadingText);
|
||||
const int textH = renderer.getLineHeight(SMALL_FONT_ID);
|
||||
renderer.drawText(SMALL_FONT_ID, cellX + (tw - textW) / 2, cellY + (th - textH) / 2, loadingText, true);
|
||||
}
|
||||
} else {
|
||||
// No cover — clear the whole interior so the placeholder looks clean.
|
||||
renderer.fillRect(cellX + 1, cellY + 1, tw - 2, th - 2, false);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalStorage.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -32,10 +33,12 @@ void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con
|
||||
// Only load from SD on first render, then use stored buffer
|
||||
if (hasContinueReading) {
|
||||
if (!coverRendered) {
|
||||
bool anyPending = false;
|
||||
for (int i = 0;
|
||||
i < std::min(static_cast<int>(recentBooks.size()), Lyra3CoversMetrics::values.homeRecentBooksCount); i++) {
|
||||
std::string coverPath = recentBooks[i].coverBmpPath;
|
||||
bool hasCover = true;
|
||||
bool tilePending = false;
|
||||
int tileX = Lyra3CoversMetrics::values.contentSidePadding + tileWidth * i;
|
||||
if (coverPath.empty()) {
|
||||
hasCover = false;
|
||||
@@ -62,6 +65,8 @@ void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con
|
||||
file.close();
|
||||
} else {
|
||||
hasCover = false;
|
||||
tilePending = true; // path exists but BMP not ready yet
|
||||
anyPending = true;
|
||||
}
|
||||
}
|
||||
// Draw either way
|
||||
@@ -69,15 +74,29 @@ void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con
|
||||
coverHeight, true);
|
||||
|
||||
if (!hasCover) {
|
||||
// Render empty cover
|
||||
renderer.fillRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection + (coverHeight / 3),
|
||||
tileWidth - 2 * hPaddingInSelection, 2 * coverHeight / 3, true);
|
||||
renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32);
|
||||
if (tilePending) {
|
||||
// Cover is being generated — show a loading label centred in the tile
|
||||
const char* loadingText = tr(STR_LOADING);
|
||||
const int textW = renderer.getTextWidth(SMALL_FONT_ID, loadingText);
|
||||
const int textH = renderer.getLineHeight(SMALL_FONT_ID);
|
||||
renderer.drawText(SMALL_FONT_ID,
|
||||
tileX + hPaddingInSelection + (tileWidth - 2 * hPaddingInSelection - textW) / 2,
|
||||
tileY + hPaddingInSelection + (coverHeight - textH) / 2, loadingText, true);
|
||||
} else {
|
||||
// No cover at all — render empty cover placeholder
|
||||
renderer.fillRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection + (coverHeight / 3),
|
||||
tileWidth - 2 * hPaddingInSelection, 2 * coverHeight / 3, true);
|
||||
renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
coverBufferStored = storeCoverBuffer();
|
||||
coverRendered = coverBufferStored; // Only consider it rendered if we successfully stored the buffer
|
||||
// Only cache the frame buffer once all tiles are definitively resolved.
|
||||
// If any cover is still being generated we keep coverRendered=false so the next render will retry.
|
||||
if (!anyPending) {
|
||||
coverBufferStored = storeCoverBuffer();
|
||||
coverRendered = coverBufferStored;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < std::min(static_cast<int>(recentBooks.size()), Lyra3CoversMetrics::values.homeRecentBooksCount);
|
||||
|
||||
@@ -317,6 +317,8 @@ void LyraCarouselTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect,
|
||||
|
||||
// Returns true if a book exists at bookIdx (cover image or placeholder drawn).
|
||||
// Returns false only when the slot has no book — caller skips the border too.
|
||||
// anyPending is set to true when a book has a coverBmpPath but the BMP isn't readable yet.
|
||||
bool anyPending = false;
|
||||
auto drawCover = [&](int bookIdx, int x, int y, int maxW, int maxH) -> bool {
|
||||
if (bookIdx < 0 || bookIdx >= bookCount) return false;
|
||||
const RecentBook& book = recentBooks[bookIdx];
|
||||
@@ -324,6 +326,7 @@ void LyraCarouselTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect,
|
||||
const bool roundLeft = (x >= 0);
|
||||
const bool roundRight = (x + maxW <= screenW);
|
||||
bool hasCover = false;
|
||||
bool tilePending = false;
|
||||
if (!book.coverBmpPath.empty()) {
|
||||
const std::string thumbPath = UITheme::getCoverThumbPath(book.coverBmpPath, maxW, maxH);
|
||||
FsFile file;
|
||||
@@ -361,14 +364,25 @@ void LyraCarouselTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect,
|
||||
hasCover = true;
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
tilePending = true; // path exists but BMP not ready yet
|
||||
anyPending = true;
|
||||
}
|
||||
}
|
||||
if (!hasCover) {
|
||||
renderer.drawRoundedRect(x, y, maxW, maxH, 1, kCornerRadius, roundLeft, roundRight, roundLeft, roundRight, true);
|
||||
renderer.fillRoundedRect(x, y + maxH / 3, maxW, 2 * maxH / 3, kCornerRadius, /*roundTopLeft=*/false,
|
||||
/*roundTopRight=*/false, /*roundBottomLeft=*/roundLeft, /*roundBottomRight=*/roundRight,
|
||||
Color::Black);
|
||||
renderer.drawIcon(CoverIcon, x + maxW / 2 - 16, y + 8, 32, 32);
|
||||
if (tilePending) {
|
||||
// Cover is being generated — show a loading label centred in the tile
|
||||
const char* loadingText = tr(STR_LOADING);
|
||||
const int textW = renderer.getTextWidth(SMALL_FONT_ID, loadingText);
|
||||
const int textH = renderer.getLineHeight(SMALL_FONT_ID);
|
||||
renderer.drawText(SMALL_FONT_ID, x + (maxW - textW) / 2, y + (maxH - textH) / 2, loadingText, true);
|
||||
} else {
|
||||
renderer.fillRoundedRect(x, y + maxH / 3, maxW, 2 * maxH / 3, kCornerRadius, /*roundTopLeft=*/false,
|
||||
/*roundTopRight=*/false, /*roundBottomLeft=*/roundLeft,
|
||||
/*roundBottomRight=*/roundRight, Color::Black);
|
||||
renderer.drawIcon(CoverIcon, x + maxW / 2 - 16, y + 8, 32, 32);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -437,8 +451,12 @@ void LyraCarouselTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect,
|
||||
const int titleW = renderer.getTextWidth(kTitleFontId, titleTrunc.c_str());
|
||||
renderer.drawText(kTitleFontId, centerX + (kCenterCoverMaxW - titleW) / 2, titleY, titleTrunc.c_str(), true);
|
||||
|
||||
coverBufferStored = storeCoverBuffer();
|
||||
coverRendered = coverBufferStored;
|
||||
// Only cache the frame buffer once all tiles are definitively resolved.
|
||||
// If any cover is still being generated we keep coverRendered=false so the next render will retry.
|
||||
if (!anyPending) {
|
||||
coverBufferStored = storeCoverBuffer();
|
||||
coverRendered = coverBufferStored;
|
||||
}
|
||||
}
|
||||
|
||||
// Always outline the centre cover at its own edge (white ring sits outside the black line);
|
||||
|
||||
@@ -554,6 +554,7 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
||||
if (!coverRendered) {
|
||||
std::string coverPath = book.coverBmpPath;
|
||||
bool hasCover = true;
|
||||
bool coverPending = false;
|
||||
int tileX = LyraMetrics::values.contentSidePadding;
|
||||
if (coverPath.empty()) {
|
||||
hasCover = false;
|
||||
@@ -572,6 +573,9 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
||||
hasCover = false;
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
hasCover = false;
|
||||
coverPending = true; // path exists but BMP not ready yet
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,14 +583,27 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
||||
renderer.drawRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection, coverWidth, coverHeight, true);
|
||||
|
||||
if (!hasCover) {
|
||||
// Render empty cover
|
||||
renderer.fillRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection + (coverHeight / 3), coverWidth,
|
||||
2 * coverHeight / 3, true);
|
||||
renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32);
|
||||
if (coverPending) {
|
||||
// Cover is being generated — show a loading label centred in the cover area
|
||||
const char* loadingText = tr(STR_LOADING);
|
||||
const int textW = renderer.getTextWidth(SMALL_FONT_ID, loadingText);
|
||||
const int textH = renderer.getLineHeight(SMALL_FONT_ID);
|
||||
renderer.drawText(SMALL_FONT_ID, tileX + hPaddingInSelection + (coverWidth - textW) / 2,
|
||||
tileY + hPaddingInSelection + (coverHeight - textH) / 2, loadingText, true);
|
||||
} else {
|
||||
// No cover at all — render empty cover placeholder
|
||||
renderer.fillRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection + (coverHeight / 3), coverWidth,
|
||||
2 * coverHeight / 3, true);
|
||||
renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32);
|
||||
}
|
||||
}
|
||||
|
||||
coverBufferStored = storeCoverBuffer();
|
||||
coverRendered = coverBufferStored; // Only consider it rendered if we successfully stored the buffer
|
||||
// Only cache the frame buffer once the cover is definitively resolved (loaded or confirmed absent).
|
||||
// If a cover is still being generated we keep coverRendered=false so the next render will retry.
|
||||
if (!coverPending) {
|
||||
coverBufferStored = storeCoverBuffer();
|
||||
coverRendered = coverBufferStored;
|
||||
}
|
||||
}
|
||||
|
||||
bool bookSelected = (selectorIndex == 0);
|
||||
|
||||
Reference in New Issue
Block a user