Files
Crosspoint/src/SdCardFontSystem.cpp
T
2026-05-02 18:19:23 +02:00

164 lines
6.4 KiB
C++

#include "SdCardFontSystem.h"
#include <GfxRenderer.h>
#include <I18n.h>
#include <Logging.h>
#include <climits>
#include <cstdlib>
#include <cstring>
#include "CrossPointSettings.h"
#include "SdCardFontGlobals.h"
// Free-function resolver used by CrossPointSettings::getReaderFontId().
// Resolved by the linker — no callback indirection stored in settings.
int resolveSdCardFontId(const char* familyName, uint8_t fontSizeEnum) {
return sdFontSystem.resolveFontId(familyName, fontSizeEnum);
}
// --- Font-family dynamic SettingInfo trampolines ---
//
// The font-family SettingInfo lives in a namespace-static SettingsList that is
// initialized at global-static phase, well before sdFontSystem.begin() runs.
// We therefore cannot bake the SD family list into the SettingInfo at
// construction; instead the SettingInfo holds these stateless trampolines that
// consult sdFontSystem at every call. enumLabels is enriched lazily by the
// consumers (SettingsActivity, CrossPointWebServer) before each iteration.
uint8_t fontFamilyDynamicGetter(const void* /*ctx*/) {
if (SETTINGS.sdFontFamilyName[0] != '\0') {
const auto& families = sdFontSystem.registry().getFamilies();
for (size_t i = 0; i < families.size(); i++) {
if (families[i].name == SETTINGS.sdFontFamilyName) {
return static_cast<uint8_t>(CrossPointSettings::BUILTIN_FONT_COUNT + i);
}
}
// SD family no longer present (card removed?); fall through to built-in.
}
return SETTINGS.fontFamily < CrossPointSettings::BUILTIN_FONT_COUNT ? SETTINGS.fontFamily
: CrossPointSettings::BOOKERLY;
}
void fontFamilyDynamicSetter(void* /*ctx*/, uint8_t value) {
if (value < CrossPointSettings::BUILTIN_FONT_COUNT) {
SETTINGS.fontFamily = value;
SETTINGS.sdFontFamilyName[0] = '\0';
return;
}
const auto& families = sdFontSystem.registry().getFamilies();
uint8_t sdIdx = value - CrossPointSettings::BUILTIN_FONT_COUNT;
if (sdIdx < families.size()) {
strncpy(SETTINGS.sdFontFamilyName, families[sdIdx].name.c_str(), sizeof(SETTINGS.sdFontFamilyName) - 1);
SETTINGS.sdFontFamilyName[sizeof(SETTINGS.sdFontFamilyName) - 1] = '\0';
}
}
uint8_t fontFamilyOptionCount() {
return static_cast<uint8_t>(CrossPointSettings::BUILTIN_FONT_COUNT + sdFontSystem.registry().getFamilies().size());
}
std::string fontFamilyOptionLabel(uint8_t i) {
if (i < CrossPointSettings::BUILTIN_FONT_COUNT) {
static const StrId BUILTIN_LABELS[] = {StrId::STR_BOOKERLY, StrId::STR_NOTO_SANS};
return I18N.get(BUILTIN_LABELS[i]);
}
const auto& families = sdFontSystem.registry().getFamilies();
uint8_t sdIdx = i - CrossPointSettings::BUILTIN_FONT_COUNT;
return sdIdx < families.size() ? families[sdIdx].name : std::string();
}
// Map fontSize enum (SMALL=0, MEDIUM=1, LARGE=2, EXTRA_LARGE=3, TINY=4) to point sizes.
static constexpr uint8_t FONT_SIZE_TO_PT[] = {12, 14, 16, 18, 10};
static uint8_t targetPtSizeFromSettings() {
uint8_t e = SETTINGS.fontSize;
if (e >= sizeof(FONT_SIZE_TO_PT)) e = 1; // default to MEDIUM
return FONT_SIZE_TO_PT[e];
}
void SdCardFontSystem::begin(GfxRenderer& renderer) {
registry_.discover();
// If user has a saved SD font selection, load it
if (SETTINGS.sdFontFamilyName[0] != '\0') {
const auto* family = registry_.findFamily(SETTINGS.sdFontFamilyName);
if (family) {
if (manager_.loadFamily(*family, renderer, targetPtSizeFromSettings())) {
LOG_DBG("SDFS", "Loaded SD card font family: %s", SETTINGS.sdFontFamilyName);
} else {
LOG_ERR("SDFS", "Failed to load SD font family: %s (clearing)", SETTINGS.sdFontFamilyName);
SETTINGS.sdFontFamilyName[0] = '\0';
}
} else {
LOG_DBG("SDFS", "SD font family not found on card: %s (clearing)", SETTINGS.sdFontFamilyName);
SETTINGS.sdFontFamilyName[0] = '\0';
}
}
LOG_DBG("SDFS", "SD font system ready (%d families discovered)", registry_.getFamilyCount());
}
void SdCardFontSystem::ensureLoaded(GfxRenderer& renderer) {
const char* wantedFamily = SETTINGS.sdFontFamilyName;
const std::string& currentFamily = manager_.currentFamilyName();
const uint8_t targetPt = targetPtSizeFromSettings();
if (wantedFamily[0] == '\0') {
if (!currentFamily.empty()) {
manager_.unloadAll(renderer);
}
return;
}
// Reload if family changed OR if the user-selected size changed and the
// family has a closer file than what's currently loaded.
bool familyMatches = (currentFamily == wantedFamily);
if (familyMatches) {
const auto* family = registry_.findFamily(wantedFamily);
if (!family) {
LOG_DBG("SDFS", "SD font family disappeared: %s (clearing)", wantedFamily);
manager_.unloadAll(renderer);
SETTINGS.sdFontFamilyName[0] = '\0';
return;
}
const auto* best = family->pickClosestSize(targetPt);
const uint8_t bestPt = best ? best->pointSize : 0;
if (bestPt == manager_.currentPointSize()) return; // already loaded with the right size
LOG_DBG("SDFS", "Reloading %s: size %u -> %u (target %u)", wantedFamily, manager_.currentPointSize(), bestPt,
targetPt);
}
if (!currentFamily.empty()) {
manager_.unloadAll(renderer);
}
const auto* family = registry_.findFamily(wantedFamily);
if (family) {
if (manager_.loadFamily(*family, renderer, targetPt)) {
LOG_DBG("SDFS", "Loaded SD font family: %s", wantedFamily);
} else {
LOG_ERR("SDFS", "Failed to load SD font family: %s (clearing)", wantedFamily);
SETTINGS.sdFontFamilyName[0] = '\0';
}
} else {
LOG_DBG("SDFS", "SD font family not found: %s (clearing)", wantedFamily);
SETTINGS.sdFontFamilyName[0] = '\0';
}
}
static uint8_t targetPtSizeFromEnum(uint8_t fontSizeEnum) {
if (fontSizeEnum >= sizeof(FONT_SIZE_TO_PT)) fontSizeEnum = 1; // default to MEDIUM
return FONT_SIZE_TO_PT[fontSizeEnum];
}
int SdCardFontSystem::resolveFontId(const char* familyName, uint8_t fontSizeEnum) const {
// The manager loads exactly one size for the active SD family. Resolve only
// if the requested family matches the loaded family and the requested size
// matches the loaded size. otherwise return 0 so callers can fall back.
if (!familyName || familyName[0] == '\0') return 0;
if (manager_.currentFamilyName() != familyName) return 0;
if (manager_.currentPointSize() != targetPtSizeFromEnum(fontSizeEnum)) return 0;
return manager_.getFontId(familyName);
}