diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 998abb74..802c3357 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -419,7 +419,7 @@ void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, co } void GfxRenderer::drawIcon(const uint8_t bitmap[], const int x, const int y, const int width, const int height) const { - display.drawImage(bitmap, y, getScreenWidth() - width - x, height, width); + display.drawImageTransparent(bitmap, y, getScreenWidth() - width - x, height, width); } void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, const int maxWidth, const int maxHeight, diff --git a/lib/hal/HalDisplay.cpp b/lib/hal/HalDisplay.cpp index 0fafdbb5..240c8925 100644 --- a/lib/hal/HalDisplay.cpp +++ b/lib/hal/HalDisplay.cpp @@ -16,6 +16,11 @@ void HalDisplay::drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uin einkDisplay.drawImage(imageData, x, y, w, h, fromProgmem); } +void HalDisplay::drawImageTransparent(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + bool fromProgmem) const { + einkDisplay.drawImageTransparent(imageData, x, y, w, h, fromProgmem); +} + EInkDisplay::RefreshMode convertRefreshMode(HalDisplay::RefreshMode mode) { switch (mode) { case HalDisplay::FULL_REFRESH: diff --git a/lib/hal/HalDisplay.h b/lib/hal/HalDisplay.h index 238832b0..25e34423 100644 --- a/lib/hal/HalDisplay.h +++ b/lib/hal/HalDisplay.h @@ -30,6 +30,8 @@ class HalDisplay { void clearScreen(uint8_t color = 0xFF) const; void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem = false) const; + void drawImageTransparent(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + bool fromProgmem = false) const; void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); diff --git a/scripts/convert_icon.py b/scripts/convert_icon.py new file mode 100644 index 00000000..56ea7d4b --- /dev/null +++ b/scripts/convert_icon.py @@ -0,0 +1,80 @@ +import sys +import os +from PIL import Image +import cairosvg +import io + +threshold = 128 + +def svg_to_png_bytes(svg_path, width, height): + with open(svg_path, 'rb') as f: + svg_data = f.read() + png_bytes = cairosvg.svg2png(bytestring=svg_data, output_width=width, output_height=height) + return png_bytes + +def load_image(path, width, height): + ext = os.path.splitext(path)[1].lower() + if ext == '.svg': + png_bytes = svg_to_png_bytes(path, width, height) + img = Image.open(io.BytesIO(png_bytes)) + else: + img = Image.open(path) + img = img.convert('RGBA') + img = img.resize((width, height), Image.LANCZOS) + # Flatten alpha: paste on white background + background = Image.new('RGBA', img.size, (255, 255, 255, 255)) + background.paste(img, mask=img.split()[3]) + img = background + # Rotate 90 degrees counterclockwise + img = img.rotate(90, expand=True) + return img + +def image_to_c_array(img, array_name): + # Convert to grayscale, then threshold to get white=1, black=0 + # Convert to grayscale + img = img.convert('L') + width, height = img.size + pixels = list(img.getdata()) + packed = [] + for y in range(height): + for x in range(0, width, 8): + byte = 0 + for b in range(8): + if x + b < width: + v = pixels[y * width + x + b] + # 1 for white, 0 for black + bit = 1 if v >= threshold else 0 + byte |= (bit << (7 - b)) + packed.append(byte) + # Format as C array + c = f'#pragma once\n#include \n\n' + c += f'// size: {width}x{height}\n' + c += f'static const uint8_t {array_name}[] = {{\n ' + for i, v in enumerate(packed): + c += f'0x{v:02X}, ' + if (i + 1) % 16 == 0: + c += '\n ' + c = c.rstrip(', \n') + '\n};\n' + return c + +def main(): + if len(sys.argv) < 5: + print('Usage: python convert_image.py input.png output_name width height') + sys.exit(1) + input_path, output_name, width, height = sys.argv[1:5] + array_name = output_name.capitalize() + 'Icon' + width, height = int(width), int(height) + img = load_image(input_path, width, height) + c_array = image_to_c_array(img, array_name) + + # Always save to src/components/icons/[output_name].h relative to project root + project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + output_dir = os.path.join(project_root, 'src', 'components', 'icons') + os.makedirs(output_dir, exist_ok=True) + output_path = os.path.join(output_dir, f'{output_name}.h') + with open(output_path, 'w') as f: + f.write(c_array) + print(f'Wrote {output_path}') + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index cd8bcdf1..d4e11509 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -229,9 +229,12 @@ void HomeActivity::render(Activity::RenderLock&&) { // Build menu items dynamically std::vector menuItems = {tr(STR_BROWSE_FILES), tr(STR_MENU_RECENT_BOOKS), tr(STR_FILE_TRANSFER), tr(STR_SETTINGS_TITLE)}; + std::vector menuIcons = {Folder, Recent, Transfer, Settings}; + if (hasOpdsUrl) { // Insert OPDS Browser after My Library menuItems.insert(menuItems.begin() + 2, tr(STR_OPDS_BROWSER)); + menuIcons.insert(menuIcons.begin() + 2, Library); } GUI.drawButtonMenu( @@ -240,7 +243,8 @@ void HomeActivity::render(Activity::RenderLock&&) { pageHeight - (metrics.headerHeight + metrics.homeTopPadding + metrics.verticalSpacing * 2 + metrics.buttonHintsHeight)}, static_cast(menuItems.size()), selectorIndex - recentBooks.size(), - [&menuItems](int index) { return std::string(menuItems[index]); }, nullptr); + [&menuItems](int index) { return std::string(menuItems[index]); }, + [&menuIcons](int index) { return menuIcons[index]; }); const auto labels = mappedInput.mapLabels("", tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); diff --git a/src/activities/home/MyLibraryActivity.cpp b/src/activities/home/MyLibraryActivity.cpp index a651799f..8b44d7d1 100644 --- a/src/activities/home/MyLibraryActivity.cpp +++ b/src/activities/home/MyLibraryActivity.cpp @@ -188,6 +188,14 @@ void MyLibraryActivity::loop() { }); } +std::string getFileName(std::string filename) { + if (filename.back() == '/') { + return filename.substr(0, filename.length() - 1); + } + const auto pos = filename.rfind('.'); + return filename.substr(0, pos); +} + void MyLibraryActivity::render(Activity::RenderLock&&) { renderer.clearScreen(); @@ -205,7 +213,8 @@ void MyLibraryActivity::render(Activity::RenderLock&&) { } else { GUI.drawList( renderer, Rect{0, contentTop, pageWidth, contentHeight}, files.size(), selectorIndex, - [this](int index) { return files[index]; }, nullptr, nullptr, nullptr); + [this](int index) { return getFileName(files[index]); }, nullptr, + [this](int index) { return UITheme::getFileIcon(files[index]); }); } // Help text diff --git a/src/activities/home/RecentBooksActivity.cpp b/src/activities/home/RecentBooksActivity.cpp index 05317214..2ea9ff01 100644 --- a/src/activities/home/RecentBooksActivity.cpp +++ b/src/activities/home/RecentBooksActivity.cpp @@ -102,7 +102,7 @@ void RecentBooksActivity::render(Activity::RenderLock&&) { GUI.drawList( renderer, Rect{0, contentTop, pageWidth, contentHeight}, recentBooks.size(), selectorIndex, [this](int index) { return recentBooks[index].title; }, [this](int index) { return recentBooks[index].author; }, - nullptr, nullptr); + [this](int index) { return UITheme::getFileIcon(recentBooks[index].path); }); } // Help text diff --git a/src/activities/network/NetworkModeSelectionActivity.cpp b/src/activities/network/NetworkModeSelectionActivity.cpp index 8c76ba14..af300802 100644 --- a/src/activities/network/NetworkModeSelectionActivity.cpp +++ b/src/activities/network/NetworkModeSelectionActivity.cpp @@ -70,11 +70,12 @@ void NetworkModeSelectionActivity::render(Activity::RenderLock&&) { StrId::STR_CREATE_HOTSPOT}; static constexpr StrId menuDescs[MENU_ITEM_COUNT] = {StrId::STR_JOIN_DESC, StrId::STR_CALIBRE_DESC, StrId::STR_HOTSPOT_DESC}; + static constexpr UIIcon menuIcons[MENU_ITEM_COUNT] = {UIIcon::Wifi, UIIcon::Library, UIIcon::Hotspot}; GUI.drawList( renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast(MENU_ITEM_COUNT), selectedIndex, [](int index) { return std::string(I18N.get(menuItems[index])); }, - [](int index) { return std::string(I18N.get(menuDescs[index])); }); + [](int index) { return std::string(I18N.get(menuDescs[index])); }, [](int index) { return menuIcons[index]; }); // Draw help text at bottom const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); diff --git a/src/components/UITheme.cpp b/src/components/UITheme.cpp index e1e8c506..1779be3a 100644 --- a/src/components/UITheme.cpp +++ b/src/components/UITheme.cpp @@ -5,10 +5,16 @@ #include +#include "MappedInputManager.h" #include "RecentBooksStore.h" #include "components/themes/BaseTheme.h" #include "components/themes/lyra/Lyra3CoversTheme.h" #include "components/themes/lyra/LyraTheme.h" +#include "util/StringUtils.h" + +namespace { +constexpr int SKIP_PAGE_MS = 700; +} // namespace UITheme UITheme::instance; @@ -67,3 +73,20 @@ std::string UITheme::getCoverThumbPath(std::string coverBmpPath, int coverHeight } return coverBmpPath; } + +UIIcon UITheme::getFileIcon(std::string filename) { + if (filename.back() == '/') { + return Folder; + } + if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") || + StringUtils::checkFileExtension(filename, ".xtc")) { + return Book; + } + if (StringUtils::checkFileExtension(filename, ".txt") || StringUtils::checkFileExtension(filename, ".md")) { + return Text; + } + if (StringUtils::checkFileExtension(filename, ".bmp")) { + return Image; + } + return File; +} \ No newline at end of file diff --git a/src/components/UITheme.h b/src/components/UITheme.h index 08a3ede9..dc3e55f0 100644 --- a/src/components/UITheme.h +++ b/src/components/UITheme.h @@ -5,6 +5,8 @@ #include "CrossPointSettings.h" #include "components/themes/BaseTheme.h" +class MappedInputManager; + class UITheme { // Static instance static UITheme instance; @@ -20,6 +22,7 @@ class UITheme { 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); private: const ThemeMetrics* currentMetrics; diff --git a/src/components/icons/book.h b/src/components/icons/book.h new file mode 100644 index 00000000..928db130 --- /dev/null +++ b/src/components/icons/book.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t BookIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, + 0x07, 0xC0, 0x00, 0x00, 0x03, 0xCF, 0xFF, 0xF8, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, + 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, + 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, + 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, + 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xC3, 0xFF, 0xFC, 0x23, 0xE0, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/book24.h b/src/components/icons/book24.h new file mode 100644 index 00000000..c9f1db42 --- /dev/null +++ b/src/components/icons/book24.h @@ -0,0 +1,9 @@ +#pragma once +#include + +// size: 24x24 +static const uint8_t Book24Icon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x01, 0x9F, 0xFF, 0x39, + 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, + 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, 0x9F, 0xFF, 0x39, + 0x9F, 0xFF, 0x39, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/cog.h b/src/components/icons/cog.h new file mode 100644 index 00000000..899d81a9 --- /dev/null +++ b/src/components/icons/cog.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t CogIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xBE, 0x7D, 0xFF, 0xFF, 0x1C, 0x38, + 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x82, 0x41, 0xFF, 0xFF, 0x0E, 0x70, 0xFF, 0xF6, 0x3E, 0x7C, 0x6F, 0xE0, 0x7E, + 0x7E, 0x07, 0xF0, 0xFE, 0x7F, 0x0F, 0xF8, 0xFE, 0x7F, 0x1F, 0xF9, 0xFE, 0x7F, 0x9F, 0xF9, 0xF8, 0x1F, 0x9F, 0xF3, + 0xF8, 0x1F, 0xCF, 0xC3, 0xF3, 0xCF, 0xC3, 0xC3, 0xF1, 0x8F, 0xC3, 0xF3, 0xE1, 0x87, 0xCF, 0xF9, 0xC0, 0x03, 0x9F, + 0xF9, 0x0E, 0x70, 0x9F, 0xF8, 0x3F, 0xFC, 0x1F, 0xF0, 0x7F, 0xFE, 0x0F, 0xE0, 0x7F, 0xFE, 0x07, 0xF6, 0x3F, 0xFC, + 0x6F, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0x83, 0xC1, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x1C, 0x38, 0xFF, 0xFF, 0xBE, + 0x7D, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/cover.h b/src/components/icons/cover.h new file mode 100644 index 00000000..38eda9ff --- /dev/null +++ b/src/components/icons/cover.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t CoverIcon[] = { + 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFE, 0x1F, 0xE0, 0x00, 0x02, 0x1F, 0xE0, 0x00, 0x03, + 0x1F, 0xE0, 0x00, 0x03, 0x1F, 0xF0, 0x00, 0x01, 0x1F, 0xF0, 0x00, 0x01, 0x1F, 0xF0, 0x00, 0x01, 0x9F, 0xF8, 0x00, + 0x00, 0x9F, 0xF8, 0x00, 0x00, 0xDF, 0xFC, 0x00, 0x00, 0x6F, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x1F, + 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x6F, 0xF8, 0x00, 0x00, 0xDF, 0xF8, 0x00, 0x00, 0x9F, 0xF0, 0x00, 0x01, + 0x9F, 0xF0, 0x00, 0x01, 0x1F, 0xE0, 0x00, 0x01, 0x1F, 0xE0, 0x00, 0x03, 0x1F, 0xE0, 0x00, 0x02, 0x1F, 0xE0, 0x00, + 0x02, 0x1F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x1F}; diff --git a/src/components/icons/file24.h b/src/components/icons/file24.h new file mode 100644 index 00000000..197097b3 --- /dev/null +++ b/src/components/icons/file24.h @@ -0,0 +1,9 @@ +#pragma once +#include + +// size: 24x24 +static const uint8_t File24Icon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xF8, 0x00, 0x03, 0xF0, 0x7F, 0xF1, + 0xE2, 0x7F, 0xF9, 0xC6, 0x7F, 0xF9, 0x8E, 0x7F, 0xF9, 0x80, 0x7F, 0xF9, 0x80, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, + 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, 0x9F, 0xFF, 0xF9, + 0x8F, 0xFF, 0xF1, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/folder.h b/src/components/icons/folder.h new file mode 100644 index 00000000..b0997c45 --- /dev/null +++ b/src/components/icons/folder.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t FolderIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x1F, 0xFE, 0x3F, 0xFF, + 0x9F, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, + 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, + 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFE, 0x7F, 0xFF, 0xCF, 0xFC, 0x7F, 0xFF, 0xCF, 0xF0, 0xFF, 0xFF, 0xCF, + 0xE1, 0xFF, 0xFF, 0xCF, 0xE3, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, + 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0x8F, 0xE7, 0xFF, 0xFF, 0x8F, 0xF0, 0x00, + 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/folder24.h b/src/components/icons/folder24.h new file mode 100644 index 00000000..20f47007 --- /dev/null +++ b/src/components/icons/folder24.h @@ -0,0 +1,9 @@ +#pragma once +#include + +// size: 24x24 +static const uint8_t Folder24Icon[] = { + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFC, 0x00, 0x0F, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, + 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, + 0xF9, 0xFF, 0xE7, 0xF3, 0xFF, 0xE7, 0xE7, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, + 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xCF, 0xFF, 0xE7, 0xE0, 0x00, 0x0F, 0xF0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/hotspot.h b/src/components/icons/hotspot.h new file mode 100644 index 00000000..81683325 --- /dev/null +++ b/src/components/icons/hotspot.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t HotspotIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFE, 0x0F, 0xF0, + 0x7F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0xFF, 0xFE, 0x3F, 0xFD, 0xF8, 0x1F, 0xBF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xC3, + 0xC3, 0xFF, 0xFF, 0xCF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, + 0xF9, 0x9F, 0xFF, 0xFF, 0xF3, 0xCF, 0xFF, 0xFF, 0xF3, 0xCF, 0xFF, 0xFF, 0xF9, 0x9F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, + 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xF3, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xE0, 0x07, + 0xFF, 0xFD, 0xF8, 0x1F, 0xBF, 0xFC, 0x7F, 0xFF, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFE, 0x0F, 0xF0, 0x7F, 0xFF, 0x80, + 0x01, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/image24.h b/src/components/icons/image24.h new file mode 100644 index 00000000..78303804 --- /dev/null +++ b/src/components/icons/image24.h @@ -0,0 +1,9 @@ +#pragma once +#include + +// size: 24x24 +static const uint8_t Image24Icon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xCF, 0xF1, 0xF3, 0xCF, 0xE3, 0xF3, + 0xCF, 0xE7, 0xF3, 0xCF, 0xEF, 0xF3, 0xCF, 0xE7, 0xF3, 0xCF, 0xE3, 0xF3, 0xCF, 0xF1, 0xF3, 0xCF, 0xF8, 0xF3, + 0xCF, 0x3C, 0x73, 0xCE, 0x1E, 0x33, 0xCC, 0xCF, 0x13, 0xCC, 0xCF, 0x83, 0xCE, 0x1F, 0xC3, 0xCF, 0x3F, 0xE3, + 0xCF, 0xFF, 0xF3, 0xCF, 0xFF, 0xF3, 0xE0, 0x00, 0x07, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/library.h b/src/components/icons/library.h new file mode 100644 index 00000000..ab79e61c --- /dev/null +++ b/src/components/icons/library.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t LibraryIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0x1F, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xC1, 0xCF, 0xFF, 0xFE, 0x07, 0xE7, 0xFF, 0xF0, 0x3F, 0xE7, 0xFF, 0x81, + 0xFF, 0x87, 0xFC, 0x0F, 0xFC, 0x0F, 0xF0, 0x3F, 0xF0, 0x3F, 0xE1, 0xFF, 0x81, 0xFF, 0xE7, 0xFC, 0x0F, 0xFF, 0xE7, + 0xE0, 0x7F, 0xFF, 0xF3, 0x83, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x07, 0xE7, 0xFF, 0xFF, 0xE7, + 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, + 0x07, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xE0, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/recent.h b/src/components/icons/recent.h new file mode 100644 index 00000000..fd61b95b --- /dev/null +++ b/src/components/icons/recent.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t RecentIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, + 0x07, 0xC0, 0x00, 0x00, 0x03, 0xCF, 0xFF, 0xF8, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, + 0xFC, 0xF3, 0xC0, 0x03, 0xFC, 0xF3, 0xC0, 0x03, 0xFC, 0xF3, 0xCF, 0xC7, 0xFC, 0xF3, 0xCF, 0x8F, 0xFC, 0xF3, 0xCF, + 0x1F, 0xFC, 0xF3, 0xCF, 0x8F, 0xFC, 0xF3, 0xCF, 0x87, 0xFC, 0xF3, 0xCF, 0xC3, 0xFC, 0xF3, 0xC0, 0x03, 0xFC, 0xF3, + 0xC0, 0x03, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFC, + 0xF3, 0xCF, 0xFF, 0xFC, 0xF3, 0xC3, 0xFF, 0xFC, 0x23, 0xE0, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/settings.h b/src/components/icons/settings.h new file mode 100644 index 00000000..3c924be2 --- /dev/null +++ b/src/components/icons/settings.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t SettingsIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xE1, 0xFF, 0xFF, 0x03, 0xC0, + 0xFF, 0xFE, 0x30, 0x8C, 0x7F, 0xFE, 0x78, 0x1E, 0x7F, 0xFE, 0x7C, 0x7E, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, + 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x7C, 0x3E, 0x3F, 0xF0, 0xF0, 0x0F, 0x07, 0xC1, 0xF3, 0xCF, 0x83, 0xC7, + 0xE7, 0xE7, 0xE3, 0xCF, 0xE7, 0xE7, 0xF3, 0xCF, 0xE7, 0xE7, 0xF3, 0xC7, 0xE7, 0xE7, 0xE3, 0xC1, 0xF3, 0xCF, 0x83, + 0xE0, 0xF0, 0x0F, 0x0F, 0xFC, 0x7C, 0x3E, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, + 0x7F, 0xFE, 0x7E, 0x3E, 0x7F, 0xFE, 0x78, 0x1E, 0x7F, 0xFE, 0x31, 0x0C, 0x7F, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x87, + 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/settings2.h b/src/components/icons/settings2.h new file mode 100644 index 00000000..3aee5511 --- /dev/null +++ b/src/components/icons/settings2.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t Settings2Icon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, + 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0x3F, 0xE3, 0x1F, 0xFF, 0x3F, 0xCF, 0x9F, 0xFF, 0x3F, 0xCF, 0xCF, 0xFF, 0x3F, + 0xCF, 0xCF, 0xFF, 0x3F, 0xCF, 0x8F, 0xFF, 0x3F, 0xC7, 0x9F, 0xFF, 0x3F, 0xE0, 0x1F, 0xFF, 0x3F, 0xF0, 0x7F, 0xFF, + 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFE, 0x0F, 0xFC, 0xFF, + 0xF8, 0x07, 0xFC, 0xFF, 0xF9, 0xE3, 0xFC, 0xFF, 0xF1, 0xF3, 0xFC, 0xFF, 0xF3, 0xF3, 0xFC, 0xFF, 0xF3, 0xF3, 0xFC, + 0xFF, 0xF9, 0xF3, 0xFC, 0xFF, 0xF8, 0xC7, 0xFC, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/text24.h b/src/components/icons/text24.h new file mode 100644 index 00000000..9020ec05 --- /dev/null +++ b/src/components/icons/text24.h @@ -0,0 +1,9 @@ +#pragma once +#include + +// size: 24x24 +static const uint8_t Text24Icon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xF8, 0x00, 0x03, 0xF0, 0x7F, 0xF9, + 0xE2, 0x7F, 0xF9, 0xC6, 0x73, 0x39, 0xCE, 0x73, 0x39, 0x80, 0x73, 0x39, 0x80, 0xF3, 0x39, 0x9F, 0xF3, 0x39, + 0x9F, 0xF3, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0x33, 0x39, 0x9F, 0xFF, 0xF9, + 0x9F, 0xFF, 0xF9, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/transfer.h b/src/components/icons/transfer.h new file mode 100644 index 00000000..fd28566d --- /dev/null +++ b/src/components/icons/transfer.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t TransferIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, + 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0, 0x4F, 0xFF, 0xFF, 0xE2, 0x4F, 0xFF, 0xFF, 0xE2, + 0x47, 0xFF, 0xFF, 0xC6, 0x67, 0xFF, 0xFF, 0xC6, 0x63, 0xFF, 0xFF, 0xCE, 0x73, 0xFF, 0xFF, 0x8E, 0x71, 0xFF, 0xFF, + 0x9E, 0x79, 0xFF, 0xFF, 0x1E, 0x78, 0xFF, 0xFF, 0x3E, 0x7C, 0xFF, 0xFE, 0x3E, 0x7C, 0x7F, 0xFE, 0x3E, 0x7E, 0x7F, + 0xFC, 0x7E, 0x7E, 0x3F, 0xFC, 0x7E, 0x7F, 0x3F, 0xF8, 0xFE, 0x7F, 0x3F, 0xF8, 0xFE, 0x7F, 0x9F, 0xF1, 0xFC, 0x1F, + 0x9F, 0xF1, 0xE0, 0x07, 0xCF, 0xE3, 0x83, 0x80, 0xCF, 0xE0, 0x0F, 0xF0, 0x07, 0xE0, 0x7F, 0xFC, 0x07, 0xF3, 0xFF, + 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/icons/wifi.h b/src/components/icons/wifi.h new file mode 100644 index 00000000..d84f5aed --- /dev/null +++ b/src/components/icons/wifi.h @@ -0,0 +1,12 @@ +#pragma once +#include + +// size: 32x32 +static const uint8_t WifiIcon[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, + 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x1F, 0x3F, 0xFF, 0xFE, 0x3E, 0x3F, 0xFF, 0xFE, 0x3C, 0x7F, 0xFF, 0xFE, 0x7C, + 0x7F, 0xFF, 0xFC, 0x78, 0xFD, 0xFF, 0xFC, 0x78, 0xF1, 0xFF, 0xFC, 0xF9, 0xF1, 0xFF, 0xFC, 0xF1, 0xE3, 0xFF, 0xFC, + 0xF1, 0xE3, 0xFF, 0xFC, 0xF1, 0xE7, 0xDF, 0xFC, 0xF3, 0xE7, 0xDF, 0xFC, 0xF1, 0xE7, 0xFF, 0xFC, 0xF9, 0xE3, 0xFF, + 0xFC, 0xF9, 0xF3, 0xFF, 0xFC, 0xF9, 0xF1, 0xFF, 0xFC, 0x7C, 0xFB, 0xFF, 0xFE, 0x7C, 0xFF, 0xFF, 0xFE, 0x7C, 0x7F, + 0xFF, 0xFF, 0x3E, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xCF, + 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index f195037e..950b79f3 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -186,7 +186,7 @@ void BaseTheme::drawSideButtonHints(const GfxRenderer& renderer, const char* top void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, const std::function& rowTitle, const std::function& rowSubtitle, - const std::function& rowIcon, + const std::function& rowIcon, const std::function& rowValue, bool highlightValue) const { int rowHeight = (rowSubtitle != nullptr) ? BaseMetrics::values.listWithSubtitleRowHeight : BaseMetrics::values.listRowHeight; @@ -635,7 +635,7 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: void BaseTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, const std::function& buttonLabel, - const std::function& rowIcon) const { + const std::function& rowIcon) const { for (int i = 0; i < buttonCount; ++i) { const int tileY = BaseMetrics::values.verticalSpacing + rect.y + static_cast(i) * (BaseMetrics::values.menuRowHeight + BaseMetrics::values.menuSpacing); @@ -727,4 +727,4 @@ void BaseTheme::drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const ch renderer.drawText(UI_10_FONT_ID, textX + itemWidth, rect.y, "]"); } renderer.drawText(UI_10_FONT_ID, textX, rect.y, label); -} \ No newline at end of file +} diff --git a/src/components/themes/BaseTheme.h b/src/components/themes/BaseTheme.h index 07a7951c..7bc89f74 100644 --- a/src/components/themes/BaseTheme.h +++ b/src/components/themes/BaseTheme.h @@ -61,6 +61,8 @@ struct ThemeMetrics { bool keyboardCenteredText; }; +enum UIIcon { Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot }; + // Default theme implementation (Classic Theme) // Additional themes can inherit from this and override methods as needed @@ -111,7 +113,7 @@ class BaseTheme { virtual void drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, const std::function& rowTitle, const std::function& rowSubtitle = nullptr, - const std::function& rowIcon = nullptr, + const std::function& rowIcon = nullptr, const std::function& rowValue = nullptr, bool highlightValue = false) const; virtual void drawHeader(const GfxRenderer& renderer, Rect rect, const char* title, @@ -125,11 +127,11 @@ class BaseTheme { bool& bufferRestored, std::function storeCoverBuffer) const; virtual void drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, const std::function& buttonLabel, - const std::function& rowIcon) const; + const std::function& rowIcon) const; virtual Rect drawPopup(const GfxRenderer& renderer, const char* message) const; virtual void fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const; virtual void drawReadingProgressBar(const GfxRenderer& renderer, const size_t bookProgress) const; virtual void drawHelpText(const GfxRenderer& renderer, Rect rect, const char* label) const; virtual void drawTextField(const GfxRenderer& renderer, Rect rect, const int textWidth) const; virtual void drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const char* label, const bool isSelected) const; -}; \ No newline at end of file +}; diff --git a/src/components/themes/lyra/Lyra3CoversTheme.cpp b/src/components/themes/lyra/Lyra3CoversTheme.cpp index 50506280..54c745b6 100644 --- a/src/components/themes/lyra/Lyra3CoversTheme.cpp +++ b/src/components/themes/lyra/Lyra3CoversTheme.cpp @@ -6,11 +6,10 @@ #include #include -#include "Battery.h" #include "RecentBooksStore.h" #include "components/UITheme.h" +#include "components/icons/cover.h" #include "fontIds.h" -#include "util/StringUtils.h" // Internal constants namespace { @@ -67,8 +66,14 @@ void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con } if (!hasCover) { + // Render empty cover renderer.drawRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection, - tileWidth - 2 * hPaddingInSelection, Lyra3CoversMetrics::values.homeCoverHeight); + tileWidth - 2 * hPaddingInSelection, Lyra3CoversMetrics::values.homeCoverHeight, true); + renderer.fillRect(tileX + hPaddingInSelection, + tileY + hPaddingInSelection + (Lyra3CoversMetrics::values.homeCoverHeight / 3), + tileWidth - 2 * hPaddingInSelection, 2 * Lyra3CoversMetrics::values.homeCoverHeight / 3, + true); + renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32); } } @@ -101,4 +106,4 @@ void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con } else { drawEmptyRecents(renderer, rect); } -} \ No newline at end of file +} diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index a89fef9f..38a8aff2 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -10,8 +10,21 @@ #include "Battery.h" #include "RecentBooksStore.h" #include "components/UITheme.h" +#include "components/icons/book.h" +#include "components/icons/book24.h" +#include "components/icons/cover.h" +#include "components/icons/file24.h" +#include "components/icons/folder.h" +#include "components/icons/folder24.h" +#include "components/icons/hotspot.h" +#include "components/icons/image24.h" +#include "components/icons/library.h" +#include "components/icons/recent.h" +#include "components/icons/settings2.h" +#include "components/icons/text24.h" +#include "components/icons/transfer.h" +#include "components/icons/wifi.h" #include "fontIds.h" -#include "util/StringUtils.h" // Internal constants namespace { @@ -23,7 +36,51 @@ constexpr int popupMarginX = 16; constexpr int popupMarginY = 12; constexpr int maxSubtitleWidth = 100; constexpr int maxListValueWidth = 200; +constexpr int mainMenuIconSize = 32; +constexpr int listIconSize = 24; +constexpr int mainMenuColumns = 2; int coverWidth = 0; + +const uint8_t* iconForName(UIIcon icon, int size) { + if (size == 24) { + switch (icon) { + case UIIcon::Folder: + return Folder24Icon; + case UIIcon::Text: + return Text24Icon; + case UIIcon::Image: + return Image24Icon; + case UIIcon::Book: + return Book24Icon; + case UIIcon::File: + return File24Icon; + default: + return nullptr; + } + } else if (size == 32) { + switch (icon) { + case UIIcon::Folder: + return FolderIcon; + case UIIcon::Book: + return BookIcon; + case UIIcon::Recent: + return RecentIcon; + case UIIcon::Settings: + return Settings2Icon; + case UIIcon::Transfer: + return TransferIcon; + case UIIcon::Library: + return LibraryIcon; + case UIIcon::Wifi: + return WifiIcon; + case UIIcon::Hotspot: + return HotspotIcon; + default: + return nullptr; + } + } + return nullptr; +} } // namespace void LyraTheme::drawBatteryLeft(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { @@ -124,7 +181,7 @@ void LyraTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* t renderer.drawText(UI_12_FONT_ID, rect.x + LyraMetrics::values.contentSidePadding, rect.y + LyraMetrics::values.batteryBarHeight + 3, truncatedTitle.c_str(), true, EpdFontFamily::BOLD); - renderer.drawLine(rect.x, rect.y + rect.height - 3, rect.x + rect.width, rect.y + rect.height - 3, 3, true); + renderer.drawLine(rect.x, rect.y + rect.height - 3, rect.x + rect.width - 1, rect.y + rect.height - 3, 3, true); } if (subtitle) { @@ -152,7 +209,7 @@ void LyraTheme::drawSubHeader(const GfxRenderer& renderer, Rect rect, const char UI_10_FONT_ID, label, rect.width - LyraMetrics::values.contentSidePadding - rightSpace, EpdFontFamily::REGULAR); renderer.drawText(UI_10_FONT_ID, currentX, rect.y + 6, truncatedLabel.c_str(), true, EpdFontFamily::REGULAR); - renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1, true); + renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1, true); } void LyraTheme::drawTabBar(const GfxRenderer& renderer, Rect rect, const std::vector& tabs, @@ -184,13 +241,13 @@ void LyraTheme::drawTabBar(const GfxRenderer& renderer, Rect rect, const std::ve currentX += textWidth + LyraMetrics::values.tabSpacing + 2 * hPaddingInSelection; } - renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1, true); + renderer.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1, true); } void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, const std::function& rowTitle, const std::function& rowSubtitle, - const std::function& rowIcon, + const std::function& rowIcon, const std::function& rowValue, bool highlightValue) const { int rowHeight = (rowSubtitle != nullptr) ? LyraMetrics::values.listWithSubtitleRowHeight : LyraMetrics::values.listRowHeight; @@ -220,10 +277,21 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, Color::LightGray); } + int textX = rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection; + int textWidth = contentWidth - LyraMetrics::values.contentSidePadding * 2 - hPaddingInSelection * 2; + int iconSize; + if (rowIcon != nullptr) { + iconSize = (rowSubtitle != nullptr) ? mainMenuIconSize : listIconSize; + textX += iconSize + hPaddingInSelection; + textWidth -= iconSize + hPaddingInSelection; + } + // Draw all items const auto pageStartIndex = selectedIndex / pageItems * pageItems; + int iconY = (rowSubtitle != nullptr) ? 16 : 10; for (int i = pageStartIndex; i < itemCount && i < pageStartIndex + pageItems; i++) { const int itemY = rect.y + (i % pageItems) * rowHeight; + int rowTextWidth = textWidth; // Draw name int valueWidth = 0; @@ -232,19 +300,27 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, valueText = rowValue(i); valueText = renderer.truncatedText(UI_10_FONT_ID, valueText.c_str(), maxListValueWidth); valueWidth = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str()) + hPaddingInSelection; + rowTextWidth -= valueWidth; } - int textWidth = contentWidth - LyraMetrics::values.contentSidePadding * 2 - hPaddingInSelection * 2 - valueWidth; + auto itemName = rowTitle(i); - auto item = renderer.truncatedText(UI_10_FONT_ID, itemName.c_str(), textWidth); - renderer.drawText(UI_10_FONT_ID, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection * 2, - itemY + 6, item.c_str(), true); + auto item = renderer.truncatedText(UI_10_FONT_ID, itemName.c_str(), rowTextWidth); + renderer.drawText(UI_10_FONT_ID, textX, itemY + 7, item.c_str(), true); + + if (rowIcon != nullptr) { + UIIcon icon = rowIcon(i); + const uint8_t* iconBitmap = iconForName(icon, iconSize); + if (iconBitmap != nullptr) { + renderer.drawIcon(iconBitmap, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection, + itemY + iconY, iconSize, iconSize); + } + } if (rowSubtitle != nullptr) { // Draw subtitle std::string subtitleText = rowSubtitle(i); - auto subtitle = renderer.truncatedText(SMALL_FONT_ID, subtitleText.c_str(), textWidth); - renderer.drawText(SMALL_FONT_ID, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection * 2, - itemY + 30, subtitle.c_str(), true); + auto subtitle = renderer.truncatedText(SMALL_FONT_ID, subtitleText.c_str(), rowTextWidth); + renderer.drawText(SMALL_FONT_ID, textX, itemY + 30, subtitle.c_str(), true); } // Draw value @@ -373,8 +449,13 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: } if (!hasCover) { + // Render empty cover renderer.drawRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection, coverWidth, - LyraMetrics::values.homeCoverHeight); + LyraMetrics::values.homeCoverHeight, true); + renderer.fillRect(tileX + hPaddingInSelection, + tileY + hPaddingInSelection + (LyraMetrics::values.homeCoverHeight / 3), coverWidth, + 2 * LyraMetrics::values.homeCoverHeight / 3, true); + renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32); } coverBufferStored = storeCoverBuffer(); @@ -421,7 +502,7 @@ void LyraTheme::drawEmptyRecents(const GfxRenderer& renderer, const Rect rect) c void LyraTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, const std::function& buttonLabel, - const std::function& rowIcon) const { + const std::function& rowIcon) const { for (int i = 0; i < buttonCount; ++i) { int tileWidth = rect.width - LyraMetrics::values.contentSidePadding * 2; Rect tileRect = Rect{rect.x + LyraMetrics::values.contentSidePadding, @@ -436,11 +517,19 @@ void LyraTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount std::string labelStr = buttonLabel(i); const char* label = labelStr.c_str(); - const int textX = tileRect.x + 16; + int textX = tileRect.x + 16; const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); const int textY = tileRect.y + (LyraMetrics::values.menuRowHeight - lineHeight) / 2; - // Invert text when the tile is selected, to contrast with the filled background + 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); } } diff --git a/src/components/themes/lyra/LyraTheme.h b/src/components/themes/lyra/LyraTheme.h index 5e3f545a..3fc451a8 100644 --- a/src/components/themes/lyra/LyraTheme.h +++ b/src/components/themes/lyra/LyraTheme.h @@ -50,14 +50,14 @@ class LyraTheme : public BaseTheme { void drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex, const std::function& rowTitle, const std::function& rowSubtitle, - const std::function& rowIcon, - const std::function& rowValue, bool highlightValue) const override; + const std::function& rowIcon, const std::function& rowValue, + bool highlightValue) const override; void drawButtonHints(GfxRenderer& renderer, const char* btn1, const char* btn2, const char* btn3, const char* btn4) const override; void drawSideButtonHints(const GfxRenderer& renderer, const char* topBtn, const char* bottomBtn) const override; void drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex, const std::function& buttonLabel, - const std::function& rowIcon) const override; + const std::function& rowIcon) const override; void drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std::vector& recentBooks, const int selectorIndex, bool& coverRendered, bool& coverBufferStored, bool& bufferRestored, std::function storeCoverBuffer) const override;