Support swapping the functionality of the front buttons (#133)

## Summary

**What is the goal of this PR?** 

Adds a setting to swap the front buttons. The default functionality are:
Back/Confirm/Left/Right. When this setting is enabled they become:
Left/Right/Back/Confirm. This makes it more comfortable to use when
holding in your right hand since your thumb can more easily rest on the
next button. The original firmware has a similar setting.

**What changes are included?**

- Add the new setting.
- Create a mapper to dynamically switch the buttons based on the
setting.
- Use mapper on the various activity screens.
- Update the button hints to reflect the swapped buttons.

## Additional Context

Full disclosure: I used Codex CLI to put this PR together, but did
review it to make sure it makes sense.

Also tested on my device:
https://share.cleanshot.com/k76891NY
This commit is contained in:
dangson 2025-12-28 21:59:14 -06:00 committed by GitHub
parent 534504cf7a
commit 140d8749a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 285 additions and 140 deletions

View File

@ -67,6 +67,7 @@ The Settings screen allows you to configure the device's behavior. There are a f
- **Extra Paragraph Spacing**: If enabled, vertical space will be added between paragraphs in the book, if disabled,
paragraphs will not have vertical space between them, but will have first word indentation.
- **Short Power Button Click**: Whether to trigger the power button on a short press or a long press.
- **Front Button Layout**: Swap the order of the bottom edge buttons from Back/Confirm/Left/Right to Left/Right/Back/Confirm.
### 3.6 Sleep Screen

View File

@ -11,7 +11,7 @@ CrossPointSettings CrossPointSettings::instance;
namespace {
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
// Increment this when adding new persisted settings fields
constexpr uint8_t SETTINGS_COUNT = 5;
constexpr uint8_t SETTINGS_COUNT = 6;
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
} // namespace
@ -31,6 +31,7 @@ bool CrossPointSettings::saveToFile() const {
serialization::writePod(outputFile, shortPwrBtn);
serialization::writePod(outputFile, statusBar);
serialization::writePod(outputFile, orientation);
serialization::writePod(outputFile, frontButtonLayout);
outputFile.close();
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
@ -67,6 +68,8 @@ bool CrossPointSettings::loadFromFile() {
if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, orientation);
if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, frontButtonLayout);
if (++settingsRead >= fileSettingsCount) break;
} while (false);
inputFile.close();

View File

@ -28,6 +28,11 @@ class CrossPointSettings {
LANDSCAPE_CCW = 3 // 800x480 logical coordinates, native panel orientation
};
// Front button layout options
// Default: Back, Confirm, Left, Right
// Swapped: Left, Right, Back, Confirm
enum FRONT_BUTTON_LAYOUT { BACK_CONFIRM_LEFT_RIGHT = 0, LEFT_RIGHT_BACK_CONFIRM = 1 };
// Sleep screen settings
uint8_t sleepScreen = DARK;
// Status bar settings
@ -39,6 +44,8 @@ class CrossPointSettings {
// EPUB reading orientation settings
// 0 = portrait (default), 1 = landscape clockwise, 2 = inverted, 3 = landscape counter-clockwise
uint8_t orientation = PORTRAIT;
// Front button layout
uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT;
~CrossPointSettings() = default;

View File

@ -0,0 +1,77 @@
#include "MappedInputManager.h"
decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button button) const {
const auto layout = static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(SETTINGS.frontButtonLayout);
switch (button) {
case Button::Back:
switch (layout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return InputManager::BTN_LEFT;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default:
return InputManager::BTN_BACK;
}
case Button::Confirm:
switch (layout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return InputManager::BTN_RIGHT;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default:
return InputManager::BTN_CONFIRM;
}
case Button::Left:
switch (layout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return InputManager::BTN_BACK;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default:
return InputManager::BTN_LEFT;
}
case Button::Right:
switch (layout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return InputManager::BTN_CONFIRM;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default:
return InputManager::BTN_RIGHT;
}
case Button::Up:
return InputManager::BTN_UP;
case Button::Down:
return InputManager::BTN_DOWN;
case Button::Power:
return InputManager::BTN_POWER;
case Button::PageBack:
return InputManager::BTN_UP;
case Button::PageForward:
return InputManager::BTN_DOWN;
}
return InputManager::BTN_BACK;
}
bool MappedInputManager::wasPressed(const Button button) const { return inputManager.wasPressed(mapButton(button)); }
bool MappedInputManager::wasReleased(const Button button) const { return inputManager.wasReleased(mapButton(button)); }
bool MappedInputManager::isPressed(const Button button) const { return inputManager.isPressed(mapButton(button)); }
bool MappedInputManager::wasAnyPressed() const { return inputManager.wasAnyPressed(); }
bool MappedInputManager::wasAnyReleased() const { return inputManager.wasAnyReleased(); }
unsigned long MappedInputManager::getHeldTime() const { return inputManager.getHeldTime(); }
MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const char* confirm, const char* previous,
const char* next) const {
const auto layout = static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(SETTINGS.frontButtonLayout);
switch (layout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return {previous, next, back, confirm};
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default:
return {back, confirm, previous, next};
}
}

31
src/MappedInputManager.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <InputManager.h>
#include "CrossPointSettings.h"
class MappedInputManager {
public:
enum class Button { Back, Confirm, Left, Right, Up, Down, Power, PageBack, PageForward };
struct Labels {
const char* btn1;
const char* btn2;
const char* btn3;
const char* btn4;
};
explicit MappedInputManager(InputManager& inputManager) : inputManager(inputManager) {}
bool wasPressed(Button button) const;
bool wasReleased(Button button) const;
bool isPressed(Button button) const;
bool wasAnyPressed() const;
bool wasAnyReleased() const;
unsigned long getHeldTime() const;
Labels mapLabels(const char* back, const char* confirm, const char* previous, const char* next) const;
private:
InputManager& inputManager;
decltype(InputManager::BTN_BACK) mapButton(Button button) const;
};

View File

@ -5,18 +5,19 @@
#include <string>
#include <utility>
class InputManager;
#include "../MappedInputManager.h"
class GfxRenderer;
class Activity {
protected:
std::string name;
GfxRenderer& renderer;
InputManager& inputManager;
MappedInputManager& mappedInput;
public:
explicit Activity(std::string name, GfxRenderer& renderer, InputManager& inputManager)
: name(std::move(name)), renderer(renderer), inputManager(inputManager) {}
explicit Activity(std::string name, GfxRenderer& renderer, MappedInputManager& mappedInput)
: name(std::move(name)), renderer(renderer), mappedInput(mappedInput) {}
virtual ~Activity() = default;
virtual void onEnter() { Serial.printf("[%lu] [ACT] Entering activity: %s\n", millis(), name.c_str()); }
virtual void onExit() { Serial.printf("[%lu] [ACT] Exiting activity: %s\n", millis(), name.c_str()); }

View File

@ -10,8 +10,8 @@ class ActivityWithSubactivity : public Activity {
void enterNewActivity(Activity* activity);
public:
explicit ActivityWithSubactivity(std::string name, GfxRenderer& renderer, InputManager& inputManager)
: Activity(std::move(name), renderer, inputManager) {}
explicit ActivityWithSubactivity(std::string name, GfxRenderer& renderer, MappedInputManager& mappedInput)
: Activity(std::move(name), renderer, mappedInput) {}
void loop() override;
void onExit() override;
};

View File

@ -3,6 +3,7 @@
class BootActivity final : public Activity {
public:
explicit BootActivity(GfxRenderer& renderer, InputManager& inputManager) : Activity("Boot", renderer, inputManager) {}
explicit BootActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
: Activity("Boot", renderer, mappedInput) {}
void onEnter() override;
};

View File

@ -5,8 +5,8 @@ class Bitmap;
class SleepActivity final : public Activity {
public:
explicit SleepActivity(GfxRenderer& renderer, InputManager& inputManager)
: Activity("Sleep", renderer, inputManager) {}
explicit SleepActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
: Activity("Sleep", renderer, mappedInput) {}
void onEnter() override;
private:

View File

@ -49,14 +49,14 @@ void HomeActivity::onExit() {
}
void HomeActivity::loop() {
const bool prevPressed =
inputManager.wasPressed(InputManager::BTN_UP) || inputManager.wasPressed(InputManager::BTN_LEFT);
const bool nextPressed =
inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT);
const bool prevPressed = mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left);
const bool nextPressed = mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right);
const int menuCount = getMenuItemCount();
if (inputManager.wasReleased(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (hasContinueReading) {
// Menu: Continue Reading, Browse, File transfer, Settings
if (selectorIndex == 0) {
@ -143,7 +143,8 @@ void HomeActivity::render() const {
renderer.drawText(UI_FONT_ID, 20, menuY, "Settings", selectorIndex != menuIndex);
renderer.drawButtonHints(UI_FONT_ID, "Back", "Confirm", "Left", "Right");
const auto labels = mappedInput.mapLabels("Back", "Confirm", "Left", "Right");
renderer.drawButtonHints(UI_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
}

View File

@ -24,10 +24,10 @@ class HomeActivity final : public Activity {
int getMenuItemCount() const;
public:
explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager,
explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onContinueReading, const std::function<void()>& onReaderOpen,
const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen)
: Activity("Home", renderer, inputManager),
: Activity("Home", renderer, mappedInput),
onContinueReading(onContinueReading),
onReaderOpen(onReaderOpen),
onSettingsOpen(onSettingsOpen),

View File

@ -57,7 +57,7 @@ void CrossPointWebServerActivity::onEnter() {
// Launch network mode selection subactivity
Serial.printf("[%lu] [WEBACT] Launching NetworkModeSelectionActivity...\n", millis());
enterNewActivity(new NetworkModeSelectionActivity(
renderer, inputManager, [this](const NetworkMode mode) { onNetworkModeSelected(mode); },
renderer, mappedInput, [this](const NetworkMode mode) { onNetworkModeSelected(mode); },
[this]() { onGoBack(); } // Cancel goes back to home
));
}
@ -141,7 +141,7 @@ void CrossPointWebServerActivity::onNetworkModeSelected(const NetworkMode mode)
state = WebServerActivityState::WIFI_SELECTION;
Serial.printf("[%lu] [WEBACT] Launching WifiSelectionActivity...\n", millis());
enterNewActivity(new WifiSelectionActivity(renderer, inputManager,
enterNewActivity(new WifiSelectionActivity(renderer, mappedInput,
[this](const bool connected) { onWifiSelectionComplete(connected); }));
} else {
// AP mode - start access point
@ -174,7 +174,7 @@ void CrossPointWebServerActivity::onWifiSelectionComplete(const bool connected)
exitActivity();
state = WebServerActivityState::MODE_SELECTION;
enterNewActivity(new NetworkModeSelectionActivity(
renderer, inputManager, [this](const NetworkMode mode) { onNetworkModeSelected(mode); },
renderer, mappedInput, [this](const NetworkMode mode) { onNetworkModeSelected(mode); },
[this]() { onGoBack(); }));
}
}
@ -305,7 +305,7 @@ void CrossPointWebServerActivity::loop() {
}
// Handle exit on Back button
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
onGoBack();
return;
}
@ -428,5 +428,6 @@ void CrossPointWebServerActivity::renderServerRunning() const {
REGULAR);
}
renderer.drawButtonHints(UI_FONT_ID, "« Exit", "", "", "");
const auto labels = mappedInput.mapLabels("« Exit", "", "", "");
renderer.drawButtonHints(UI_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
}

View File

@ -63,9 +63,9 @@ class CrossPointWebServerActivity final : public ActivityWithSubactivity {
void stopWebServer();
public:
explicit CrossPointWebServerActivity(GfxRenderer& renderer, InputManager& inputManager,
explicit CrossPointWebServerActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onGoBack)
: ActivityWithSubactivity("CrossPointWebServer", renderer, inputManager), onGoBack(onGoBack) {}
: ActivityWithSubactivity("CrossPointWebServer", renderer, mappedInput), onGoBack(onGoBack) {}
void onEnter() override;
void onExit() override;
void loop() override;

View File

@ -51,23 +51,23 @@ void NetworkModeSelectionActivity::onExit() {
void NetworkModeSelectionActivity::loop() {
// Handle back button - cancel
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
onCancel();
return;
}
// Handle confirm button - select current option
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
const NetworkMode mode = (selectedIndex == 0) ? NetworkMode::JOIN_NETWORK : NetworkMode::CREATE_HOTSPOT;
onModeSelected(mode);
return;
}
// Handle navigation
const bool prevPressed =
inputManager.wasPressed(InputManager::BTN_UP) || inputManager.wasPressed(InputManager::BTN_LEFT);
const bool nextPressed =
inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT);
const bool prevPressed = mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left);
const bool nextPressed = mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right);
if (prevPressed) {
selectedIndex = (selectedIndex + MENU_ITEM_COUNT - 1) % MENU_ITEM_COUNT;
@ -122,7 +122,8 @@ void NetworkModeSelectionActivity::render() const {
}
// Draw help text at bottom
renderer.drawButtonHints(UI_FONT_ID, "« Back", "Select", "", "");
const auto labels = mappedInput.mapLabels("« Back", "Select", "", "");
renderer.drawButtonHints(UI_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
}

View File

@ -31,10 +31,10 @@ class NetworkModeSelectionActivity final : public Activity {
void render() const;
public:
explicit NetworkModeSelectionActivity(GfxRenderer& renderer, InputManager& inputManager,
explicit NetworkModeSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void(NetworkMode)>& onModeSelected,
const std::function<void()>& onCancel)
: Activity("NetworkModeSelection", renderer, inputManager), onModeSelected(onModeSelected), onCancel(onCancel) {}
: Activity("NetworkModeSelection", renderer, mappedInput), onModeSelected(onModeSelected), onCancel(onCancel) {}
void onEnter() override;
void onExit() override;
void loop() override;

View File

@ -190,7 +190,7 @@ void WifiSelectionActivity::selectNetwork(const int index) {
// Don't allow screen updates while changing activity
xSemaphoreTake(renderingMutex, portMAX_DELAY);
enterNewActivity(new KeyboardEntryActivity(
renderer, inputManager, "Enter WiFi Password",
renderer, mappedInput, "Enter WiFi Password",
"", // No initial text
50, // Y position
64, // Max password length
@ -302,17 +302,19 @@ void WifiSelectionActivity::loop() {
// Handle save prompt state
if (state == WifiSelectionState::SAVE_PROMPT) {
if (inputManager.wasPressed(InputManager::BTN_LEFT) || inputManager.wasPressed(InputManager::BTN_UP)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (savePromptSelection > 0) {
savePromptSelection--;
updateRequired = true;
}
} else if (inputManager.wasPressed(InputManager::BTN_RIGHT) || inputManager.wasPressed(InputManager::BTN_DOWN)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
if (savePromptSelection < 1) {
savePromptSelection++;
updateRequired = true;
}
} else if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
if (savePromptSelection == 0) {
// User chose "Yes" - save the password
xSemaphoreTake(renderingMutex, portMAX_DELAY);
@ -321,7 +323,7 @@ void WifiSelectionActivity::loop() {
}
// Complete - parent will start web server
onComplete(true);
} else if (inputManager.wasPressed(InputManager::BTN_BACK)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
// Skip saving, complete anyway
onComplete(true);
}
@ -330,17 +332,19 @@ void WifiSelectionActivity::loop() {
// Handle forget prompt state (connection failed with saved credentials)
if (state == WifiSelectionState::FORGET_PROMPT) {
if (inputManager.wasPressed(InputManager::BTN_LEFT) || inputManager.wasPressed(InputManager::BTN_UP)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (forgetPromptSelection > 0) {
forgetPromptSelection--;
updateRequired = true;
}
} else if (inputManager.wasPressed(InputManager::BTN_RIGHT) || inputManager.wasPressed(InputManager::BTN_DOWN)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
if (forgetPromptSelection < 1) {
forgetPromptSelection++;
updateRequired = true;
}
} else if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
if (forgetPromptSelection == 0) {
// User chose "Yes" - forget the network
xSemaphoreTake(renderingMutex, portMAX_DELAY);
@ -356,7 +360,7 @@ void WifiSelectionActivity::loop() {
// Go back to network list
state = WifiSelectionState::NETWORK_LIST;
updateRequired = true;
} else if (inputManager.wasPressed(InputManager::BTN_BACK)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
// Skip forgetting, go back to network list
state = WifiSelectionState::NETWORK_LIST;
updateRequired = true;
@ -373,7 +377,8 @@ void WifiSelectionActivity::loop() {
// Handle connection failed state
if (state == WifiSelectionState::CONNECTION_FAILED) {
if (inputManager.wasPressed(InputManager::BTN_BACK) || inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back) ||
mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
// If we used saved credentials, offer to forget the network
if (usedSavedPassword) {
state = WifiSelectionState::FORGET_PROMPT;
@ -390,13 +395,13 @@ void WifiSelectionActivity::loop() {
// Handle network list state
if (state == WifiSelectionState::NETWORK_LIST) {
// Check for Back button to exit (cancel)
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
onComplete(false);
return;
}
// Check for Confirm button to select network or rescan
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
if (!networks.empty()) {
selectNetwork(selectedNetworkIndex);
} else {
@ -406,12 +411,14 @@ void WifiSelectionActivity::loop() {
}
// Handle UP/DOWN navigation
if (inputManager.wasPressed(InputManager::BTN_UP) || inputManager.wasPressed(InputManager::BTN_LEFT)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (selectedNetworkIndex > 0) {
selectedNetworkIndex--;
updateRequired = true;
}
} else if (inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
if (!networks.empty() && selectedNetworkIndex < static_cast<int>(networks.size()) - 1) {
selectedNetworkIndex++;
updateRequired = true;
@ -557,7 +564,8 @@ void WifiSelectionActivity::renderNetworkList() const {
// Draw help text
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved");
renderer.drawButtonHints(UI_FONT_ID, "« Back", "Connect", "", "");
const auto labels = mappedInput.mapLabels("« Back", "Connect", "", "");
renderer.drawButtonHints(UI_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
}
void WifiSelectionActivity::renderConnecting() const {

View File

@ -92,9 +92,9 @@ class WifiSelectionActivity final : public ActivityWithSubactivity {
std::string getSignalStrengthIndicator(int32_t rssi) const;
public:
explicit WifiSelectionActivity(GfxRenderer& renderer, InputManager& inputManager,
explicit WifiSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void(bool connected)>& onComplete)
: ActivityWithSubactivity("WifiSelection", renderer, inputManager), onComplete(onComplete) {}
: ActivityWithSubactivity("WifiSelection", renderer, mappedInput), onComplete(onComplete) {}
void onEnter() override;
void onExit() override;
void loop() override;

View File

@ -106,12 +106,12 @@ void EpubReaderActivity::loop() {
}
// Enter chapter selection activity
if (inputManager.wasReleased(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
// Don't start activity transition while rendering
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new EpubReaderChapterSelectionActivity(
this->renderer, this->inputManager, epub, currentSpineIndex,
this->renderer, this->mappedInput, epub, currentSpineIndex,
[this] {
exitActivity();
updateRequired = true;
@ -129,21 +129,21 @@ void EpubReaderActivity::loop() {
}
// Long press BACK (1s+) goes directly to home
if (inputManager.isPressed(InputManager::BTN_BACK) && inputManager.getHeldTime() >= goHomeMs) {
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
onGoHome();
return;
}
// Short press BACK goes to file selection
if (inputManager.wasReleased(InputManager::BTN_BACK) && inputManager.getHeldTime() < goHomeMs) {
if (mappedInput.wasReleased(MappedInputManager::Button::Back) && mappedInput.getHeldTime() < goHomeMs) {
onGoBack();
return;
}
const bool prevReleased =
inputManager.wasReleased(InputManager::BTN_UP) || inputManager.wasReleased(InputManager::BTN_LEFT);
const bool nextReleased =
inputManager.wasReleased(InputManager::BTN_DOWN) || inputManager.wasReleased(InputManager::BTN_RIGHT);
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
mappedInput.wasReleased(MappedInputManager::Button::Left);
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
mappedInput.wasReleased(MappedInputManager::Button::Right);
if (!prevReleased && !nextReleased) {
return;
@ -157,7 +157,7 @@ void EpubReaderActivity::loop() {
return;
}
const bool skipChapter = inputManager.getHeldTime() > skipChapterMs;
const bool skipChapter = mappedInput.getHeldTime() > skipChapterMs;
if (skipChapter) {
// We don't want to delete the section mid-render, so grab the semaphore

View File

@ -27,9 +27,9 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
void renderStatusBar(int orientedMarginRight, int orientedMarginBottom, int orientedMarginLeft) const;
public:
explicit EpubReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr<Epub> epub,
explicit EpubReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Epub> epub,
const std::function<void()>& onGoBack, const std::function<void()>& onGoHome)
: ActivityWithSubactivity("EpubReader", renderer, inputManager),
: ActivityWithSubactivity("EpubReader", renderer, mappedInput),
epub(std::move(epub)),
onGoBack(onGoBack),
onGoHome(onGoHome) {}

View File

@ -66,17 +66,17 @@ void EpubReaderChapterSelectionActivity::onExit() {
}
void EpubReaderChapterSelectionActivity::loop() {
const bool prevReleased =
inputManager.wasReleased(InputManager::BTN_UP) || inputManager.wasReleased(InputManager::BTN_LEFT);
const bool nextReleased =
inputManager.wasReleased(InputManager::BTN_DOWN) || inputManager.wasReleased(InputManager::BTN_RIGHT);
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) ||
mappedInput.wasReleased(MappedInputManager::Button::Left);
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
mappedInput.wasReleased(MappedInputManager::Button::Right);
const bool skipPage = inputManager.getHeldTime() > SKIP_PAGE_MS;
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
const int pageItems = getPageItems();
if (inputManager.wasReleased(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
onSelectSpineIndex(selectorIndex);
} else if (inputManager.wasReleased(InputManager::BTN_BACK)) {
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
onGoBack();
} else if (prevReleased) {
if (skipPage) {

View File

@ -27,11 +27,11 @@ class EpubReaderChapterSelectionActivity final : public Activity {
void renderScreen();
public:
explicit EpubReaderChapterSelectionActivity(GfxRenderer& renderer, InputManager& inputManager,
explicit EpubReaderChapterSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::shared_ptr<Epub>& epub, const int currentSpineIndex,
const std::function<void()>& onGoBack,
const std::function<void(int newSpineIndex)>& onSelectSpineIndex)
: Activity("EpubReaderChapterSelection", renderer, inputManager),
: Activity("EpubReaderChapterSelection", renderer, mappedInput),
epub(epub),
currentSpineIndex(currentSpineIndex),
onGoBack(onGoBack),

View File

@ -89,7 +89,7 @@ void FileSelectionActivity::onExit() {
void FileSelectionActivity::loop() {
// Long press BACK (1s+) goes to root folder
if (inputManager.isPressed(InputManager::BTN_BACK) && inputManager.getHeldTime() >= GO_HOME_MS) {
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= GO_HOME_MS) {
if (basepath != "/") {
basepath = "/";
loadFiles();
@ -98,14 +98,14 @@ void FileSelectionActivity::loop() {
return;
}
const bool prevReleased =
inputManager.wasReleased(InputManager::BTN_UP) || inputManager.wasReleased(InputManager::BTN_LEFT);
const bool nextReleased =
inputManager.wasReleased(InputManager::BTN_DOWN) || inputManager.wasReleased(InputManager::BTN_RIGHT);
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) ||
mappedInput.wasReleased(MappedInputManager::Button::Left);
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
mappedInput.wasReleased(MappedInputManager::Button::Right);
const bool skipPage = inputManager.getHeldTime() > SKIP_PAGE_MS;
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
if (inputManager.wasReleased(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (files.empty()) {
return;
}
@ -118,9 +118,9 @@ void FileSelectionActivity::loop() {
} else {
onSelect(basepath + files[selectorIndex]);
}
} else if (inputManager.wasReleased(InputManager::BTN_BACK)) {
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
// Short press: go up one directory, or go home if at root
if (inputManager.getHeldTime() < GO_HOME_MS) {
if (mappedInput.getHeldTime() < GO_HOME_MS) {
if (basepath != "/") {
basepath.replace(basepath.find_last_of('/'), std::string::npos, "");
if (basepath.empty()) basepath = "/";
@ -166,7 +166,8 @@ void FileSelectionActivity::render() const {
renderer.drawCenteredText(READER_FONT_ID, 10, "Books", true, BOLD);
// Help text
renderer.drawButtonHints(UI_FONT_ID, "« Home", "Open", "", "");
const auto labels = mappedInput.mapLabels("« Home", "Open", "", "");
renderer.drawButtonHints(UI_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
if (files.empty()) {
renderer.drawText(UI_FONT_ID, 20, 60, "No books found");

View File

@ -25,10 +25,10 @@ class FileSelectionActivity final : public Activity {
void loadFiles();
public:
explicit FileSelectionActivity(GfxRenderer& renderer, InputManager& inputManager,
explicit FileSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void(const std::string&)>& onSelect,
const std::function<void()>& onGoHome, std::string initialPath = "/")
: Activity("FileSelection", renderer, inputManager),
: Activity("FileSelection", renderer, mappedInput),
basepath(initialPath.empty() ? "/" : std::move(initialPath)),
onSelect(onSelect),
onGoHome(onGoHome) {}

View File

@ -61,7 +61,7 @@ std::unique_ptr<Xtc> ReaderActivity::loadXtc(const std::string& path) {
void ReaderActivity::onSelectBookFile(const std::string& path) {
currentBookPath = path; // Track current book path
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Loading..."));
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Loading..."));
if (isXtcFile(path)) {
// Load XTC file
@ -70,7 +70,7 @@ void ReaderActivity::onSelectBookFile(const std::string& path) {
onGoToXtcReader(std::move(xtc));
} else {
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Failed to load XTC", REGULAR,
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load XTC", REGULAR,
EInkDisplay::HALF_REFRESH));
delay(2000);
onGoToFileSelection();
@ -82,7 +82,7 @@ void ReaderActivity::onSelectBookFile(const std::string& path) {
onGoToEpubReader(std::move(epub));
} else {
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Failed to load epub", REGULAR,
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load epub", REGULAR,
EInkDisplay::HALF_REFRESH));
delay(2000);
onGoToFileSelection();
@ -95,7 +95,7 @@ void ReaderActivity::onGoToFileSelection(const std::string& fromBookPath) {
// If coming from a book, start in that book's folder; otherwise start from root
const auto initialPath = fromBookPath.empty() ? "/" : extractFolderPath(fromBookPath);
enterNewActivity(new FileSelectionActivity(
renderer, inputManager, [this](const std::string& path) { onSelectBookFile(path); }, onGoBack, initialPath));
renderer, mappedInput, [this](const std::string& path) { onSelectBookFile(path); }, onGoBack, initialPath));
}
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
@ -103,7 +103,7 @@ void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
currentBookPath = epubPath;
exitActivity();
enterNewActivity(new EpubReaderActivity(
renderer, inputManager, std::move(epub), [this, epubPath] { onGoToFileSelection(epubPath); },
renderer, mappedInput, std::move(epub), [this, epubPath] { onGoToFileSelection(epubPath); },
[this] { onGoBack(); }));
}
@ -112,7 +112,7 @@ void ReaderActivity::onGoToXtcReader(std::unique_ptr<Xtc> xtc) {
currentBookPath = xtcPath;
exitActivity();
enterNewActivity(new XtcReaderActivity(
renderer, inputManager, std::move(xtc), [this, xtcPath] { onGoToFileSelection(xtcPath); },
renderer, mappedInput, std::move(xtc), [this, xtcPath] { onGoToFileSelection(xtcPath); },
[this] { onGoBack(); }));
}

View File

@ -21,9 +21,9 @@ class ReaderActivity final : public ActivityWithSubactivity {
void onGoToXtcReader(std::unique_ptr<Xtc> xtc);
public:
explicit ReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::string initialBookPath,
explicit ReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialBookPath,
const std::function<void()>& onGoBack)
: ActivityWithSubactivity("Reader", renderer, inputManager),
: ActivityWithSubactivity("Reader", renderer, mappedInput),
initialBookPath(std::move(initialBookPath)),
onGoBack(onGoBack) {}
void onEnter() override;

View File

@ -71,21 +71,21 @@ void XtcReaderActivity::onExit() {
void XtcReaderActivity::loop() {
// Long press BACK (1s+) goes directly to home
if (inputManager.isPressed(InputManager::BTN_BACK) && inputManager.getHeldTime() >= goHomeMs) {
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
onGoHome();
return;
}
// Short press BACK goes to file selection
if (inputManager.wasReleased(InputManager::BTN_BACK) && inputManager.getHeldTime() < goHomeMs) {
if (mappedInput.wasReleased(MappedInputManager::Button::Back) && mappedInput.getHeldTime() < goHomeMs) {
onGoBack();
return;
}
const bool prevReleased =
inputManager.wasReleased(InputManager::BTN_UP) || inputManager.wasReleased(InputManager::BTN_LEFT);
const bool nextReleased =
inputManager.wasReleased(InputManager::BTN_DOWN) || inputManager.wasReleased(InputManager::BTN_RIGHT);
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
mappedInput.wasReleased(MappedInputManager::Button::Left);
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
mappedInput.wasReleased(MappedInputManager::Button::Right);
if (!prevReleased && !nextReleased) {
return;
@ -98,7 +98,7 @@ void XtcReaderActivity::loop() {
return;
}
const bool skipPages = inputManager.getHeldTime() > skipPageMs;
const bool skipPages = mappedInput.getHeldTime() > skipPageMs;
const int skipAmount = skipPages ? 10 : 1;
if (prevReleased) {

View File

@ -32,9 +32,9 @@ class XtcReaderActivity final : public Activity {
void loadProgress();
public:
explicit XtcReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr<Xtc> xtc,
explicit XtcReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Xtc> xtc,
const std::function<void()>& onGoBack, const std::function<void()>& onGoHome)
: Activity("XtcReader", renderer, inputManager), xtc(std::move(xtc)), onGoBack(onGoBack), onGoHome(onGoHome) {}
: Activity("XtcReader", renderer, mappedInput), xtc(std::move(xtc)), onGoBack(onGoBack), onGoHome(onGoHome) {}
void onEnter() override;
void onExit() override;
void loop() override;

View File

@ -72,7 +72,7 @@ void OtaUpdateActivity::onEnter() {
// Launch WiFi selection subactivity
Serial.printf("[%lu] [OTA] Launching WifiSelectionActivity...\n", millis());
enterNewActivity(new WifiSelectionActivity(renderer, inputManager,
enterNewActivity(new WifiSelectionActivity(renderer, mappedInput,
[this](const bool connected) { onWifiSelectionComplete(connected); }));
}
@ -191,7 +191,7 @@ void OtaUpdateActivity::loop() {
}
if (state == WAITING_CONFIRMATION) {
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
Serial.printf("[%lu] [OTA] New update available, starting download...\n", millis());
xSemaphoreTake(renderingMutex, portMAX_DELAY);
state = UPDATE_IN_PROGRESS;
@ -215,7 +215,7 @@ void OtaUpdateActivity::loop() {
updateRequired = true;
}
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
goBack();
}
@ -223,14 +223,14 @@ void OtaUpdateActivity::loop() {
}
if (state == FAILED) {
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
goBack();
}
return;
}
if (state == NO_UPDATE) {
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
goBack();
}
return;

View File

@ -35,8 +35,9 @@ class OtaUpdateActivity : public ActivityWithSubactivity {
void render();
public:
explicit OtaUpdateActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function<void()>& goBack)
: ActivityWithSubactivity("OtaUpdate", renderer, inputManager), goBack(goBack), updater() {}
explicit OtaUpdateActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& goBack)
: ActivityWithSubactivity("OtaUpdate", renderer, mappedInput), goBack(goBack), updater() {}
void onEnter() override;
void onExit() override;
void loop() override;

View File

@ -9,7 +9,7 @@
// Define the static settings list
namespace {
constexpr int settingsCount = 6;
constexpr int settingsCount = 7;
const SettingInfo settingsList[settingsCount] = {
// Should match with SLEEP_SCREEN_MODE
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
@ -20,6 +20,10 @@ const SettingInfo settingsList[settingsCount] = {
SettingType::ENUM,
&CrossPointSettings::orientation,
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}},
{"Front Button Layout",
SettingType::ENUM,
&CrossPointSettings::frontButtonLayout,
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm"}},
{"Check for updates", SettingType::ACTION, nullptr, {}},
};
} // namespace
@ -68,24 +72,26 @@ void SettingsActivity::loop() {
}
// Handle actions with early return
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
toggleCurrentSetting();
updateRequired = true;
return;
}
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
SETTINGS.saveToFile();
onGoHome();
return;
}
// Handle navigation
if (inputManager.wasPressed(InputManager::BTN_UP) || inputManager.wasPressed(InputManager::BTN_LEFT)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
// Move selection up (with wrap-around)
selectedSettingIndex = (selectedSettingIndex > 0) ? (selectedSettingIndex - 1) : (settingsCount - 1);
updateRequired = true;
} else if (inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
// Move selection down
if (selectedSettingIndex < settingsCount - 1) {
selectedSettingIndex++;
@ -113,7 +119,7 @@ void SettingsActivity::toggleCurrentSetting() {
if (std::string(setting.name) == "Check for updates") {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new OtaUpdateActivity(renderer, inputManager, [this] {
enterNewActivity(new OtaUpdateActivity(renderer, mappedInput, [this] {
exitActivity();
updateRequired = true;
}));
@ -173,10 +179,13 @@ void SettingsActivity::render() const {
}
}
// Draw help text
renderer.drawButtonHints(UI_FONT_ID, "« Save", "Toggle", "", "");
// Draw version text above button hints
renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
pageHeight - 30, CROSSPOINT_VERSION);
pageHeight - 60, CROSSPOINT_VERSION);
// Draw help text
const auto labels = mappedInput.mapLabels("« Save", "Toggle", "", "");
renderer.drawButtonHints(UI_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
// Always use standard refresh for settings screen
renderer.displayBuffer();

View File

@ -34,8 +34,9 @@ class SettingsActivity final : public ActivityWithSubactivity {
void toggleCurrentSetting();
public:
explicit SettingsActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function<void()>& onGoHome)
: ActivityWithSubactivity("Settings", renderer, inputManager), onGoHome(onGoHome) {}
explicit SettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onGoHome)
: ActivityWithSubactivity("Settings", renderer, mappedInput), onGoHome(onGoHome) {}
void onEnter() override;
void onExit() override;
void loop() override;

View File

@ -13,10 +13,10 @@ class FullScreenMessageActivity final : public Activity {
EInkDisplay::RefreshMode refreshMode;
public:
explicit FullScreenMessageActivity(GfxRenderer& renderer, InputManager& inputManager, std::string text,
explicit FullScreenMessageActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string text,
const EpdFontStyle style = REGULAR,
const EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH)
: Activity("FullScreenMessage", renderer, inputManager),
: Activity("FullScreenMessage", renderer, mappedInput),
text(std::move(text)),
style(style),
refreshMode(refreshMode) {}

View File

@ -138,7 +138,7 @@ void KeyboardEntryActivity::handleKeyPress() {
void KeyboardEntryActivity::loop() {
// Navigation
if (inputManager.wasPressed(InputManager::BTN_UP)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up)) {
if (selectedRow > 0) {
selectedRow--;
// Clamp column to valid range for new row
@ -148,7 +148,7 @@ void KeyboardEntryActivity::loop() {
updateRequired = true;
}
if (inputManager.wasPressed(InputManager::BTN_DOWN)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Down)) {
if (selectedRow < NUM_ROWS - 1) {
selectedRow++;
const int maxCol = getRowLength(selectedRow) - 1;
@ -157,7 +157,7 @@ void KeyboardEntryActivity::loop() {
updateRequired = true;
}
if (inputManager.wasPressed(InputManager::BTN_LEFT)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
// Special bottom row case
if (selectedRow == SPECIAL_ROW) {
// Bottom row has special key widths
@ -187,7 +187,7 @@ void KeyboardEntryActivity::loop() {
updateRequired = true;
}
if (inputManager.wasPressed(InputManager::BTN_RIGHT)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
const int maxCol = getRowLength(selectedRow) - 1;
// Special bottom row case
@ -220,13 +220,13 @@ void KeyboardEntryActivity::loop() {
}
// Selection
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
handleKeyPress();
updateRequired = true;
}
// Cancel
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
if (onCancel) {
onCancel();
}

View File

@ -1,6 +1,5 @@
#pragma once
#include <GfxRenderer.h>
#include <InputManager.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
@ -31,7 +30,7 @@ class KeyboardEntryActivity : public Activity {
/**
* Constructor
* @param renderer Reference to the GfxRenderer for drawing
* @param inputManager Reference to InputManager for handling input
* @param mappedInput Reference to MappedInputManager for handling input
* @param title Title to display above the keyboard
* @param initialText Initial text to show in the input field
* @param startY Y position to start rendering the keyboard
@ -40,11 +39,11 @@ class KeyboardEntryActivity : public Activity {
* @param onComplete Callback invoked when input is complete
* @param onCancel Callback invoked when input is cancelled
*/
explicit KeyboardEntryActivity(GfxRenderer& renderer, InputManager& inputManager, std::string title = "Enter Text",
std::string initialText = "", const int startY = 10, const size_t maxLength = 0,
const bool isPassword = false, OnCompleteCallback onComplete = nullptr,
OnCancelCallback onCancel = nullptr)
: Activity("KeyboardEntry", renderer, inputManager),
explicit KeyboardEntryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
std::string title = "Enter Text", std::string initialText = "", const int startY = 10,
const size_t maxLength = 0, const bool isPassword = false,
OnCompleteCallback onComplete = nullptr, OnCancelCallback onCancel = nullptr)
: Activity("KeyboardEntry", renderer, mappedInput),
title(std::move(title)),
text(std::move(initialText)),
startY(startY),

View File

@ -16,6 +16,7 @@
#include "Battery.h"
#include "CrossPointSettings.h"
#include "CrossPointState.h"
#include "MappedInputManager.h"
#include "activities/boot_sleep/BootActivity.h"
#include "activities/boot_sleep/SleepActivity.h"
#include "activities/home/HomeActivity.h"
@ -41,6 +42,7 @@
EInkDisplay einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY);
InputManager inputManager;
MappedInputManager mappedInputManager(inputManager);
GfxRenderer renderer(einkDisplay);
Activity* currentActivity;
@ -124,7 +126,7 @@ void waitForPowerRelease() {
// Enter deep sleep mode
void enterDeepSleep() {
exitActivity();
enterNewActivity(new SleepActivity(renderer, inputManager));
enterNewActivity(new SleepActivity(renderer, mappedInputManager));
einkDisplay.deepSleep();
Serial.printf("[%lu] [ ] Power button press calibration value: %lu ms\n", millis(), t2 - t1);
@ -139,24 +141,24 @@ void enterDeepSleep() {
void onGoHome();
void onGoToReader(const std::string& initialEpubPath) {
exitActivity();
enterNewActivity(new ReaderActivity(renderer, inputManager, initialEpubPath, onGoHome));
enterNewActivity(new ReaderActivity(renderer, mappedInputManager, initialEpubPath, onGoHome));
}
void onGoToReaderHome() { onGoToReader(std::string()); }
void onContinueReading() { onGoToReader(APP_STATE.openEpubPath); }
void onGoToFileTransfer() {
exitActivity();
enterNewActivity(new CrossPointWebServerActivity(renderer, inputManager, onGoHome));
enterNewActivity(new CrossPointWebServerActivity(renderer, mappedInputManager, onGoHome));
}
void onGoToSettings() {
exitActivity();
enterNewActivity(new SettingsActivity(renderer, inputManager, onGoHome));
enterNewActivity(new SettingsActivity(renderer, mappedInputManager, onGoHome));
}
void onGoHome() {
exitActivity();
enterNewActivity(new HomeActivity(renderer, inputManager, onContinueReading, onGoToReaderHome, onGoToSettings,
enterNewActivity(new HomeActivity(renderer, mappedInputManager, onContinueReading, onGoToReaderHome, onGoToSettings,
onGoToFileTransfer));
}
@ -193,7 +195,7 @@ void setup() {
Serial.printf("[%lu] [ ] SD card initialization failed\n", millis());
setupDisplayAndFonts();
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "SD card error", BOLD));
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInputManager, "SD card error", BOLD));
return;
}
@ -205,7 +207,7 @@ void setup() {
setupDisplayAndFonts();
exitActivity();
enterNewActivity(new BootActivity(renderer, inputManager));
enterNewActivity(new BootActivity(renderer, mappedInputManager));
APP_STATE.loadFromFile();
if (APP_STATE.openEpubPath.empty()) {