mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-06 15:47:39 +03:00
Compare commits
4 Commits
e4c18b18d8
...
5ee4747b49
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ee4747b49 | ||
|
|
7185e5d287 | ||
|
|
12940cc546 | ||
|
|
28152b3fee |
@ -203,7 +203,7 @@ bool Xtc::generateCoverBmp() const {
|
||||
coverBmp.write(reinterpret_cast<const uint8_t*>(&colorsImportant), 4);
|
||||
|
||||
// Color palette (2 colors for 1-bit)
|
||||
// XTC uses inverted polarity: 0 = black, 1 = white
|
||||
// XTC 1-bit polarity: 0 = black, 1 = white (standard BMP palette order)
|
||||
// Color 0: Black (text/foreground in XTC)
|
||||
uint8_t black[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
coverBmp.write(black, 4);
|
||||
@ -506,8 +506,8 @@ bool Xtc::generateThumbBmp() const {
|
||||
// Bounds check for buffer access
|
||||
if (byteIdx < bitmapSize) {
|
||||
const uint8_t pixelBit = (pageBuffer[byteIdx] >> bitIdx) & 1;
|
||||
// XTC polarity: 1=black, 0=white
|
||||
grayValue = pixelBit ? 0 : 255;
|
||||
// XTC 1-bit polarity: 0=black, 1=white (same as BMP palette)
|
||||
grayValue = pixelBit ? 255 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,8 @@
|
||||
|
||||
namespace {
|
||||
// Time threshold for treating a long press as a page-up/page-down
|
||||
constexpr int SKIP_PAGE_MS = 700;
|
||||
constexpr int RAPID_NAV_START_MS = 500;
|
||||
constexpr int RAPID_NAV_DELAY_MS = 700;
|
||||
} // namespace
|
||||
|
||||
int EpubReaderChapterSelectionActivity::getPageItems() const {
|
||||
@ -75,8 +76,17 @@ void EpubReaderChapterSelectionActivity::loop() {
|
||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||
|
||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||
const bool prevPressed =
|
||||
mappedInput.isPressed(MappedInputManager::Button::Up) || mappedInput.isPressed(MappedInputManager::Button::Left);
|
||||
const bool nextPressed = mappedInput.isPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.isPressed(MappedInputManager::Button::Right);
|
||||
|
||||
const int pageItems = getPageItems();
|
||||
const int currentTocPage = selectorIndex / pageItems;
|
||||
const int lastTocPage = (epub->getTocItemsCount() - 1) / pageItems;
|
||||
|
||||
const bool shouldNavigateRapidly = mappedInput.getHeldTime() > RAPID_NAV_START_MS;
|
||||
const bool isRapidNavigationDue = (millis() - lastRapidNavTime) > RAPID_NAV_DELAY_MS;
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
const auto newSpineIndex = epub->getSpineIndexForTocIndex(selectorIndex);
|
||||
@ -88,20 +98,29 @@ void EpubReaderChapterSelectionActivity::loop() {
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
onGoBack();
|
||||
} else if (prevReleased) {
|
||||
if (skipPage) {
|
||||
selectorIndex =
|
||||
((selectorIndex / pageItems - 1) * pageItems + epub->getTocItemsCount()) % epub->getTocItemsCount();
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + epub->getTocItemsCount() - 1) % epub->getTocItemsCount();
|
||||
if (lastRapidNavTime != 0) {
|
||||
lastRapidNavTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
selectorIndex = (selectorIndex + epub->getTocItemsCount() - 1) % epub->getTocItemsCount();
|
||||
updateRequired = true;
|
||||
} else if (nextReleased) {
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % epub->getTocItemsCount();
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + 1) % epub->getTocItemsCount();
|
||||
if (lastRapidNavTime != 0) {
|
||||
lastRapidNavTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
selectorIndex = (selectorIndex + 1) % epub->getTocItemsCount();
|
||||
updateRequired = true;
|
||||
} else if (prevPressed && shouldNavigateRapidly && isRapidNavigationDue) {
|
||||
selectorIndex = currentTocPage > 0 ? (currentTocPage - 1) * pageItems : lastTocPage * pageItems;
|
||||
updateRequired = true;
|
||||
lastRapidNavTime = millis();
|
||||
} else if (nextPressed && shouldNavigateRapidly && isRapidNavigationDue) {
|
||||
selectorIndex = currentTocPage < lastTocPage ? (currentTocPage + 1) * pageItems : 0;
|
||||
updateRequired = true;
|
||||
lastRapidNavTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ class EpubReaderChapterSelectionActivity final : public Activity {
|
||||
int currentSpineIndex = 0;
|
||||
int selectorIndex = 0;
|
||||
bool updateRequired = false;
|
||||
unsigned long lastRapidNavTime = 0;
|
||||
const std::function<void()> onGoBack;
|
||||
const std::function<void(int newSpineIndex)> onSelectSpineIndex;
|
||||
|
||||
|
||||
@ -6,7 +6,8 @@
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
constexpr int SKIP_PAGE_MS = 700;
|
||||
constexpr int RAPID_NAV_START_MS = 500;
|
||||
constexpr int RAPID_NAV_DELAY_MS = 700;
|
||||
} // namespace
|
||||
|
||||
int XtcReaderChapterSelectionActivity::getPageItems() const {
|
||||
@ -80,8 +81,18 @@ void XtcReaderChapterSelectionActivity::loop() {
|
||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||
|
||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||
const bool prevPressed =
|
||||
mappedInput.isPressed(MappedInputManager::Button::Up) || mappedInput.isPressed(MappedInputManager::Button::Left);
|
||||
const bool nextPressed = mappedInput.isPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.isPressed(MappedInputManager::Button::Right);
|
||||
|
||||
const int pageItems = getPageItems();
|
||||
const int currentTocPage = selectorIndex / pageItems;
|
||||
const int total = static_cast<int>(xtc->getChapters().size());
|
||||
const int lastTocPage = total > 0 ? (total - 1) / pageItems : 0;
|
||||
|
||||
const bool shouldNavigateRapidly = mappedInput.getHeldTime() > RAPID_NAV_START_MS;
|
||||
const bool isRapidNavigationDue = (millis() - lastRapidNavTime) > RAPID_NAV_DELAY_MS;
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
const auto& chapters = xtc->getChapters();
|
||||
@ -91,27 +102,37 @@ void XtcReaderChapterSelectionActivity::loop() {
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
onGoBack();
|
||||
} else if (prevReleased) {
|
||||
const int total = static_cast<int>(xtc->getChapters().size());
|
||||
if (total == 0) {
|
||||
if (total == 0 || lastRapidNavTime != 0) {
|
||||
lastRapidNavTime = 0;
|
||||
return;
|
||||
}
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + total) % total;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + total - 1) % total;
|
||||
}
|
||||
|
||||
selectorIndex = (selectorIndex + total - 1) % total;
|
||||
updateRequired = true;
|
||||
} else if (nextReleased) {
|
||||
const int total = static_cast<int>(xtc->getChapters().size());
|
||||
if (total == 0 || lastRapidNavTime != 0) {
|
||||
lastRapidNavTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
selectorIndex = (selectorIndex + 1) % total;
|
||||
updateRequired = true;
|
||||
} else if (prevPressed && shouldNavigateRapidly && isRapidNavigationDue) {
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % total;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + 1) % total;
|
||||
}
|
||||
|
||||
selectorIndex = currentTocPage > 0 ? (currentTocPage - 1) * pageItems : lastTocPage * pageItems;
|
||||
updateRequired = true;
|
||||
lastRapidNavTime = millis();
|
||||
} else if (nextPressed && shouldNavigateRapidly && isRapidNavigationDue) {
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectorIndex = currentTocPage < lastTocPage ? (currentTocPage + 1) * pageItems : 0;
|
||||
updateRequired = true;
|
||||
lastRapidNavTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ class XtcReaderChapterSelectionActivity final : public Activity {
|
||||
uint32_t currentPage = 0;
|
||||
int selectorIndex = 0;
|
||||
bool updateRequired = false;
|
||||
unsigned long lastRapidNavTime = 0;
|
||||
const std::function<void()> onGoBack;
|
||||
const std::function<void(uint32_t newPage)> onSelectPage;
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ int KeyboardEntryActivity::getRowLength(const int row) const {
|
||||
case 3:
|
||||
return 10; // zxcvbnm,./
|
||||
case 4:
|
||||
return 10; // caps (2 wide), space (5 wide), backspace (2 wide), OK
|
||||
return 10; // shift (2 wide), space (5 wide), backspace (2 wide), OK
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -145,6 +145,11 @@ void KeyboardEntryActivity::loop() {
|
||||
// Clamp column to valid range for new row
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
} else {
|
||||
// Wrap to bottom row
|
||||
selectedRow = NUM_ROWS - 1;
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
}
|
||||
updateRequired = true;
|
||||
}
|
||||
@ -154,16 +159,24 @@ void KeyboardEntryActivity::loop() {
|
||||
selectedRow++;
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
} else {
|
||||
// Wrap to top row
|
||||
selectedRow = 0;
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
}
|
||||
updateRequired = true;
|
||||
}
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
|
||||
// Special bottom row case
|
||||
if (selectedRow == SPECIAL_ROW) {
|
||||
// Bottom row has special key widths
|
||||
if (selectedCol >= SHIFT_COL && selectedCol < SPACE_COL) {
|
||||
// In shift key, do nothing
|
||||
// In shift key, wrap to end of row
|
||||
selectedCol = maxCol;
|
||||
} else if (selectedCol >= SPACE_COL && selectedCol < BACKSPACE_COL) {
|
||||
// In space bar, move to shift
|
||||
selectedCol = SHIFT_COL;
|
||||
@ -180,10 +193,9 @@ void KeyboardEntryActivity::loop() {
|
||||
|
||||
if (selectedCol > 0) {
|
||||
selectedCol--;
|
||||
} else if (selectedRow > 0) {
|
||||
// Wrap to previous row
|
||||
selectedRow--;
|
||||
selectedCol = getRowLength(selectedRow) - 1;
|
||||
} else {
|
||||
// Wrap to end of current row
|
||||
selectedCol = maxCol;
|
||||
}
|
||||
updateRequired = true;
|
||||
}
|
||||
@ -204,7 +216,8 @@ void KeyboardEntryActivity::loop() {
|
||||
// In backspace, move to done
|
||||
selectedCol = DONE_COL;
|
||||
} else if (selectedCol >= DONE_COL) {
|
||||
// At done button, do nothing
|
||||
// At done button, wrap to beginning of row
|
||||
selectedCol = SHIFT_COL;
|
||||
}
|
||||
updateRequired = true;
|
||||
return;
|
||||
@ -212,9 +225,8 @@ void KeyboardEntryActivity::loop() {
|
||||
|
||||
if (selectedCol < maxCol) {
|
||||
selectedCol++;
|
||||
} else if (selectedRow < NUM_ROWS - 1) {
|
||||
// Wrap to next row
|
||||
selectedRow++;
|
||||
} else {
|
||||
// Wrap to beginning of current row
|
||||
selectedCol = 0;
|
||||
}
|
||||
updateRequired = true;
|
||||
@ -288,14 +300,14 @@ void KeyboardEntryActivity::render() const {
|
||||
|
||||
// Handle bottom row (row 4) specially with proper multi-column keys
|
||||
if (row == 4) {
|
||||
// Bottom row layout: CAPS (2 cols) | SPACE (5 cols) | <- (2 cols) | OK (2 cols)
|
||||
// Bottom row layout: SHIFT (2 cols) | SPACE (5 cols) | <- (2 cols) | OK (2 cols)
|
||||
// Total: 11 visual columns, but we use logical positions for selection
|
||||
|
||||
int currentX = startX;
|
||||
|
||||
// CAPS key (logical col 0, spans 2 key widths)
|
||||
const bool capsSelected = (selectedRow == 4 && selectedCol >= SHIFT_COL && selectedCol < SPACE_COL);
|
||||
renderItemWithSelector(currentX + 2, rowY, shiftActive ? "CAPS" : "caps", capsSelected);
|
||||
// SHIFT key (logical col 0, spans 2 key widths)
|
||||
const bool shiftSelected = (selectedRow == 4 && selectedCol >= SHIFT_COL && selectedCol < SPACE_COL);
|
||||
renderItemWithSelector(currentX + 2, rowY, shiftActive ? "SHIFT" : "shift", shiftSelected);
|
||||
currentX += 2 * (keyWidth + keySpacing);
|
||||
|
||||
// Space bar (logical cols 2-6, spans 5 key widths)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user