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
35 changed files with 285 additions and 140 deletions

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),