Compare commits

...

5 Commits

Author SHA1 Message Date
Jonas Diemer
bb2e194713
Merge 2f57565b9d into d403044f76 2026-02-03 18:42:54 +03:00
Aaron Cunliffe
d403044f76
fix: Increase network SSID display length (#670)
Some checks are pending
CI / build (push) Waiting to run
## Rationale 

I have 2 wifi access points with almost identical names, just one has
`_EXT` at the end of it. With the current display limit of 13 characters
before adding ellipsis, I can't tell which is which.

Before device screenshot with masked SSIDs:
<img
src="https://github.com/user-attachments/assets/3c5cbbaa-b2f6-412f-b5a8-6278963bd0f2"
width="300">


## Summary

Adjusted displayed length from 13 characters to 30 in the Wifi selection
screen - I've left some space for potential proportional font changes in
the future

After image with masked SSIDs:
<img
src="https://github.com/user-attachments/assets/c5f0712b-bbd3-4eec-9820-4693fae90c9f"
width="300">

---

### 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? _**< NO >**_
2026-02-03 18:24:23 +03:00
Jonas Diemer
2f57565b9d Round correctly and remove debug print. 2026-02-01 13:49:52 +01:00
Jonas Diemer
1a7fed2a69 Allow also 75%. 2026-02-01 13:20:33 +01:00
Jonas Diemer
440bf9e733 Added option to increase word spacing. 2026-02-01 13:16:53 +01:00
12 changed files with 43 additions and 25 deletions

View File

@ -68,7 +68,7 @@ void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fo
applyParagraphIndent();
const int pageWidth = viewportWidth;
const int spaceWidth = renderer.getSpaceWidth(fontId);
const int spaceWidth = std::round(static_cast<float>(wordSpacing) / 100.0 * renderer.getSpaceWidth(fontId));
auto wordWidths = calculateWordWidths(renderer, fontId);
std::vector<size_t> lineBreakIndices;
if (hyphenationEnabled) {
@ -385,4 +385,4 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
}
processLine(std::make_shared<TextBlock>(std::move(lineWords), std::move(lineXPos), std::move(lineWordStyles), style));
}
}

View File

@ -18,6 +18,7 @@ class ParsedText {
TextBlock::Style style;
bool extraParagraphSpacing;
bool hyphenationEnabled;
uint8_t wordSpacing;
void applyParagraphIndent();
std::vector<size_t> computeLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth, int spaceWidth,
@ -33,8 +34,11 @@ class ParsedText {
public:
explicit ParsedText(const TextBlock::Style style, const bool extraParagraphSpacing,
const bool hyphenationEnabled = false)
: style(style), extraParagraphSpacing(extraParagraphSpacing), hyphenationEnabled(hyphenationEnabled) {}
const bool hyphenationEnabled = false, const uint8_t wordSpacing = 100)
: style(style),
extraParagraphSpacing(extraParagraphSpacing),
hyphenationEnabled(hyphenationEnabled),
wordSpacing(wordSpacing) {}
~ParsedText() = default;
void addWord(std::string word, EpdFontFamily::Style fontStyle);
@ -45,4 +49,4 @@ class ParsedText {
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, uint16_t viewportWidth,
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
bool includeLastLine = true);
};
};

View File

@ -8,10 +8,10 @@
#include "parsers/ChapterHtmlSlimParser.h"
namespace {
constexpr uint8_t SECTION_FILE_VERSION = 10;
constexpr uint8_t SECTION_FILE_VERSION = 11;
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(uint8_t) +
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(bool) +
sizeof(uint32_t);
sizeof(uint32_t) + sizeof(uint8_t);
} // namespace
uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
@ -33,7 +33,8 @@ uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
void Section::writeSectionFileHeader(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled) {
const uint16_t viewportHeight, const bool hyphenationEnabled,
const uint8_t wordSpacing) {
if (!file) {
Serial.printf("[%lu] [SCT] File not open for writing header\n", millis());
return;
@ -41,7 +42,7 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
static_assert(HEADER_SIZE == sizeof(SECTION_FILE_VERSION) + sizeof(fontId) + sizeof(lineCompression) +
sizeof(extraParagraphSpacing) + sizeof(paragraphAlignment) + sizeof(viewportWidth) +
sizeof(viewportHeight) + sizeof(pageCount) + sizeof(hyphenationEnabled) +
sizeof(uint32_t),
sizeof(uint32_t) + sizeof(wordSpacing),
"Header size mismatch");
serialization::writePod(file, SECTION_FILE_VERSION);
serialization::writePod(file, fontId);
@ -51,13 +52,14 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
serialization::writePod(file, viewportWidth);
serialization::writePod(file, viewportHeight);
serialization::writePod(file, hyphenationEnabled);
serialization::writePod(file, wordSpacing);
serialization::writePod(file, pageCount); // Placeholder for page count (will be initially 0 when written)
serialization::writePod(file, static_cast<uint32_t>(0)); // Placeholder for LUT offset
}
bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled) {
const uint16_t viewportHeight, const bool hyphenationEnabled, uint8_t wordSpacing) {
if (!SdMan.openFileForRead("SCT", filePath, file)) {
return false;
}
@ -79,6 +81,8 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
bool fileExtraParagraphSpacing;
uint8_t fileParagraphAlignment;
bool fileHyphenationEnabled;
uint8_t fileWordSpacing;
serialization::readPod(file, fileFontId);
serialization::readPod(file, fileLineCompression);
serialization::readPod(file, fileExtraParagraphSpacing);
@ -86,11 +90,12 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
serialization::readPod(file, fileViewportWidth);
serialization::readPod(file, fileViewportHeight);
serialization::readPod(file, fileHyphenationEnabled);
serialization::readPod(file, fileWordSpacing);
if (fontId != fileFontId || lineCompression != fileLineCompression ||
extraParagraphSpacing != fileExtraParagraphSpacing || paragraphAlignment != fileParagraphAlignment ||
viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight ||
hyphenationEnabled != fileHyphenationEnabled) {
hyphenationEnabled != fileHyphenationEnabled || wordSpacing != fileWordSpacing) {
file.close();
Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis());
clearCache();
@ -122,7 +127,7 @@ bool Section::clearCache() const {
bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled,
const uint16_t viewportHeight, const bool hyphenationEnabled, const uint8_t wordSpacing,
const std::function<void()>& popupFn) {
const auto localPath = epub->getSpineItem(spineIndex).href;
const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html";
@ -173,12 +178,12 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
return false;
}
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
viewportHeight, hyphenationEnabled);
viewportHeight, hyphenationEnabled, wordSpacing);
std::vector<uint32_t> lut = {};
ChapterHtmlSlimParser visitor(
tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
viewportHeight, hyphenationEnabled,
viewportHeight, hyphenationEnabled, wordSpacing,
[this, &lut](std::unique_ptr<Page> page) { lut.emplace_back(this->onPageComplete(std::move(page))); }, popupFn);
Hyphenator::setPreferredLanguage(epub->getLanguage());
success = visitor.parseAndBuildPages();

View File

@ -15,7 +15,8 @@ class Section {
FsFile file;
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled);
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled,
uint8_t wordSpacing);
uint32_t onPageComplete(std::unique_ptr<Page> page);
public:
@ -29,10 +30,10 @@ class Section {
filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {}
~Section() = default;
bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled);
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, uint8_t wordSpacing);
bool clearCache() const;
bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled,
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, uint8_t wordSpacing,
const std::function<void()>& popupFn = nullptr);
std::unique_ptr<Page> loadPageFromSectionFile();
};

View File

@ -68,7 +68,7 @@ void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::Style style) {
makePages();
}
currentTextBlock.reset(new ParsedText(style, extraParagraphSpacing, hyphenationEnabled));
currentTextBlock.reset(new ParsedText(style, extraParagraphSpacing, hyphenationEnabled, wordSpacing));
}
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {

View File

@ -37,6 +37,7 @@ class ChapterHtmlSlimParser {
uint16_t viewportWidth;
uint16_t viewportHeight;
bool hyphenationEnabled;
uint8_t wordSpacing;
void startNewTextBlock(TextBlock::Style style);
void flushPartWordBuffer();
@ -51,12 +52,14 @@ class ChapterHtmlSlimParser {
const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled,
const uint8_t wordSpacing,
const std::function<void(std::unique_ptr<Page>)>& completePageFn,
const std::function<void()>& popupFn = nullptr)
: filepath(filepath),
renderer(renderer),
fontId(fontId),
lineCompression(lineCompression),
wordSpacing(wordSpacing),
extraParagraphSpacing(extraParagraphSpacing),
paragraphAlignment(paragraphAlignment),
viewportWidth(viewportWidth),

View File

@ -22,7 +22,7 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) {
namespace {
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
// Increment this when adding new persisted settings fields
constexpr uint8_t SETTINGS_COUNT = 23;
constexpr uint8_t SETTINGS_COUNT = 24;
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
} // namespace
@ -60,6 +60,7 @@ bool CrossPointSettings::saveToFile() const {
serialization::writeString(outputFile, std::string(opdsUsername));
serialization::writeString(outputFile, std::string(opdsPassword));
serialization::writePod(outputFile, sleepScreenCoverFilter);
serialization::writePod(outputFile, wordSpacing);
// New fields added at end for backward compatibility
outputFile.close();
@ -148,6 +149,8 @@ bool CrossPointSettings::loadFromFile() {
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, wordSpacing, 255);
if (++settingsRead >= fileSettingsCount) break;
// New fields added at end for backward compatibility
} while (false);

View File

@ -107,6 +107,7 @@ class CrossPointSettings {
uint8_t statusBar = FULL;
// Text rendering settings
uint8_t extraParagraphSpacing = 1;
uint8_t wordSpacing = 100;
uint8_t textAntiAliasing = 1;
// Short power button click behaviour
uint8_t shortPwrBtn = IGNORE;

View File

@ -546,8 +546,8 @@ void WifiSelectionActivity::renderNetworkList() const {
// Draw network name (truncate if too long)
std::string displayName = network.ssid;
if (displayName.length() > 16) {
displayName.replace(13, displayName.length() - 13, "...");
if (displayName.length() > 33) {
displayName.replace(30, displayName.length() - 30, "...");
}
renderer.drawText(UI_10_FONT_ID, 20, networkY, displayName.c_str());

View File

@ -366,14 +366,14 @@ void EpubReaderActivity::renderScreen() {
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled)) {
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.wordSpacing)) {
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
const auto popupFn = [this]() { ScreenComponents::drawPopup(renderer, "Indexing..."); };
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, popupFn)) {
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.wordSpacing, popupFn)) {
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
section.reset();
return;

View File

@ -88,7 +88,7 @@ void CategorySettingsActivity::toggleCurrentSetting() {
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
const int8_t currentValue = SETTINGS.*(setting.valuePtr);
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
SETTINGS.*(setting.valuePtr) = setting.valueRange.min;
} else {

View File

@ -24,11 +24,12 @@ const SettingInfo displaySettings[displaySettingsCount] = {
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})};
constexpr int readerSettingsCount = 9;
constexpr int readerSettingsCount = 10;
const SettingInfo readerSettings[readerSettingsCount] = {
SettingInfo::Enum("Font Family", &CrossPointSettings::fontFamily, {"Bookerly", "Noto Sans", "Open Dyslexic"}),
SettingInfo::Enum("Font Size", &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}),
SettingInfo::Enum("Line Spacing", &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}),
SettingInfo::Value("Word Spacing %", &CrossPointSettings::wordSpacing, {75, 250, 25}),
SettingInfo::Value("Screen Margin", &CrossPointSettings::screenMargin, {5, 40, 5}),
SettingInfo::Enum("Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
{"Justify", "Left", "Center", "Right"}),