Change touch behavior
This commit is contained in:
parent
918a1941d0
commit
cee71cc445
18
README.md
18
README.md
|
|
@ -215,29 +215,39 @@ If the board is not detected or an upload repeatedly fails, force download mode:
|
||||||
|
|
||||||
| Control | Reading view | Menu or picker |
|
| Control | Reading view | Menu or picker |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| BOOT / GPIO0 | Open menu | Close/cancel without saving |
|
| BOOT / GPIO0 | Play or pause | Select / confirm |
|
||||||
| Touch edge Home button | Play or pause | Open highlighted option / apply |
|
| Touchscreen | Open menu | No action |
|
||||||
| Upper-right / GPIO12 | Previous paragraph or section | Move selection backward |
|
| Upper-right / GPIO12 | Previous paragraph or section | Move selection backward |
|
||||||
| Lower-right / GPIO16 | Next paragraph or section | Move selection forward |
|
| Lower-right / GPIO16 | Next paragraph or section | Move selection forward |
|
||||||
|
|
||||||
|
The touch-edge Home button has no action.
|
||||||
|
|
||||||
Do not hold BOOT while pressing RESET during normal use; that combination is
|
Do not hold BOOT while pressing RESET during normal use; that combination is
|
||||||
the manual download mode used for recovery above.
|
the manual download mode used for recovery above.
|
||||||
|
|
||||||
|
The menu opens with **Back** selected. BOOT on Back returns to reading. Pickers
|
||||||
|
start at the current setting and move forward when confirmed. The Font option
|
||||||
|
selects style first, then size; the size preview displays the word “size” in
|
||||||
|
the selected font and size. The selected font style also applies to the status,
|
||||||
|
footer, menus, and picker labels.
|
||||||
|
|
||||||
The default is 300 WPM. While paused, the backlight dims after 20 seconds,
|
The default is 300 WPM. While paused, the backlight dims after 20 seconds,
|
||||||
switches off after one minute, and the ESP32 deep-sleeps after ten minutes.
|
switches off after one minute, and the ESP32 deep-sleeps after ten minutes.
|
||||||
GPIO12 wakes it.
|
GPIO12 wakes it.
|
||||||
|
The footer shows the chapter title, its fixed total word count, and reading
|
||||||
|
progress as a rounded percentage.
|
||||||
|
|
||||||
## Resume and power behavior
|
## Resume and power behavior
|
||||||
|
|
||||||
The reader saves the selected section, paragraph-level position, WPM,
|
The reader saves the selected section, paragraph-level position, WPM,
|
||||||
brightness, and font settings in the ESP32's nonvolatile storage. On the next
|
brightness, and font settings in the ESP32's nonvolatile storage. On the next
|
||||||
boot or wake it opens the same section's title card and remains paused;
|
boot or wake it opens the same section's title card and remains paused;
|
||||||
pressing the touch-edge Home button begins at the saved paragraph. It opens the
|
pressing BOOT begins at the saved paragraph. It opens the
|
||||||
saved section directly, then falls back to the first TOC-listed section if the
|
saved section directly, then falls back to the first TOC-listed section if the
|
||||||
card has changed.
|
card has changed.
|
||||||
|
|
||||||
When continuous reading reaches the end of a section, it opens the next
|
When continuous reading reaches the end of a section, it opens the next
|
||||||
section's title card and pauses. Press the touch-edge Home button to begin that
|
section's title card and pauses. Press BOOT to begin that
|
||||||
section.
|
section.
|
||||||
|
|
||||||
The physical power switch only disconnects the battery. USB power is sufficient
|
The physical power switch only disconnects the battery. USB power is sufficient
|
||||||
|
|
|
||||||
5
TODO.md
5
TODO.md
|
|
@ -12,6 +12,11 @@ Add selectable reading profiles for punctuation and paragraph pause lengths,
|
||||||
long-word delay, unfamiliar-word timing, and WPM presets. Keep a plain/default
|
long-word delay, unfamiliar-word timing, and WPM presets. Keep a plain/default
|
||||||
profile that preserves the current cadence.
|
profile that preserves the current cadence.
|
||||||
|
|
||||||
|
Replace fixed 2×/3× sidecar word-delay values with categorical timing tags.
|
||||||
|
Map each category to a configurable delay in reader settings, so changing a
|
||||||
|
delay does not require regenerating sidecars. Version the format change and
|
||||||
|
keep existing v1/v2 sidecars readable.
|
||||||
|
|
||||||
## Dialogue and character colors
|
## Dialogue and character colors
|
||||||
|
|
||||||
Add optional color rendering for dialogue and character names. The converter
|
Add optional color rendering for dialogue and character names. The converter
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ class Board {
|
||||||
bool begin();
|
bool begin();
|
||||||
bool beginSd();
|
bool beginSd();
|
||||||
void setReadingContext(const String& bookTitle, const String& chapterTitle,
|
void setReadingContext(const String& bookTitle, const String& chapterTitle,
|
||||||
uint8_t progressPercent);
|
uint32_t wordCount, uint8_t progressPercent);
|
||||||
void drawWord(const String& word, uint16_t wpm, uint8_t styleFlags = 0);
|
void drawWord(const String& word, uint16_t wpm, uint8_t styleFlags = 0);
|
||||||
void drawChapterTitle(const String& chapter, uint16_t wpm);
|
void drawChapterTitle(const String& chapter, uint16_t wpm);
|
||||||
void drawTheEnd(uint16_t wpm);
|
void drawTheEnd(uint16_t wpm);
|
||||||
|
|
@ -35,6 +35,7 @@ class Board {
|
||||||
int batteryPercent() const;
|
int batteryPercent() const;
|
||||||
void setLandscape();
|
void setLandscape();
|
||||||
void setReaderFont(uint8_t style, uint8_t size, uint8_t styleFlags = 0);
|
void setReaderFont(uint8_t style, uint8_t size, uint8_t styleFlags = 0);
|
||||||
|
void setUiFont(uint8_t style);
|
||||||
void drawStatus(uint16_t wpm);
|
void drawStatus(uint16_t wpm);
|
||||||
void drawFooter();
|
void drawFooter();
|
||||||
String ellipsize(const String& text, int16_t maximumWidth) const;
|
String ellipsize(const String& text, int16_t maximumWidth) const;
|
||||||
|
|
@ -54,6 +55,8 @@ class Board {
|
||||||
String bookTitle_;
|
String bookTitle_;
|
||||||
String chapterTitle_;
|
String chapterTitle_;
|
||||||
String displayedChapterTitle_;
|
String displayedChapterTitle_;
|
||||||
|
uint32_t wordCount_ = 0;
|
||||||
|
uint32_t displayedWordCount_ = 0;
|
||||||
uint8_t progressPercent_ = 0;
|
uint8_t progressPercent_ = 0;
|
||||||
DrawBounds previousWordBounds_;
|
DrawBounds previousWordBounds_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class Reader {
|
||||||
bool nextParagraph();
|
bool nextParagraph();
|
||||||
bool lastParagraph();
|
bool lastParagraph();
|
||||||
uint32_t currentIndex() const;
|
uint32_t currentIndex() const;
|
||||||
|
uint32_t wordCount() const;
|
||||||
uint8_t progressPercent() const;
|
uint8_t progressPercent() const;
|
||||||
uint32_t delayMs(uint16_t wpm) const;
|
uint32_t delayMs(uint16_t wpm) const;
|
||||||
uint8_t currentStyleFlags() const;
|
uint8_t currentStyleFlags() const;
|
||||||
|
|
|
||||||
120
src/Board.cpp
120
src/Board.cpp
|
|
@ -51,11 +51,6 @@ const GFXfont* const kReaderFonts[3][4][4] = {
|
||||||
&FreeMonoBoldOblique24pt7b},
|
&FreeMonoBoldOblique24pt7b},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
String fontSizeLabel(uint8_t size) {
|
|
||||||
const uint8_t whole = size / 10;
|
|
||||||
return size % 10 == 0 ? String(whole) : String(whole) + ".5";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Board::begin() {
|
bool Board::begin() {
|
||||||
|
|
@ -75,7 +70,7 @@ bool Board::begin() {
|
||||||
// manager on the shared I2C bus.
|
// manager on the shared I2C bus.
|
||||||
powerMonitorAvailable_ = powerMonitor.init(
|
powerMonitorAvailable_ = powerMonitor.init(
|
||||||
Wire, config::kI2cSda, config::kI2cScl, SY6970_SLAVE_ADDRESS);
|
Wire, config::kI2cSda, config::kI2cScl, SY6970_SLAVE_ADDRESS);
|
||||||
if (powerMonitorAvailable_) powerMonitor.enableADCMeasure();
|
if (powerMonitorAvailable_) powerMonitor.enableMeasure();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +99,7 @@ int Board::batteryPercent() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::setReadingContext(const String& bookTitle, const String& chapterTitle,
|
void Board::setReadingContext(const String& bookTitle, const String& chapterTitle,
|
||||||
uint8_t progressPercent) {
|
uint32_t wordCount, uint8_t progressPercent) {
|
||||||
if (bookTitle_ != bookTitle) {
|
if (bookTitle_ != bookTitle) {
|
||||||
bookTitle_ = bookTitle;
|
bookTitle_ = bookTitle;
|
||||||
statusDrawn_ = false;
|
statusDrawn_ = false;
|
||||||
|
|
@ -116,6 +111,7 @@ void Board::setReadingContext(const String& bookTitle, const String& chapterTitl
|
||||||
chapterTitle_ = chapterTitle;
|
chapterTitle_ = chapterTitle;
|
||||||
footerDrawn_ = false;
|
footerDrawn_ = false;
|
||||||
}
|
}
|
||||||
|
wordCount_ = wordCount;
|
||||||
progressPercent_ = progressPercent;
|
progressPercent_ = progressPercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,25 +161,28 @@ void Board::setReaderFont(uint8_t style, uint8_t size, uint8_t styleFlags) {
|
||||||
display_->setTextSize(scale);
|
display_->setTextSize(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Board::setUiFont(uint8_t style) {
|
||||||
|
setReaderFont(style, config::kMinimumFontSize);
|
||||||
|
}
|
||||||
|
|
||||||
void Board::drawStatus(uint16_t wpm) {
|
void Board::drawStatus(uint16_t wpm) {
|
||||||
constexpr int16_t kMargin = 8;
|
constexpr int16_t kMargin = 8;
|
||||||
constexpr int16_t kBatteryWidth = 28;
|
constexpr int16_t kBatteryWidth = 28;
|
||||||
constexpr int16_t kBatteryHeight = 12;
|
constexpr int16_t kBatteryHeight = 12;
|
||||||
constexpr int16_t kBatteryTipWidth = 3;
|
constexpr int16_t kBatteryTipWidth = 3;
|
||||||
|
|
||||||
display_->setTextFont(2);
|
setUiFont(fontStyle_);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextColor(TFT_WHITE);
|
display_->setTextColor(TFT_WHITE);
|
||||||
display_->setTextDatum(TL_DATUM);
|
display_->setTextDatum(ML_DATUM);
|
||||||
const String wpmLabel = String(wpm) + " WPM";
|
const String wpmLabel = String(wpm) + " WPM";
|
||||||
display_->drawString(wpmLabel, kMargin, kMargin);
|
display_->drawString(wpmLabel, kMargin, kStatusHeight / 2);
|
||||||
|
|
||||||
const int16_t batteryX = display_->width() - kMargin - kBatteryTipWidth - kBatteryWidth;
|
const int16_t batteryX = display_->width() - kMargin - kBatteryTipWidth - kBatteryWidth;
|
||||||
const int16_t batteryY = kMargin + 2;
|
const int16_t batteryY = kMargin + 2;
|
||||||
const int percentage = batteryPercent();
|
const int percentage = batteryPercent();
|
||||||
const String label = percentage < 0 ? "--" : String(percentage) + "%";
|
const String label = percentage < 0 ? "--" : String(percentage) + "%";
|
||||||
display_->setTextDatum(TR_DATUM);
|
display_->setTextDatum(MR_DATUM);
|
||||||
display_->drawString(label, batteryX - 5, kMargin);
|
display_->drawString(label, batteryX - 5, kStatusHeight / 2);
|
||||||
display_->drawRoundRect(batteryX, batteryY, kBatteryWidth, kBatteryHeight, 2, TFT_WHITE);
|
display_->drawRoundRect(batteryX, batteryY, kBatteryWidth, kBatteryHeight, 2, TFT_WHITE);
|
||||||
display_->fillRect(batteryX + kBatteryWidth, batteryY + 4, kBatteryTipWidth, 4, TFT_WHITE);
|
display_->fillRect(batteryX + kBatteryWidth, batteryY + 4, kBatteryTipWidth, 4, TFT_WHITE);
|
||||||
if (percentage > 0) {
|
if (percentage > 0) {
|
||||||
|
|
@ -197,8 +196,9 @@ void Board::drawStatus(uint16_t wpm) {
|
||||||
const int16_t maximumWidth =
|
const int16_t maximumWidth =
|
||||||
2 * min(centerX - left, right - centerX);
|
2 * min(centerX - left, right - centerX);
|
||||||
if (maximumWidth > 0 && !bookTitle_.isEmpty()) {
|
if (maximumWidth > 0 && !bookTitle_.isEmpty()) {
|
||||||
display_->setTextDatum(TC_DATUM);
|
display_->setTextDatum(MC_DATUM);
|
||||||
display_->drawString(ellipsize(bookTitle_, maximumWidth), centerX, kMargin);
|
display_->drawString(ellipsize(bookTitle_, maximumWidth), centerX,
|
||||||
|
kStatusHeight / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,25 +216,28 @@ String Board::ellipsize(const String& text, int16_t maximumWidth) const {
|
||||||
|
|
||||||
void Board::drawFooter() {
|
void Board::drawFooter() {
|
||||||
if (footerDrawn_ && displayedChapterTitle_ == chapterTitle_ &&
|
if (footerDrawn_ && displayedChapterTitle_ == chapterTitle_ &&
|
||||||
|
displayedWordCount_ == wordCount_ &&
|
||||||
displayedProgressPercent_ == progressPercent_) {
|
displayedProgressPercent_ == progressPercent_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
display_->fillRect(0, display_->height() - kFooterHeight, display_->width(),
|
display_->fillRect(0, display_->height() - kFooterHeight, display_->width(),
|
||||||
kFooterHeight, TFT_BLACK);
|
kFooterHeight, TFT_BLACK);
|
||||||
display_->setTextFont(2);
|
setUiFont(fontStyle_);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextColor(TFT_WHITE);
|
display_->setTextColor(TFT_WHITE);
|
||||||
const String progress = String(progressPercent_) + "%";
|
const String details = String(wordCount_) + " words - " +
|
||||||
|
String(progressPercent_) + "%";
|
||||||
|
const String separator = " - ";
|
||||||
const int16_t maximumChapterWidth =
|
const int16_t maximumChapterWidth =
|
||||||
display_->width() - display_->textWidth(progress) - 16;
|
display_->width() - display_->textWidth(separator + details) - 16;
|
||||||
const String chapter = ellipsize(chapterTitle_, maximumChapterWidth);
|
const String chapter = ellipsize(chapterTitle_, maximumChapterWidth);
|
||||||
const String label = chapter.isEmpty() ? progress : chapter + " " + progress;
|
const String label = chapter.isEmpty() ? details : chapter + separator + details;
|
||||||
display_->setTextDatum(TC_DATUM);
|
display_->setTextDatum(MC_DATUM);
|
||||||
display_->drawString(label, display_->width() / 2,
|
display_->drawString(label, display_->width() / 2,
|
||||||
display_->height() - kFooterHeight + 5);
|
display_->height() - kFooterHeight / 2);
|
||||||
footerDrawn_ = true;
|
footerDrawn_ = true;
|
||||||
displayedChapterTitle_ = chapterTitle_;
|
displayedChapterTitle_ = chapterTitle_;
|
||||||
|
displayedWordCount_ = wordCount_;
|
||||||
displayedProgressPercent_ = progressPercent_;
|
displayedProgressPercent_ = progressPercent_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,8 +320,7 @@ void Board::drawMenu(uint8_t selectedOption) {
|
||||||
footerDrawn_ = false;
|
footerDrawn_ = false;
|
||||||
displayedProgressPercent_ = 255;
|
displayedProgressPercent_ = 255;
|
||||||
previousWordBounds_.valid = false;
|
previousWordBounds_.valid = false;
|
||||||
display_->setTextFont(2);
|
setUiFont(fontStyle_);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextColor(TFT_WHITE);
|
display_->setTextColor(TFT_WHITE);
|
||||||
display_->setTextDatum(TC_DATUM);
|
display_->setTextDatum(TC_DATUM);
|
||||||
display_->drawString("MENU", display_->width() / 2, 18);
|
display_->drawString("MENU", display_->width() / 2, 18);
|
||||||
|
|
@ -326,8 +328,8 @@ void Board::drawMenu(uint8_t selectedOption) {
|
||||||
constexpr int16_t kRowHeight = 58;
|
constexpr int16_t kRowHeight = 58;
|
||||||
constexpr int16_t kFirstRowY = 60;
|
constexpr int16_t kFirstRowY = 60;
|
||||||
constexpr int16_t kRowSpacing = 68;
|
constexpr int16_t kRowSpacing = 68;
|
||||||
const char* labels[] = {"Book selection", "Chapter selection", "Brightness",
|
const char* labels[] = {"Back", "Book selection", "Chapter selection",
|
||||||
"Font style", "Font size"};
|
"Brightness", "Font"};
|
||||||
display_->setTextDatum(ML_DATUM);
|
display_->setTextDatum(ML_DATUM);
|
||||||
for (uint8_t index = 0; index < 5; ++index) {
|
for (uint8_t index = 0; index < 5; ++index) {
|
||||||
const int16_t y = kFirstRowY + index * kRowSpacing;
|
const int16_t y = kFirstRowY + index * kRowSpacing;
|
||||||
|
|
@ -341,13 +343,11 @@ void Board::drawMenu(uint8_t selectedOption) {
|
||||||
display_->drawString(labels[index], 24, y + kRowHeight / 2);
|
display_->drawString(labels[index], 24, y + kRowHeight / 2);
|
||||||
}
|
}
|
||||||
display_->setTextColor(TFT_WHITE);
|
display_->setTextColor(TFT_WHITE);
|
||||||
display_->setTextFont(1);
|
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextDatum(BC_DATUM);
|
display_->setTextDatum(BC_DATUM);
|
||||||
display_->drawString("BACK / FORWARD to choose", display_->width() / 2,
|
display_->drawString("PREV/NEXT choose", display_->width() / 2,
|
||||||
display_->height() - 30);
|
display_->height() - 36);
|
||||||
display_->drawString("HOME open / BOOT close", display_->width() / 2,
|
display_->drawString("BOOT select", display_->width() / 2,
|
||||||
display_->height() - 12);
|
display_->height() - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::drawPicker(const String& title, const String& value) {
|
void Board::drawPicker(const String& title, const String& value) {
|
||||||
|
|
@ -357,27 +357,26 @@ void Board::drawPicker(const String& title, const String& value) {
|
||||||
display_->fillScreen(TFT_BLACK);
|
display_->fillScreen(TFT_BLACK);
|
||||||
statusDrawn_ = false;
|
statusDrawn_ = false;
|
||||||
previousWordBounds_.valid = false;
|
previousWordBounds_.valid = false;
|
||||||
display_->setTextFont(2);
|
setUiFont(fontStyle_);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextColor(TFT_WHITE);
|
display_->setTextColor(TFT_WHITE);
|
||||||
display_->setTextDatum(TC_DATUM);
|
display_->setTextDatum(TC_DATUM);
|
||||||
display_->drawString(title, display_->width() / 2, 16);
|
display_->drawString(title, display_->width() / 2, 16);
|
||||||
|
|
||||||
display_->setTextFont(1);
|
uint8_t scale = 3;
|
||||||
const size_t glyphs = value.length() == 0 ? 1 : value.length();
|
|
||||||
uint8_t scale = display_->width() / (glyphs * 7);
|
|
||||||
if (scale < 2) scale = 2;
|
|
||||||
if (scale > 6) scale = 6;
|
|
||||||
display_->setTextSize(scale);
|
display_->setTextSize(scale);
|
||||||
|
while (scale > 1 && display_->textWidth(value) > display_->width() - 16) {
|
||||||
|
display_->setTextSize(--scale);
|
||||||
|
}
|
||||||
display_->setTextDatum(MC_DATUM);
|
display_->setTextDatum(MC_DATUM);
|
||||||
display_->drawString(value, display_->width() / 2, display_->height() / 2 - 8);
|
display_->drawString(ellipsize(value, display_->width() - 16),
|
||||||
|
display_->width() / 2, display_->height() / 2 - 8);
|
||||||
|
|
||||||
display_->setTextSize(1);
|
setUiFont(fontStyle_);
|
||||||
display_->setTextDatum(BC_DATUM);
|
display_->setTextDatum(BC_DATUM);
|
||||||
display_->drawString("BACK / FORWARD to choose", display_->width() / 2,
|
display_->drawString("PREV/NEXT choose", display_->width() / 2,
|
||||||
display_->height() - 30);
|
display_->height() - 36);
|
||||||
display_->drawString("HOME apply / BOOT cancel", display_->width() / 2,
|
display_->drawString("BOOT apply", display_->width() / 2,
|
||||||
display_->height() - 12);
|
display_->height() - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::drawFontStylePicker(uint8_t style, uint8_t size) {
|
void Board::drawFontStylePicker(uint8_t style, uint8_t size) {
|
||||||
|
|
@ -387,8 +386,7 @@ void Board::drawFontStylePicker(uint8_t style, uint8_t size) {
|
||||||
display_->fillScreen(TFT_BLACK);
|
display_->fillScreen(TFT_BLACK);
|
||||||
statusDrawn_ = false;
|
statusDrawn_ = false;
|
||||||
previousWordBounds_.valid = false;
|
previousWordBounds_.valid = false;
|
||||||
display_->setTextFont(2);
|
setUiFont(style);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextColor(TFT_WHITE);
|
display_->setTextColor(TFT_WHITE);
|
||||||
display_->setTextDatum(TC_DATUM);
|
display_->setTextDatum(TC_DATUM);
|
||||||
display_->drawString("FONT STYLE", display_->width() / 2, 16);
|
display_->drawString("FONT STYLE", display_->width() / 2, 16);
|
||||||
|
|
@ -398,16 +396,14 @@ void Board::drawFontStylePicker(uint8_t style, uint8_t size) {
|
||||||
display_->setTextDatum(MC_DATUM);
|
display_->setTextDatum(MC_DATUM);
|
||||||
display_->drawString("Sample", display_->width() / 2, display_->height() / 2 - 8);
|
display_->drawString("Sample", display_->width() / 2, display_->height() / 2 - 8);
|
||||||
|
|
||||||
display_->setTextFont(2);
|
setUiFont(style);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextDatum(TC_DATUM);
|
display_->setTextDatum(TC_DATUM);
|
||||||
display_->drawString(names[constrain(style, 0, 2)], display_->width() / 2, 48);
|
display_->drawString(names[constrain(style, 0, 2)], display_->width() / 2, 48);
|
||||||
display_->setTextFont(1);
|
|
||||||
display_->setTextDatum(BC_DATUM);
|
display_->setTextDatum(BC_DATUM);
|
||||||
display_->drawString("BACK / FORWARD to choose", display_->width() / 2,
|
display_->drawString("PREV/NEXT choose", display_->width() / 2,
|
||||||
display_->height() - 30);
|
display_->height() - 36);
|
||||||
display_->drawString("HOME apply / BOOT cancel", display_->width() / 2,
|
display_->drawString("BOOT next", display_->width() / 2,
|
||||||
display_->height() - 12);
|
display_->height() - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::drawFontSizePicker(uint8_t style, uint8_t size) {
|
void Board::drawFontSizePicker(uint8_t style, uint8_t size) {
|
||||||
|
|
@ -417,24 +413,22 @@ void Board::drawFontSizePicker(uint8_t style, uint8_t size) {
|
||||||
display_->fillScreen(TFT_BLACK);
|
display_->fillScreen(TFT_BLACK);
|
||||||
statusDrawn_ = false;
|
statusDrawn_ = false;
|
||||||
previousWordBounds_.valid = false;
|
previousWordBounds_.valid = false;
|
||||||
display_->setTextFont(2);
|
setUiFont(style);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextColor(TFT_WHITE);
|
display_->setTextColor(TFT_WHITE);
|
||||||
display_->setTextDatum(TC_DATUM);
|
display_->setTextDatum(TC_DATUM);
|
||||||
display_->drawString("FONT SIZE", display_->width() / 2, 16);
|
display_->drawString("FONT SIZE", display_->width() / 2, 16);
|
||||||
|
|
||||||
setReaderFont(style, size);
|
setReaderFont(style, size);
|
||||||
display_->setTextDatum(MC_DATUM);
|
display_->setTextDatum(MC_DATUM);
|
||||||
display_->drawString(fontSizeLabel(size), display_->width() / 2,
|
display_->drawString("size", display_->width() / 2,
|
||||||
display_->height() / 2 - 8);
|
display_->height() / 2 - 8);
|
||||||
|
|
||||||
display_->setTextFont(1);
|
setUiFont(style);
|
||||||
display_->setTextSize(1);
|
|
||||||
display_->setTextDatum(BC_DATUM);
|
display_->setTextDatum(BC_DATUM);
|
||||||
display_->drawString("BACK / FORWARD to choose", display_->width() / 2,
|
display_->drawString("PREV/NEXT choose", display_->width() / 2,
|
||||||
display_->height() - 30);
|
display_->height() - 36);
|
||||||
display_->drawString("HOME apply / BOOT cancel", display_->width() / 2,
|
display_->drawString("BOOT apply", display_->width() / 2,
|
||||||
display_->height() - 12);
|
display_->height() - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::setBrightness(uint8_t level) {
|
void Board::setBrightness(uint8_t level) {
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,10 @@ bool Reader::lastParagraph() {
|
||||||
|
|
||||||
uint32_t Reader::currentIndex() const { return index_; }
|
uint32_t Reader::currentIndex() const { return index_; }
|
||||||
|
|
||||||
|
uint32_t Reader::wordCount() const {
|
||||||
|
return static_cast<uint32_t>(tokens_.size());
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t Reader::progressPercent() const {
|
uint8_t Reader::progressPercent() const {
|
||||||
if (tokens_.empty()) return 0;
|
if (tokens_.empty()) return 0;
|
||||||
if (tokens_.size() == 1) return 100;
|
if (tokens_.size() == 1) return 100;
|
||||||
|
|
|
||||||
56
src/main.cpp
56
src/main.cpp
|
|
@ -25,8 +25,7 @@ uint32_t lastInteractionMs = 0;
|
||||||
uint32_t nextWordMs = 0;
|
uint32_t nextWordMs = 0;
|
||||||
TouchDrvCSTXXX touch;
|
TouchDrvCSTXXX touch;
|
||||||
bool touchAvailable = false;
|
bool touchAvailable = false;
|
||||||
bool touchPlayPauseRequested = false;
|
bool screenWasTouched = false;
|
||||||
uint32_t lastTouchHomeMs = 0;
|
|
||||||
|
|
||||||
enum class Screen {
|
enum class Screen {
|
||||||
kReader,
|
kReader,
|
||||||
|
|
@ -61,7 +60,7 @@ struct Button {
|
||||||
uint32_t changedAt = 0;
|
uint32_t changedAt = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Button menu{config::kButtonPlayPause};
|
Button boot{config::kButtonPlayPause};
|
||||||
Button previous{config::kButtonPrevious};
|
Button previous{config::kButtonPrevious};
|
||||||
Button next{config::kButtonNext};
|
Button next{config::kButtonNext};
|
||||||
|
|
||||||
|
|
@ -92,7 +91,8 @@ void persistState() {
|
||||||
|
|
||||||
void setReadingContext(uint8_t progressPercent) {
|
void setReadingContext(uint8_t progressPercent) {
|
||||||
board.setReadingContext(library.bookName(state.bookPath),
|
board.setReadingContext(library.bookName(state.bookPath),
|
||||||
library.chapterName(state.bookPath), progressPercent);
|
library.chapterName(state.bookPath), reader.wordCount(),
|
||||||
|
progressPercent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showCurrentWord() {
|
void showCurrentWord() {
|
||||||
|
|
@ -143,12 +143,6 @@ void returnToReader() {
|
||||||
showReaderScreen();
|
showReaderScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeMenuWithoutSaving() {
|
|
||||||
// Brightness is the only picker that changes hardware before confirmation.
|
|
||||||
board.setPreferredBrightness(state.brightness);
|
|
||||||
returnToReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawPicker() {
|
void drawPicker() {
|
||||||
switch (screen) {
|
switch (screen) {
|
||||||
case Screen::kBookPicker:
|
case Screen::kBookPicker:
|
||||||
|
|
@ -222,11 +216,15 @@ void changePicker(int direction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyPicker() {
|
void applyPicker() {
|
||||||
|
if (screen == Screen::kFontStylePicker) {
|
||||||
|
screen = Screen::kFontSizePicker;
|
||||||
|
drawPicker();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (screen == Screen::kBrightnessPicker) {
|
if (screen == Screen::kBrightnessPicker) {
|
||||||
state.brightness = pendingBrightness;
|
state.brightness = pendingBrightness;
|
||||||
board.setPreferredBrightness(state.brightness);
|
board.setPreferredBrightness(state.brightness);
|
||||||
} else if (screen == Screen::kFontStylePicker ||
|
} else if (screen == Screen::kFontSizePicker) {
|
||||||
screen == Screen::kFontSizePicker) {
|
|
||||||
state.fontStyle = pendingFontStyle;
|
state.fontStyle = pendingFontStyle;
|
||||||
state.fontSize = pendingFontSize;
|
state.fontSize = pendingFontSize;
|
||||||
board.setFont(state.fontStyle, state.fontSize);
|
board.setFont(state.fontStyle, state.fontSize);
|
||||||
|
|
@ -322,13 +320,6 @@ void navigateNextPaused() {
|
||||||
if (positionChanged) persistState();
|
if (positionChanged) persistState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTouchHome(void*) {
|
|
||||||
constexpr uint32_t kTouchDebounceMs = 200;
|
|
||||||
if (millis() - lastTouchHomeMs < kTouchDebounceMs) return;
|
|
||||||
lastTouchHomeMs = millis();
|
|
||||||
touchPlayPauseRequested = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void confirmMenuSelection() {
|
void confirmMenuSelection() {
|
||||||
if (screen == Screen::kReader) {
|
if (screen == Screen::kReader) {
|
||||||
togglePlayback();
|
togglePlayback();
|
||||||
|
|
@ -336,11 +327,11 @@ void confirmMenuSelection() {
|
||||||
}
|
}
|
||||||
if (screen == Screen::kMenu) {
|
if (screen == Screen::kMenu) {
|
||||||
switch (menuSelection) {
|
switch (menuSelection) {
|
||||||
case 0: openPicker(Screen::kBookPicker); break;
|
case 0: returnToReader(); break;
|
||||||
case 1: openPicker(Screen::kChapterPicker); break;
|
case 1: openPicker(Screen::kBookPicker); break;
|
||||||
case 2: openPicker(Screen::kBrightnessPicker); break;
|
case 2: openPicker(Screen::kChapterPicker); break;
|
||||||
case 3: openPicker(Screen::kFontStylePicker); break;
|
case 3: openPicker(Screen::kBrightnessPicker); break;
|
||||||
case 4: openPicker(Screen::kFontSizePicker); break;
|
case 4: openPicker(Screen::kFontStylePicker); break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +346,6 @@ void beginTouch() {
|
||||||
if (!touchAvailable) return;
|
if (!touchAvailable) return;
|
||||||
|
|
||||||
configureTouchLandscape();
|
configureTouchLandscape();
|
||||||
touch.setHomeButtonCallback(onTouchHome);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void configureTouchLandscape() {
|
void configureTouchLandscape() {
|
||||||
|
|
@ -368,11 +358,9 @@ void configureTouchLandscape() {
|
||||||
|
|
||||||
void handleTouch() {
|
void handleTouch() {
|
||||||
if (!touchAvailable) return;
|
if (!touchAvailable) return;
|
||||||
touch.getTouchPoints(); // Polling delivers home-button callbacks.
|
const bool screenTouched = touch.getTouchPoints().hasPoints();
|
||||||
if (touchPlayPauseRequested) {
|
if (screenTouched && !screenWasTouched && screen == Screen::kReader) openMenu();
|
||||||
touchPlayPauseRequested = false;
|
screenWasTouched = screenTouched;
|
||||||
confirmMenuSelection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wakeDisplay() {
|
void wakeDisplay() {
|
||||||
|
|
@ -394,12 +382,8 @@ void enterDeepSleep() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleInput() {
|
void handleInput() {
|
||||||
if (released(menu)) {
|
if (released(boot)) {
|
||||||
if (screen == Screen::kReader) {
|
confirmMenuSelection();
|
||||||
openMenu();
|
|
||||||
} else {
|
|
||||||
closeMenuWithoutSaving();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue