fix: resolve mod build errors after upstream sync
- Update open-x4-sdk submodule to 9f76376 (BatteryMonitor ESP-IDF 5.x compat) - Add RTC_NOINIT bounds check for logHead in Logging.cpp - Add drawTextRotated90CCW to GfxRenderer for dictionary UI - Add getWordXpos() accessor to TextBlock for dictionary word selection - Fix bare include paths (ActivityResult.h, RenderLock.h) across 10 files - Fix rvalue ref binding in setResult() lambdas (std::move pattern) - Fix std::max type mismatch (uint8_t vs int) in EpubReaderActivity - Fix FsFile forward declaration conflict in Dictionary.h - Restore StringUtils::checkFileExtension() and sortFileList() - Restore RecentBooksStore::removeBook() Made-with: Cursor
This commit is contained in:
@@ -28,6 +28,7 @@ class TextBlock final : public Block {
|
|||||||
void setBlockStyle(const BlockStyle& blockStyle) { this->blockStyle = blockStyle; }
|
void setBlockStyle(const BlockStyle& blockStyle) { this->blockStyle = blockStyle; }
|
||||||
const BlockStyle& getBlockStyle() const { return blockStyle; }
|
const BlockStyle& getBlockStyle() const { return blockStyle; }
|
||||||
const std::vector<std::string>& getWords() const { return words; }
|
const std::vector<std::string>& getWords() const { return words; }
|
||||||
|
const std::vector<int16_t>& getWordXpos() const { return wordXpos; }
|
||||||
bool isEmpty() override { return words.empty(); }
|
bool isEmpty() override { return words.empty(); }
|
||||||
size_t wordCount() const { return words.size(); }
|
size_t wordCount() const { return words.size(); }
|
||||||
// given a renderer works out where to break the words into lines
|
// given a renderer works out where to break the words into lines
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ static inline void rotateCoordinates(const GfxRenderer::Orientation orientation,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TextRotation { None, Rotated90CW };
|
enum class TextRotation { None, Rotated90CW, Rotated90CCW };
|
||||||
|
|
||||||
// Shared glyph rendering logic for normal and rotated text.
|
// Shared glyph rendering logic for normal and rotated text.
|
||||||
// Coordinate mapping and cursor advance direction are selected at compile time via the template parameter.
|
// Coordinate mapping and cursor advance direction are selected at compile time via the template parameter.
|
||||||
@@ -91,6 +91,9 @@ static void renderCharImpl(const GfxRenderer& renderer, GfxRenderer::RenderMode
|
|||||||
if constexpr (rotation == TextRotation::Rotated90CW) {
|
if constexpr (rotation == TextRotation::Rotated90CW) {
|
||||||
outerBase = cursorX + fontData->ascender - top; // screenX = outerBase + glyphY
|
outerBase = cursorX + fontData->ascender - top; // screenX = outerBase + glyphY
|
||||||
innerBase = cursorY - left; // screenY = innerBase - glyphX
|
innerBase = cursorY - left; // screenY = innerBase - glyphX
|
||||||
|
} else if constexpr (rotation == TextRotation::Rotated90CCW) {
|
||||||
|
outerBase = cursorX + fontData->advanceY - 1 - fontData->ascender + top; // screenX = outerBase - glyphY
|
||||||
|
innerBase = cursorY + left; // screenY = innerBase + glyphX
|
||||||
} else {
|
} else {
|
||||||
outerBase = cursorY - top; // screenY = outerBase + glyphY
|
outerBase = cursorY - top; // screenY = outerBase + glyphY
|
||||||
innerBase = cursorX + left; // screenX = innerBase + glyphX
|
innerBase = cursorX + left; // screenX = innerBase + glyphX
|
||||||
@@ -99,12 +102,16 @@ static void renderCharImpl(const GfxRenderer& renderer, GfxRenderer::RenderMode
|
|||||||
if (is2Bit) {
|
if (is2Bit) {
|
||||||
int pixelPosition = 0;
|
int pixelPosition = 0;
|
||||||
for (int glyphY = 0; glyphY < height; glyphY++) {
|
for (int glyphY = 0; glyphY < height; glyphY++) {
|
||||||
const int outerCoord = outerBase + glyphY;
|
const int outerCoord =
|
||||||
|
(rotation == TextRotation::Rotated90CCW) ? outerBase - glyphY : outerBase + glyphY;
|
||||||
for (int glyphX = 0; glyphX < width; glyphX++, pixelPosition++) {
|
for (int glyphX = 0; glyphX < width; glyphX++, pixelPosition++) {
|
||||||
int screenX, screenY;
|
int screenX, screenY;
|
||||||
if constexpr (rotation == TextRotation::Rotated90CW) {
|
if constexpr (rotation == TextRotation::Rotated90CW) {
|
||||||
screenX = outerCoord;
|
screenX = outerCoord;
|
||||||
screenY = innerBase - glyphX;
|
screenY = innerBase - glyphX;
|
||||||
|
} else if constexpr (rotation == TextRotation::Rotated90CCW) {
|
||||||
|
screenX = outerCoord;
|
||||||
|
screenY = innerBase + glyphX;
|
||||||
} else {
|
} else {
|
||||||
screenX = innerBase + glyphX;
|
screenX = innerBase + glyphX;
|
||||||
screenY = outerCoord;
|
screenY = outerCoord;
|
||||||
@@ -133,12 +140,16 @@ static void renderCharImpl(const GfxRenderer& renderer, GfxRenderer::RenderMode
|
|||||||
} else {
|
} else {
|
||||||
int pixelPosition = 0;
|
int pixelPosition = 0;
|
||||||
for (int glyphY = 0; glyphY < height; glyphY++) {
|
for (int glyphY = 0; glyphY < height; glyphY++) {
|
||||||
const int outerCoord = outerBase + glyphY;
|
const int outerCoord =
|
||||||
|
(rotation == TextRotation::Rotated90CCW) ? outerBase - glyphY : outerBase + glyphY;
|
||||||
for (int glyphX = 0; glyphX < width; glyphX++, pixelPosition++) {
|
for (int glyphX = 0; glyphX < width; glyphX++, pixelPosition++) {
|
||||||
int screenX, screenY;
|
int screenX, screenY;
|
||||||
if constexpr (rotation == TextRotation::Rotated90CW) {
|
if constexpr (rotation == TextRotation::Rotated90CW) {
|
||||||
screenX = outerCoord;
|
screenX = outerCoord;
|
||||||
screenY = innerBase - glyphX;
|
screenY = innerBase - glyphX;
|
||||||
|
} else if constexpr (rotation == TextRotation::Rotated90CCW) {
|
||||||
|
screenX = outerCoord;
|
||||||
|
screenY = innerBase + glyphX;
|
||||||
} else {
|
} else {
|
||||||
screenX = innerBase + glyphX;
|
screenX = innerBase + glyphX;
|
||||||
screenY = outerCoord;
|
screenY = outerCoord;
|
||||||
@@ -1243,6 +1254,64 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GfxRenderer::drawTextRotated90CCW(const int fontId, const int x, const int y, const char* text, const bool black,
|
||||||
|
const EpdFontFamily::Style style) const {
|
||||||
|
if (text == nullptr || *text == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& font = fontIt->second;
|
||||||
|
|
||||||
|
int32_t yPosFP = fp4::fromPixel(y);
|
||||||
|
int lastBaseY = y;
|
||||||
|
int lastBaseAdvanceFP = 0;
|
||||||
|
int lastBaseTop = 0;
|
||||||
|
constexpr int MIN_COMBINING_GAP_PX = 1;
|
||||||
|
|
||||||
|
uint32_t cp;
|
||||||
|
uint32_t prevCp = 0;
|
||||||
|
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
|
||||||
|
if (utf8IsCombiningMark(cp)) {
|
||||||
|
const EpdGlyph* combiningGlyph = font.getGlyph(cp, style);
|
||||||
|
int raiseBy = 0;
|
||||||
|
if (combiningGlyph) {
|
||||||
|
const int currentGap = combiningGlyph->top - combiningGlyph->height - lastBaseTop;
|
||||||
|
if (currentGap < MIN_COMBINING_GAP_PX) {
|
||||||
|
raiseBy = MIN_COMBINING_GAP_PX - currentGap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int combiningX = x + raiseBy;
|
||||||
|
const int combiningY = lastBaseY + fp4::toPixel(lastBaseAdvanceFP / 2);
|
||||||
|
renderCharImpl<TextRotation::Rotated90CCW>(*this, renderMode, font, cp, combiningX, combiningY, black, style);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = font.applyLigatures(cp, text, style);
|
||||||
|
if (prevCp != 0) {
|
||||||
|
yPosFP += font.getKerning(prevCp, cp, style); // add for CCW (opposite of CW)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastBaseY = fp4::toPixel(yPosFP);
|
||||||
|
const EpdGlyph* glyph = font.getGlyph(cp, style);
|
||||||
|
|
||||||
|
lastBaseAdvanceFP = glyph ? glyph->advanceX : 0;
|
||||||
|
lastBaseTop = glyph ? glyph->top : 0;
|
||||||
|
|
||||||
|
renderCharImpl<TextRotation::Rotated90CCW>(*this, renderMode, font, cp, x, lastBaseY, black, style);
|
||||||
|
if (glyph) {
|
||||||
|
yPosFP += glyph->advanceX; // add for CCW (opposite of CW)
|
||||||
|
}
|
||||||
|
prevCp = cp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* GfxRenderer::getFrameBuffer() const { return frameBuffer; }
|
uint8_t* GfxRenderer::getFrameBuffer() const { return frameBuffer; }
|
||||||
|
|
||||||
size_t GfxRenderer::getBufferSize() { return HalDisplay::BUFFER_SIZE; }
|
size_t GfxRenderer::getBufferSize() { return HalDisplay::BUFFER_SIZE; }
|
||||||
|
|||||||
@@ -135,9 +135,11 @@ class GfxRenderer {
|
|||||||
std::vector<std::string> wrappedText(int fontId, const char* text, int maxWidth, int maxLines,
|
std::vector<std::string> wrappedText(int fontId, const char* text, int maxWidth, int maxLines,
|
||||||
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
|
|
||||||
// Helper for drawing rotated text (90 degrees clockwise, for side buttons)
|
// Helpers for drawing rotated text (for side buttons)
|
||||||
void drawTextRotated90CW(int fontId, int x, int y, const char* text, bool black = true,
|
void drawTextRotated90CW(int fontId, int x, int y, const char* text, bool black = true,
|
||||||
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
|
void drawTextRotated90CCW(int fontId, int x, int y, const char* text, bool black = true,
|
||||||
|
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||||
int getTextHeight(int fontId) const;
|
int getTextHeight(int fontId) const;
|
||||||
|
|
||||||
// Grayscale functions
|
// Grayscale functions
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ RTC_NOINIT_ATTR char logMessages[MAX_LOG_LINES][MAX_ENTRY_LEN];
|
|||||||
RTC_NOINIT_ATTR size_t logHead = 0;
|
RTC_NOINIT_ATTR size_t logHead = 0;
|
||||||
|
|
||||||
void addToLogRingBuffer(const char* message) {
|
void addToLogRingBuffer(const char* message) {
|
||||||
// Add the message to the ring buffer, overwriting old messages if necessary
|
// RTC_NOINIT memory may contain garbage after flash erase or power loss
|
||||||
|
if (logHead >= MAX_LOG_LINES) {
|
||||||
|
logHead = 0;
|
||||||
|
}
|
||||||
strncpy(logMessages[logHead], message, MAX_ENTRY_LEN - 1);
|
strncpy(logMessages[logHead], message, MAX_ENTRY_LEN - 1);
|
||||||
logMessages[logHead][MAX_ENTRY_LEN - 1] = '\0';
|
logMessages[logHead][MAX_ENTRY_LEN - 1] = '\0';
|
||||||
logHead = (logHead + 1) % MAX_LOG_LINES;
|
logHead = (logHead + 1) % MAX_LOG_LINES;
|
||||||
|
|||||||
Submodule open-x4-sdk updated: 91e7e2bef7...9f76376a5c
@@ -53,6 +53,15 @@ void RecentBooksStore::updateBook(const std::string& path, const std::string& ti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecentBooksStore::removeBook(const std::string& path) {
|
||||||
|
auto it =
|
||||||
|
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
|
||||||
|
if (it != recentBooks.end()) {
|
||||||
|
recentBooks.erase(it);
|
||||||
|
saveToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool RecentBooksStore::saveToFile() const {
|
bool RecentBooksStore::saveToFile() const {
|
||||||
Storage.mkdir("/.crosspoint");
|
Storage.mkdir("/.crosspoint");
|
||||||
return JsonSettingsIO::saveRecentBooks(*this, RECENT_BOOKS_FILE_JSON);
|
return JsonSettingsIO::saveRecentBooks(*this, RECENT_BOOKS_FILE_JSON);
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ class RecentBooksStore {
|
|||||||
void updateBook(const std::string& path, const std::string& title, const std::string& author,
|
void updateBook(const std::string& path, const std::string& title, const std::string& author,
|
||||||
const std::string& coverBmpPath);
|
const std::string& coverBmpPath);
|
||||||
|
|
||||||
|
void removeBook(const std::string& path);
|
||||||
|
|
||||||
// Get the list of recent books (most recent first)
|
// Get the list of recent books (most recent first)
|
||||||
const std::vector<RecentBook>& getBooks() const { return recentBooks; }
|
const std::vector<RecentBook>& getBooks() const { return recentBooks; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include "DictionaryDefinitionActivity.h"
|
#include "DictionaryDefinitionActivity.h"
|
||||||
#include "HalDisplay.h"
|
#include "HalDisplay.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "RenderLock.h"
|
#include "activities/RenderLock.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "util/Dictionary.h"
|
#include "util/Dictionary.h"
|
||||||
@@ -56,9 +56,9 @@ void DictionarySuggestionsActivity::loop() {
|
|||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, selected, definition, readerFontId,
|
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, selected, definition, readerFontId,
|
||||||
orientation, true),
|
orientation, true),
|
||||||
[this](const ActivityResult& result) {
|
[this](ActivityResult result) {
|
||||||
if (!result.isCancelled) {
|
if (!result.isCancelled) {
|
||||||
setResult(result);
|
setResult(std::move(result));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
#include "DictionaryDefinitionActivity.h"
|
#include "DictionaryDefinitionActivity.h"
|
||||||
#include "DictionarySuggestionsActivity.h"
|
#include "DictionarySuggestionsActivity.h"
|
||||||
@@ -353,9 +353,9 @@ void DictionaryWordSelectActivity::loop() {
|
|||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, cleaned, definition, fontId, orientation,
|
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, cleaned, definition, fontId, orientation,
|
||||||
true),
|
true),
|
||||||
[this](const ActivityResult& result) {
|
[this](ActivityResult result) {
|
||||||
if (!result.isCancelled) {
|
if (!result.isCancelled) {
|
||||||
setResult(result);
|
setResult(std::move(result));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
@@ -372,9 +372,9 @@ void DictionaryWordSelectActivity::loop() {
|
|||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, stem, stemDef, fontId, orientation,
|
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, stem, stemDef, fontId, orientation,
|
||||||
true),
|
true),
|
||||||
[this](const ActivityResult& result) {
|
[this](ActivityResult result) {
|
||||||
if (!result.isCancelled) {
|
if (!result.isCancelled) {
|
||||||
setResult(result);
|
setResult(std::move(result));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
@@ -390,9 +390,9 @@ void DictionaryWordSelectActivity::loop() {
|
|||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<DictionarySuggestionsActivity>(renderer, mappedInput, cleaned, similar, fontId, orientation,
|
std::make_unique<DictionarySuggestionsActivity>(renderer, mappedInput, cleaned, similar, fontId, orientation,
|
||||||
cachePath),
|
cachePath),
|
||||||
[this](const ActivityResult& result) {
|
[this](ActivityResult result) {
|
||||||
if (!result.isCancelled) {
|
if (!result.isCancelled) {
|
||||||
setResult(result);
|
setResult(std::move(result));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ bool EpubReaderActivity::silentIndexNextChapterIfNeeded() {
|
|||||||
marginTop += SETTINGS.screenMargin;
|
marginTop += SETTINGS.screenMargin;
|
||||||
marginLeft += SETTINGS.screenMargin;
|
marginLeft += SETTINGS.screenMargin;
|
||||||
marginRight += SETTINGS.screenMargin;
|
marginRight += SETTINGS.screenMargin;
|
||||||
marginBottom += std::max(SETTINGS.screenMargin, UITheme::getInstance().getStatusBarHeight());
|
marginBottom += std::max(static_cast<int>(SETTINGS.screenMargin), UITheme::getInstance().getStatusBarHeight());
|
||||||
const uint16_t vpWidth = renderer.getScreenWidth() - marginLeft - marginRight;
|
const uint16_t vpWidth = renderer.getScreenWidth() - marginLeft - marginRight;
|
||||||
const uint16_t vpHeight = renderer.getScreenHeight() - marginTop - marginBottom;
|
const uint16_t vpHeight = renderer.getScreenHeight() - marginTop - marginBottom;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "DictionaryDefinitionActivity.h"
|
#include "DictionaryDefinitionActivity.h"
|
||||||
#include "DictionarySuggestionsActivity.h"
|
#include "DictionarySuggestionsActivity.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
@@ -143,9 +143,9 @@ void LookedUpWordsActivity::loop() {
|
|||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, headword, definition, readerFontId,
|
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, headword, definition, readerFontId,
|
||||||
orientation, true),
|
orientation, true),
|
||||||
[this](const ActivityResult& result) {
|
[this](ActivityResult result) {
|
||||||
if (!result.isCancelled) {
|
if (!result.isCancelled) {
|
||||||
setResult(result);
|
setResult(std::move(result));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
@@ -162,9 +162,9 @@ void LookedUpWordsActivity::loop() {
|
|||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, stem, stemDef, readerFontId,
|
std::make_unique<DictionaryDefinitionActivity>(renderer, mappedInput, stem, stemDef, readerFontId,
|
||||||
orientation, true),
|
orientation, true),
|
||||||
[this](const ActivityResult& result) {
|
[this](ActivityResult result) {
|
||||||
if (!result.isCancelled) {
|
if (!result.isCancelled) {
|
||||||
setResult(result);
|
setResult(std::move(result));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
@@ -180,9 +180,9 @@ void LookedUpWordsActivity::loop() {
|
|||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<DictionarySuggestionsActivity>(renderer, mappedInput, headword, similar, readerFontId,
|
std::make_unique<DictionarySuggestionsActivity>(renderer, mappedInput, headword, similar, readerFontId,
|
||||||
orientation, cachePath),
|
orientation, cachePath),
|
||||||
[this](const ActivityResult& result) {
|
[this](ActivityResult result) {
|
||||||
if (!result.isCancelled) {
|
if (!result.isCancelled) {
|
||||||
setResult(result);
|
setResult(std::move(result));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "activities/network/WifiSelectionActivity.h"
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "OpdsServerStore.h"
|
#include "OpdsServerStore.h"
|
||||||
#include "OpdsSettingsActivity.h"
|
#include "OpdsSettingsActivity.h"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "OpdsServerStore.h"
|
#include "OpdsServerStore.h"
|
||||||
#include "activities/util/DirectoryPickerActivity.h"
|
#include "activities/util/DirectoryPickerActivity.h"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "ActivityResult.h"
|
#include "activities/ActivityResult.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <HalStorage.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class FsFile;
|
|
||||||
|
|
||||||
class Dictionary {
|
class Dictionary {
|
||||||
public:
|
public:
|
||||||
static bool exists();
|
static bool exists();
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
#include <Utf8.h>
|
#include <Utf8.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
|
|
||||||
std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
||||||
@@ -43,4 +47,48 @@ std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
|||||||
return result.empty() ? "book" : result;
|
return result.empty() ? "book" : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkFileExtension(const std::string& fileName, const char* extension) {
|
||||||
|
const size_t extLen = strlen(extension);
|
||||||
|
if (fileName.length() < extLen) return false;
|
||||||
|
const std::string tail = fileName.substr(fileName.length() - extLen);
|
||||||
|
for (size_t i = 0; i < extLen; i++) {
|
||||||
|
if (tolower(static_cast<unsigned char>(tail[i])) != tolower(static_cast<unsigned char>(extension[i]))) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sortFileList(std::vector<std::string>& entries) {
|
||||||
|
std::sort(entries.begin(), entries.end(), [](const std::string& a, const std::string& b) {
|
||||||
|
bool isDir1 = !a.empty() && a.back() == '/';
|
||||||
|
bool isDir2 = !b.empty() && b.back() == '/';
|
||||||
|
if (isDir1 != isDir2) return isDir1;
|
||||||
|
|
||||||
|
const char* s1 = a.c_str();
|
||||||
|
const char* s2 = b.c_str();
|
||||||
|
|
||||||
|
while (*s1 && *s2) {
|
||||||
|
if (isdigit(*s1) && isdigit(*s2)) {
|
||||||
|
while (*s1 == '0') s1++;
|
||||||
|
while (*s2 == '0') s2++;
|
||||||
|
int len1 = 0, len2 = 0;
|
||||||
|
while (isdigit(s1[len1])) len1++;
|
||||||
|
while (isdigit(s2[len2])) len2++;
|
||||||
|
if (len1 != len2) return len1 < len2;
|
||||||
|
for (int i = 0; i < len1; i++) {
|
||||||
|
if (s1[i] != s2[i]) return s1[i] < s2[i];
|
||||||
|
}
|
||||||
|
s1 += len1;
|
||||||
|
s2 += len2;
|
||||||
|
} else {
|
||||||
|
char c1 = tolower(*s1);
|
||||||
|
char c2 = tolower(*s2);
|
||||||
|
if (c1 != c2) return c1 < c2;
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *s1 == 0 && *s2 != 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
|
|
||||||
@@ -11,4 +12,15 @@ namespace StringUtils {
|
|||||||
*/
|
*/
|
||||||
std::string sanitizeFilename(const std::string& name, size_t maxBytes = 100);
|
std::string sanitizeFilename(const std::string& name, size_t maxBytes = 100);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case-insensitive file extension check.
|
||||||
|
*/
|
||||||
|
bool checkFileExtension(const std::string& fileName, const char* extension);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort a file/directory list with directories first, using case-insensitive natural sort.
|
||||||
|
* Directory entries are identified by a trailing '/'.
|
||||||
|
*/
|
||||||
|
void sortFileList(std::vector<std::string>& entries);
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
|||||||
Reference in New Issue
Block a user