Conversation
809fd6f to
3f5541a
Compare
can we use single line for each item, like in classic theme? Seems cramped now |
|
@osteotek just simulated how it would feels like to have a list item instead of mosaic. we could have it on settings to be changed. Same for bookshelf. |
I think both of these look great, personally. But I'm unsure how much we want to go in the direction of having theme-specific settings for customization? |
|
@lukestein Agreed. |
I think the bookshelf could offer both single and multiple-item options. Personally, even if I have many books, I only read one at a time. Also, using two lines of text instead of one improves book title visibility in the current build. |
|
We could also offer both options through two separate themes. I personally really like the visual richness of 3 recent books on the home page, and it was too much of a PITA to implement to just scrape it now 😅 |
|
Icons look cool! Single row layout of menu options fills empty space nicely :) I have a suggestion. "Calibre library" could be more broad, maybe something like "Remote library"? |
I dont know if maybe my PR just isn't in yet but I already renamed it to that haha |
|
fwiw horiztonal with icons looks SO much better |
d67c941 to
2c82317
Compare
136e0e4 to
0347712
Compare
Adding a method to the display renderer to support drawing images without coloring the white pixels, essentially drawing transparent images for icons in the Lyra Icons PR crosspoint-reader/crosspoint-reader#725
3ebdb27 to
7cf2dc2
Compare
|
Caution Review failedThe pull request is closed. 📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds an icon system: new transparent image draw call in HAL, many embedded icon headers, a UIIcon enum and file-type classifier, theme and activity APIs updated to accept UIIcon-based icon lambdas, Lyra theme icon rendering, and a script to convert images/SVGs to C bitmap headers. Changes
Sequence Diagram(s)sequenceDiagram
participant Activity as Activity (Home/Network/Recent)
participant Theme as Theme (Lyra/Base)
participant Renderer as GfxRenderer
participant HAL as HalDisplay
participant EInk as einkDisplay
Activity->>Theme: request drawList / drawButtonMenu (rowTitle, rowSubtitle, rowIcon -> UIIcon)
Theme->>Renderer: layout items, request icon bitmap for UIIcon
Renderer->>HAL: drawImageTransparent(imageData, x, y, w, h, fromProgmem)
HAL->>EInk: einkDisplay.drawImageTransparent(imageData, x, y, w, h, fromProgmem)
EInk-->>HAL: rendered
HAL-->>Renderer: ack
Renderer-->>Theme: draw complete
Theme-->>Activity: UI rendered
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 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. Comment |
|
oh uh, I didn't mean for this to run everywhere straight away |
7cf2dc2 to
3d4dc30
Compare
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/activities/network/WifiSelectionActivity.h (1)
47-103:⚠️ Potential issue | 🟡 MinorAlign
selectedNetworkIndextype withint-based APIs.
selectedNetworkIndexis declared assize_tbut is passed toselectNetwork(int)andButtonNavigator::nextIndex/previousIndex(int, int), which expectint. While the current code doesn't use negative values or sentinels (ButtonNavigator's modulo logic always returns valid non-negative results), this type mismatch creates implicit conversions at lines 449, 475, and 480, and misaligns with GUI.drawList expectations at line 585. ChangeselectedNetworkIndextointfor consistency.src/components/UITheme.cpp (1)
28-45:⚠️ Potential issue | 🟠 MajorMemory leak when switching themes.
When
setThemeis called (e.g., viareload()), a new theme is allocated withnewbut the previouscurrentThemepointer is not deleted, causing a memory leak each time the theme is changed.🐛 Proposed fix
void UITheme::setTheme(CrossPointSettings::UI_THEME type) { + delete currentTheme; + currentTheme = nullptr; + switch (type) { case CrossPointSettings::UI_THEME::CLASSIC: Serial.printf("[%lu] [UI] Using Classic theme\n", millis()); currentTheme = new BaseTheme();src/activities/settings/OtaUpdateActivity.cpp (1)
231-233:⚠️ Potential issue | 🟡 MinorRemove unreachable
SHUTTING_DOWNstate handler—this code is never executed.The
SHUTTING_DOWNstate is defined in the enum but never assigned anywhere inOtaUpdateActivity. State transitions flow fromUPDATE_IN_PROGRESSdirectly toFAILEDorFINISHED. The handler at lines 231–232 is dead code and should be removed.
🤖 Fix all issues with AI agents
In `@lib/GfxRenderer/GfxRenderer.cpp`:
- Around line 406-408: The drawIcon implementation passes parameters to
display.drawImageTransparent in the wrong order; in GfxRenderer::drawIcon swap
the x/y and width/height arguments so the call matches the API (imageData, x, y,
w, h). Replace the current call to display.drawImageTransparent(bitmap, y,
getScreenWidth() - width - x, height, width) with a call that supplies
getScreenWidth() - width - x as the x argument, y as the y argument, and width
then height as the final two args (i.e., imageData, x, y, w, h) so non-square
icons render correctly.
In `@scripts/convert_icon.py`:
- Around line 61-66: Update the usage string to reference convert_icon.py and
sanitize output_name before building array_name: replace or map non-alphanumeric
characters to underscores and ensure the identifier does not start with a digit
(prefix with an underscore if it does), then construct array_name from the
sanitized name (e.g., sanitized_name + 'Icon') instead of using capitalize();
keep the existing sys.argv unpacking (input_path, output_name, width, height)
and convert width/height to int as before.
- Around line 15-27: The SVG branch in load_image uses svg_to_png_bytes(path,
width, height) and Image.open on the resulting bytes but does not composite
transparent pixels onto white, causing transparent areas to become dark after
grayscale/threshold; modify the SVG branch so after opening the png bytes (img =
Image.open(io.BytesIO(png_bytes))) you convert to 'RGBA', resize if needed, then
composite/paste the image onto a white RGBA background (same technique as the
raster branch: create background = Image.new('RGBA', img.size,
(255,255,255,255')) and background.paste(img, mask=img.split()[3]) ) and assign
img = background before returning/processing further.
In `@src/activities/home/RecentBooksActivity.cpp`:
- Around line 140-145: The lambda that extracts extensions from recentBooks
(uses filename.rfind('.') and substr) must guard against no-extension cases and
dots in directory names: compute auto dot = filename.rfind('.'); if dot ==
std::string::npos then return empty string; also find the last path separator
with auto sep = filename.find_last_of("/\\"); only treat dot as an extension
when dot > sep; otherwise return empty string; call substr(dot + 1) only in the
valid branch so you never return partial/full paths.
In `@src/activities/network/CalibreConnectActivity.cpp`:
- Around line 218-264: The button hints are only drawn inside the SERVER_RUNNING
branch, leaving SERVER_STARTING and ERROR without navigation hints; move the
call that obtains labels via mappedInput.mapLabels("« Exit", "", "", "") and the
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3,
labels.btn4) out of the state-specific if/else block so it runs for all states
(or alternatively call GUI.drawButtonHints in both the SERVER_STARTING and ERROR
branches), ensuring CalibreConnectState::SERVER_STARTING and
CalibreConnectState::ERROR also render the exit hint.
In `@src/activities/settings/OtaUpdateActivity.cpp`:
- Around line 166-169: The NO_UPDATE and FAILED render branches currently call
renderer.drawCenteredText but omit the Back button hints; update those branches
(state == NO_UPDATE and state == FAILED) to also draw the same Back button hint
used elsewhere in this activity (use the same renderer button-hint call that
other states use) so users see they can exit—keep the existing centered text and
add the button-hint drawing with the same styling and placement as other states;
no changes to loop() handler are needed.
In `@src/components/themes/BaseTheme.cpp`:
- Line 20: Replace the hard-coded subtitleY constant with a computed value
derived from the header rectangle so subtitles follow changes in header height
or screen size: locate the usage of subtitleY in BaseTheme (and the similar code
around the other occurrences at lines referenced 252-258), remove constexpr int
subtitleY = 738 and compute subtitleY at runtime from the header rect (e.g., use
headerRect.bottom or headerRect.y + headerRect.height plus an optional margin)
wherever subtitleY is used (e.g., in layout/subtitle drawing functions) so the
subtitle position is always based on headerRect properties instead of a fixed
coordinate.
- Line 67: The Serial.printf call in the progress-bar rendering path should be
guarded and use the correct size_t formatting: replace the unguarded
Serial.printf("Drawing progress bar: current=%u, total=%u, percent=%d\n",
current, total, percent); with a conditional log (either a runtime verbose/debug
flag check or a compile-time `#ifdef` DEBUG/LOG_ENABLED) so it does not run in the
hot render path, and change the format specifiers for current and total to %zu
(or cast to an explicit integer type and use %lu if your platform lacks %zu)
while keeping percent as %d; locate the call by searching for Serial.printf in
the progress-bar drawing function and wrap it accordingly.
In `@src/components/themes/lyra/Lyra3CoversTheme.cpp`:
- Around line 70-78: The empty-cover rendering block inside Lyra3CoversTheme.cpp
uses LyraMetrics::values.homeCoverHeight causing inconsistency with the rest of
this theme; update the three occurrences used in the hasCover == false branch
(the calls to renderer.drawRect and renderer.fillRect that reference
homeCoverHeight, and any related calculations in that block) to use
Lyra3CoversMetrics::values.homeCoverHeight so the placeholder height matches the
Lyra3Covers theme metrics.
In `@src/components/themes/lyra/Lyra3CoversTheme.h`:
- Around line 38-40: The declaration of Lyra3CoversTheme::drawRecentBookCover
has a mismatched parameter constness (GfxRenderer& renderer) so it does not
override the base virtual; change the first parameter to const GfxRenderer&
renderer to match LyraTheme::drawRecentBookCover and update any corresponding
definition/implementation signatures (e.g., the drawRecentBookCover method in
Lyra3CoversTheme.cpp) so the function properly overrides with the const
qualifier and keeps the existing override specifier.
In `@src/components/themes/lyra/LyraTheme.cpp`:
- Around line 359-405: The code currently calls storeCoverBuffer() on every
render which copies the framebuffer; change it so storeCoverBuffer() is only
invoked when the cover has just been rendered or when the cover source changes:
call storeCoverBuffer() inside the branch where coverRendered is transitioned to
true (i.e., immediately after successfully drawing the bitmap or rendering the
empty cover) and only if the coverBufferStored flag is not already set or the
current RecentBook cover path (book.coverBmpPath / coverPath) differs from the
previously stored cover path; update coverBufferStored and remember the stored
cover path when you call storeCoverBuffer(), and remove the unconditional call
to storeCoverBuffer() that currently executes every render.
- Around line 449-459: The code uses a dangling pointer from
buttonLabel(i).c_str() and ignores the rowIcon parameter; fix by storing the
label in a local std::string (e.g., std::string label = buttonLabel(i);) so its
.c_str() remains valid for later text rendering, and use rowIcon(i) when
resolving the icon (e.g., const char* iconName = rowIcon(i); if (iconName !=
nullptr) { const uint8_t* icon = iconForName(iconName, mainMenuIconSize); ...
}). Keep the existing null checks and only advance textX when an icon was
actually drawn.
🧹 Nitpick comments (10)
src/activities/home/MyLibraryActivity.cpp (1)
227-241: Pass parameters byconst&and handle missing extensions.
Copy overhead: Both methods take
std::stringby value, causing unnecessary allocations on each call. Useconst std::string&instead.Edge case: For files without an extension (e.g.,
README,Makefile),rfind('.')returnsnpos. IngetFileType,substr(npos + 1)wraps tosubstr(0), returning the entire filename as the type. IngetFileName, it returns the full name (acceptable but inconsistent).♻️ Proposed fix
-std::string MyLibraryActivity::getFileType(std::string filename) const { +std::string MyLibraryActivity::getFileType(const std::string& filename) const { if (filename.back() == '/') { return "Folder"; } const auto pos = filename.rfind('.'); + if (pos == std::string::npos) { + return "File"; + } return filename.substr(pos + 1); } -std::string MyLibraryActivity::getFileName(std::string filename) const { +std::string MyLibraryActivity::getFileName(const std::string& filename) const { if (filename.back() == '/') { return filename.substr(0, filename.length() - 1); } const auto pos = filename.rfind('.'); + if (pos == std::string::npos) { + return filename; + } return filename.substr(0, pos); }src/activities/home/MyLibraryActivity.h (1)
38-39: Update declarations to useconst std::string&.Consistent with the implementation fix suggested in
MyLibraryActivity.cpp, update these declarations to avoid pass-by-value copies.♻️ Proposed fix
- std::string getFileType(std::string filename) const; - std::string getFileName(std::string filename) const; + std::string getFileType(const std::string& filename) const; + std::string getFileName(const std::string& filename) const;src/activities/network/NetworkModeSelectionActivity.cpp (1)
10-21: Consider deriving item count from the arrays to prevent drift.
MENU_ITEM_COUNTcan diverge from the actual array sizes as items evolve. Usingstd::arrayandsize()avoids this risk.♻️ Suggested refactor
+#include <array> namespace { -constexpr int MENU_ITEM_COUNT = 3; -const char* MENU_ITEMS[MENU_ITEM_COUNT] = {"Join a Network", "Connect to Calibre", "Create Hotspot"}; -const char* MENU_DESCRIPTIONS[MENU_ITEM_COUNT] = { +constexpr std::array<const char*, 3> MENU_ITEMS = { + "Join a Network", "Connect to Calibre", "Create Hotspot"}; +constexpr std::array<const char*, 3> MENU_DESCRIPTIONS = { "Connect to an existing WiFi network", "Use Calibre wireless device transfers", "Create a WiFi network others can join", }; -const char* MENU_ICONS[MENU_ITEM_COUNT] = { +constexpr std::array<const char*, 3> MENU_ICONS = { "wifi", - "OPDS Browser", + "OPDS Browser", "hotspot", }; +constexpr int MENU_ITEM_COUNT = static_cast<int>(MENU_ITEMS.size()); } // namespacesrc/activities/network/CrossPointWebServerActivity.cpp (1)
431-494: Avoid double header/subheader rendering.
render()already draws the header/subheader forSERVER_RUNNING; doing it again here adds extra draw work with no visual benefit. IfrenderServerRunning()is only called fromrender(), you can drop these calls.♻️ Suggested trim
- GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, - isApMode ? "Hotspot Mode" : "Network Transfer", nullptr); - GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, - connectedSSID.c_str());src/components/icons/library.h (1)
1-12: Avoid per‑TU duplication of icon data.
staticin a header produces one copy per translation unit. For embedded builds this can add up across many icons. If C++17 is available, preferinline constexprto keep a single ODR entity, or move the definition to a.cppwith anexterndeclaration.💡 Example (C++17 inline variable)
- static const uint8_t LibraryIcon[] = { + inline constexpr uint8_t LibraryIcon[] = {src/components/themes/lyra/LyraTheme.cpp (2)
47-63: Normalize icon names once to avoid case drift and brittle matches.A single lowercase normalization makes icon lookups resilient to label casing changes and keeps comparisons consistent.
🛠️ Proposed refactor
@@ -#include <algorithm> +#include <algorithm> +#include <cctype> @@ const uint8_t* iconForName(const std::string& iconName, int size) { + std::string key = iconName; + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return static_cast<char>(std::tolower(c)); }); if (size == 24) { - if (iconName == "Folder") return Folder24Icon; - if (iconName == "epub" || iconName == "xtc" || iconName == "xtch") return Book24Icon; - if (iconName == "txt") return Text24Icon; - if (iconName == "bmp") return Image24Icon; + if (key == "folder") return Folder24Icon; + if (key == "epub" || key == "xtc" || key == "xtch") return Book24Icon; + if (key == "txt") return Text24Icon; + if (key == "bmp") return Image24Icon; } else if (size == 32) { - if (iconName == "epub" || iconName == "xtc" || iconName == "xtch") return BookIcon; - if (iconName == "Browse Files") return FolderIcon; - if (iconName == "Recents") return RecentIcon; - if (iconName == "Settings") return CogIcon; - if (iconName == "File Transfer") return TransferIcon; - if (iconName == "OPDS Browser") return LibraryIcon; - if (iconName == "wifi") return WifiIcon; - if (iconName == "hotspot") return HotspotIcon; + if (key == "epub" || key == "xtc" || key == "xtch") return BookIcon; + if (key == "browse files") return FolderIcon; + if (key == "recents") return RecentIcon; + if (key == "settings") return CogIcon; + if (key == "file transfer") return TransferIcon; + if (key == "opds browser") return LibraryIcon; + if (key == "wifi") return WifiIcon; + if (key == "hotspot") return HotspotIcon; } return nullptr; }
223-279: Consider caching icon/value text for large lists to reduce per-frame allocations.Each redraw allocates multiple
std::strings and performs icon lookups per row, which can contribute to the reported file-browser slowdown. Caching resolved icon pointers or switching callbacks to returnstd::string_view/const char*(or memoized strings) would reduce heap churn.src/activities/settings/KOReaderAuthActivity.cpp (1)
126-148: Consider using metrics for vertical spacing between text lines.The hardcoded
+ 10spacing between lines (lines 140, 143) could be replaced withmetrics.verticalSpacingfor consistency with the metrics-driven approach used elsewhere in the render function.♻️ Suggested improvement
+ auto metrics = UITheme::getInstance().getMetrics(); const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); ... if (state == SUCCESS) { renderer.drawCenteredText(UI_10_FONT_ID, top, "Success!", true, EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, top + height + 10, "KOReader sync is ready to use"); + renderer.drawCenteredText(UI_10_FONT_ID, top + height + metrics.verticalSpacing, "KOReader sync is ready to use"); } else if (state == FAILED) { renderer.drawCenteredText(UI_10_FONT_ID, top, "Error", true, EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, top + height + 10, errorMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, top + height + metrics.verticalSpacing, errorMessage.c_str()); }src/components/themes/lyra/Lyra3CoversTheme.cpp (1)
17-21: Unused mutable globalcoverWidthin anonymous namespace.The
coverWidthvariable at line 20 is declared but never used in this file. The local variablecoverWidthat line 54 shadows it. This appears to be dead code copied fromLyraTheme.cppwhere it serves a caching purpose.♻️ Proposed fix
namespace { constexpr int hPaddingInSelection = 8; constexpr int cornerRadius = 6; -int coverWidth = 0; } // namespacesrc/activities/settings/OtaUpdateActivity.cpp (1)
121-137: Early return afterclearScreen()wastes work.When the progress throttling condition triggers (line 134), the function returns after
clearScreen()anddrawHeader()have already executed. While this doesn't cause visual issues (sincedisplayBuffer()isn't called), these operations are performed unnecessarily.♻️ Move throttling check before drawing operations
void OtaUpdateActivity::render() { if (subActivity) { // Subactivity handles its own rendering return; } + // Throttle progress updates early to avoid unnecessary draw operations + if (state == UPDATE_IN_PROGRESS) { + float progress = static_cast<float>(updater.getProcessedSize()) / static_cast<float>(updater.getTotalSize()); + if (static_cast<int>(progress * 50) == lastUpdaterPercentage / 2) { + return; + } + lastUpdaterPercentage = static_cast<int>(progress * 100); + } + auto metrics = UITheme::getInstance().getMetrics(); const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); renderer.clearScreen(); GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, "Update"); const auto height = renderer.getLineHeight(UI_10_FONT_ID); const auto top = (pageHeight - height) / 2; - float updaterProgress = 0; - if (state == UPDATE_IN_PROGRESS) { - Serial.printf("[%lu] [OTA] Update progress: %d / %d\n", millis(), updater.getProcessedSize(), - updater.getTotalSize()); - updaterProgress = static_cast<float>(updater.getProcessedSize()) / static_cast<float>(updater.getTotalSize()); - // Only update every 2% at the most - if (static_cast<int>(updaterProgress * 50) == lastUpdaterPercentage / 2) { - return; - } - lastUpdaterPercentage = static_cast<int>(updaterProgress * 100); - } + float updaterProgress = (state == UPDATE_IN_PROGRESS) + ? static_cast<float>(updater.getProcessedSize()) / static_cast<float>(updater.getTotalSize()) + : 0;
| int textX = tileRect.x + 16; | ||
| const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); | ||
| const int textY = tileRect.y + (LyraMetrics::values.menuRowHeight - lineHeight) / 2; | ||
| const char* label = buttonLabel(i).c_str(); | ||
|
|
||
| if (rowIcon != nullptr) { | ||
| const uint8_t* icon = iconForName(label, mainMenuIconSize); | ||
| if (icon != nullptr) { | ||
| renderer.drawIcon(icon, textX, textY, mainMenuIconSize, mainMenuIconSize); | ||
| textX += mainMenuIconSize + hPaddingInSelection; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the file and read the code around lines 449-459
cat -n src/components/themes/lyra/LyraTheme.cpp | sed -n '440,470p'Repository: crosspoint-reader/crosspoint-reader
Length of output: 1557
🏁 Script executed:
# Search for buttonLabel function definition
rg -A 5 "buttonLabel\s*\(" src/components/themes/lyra/LyraTheme.cpp | head -20Repository: crosspoint-reader/crosspoint-reader
Length of output: 335
🏁 Script executed:
# Find the header file for LyraTheme
fd "LyraTheme" src/components/themes/lyra/Repository: crosspoint-reader/crosspoint-reader
Length of output: 158
🏁 Script executed:
# Check the header file for class definition and method signatures
cat -n src/components/themes/lyra/LyraTheme.hRepository: crosspoint-reader/crosspoint-reader
Length of output: 3855
🏁 Script executed:
# Let's see the broader context of the drawButtonMenu function
cat -n src/components/themes/lyra/LyraTheme.cpp | sed -n '420,465p'Repository: crosspoint-reader/crosspoint-reader
Length of output: 2650
🏁 Script executed:
# Let's check the iconForName function to understand what it expects
rg -B 3 -A 10 "iconForName" src/components/themes/lyra/LyraTheme.cpp | head -30Repository: crosspoint-reader/crosspoint-reader
Length of output: 1264
🏁 Script executed:
# Let's see more of the iconForName function and confirm the signature
rg -B 2 -A 20 "const uint8_t\* iconForName" src/components/themes/lyra/LyraTheme.cpp | head -40Repository: crosspoint-reader/crosspoint-reader
Length of output: 1026
🏁 Script executed:
# Check if label is used anywhere else after line 462 in drawButtonMenu
cat -n src/components/themes/lyra/LyraTheme.cpp | sed -n '462,475p'Repository: crosspoint-reader/crosspoint-reader
Length of output: 737
Fix dangling label pointer and honor rowIcon parameter.
buttonLabel(i).c_str() returns a pointer to a temporary std::string that is destroyed at statement end, leaving a dangling pointer used by renderer.drawText() on line 462. Additionally, the icon lookup uses the button label instead of calling rowIcon(i), which makes icons brittle to label changes and ignores the parameter entirely.
🛠️ Proposed fix
@@
- const char* label = buttonLabel(i).c_str();
+ std::string labelStr = buttonLabel(i);
+ const char* label = labelStr.c_str();
@@
- if (rowIcon != nullptr) {
- const uint8_t* icon = iconForName(label, mainMenuIconSize);
+ if (rowIcon != nullptr) {
+ std::string iconKey = rowIcon(i);
+ const uint8_t* icon = iconForName(iconKey, mainMenuIconSize);
if (icon != nullptr) {
renderer.drawIcon(icon, textX, textY, mainMenuIconSize, mainMenuIconSize);
textX += mainMenuIconSize + hPaddingInSelection;
}
}🤖 Prompt for AI Agents
In `@src/components/themes/lyra/LyraTheme.cpp` around lines 449 - 459, The code
uses a dangling pointer from buttonLabel(i).c_str() and ignores the rowIcon
parameter; fix by storing the label in a local std::string (e.g., std::string
label = buttonLabel(i);) so its .c_str() remains valid for later text rendering,
and use rowIcon(i) when resolving the icon (e.g., const char* iconName =
rowIcon(i); if (iconName != nullptr) { const uint8_t* icon =
iconForName(iconName, mainMenuIconSize); ... }). Keep the existing null checks
and only advance textX when an icon was actually drawn.
25ade24 to
12f3902
Compare
127f102 to
30be876
Compare
30be876 to
edb541f
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/activities/settings/OtaUpdateActivity.cpp (1)
99-105:⚠️ Potential issue | 🟠 MajorGuard against zero
getTotalSize()before division.Line 100 divides by
updater.getTotalSize(). If the total is 0 (unknown length or early stage), this can yield NaN/inf and lead to undefined behavior when cast to int later in the render path. Add a zero/negative guard.🛠️ Suggested fix
- updaterProgress = static_cast<float>(updater.getProcessedSize()) / static_cast<float>(updater.getTotalSize()); + const auto totalSize = updater.getTotalSize(); + const auto processedSize = updater.getProcessedSize(); + if (totalSize <= 0) { + updaterProgress = 0.0f; + } else { + updaterProgress = static_cast<float>(processedSize) / static_cast<float>(totalSize); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/activities/settings/OtaUpdateActivity.cpp` around lines 99 - 105, The division by updater.getTotalSize() in OtaUpdateActivity.cpp can divide by zero; modify the updater progress calculation (where updaterProgress, updater.getProcessedSize(), updater.getTotalSize(), and lastUpdaterPercentage are used) to first check that updater.getTotalSize() > 0 (or >= 1) and only compute the ratio when positive; if total is zero or negative, set updaterProgress to 0 (or an appropriate sentinel) and skip the percentage-based early return/update logic so subsequent casts to int and comparisons cannot use NaN/inf.
🧹 Nitpick comments (14)
lib/GfxRenderer/GfxRenderer.cpp (1)
323-325: Clarify the heuristic assumption in the comment.The comment says "Assume if we're not rounding all corners then we are only rounding one side," but the condition triggers when any corner is not rounded. This assumption holds for common cases (e.g., rounding only top or only left side) but could produce unexpected results if:
- Diagonal corners are rounded (top-left + bottom-right)
- Three corners are rounded
If these scenarios are intentionally unsupported, consider documenting that constraint. Otherwise, this is a reasonable pragmatic heuristic.
📝 Suggested comment clarification
- // Assume if we're not rounding all corners then we are only rounding one side + // When partially rounding corners, assume we're rounding one side (top, bottom, left, or right). + // Diagonal or three-corner rounding may produce suboptimal radius clamping. const int roundedSides = (!roundTopLeft || !roundTopRight || !roundBottomLeft || !roundBottomRight) ? 1 : 2;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/GfxRenderer/GfxRenderer.cpp` around lines 323 - 325, The current comment misstates the condition used to compute roundedSides: the code sets roundedSides based on (!roundTopLeft || !roundTopRight || !roundBottomLeft || !roundBottomRight) which triggers when any corner is not rounded, so update the comment to clearly describe this heuristic and its limitations (it assumes either all corners or effectively one side is rounded and may produce odd results for diagonal or three-corner cases) and either (A) document that diagonal/three-corner cases are unsupported, or (B) replace the heuristic by computing an explicit roundedCount (e.g., count true corner flags) and use that to derive roundedSides/maxRadius to handle mixed cases; reference the roundedSides, cornerRadius and maxRadius symbols when making the change.src/activities/settings/OtaUpdateActivity.cpp (1)
87-105: Avoid doing full render work when throttling progress updates.When the 2% throttle triggers (Line 102), the function returns after
clearScreen()and header draw, doing work that never reachesdisplayBuffer(). Consider moving the throttle check earlier (before clearing/drawing) to save CPU/battery.♻️ Possible refactor
- renderer.clearScreen(); - - GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_UPDATE)); - const auto height = renderer.getLineHeight(UI_10_FONT_ID); - const auto top = (pageHeight - height) / 2; - float updaterProgress = 0; if (state == UPDATE_IN_PROGRESS) { LOG_DBG("OTA", "Update progress: %d / %d", updater.getProcessedSize(), updater.getTotalSize()); updaterProgress = static_cast<float>(updater.getProcessedSize()) / static_cast<float>(updater.getTotalSize()); // Only update every 2% at the most if (static_cast<int>(updaterProgress * 50) == lastUpdaterPercentage / 2) { return; } lastUpdaterPercentage = static_cast<int>(updaterProgress * 100); } + + renderer.clearScreen(); + GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_UPDATE)); + const auto height = renderer.getLineHeight(UI_10_FONT_ID); + const auto top = (pageHeight - height) / 2;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/activities/settings/OtaUpdateActivity.cpp` around lines 87 - 105, Compute updaterProgress and perform the 2% throttle check before doing any heavy rendering (i.e., before calling renderer.clearScreen() and GUI.drawHeader()). Specifically, call updater.getProcessedSize() and updater.getTotalSize(), compute updaterProgress, compare the throttled percentage against lastUpdaterPercentage, and return early if unchanged; only if the check passes proceed to clear the screen, draw the header and continue updating lastUpdaterPercentage. Update references to updaterProgress, lastUpdaterPercentage, renderer.clearScreen(), GUI.drawHeader(), updater.getProcessedSize(), and updater.getTotalSize() accordingly so no unnecessary work is done when throttled.src/components/UITheme.h (2)
26-26: Consider passingfilenameby const reference to avoid unnecessary copy.The PR mentions the file browser is noticeably slower after adding icons. Passing
std::stringby value creates an unnecessary copy on each call. SincegetFileIcononly reads the string, passing byconst std::string&would be more efficient.♻️ Proposed fix
- static UIIcon getFileIcon(std::string filename); + static UIIcon getFileIcon(const std::string& filename);The implementation in
UITheme.cppshould also be updated to match.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/UITheme.h` at line 26, Change the getFileIcon API to accept the filename by const reference to avoid copies: update the declaration static UIIcon getFileIcon(const std::string& filename) in UITheme.h and update the corresponding definition in UITheme.cpp to match (function name getFileIcon). Also update any call sites that relied on the by-value signature (pass std::string or const std::string& as before) to ensure they compile. Rebuild and run the file browser performance regression test to confirm improvement.
9-10: Unused forward declaration.
MappedInputManageris forward-declared here but doesn't appear to be used anywhere in this header file. If it's only needed in the implementation file (UITheme.cpp), consider moving the forward declaration or include there instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/UITheme.h` around lines 9 - 10, Remove the unused forward declaration of MappedInputManager from UITheme.h; if the type is only referenced in UITheme.cpp, move the forward declaration or the `#include` for MappedInputManager into UITheme.cpp (or include the header where it is actually used) so UITheme.h no longer contains an unused forward declaration.src/activities/settings/ButtonRemapActivity.cpp (1)
136-141: Help text positioning is tightly coupled to list row count.The calculations
4 * metrics.listRowHeightand5 * metrics.verticalSpacingassume exactly 4 list items. While this matcheskRoleCount, if the role count changes, these magic multipliers would need manual updates. Consider deriving the position from the list's computed bounds or usingkRoleCountin the calculation.♻️ Proposed improvement
// Provide side button actions at the bottom of the screen (split across two lines). + const int helpTextTop = topOffset + kRoleCount * metrics.listRowHeight + kRoleCount * metrics.verticalSpacing; GUI.drawHelpText(renderer, - Rect{0, topOffset + 4 * metrics.listRowHeight + 4 * metrics.verticalSpacing, pageWidth, 20}, + Rect{0, helpTextTop, pageWidth, 20}, tr(STR_REMAP_RESET_HINT)); GUI.drawHelpText(renderer, - Rect{0, topOffset + 4 * metrics.listRowHeight + 5 * metrics.verticalSpacing + 20, pageWidth, 20}, + Rect{0, helpTextTop + metrics.verticalSpacing + 20, pageWidth, 20}, tr(STR_REMAP_CANCEL_HINT));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/activities/settings/ButtonRemapActivity.cpp` around lines 136 - 141, The help text Y offsets are hard-coded with “4 * metrics.listRowHeight” and “5 * metrics.verticalSpacing” which assumes 4 list items; update the calculation to derive the vertical position from the actual list bounds or the canonical role count instead of magic numbers: use the list's computed bounds (e.g. the variable that holds the list Rect or call that computes its height) or replace the literal multipliers with kRoleCount * metrics.listRowHeight and kRoleCount * metrics.verticalSpacing so GUI.drawHelpText calls compute topOffset + listBounds.height (or topOffset + kRoleCount * metrics.listRowHeight) + additional spacing for the two help lines, referencing GUI.drawHelpText, metrics.listRowHeight, metrics.verticalSpacing, and kRoleCount (or the list bounds variable) to locate and update the code.src/components/themes/lyra/Lyra3CoversTheme.h (1)
1-5: Minor: Remove leading blank lines.The file starts with two blank lines before
#pragma once. This is a minor style inconsistency.♻️ Proposed fix
- - `#pragma` once `#include` "components/themes/lyra/LyraTheme.h"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/Lyra3CoversTheme.h` around lines 1 - 5, The file Lyra3CoversTheme.h contains extra leading blank lines before the `#pragma` once directive; open the header (Lyra3CoversTheme.h) and remove any blank lines above the `#pragma` once so the file starts immediately with `#pragma` once, keeping the existing `#include` "components/themes/lyra/LyraTheme.h" unchanged.src/activities/home/MyLibraryActivity.cpp (1)
191-197: Consider passingfilenameby const reference to avoid unnecessary copy.The function takes
std::stringby value, creating a copy on each call. Since the string is only read, passing byconst std::string&would be more efficient, especially when called repeatedly in list rendering.♻️ Proposed fix
-std::string getFileName(std::string filename) { +std::string getFileName(const std::string& filename) { if (filename.back() == '/') { return filename.substr(0, filename.length() - 1); } const auto pos = filename.rfind('.'); return filename.substr(0, pos); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/activities/home/MyLibraryActivity.cpp` around lines 191 - 197, The getFileName function currently takes filename by value, causing an unnecessary copy on each call; change its signature to accept const std::string& filename and update any internal uses accordingly (e.g., in getFileName and any callers if needed) so the function reads from filename without copying; keep the same logic for trimming a trailing '/' and finding the last '.' (ensure you still handle std::string::npos correctly).src/activities/settings/KOReaderAuthActivity.cpp (1)
104-109: Consider using metrics for vertical spacing instead of magic number.The hardcoded
+ 10spacing between lines could usemetrics.verticalSpacingor a fraction thereof for consistency with the theme system used elsewhere in the file.♻️ Proposed fix
} else if (state == SUCCESS) { renderer.drawCenteredText(UI_10_FONT_ID, top, tr(STR_AUTH_SUCCESS), true, EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, top + height + 10, tr(STR_SYNC_READY)); + renderer.drawCenteredText(UI_10_FONT_ID, top + height + metrics.verticalSpacing, tr(STR_SYNC_READY)); } else if (state == FAILED) { renderer.drawCenteredText(UI_10_FONT_ID, top, tr(STR_AUTH_FAILED), true, EpdFontFamily::BOLD); - renderer.drawCenteredText(UI_10_FONT_ID, top + height + 10, errorMessage.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, top + height + metrics.verticalSpacing, errorMessage.c_str()); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/activities/settings/KOReaderAuthActivity.cpp` around lines 104 - 109, The code uses a magic "+ 10" vertical offset when calling renderer.drawCenteredText for the second line in both SUCCESS and FAILED branches; replace that hardcoded 10 with the theme metric (e.g., metrics.verticalSpacing or the appropriate Theme metrics variable used elsewhere in this file) so both calls use top + height + metrics.verticalSpacing (or a fraction like metrics.verticalSpacing / 2 if desired) to keep spacing consistent; update both renderer.drawCenteredText calls that currently use "+ 10" to reference the metric instead.src/components/themes/lyra/Lyra3CoversTheme.cpp (1)
17-21: File-scope mutablecoverWidthis shadowed and unused.The file-scope
coverWidthvariable at line 20 is shadowed by the local variablecoverWidthdeclared at line 54. This makes the file-scope variable unused in this implementation while potentially causing confusion.♻️ Proposed fix - remove unused file-scope variable
namespace { constexpr int hPaddingInSelection = 8; constexpr int cornerRadius = 6; -int coverWidth = 0; } // namespace🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/Lyra3CoversTheme.cpp` around lines 17 - 21, The file-scope mutable variable coverWidth inside the anonymous namespace is shadowed by a local coverWidth (declared near line 54) and is unused—remove the file-scope coverWidth declaration to avoid confusion; keep the anonymous namespace with the constexprs (hPaddingInSelection, cornerRadius) intact and ensure all references use the local coverWidth in the function where it is declared.src/activities/util/KeyboardEntryActivity.cpp (1)
319-320: Consider using clearer symbols for vertical navigation hints.The side button hints use
">"and"<"which typically indicate horizontal direction. For up/down navigation, consider using"^"/"v"or arrow unicode characters if supported by the font, to avoid confusion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/activities/util/KeyboardEntryActivity.cpp` around lines 319 - 320, Replace the horizontal indicators passed to GUI.drawSideButtonHints(renderer, ">", "<") with vertical navigation symbols to avoid confusion; update the call to use "^" and "v" (or "↑" and "↓" if your font supports Unicode) so the side button hints clearly indicate Up/Down navigation.src/activities/network/CrossPointWebServerActivity.cpp (1)
366-386: Consider makingdrawQRCodestatic or moving to anonymous namespace.The
drawQRCodefunction is defined at file scope withoutstaticlinkage. If another translation unit defines a function with the same name, this could cause ODR violations or linking errors.🛠️ Proposed fix
-void drawQRCode(const GfxRenderer& renderer, const int x, const int y, const std::string& data) { +namespace { +void drawQRCode(const GfxRenderer& renderer, const int x, const int y, const std::string& data) { // Implementation... } +} // namespace🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/activities/network/CrossPointWebServerActivity.cpp` around lines 366 - 386, drawQRCode currently has external linkage at file scope which can cause ODR/linkage conflicts; change its linkage to internal by either marking the function static (add the static keyword to drawQRCode) or move its definition into an anonymous namespace at top of file so it becomes translation-unit-local; update any callers in this file to continue using drawQRCode and ensure symbols like QRCode, qrcode_getBufferSize, qrcode_initText, and qrcode_getModule remain visible in that scope.src/components/themes/BaseTheme.cpp (1)
716-719:drawTextFieldignorestextWidthparameter for bracket positioning.The
textWidthparameter is accepted but the brackets are drawn at fixed positions (rect.x + 10andrect.x + rect.width - 15) rather than being positioned relative to the text width. This differs from the LyraTheme implementation which usestextWidthto position a centered underline.🤔 Consider aligning with LyraTheme approach
If the intent is to frame the text, consider using
textWidthto position the brackets around the actual text content, or document the different behavior between themes.🤖 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 716 - 719, BaseTheme::drawTextField currently ignores the textWidth parameter and uses fixed positions for brackets; change it to compute the text's horizontal origin from textWidth (e.g., center the text inside rect or use the same centering logic as LyraTheme) and place the left bracket relative to that text origin and the right bracket at textOrigin + textWidth plus a small gap; update renderer.drawText calls (the ones drawing "[" and "]") to use these computed positions so brackets frame the actual text width instead of fixed offsets.src/components/themes/BaseTheme.h (1)
64-64: Consider usingenum classfor type safety.The
UIIconenum uses a plainenumwhich exposes its enumerators to the enclosing scope, potentially causing naming conflicts. Usingenum classwould provide stronger type safety and scoping.♻️ Suggested change
-enum UIIcon { Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot }; +enum class UIIcon { Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot };This would require updating call sites to use
UIIcon::Folderetc., but provides better namespace hygiene and prevents implicit conversions to int.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/BaseTheme.h` at line 64, Change the plain enum UIIcon to a scoped enum (enum class UIIcon) to prevent its enumerators from leaking into the enclosing scope; update all call sites that currently use raw enumerators (e.g., Folder, Text, Image, etc.) to qualify them as UIIcon::Folder, UIIcon::Text, UIIcon::Image, and adjust any implicit-int usage or comparisons (casts to/from int) to use explicit casts or overloads so code using UIIcon (in BaseTheme.h and its consumers) compiles cleanly.src/components/themes/lyra/LyraTheme.cpp (1)
42-42: Mutable global state in anonymous namespace is problematic.
coverWidthis a mutable variable in the anonymous namespace, which means it persists across calls and could cause issues if multiple theme instances exist or if the cover dimensions change. This should be either:
- A member variable of
LyraTheme, or- Computed/passed each time it's needed
♻️ Suggested approach
Consider making
coverWidtha mutable member ofLyraThemeor computing it locally withindrawRecentBookCover. The current approach caches the width globally after the first bitmap load, which could lead to stale values if covers of different sizes are loaded.// In anonymous namespace, remove: -int coverWidth = 0; // In LyraTheme class (header), add: +mutable int coverWidth_ = 0; // In drawRecentBookCover, update references: - if (coverWidth == 0) { - coverWidth = LyraMetrics::values.homeCoverHeight * 0.6; + if (coverWidth_ == 0) { + coverWidth_ = LyraMetrics::values.homeCoverHeight * 0.6;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/LyraTheme.cpp` at line 42, The global mutable coverWidth in the anonymous namespace should be removed and its value maintained per-theme or computed on demand: make coverWidth a member variable of the LyraTheme class (e.g., add an int coverWidth_ field) or compute the width inside drawRecentBookCover each time by querying the bitmap (replace references to coverWidth with either this->coverWidth_ or a local variable computed from the loaded Bitmap), and update any initialization logic that currently caches the width globally (e.g., where coverWidth is set after bitmap load) to set the new member or local variable instead; update all usages in LyraTheme::drawRecentBookCover and related LyraTheme methods to reference the new member/local name instead of the anonymous-namespace coverWidth.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (59)
lib/GfxRenderer/GfxRenderer.cpplib/I18n/I18nKeys.hlib/I18n/translations/czech.yamllib/I18n/translations/english.yamllib/I18n/translations/french.yamllib/I18n/translations/german.yamllib/I18n/translations/portuguese.yamllib/I18n/translations/russian.yamllib/I18n/translations/spanish.yamllib/I18n/translations/swedish.yamllib/hal/HalDisplay.cpplib/hal/HalDisplay.hopen-x4-sdkscripts/convert_icon.pysrc/CrossPointSettings.hsrc/SettingsList.hsrc/activities/home/HomeActivity.cppsrc/activities/home/MyLibraryActivity.cppsrc/activities/home/RecentBooksActivity.cppsrc/activities/network/CalibreConnectActivity.cppsrc/activities/network/CrossPointWebServerActivity.cppsrc/activities/network/NetworkModeSelectionActivity.cppsrc/activities/network/WifiSelectionActivity.cppsrc/activities/network/WifiSelectionActivity.hsrc/activities/settings/ButtonRemapActivity.cppsrc/activities/settings/CalibreSettingsActivity.cppsrc/activities/settings/CalibreSettingsActivity.hsrc/activities/settings/ClearCacheActivity.cppsrc/activities/settings/KOReaderAuthActivity.cppsrc/activities/settings/KOReaderSettingsActivity.cppsrc/activities/settings/KOReaderSettingsActivity.hsrc/activities/settings/OtaUpdateActivity.cppsrc/activities/settings/SettingsActivity.cppsrc/activities/util/KeyboardEntryActivity.cppsrc/activities/util/KeyboardEntryActivity.hsrc/components/UITheme.cppsrc/components/UITheme.hsrc/components/icons/book.hsrc/components/icons/book24.hsrc/components/icons/cog.hsrc/components/icons/cover.hsrc/components/icons/file24.hsrc/components/icons/folder.hsrc/components/icons/folder24.hsrc/components/icons/hotspot.hsrc/components/icons/image24.hsrc/components/icons/library.hsrc/components/icons/recent.hsrc/components/icons/settings.hsrc/components/icons/settings2.hsrc/components/icons/text24.hsrc/components/icons/transfer.hsrc/components/icons/wifi.hsrc/components/themes/BaseTheme.cppsrc/components/themes/BaseTheme.hsrc/components/themes/lyra/Lyra3CoversTheme.cppsrc/components/themes/lyra/Lyra3CoversTheme.hsrc/components/themes/lyra/LyraTheme.cppsrc/components/themes/lyra/LyraTheme.h
🚧 Files skipped from review as they are similar to previous changes (11)
- src/components/icons/book.h
- src/components/UITheme.cpp
- src/components/icons/folder.h
- src/components/icons/text24.h
- src/SettingsList.h
- src/components/icons/recent.h
- open-x4-sdk
- src/components/icons/cog.h
- src/components/icons/image24.h
- src/activities/home/RecentBooksActivity.cpp
- src/components/icons/book24.h
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-02-16T22:25:35.674Z
Learnt from: whyte-j
Repo: crosspoint-reader/crosspoint-reader PR: 733
File: lib/I18n/I18nKeys.h:271-272
Timestamp: 2026-02-16T22:25:35.674Z
Learning: In the crosspoint-reader i18n system (lib/I18n/), missing translation keys in non-English YAML files are automatically filled with English strings at build time by the gen_i18n.py script. All generated string arrays (STRINGS_EN, STRINGS_ES, etc.) have identical lengths, so runtime array indexing is safe without per-string fallback logic. The system prints "INFO: '{key}' missing in {lang_code}, using English fallback" during code generation when this occurs.
Applied to files:
lib/I18n/translations/spanish.yamllib/I18n/translations/english.yaml
📚 Learning: 2026-02-12T06:57:35.955Z
Learnt from: CaptainFrito
Repo: crosspoint-reader/crosspoint-reader PR: 725
File: src/activities/network/CalibreConnectActivity.cpp:218-264
Timestamp: 2026-02-12T06:57:35.955Z
Learning: In src/activities/network/CalibreConnectActivity.cpp, button hints (« Exit) are intentionally omitted from the SERVER_STARTING and ERROR states—only the SERVER_RUNNING state displays navigation hints. This is a deliberate design decision by the author.
Applied to files:
src/activities/settings/ButtonRemapActivity.cppsrc/activities/network/NetworkModeSelectionActivity.cppsrc/activities/network/CalibreConnectActivity.cppsrc/activities/util/KeyboardEntryActivity.cppsrc/activities/network/CrossPointWebServerActivity.cppsrc/activities/settings/KOReaderAuthActivity.cppsrc/activities/settings/CalibreSettingsActivity.cppsrc/activities/settings/OtaUpdateActivity.cpp
📚 Learning: 2026-02-16T20:43:19.339Z
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().
Applied to files:
src/activities/settings/ClearCacheActivity.cppsrc/activities/network/CalibreConnectActivity.cppsrc/activities/util/KeyboardEntryActivity.hsrc/activities/settings/KOReaderAuthActivity.cppsrc/activities/settings/CalibreSettingsActivity.cppsrc/activities/settings/KOReaderSettingsActivity.cpp
🧬 Code graph analysis (17)
src/activities/settings/ButtonRemapActivity.cpp (3)
src/CrossPointSettings.h (1)
getInstance(181-181)lib/I18n/I18n.cpp (2)
getInstance(15-18)getInstance(15-15)src/main.cpp (1)
renderer(37-37)
src/activities/network/NetworkModeSelectionActivity.cpp (2)
src/components/UITheme.h (1)
getInstance(17-17)lib/I18n/I18n.cpp (2)
getInstance(15-18)getInstance(15-15)
src/components/themes/lyra/Lyra3CoversTheme.h (3)
src/components/themes/lyra/LyraTheme.h (1)
LyraTheme(39-68)src/components/themes/BaseTheme.cpp (2)
drawRecentBookCover(341-634)drawRecentBookCover(341-343)src/components/themes/lyra/Lyra3CoversTheme.cpp (2)
drawRecentBookCover(23-110)drawRecentBookCover(23-25)
lib/hal/HalDisplay.h (1)
lib/hal/HalDisplay.cpp (2)
drawImageTransparent(19-22)drawImageTransparent(19-20)
src/activities/util/KeyboardEntryActivity.cpp (2)
src/components/UITheme.h (1)
getInstance(17-17)lib/I18n/I18n.cpp (2)
getInstance(15-18)getInstance(15-15)
src/activities/settings/KOReaderAuthActivity.cpp (1)
src/components/UITheme.h (1)
getInstance(17-17)
src/components/UITheme.h (1)
src/components/UITheme.cpp (2)
getFileIcon(77-92)getFileIcon(77-77)
src/components/themes/lyra/Lyra3CoversTheme.cpp (4)
src/components/themes/BaseTheme.cpp (3)
drawRecentBookCover(341-634)drawRecentBookCover(341-343)bitmap(370-370)src/components/themes/lyra/LyraTheme.cpp (5)
drawRecentBookCover(411-493)drawRecentBookCover(411-413)bitmap(439-439)drawEmptyRecents(495-501)drawEmptyRecents(495-495)src/activities/home/HomeActivity.cpp (2)
storeCoverBuffer(134-151)storeCoverBuffer(134-134)src/components/UITheme.cpp (2)
getCoverThumbPath(69-75)getCoverThumbPath(69-69)
src/activities/settings/CalibreSettingsActivity.cpp (3)
src/components/UITheme.h (1)
getInstance(17-17)lib/I18n/I18n.cpp (2)
getInstance(15-18)getInstance(15-15)src/SettingsList.h (3)
string(88-91)string(95-98)string(102-105)
src/components/themes/lyra/LyraTheme.h (1)
src/components/themes/lyra/LyraTheme.cpp (10)
drawHeader(165-194)drawHeader(165-165)drawList(247-338)drawList(247-251)drawEmptyRecents(495-501)drawEmptyRecents(495-495)drawTextField(556-560)drawTextField(556-556)drawKeyboardKey(562-572)drawKeyboardKey(562-563)
src/activities/settings/KOReaderSettingsActivity.cpp (4)
src/components/UITheme.h (1)
getInstance(17-17)lib/I18n/I18n.cpp (2)
getInstance(15-18)getInstance(15-15)lib/KOReaderSync/KOReaderCredentialStore.h (1)
getInstance(36-36)src/SettingsList.h (3)
string(88-91)string(95-98)string(102-105)
src/activities/network/WifiSelectionActivity.cpp (1)
src/components/UITheme.h (1)
getInstance(17-17)
src/activities/settings/OtaUpdateActivity.cpp (1)
src/components/UITheme.h (1)
getInstance(17-17)
src/components/themes/lyra/LyraTheme.cpp (3)
src/components/themes/BaseTheme.cpp (2)
drawButtonMenu(636-663)drawButtonMenu(636-638)src/components/UITheme.cpp (2)
getCoverThumbPath(69-75)getCoverThumbPath(69-69)src/activities/home/HomeActivity.cpp (2)
storeCoverBuffer(134-151)storeCoverBuffer(134-134)
src/activities/home/MyLibraryActivity.cpp (1)
src/components/UITheme.cpp (2)
getFileIcon(77-92)getFileIcon(77-77)
src/components/themes/BaseTheme.cpp (1)
src/components/themes/lyra/LyraTheme.cpp (8)
drawHeader(165-194)drawHeader(165-165)drawSubHeader(196-213)drawSubHeader(196-196)drawTextField(556-560)drawTextField(556-556)drawKeyboardKey(562-572)drawKeyboardKey(562-563)
src/components/themes/BaseTheme.h (1)
src/components/themes/BaseTheme.cpp (22)
drawHeader(256-286)drawHeader(256-256)drawSubHeader(288-307)drawSubHeader(288-288)drawTabBar(309-337)drawTabBar(309-310)drawRecentBookCover(341-634)drawRecentBookCover(341-343)drawButtonMenu(636-663)drawButtonMenu(636-638)drawPopup(665-682)drawPopup(665-665)fillPopupProgress(684-695)fillPopupProgress(684-684)drawReadingProgressBar(697-707)drawReadingProgressBar(697-697)drawHelpText(709-714)drawHelpText(709-709)drawTextField(716-719)drawTextField(716-716)drawKeyboardKey(721-730)drawKeyboardKey(721-722)
🪛 Ruff (0.15.0)
scripts/convert_icon.py
[error] 50-50: f-string without any placeholders
Remove extraneous f prefix
(F541)
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/convert_icon.py`:
- Line 50: The string assignment to variable c uses an unnecessary f-string
prefix despite containing no placeholders; remove the leading 'f' from the
literal assigned to c (the assignment "c = f'#pragma once\n#include
<cstdint>\n\n'") so it becomes a plain string literal, ensuring code uses
regular string formatting where needed and clears the static analysis warning.
In `@src/activities/network/CalibreConnectActivity.cpp`:
- Around line 195-198: The instruction block uses SMALL_FONT_ID for drawText but
uses the wrong line height variable (currently using height derived from
UI_10_FONT_ID); update the vertical spacing for the four renderer.drawText calls
so they use renderer.getLineHeight(SMALL_FONT_ID) instead of the existing height
value to compute y offsets (i.e., replace uses of height in that instruction
block with renderer.getLineHeight(SMALL_FONT_ID)) so spacing matches the
SMALL_FONT_ID font metrics.
In `@src/activities/network/CrossPointWebServerActivity.cpp`:
- Around line 388-396: The header/subheader are being drawn twice: once in
render() and again inside renderServerRunning(); remove the duplicated
GUI.drawHeader(...) and GUI.drawSubHeader(...) calls from renderServerRunning()
so that only render() performs header/subheader rendering (keep
renderServerRunning() responsible for the server-specific body UI like
connectedSSID usage and body content); update renderServerRunning() to rely on
render() for headers and only draw its unique elements, leaving symbols
renderServerRunning(), render(), GUI.drawHeader, GUI.drawSubHeader, isApMode,
and connectedSSID as references to locate the code to change.
In `@src/activities/network/WifiSelectionActivity.h`:
- Line 48: selectedNetworkIndex is size_t but selectNetwork currently takes an
int; change selectNetwork(int index) to selectNetwork(size_t index) to align
types and improve safety, then update all callers: where ButtonNavigator returns
int (e.g., usages that pass its return directly), perform an explicit negative
check before converting or cast the int to size_t with a clear guard, and remove
any casts that convert size_t to int when calling selectNetwork; reference the
symbols selectedNetworkIndex and selectNetwork to locate the changes and ensure
all prototypes/signatures and declarations are updated consistently (header and
implementation).
---
Outside diff comments:
In `@src/activities/settings/OtaUpdateActivity.cpp`:
- Around line 99-105: The division by updater.getTotalSize() in
OtaUpdateActivity.cpp can divide by zero; modify the updater progress
calculation (where updaterProgress, updater.getProcessedSize(),
updater.getTotalSize(), and lastUpdaterPercentage are used) to first check that
updater.getTotalSize() > 0 (or >= 1) and only compute the ratio when positive;
if total is zero or negative, set updaterProgress to 0 (or an appropriate
sentinel) and skip the percentage-based early return/update logic so subsequent
casts to int and comparisons cannot use NaN/inf.
---
Duplicate comments:
In `@scripts/convert_icon.py`:
- Around line 15-27: The SVG branch in load_image currently opens the PNG bytes
but doesn't flatten alpha onto a white background like the raster branch; update
the SVG path (after calling svg_to_png_bytes and Image.open) to mirror the
raster handling: convert to 'RGBA', resize to (width, height) with Image.LANCZOS
if needed, create a new white RGBA background, paste the image onto it using the
image alpha channel as mask, and set img to that flattened background so
downstream grayscale/thresholding won't produce dark transparent pixels.
- Around line 61-66: Update the usage string to reference convert_icon.py and
sanitize the C identifier derived from output_name before creating array_name:
replace/normalize invalid characters (e.g., convert hyphens and other
non-alphanumeric chars to underscores), ensure it doesn't start with a digit
(prefix with an underscore or a letter if it does), and then append or format
the suffix (use a safe capitalization like title-casing alphanumeric segments)
to produce array_name (currently created from output_name.capitalize() =>
array_name). Implement this normalization where input_path, output_name, width,
height are unpacked so array_name is always a valid C identifier.
In `@src/activities/settings/OtaUpdateActivity.cpp`:
- Around line 135-138: The NO_UPDATE and FAILED branches render centered text
but omit the Back button hint displayed in other states; update the branches in
OtaUpdateActivity.cpp (the else if (state == NO_UPDATE) and else if (state ==
FAILED) blocks that call renderer.drawCenteredText) to also render the Back hint
using the same renderer button-hint call used elsewhere in this file (so the UI
matches the loop() Back handling), keeping the hint text/style consistent with
the other state branches.
In `@src/components/themes/BaseTheme.cpp`:
- Line 91: The log call uses wrong format specifiers for size_t: update the
LOG_DBG invocation that logs current, total, percent (the call where
LOG_DBG("UI", "Drawing progress bar: current=%u, total=%u, percent=%d", current,
total, percent)) to use safe specifiers for size_t (e.g., "%zu" for current and
total) or explicitly cast current and total to a fixed-width integer type (e.g.,
(unsigned long) or (uint32_t)) and adjust the format string accordingly so the
types and specifiers match.
- Line 22: The hard-coded constexpr subtitleY = 738 in BaseTheme.cpp causes
misplacement when header height or screen size changes; replace this fixed
constant with a computed value at layout time (not a compile-time constexpr)
that derives subtitleY from the actual header height or screen dimensions (e.g.,
subtitleY = headerBottom + desiredOffset or subtitleY = screenHeight *
relativeFactor), using the existing header height accessor or screen size API
your codebase provides; update the code that references subtitleY to use the
computed variable (instead of the constexpr) so subtitle positioning adapts to
dynamic header/screen changes.
In `@src/components/themes/lyra/Lyra3CoversTheme.cpp`:
- Around line 70-78: The empty-cover rendering block uses
LyraMetrics::values.homeCoverHeight instead of the theme-specific
Lyra3CoversMetrics::values.homeCoverHeight; update all occurrences in the
conditional that draws the empty cover (the calls to renderer.drawRect,
renderer.fillRect, and any position math that references
LyraMetrics::values.homeCoverHeight) to use
Lyra3CoversMetrics::values.homeCoverHeight so the Lyra3CoversTheme uses its own
metrics for height and vertical calculations.
---
Nitpick comments:
In `@lib/GfxRenderer/GfxRenderer.cpp`:
- Around line 323-325: The current comment misstates the condition used to
compute roundedSides: the code sets roundedSides based on (!roundTopLeft ||
!roundTopRight || !roundBottomLeft || !roundBottomRight) which triggers when any
corner is not rounded, so update the comment to clearly describe this heuristic
and its limitations (it assumes either all corners or effectively one side is
rounded and may produce odd results for diagonal or three-corner cases) and
either (A) document that diagonal/three-corner cases are unsupported, or (B)
replace the heuristic by computing an explicit roundedCount (e.g., count true
corner flags) and use that to derive roundedSides/maxRadius to handle mixed
cases; reference the roundedSides, cornerRadius and maxRadius symbols when
making the change.
In `@src/activities/home/MyLibraryActivity.cpp`:
- Around line 191-197: The getFileName function currently takes filename by
value, causing an unnecessary copy on each call; change its signature to accept
const std::string& filename and update any internal uses accordingly (e.g., in
getFileName and any callers if needed) so the function reads from filename
without copying; keep the same logic for trimming a trailing '/' and finding the
last '.' (ensure you still handle std::string::npos correctly).
In `@src/activities/network/CrossPointWebServerActivity.cpp`:
- Around line 366-386: drawQRCode currently has external linkage at file scope
which can cause ODR/linkage conflicts; change its linkage to internal by either
marking the function static (add the static keyword to drawQRCode) or move its
definition into an anonymous namespace at top of file so it becomes
translation-unit-local; update any callers in this file to continue using
drawQRCode and ensure symbols like QRCode, qrcode_getBufferSize,
qrcode_initText, and qrcode_getModule remain visible in that scope.
In `@src/activities/settings/ButtonRemapActivity.cpp`:
- Around line 136-141: The help text Y offsets are hard-coded with “4 *
metrics.listRowHeight” and “5 * metrics.verticalSpacing” which assumes 4 list
items; update the calculation to derive the vertical position from the actual
list bounds or the canonical role count instead of magic numbers: use the list's
computed bounds (e.g. the variable that holds the list Rect or call that
computes its height) or replace the literal multipliers with kRoleCount *
metrics.listRowHeight and kRoleCount * metrics.verticalSpacing so
GUI.drawHelpText calls compute topOffset + listBounds.height (or topOffset +
kRoleCount * metrics.listRowHeight) + additional spacing for the two help lines,
referencing GUI.drawHelpText, metrics.listRowHeight, metrics.verticalSpacing,
and kRoleCount (or the list bounds variable) to locate and update the code.
In `@src/activities/settings/KOReaderAuthActivity.cpp`:
- Around line 104-109: The code uses a magic "+ 10" vertical offset when calling
renderer.drawCenteredText for the second line in both SUCCESS and FAILED
branches; replace that hardcoded 10 with the theme metric (e.g.,
metrics.verticalSpacing or the appropriate Theme metrics variable used elsewhere
in this file) so both calls use top + height + metrics.verticalSpacing (or a
fraction like metrics.verticalSpacing / 2 if desired) to keep spacing
consistent; update both renderer.drawCenteredText calls that currently use "+
10" to reference the metric instead.
In `@src/activities/settings/OtaUpdateActivity.cpp`:
- Around line 87-105: Compute updaterProgress and perform the 2% throttle check
before doing any heavy rendering (i.e., before calling renderer.clearScreen()
and GUI.drawHeader()). Specifically, call updater.getProcessedSize() and
updater.getTotalSize(), compute updaterProgress, compare the throttled
percentage against lastUpdaterPercentage, and return early if unchanged; only if
the check passes proceed to clear the screen, draw the header and continue
updating lastUpdaterPercentage. Update references to updaterProgress,
lastUpdaterPercentage, renderer.clearScreen(), GUI.drawHeader(),
updater.getProcessedSize(), and updater.getTotalSize() accordingly so no
unnecessary work is done when throttled.
In `@src/activities/util/KeyboardEntryActivity.cpp`:
- Around line 319-320: Replace the horizontal indicators passed to
GUI.drawSideButtonHints(renderer, ">", "<") with vertical navigation symbols to
avoid confusion; update the call to use "^" and "v" (or "↑" and "↓" if your font
supports Unicode) so the side button hints clearly indicate Up/Down navigation.
In `@src/components/themes/BaseTheme.cpp`:
- Around line 716-719: BaseTheme::drawTextField currently ignores the textWidth
parameter and uses fixed positions for brackets; change it to compute the text's
horizontal origin from textWidth (e.g., center the text inside rect or use the
same centering logic as LyraTheme) and place the left bracket relative to that
text origin and the right bracket at textOrigin + textWidth plus a small gap;
update renderer.drawText calls (the ones drawing "[" and "]") to use these
computed positions so brackets frame the actual text width instead of fixed
offsets.
In `@src/components/themes/BaseTheme.h`:
- Line 64: Change the plain enum UIIcon to a scoped enum (enum class UIIcon) to
prevent its enumerators from leaking into the enclosing scope; update all call
sites that currently use raw enumerators (e.g., Folder, Text, Image, etc.) to
qualify them as UIIcon::Folder, UIIcon::Text, UIIcon::Image, and adjust any
implicit-int usage or comparisons (casts to/from int) to use explicit casts or
overloads so code using UIIcon (in BaseTheme.h and its consumers) compiles
cleanly.
In `@src/components/themes/lyra/Lyra3CoversTheme.cpp`:
- Around line 17-21: The file-scope mutable variable coverWidth inside the
anonymous namespace is shadowed by a local coverWidth (declared near line 54)
and is unused—remove the file-scope coverWidth declaration to avoid confusion;
keep the anonymous namespace with the constexprs (hPaddingInSelection,
cornerRadius) intact and ensure all references use the local coverWidth in the
function where it is declared.
In `@src/components/themes/lyra/Lyra3CoversTheme.h`:
- Around line 1-5: The file Lyra3CoversTheme.h contains extra leading blank
lines before the `#pragma` once directive; open the header (Lyra3CoversTheme.h)
and remove any blank lines above the `#pragma` once so the file starts immediately
with `#pragma` once, keeping the existing `#include`
"components/themes/lyra/LyraTheme.h" unchanged.
In `@src/components/themes/lyra/LyraTheme.cpp`:
- Line 42: The global mutable coverWidth in the anonymous namespace should be
removed and its value maintained per-theme or computed on demand: make
coverWidth a member variable of the LyraTheme class (e.g., add an int
coverWidth_ field) or compute the width inside drawRecentBookCover each time by
querying the bitmap (replace references to coverWidth with either
this->coverWidth_ or a local variable computed from the loaded Bitmap), and
update any initialization logic that currently caches the width globally (e.g.,
where coverWidth is set after bitmap load) to set the new member or local
variable instead; update all usages in LyraTheme::drawRecentBookCover and
related LyraTheme methods to reference the new member/local name instead of the
anonymous-namespace coverWidth.
In `@src/components/UITheme.h`:
- Line 26: Change the getFileIcon API to accept the filename by const reference
to avoid copies: update the declaration static UIIcon getFileIcon(const
std::string& filename) in UITheme.h and update the corresponding definition in
UITheme.cpp to match (function name getFileIcon). Also update any call sites
that relied on the by-value signature (pass std::string or const std::string& as
before) to ensure they compile. Rebuild and run the file browser performance
regression test to confirm improvement.
- Around line 9-10: Remove the unused forward declaration of MappedInputManager
from UITheme.h; if the type is only referenced in UITheme.cpp, move the forward
declaration or the `#include` for MappedInputManager into UITheme.cpp (or include
the header where it is actually used) so UITheme.h no longer contains an unused
forward declaration.
| byte |= (bit << (7 - b)) | ||
| packed.append(byte) | ||
| # Format as C array | ||
| c = f'#pragma once\n#include <cstdint>\n\n' |
There was a problem hiding this comment.
Remove extraneous f prefix from string literal.
This string has no placeholders, so the f prefix is unnecessary and was flagged by static analysis.
🔧 Proposed fix
- c = f'#pragma once\n#include <cstdint>\n\n'
+ c = '#pragma once\n#include <cstdint>\n\n'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| c = f'#pragma once\n#include <cstdint>\n\n' | |
| c = '#pragma once\n#include <cstdint>\n\n' |
🧰 Tools
🪛 Ruff (0.15.0)
[error] 50-50: f-string without any placeholders
Remove extraneous f prefix
(F541)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/convert_icon.py` at line 50, The string assignment to variable c uses
an unnecessary f-string prefix despite containing no placeholders; remove the
leading 'f' from the literal assigned to c (the assignment "c = f'#pragma
once\n#include <cstdint>\n\n'") so it becomes a plain string literal, ensuring
code uses regular string formatting where needed and clears the static analysis
warning.
| renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y, tr(STR_CALIBRE_INSTRUCTION_1)); | ||
| renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y + height, tr(STR_CALIBRE_INSTRUCTION_2)); | ||
| renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y + height * 2, tr(STR_CALIBRE_INSTRUCTION_3)); | ||
| renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, y + height * 3, tr(STR_CALIBRE_INSTRUCTION_4)); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find font ID definitions
rg -n "SMALL_FONT_ID|UI_10_FONT_ID" --type-add 'header:*.h' --type-add 'cpp:*.cpp' | head -50Repository: crosspoint-reader/crosspoint-reader
Length of output: 6816
🏁 Script executed:
# Check the specific file and lines around the height calculation
cat -n src/activities/network/CalibreConnectActivity.cpp | sed -n '170,200p'Repository: crosspoint-reader/crosspoint-reader
Length of output: 2185
🏁 Script executed:
# Find the font family definitions in main.cpp
cat -n src/main.cpp | sed -n '270,285p'Repository: crosspoint-reader/crosspoint-reader
Length of output: 821
🏁 Script executed:
# Search for font ID definitions
fd -e h . | xargs grep -l "SMALL_FONT_ID\|UI_10_FONT_ID" | head -5Repository: crosspoint-reader/crosspoint-reader
Length of output: 92
🏁 Script executed:
# Check if there's a fonts definitions file
fd -e h . | xargs grep -l "ui10FontFamily\|smallFontFamily" 2>/dev/nullRepository: crosspoint-reader/crosspoint-reader
Length of output: 63
🏁 Script executed:
# Check fontIds.h for font ID definitions
cat src/fontIds.hRepository: crosspoint-reader/crosspoint-reader
Length of output: 796
🏁 Script executed:
# Search for where ui10FontFamily and smallFontFamily are defined
rg -n "ui10FontFamily|smallFontFamily" src/main.cpp | head -20Repository: crosspoint-reader/crosspoint-reader
Length of output: 309
🏁 Script executed:
# Look for font family definitions in main.cpp earlier lines
cat -n src/main.cpp | sed -n '1,100p' | grep -i "font"Repository: crosspoint-reader/crosspoint-reader
Length of output: 4185
🏁 Script executed:
# Find smallFont and ui10RegularFont definitions
rg -n "EpdFont smallFont|EpdFont ui10RegularFont" src/main.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 171
🏁 Script executed:
# Look for these font definitions - they might be in builtinFonts headers
cat -n src/main.cpp | sed -n '100,130p'Repository: crosspoint-reader/crosspoint-reader
Length of output: 1964
🏁 Script executed:
# Verify the actual line heights by checking if there are any comments or docs about font metrics
rg -n "getLineHeight|getTextHeight" src/activities/network/CalibreConnectActivity.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 216
Use the correct font's line height for instruction text spacing.
Lines 195-198 use SMALL_FONT_ID (Noto Sans 8pt) with vertical spacing calculated from UI_10_FONT_ID line height (Ubuntu 10pt). Since these are different fonts with different point sizes, they will have different line heights, resulting in incorrect spacing. Replace height with renderer.getLineHeight(SMALL_FONT_ID) for the instruction block spacing.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/activities/network/CalibreConnectActivity.cpp` around lines 195 - 198,
The instruction block uses SMALL_FONT_ID for drawText but uses the wrong line
height variable (currently using height derived from UI_10_FONT_ID); update the
vertical spacing for the four renderer.drawText calls so they use
renderer.getLineHeight(SMALL_FONT_ID) instead of the existing height value to
compute y offsets (i.e., replace uses of height in that instruction block with
renderer.getLineHeight(SMALL_FONT_ID)) so spacing matches the SMALL_FONT_ID font
metrics.
| void CrossPointWebServerActivity::renderServerRunning() const { | ||
| // Use consistent line spacing | ||
| constexpr int LINE_SPACING = 28; // Space between lines | ||
| auto metrics = UITheme::getInstance().getMetrics(); | ||
| const auto pageWidth = renderer.getScreenWidth(); | ||
|
|
||
| renderer.drawCenteredText(UI_12_FONT_ID, 15, tr(STR_FILE_TRANSFER), true, EpdFontFamily::BOLD); | ||
| GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, | ||
| isApMode ? tr(STR_HOTSPOT_MODE) : tr(STR_FILE_TRANSFER), nullptr); | ||
| GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, | ||
| connectedSSID.c_str()); | ||
|
|
There was a problem hiding this comment.
Duplicate header/subheader rendering detected.
The header and subheader are drawn twice:
- In
render()at lines 350-355 - Again in
renderServerRunning()at lines 392-395
This causes unnecessary rendering overhead and potential visual artifacts if the two calls produce slightly different results.
🐛 Proposed fix - remove duplicate from renderServerRunning
void CrossPointWebServerActivity::renderServerRunning() const {
auto metrics = UITheme::getInstance().getMetrics();
const auto pageWidth = renderer.getScreenWidth();
- GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight},
- isApMode ? tr(STR_HOTSPOT_MODE) : tr(STR_FILE_TRANSFER), nullptr);
- GUI.drawSubHeader(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight},
- connectedSSID.c_str());
-
int startY = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 2;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/activities/network/CrossPointWebServerActivity.cpp` around lines 388 -
396, The header/subheader are being drawn twice: once in render() and again
inside renderServerRunning(); remove the duplicated GUI.drawHeader(...) and
GUI.drawSubHeader(...) calls from renderServerRunning() so that only render()
performs header/subheader rendering (keep renderServerRunning() responsible for
the server-specific body UI like connectedSSID usage and body content); update
renderServerRunning() to rely on render() for headers and only draw its unique
elements, leaving symbols renderServerRunning(), render(), GUI.drawHeader,
GUI.drawSubHeader, isApMode, and connectedSSID as references to locate the code
to change.
|
|
||
| WifiSelectionState state = WifiSelectionState::SCANNING; | ||
| int selectedNetworkIndex = 0; | ||
| size_t selectedNetworkIndex = 0; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate the file and examine the relevant lines
find . -name "WifiSelectionActivity.h" -type fRepository: crosspoint-reader/crosspoint-reader
Length of output: 127
🏁 Script executed:
# Check the file content around the mentioned lines
head -100 src/activities/network/WifiSelectionActivity.h | tail -60Repository: crosspoint-reader/crosspoint-reader
Length of output: 2021
🏁 Script executed:
# Search for selectNetwork method declarations and call sites
rg -nP --type=cpp -B2 -A2 'selectNetwork\s*\('Repository: crosspoint-reader/crosspoint-reader
Length of output: 1342
🏁 Script executed:
# Search for selectedNetworkIndex usage
rg -nP --type=cpp -B2 -A2 'selectedNetworkIndex'Repository: crosspoint-reader/crosspoint-reader
Length of output: 4456
🏁 Script executed:
# Find ButtonNavigator implementation
find . -name "*ButtonNavigator*" -type fRepository: crosspoint-reader/crosspoint-reader
Length of output: 138
🏁 Script executed:
# Search for nextIndex and previousIndex implementations
rg -nP --type=cpp -B2 -A5 'nextIndex|previousIndex' --max-count=10Repository: crosspoint-reader/crosspoint-reader
Length of output: 17931
Align parameter type with size_t index variable.
selectedNetworkIndex uses size_t, but selectNetwork(int) accepts a signed int parameter. While bounds checking in selectNetwork() prevents invalid access, the implicit conversions at lines 439 and 444 (ButtonNavigator returns int) and line 413 (passing size_t to int) introduce unnecessary type mismatches. Aligning the parameter to size_t improves type safety and clarity.
🔧 Suggested header alignment
- void selectNetwork(int index);
+ void selectNetwork(size_t index);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/activities/network/WifiSelectionActivity.h` at line 48,
selectedNetworkIndex is size_t but selectNetwork currently takes an int; change
selectNetwork(int index) to selectNetwork(size_t index) to align types and
improve safety, then update all callers: where ButtonNavigator returns int
(e.g., usages that pass its return directly), perform an explicit negative check
before converting or cast the int to size_t with a clear guard, and remove any
casts that convert size_t to int when calling selectNetwork; reference the
symbols selectedNetworkIndex and selectNetwork to locate the changes and ensure
all prototypes/signatures and declarations are updated consistently (header and
implementation).
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/components/themes/lyra/LyraTheme.cpp (2)
451-459: Consider extracting magic numbers for icon positioning.The icon offset
24appears both here and inLyra3CoversTheme.cpp(line 75). Consider defining a constant likeconstexpr int coverIconOffset = 24;for consistency and maintainability.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/LyraTheme.cpp` around lines 451 - 459, The drawIcon call in the hasCover false branch uses the magic number 24 for X/Y offsets (renderer.drawIcon(CoverIcon, ... + 24, ... + 24, 32, 32)); introduce a named constant (e.g., constexpr int coverIconOffset = 24) in the Lyra theme source or header shared with Lyra3CoversTheme, replace the hardcoded 24s with coverIconOffset in LyraTheme.cpp (and update Lyra3CoversTheme.cpp line 75 to use the same constant) so positioning is consistent and maintainable while leaving the existing renderer.drawIcon and CoverIcon usage unchanged.
39-42: Remove unusedmainMenuColumnsconstant or add a clarifying comment.The constant
mainMenuColumns = 2at line 41 is never referenced anywhere in the codebase. Either remove it or add a comment explaining its intended purpose if it's planned for future use.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/LyraTheme.cpp` around lines 39 - 42, The constant mainMenuColumns in LyraTheme.cpp is unused; either remove the declaration constexpr int mainMenuColumns = 2 from LyraTheme.cpp, or if it’s intended for future layout logic, keep it but add a one-line comment above the declaration explaining its intended purpose (e.g., "number of columns for main menu layout - reserved for future use") so its presence is intentional and clear; update any relevant unit tests or style checks if they expect no unused symbols.src/components/themes/BaseTheme.h (1)
64-65: Consider usingenum classfor stronger type safety.The plain
enum UIIconplaces its enumerators (e.g.,File,Text,Image) in the enclosing namespace. These generic names risk collisions with other identifiers. Usingenum class UIIconwould scope the values and require explicit qualification (e.g.,UIIcon::File).♻️ Proposed refactor
-enum UIIcon { Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot }; +enum class UIIcon { Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot };Note: This would require updating all usages to use the
UIIcon::prefix (which already appears to be the convention in the codebase based on the LyraTheme.cpp usage).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/BaseTheme.h` around lines 64 - 65, Change the plain enum UIIcon to a scoped enum (enum class UIIcon) to prevent polluting the enclosing namespace; update all call sites to use the scoped qualifiers (e.g., UIIcon::File, UIIcon::Text, UIIcon::Image, etc.), including usage in LyraTheme.cpp and any switch/statements or variable declarations that currently reference unqualified enumerators, and ensure any implicit conversions to/from integers are made explicit where needed.src/components/UITheme.h (1)
25-25: Use const reference to avoid unnecessary copies in hot path.The signature takes
std::stringby value, which creates a copy on every call. Since this function is called in the rendering loop (bothRecentBooksActivity::render()andMyLibraryActivity::render()), it's a hot path. The implementation only reads the filename (checking extensions and directory status), never modifies it, makingconst std::string&the appropriate choice.Update both the declaration and definition:
Required changes
- static UIIcon getFileIcon(std::string filename); + static UIIcon getFileIcon(const std::string& filename);-UIIcon UITheme::getFileIcon(std::string filename) { +UIIcon UITheme::getFileIcon(const std::string& filename) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/UITheme.h` at line 25, Change the hot-path function UIIcon getFileIcon(std::string filename) to accept a const reference: UIIcon getFileIcon(const std::string& filename); update the corresponding definition implementation (the function with the same name) to match the new signature and ensure all callers (e.g., RecentBooksActivity::render and MyLibraryActivity::render) continue to compile; this avoids unnecessary copies on each render while preserving const-correctness since the function only reads filename.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
lib/GfxRenderer/GfxRenderer.cppsrc/activities/home/HomeActivity.cppsrc/activities/home/MyLibraryActivity.cppsrc/components/UITheme.hsrc/components/themes/BaseTheme.cppsrc/components/themes/BaseTheme.hsrc/components/themes/lyra/Lyra3CoversTheme.cppsrc/components/themes/lyra/LyraTheme.cppsrc/components/themes/lyra/LyraTheme.h
🚧 Files skipped from review as they are similar to previous changes (3)
- lib/GfxRenderer/GfxRenderer.cpp
- src/activities/home/MyLibraryActivity.cpp
- src/components/themes/lyra/LyraTheme.h
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/UITheme.h (1)
src/components/UITheme.cpp (2)
getFileIcon(77-92)getFileIcon(77-77)
⏰ 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 comments (9)
src/activities/home/HomeActivity.cpp (2)
232-237: LGTM!The
menuIconsvector is correctly aligned withmenuItems, and the OPDSLibraryicon is inserted at the same index (2) as the menu label, maintaining proper correspondence between items and icons.
240-247: LGTM!The lambdas correctly capture local vectors by reference for use within the synchronous
drawButtonMenucall. The icons lambda follows the same pattern as the existing label-mapper lambda.src/components/themes/lyra/Lyra3CoversTheme.cpp (1)
68-76: LGTM! Empty cover placeholder rendering with icon.The empty cover rendering correctly uses
Lyra3CoversMetrics::values.homeCoverHeightconsistently, and theCoverIconprovides visual feedback when no cover is available. The past review concern about inconsistent metrics namespace has been addressed.src/components/themes/BaseTheme.h (1)
113-118: LGTM! Updated method signatures for icon support.The
drawListanddrawButtonMenusignatures are updated to acceptUIIcon-based icon callbacks. Default parameters maintain backward compatibility.Also applies to: 128-130
src/components/themes/BaseTheme.cpp (2)
186-190: LGTM! UpdateddrawListsignature acceptsUIIconcallback.The
BaseThemeimplementation intentionally does not render icons (classic theme behavior). Derived themes likeLyraThemeoverride this method to render icons. The signature change maintains API consistency across themes.
636-638: LGTM! UpdateddrawButtonMenusignature acceptsUIIconcallback.Similar to
drawList, the base implementation doesn't render icons but accepts the parameter for interface consistency with derived themes.src/components/themes/lyra/LyraTheme.cpp (3)
247-251: LGTM! Icon rendering indrawListis well-implemented.The icon size adapts based on subtitle presence (32px for rows with subtitles, 24px otherwise), and layout calculations properly account for icon width. The icon is correctly fetched via
iconForNameand rendered withdrawIcon.Also applies to: 280-317
503-534: LGTM!drawButtonMenucorrectly handles icon rendering.The previous review concerns have been addressed:
- The label string is now stored in
labelStr(line 518), preventing the dangling pointer issue.- The
rowIconparameter is now properly used viarowIcon(i)to determine which icon to render (lines 525-526).
44-83: > Likely an incorrect or invalid review comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/UITheme.h`:
- Line 25: UITheme::getFileIcon currently calls filename.back() without checking
for an empty string; add an early guard at the start of UITheme::getFileIcon to
detect filename.empty() and return a safe default UIIcon (e.g.,
UIIcon::GenericFile or UIIcon::Unknown) instead of accessing filename.back(),
ensuring the public API doesn’t UB on empty inputs.
---
Duplicate comments:
In `@src/components/themes/BaseTheme.cpp`:
- Line 22: The hard-coded subtitleY = 738 in BaseTheme.cpp causes layout
breakage on different screen sizes; replace this magic constant with a dynamic
calculation (e.g., compute subtitleY from the current viewport or container
height and the header/footer metrics) inside the BaseTheme code path that
positions subtitles—use existing APIs or members that provide screen size or
header height (e.g., a getScreenHeight(), theme metrics, or Header::height())
and compute subtitleY = headerHeight + desiredOffset (or a percentage of the
screen) so subtitle positioning adapts to varying screen sizes.
---
Nitpick comments:
In `@src/components/themes/BaseTheme.h`:
- Around line 64-65: Change the plain enum UIIcon to a scoped enum (enum class
UIIcon) to prevent polluting the enclosing namespace; update all call sites to
use the scoped qualifiers (e.g., UIIcon::File, UIIcon::Text, UIIcon::Image,
etc.), including usage in LyraTheme.cpp and any switch/statements or variable
declarations that currently reference unqualified enumerators, and ensure any
implicit conversions to/from integers are made explicit where needed.
In `@src/components/themes/lyra/LyraTheme.cpp`:
- Around line 451-459: The drawIcon call in the hasCover false branch uses the
magic number 24 for X/Y offsets (renderer.drawIcon(CoverIcon, ... + 24, ... +
24, 32, 32)); introduce a named constant (e.g., constexpr int coverIconOffset =
24) in the Lyra theme source or header shared with Lyra3CoversTheme, replace the
hardcoded 24s with coverIconOffset in LyraTheme.cpp (and update
Lyra3CoversTheme.cpp line 75 to use the same constant) so positioning is
consistent and maintainable while leaving the existing renderer.drawIcon and
CoverIcon usage unchanged.
- Around line 39-42: The constant mainMenuColumns in LyraTheme.cpp is unused;
either remove the declaration constexpr int mainMenuColumns = 2 from
LyraTheme.cpp, or if it’s intended for future layout logic, keep it but add a
one-line comment above the declaration explaining its intended purpose (e.g.,
"number of columns for main menu layout - reserved for future use") so its
presence is intentional and clear; update any relevant unit tests or style
checks if they expect no unused symbols.
In `@src/components/UITheme.h`:
- Line 25: Change the hot-path function UIIcon getFileIcon(std::string filename)
to accept a const reference: UIIcon getFileIcon(const std::string& filename);
update the corresponding definition implementation (the function with the same
name) to match the new signature and ensure all callers (e.g.,
RecentBooksActivity::render and MyLibraryActivity::render) continue to compile;
this avoids unnecessary copies on each render while preserving const-correctness
since the function only reads filename.
| static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints, | ||
| bool hasSubtitle); | ||
| static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight); | ||
| static UIIcon getFileIcon(std::string filename); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the getFileIcon implementation in UITheme.cpp
rg -n 'getFileIcon' src/components/UITheme.cpp -A 15Repository: crosspoint-reader/crosspoint-reader
Length of output: 657
🏁 Script executed:
#!/bin/bash
# Find all call sites of getFileIcon
rg -n '\bgetFileIcon\s*\(' --type cpp --type h -B 2 -A 2Repository: crosspoint-reader/crosspoint-reader
Length of output: 1714
🏁 Script executed:
#!/bin/bash
# Check RecentBooksActivity to understand recentBooks source
rg -n 'recentBooks' src/activities/home/RecentBooksActivity.cpp -B 5 -A 1 | head -40Repository: crosspoint-reader/crosspoint-reader
Length of output: 1311
🏁 Script executed:
#!/bin/bash
# Check MyLibraryActivity to understand files source
rg -n '\bfiles\b' src/activities/home/MyLibraryActivity.cpp -B 3 -A 1 | grep -A 3 -B 3 'getFileIcon\|files ='Repository: crosspoint-reader/crosspoint-reader
Length of output: 372
🏁 Script executed:
#!/bin/bash
# Check where 'files' variable is populated in MyLibraryActivity
rg -n 'files\s*=' src/activities/home/MyLibraryActivity.cpp -B 3 -A 3Repository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Check the type of files variable in MyLibraryActivity
rg -n 'std::vector|files' src/activities/home/MyLibraryActivity.h -B 2 -A 2Repository: crosspoint-reader/crosspoint-reader
Length of output: 190
🏁 Script executed:
#!/bin/bash
# Find where files vector is populated in MyLibraryActivity.cpp
rg -n 'files\.push_back\|files\.emplace_back\|\.listDir\|Storage' src/activities/home/MyLibraryActivity.cpp -B 2 -A 2Repository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Search for file population logic in MyLibraryActivity.cpp
rg -n 'files' src/activities/home/MyLibraryActivity.cpp -B 2 -A 2 | head -60Repository: crosspoint-reader/crosspoint-reader
Length of output: 1988
🏁 Script executed:
#!/bin/bash
# Check if there are any other potential sources for getFileIcon calls
# Look for any dynamic construction or user input paths
rg -n 'getFileIcon' --type cpp --type hRepository: crosspoint-reader/crosspoint-reader
Length of output: 494
Add defensive guard for empty filename to prevent undefined behavior.
UITheme::getFileIcon at line 78 of src/components/UITheme.cpp calls filename.back() without checking if the string is empty. While current call sites only pass valid file system paths from directory listings, the public API does not enforce this contract. Add a guard to safely handle empty inputs:
UIIcon UITheme::getFileIcon(std::string filename) {
+ if (filename.empty()) {
+ return File;
+ }
if (filename.back() == '/') {
return Folder;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/UITheme.h` at line 25, UITheme::getFileIcon currently calls
filename.back() without checking for an empty string; add an early guard at the
start of UITheme::getFileIcon to detect filename.empty() and return a safe
default UIIcon (e.g., UIIcon::GenericFile or UIIcon::Unknown) instead of
accessing filename.back(), ensuring the public API doesn’t UB on empty inputs.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/components/themes/lyra/LyraTheme.cpp (1)
503-533: Consider truncating menu labels after adding icons.
Icons reduce horizontal space, so long labels can clip or crowd. Truncating to the remaining width keeps layout stable.♻️ Suggested adjustment
- const char* label = labelStr.c_str(); int textX = tileRect.x + 16; const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); const int textY = tileRect.y + (LyraMetrics::values.menuRowHeight - lineHeight) / 2; if (rowIcon != nullptr) { UIIcon icon = rowIcon(i); const uint8_t* iconBitmap = iconForName(icon, mainMenuIconSize); if (iconBitmap != nullptr) { renderer.drawIcon(iconBitmap, textX, textY + 3, mainMenuIconSize, mainMenuIconSize); textX += mainMenuIconSize + hPaddingInSelection + 2; } } - renderer.drawText(UI_12_FONT_ID, textX, textY, label, true); + const int maxLabelWidth = tileRect.width - (textX - tileRect.x) - hPaddingInSelection; + auto truncatedLabel = + renderer.truncatedText(UI_12_FONT_ID, labelStr.c_str(), maxLabelWidth, EpdFontFamily::REGULAR); + renderer.drawText(UI_12_FONT_ID, textX, textY, truncatedLabel.c_str(), true);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/themes/lyra/LyraTheme.cpp` around lines 503 - 533, In LyraTheme::drawButtonMenu the label can overflow when an icon is drawn; compute the available width for text inside tileRect after accounting for initial textX, mainMenuIconSize and hPaddingInSelection, then measure and truncate the string from buttonLabel(i) (append ellipsis) so renderer.drawText(UI_12_FONT_ID, textX, textY, ...) is passed a label that fits; use renderer text-measure API (e.g., getTextWidth or getSubstringWidth) to iteratively trim or binary-search the maximum characters that fit the available width before calling drawText.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/themes/lyra/LyraTheme.cpp
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/themes/lyra/LyraTheme.cpp (1)
src/components/themes/BaseTheme.cpp (2)
drawList(186-254)drawList(186-190)
⏰ 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: build
- GitHub Check: cppcheck
🔇 Additional comments (4)
src/components/themes/lyra/LyraTheme.cpp (4)
13-83: Icon includes and UIIcon size mapping look consistent.
184-184: Line-end adjustments for header, subheader, and tab bar look good.Also applies to: 212-212, 244-244
247-337: List icon integration aligns cleanly with UIIcon-based rendering.
452-458: Empty-cover fallback rendering is clear and self-contained.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/components/themes/lyra/LyraTheme.cpp`:
- Around line 503-533: In LyraTheme::drawButtonMenu the label can overflow when
an icon is drawn; compute the available width for text inside tileRect after
accounting for initial textX, mainMenuIconSize and hPaddingInSelection, then
measure and truncate the string from buttonLabel(i) (append ellipsis) so
renderer.drawText(UI_12_FONT_ID, textX, textY, ...) is passed a label that fits;
use renderer text-measure API (e.g., getTextWidth or getSubstringWidth) to
iteratively trim or binary-search the maximum characters that fit the available
width before calling drawText.
|
They look incredible!! Thanks @CaptainFrito |
/!\ This PR depends on crosspoint-reader#732 being merged first Also requires the open-x4-epaper/community-sdk#18 PR ## Summary Lyra theme icons on the home menu, in the file browser and on empty book covers     ## Additional Context - Added a function to the open-x4-sdk renderer to draw transparent images - Added a scripts/convert_icon.py script to convert svg/png icons into a C array that can be directly imported into the project. Usage: ```bash python ./scripts/convert_icon.py 'path/to/icon.png' cover 32 32 ``` This will create a components/icons/cover.h file with a C array called CoverIcon, of size 32x32px. Lyra uses icons from https://lucide.dev/icons with a stroke width of 2px, that can be downloaded with any desired size on the site. > The file browser is noticeably slower with the addition of icons, and using an image buffer like on the home page doesn't help very much. Any suggestions to optimize this are welcome. --- ### 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? _**PARTIALLY**_ The icon conversion python script was generated by Copilot as I am not a python dev. --------- Co-authored-by: Dave Allie <[email protected]>
/!\ This PR depends on crosspoint-reader#732 being merged first Also requires the open-x4-epaper/community-sdk#18 PR ## Summary Lyra theme icons on the home menu, in the file browser and on empty book covers     ## Additional Context - Added a function to the open-x4-sdk renderer to draw transparent images - Added a scripts/convert_icon.py script to convert svg/png icons into a C array that can be directly imported into the project. Usage: ```bash python ./scripts/convert_icon.py 'path/to/icon.png' cover 32 32 ``` This will create a components/icons/cover.h file with a C array called CoverIcon, of size 32x32px. Lyra uses icons from https://lucide.dev/icons with a stroke width of 2px, that can be downloaded with any desired size on the site. > The file browser is noticeably slower with the addition of icons, and using an image buffer like on the home page doesn't help very much. Any suggestions to optimize this are welcome. --- ### 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? _**PARTIALLY**_ The icon conversion python script was generated by Copilot as I am not a python dev. --------- Co-authored-by: Dave Allie <[email protected]>
/!\ This PR depends on crosspoint-reader#732 being merged first Also requires the open-x4-epaper/community-sdk#18 PR ## Summary Lyra theme icons on the home menu, in the file browser and on empty book covers     ## Additional Context - Added a function to the open-x4-sdk renderer to draw transparent images - Added a scripts/convert_icon.py script to convert svg/png icons into a C array that can be directly imported into the project. Usage: ```bash python ./scripts/convert_icon.py 'path/to/icon.png' cover 32 32 ``` This will create a components/icons/cover.h file with a C array called CoverIcon, of size 32x32px. Lyra uses icons from https://lucide.dev/icons with a stroke width of 2px, that can be downloaded with any desired size on the site. > The file browser is noticeably slower with the addition of icons, and using an image buffer like on the home page doesn't help very much. Any suggestions to optimize this are welcome. --- ### 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? _**PARTIALLY**_ The icon conversion python script was generated by Copilot as I am not a python dev. --------- Co-authored-by: Dave Allie <[email protected]>

/!\ This PR depends on #732 being merged first
Also requires the open-x4-epaper/community-sdk#18 PR
Summary
Lyra theme icons on the home menu, in the file browser and on empty book covers
Additional Context
python ./scripts/convert_icon.py 'path/to/icon.png' cover 32 32This will create a components/icons/cover.h file with a C array called CoverIcon, of size 32x32px. Lyra uses icons from https://lucide.dev/icons with a stroke width of 2px, that can be downloaded with any desired size on the site.
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? PARTIALLY
The icon conversion python script was generated by Copilot as I am not a python dev.