A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Lyra Icons (#725)

/!\ This PR depends on
https://github.com/crosspoint-reader/crosspoint-reader/pull/732 being
merged first

Also requires the
https://github.com/open-x4-epaper/community-sdk/pull/18 PR

## Summary

Lyra theme icons on the home menu, in the file browser and on empty book
covers

![IMG_8023
Medium](https://github.com/user-attachments/assets/ba7c1407-94d2-4353-80ff-d5b800c6ac5b)
![IMG_8024
Medium](https://github.com/user-attachments/assets/edb59e13-b1c9-4c86-bef3-c61cc8134e64)
![IMG_7958
Medium](https://github.com/user-attachments/assets/d3079ce1-95f0-43f4-bbc7-1f747cc70203)
![IMG_8033
Medium](https://github.com/user-attachments/assets/f3e2e03b-0fa8-47b7-8717-c0b71361b7a8)


## 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 <dave@daveallie.com>

authored by

CaptainFrito
Dave Allie
and committed by
GitHub
fdcd71e9 e7ee6ff0

+434 -34
+1 -1
lib/GfxRenderer/GfxRenderer.cpp
··· 419 419 } 420 420 421 421 void GfxRenderer::drawIcon(const uint8_t bitmap[], const int x, const int y, const int width, const int height) const { 422 - display.drawImage(bitmap, y, getScreenWidth() - width - x, height, width); 422 + display.drawImageTransparent(bitmap, y, getScreenWidth() - width - x, height, width); 423 423 } 424 424 425 425 void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, const int maxWidth, const int maxHeight,
+5
lib/hal/HalDisplay.cpp
··· 16 16 einkDisplay.drawImage(imageData, x, y, w, h, fromProgmem); 17 17 } 18 18 19 + void HalDisplay::drawImageTransparent(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, 20 + bool fromProgmem) const { 21 + einkDisplay.drawImageTransparent(imageData, x, y, w, h, fromProgmem); 22 + } 23 + 19 24 EInkDisplay::RefreshMode convertRefreshMode(HalDisplay::RefreshMode mode) { 20 25 switch (mode) { 21 26 case HalDisplay::FULL_REFRESH:
+2
lib/hal/HalDisplay.h
··· 30 30 void clearScreen(uint8_t color = 0xFF) const; 31 31 void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, 32 32 bool fromProgmem = false) const; 33 + void drawImageTransparent(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, 34 + bool fromProgmem = false) const; 33 35 34 36 void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); 35 37 void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false);
+80
scripts/convert_icon.py
··· 1 + import sys 2 + import os 3 + from PIL import Image 4 + import cairosvg 5 + import io 6 + 7 + threshold = 128 8 + 9 + def svg_to_png_bytes(svg_path, width, height): 10 + with open(svg_path, 'rb') as f: 11 + svg_data = f.read() 12 + png_bytes = cairosvg.svg2png(bytestring=svg_data, output_width=width, output_height=height) 13 + return png_bytes 14 + 15 + def load_image(path, width, height): 16 + ext = os.path.splitext(path)[1].lower() 17 + if ext == '.svg': 18 + png_bytes = svg_to_png_bytes(path, width, height) 19 + img = Image.open(io.BytesIO(png_bytes)) 20 + else: 21 + img = Image.open(path) 22 + img = img.convert('RGBA') 23 + img = img.resize((width, height), Image.LANCZOS) 24 + # Flatten alpha: paste on white background 25 + background = Image.new('RGBA', img.size, (255, 255, 255, 255)) 26 + background.paste(img, mask=img.split()[3]) 27 + img = background 28 + # Rotate 90 degrees counterclockwise 29 + img = img.rotate(90, expand=True) 30 + return img 31 + 32 + def image_to_c_array(img, array_name): 33 + # Convert to grayscale, then threshold to get white=1, black=0 34 + # Convert to grayscale 35 + img = img.convert('L') 36 + width, height = img.size 37 + pixels = list(img.getdata()) 38 + packed = [] 39 + for y in range(height): 40 + for x in range(0, width, 8): 41 + byte = 0 42 + for b in range(8): 43 + if x + b < width: 44 + v = pixels[y * width + x + b] 45 + # 1 for white, 0 for black 46 + bit = 1 if v >= threshold else 0 47 + byte |= (bit << (7 - b)) 48 + packed.append(byte) 49 + # Format as C array 50 + c = f'#pragma once\n#include <cstdint>\n\n' 51 + c += f'// size: {width}x{height}\n' 52 + c += f'static const uint8_t {array_name}[] = {{\n ' 53 + for i, v in enumerate(packed): 54 + c += f'0x{v:02X}, ' 55 + if (i + 1) % 16 == 0: 56 + c += '\n ' 57 + c = c.rstrip(', \n') + '\n};\n' 58 + return c 59 + 60 + def main(): 61 + if len(sys.argv) < 5: 62 + print('Usage: python convert_image.py input.png output_name width height') 63 + sys.exit(1) 64 + input_path, output_name, width, height = sys.argv[1:5] 65 + array_name = output_name.capitalize() + 'Icon' 66 + width, height = int(width), int(height) 67 + img = load_image(input_path, width, height) 68 + c_array = image_to_c_array(img, array_name) 69 + 70 + # Always save to src/components/icons/[output_name].h relative to project root 71 + project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 72 + output_dir = os.path.join(project_root, 'src', 'components', 'icons') 73 + os.makedirs(output_dir, exist_ok=True) 74 + output_path = os.path.join(output_dir, f'{output_name}.h') 75 + with open(output_path, 'w') as f: 76 + f.write(c_array) 77 + print(f'Wrote {output_path}') 78 + 79 + if __name__ == '__main__': 80 + main()
+5 -1
src/activities/home/HomeActivity.cpp
··· 229 229 // Build menu items dynamically 230 230 std::vector<const char*> menuItems = {tr(STR_BROWSE_FILES), tr(STR_MENU_RECENT_BOOKS), tr(STR_FILE_TRANSFER), 231 231 tr(STR_SETTINGS_TITLE)}; 232 + std::vector<UIIcon> menuIcons = {Folder, Recent, Transfer, Settings}; 233 + 232 234 if (hasOpdsUrl) { 233 235 // Insert OPDS Browser after My Library 234 236 menuItems.insert(menuItems.begin() + 2, tr(STR_OPDS_BROWSER)); 237 + menuIcons.insert(menuIcons.begin() + 2, Library); 235 238 } 236 239 237 240 GUI.drawButtonMenu( ··· 240 243 pageHeight - (metrics.headerHeight + metrics.homeTopPadding + metrics.verticalSpacing * 2 + 241 244 metrics.buttonHintsHeight)}, 242 245 static_cast<int>(menuItems.size()), selectorIndex - recentBooks.size(), 243 - [&menuItems](int index) { return std::string(menuItems[index]); }, nullptr); 246 + [&menuItems](int index) { return std::string(menuItems[index]); }, 247 + [&menuIcons](int index) { return menuIcons[index]; }); 244 248 245 249 const auto labels = mappedInput.mapLabels("", tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); 246 250 GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
+10 -1
src/activities/home/MyLibraryActivity.cpp
··· 188 188 }); 189 189 } 190 190 191 + std::string getFileName(std::string filename) { 192 + if (filename.back() == '/') { 193 + return filename.substr(0, filename.length() - 1); 194 + } 195 + const auto pos = filename.rfind('.'); 196 + return filename.substr(0, pos); 197 + } 198 + 191 199 void MyLibraryActivity::render(Activity::RenderLock&&) { 192 200 renderer.clearScreen(); 193 201 ··· 205 213 } else { 206 214 GUI.drawList( 207 215 renderer, Rect{0, contentTop, pageWidth, contentHeight}, files.size(), selectorIndex, 208 - [this](int index) { return files[index]; }, nullptr, nullptr, nullptr); 216 + [this](int index) { return getFileName(files[index]); }, nullptr, 217 + [this](int index) { return UITheme::getFileIcon(files[index]); }); 209 218 } 210 219 211 220 // Help text
+1 -1
src/activities/home/RecentBooksActivity.cpp
··· 102 102 GUI.drawList( 103 103 renderer, Rect{0, contentTop, pageWidth, contentHeight}, recentBooks.size(), selectorIndex, 104 104 [this](int index) { return recentBooks[index].title; }, [this](int index) { return recentBooks[index].author; }, 105 - nullptr, nullptr); 105 + [this](int index) { return UITheme::getFileIcon(recentBooks[index].path); }); 106 106 } 107 107 108 108 // Help text
+2 -1
src/activities/network/NetworkModeSelectionActivity.cpp
··· 70 70 StrId::STR_CREATE_HOTSPOT}; 71 71 static constexpr StrId menuDescs[MENU_ITEM_COUNT] = {StrId::STR_JOIN_DESC, StrId::STR_CALIBRE_DESC, 72 72 StrId::STR_HOTSPOT_DESC}; 73 + static constexpr UIIcon menuIcons[MENU_ITEM_COUNT] = {UIIcon::Wifi, UIIcon::Library, UIIcon::Hotspot}; 73 74 74 75 GUI.drawList( 75 76 renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast<int>(MENU_ITEM_COUNT), selectedIndex, 76 77 [](int index) { return std::string(I18N.get(menuItems[index])); }, 77 - [](int index) { return std::string(I18N.get(menuDescs[index])); }); 78 + [](int index) { return std::string(I18N.get(menuDescs[index])); }, [](int index) { return menuIcons[index]; }); 78 79 79 80 // Draw help text at bottom 80 81 const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
+23
src/components/UITheme.cpp
··· 5 5 6 6 #include <memory> 7 7 8 + #include "MappedInputManager.h" 8 9 #include "RecentBooksStore.h" 9 10 #include "components/themes/BaseTheme.h" 10 11 #include "components/themes/lyra/Lyra3CoversTheme.h" 11 12 #include "components/themes/lyra/LyraTheme.h" 13 + #include "util/StringUtils.h" 14 + 15 + namespace { 16 + constexpr int SKIP_PAGE_MS = 700; 17 + } // namespace 12 18 13 19 UITheme UITheme::instance; 14 20 ··· 67 73 } 68 74 return coverBmpPath; 69 75 } 76 + 77 + UIIcon UITheme::getFileIcon(std::string filename) { 78 + if (filename.back() == '/') { 79 + return Folder; 80 + } 81 + if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") || 82 + StringUtils::checkFileExtension(filename, ".xtc")) { 83 + return Book; 84 + } 85 + if (StringUtils::checkFileExtension(filename, ".txt") || StringUtils::checkFileExtension(filename, ".md")) { 86 + return Text; 87 + } 88 + if (StringUtils::checkFileExtension(filename, ".bmp")) { 89 + return Image; 90 + } 91 + return File; 92 + }
+3
src/components/UITheme.h
··· 5 5 #include "CrossPointSettings.h" 6 6 #include "components/themes/BaseTheme.h" 7 7 8 + class MappedInputManager; 9 + 8 10 class UITheme { 9 11 // Static instance 10 12 static UITheme instance; ··· 20 22 static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints, 21 23 bool hasSubtitle); 22 24 static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight); 25 + static UIIcon getFileIcon(std::string filename); 23 26 24 27 private: 25 28 const ThemeMetrics* currentMetrics;
+12
src/components/icons/book.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t BookIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 7 + 0x07, 0xC0, 0x00, 0x00, 0x03, 0xCF, 0xFF, 0xF8, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 8 + 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 9 + 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 10 + 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 11 + 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xC3, 0xFF, 0xFC, 0x23, 0xE0, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 12 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+9
src/components/icons/book24.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 24x24 5 + static const uint8_t Book24Icon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x01, 0x9F, 0xFF, 0x39, 7 + 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 8 + 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 9 + 0x9F, 0xFF, 0x39, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/cog.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t CogIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xBE, 0x7D, 0xFF, 0xFF, 0x1C, 0x38, 7 + 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x82, 0x41, 0xFF, 0xFF, 0x0E, 0x70, 0xFF, 0xF6, 0x3E, 0x7C, 0x6F, 0xE0, 0x7E, 8 + 0x7E, 0x07, 0xF0, 0xFE, 0x7F, 0x0F, 0xF8, 0xFE, 0x7F, 0x1F, 0xF9, 0xFE, 0x7F, 0x9F, 0xF9, 0xF8, 0x1F, 0x9F, 0xF3, 9 + 0xF8, 0x1F, 0xCF, 0xC3, 0xF3, 0xCF, 0xC3, 0xC3, 0xF1, 0x8F, 0xC3, 0xF3, 0xE1, 0x87, 0xCF, 0xF9, 0xC0, 0x03, 0x9F, 10 + 0xF9, 0x0E, 0x70, 0x9F, 0xF8, 0x3F, 0xFC, 0x1F, 0xF0, 0x7F, 0xFE, 0x0F, 0xE0, 0x7F, 0xFE, 0x07, 0xF6, 0x3F, 0xFC, 11 + 0x6F, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0x83, 0xC1, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x1C, 0x38, 0xFF, 0xFF, 0xBE, 12 + 0x7D, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/cover.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t CoverIcon[] = { 6 + 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFE, 0x1F, 0xE0, 0x00, 0x02, 0x1F, 0xE0, 0x00, 0x03, 7 + 0x1F, 0xE0, 0x00, 0x03, 0x1F, 0xF0, 0x00, 0x01, 0x1F, 0xF0, 0x00, 0x01, 0x1F, 0xF0, 0x00, 0x01, 0x9F, 0xF8, 0x00, 8 + 0x00, 0x9F, 0xF8, 0x00, 0x00, 0xDF, 0xFC, 0x00, 0x00, 0x6F, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 9 + 0x80, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x1F, 10 + 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x6F, 0xF8, 0x00, 0x00, 0xDF, 0xF8, 0x00, 0x00, 0x9F, 0xF0, 0x00, 0x01, 11 + 0x9F, 0xF0, 0x00, 0x01, 0x1F, 0xE0, 0x00, 0x01, 0x1F, 0xE0, 0x00, 0x03, 0x1F, 0xE0, 0x00, 0x02, 0x1F, 0xE0, 0x00, 12 + 0x02, 0x1F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x1F};
+9
src/components/icons/file24.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 24x24 5 + static const uint8_t File24Icon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xF8, 0x00, 0x03, 0xF0, 0x7F, 0xF1, 7 + 0xE2, 0x7F, 0xF9, 0xC6, 0x7F, 0xF9, 0x8E, 0x7F, 0xF9, 0x80, 0x7F, 0xF9, 0x80, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 8 + 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 9 + 0x8F, 0xFF, 0xF1, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/folder.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t FolderIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x1F, 0xFE, 0x3F, 0xFF, 7 + 0x9F, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 8 + 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 9 + 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFC, 0x7F, 0xFF, 0xCF, 0xF0, 0xFF, 0xFF, 0xCF, 10 + 0xE1, 0xFF, 0xFF, 0xCF, 0xE3, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 11 + 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0x8F, 0xE7, 0xFF, 0xFF, 0x8F, 0xF0, 0x00, 12 + 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+9
src/components/icons/folder24.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 24x24 5 + static const uint8_t Folder24Icon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFC, 0x00, 0x0F, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 7 + 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 8 + 0xF9, 0xFF, 0xE7, 0xF3, 0xFF, 0xE7, 0xE7, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 9 + 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/hotspot.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t HotspotIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFE, 0x0F, 0xF0, 7 + 0x7F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0xFF, 0xFE, 0x3F, 0xFD, 0xF8, 0x1F, 0xBF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xC3, 8 + 0xC3, 0xFF, 0xFF, 0xCF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 9 + 0xF9, 0x9F, 0xFF, 0xFF, 0xF3, 0xCF, 0xFF, 0xFF, 0xF3, 0xCF, 0xFF, 0xFF, 0xF9, 0x9F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 10 + 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xF3, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xE0, 0x07, 11 + 0xFF, 0xFD, 0xF8, 0x1F, 0xBF, 0xFC, 0x7F, 0xFF, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFE, 0x0F, 0xF0, 0x7F, 0xFF, 0x80, 12 + 0x01, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+9
src/components/icons/image24.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 24x24 5 + static const uint8_t Image24Icon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xCF, 0xF1, 0xF3, 0xCF, 0xE3, 0xF3, 7 + 0xCF, 0xE7, 0xF3, 0xCF, 0xEF, 0xF3, 0xCF, 0xE7, 0xF3, 0xCF, 0xE3, 0xF3, 0xCF, 0xF1, 0xF3, 0xCF, 0xF8, 0xF3, 8 + 0xCF, 0x3C, 0x73, 0xCE, 0x1E, 0x33, 0xCC, 0xCF, 0x13, 0xCC, 0xCF, 0x83, 0xCE, 0x1F, 0xC3, 0xCF, 0x3F, 0xE3, 9 + 0xCF, 0xFF, 0xF3, 0xCF, 0xFF, 0xF3, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/library.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t LibraryIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 7 + 0x1F, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xC1, 0xCF, 0xFF, 0xFE, 0x07, 0xE7, 0xFF, 0xF0, 0x3F, 0xE7, 0xFF, 0x81, 8 + 0xFF, 0x87, 0xFC, 0x0F, 0xFC, 0x0F, 0xF0, 0x3F, 0xF0, 0x3F, 0xE1, 0xFF, 0x81, 0xFF, 0xE7, 0xFC, 0x0F, 0xFF, 0xE7, 9 + 0xE0, 0x7F, 0xFF, 0xF3, 0x83, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x07, 0xE7, 0xFF, 0xFF, 0xE7, 10 + 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 11 + 0x07, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE0, 0x00, 0x00, 0x07, 0xF0, 0x00, 12 + 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/recent.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t RecentIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 7 + 0x07, 0xC0, 0x00, 0x00, 0x03, 0xCF, 0xFF, 0xF8, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 8 + 0xFC, 0xF3, 0xC0, 0x03, 0xFC, 0xF3, 0xC0, 0x03, 0xFC, 0xF3, 0xCF, 0xC7, 0xFC, 0xF3, 0xCF, 0x8F, 0xFC, 0xF3, 0xCF, 9 + 0x1F, 0xFC, 0xF3, 0xCF, 0x8F, 0xFC, 0xF3, 0xCF, 0x87, 0xFC, 0xF3, 0xCF, 0xC3, 0xFC, 0xF3, 0xC0, 0x03, 0xFC, 0xF3, 10 + 0xC0, 0x03, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 11 + 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xC3, 0xFF, 0xFC, 0x23, 0xE0, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 12 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/settings.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t SettingsIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xE1, 0xFF, 0xFF, 0x03, 0xC0, 7 + 0xFF, 0xFE, 0x30, 0x8C, 0x7F, 0xFE, 0x78, 0x1E, 0x7F, 0xFE, 0x7C, 0x7E, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 8 + 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x7C, 0x3E, 0x3F, 0xF0, 0xF0, 0x0F, 0x07, 0xC1, 0xF3, 0xCF, 0x83, 0xC7, 9 + 0xE7, 0xE7, 0xE3, 0xCF, 0xE7, 0xE7, 0xF3, 0xCF, 0xE7, 0xE7, 0xF3, 0xC7, 0xE7, 0xE7, 0xE3, 0xC1, 0xF3, 0xCF, 0x83, 10 + 0xE0, 0xF0, 0x0F, 0x0F, 0xFC, 0x7C, 0x3E, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 11 + 0x7F, 0xFE, 0x7E, 0x3E, 0x7F, 0xFE, 0x78, 0x1E, 0x7F, 0xFE, 0x31, 0x0C, 0x7F, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x87, 12 + 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/settings2.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t Settings2Icon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 7 + 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0x3F, 0xE3, 0x1F, 0xFF, 0x3F, 0xCF, 0x9F, 0xFF, 0x3F, 0xCF, 0xCF, 0xFF, 0x3F, 8 + 0xCF, 0xCF, 0xFF, 0x3F, 0xCF, 0x8F, 0xFF, 0x3F, 0xC7, 0x9F, 0xFF, 0x3F, 0xE0, 0x1F, 0xFF, 0x3F, 0xF0, 0x7F, 0xFF, 9 + 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFE, 0x0F, 0xFC, 0xFF, 10 + 0xF8, 0x07, 0xFC, 0xFF, 0xF9, 0xE3, 0xFC, 0xFF, 0xF1, 0xF3, 0xFC, 0xFF, 0xF3, 0xF3, 0xFC, 0xFF, 0xF3, 0xF3, 0xFC, 11 + 0xFF, 0xF9, 0xF3, 0xFC, 0xFF, 0xF8, 0xC7, 0xFC, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 12 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+9
src/components/icons/text24.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 24x24 5 + static const uint8_t Text24Icon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xF8, 0x00, 0x03, 0xF0, 0x7F, 0xF9, 7 + 0xE2, 0x7F, 0xF9, 0xC6, 0x73, 0x39, 0xCE, 0x73, 0x39, 0x80, 0x73, 0x39, 0x80, 0xF3, 0x39, 0x9F, 0xF3, 0x39, 8 + 0x9F, 0xF3, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0xFF, 0xF9, 9 + 0x9F, 0xFF, 0xF9, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/transfer.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t TransferIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, 7 + 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0, 0x4F, 0xFF, 0xFF, 0xE2, 0x4F, 0xFF, 0xFF, 0xE2, 8 + 0x47, 0xFF, 0xFF, 0xC6, 0x67, 0xFF, 0xFF, 0xC6, 0x63, 0xFF, 0xFF, 0xCE, 0x73, 0xFF, 0xFF, 0x8E, 0x71, 0xFF, 0xFF, 9 + 0x9E, 0x79, 0xFF, 0xFF, 0x1E, 0x78, 0xFF, 0xFF, 0x3E, 0x7C, 0xFF, 0xFE, 0x3E, 0x7C, 0x7F, 0xFE, 0x3E, 0x7E, 0x7F, 10 + 0xFC, 0x7E, 0x7E, 0x3F, 0xFC, 0x7E, 0x7F, 0x3F, 0xF8, 0xFE, 0x7F, 0x3F, 0xF8, 0xFE, 0x7F, 0x9F, 0xF1, 0xFC, 0x1F, 11 + 0x9F, 0xF1, 0xE0, 0x07, 0xCF, 0xE3, 0x83, 0x80, 0xCF, 0xE0, 0x0F, 0xF0, 0x07, 0xE0, 0x7F, 0xFC, 0x07, 0xF3, 0xFF, 12 + 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+12
src/components/icons/wifi.h
··· 1 + #pragma once 2 + #include <cstdint> 3 + 4 + // size: 32x32 5 + static const uint8_t WifiIcon[] = { 6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 7 + 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x1F, 0x3F, 0xFF, 0xFE, 0x3E, 0x3F, 0xFF, 0xFE, 0x3C, 0x7F, 0xFF, 0xFE, 0x7C, 8 + 0x7F, 0xFF, 0xFC, 0x78, 0xFD, 0xFF, 0xFC, 0x78, 0xF1, 0xFF, 0xFC, 0xF9, 0xF1, 0xFF, 0xFC, 0xF1, 0xE3, 0xFF, 0xFC, 9 + 0xF1, 0xE3, 0xFF, 0xFC, 0xF1, 0xE7, 0xDF, 0xFC, 0xF3, 0xE7, 0xDF, 0xFC, 0xF1, 0xE7, 0xFF, 0xFC, 0xF9, 0xE3, 0xFF, 10 + 0xFC, 0xF9, 0xF3, 0xFF, 0xFC, 0xF9, 0xF1, 0xFF, 0xFC, 0x7C, 0xFB, 0xFF, 0xFE, 0x7C, 0xFF, 0xFF, 0xFE, 0x7C, 0x7F, 11 + 0xFF, 0xFF, 0x3E, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xCF, 12 + 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+3 -3
src/components/themes/BaseTheme.cpp
··· 186 186 void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, 187 187 const std::function<std::string(int index)>& rowTitle, 188 188 const std::function<std::string(int index)>& rowSubtitle, 189 - const std::function<std::string(int index)>& rowIcon, 189 + const std::function<UIIcon(int index)>& rowIcon, 190 190 const std::function<std::string(int index)>& rowValue, bool highlightValue) const { 191 191 int rowHeight = 192 192 (rowSubtitle != nullptr) ? BaseMetrics::values.listWithSubtitleRowHeight : BaseMetrics::values.listRowHeight; ··· 635 635 636 636 void BaseTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, 637 637 const std::function<std::string(int index)>& buttonLabel, 638 - const std::function<std::string(int index)>& rowIcon) const { 638 + const std::function<UIIcon(int index)>& rowIcon) const { 639 639 for (int i = 0; i < buttonCount; ++i) { 640 640 const int tileY = BaseMetrics::values.verticalSpacing + rect.y + 641 641 static_cast<int>(i) * (BaseMetrics::values.menuRowHeight + BaseMetrics::values.menuSpacing); ··· 727 727 renderer.drawText(UI_10_FONT_ID, textX + itemWidth, rect.y, "]"); 728 728 } 729 729 renderer.drawText(UI_10_FONT_ID, textX, rect.y, label); 730 - } 730 + }
+5 -3
src/components/themes/BaseTheme.h
··· 61 61 bool keyboardCenteredText; 62 62 }; 63 63 64 + enum UIIcon { Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot }; 65 + 64 66 // Default theme implementation (Classic Theme) 65 67 // Additional themes can inherit from this and override methods as needed 66 68 ··· 111 113 virtual void drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, 112 114 const std::function<std::string(int index)>& rowTitle, 113 115 const std::function<std::string(int index)>& rowSubtitle = nullptr, 114 - const std::function<std::string(int index)>& rowIcon = nullptr, 116 + const std::function<UIIcon(int index)>& rowIcon = nullptr, 115 117 const std::function<std::string(int index)>& rowValue = nullptr, 116 118 bool highlightValue = false) const; 117 119 virtual void drawHeader(const GfxRenderer& renderer, Rect rect, const char* title, ··· 125 127 bool& bufferRestored, std::function<bool()> storeCoverBuffer) const; 126 128 virtual void drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, 127 129 const std::function<std::string(int index)>& buttonLabel, 128 - const std::function<std::string(int index)>& rowIcon) const; 130 + const std::function<UIIcon(int index)>& rowIcon) const; 129 131 virtual Rect drawPopup(const GfxRenderer& renderer, const char* message) const; 130 132 virtual void fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const; 131 133 virtual void drawReadingProgressBar(const GfxRenderer& renderer, const size_t bookProgress) const; 132 134 virtual void drawHelpText(const GfxRenderer& renderer, Rect rect, const char* label) const; 133 135 virtual void drawTextField(const GfxRenderer& renderer, Rect rect, const int textWidth) const; 134 136 virtual void drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const char* label, const bool isSelected) const; 135 - }; 137 + };
+9 -4
src/components/themes/lyra/Lyra3CoversTheme.cpp
··· 6 6 #include <cstdint> 7 7 #include <string> 8 8 9 - #include "Battery.h" 10 9 #include "RecentBooksStore.h" 11 10 #include "components/UITheme.h" 11 + #include "components/icons/cover.h" 12 12 #include "fontIds.h" 13 - #include "util/StringUtils.h" 14 13 15 14 // Internal constants 16 15 namespace { ··· 67 66 } 68 67 69 68 if (!hasCover) { 69 + // Render empty cover 70 70 renderer.drawRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection, 71 - tileWidth - 2 * hPaddingInSelection, Lyra3CoversMetrics::values.homeCoverHeight); 71 + tileWidth - 2 * hPaddingInSelection, Lyra3CoversMetrics::values.homeCoverHeight, true); 72 + renderer.fillRect(tileX + hPaddingInSelection, 73 + tileY + hPaddingInSelection + (Lyra3CoversMetrics::values.homeCoverHeight / 3), 74 + tileWidth - 2 * hPaddingInSelection, 2 * Lyra3CoversMetrics::values.homeCoverHeight / 3, 75 + true); 76 + renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32); 72 77 } 73 78 } 74 79 ··· 101 106 } else { 102 107 drawEmptyRecents(renderer, rect); 103 108 } 104 - } 109 + }
+105 -16
src/components/themes/lyra/LyraTheme.cpp
··· 10 10 #include "Battery.h" 11 11 #include "RecentBooksStore.h" 12 12 #include "components/UITheme.h" 13 + #include "components/icons/book.h" 14 + #include "components/icons/book24.h" 15 + #include "components/icons/cover.h" 16 + #include "components/icons/file24.h" 17 + #include "components/icons/folder.h" 18 + #include "components/icons/folder24.h" 19 + #include "components/icons/hotspot.h" 20 + #include "components/icons/image24.h" 21 + #include "components/icons/library.h" 22 + #include "components/icons/recent.h" 23 + #include "components/icons/settings2.h" 24 + #include "components/icons/text24.h" 25 + #include "components/icons/transfer.h" 26 + #include "components/icons/wifi.h" 13 27 #include "fontIds.h" 14 - #include "util/StringUtils.h" 15 28 16 29 // Internal constants 17 30 namespace { ··· 23 36 constexpr int popupMarginY = 12; 24 37 constexpr int maxSubtitleWidth = 100; 25 38 constexpr int maxListValueWidth = 200; 39 + constexpr int mainMenuIconSize = 32; 40 + constexpr int listIconSize = 24; 41 + constexpr int mainMenuColumns = 2; 26 42 int coverWidth = 0; 43 + 44 + const uint8_t* iconForName(UIIcon icon, int size) { 45 + if (size == 24) { 46 + switch (icon) { 47 + case UIIcon::Folder: 48 + return Folder24Icon; 49 + case UIIcon::Text: 50 + return Text24Icon; 51 + case UIIcon::Image: 52 + return Image24Icon; 53 + case UIIcon::Book: 54 + return Book24Icon; 55 + case UIIcon::File: 56 + return File24Icon; 57 + default: 58 + return nullptr; 59 + } 60 + } else if (size == 32) { 61 + switch (icon) { 62 + case UIIcon::Folder: 63 + return FolderIcon; 64 + case UIIcon::Book: 65 + return BookIcon; 66 + case UIIcon::Recent: 67 + return RecentIcon; 68 + case UIIcon::Settings: 69 + return Settings2Icon; 70 + case UIIcon::Transfer: 71 + return TransferIcon; 72 + case UIIcon::Library: 73 + return LibraryIcon; 74 + case UIIcon::Wifi: 75 + return WifiIcon; 76 + case UIIcon::Hotspot: 77 + return HotspotIcon; 78 + default: 79 + return nullptr; 80 + } 81 + } 82 + return nullptr; 83 + } 27 84 } // namespace 28 85 29 86 void LyraTheme::drawBatteryLeft(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { ··· 124 181 renderer.drawText(UI_12_FONT_ID, rect.x + LyraMetrics::values.contentSidePadding, 125 182 rect.y + LyraMetrics::values.batteryBarHeight + 3, truncatedTitle.c_str(), true, 126 183 EpdFontFamily::BOLD); 127 - renderer.drawLine(rect.x, rect.y + rect.height - 3, rect.x + rect.width, rect.y + rect.height - 3, 3, true); 184 + renderer.drawLine(rect.x, rect.y + rect.height - 3, rect.x + rect.width - 1, rect.y + rect.height - 3, 3, true); 128 185 } 129 186 130 187 if (subtitle) { ··· 152 209 UI_10_FONT_ID, label, rect.width - LyraMetrics::values.contentSidePadding - rightSpace, EpdFontFamily::REGULAR); 153 210 renderer.drawText(UI_10_FONT_ID, currentX, rect.y + 6, truncatedLabel.c_str(), true, EpdFontFamily::REGULAR); 154 211 155 - renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1, true); 212 + renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1, true); 156 213 } 157 214 158 215 void LyraTheme::drawTabBar(const GfxRenderer& renderer, Rect rect, const std::vector<TabInfo>& tabs, ··· 184 241 currentX += textWidth + LyraMetrics::values.tabSpacing + 2 * hPaddingInSelection; 185 242 } 186 243 187 - renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1, true); 244 + renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1, true); 188 245 } 189 246 190 247 void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, 191 248 const std::function<std::string(int index)>& rowTitle, 192 249 const std::function<std::string(int index)>& rowSubtitle, 193 - const std::function<std::string(int index)>& rowIcon, 250 + const std::function<UIIcon(int index)>& rowIcon, 194 251 const std::function<std::string(int index)>& rowValue, bool highlightValue) const { 195 252 int rowHeight = 196 253 (rowSubtitle != nullptr) ? LyraMetrics::values.listWithSubtitleRowHeight : LyraMetrics::values.listRowHeight; ··· 218 275 renderer.fillRoundedRect(LyraMetrics::values.contentSidePadding, rect.y + selectedIndex % pageItems * rowHeight, 219 276 contentWidth - LyraMetrics::values.contentSidePadding * 2, rowHeight, cornerRadius, 220 277 Color::LightGray); 278 + } 279 + 280 + int textX = rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection; 281 + int textWidth = contentWidth - LyraMetrics::values.contentSidePadding * 2 - hPaddingInSelection * 2; 282 + int iconSize; 283 + if (rowIcon != nullptr) { 284 + iconSize = (rowSubtitle != nullptr) ? mainMenuIconSize : listIconSize; 285 + textX += iconSize + hPaddingInSelection; 286 + textWidth -= iconSize + hPaddingInSelection; 221 287 } 222 288 223 289 // Draw all items 224 290 const auto pageStartIndex = selectedIndex / pageItems * pageItems; 291 + int iconY = (rowSubtitle != nullptr) ? 16 : 10; 225 292 for (int i = pageStartIndex; i < itemCount && i < pageStartIndex + pageItems; i++) { 226 293 const int itemY = rect.y + (i % pageItems) * rowHeight; 294 + int rowTextWidth = textWidth; 227 295 228 296 // Draw name 229 297 int valueWidth = 0; ··· 232 300 valueText = rowValue(i); 233 301 valueText = renderer.truncatedText(UI_10_FONT_ID, valueText.c_str(), maxListValueWidth); 234 302 valueWidth = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str()) + hPaddingInSelection; 303 + rowTextWidth -= valueWidth; 235 304 } 236 - int textWidth = contentWidth - LyraMetrics::values.contentSidePadding * 2 - hPaddingInSelection * 2 - valueWidth; 305 + 237 306 auto itemName = rowTitle(i); 238 - auto item = renderer.truncatedText(UI_10_FONT_ID, itemName.c_str(), textWidth); 239 - renderer.drawText(UI_10_FONT_ID, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection * 2, 240 - itemY + 6, item.c_str(), true); 307 + auto item = renderer.truncatedText(UI_10_FONT_ID, itemName.c_str(), rowTextWidth); 308 + renderer.drawText(UI_10_FONT_ID, textX, itemY + 7, item.c_str(), true); 309 + 310 + if (rowIcon != nullptr) { 311 + UIIcon icon = rowIcon(i); 312 + const uint8_t* iconBitmap = iconForName(icon, iconSize); 313 + if (iconBitmap != nullptr) { 314 + renderer.drawIcon(iconBitmap, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection, 315 + itemY + iconY, iconSize, iconSize); 316 + } 317 + } 241 318 242 319 if (rowSubtitle != nullptr) { 243 320 // Draw subtitle 244 321 std::string subtitleText = rowSubtitle(i); 245 - auto subtitle = renderer.truncatedText(SMALL_FONT_ID, subtitleText.c_str(), textWidth); 246 - renderer.drawText(SMALL_FONT_ID, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection * 2, 247 - itemY + 30, subtitle.c_str(), true); 322 + auto subtitle = renderer.truncatedText(SMALL_FONT_ID, subtitleText.c_str(), rowTextWidth); 323 + renderer.drawText(SMALL_FONT_ID, textX, itemY + 30, subtitle.c_str(), true); 248 324 } 249 325 250 326 // Draw value ··· 373 449 } 374 450 375 451 if (!hasCover) { 452 + // Render empty cover 376 453 renderer.drawRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection, coverWidth, 377 - LyraMetrics::values.homeCoverHeight); 454 + LyraMetrics::values.homeCoverHeight, true); 455 + renderer.fillRect(tileX + hPaddingInSelection, 456 + tileY + hPaddingInSelection + (LyraMetrics::values.homeCoverHeight / 3), coverWidth, 457 + 2 * LyraMetrics::values.homeCoverHeight / 3, true); 458 + renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32); 378 459 } 379 460 380 461 coverBufferStored = storeCoverBuffer(); ··· 421 502 422 503 void LyraTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, 423 504 const std::function<std::string(int index)>& buttonLabel, 424 - const std::function<std::string(int index)>& rowIcon) const { 505 + const std::function<UIIcon(int index)>& rowIcon) const { 425 506 for (int i = 0; i < buttonCount; ++i) { 426 507 int tileWidth = rect.width - LyraMetrics::values.contentSidePadding * 2; 427 508 Rect tileRect = Rect{rect.x + LyraMetrics::values.contentSidePadding, ··· 436 517 437 518 std::string labelStr = buttonLabel(i); 438 519 const char* label = labelStr.c_str(); 439 - const int textX = tileRect.x + 16; 520 + int textX = tileRect.x + 16; 440 521 const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); 441 522 const int textY = tileRect.y + (LyraMetrics::values.menuRowHeight - lineHeight) / 2; 442 523 443 - // Invert text when the tile is selected, to contrast with the filled background 524 + if (rowIcon != nullptr) { 525 + UIIcon icon = rowIcon(i); 526 + const uint8_t* iconBitmap = iconForName(icon, mainMenuIconSize); 527 + if (iconBitmap != nullptr) { 528 + renderer.drawIcon(iconBitmap, textX, textY + 3, mainMenuIconSize, mainMenuIconSize); 529 + textX += mainMenuIconSize + hPaddingInSelection + 2; 530 + } 531 + } 532 + 444 533 renderer.drawText(UI_12_FONT_ID, textX, textY, label, true); 445 534 } 446 535 }
+3 -3
src/components/themes/lyra/LyraTheme.h
··· 50 50 void drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, 51 51 const std::function<std::string(int index)>& rowTitle, 52 52 const std::function<std::string(int index)>& rowSubtitle, 53 - const std::function<std::string(int index)>& rowIcon, 54 - const std::function<std::string(int index)>& rowValue, bool highlightValue) const override; 53 + const std::function<UIIcon(int index)>& rowIcon, const std::function<std::string(int index)>& rowValue, 54 + bool highlightValue) const override; 55 55 void drawButtonHints(GfxRenderer& renderer, const char* btn1, const char* btn2, const char* btn3, 56 56 const char* btn4) const override; 57 57 void drawSideButtonHints(const GfxRenderer& renderer, const char* topBtn, const char* bottomBtn) const override; 58 58 void drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, 59 59 const std::function<std::string(int index)>& buttonLabel, 60 - const std::function<std::string(int index)>& rowIcon) const override; 60 + const std::function<UIIcon(int index)>& rowIcon) const override; 61 61 void drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std::vector<RecentBook>& recentBooks, 62 62 const int selectorIndex, bool& coverRendered, bool& coverBufferStored, bool& bufferRestored, 63 63 std::function<bool()> storeCoverBuffer) const override;