Add icon generation script and list scroll helper

Introduce gen_icons.sh to regenerate UI icons from a manifest file using the FreeInk SDK's Lucide icon set. Add icons.manifest mapping UI icons to Lucide names. Refactor vertical swipe list scrolling into a reusable wasListScroll() helper method used by both reader chapter selection and settings activities. Add static assertion to ensure ThemeMetrics scaling stays in sync with struct changes.
This commit is contained in:
Justin Mitchell
2026-06-16 00:15:18 -04:00
parent 1c63847631
commit 4a17d21d80
7 changed files with 86 additions and 37 deletions
+5
View File
@@ -104,6 +104,11 @@ struct ThemeMetrics {
int textFieldLineEndOffset;
};
// Expected sizeof(ThemeMetrics), in bytes. scaleThemeMetrics() static_asserts
// against this so a new/removed metric field can't slip through unclassified;
// update it after adding a field (and decide there whether the field scales).
inline constexpr unsigned THEME_METRICS_SIZEOF = 216;
enum UIIcon { None = 0, Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot, Bookmark };
enum class KeyboardKeyType { Normal, Shift, Mode, Space, Del, Ok, Disabled };
+24 -30
View File
@@ -34,9 +34,29 @@ constexpr int listIconSize = 24;
constexpr int mainMenuColumns = 2;
int coverWidth = 0;
// Pick the generated Lucide icon variant nearest to targetPx for a UIIcon. The
// generator emits 24/32/40/48px with each icon's optical center baked in, so a
// scaled UI gets a crisp larger asset (not an upscaled one) and exact alignment.
// The generated icon sizes (px). The generator (gen_icons.py) emits these.
constexpr int kIconSizes[] = {24, 32, 40, 48};
// Scale a base icon size by the board uiScale.
int scaledIcon(int base) { return static_cast<int>(base * UITheme::uiScale() + 0.5f); }
// Index of the generated size nearest targetPx, and that size in px.
int iconVariantIndex(int targetPx) {
int best = 0, bestDist = 1 << 30;
for (int i = 0; i < 4; ++i) {
const int d = kIconSizes[i] > targetPx ? kIconSizes[i] - targetPx : targetPx - kIconSizes[i];
if (d < bestDist) {
bestDist = d;
best = i;
}
}
return best;
}
int nearestIconSize(int targetPx) { return kIconSizes[iconVariantIndex(targetPx)]; }
// Generated Lucide icon variant nearest targetPx for a UIIcon. Each icon ships at
// every size with its optical center baked in, so a scaled UI gets a crisp larger
// asset (not an upscale) with exact alignment.
const freeink::Icon* pickIcon(UIIcon icon, int targetPx) {
const freeink::Icon* const* v = nullptr;
#define VARIANTS(n) \
@@ -61,33 +81,7 @@ const freeink::Icon* pickIcon(UIIcon icon, int targetPx) {
default: return nullptr;
}
#undef VARIANTS
static constexpr int kSizes[] = {24, 32, 40, 48};
int best = 0, bestDist = 1 << 30;
for (int i = 0; i < 4; ++i) {
const int d = kSizes[i] > targetPx ? kSizes[i] - targetPx : targetPx - kSizes[i];
if (d < bestDist) {
bestDist = d;
best = i;
}
}
return v[best];
}
// Scale a base icon size by the board uiScale.
int scaledIcon(int base) { return static_cast<int>(base * UITheme::uiScale() + 0.5f); }
// Nearest generated icon size (24/32/40/48) to targetPx.
int nearestIconSize(int targetPx) {
static constexpr int kSizes[] = {24, 32, 40, 48};
int best = kSizes[0], bestDist = 1 << 30;
for (int s : kSizes) {
const int d = s > targetPx ? s - targetPx : targetPx - s;
if (d < bestDist) {
bestDist = d;
best = s;
}
}
return best;
return v[iconVariantIndex(targetPx)];
}
} // namespace