mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-07 08:07:40 +03:00
Merge branch 'master' into feature/track-reading-time
This commit is contained in:
commit
f1e8af72d5
@ -31,6 +31,16 @@ void Page::render(GfxRenderer& renderer, const int fontId, const int xOffset, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Page::wordCount() const {
|
||||||
|
size_t count = 0;
|
||||||
|
for (const auto& element : elements) {
|
||||||
|
// Only PageLine is stored in elements; avoid RTTI to stay compatible with -fno-rtti
|
||||||
|
const auto* line = static_cast<PageLine*>(element.get());
|
||||||
|
count += line->wordCount();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
bool Page::serialize(FsFile& file) const {
|
bool Page::serialize(FsFile& file) const {
|
||||||
const uint16_t count = elements.size();
|
const uint16_t count = elements.size();
|
||||||
serialization::writePod(file, count);
|
serialization::writePod(file, count);
|
||||||
|
|||||||
@ -29,6 +29,7 @@ class PageLine final : public PageElement {
|
|||||||
PageLine(std::shared_ptr<TextBlock> block, const int16_t xPos, const int16_t yPos)
|
PageLine(std::shared_ptr<TextBlock> block, const int16_t xPos, const int16_t yPos)
|
||||||
: PageElement(xPos, yPos), block(std::move(block)) {}
|
: PageElement(xPos, yPos), block(std::move(block)) {}
|
||||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override;
|
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override;
|
||||||
|
size_t wordCount() const { return block ? block->wordCount() : 0; }
|
||||||
bool serialize(FsFile& file) override;
|
bool serialize(FsFile& file) override;
|
||||||
static std::unique_ptr<PageLine> deserialize(FsFile& file);
|
static std::unique_ptr<PageLine> deserialize(FsFile& file);
|
||||||
};
|
};
|
||||||
@ -38,6 +39,7 @@ class Page {
|
|||||||
// the list of block index and line numbers on this page
|
// the list of block index and line numbers on this page
|
||||||
std::vector<std::shared_ptr<PageElement>> elements;
|
std::vector<std::shared_ptr<PageElement>> elements;
|
||||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
||||||
|
size_t wordCount() const;
|
||||||
bool serialize(FsFile& file) const;
|
bool serialize(FsFile& file) const;
|
||||||
static std::unique_ptr<Page> deserialize(FsFile& file);
|
static std::unique_ptr<Page> deserialize(FsFile& file);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -233,3 +233,64 @@ std::unique_ptr<Page> Section::loadPageFromSectionFile() {
|
|||||||
file.close();
|
file.close();
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Page> Section::loadPageAt(const uint16_t pageIndex) const {
|
||||||
|
FsFile localFile;
|
||||||
|
if (!SdMan.openFileForRead("SCT", filePath, localFile)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
localFile.seek(HEADER_SIZE - sizeof(uint32_t));
|
||||||
|
uint32_t lutOffset;
|
||||||
|
serialization::readPod(localFile, lutOffset);
|
||||||
|
if (pageIndex >= pageCount) {
|
||||||
|
localFile.close();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
localFile.seek(lutOffset + sizeof(uint32_t) * pageIndex);
|
||||||
|
uint32_t pagePos;
|
||||||
|
serialization::readPod(localFile, pagePos);
|
||||||
|
localFile.seek(pagePos);
|
||||||
|
|
||||||
|
auto page = Page::deserialize(localFile);
|
||||||
|
localFile.close();
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Section::ensureWordCountsLoaded() const {
|
||||||
|
if (wordCountsLoaded) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageWordCounts.clear();
|
||||||
|
pageWordCounts.reserve(pageCount);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < pageCount; i++) {
|
||||||
|
auto page = loadPageAt(i);
|
||||||
|
if (!page) {
|
||||||
|
pageWordCounts.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pageWordCounts.push_back(static_cast<uint32_t>(page->wordCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
wordCountsLoaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Section::getWordsLeftFrom(const uint16_t pageIndex) const {
|
||||||
|
if (pageIndex >= pageCount) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ensureWordCountsLoaded()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t total = 0;
|
||||||
|
for (size_t i = pageIndex; i < pageWordCounts.size(); i++) {
|
||||||
|
total += pageWordCounts[i];
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "Epub.h"
|
#include "Epub.h"
|
||||||
|
|
||||||
@ -13,6 +14,8 @@ class Section {
|
|||||||
GfxRenderer& renderer;
|
GfxRenderer& renderer;
|
||||||
std::string filePath;
|
std::string filePath;
|
||||||
FsFile file;
|
FsFile file;
|
||||||
|
mutable std::vector<uint32_t> pageWordCounts;
|
||||||
|
mutable bool wordCountsLoaded = false;
|
||||||
|
|
||||||
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
|
||||||
uint16_t viewportWidth, uint16_t viewportHeight);
|
uint16_t viewportWidth, uint16_t viewportHeight);
|
||||||
@ -36,4 +39,7 @@ class Section {
|
|||||||
const std::function<void()>& progressSetupFn = nullptr,
|
const std::function<void()>& progressSetupFn = nullptr,
|
||||||
const std::function<void(int)>& progressFn = nullptr);
|
const std::function<void(int)>& progressFn = nullptr);
|
||||||
std::unique_ptr<Page> loadPageFromSectionFile();
|
std::unique_ptr<Page> loadPageFromSectionFile();
|
||||||
|
std::unique_ptr<Page> loadPageAt(uint16_t pageIndex) const;
|
||||||
|
bool ensureWordCountsLoaded() const;
|
||||||
|
uint32_t getWordsLeftFrom(uint16_t pageIndex) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -36,6 +36,7 @@ class TextBlock final : public Block {
|
|||||||
// given a renderer works out where to break the words into lines
|
// given a renderer works out where to break the words into lines
|
||||||
void render(const GfxRenderer& renderer, int fontId, int x, int y) const;
|
void render(const GfxRenderer& renderer, int fontId, int x, int y) const;
|
||||||
BlockType getType() override { return TEXT_BLOCK; }
|
BlockType getType() override { return TEXT_BLOCK; }
|
||||||
|
size_t wordCount() const { return words.size(); }
|
||||||
bool serialize(FsFile& file) const;
|
bool serialize(FsFile& file) const;
|
||||||
static std::unique_ptr<TextBlock> deserialize(FsFile& file);
|
static std::unique_ptr<TextBlock> deserialize(FsFile& file);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,9 +12,9 @@
|
|||||||
CrossPointSettings CrossPointSettings::instance;
|
CrossPointSettings CrossPointSettings::instance;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
constexpr uint8_t SETTINGS_FILE_VERSION = 2;
|
||||||
// Increment this when adding new persisted settings fields
|
// Increment this when adding new persisted settings fields
|
||||||
constexpr uint8_t SETTINGS_COUNT = 17;
|
constexpr uint8_t SETTINGS_COUNT = 19;
|
||||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -46,6 +46,8 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writePod(outputFile, sleepScreenCoverMode);
|
serialization::writePod(outputFile, sleepScreenCoverMode);
|
||||||
serialization::writeString(outputFile, std::string(opdsServerUrl));
|
serialization::writeString(outputFile, std::string(opdsServerUrl));
|
||||||
serialization::writePod(outputFile, textAntiAliasing);
|
serialization::writePod(outputFile, textAntiAliasing);
|
||||||
|
serialization::writePod(outputFile, readingSpeedWpm);
|
||||||
|
serialization::writePod(outputFile, showTimeLeftInChapter);
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||||
@ -60,7 +62,7 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
|
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
serialization::readPod(inputFile, version);
|
serialization::readPod(inputFile, version);
|
||||||
if (version != SETTINGS_FILE_VERSION) {
|
if (version != SETTINGS_FILE_VERSION && version != 1) {
|
||||||
Serial.printf("[%lu] [CPS] Deserialization failed: Unknown version %u\n", millis(), version);
|
Serial.printf("[%lu] [CPS] Deserialization failed: Unknown version %u\n", millis(), version);
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
return false;
|
return false;
|
||||||
@ -110,6 +112,15 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
}
|
}
|
||||||
serialization::readPod(inputFile, textAntiAliasing);
|
serialization::readPod(inputFile, textAntiAliasing);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
if (version == 1) {
|
||||||
|
uint8_t wpmV1;
|
||||||
|
serialization::readPod(inputFile, wpmV1);
|
||||||
|
readingSpeedWpm = wpmV1;
|
||||||
|
} else {
|
||||||
|
serialization::readPod(inputFile, readingSpeedWpm);
|
||||||
|
}
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, showTimeLeftInChapter);
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
|
|||||||
@ -61,6 +61,10 @@ class CrossPointSettings {
|
|||||||
// Text rendering settings
|
// Text rendering settings
|
||||||
uint8_t extraParagraphSpacing = 1;
|
uint8_t extraParagraphSpacing = 1;
|
||||||
uint8_t textAntiAliasing = 1;
|
uint8_t textAntiAliasing = 1;
|
||||||
|
// Reading speed (words per minute) for time-left estimate
|
||||||
|
uint16_t readingSpeedWpm = 200;
|
||||||
|
// Toggle to show time remaining in the current chapter
|
||||||
|
uint8_t showTimeLeftInChapter = 0;
|
||||||
// Duration of the power button press
|
// Duration of the power button press
|
||||||
uint8_t shortPwrBtn = 0;
|
uint8_t shortPwrBtn = 0;
|
||||||
// EPUB reading orientation settings
|
// EPUB reading orientation settings
|
||||||
|
|||||||
@ -572,10 +572,20 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
|
|||||||
if (showProgress) {
|
if (showProgress) {
|
||||||
// Calculate progress in book
|
// Calculate progress in book
|
||||||
const float sectionChapterProg = static_cast<float>(section->currentPage) / section->pageCount;
|
const float sectionChapterProg = static_cast<float>(section->currentPage) / section->pageCount;
|
||||||
|
std::string timeLeftText;
|
||||||
const uint8_t bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg);
|
const uint8_t bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg);
|
||||||
|
|
||||||
|
if (SETTINGS.showTimeLeftInChapter && SETTINGS.readingSpeedWpm > 0) {
|
||||||
|
const uint32_t wordsLeft = section->getWordsLeftFrom(section->currentPage);
|
||||||
|
if (wordsLeft > 0) {
|
||||||
|
const float minutesLeft =
|
||||||
|
static_cast<float>(wordsLeft) / static_cast<float>(std::max<uint8_t>(1, SETTINGS.readingSpeedWpm));
|
||||||
|
timeLeftText = formatMinutes(minutesLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Right aligned text for progress counter
|
// Right aligned text for progress counter
|
||||||
const std::string progress = std::to_string(section->currentPage + 1) + "/" + std::to_string(section->pageCount) +
|
const std::string progress = (timeLeftText.empty() ? std::string() : timeLeftText + " ") +
|
||||||
|
std::to_string(section->currentPage + 1) + "/" + std::to_string(section->pageCount) +
|
||||||
" " + std::to_string(bookProgress) + "%";
|
" " + std::to_string(bookProgress) + "%";
|
||||||
progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progress.c_str());
|
progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progress.c_str());
|
||||||
renderer.drawText(SMALL_FONT_ID, renderer.getScreenWidth() - orientedMarginRight - progressTextWidth, textY,
|
renderer.drawText(SMALL_FONT_ID, renderer.getScreenWidth() - orientedMarginRight - progressTextWidth, textY,
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
// Define the static settings list
|
// Define the static settings list
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int settingsCount = 18;
|
constexpr int settingsCount = 20;
|
||||||
const SettingInfo settingsList[settingsCount] = {
|
const SettingInfo settingsList[settingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
||||||
@ -35,6 +35,8 @@ const SettingInfo settingsList[settingsCount] = {
|
|||||||
SettingInfo::Value("Reader Screen Margin", &CrossPointSettings::screenMargin, {5, 40, 5}),
|
SettingInfo::Value("Reader Screen Margin", &CrossPointSettings::screenMargin, {5, 40, 5}),
|
||||||
SettingInfo::Enum("Reader Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
|
SettingInfo::Enum("Reader Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
|
||||||
{"Justify", "Left", "Center", "Right"}),
|
{"Justify", "Left", "Center", "Right"}),
|
||||||
|
SettingInfo::Value("Reading Speed (WPM)", &CrossPointSettings::readingSpeedWpm, {150, 300, 5}),
|
||||||
|
SettingInfo::Toggle("Show Time Left In Chapter", &CrossPointSettings::showTimeLeftInChapter),
|
||||||
SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout,
|
SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout,
|
||||||
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
|
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
|
||||||
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
||||||
@ -127,14 +129,21 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
||||||
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
|
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
|
||||||
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
||||||
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
} else if (setting.type == SettingType::VALUE && setting.valuePtr16 != nullptr) {
|
||||||
// Decreasing would also be nice for large ranges I think but oh well can't have everything
|
// Decreasing would also be nice for large ranges I think but oh well can't have everything
|
||||||
const int8_t currentValue = SETTINGS.*(setting.valuePtr);
|
const uint16_t currentValue = SETTINGS.*(setting.valuePtr16);
|
||||||
// Wrap to minValue if exceeding setting value boundary
|
// Wrap to minValue if exceeding setting value boundary
|
||||||
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
|
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
|
||||||
SETTINGS.*(setting.valuePtr) = setting.valueRange.min;
|
SETTINGS.*(setting.valuePtr16) = setting.valueRange.min;
|
||||||
} else {
|
} else {
|
||||||
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
|
SETTINGS.*(setting.valuePtr16) = currentValue + setting.valueRange.step;
|
||||||
|
}
|
||||||
|
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
||||||
|
const uint16_t currentValue = SETTINGS.*(setting.valuePtr);
|
||||||
|
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
|
||||||
|
SETTINGS.*(setting.valuePtr) = static_cast<uint8_t>(setting.valueRange.min);
|
||||||
|
} else {
|
||||||
|
SETTINGS.*(setting.valuePtr) = static_cast<uint8_t>(currentValue + setting.valueRange.step);
|
||||||
}
|
}
|
||||||
} else if (setting.type == SettingType::ACTION) {
|
} else if (setting.type == SettingType::ACTION) {
|
||||||
if (strcmp(setting.name, "Calibre Settings") == 0) {
|
if (strcmp(setting.name, "Calibre Settings") == 0) {
|
||||||
@ -202,6 +211,8 @@ void SettingsActivity::render() const {
|
|||||||
} else if (settingsList[i].type == SettingType::ENUM && settingsList[i].valuePtr != nullptr) {
|
} else if (settingsList[i].type == SettingType::ENUM && settingsList[i].valuePtr != nullptr) {
|
||||||
const uint8_t value = SETTINGS.*(settingsList[i].valuePtr);
|
const uint8_t value = SETTINGS.*(settingsList[i].valuePtr);
|
||||||
valueText = settingsList[i].enumValues[value];
|
valueText = settingsList[i].enumValues[value];
|
||||||
|
} else if (settingsList[i].type == SettingType::VALUE && settingsList[i].valuePtr16 != nullptr) {
|
||||||
|
valueText = std::to_string(SETTINGS.*(settingsList[i].valuePtr16));
|
||||||
} else if (settingsList[i].type == SettingType::VALUE && settingsList[i].valuePtr != nullptr) {
|
} else if (settingsList[i].type == SettingType::VALUE && settingsList[i].valuePtr != nullptr) {
|
||||||
valueText = std::to_string(SETTINGS.*(settingsList[i].valuePtr));
|
valueText = std::to_string(SETTINGS.*(settingsList[i].valuePtr));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,32 +15,37 @@ enum class SettingType { TOGGLE, ENUM, ACTION, VALUE };
|
|||||||
|
|
||||||
// Structure to hold setting information
|
// Structure to hold setting information
|
||||||
struct SettingInfo {
|
struct SettingInfo {
|
||||||
const char* name; // Display name of the setting
|
const char* name; // Display name of the setting
|
||||||
SettingType type; // Type of setting
|
SettingType type; // Type of setting
|
||||||
uint8_t CrossPointSettings::* valuePtr; // Pointer to member in CrossPointSettings (for TOGGLE/ENUM/VALUE)
|
uint8_t CrossPointSettings::* valuePtr; // Pointer for 8-bit settings (TOGGLE/ENUM)
|
||||||
|
uint16_t CrossPointSettings::* valuePtr16; // Pointer for 16-bit VALUE settings
|
||||||
std::vector<std::string> enumValues;
|
std::vector<std::string> enumValues;
|
||||||
|
|
||||||
struct ValueRange {
|
struct ValueRange {
|
||||||
uint8_t min;
|
uint16_t min;
|
||||||
uint8_t max;
|
uint16_t max;
|
||||||
uint8_t step;
|
uint16_t step;
|
||||||
};
|
};
|
||||||
// Bounds/step for VALUE type settings
|
// Bounds/step for VALUE type settings
|
||||||
ValueRange valueRange;
|
ValueRange valueRange;
|
||||||
|
|
||||||
// Static constructors
|
// Static constructors
|
||||||
static SettingInfo Toggle(const char* name, uint8_t CrossPointSettings::* ptr) {
|
static SettingInfo Toggle(const char* name, uint8_t CrossPointSettings::* ptr) {
|
||||||
return {name, SettingType::TOGGLE, ptr};
|
return {name, SettingType::TOGGLE, ptr, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
static SettingInfo Enum(const char* name, uint8_t CrossPointSettings::* ptr, std::vector<std::string> values) {
|
static SettingInfo Enum(const char* name, uint8_t CrossPointSettings::* ptr, std::vector<std::string> values) {
|
||||||
return {name, SettingType::ENUM, ptr, std::move(values)};
|
return {name, SettingType::ENUM, ptr, nullptr, std::move(values)};
|
||||||
}
|
}
|
||||||
|
|
||||||
static SettingInfo Action(const char* name) { return {name, SettingType::ACTION, nullptr}; }
|
static SettingInfo Action(const char* name) { return {name, SettingType::ACTION, nullptr, nullptr}; }
|
||||||
|
|
||||||
|
static SettingInfo Value(const char* name, uint16_t CrossPointSettings::* ptr, const ValueRange valueRange) {
|
||||||
|
return {name, SettingType::VALUE, nullptr, ptr, {}, valueRange};
|
||||||
|
}
|
||||||
|
|
||||||
static SettingInfo Value(const char* name, uint8_t CrossPointSettings::* ptr, const ValueRange valueRange) {
|
static SettingInfo Value(const char* name, uint8_t CrossPointSettings::* ptr, const ValueRange valueRange) {
|
||||||
return {name, SettingType::VALUE, ptr, {}, valueRange};
|
return {name, SettingType::VALUE, ptr, nullptr, {}, valueRange};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user