119 lines
5.4 KiB
C++
119 lines
5.4 KiB
C++
#include "Lyra3CoversTheme.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <HalStorage.h>
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "RecentBooksStore.h"
|
|
#include "components/UITheme.h"
|
|
#include "components/icons/cover.h"
|
|
#include "fontIds.h"
|
|
|
|
// Internal constants
|
|
namespace {
|
|
constexpr int hPaddingInSelection = 8;
|
|
constexpr int cornerRadius = 6;
|
|
} // namespace
|
|
|
|
void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std::vector<RecentBook>& recentBooks,
|
|
const int selectorIndex, bool& coverRendered, bool& coverBufferStored,
|
|
bool& bufferRestored, std::function<bool()> storeCoverBuffer) const {
|
|
const int tileWidth = (rect.width - 2 * Lyra3CoversMetrics::values.contentSidePadding) / 3;
|
|
const int tileY = rect.y;
|
|
const int titleAreaHeight = renderer.getLineHeight(SMALL_FONT_ID) * 3 + hPaddingInSelection + 5;
|
|
const int coverHeight = std::max(120, rect.height - titleAreaHeight);
|
|
const bool hasContinueReading = !recentBooks.empty();
|
|
|
|
// Draw book card regardless, fill with message based on `hasContinueReading`
|
|
// Draw cover image as background if available (inside the box)
|
|
// Only load from SD on first render, then use stored buffer
|
|
if (hasContinueReading) {
|
|
if (!coverRendered) {
|
|
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;
|
|
int tileX = Lyra3CoversMetrics::values.contentSidePadding + tileWidth * i;
|
|
if (coverPath.empty()) {
|
|
hasCover = false;
|
|
} else {
|
|
const std::string coverBmpPath = UITheme::getCoverThumbPath(coverPath, coverHeight);
|
|
|
|
// First time: load cover from SD and render
|
|
FsFile file;
|
|
if (Storage.openFileForRead("HOME", coverBmpPath, file)) {
|
|
Bitmap bitmap(file);
|
|
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
|
|
const float bitmapHeight = static_cast<float>(bitmap.getHeight());
|
|
const float bitmapWidth = static_cast<float>(bitmap.getWidth());
|
|
const float ratio = bitmapWidth / bitmapHeight;
|
|
const float tileRatio =
|
|
static_cast<float>(tileWidth - 2 * hPaddingInSelection) / static_cast<float>(coverHeight);
|
|
const float cropX = 1.0f - (tileRatio / ratio);
|
|
|
|
renderer.drawBitmap(bitmap, tileX + hPaddingInSelection, tileY + hPaddingInSelection,
|
|
tileWidth - 2 * hPaddingInSelection, coverHeight, cropX);
|
|
} else {
|
|
hasCover = false;
|
|
}
|
|
file.close();
|
|
} else {
|
|
hasCover = false;
|
|
}
|
|
}
|
|
// Draw either way
|
|
renderer.drawRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection, tileWidth - 2 * hPaddingInSelection,
|
|
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);
|
|
}
|
|
}
|
|
|
|
coverBufferStored = storeCoverBuffer();
|
|
coverRendered = coverBufferStored; // Only consider it rendered if we successfully stored the buffer
|
|
}
|
|
|
|
for (int i = 0; i < std::min(static_cast<int>(recentBooks.size()), Lyra3CoversMetrics::values.homeRecentBooksCount);
|
|
i++) {
|
|
bool bookSelected = (selectorIndex == i);
|
|
|
|
int tileX = Lyra3CoversMetrics::values.contentSidePadding + tileWidth * i;
|
|
|
|
const int maxLineWidth = tileWidth - 2 * hPaddingInSelection;
|
|
|
|
auto titleLines = renderer.wrappedText(SMALL_FONT_ID, recentBooks[i].title.c_str(), maxLineWidth, 3);
|
|
|
|
const int titleLineHeight = renderer.getLineHeight(SMALL_FONT_ID);
|
|
const int dynamicBlockHeight = static_cast<int>(titleLines.size()) * titleLineHeight;
|
|
// Add a little padding below the text inside the selection box just like the top padding (5 + hPaddingSelection)
|
|
const int dynamicTitleBoxHeight = dynamicBlockHeight + hPaddingInSelection + 5;
|
|
|
|
if (bookSelected) {
|
|
// Draw selection box
|
|
renderer.fillRoundedRect(tileX, tileY, tileWidth, hPaddingInSelection, cornerRadius, true, true, false, false,
|
|
Color::LightGray);
|
|
renderer.fillRectDither(tileX, tileY + hPaddingInSelection, hPaddingInSelection, coverHeight, Color::LightGray);
|
|
renderer.fillRectDither(tileX + tileWidth - hPaddingInSelection, tileY + hPaddingInSelection,
|
|
hPaddingInSelection, coverHeight, Color::LightGray);
|
|
renderer.fillRoundedRect(tileX, tileY + coverHeight + hPaddingInSelection, tileWidth, dynamicTitleBoxHeight,
|
|
cornerRadius, false, false, true, true, Color::LightGray);
|
|
}
|
|
|
|
int currentY = tileY + coverHeight + hPaddingInSelection + 5;
|
|
for (const auto& line : titleLines) {
|
|
renderer.drawText(SMALL_FONT_ID, tileX + hPaddingInSelection, currentY, line.c_str(), true);
|
|
currentY += titleLineHeight;
|
|
}
|
|
}
|
|
} else {
|
|
drawEmptyRecents(renderer, rect);
|
|
}
|
|
}
|