Merge remote-tracking branch 'origin/master' into hyphenation-v3

This commit is contained in:
Arthur Tazhitdinov 2026-01-15 20:20:25 +05:00
commit 32cffaf504
8 changed files with 67 additions and 9 deletions

View File

@ -96,6 +96,10 @@ The Settings screen allows you to configure the device's behavior. There are a f
- Left, Right, Back, Confirm - Left, Right, Back, Confirm
- Left, Back, Confirm, Right - Left, Back, Confirm, Right
- **Side Button Layout (reader)**: Swap the order of the up and down volume buttons from Previous/Next to Next/Previous. This change is only in effect when reading. - **Side Button Layout (reader)**: Swap the order of the up and down volume buttons from Previous/Next to Next/Previous. This change is only in effect when reading.
- **Long-press Chapter Skip**: Set whether long-pressing page turn buttons skip to the next/previous chapter.
- "Chapter Skip" (default) - Long-pressing skips to next/previous chapter
- "Page Scroll" - Long-pressing scrolls a page up/down
- Swap the order of the up and down volume buttons from Previous/Next to Next/Previous. This change is only in effect when reading.
- **Reader Font Family**: Choose the font used for reading: - **Reader Font Family**: Choose the font used for reading:
- "Bookerly" (default) - Amazon's reading font - "Bookerly" (default) - Amazon's reading font
- "Noto Sans" - Google's sans-serif font - "Noto Sans" - Google's sans-serif font
@ -144,6 +148,9 @@ If the **Short Power Button Click** setting is set to "Page Turn", you can also
* **Next Chapter:** Press and **hold** the **Right** (or **Volume Down**) button briefly, then release. * **Next Chapter:** Press and **hold** the **Right** (or **Volume Down**) button briefly, then release.
* **Previous Chapter:** Press and **hold** the **Left** (or **Volume Up**) button briefly, then release. * **Previous Chapter:** Press and **hold** the **Left** (or **Volume Up**) button briefly, then release.
This feature can be disabled in **[Settings](#35-settings)** to help avoid changing chapters by mistake.
### System Navigation ### System Navigation
* **Return to Book Selection:** Press **Back** to close the book and return to the **[Book Selection](#32-book-selection)** screen. * **Return to Book Selection:** Press **Back** to close the book and return to the **[Book Selection](#32-book-selection)** screen.
* **Return to Home:** Press and **hold** the **Back** button to close the book and return to the **[Home](#31-home-screen)** screen. * **Return to Home:** Press and **hold** the **Back** button to close the book and return to the **[Home](#31-home-screen)** screen.

View File

@ -25,7 +25,7 @@ constexpr int NUM_ITALIC_TAGS = sizeof(ITALIC_TAGS) / sizeof(ITALIC_TAGS[0]);
const char* IMAGE_TAGS[] = {"img"}; const char* IMAGE_TAGS[] = {"img"};
constexpr int NUM_IMAGE_TAGS = sizeof(IMAGE_TAGS) / sizeof(IMAGE_TAGS[0]); constexpr int NUM_IMAGE_TAGS = sizeof(IMAGE_TAGS) / sizeof(IMAGE_TAGS[0]);
const char* SKIP_TAGS[] = {"head", "table"}; const char* SKIP_TAGS[] = {"head"};
constexpr int NUM_SKIP_TAGS = sizeof(SKIP_TAGS) / sizeof(SKIP_TAGS[0]); constexpr int NUM_SKIP_TAGS = sizeof(SKIP_TAGS) / sizeof(SKIP_TAGS[0]);
bool isWhitespace(const char c) { return c == ' ' || c == '\r' || c == '\n' || c == '\t'; } bool isWhitespace(const char c) { return c == ' ' || c == '\r' || c == '\n' || c == '\t'; }
@ -63,13 +63,44 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
return; return;
} }
if (matches(name, IMAGE_TAGS, NUM_IMAGE_TAGS)) { // Special handling for tables - show placeholder text instead of dropping silently
// TODO: Start processing image tags if (strcmp(name, "table") == 0) {
// Add placeholder text
self->startNewTextBlock(TextBlock::CENTER_ALIGN);
if (self->currentTextBlock) {
self->currentTextBlock->addWord("[Table omitted]", EpdFontFamily::ITALIC);
}
// Skip table contents
self->skipUntilDepth = self->depth; self->skipUntilDepth = self->depth;
self->depth += 1; self->depth += 1;
return; return;
} }
if (matches(name, IMAGE_TAGS, NUM_IMAGE_TAGS)) {
// TODO: Start processing image tags
std::string alt;
if (atts != nullptr) {
for (int i = 0; atts[i]; i += 2) {
if (strcmp(atts[i], "alt") == 0) {
alt = "[Image: " + std::string(atts[i + 1]) + "]";
}
}
Serial.printf("[%lu] [EHP] Image alt: %s\n", millis(), alt.c_str());
self->startNewTextBlock(TextBlock::CENTER_ALIGN);
self->italicUntilDepth = min(self->italicUntilDepth, self->depth);
self->depth += 1;
self->characterData(userData, alt.c_str(), alt.length());
} else {
// Skip for now
self->skipUntilDepth = self->depth;
self->depth += 1;
return;
}
}
if (matches(name, SKIP_TAGS, NUM_SKIP_TAGS)) { if (matches(name, SKIP_TAGS, NUM_SKIP_TAGS)) {
// start skip // start skip
self->skipUntilDepth = self->depth; self->skipUntilDepth = self->depth;

View File

@ -468,7 +468,10 @@ int GfxRenderer::getLineHeight(const int fontId) const {
} }
void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char* btn2, const char* btn3, void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char* btn2, const char* btn3,
const char* btn4) const { const char* btn4) {
const Orientation orig_orientation = getOrientation();
setOrientation(Orientation::Portrait);
const int pageHeight = getScreenHeight(); const int pageHeight = getScreenHeight();
constexpr int buttonWidth = 106; constexpr int buttonWidth = 106;
constexpr int buttonHeight = 40; constexpr int buttonHeight = 40;
@ -481,12 +484,15 @@ void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char
// Only draw if the label is non-empty // Only draw if the label is non-empty
if (labels[i] != nullptr && labels[i][0] != '\0') { if (labels[i] != nullptr && labels[i][0] != '\0') {
const int x = buttonPositions[i]; const int x = buttonPositions[i];
fillRect(x, pageHeight - buttonY, buttonWidth, buttonHeight, false);
drawRect(x, pageHeight - buttonY, buttonWidth, buttonHeight); drawRect(x, pageHeight - buttonY, buttonWidth, buttonHeight);
const int textWidth = getTextWidth(fontId, labels[i]); const int textWidth = getTextWidth(fontId, labels[i]);
const int textX = x + (buttonWidth - 1 - textWidth) / 2; const int textX = x + (buttonWidth - 1 - textWidth) / 2;
drawText(fontId, textX, pageHeight - buttonY + textYOffset, labels[i]); drawText(fontId, textX, pageHeight - buttonY + textYOffset, labels[i]);
} }
} }
setOrientation(orig_orientation);
} }
void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, const char* bottomBtn) const { void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, const char* bottomBtn) const {

View File

@ -84,7 +84,7 @@ class GfxRenderer {
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const; EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
// UI Components // UI Components
void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4) const; void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4);
void drawSideButtonHints(int fontId, const char* topBtn, const char* bottomBtn) const; void drawSideButtonHints(int fontId, const char* topBtn, const char* bottomBtn) const;
private: private:

View File

@ -45,9 +45,9 @@ lib_deps =
InputManager=symlink://open-x4-sdk/libs/hardware/InputManager InputManager=symlink://open-x4-sdk/libs/hardware/InputManager
EInkDisplay=symlink://open-x4-sdk/libs/display/EInkDisplay EInkDisplay=symlink://open-x4-sdk/libs/display/EInkDisplay
SDCardManager=symlink://open-x4-sdk/libs/hardware/SDCardManager SDCardManager=symlink://open-x4-sdk/libs/hardware/SDCardManager
ArduinoJson @ 7.4.2 bblanchon/ArduinoJson @ 7.4.2
QRCode @ 0.0.1 ricmoo/QRCode @ 0.0.1
links2004/WebSockets @ ^2.4.1 links2004/WebSockets @ 2.7.3
[env:default] [env:default]
extends = base extends = base

View File

@ -37,6 +37,14 @@ void WifiSelectionActivity::onEnter() {
savePromptSelection = 0; savePromptSelection = 0;
forgetPromptSelection = 0; forgetPromptSelection = 0;
// Cache MAC address for display
uint8_t mac[6];
WiFi.macAddress(mac);
char macStr[32];
snprintf(macStr, sizeof(macStr), "MAC address: %02x-%02x-%02x-%02x-%02x-%02x", mac[0], mac[1], mac[2], mac[3], mac[4],
mac[5]);
cachedMacAddress = std::string(macStr);
// Trigger first update to show scanning message // Trigger first update to show scanning message
updateRequired = true; updateRequired = true;
@ -572,6 +580,9 @@ void WifiSelectionActivity::renderNetworkList() const {
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 90, countStr); renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 90, countStr);
} }
// Show MAC address above the network count and legend
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 105, cachedMacAddress.c_str());
// Draw help text // Draw help text
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved"); renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved");
const auto labels = mappedInput.mapLabels("« Back", "Connect", "", ""); const auto labels = mappedInput.mapLabels("« Back", "Connect", "", "");

View File

@ -62,6 +62,9 @@ class WifiSelectionActivity final : public ActivityWithSubactivity {
// Password to potentially save (from keyboard or saved credentials) // Password to potentially save (from keyboard or saved credentials)
std::string enteredPassword; std::string enteredPassword;
// Cached MAC address string for display
std::string cachedMacAddress;
// Whether network was connected using a saved password (skip save prompt) // Whether network was connected using a saved password (skip save prompt)
bool usedSavedPassword = false; bool usedSavedPassword = false;

View File

@ -127,7 +127,7 @@ void XtcReaderActivity::loop() {
return; return;
} }
const bool skipPages = mappedInput.getHeldTime() > skipPageMs; const bool skipPages = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipPageMs;
const int skipAmount = skipPages ? 10 : 1; const int skipAmount = skipPages ? 10 : 1;
if (prevReleased) { if (prevReleased) {