feat: wrapped text in GfxRender, implemented in themes so far#1141
feat: wrapped text in GfxRender, implemented in themes so far#1141ngxson merged 14 commits intocrosspoint-reader:masterfrom
Conversation
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…emes Address znelson's PR review feedback on commit 10a2678: - Add GfxRenderer::wrappedText() utility that word-wraps text into at most N lines, delegating last-line truncation to truncatedText for correct UTF-8-safe ellipsis handling - Change ellipsis in truncatedText from ASCII '...' to UTF-8 U+2026 (…) - Replace duplicated word-wrap loops in BaseTheme, LyraTheme, and Lyra3CoversTheme with single calls to wrappedText - Replace manual author truncation loops in BaseTheme with truncatedText - Remove now-unused Utf8.h includes from the three theme files
maxLineWidth was only defined inside the removed word-wrap loop. Inline bookWidth - 40 directly and clean up author box-sizing logic.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review infoConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
🧰 Additional context used🧠 Learnings (2)📓 Common learnings📚 Learning: 2026-02-19T12:17:05.421ZApplied to files:
🔇 Additional comments (2)
📝 WalkthroughWalkthroughAdds GfxRenderer::wrappedText(...) for UTF‑8‑safe word wrapping (maxWidth/maxLines), switches truncatedText() to use U+2026 ellipsis, and replaces manual UTF‑8 wrapping/trimming in several theme files; also adjusts a cover tile height metric to accommodate multi-line titles. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/components/themes/BaseTheme.cpp (1)
513-514: Author truncation is performed twice with identical parameters.
truncatedText(UI_10_FONT_ID, lastBookAuthor.c_str(), bookWidth - 40)is called at Line 513 (for box width calculation insideif (coverRendered)) and again at Line 538 (for rendering). Since both use identical parameters, consider hoisting the result above theif (coverRendered)block to avoid redundant text measurement.Proposed refactor
+ std::string truncatedAuthor; + int authorWidth = 0; + if (!lastBookAuthor.empty()) { + truncatedAuthor = renderer.truncatedText(UI_10_FONT_ID, lastBookAuthor.c_str(), bookWidth - 40); + authorWidth = renderer.getTextWidth(UI_10_FONT_ID, truncatedAuthor.c_str()); + } + // If cover image was rendered, draw box behind title and author if (coverRendered) { constexpr int boxPadding = 8; // Calculate the max text width for the box int maxTextWidth = 0; for (const auto& line : lines) { const int lineWidth = renderer.getTextWidth(UI_12_FONT_ID, line.c_str()); if (lineWidth > maxTextWidth) { maxTextWidth = lineWidth; } } if (!lastBookAuthor.empty()) { - const auto truncatedAuthor = renderer.truncatedText(UI_10_FONT_ID, lastBookAuthor.c_str(), bookWidth - 40); - const int authorWidth = renderer.getTextWidth(UI_10_FONT_ID, truncatedAuthor.c_str()); if (authorWidth > maxTextWidth) { maxTextWidth = authorWidth; } } ... } ... if (!lastBookAuthor.empty()) { titleYStart += renderer.getLineHeight(UI_10_FONT_ID) / 2; - auto trimmedAuthor = renderer.truncatedText(UI_10_FONT_ID, lastBookAuthor.c_str(), bookWidth - 40); - renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, trimmedAuthor.c_str(), !bookSelected); + renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, truncatedAuthor.c_str(), !bookSelected); }Also applies to: 538-538
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/BaseTheme.cpp` around lines 513 - 514, Hoist the duplicated author truncation by computing truncatedAuthor once before the coverRendered check and reuse it in both measurement and rendering: call renderer.truncatedText(UI_10_FONT_ID, lastBookAuthor.c_str(), bookWidth - 40) once into a local variable (e.g., truncatedAuthor) above the if (coverRendered) block, then use that same truncatedAuthor for authorWidth via renderer.getTextWidth(UI_10_FONT_ID, truncatedAuthor.c_str()) and later when rendering; update references to avoid calling renderer.truncatedText a second time.lib/GfxRenderer/GfxRenderer.cpp (1)
855-893: Preferstatic_cast<int>over C-style casts.Lines 856 and 892 use
(int)lines.size(). The rest of this PR (e.g., Lyra3CoversTheme.cpp Line 96, BaseTheme.cpp Line 493) consistently usesstatic_cast<int>(...). Consider aligning for consistency.Proposed fix
- if ((int)lines.size() == maxLines - 1) { + if (static_cast<int>(lines.size()) == maxLines - 1) {- if (!currentLine.empty() && (int)lines.size() < maxLines) { + if (!currentLine.empty() && static_cast<int>(lines.size()) < maxLines) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/GfxRenderer/GfxRenderer.cpp` around lines 855 - 893, Replace the two C-style casts of (int)lines.size() in GfxRenderer.cpp with C++-style casts: where the loop checks if ((int)lines.size() == maxLines - 1) and where the final if uses (int)lines.size(), change them to static_cast<int>(lines.size()) so the comparisons use static_cast<int>(lines.size()) == maxLines - 1 and static_cast<int>(lines.size()) < maxLines respectively; update both occurrences to match the project's casting style.src/components/themes/lyra/Lyra3CoversTheme.cpp (1)
26-26: Remove unusedbookTitleHeightvariable.
bookTitleHeightis computed on line 26 but never referenced. The title-box rendering usesdynamicTitleBoxHeight(line 109) instead. Remove the dead code.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/Lyra3CoversTheme.cpp` at line 26, Remove the dead local variable bookTitleHeight declared in Lyra3CoversTheme.cpp (the const int bookTitleHeight = ... line); it is never referenced (rendering uses dynamicTitleBoxHeight), so delete that declaration and any related unused hPaddingInSelection-only computation tied solely to bookTitleHeight to avoid unused-variable warnings; ensure no other code paths reference bookTitleHeight before committing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/GfxRenderer/GfxRenderer.cpp`:
- Around line 878-888: The bug is that when you set currentLine = word inside
the else-branch (where currentLine was non-empty and you flushed it), you never
check whether that single-word "word" itself exceeds maxWidth, so it can later
be pushed untruncated; update the branch in GfxRenderer.cpp so after setting
currentLine = word (or instead of assigning raw word) you measure/truncate the
lone word using truncatedText(fontId, word.c_str(), maxWidth, style) whenever
that word's width exceeds maxWidth, ensuring any carried-over word placed into
currentLine is truncated immediately; keep all other logic (flushing lines,
last-line truncation) intact.
---
Nitpick comments:
In `@lib/GfxRenderer/GfxRenderer.cpp`:
- Around line 855-893: Replace the two C-style casts of (int)lines.size() in
GfxRenderer.cpp with C++-style casts: where the loop checks if
((int)lines.size() == maxLines - 1) and where the final if uses
(int)lines.size(), change them to static_cast<int>(lines.size()) so the
comparisons use static_cast<int>(lines.size()) == maxLines - 1 and
static_cast<int>(lines.size()) < maxLines respectively; update both occurrences
to match the project's casting style.
In `@src/components/themes/BaseTheme.cpp`:
- Around line 513-514: Hoist the duplicated author truncation by computing
truncatedAuthor once before the coverRendered check and reuse it in both
measurement and rendering: call renderer.truncatedText(UI_10_FONT_ID,
lastBookAuthor.c_str(), bookWidth - 40) once into a local variable (e.g.,
truncatedAuthor) above the if (coverRendered) block, then use that same
truncatedAuthor for authorWidth via renderer.getTextWidth(UI_10_FONT_ID,
truncatedAuthor.c_str()) and later when rendering; update references to avoid
calling renderer.truncatedText a second time.
In `@src/components/themes/lyra/Lyra3CoversTheme.cpp`:
- Line 26: Remove the dead local variable bookTitleHeight declared in
Lyra3CoversTheme.cpp (the const int bookTitleHeight = ... line); it is never
referenced (rendering uses dynamicTitleBoxHeight), so delete that declaration
and any related unused hPaddingInSelection-only computation tied solely to
bookTitleHeight to avoid unused-variable warnings; ensure no other code paths
reference bookTitleHeight before committing.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
lib/GfxRenderer/GfxRenderer.cpplib/GfxRenderer/GfxRenderer.hsrc/components/themes/BaseTheme.cppsrc/components/themes/lyra/Lyra3CoversTheme.cppsrc/components/themes/lyra/Lyra3CoversTheme.hsrc/components/themes/lyra/LyraTheme.cpp
📜 Review details
🔇 Additional comments (6)
lib/GfxRenderer/GfxRenderer.h (1)
120-124: Clean API addition with good documentation.The
wrappedTextdeclaration is well-documented, consistent with the existingtruncatedTextAPI pattern, and the default style parameter follows the convention used by other text methods in this class.lib/GfxRenderer/GfxRenderer.cpp (1)
831-832: Good change: UTF-8 safe ellipsis.Switching from ASCII
"..."to the proper Unicode horizontal ellipsis (U+2026) ensures consistent width measurement and correct UTF-8 handling throughout the rendering pipeline.src/components/themes/lyra/Lyra3CoversTheme.h (1)
28-28: Metric increase aligns with multi-line title support.The bump from 287 to 300 for
homeCoverTileHeightaccommodates the additional vertical space needed by dynamically-sized multi-line title blocks. Looks appropriate.src/components/themes/lyra/LyraTheme.cpp (1)
487-503: Clean migration towrappedTextAPI.The manual word-splitting logic is neatly replaced with a single
wrappedTextcall. The vertical centering calculation correctly accounts for both the title block and author line heights, and the BOLD style is consistently applied for both measurement and rendering.src/components/themes/lyra/Lyra3CoversTheme.cpp (1)
90-117: Good dynamic title box sizing.The multi-line wrapping with dynamic height calculation is well-implemented. The selection highlight and title rendering both correctly use the computed
dynamicTitleBoxHeightbased on actual content.src/components/themes/BaseTheme.cpp (1)
490-490: Good migration towrappedTextfor title rendering.The replacement of manual word-wrapping with
wrappedTextsimplifies the logic significantly while maintaining correct vertical centering and box sizing.
- GfxRenderer::wrappedText: truncate carried-over word immediately when it exceeds maxWidth, preventing it being pushed untruncated into currentLine - GfxRenderer::wrappedText: replace C-style (int) casts with static_cast<int> - BaseTheme::drawRecentBookCover: hoist truncatedText call for author above coverRendered block; reuse result for both box-size measurement and rendering - Lyra3CoversTheme::drawRecentBookCover: remove unused bookTitleHeight variable
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
lib/GfxRenderer/GfxRenderer.cpp (1)
881-883:⚠️ Potential issue | 🟠 MajorOversized word assigned to
currentLinewithout truncation — line overflow still possible.When
currentLineis non-empty and the combinedtestLineexceedsmaxWidth, the existing line is flushed andwordbecomes the newcurrentLine. However, ifwordalone is wider thanmaxWidthit will later be pushed untruncated — either when the next word also overflows (triggering another flush) or at the final push (Line 894–896). Only thecurrentLine.empty()branch (Line 884–890) and the last-line branch (Line 856–862) truncate an oversized word.Example with
maxLines = 3,maxWidth = 100, words["Hi", "Superlongword", "End"]where"Superlongword"alone > 100 px:
"Hi"fits →currentLine = "Hi""Hi Superlongword"overflows → push"Hi",currentLine = "Superlongword"(untruncated!)"Superlongword End"overflows → push"Superlongword"(exceeds maxWidth!)- Loop ends, push
"End"Result:
["Hi", "Superlongword", "End"]— line 2 overflows the render box.🐛 Proposed fix
if (!currentLine.empty()) { lines.push_back(currentLine); - currentLine = word; + if (getTextWidth(fontId, word.c_str(), style) > maxWidth) { + lines.push_back(truncatedText(fontId, word.c_str(), maxWidth, style)); + currentLine.clear(); + } else { + currentLine = word; + } } else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/GfxRenderer/GfxRenderer.cpp` around lines 881 - 883, When flushing a non-empty currentLine because testLine exceeds maxWidth, ensure the new assignment currentLine = word also truncates oversized words: check the pixel width of word against maxWidth and, if it exceeds, replace word with the same truncation routine used elsewhere (the code path in the currentLine.empty() branch and the final-line branch) before assigning to currentLine; update the logic around testLine/currentLine/word to call that truncate helper (or inline truncateToWidth behavior) so no single word pushed into lines can exceed maxWidth.
🧹 Nitpick comments (2)
lib/GfxRenderer/GfxRenderer.cpp (1)
856-856: Nitpick: preferstatic_cast<int>over C-style cast.
(int)lines.size()is used at lines 856 and 894. Preferstatic_cast<int>(lines.size())for consistency with the rest of the codebase.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/GfxRenderer/GfxRenderer.cpp` at line 856, Replace the C-style casts of lines.size() with C++ style casts: change occurrences of (int)lines.size() (notably where used in the conditional around maxLines — currently at the checks near lines 856 and 894 in GfxRenderer.cpp) to static_cast<int>(lines.size()); update both sites so the codebase uses static_cast consistently.wrapped-text-refactor.patch (1)
304-310:homeCoverTileHeightbump to 300 is tied to the font metric constraint noted above.If
SMALL_FONT_ID'sadvanceY> 17 px the tile will still be too short for 3-line titles. Consider deriving this constant programmatically (e.g.homeCoverHeight + 2 * hPaddingInSelection + 5 + 3 * lineHeight) or at minimum add a comment documenting the font-size dependency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@wrapped-text-refactor.patch` around lines 304 - 310, The hardcoded bump of ThemeMetrics.homeCoverTileHeight to 300 depends on the SMALL_FONT_ID font metric (advanceY) and can still be too short if SMALL_FONT_ID.advanceY > 17; update the value to be derived programmatically (e.g. compute homeCoverTileHeight from homeCoverHeight plus padding and 3*lineHeight using the SMALL_FONT_ID.advanceY) or, if you keep a constant, add a clear comment next to ThemeMetrics.homeCoverTileHeight explaining the dependency on SMALL_FONT_ID.advanceY and why 300 was chosen; reference ThemeMetrics.homeCoverTileHeight, SMALL_FONT_ID, SMALL_FONT_ID.advanceY, homeCoverHeight and any hPaddingInSelection/lineHeight helpers in the implementation so the relationship is obvious and robust to font changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@wrapped-text-refactor.patch`:
- Around line 267-277: The title block can exceed the tile bottom because
SMALL_FONT_ID lineHeight (renderer.getLineHeight) times 3 is taller than the
remaining space; fix by computing the available vertical space from the title
start (use tileY and the existing offsets) and deriving maxLines =
floor(availableHeight / renderer.getLineHeight(SMALL_FONT_ID)), then clamp the
wrapped text to that max (call renderer.wrappedText with min(3, maxLines) or
truncate the result/replace with a single truncated line if maxLines==1), and
recompute dynamicBlockHeight/dynamicTitleBoxHeight from the actual number of
lines; update the use of renderer.wrappedText, getLineHeight, dynamicBlockHeight
and dynamicTitleBoxHeight accordingly so text never overflows the tile.
---
Duplicate comments:
In `@lib/GfxRenderer/GfxRenderer.cpp`:
- Around line 881-883: When flushing a non-empty currentLine because testLine
exceeds maxWidth, ensure the new assignment currentLine = word also truncates
oversized words: check the pixel width of word against maxWidth and, if it
exceeds, replace word with the same truncation routine used elsewhere (the code
path in the currentLine.empty() branch and the final-line branch) before
assigning to currentLine; update the logic around testLine/currentLine/word to
call that truncate helper (or inline truncateToWidth behavior) so no single word
pushed into lines can exceed maxWidth.
---
Nitpick comments:
In `@lib/GfxRenderer/GfxRenderer.cpp`:
- Line 856: Replace the C-style casts of lines.size() with C++ style casts:
change occurrences of (int)lines.size() (notably where used in the conditional
around maxLines — currently at the checks near lines 856 and 894 in
GfxRenderer.cpp) to static_cast<int>(lines.size()); update both sites so the
codebase uses static_cast consistently.
In `@wrapped-text-refactor.patch`:
- Around line 304-310: The hardcoded bump of ThemeMetrics.homeCoverTileHeight to
300 depends on the SMALL_FONT_ID font metric (advanceY) and can still be too
short if SMALL_FONT_ID.advanceY > 17; update the value to be derived
programmatically (e.g. compute homeCoverTileHeight from homeCoverHeight plus
padding and 3*lineHeight using the SMALL_FONT_ID.advanceY) or, if you keep a
constant, add a clear comment next to ThemeMetrics.homeCoverTileHeight
explaining the dependency on SMALL_FONT_ID.advanceY and why 300 was chosen;
reference ThemeMetrics.homeCoverTileHeight, SMALL_FONT_ID,
SMALL_FONT_ID.advanceY, homeCoverHeight and any hPaddingInSelection/lineHeight
helpers in the implementation so the relationship is obvious and robust to font
changes.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
lib/GfxRenderer/GfxRenderer.cppopen-x4-sdkwrapped-text-refactor.patch
✅ Files skipped from review due to trivial changes (1)
- open-x4-sdk
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cppcheck
- GitHub Check: build
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-19T12:17:05.421Z
Learnt from: Uri-Tauber
Repo: crosspoint-reader/crosspoint-reader PR: 988
File: lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp:649-661
Timestamp: 2026-02-19T12:17:05.421Z
Learning: In ChapterHtmlSlimParser.cpp, when computing footnote word indices in endElement() for footnote links, the wordIndex must be cumulative across the 750-word mid-paragraph flush boundary. The correct calculation is: `wordIndex = wordsExtractedInBlock + currentTextBlock->size()`, not just `currentTextBlock->size()`. This ensures footnotes attach to the page containing their actual anchor word, even after layoutAndExtractLines(false) has extracted and removed earlier words from the block.
Applied to files:
wrapped-text-refactor.patchlib/GfxRenderer/GfxRenderer.cpp
🔇 Additional comments (5)
wrapped-text-refactor.patch (3)
192-192: Clean consolidation — LGTM.Replacing 82 lines of bespoke word-wrap logic with a single
renderer.wrappedText(...)call is a significant maintainability win and the parameters (UI_12_FONT_ID,bookWidth - 40, max 3 lines) are consistent with the old implementation.
380-381: LGTM —LyraTheme.cppsimplification is clean.Replacing 57 lines of manual word-wrap with
renderer.wrappedText(UI_12_FONT_ID, book.title.c_str(), textWidth, 3, EpdFontFamily::BOLD)is correct:maxWidth = textWidth,maxLines = 3, andEpdFontFamily::BOLDmatches the style that was previously used throughout the removed code.
213-218: The concern aboutmaxLineWidthscope is not applicable to the current codebase.The patch snippet shown in your review would have used an undefined
maxLineWidthvariable, but the final code that was committed takes a different approach: it pre-calculatestruncatedAuthoronce usingbookWidth - 40directly, then measures that pre-calculated string. The variablemaxLineWidthdoes not exist in the current BaseTheme.cpp, andbookWidth - 40is used inline instead. The final implementation avoids the scoping issue entirely.Likely an incorrect or invalid review comment.
lib/GfxRenderer/GfxRenderer.cpp (2)
831-832: LGTM — single U+2026 glyph is the correct replacement for ASCII"...".Using
"\xe2\x80\xa6"ensuresgetTextWidthmeasures one glyph instead of three, giving a more accurate width budget for the truncation loop and a typographically correct ellipsis.
856-862: Correct fix for the lost-currentLinelast-line bug.Combining
currentLine + " " + remainingbefore delegating totruncatedTextensures content already accumulated on the in-progress line isn't silently discarded when the line-limit is hit mid-word.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/themes/lyra/Lyra3CoversTheme.cpp (1)
25-25:⚠️ Potential issue | 🟡 MinorRemove unused variable
tileHeight(flagged by CI).The pipeline's cppcheck reports that
tileHeightis assigned but never read. This should be removed to keep the build warning-free.Proposed fix
const int tileWidth = (rect.width - 2 * Lyra3CoversMetrics::values.contentSidePadding) / 3; - const int tileHeight = rect.height; const int tileY = rect.y;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/Lyra3CoversTheme.cpp` at line 25, Remove the unused local variable tileHeight (the line "const int tileHeight = rect.height;") which is assigned but never read; delete that declaration and, if any later code intended to use tileHeight, replace those uses with rect.height directly (search for "tileHeight" references) so no unused-variable warnings remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/GfxRenderer/GfxRenderer.cpp`:
- Around line 846-904: In wrappedText, when a carried-over word is too wide and
you call truncatedText (the branch that sets currentLine = truncatedText(...)
inside wrappedText), push that truncated string into lines immediately instead
of storing it in currentLine so you don't allow a short following word to be
appended after an ellipsis; after pushing the truncated entry, clear currentLine
(or set it to empty) so subsequent words start a new line, and add a check
against maxLines (using lines.size() and maxLines) to handle the case where
pushing the truncated line reaches the limit (mirror the existing early-return
behavior that uses truncatedText for the final allowed line). Ensure references:
wrappedText, currentLine, lines, truncatedText, getTextWidth, and maxLines.
---
Outside diff comments:
In `@src/components/themes/lyra/Lyra3CoversTheme.cpp`:
- Line 25: Remove the unused local variable tileHeight (the line "const int
tileHeight = rect.height;") which is assigned but never read; delete that
declaration and, if any later code intended to use tileHeight, replace those
uses with rect.height directly (search for "tileHeight" references) so no
unused-variable warnings remain.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
lib/GfxRenderer/GfxRenderer.cppsrc/components/themes/BaseTheme.cppsrc/components/themes/lyra/Lyra3CoversTheme.cpp
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-19T12:17:05.421Z
Learnt from: Uri-Tauber
Repo: crosspoint-reader/crosspoint-reader PR: 988
File: lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp:649-661
Timestamp: 2026-02-19T12:17:05.421Z
Learning: In ChapterHtmlSlimParser.cpp, when computing footnote word indices in endElement() for footnote links, the wordIndex must be cumulative across the 750-word mid-paragraph flush boundary. The correct calculation is: `wordIndex = wordsExtractedInBlock + currentTextBlock->size()`, not just `currentTextBlock->size()`. This ensures footnotes attach to the page containing their actual anchor word, even after layoutAndExtractLines(false) has extracted and removed earlier words from the block.
Applied to files:
lib/GfxRenderer/GfxRenderer.cpp
🪛 GitHub Actions: CI (build)
src/components/themes/lyra/Lyra3CoversTheme.cpp
[warning] 25-25: cppcheck: Variable 'tileHeight' is assigned a value that is never used. [unreadVariable].
🔇 Additional comments (3)
lib/GfxRenderer/GfxRenderer.cpp (1)
831-832: Good change: UTF-8 ellipsis character.Switching from
"..."(3 ASCII dots) to U+2026 ("\xe2\x80\xa6") is the correct typographic choice for an e-reader UI and saves horizontal space since it occupies a single glyph advance.src/components/themes/BaseTheme.cpp (1)
490-541: Clean refactoring to usewrappedTextandtruncatedText.The replacement of manual wrapping logic with
renderer.wrappedText()for the title andrenderer.truncatedText()for the author is well-structured. ThetruncatedAuthorvariable is correctly hoisted and reused for both width calculation and rendering, avoiding redundant calls.src/components/themes/lyra/Lyra3CoversTheme.cpp (1)
89-116: Well-integrated use ofwrappedTextwith dynamic title box height.The dynamic height calculation based on
titleLines.size()is a nice improvement over a fixed title area. The per-line rendering loop and padding logic look correct.
…Height - wrappedText: when a carried-over word exceeds maxWidth, push its truncated form directly as a complete line instead of storing it in currentLine. Previously a subsequent short word could be appended after the ellipsis. Also returns early if the push reaches maxLines. - Lyra3CoversTheme: remove unused tileHeight variable (compiler warning)
| /// Word-wrap \p text into at most \p maxLines lines, each no wider than | ||
| /// \p maxWidth pixels. Overflowing words and excess lines are UTF-8-safely | ||
| /// truncated with an ellipsis (U+2026). | ||
| std::vector<std::string> wrappedText(int fontId, const char* text, int maxWidth, int maxLines, |
There was a problem hiding this comment.
nits, but should we have a drawWrappedText() wrapper for this? the idea is like drawCenteredText(), that will simply downstream code
There was a problem hiding this comment.
btw, is it possible to also return a bool to indicate if the text is truncated? something like bool& truncated
There was a problem hiding this comment.
I can do that, let me see
There was a problem hiding this comment.
@ngxson just getting around to looking, what do you mean by a wrapper?
There was a problem hiding this comment.
I mean the wrappedText only tell how to wrap the text, but not drawing on screen. A wrapper like drawWrappedText will call wrappedText under the hood, and also draw the text to screen
There was a problem hiding this comment.
(just optional though because I will need that for another PR)
CaptainFrito
left a comment
There was a problem hiding this comment.
Thank you so much for this!!
|
Very cool device by the way |
znelson
left a comment
There was a problem hiding this comment.
Nice! Thanks for consolidating the logic 🙂
…oint-reader#1141) ## Summary * **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.) * **What changes are included?** Conrgegate the changes of crosspoint-reader#1074 , crosspoint-reader#1013 , and extended upon crosspoint-reader#911 by @lkristensen New function implemented in GfxRenderer.cpp ```C++ std::vector<std::string> GfxRenderer::wrappedText(const int fontId, const char* text, const int maxWidth, const int maxLines, const EpdFontFamily::Style style) const ``` Applied logic to all uses in Lyra, Lyra Extended, and base theme (continue reading card as pointed out by @znelson ## Additional Context    --- ### 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? _**< YES >**_ --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Summary
What is the goal of this PR? (e.g., Implements the new feature for file uploading.)
What changes are included?
Conrgegate the changes of feat: Add text wrapping to Lyra Extended theme #1074 , fix: Fixed book title in home screen #1013 , and extended upon feat: Add ability to wrap text to UI elements #911 by @lkristensen
New function implemented in GfxRenderer.cpp
Applied logic to all uses in Lyra, Lyra Extended, and base theme (continue reading card as pointed out by @znelson
Additional Context
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? < YES >