Integrate upstream #1392 by adriancaruana

This commit is contained in:
jpirnay
2026-04-28 10:00:22 +02:00
parent ac96d89f83
commit aa9fae16d9
29 changed files with 3083 additions and 77 deletions
+160
View File
@@ -0,0 +1,160 @@
#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) { return sdFontSystem.resolveFontId(familyName, 0); }
// --- 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, StrId::STR_OPEN_DYSLEXIC};
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) to point sizes.
static constexpr uint8_t FONT_SIZE_TO_PT[] = {12, 14, 16, 18};
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;
}
uint8_t bestPt = 0;
int bestDiff = INT32_MAX;
for (const auto& f : family->files) {
int diff = abs(static_cast<int>(f.pointSize) - static_cast<int>(targetPt));
if (diff < bestDiff) {
bestDiff = diff;
bestPt = f.pointSize;
}
}
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';
}
}
int SdCardFontSystem::resolveFontId(const char* familyName, uint8_t /*fontSizeEnum*/) const {
// The manager loads exactly one size (closest to SETTINGS.fontSize), so the
// enum is implicit — always return the single loaded font ID for this family.
// ensureLoaded() must have been called with the current settings before this.
return manager_.getFontId(familyName);
}