Standardize KeyboardEntryActivity
This commit is contained in:
parent
a2676749cc
commit
16233a9ef6
@ -187,11 +187,21 @@ void WifiSelectionActivity::selectNetwork(const int index) {
|
|||||||
if (selectedRequiresPassword) {
|
if (selectedRequiresPassword) {
|
||||||
// Show password entry
|
// Show password entry
|
||||||
state = WifiSelectionState::PASSWORD_ENTRY;
|
state = WifiSelectionState::PASSWORD_ENTRY;
|
||||||
enterNewActivity(new KeyboardEntryActivity(renderer, inputManager, "Enter WiFi Password",
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
"", // No initial text
|
renderer, inputManager, "Enter WiFi Password",
|
||||||
64, // Max password length
|
"", // No initial text
|
||||||
false // Show password by default (hard keyboard to use)
|
50, // Y position
|
||||||
));
|
64, // Max password length
|
||||||
|
false, // Show password by default (hard keyboard to use)
|
||||||
|
[this](const std::string& text) {
|
||||||
|
enteredPassword = text;
|
||||||
|
exitActivity();
|
||||||
|
},
|
||||||
|
[this] {
|
||||||
|
state = WifiSelectionState::NETWORK_LIST;
|
||||||
|
updateRequired = true;
|
||||||
|
exitActivity();
|
||||||
|
}));
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else {
|
} else {
|
||||||
// Connect directly for open networks
|
// Connect directly for open networks
|
||||||
@ -208,11 +218,6 @@ void WifiSelectionActivity::attemptConnection() {
|
|||||||
|
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
// Get password from keyboard if we just entered it
|
|
||||||
if (subActivity && !usedSavedPassword) {
|
|
||||||
enteredPassword = static_cast<KeyboardEntryActivity*>(subActivity.get())->getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedRequiresPassword && !enteredPassword.empty()) {
|
if (selectedRequiresPassword && !enteredPassword.empty()) {
|
||||||
WiFi.begin(selectedSSID.c_str(), enteredPassword.c_str());
|
WiFi.begin(selectedSSID.c_str(), enteredPassword.c_str());
|
||||||
} else {
|
} else {
|
||||||
@ -269,6 +274,11 @@ void WifiSelectionActivity::checkConnectionStatus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WifiSelectionActivity::loop() {
|
void WifiSelectionActivity::loop() {
|
||||||
|
if (subActivity) {
|
||||||
|
subActivity->loop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check scan progress
|
// Check scan progress
|
||||||
if (state == WifiSelectionState::SCANNING) {
|
if (state == WifiSelectionState::SCANNING) {
|
||||||
processWifiScanResults();
|
processWifiScanResults();
|
||||||
@ -281,24 +291,9 @@ void WifiSelectionActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle password entry state
|
if (state == WifiSelectionState::PASSWORD_ENTRY) {
|
||||||
if (state == WifiSelectionState::PASSWORD_ENTRY && subActivity) {
|
// Reach here once password entry finished in subactivity
|
||||||
const auto keyboard = static_cast<KeyboardEntryActivity*>(subActivity.get());
|
attemptConnection();
|
||||||
keyboard->handleInput();
|
|
||||||
|
|
||||||
if (keyboard->isComplete()) {
|
|
||||||
attemptConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard->isCancelled()) {
|
|
||||||
state = WifiSelectionState::NETWORK_LIST;
|
|
||||||
exitActivity();
|
|
||||||
updateRequired = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateRequired = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,6 +436,10 @@ std::string WifiSelectionActivity::getSignalStrengthIndicator(const int32_t rssi
|
|||||||
|
|
||||||
void WifiSelectionActivity::displayTaskLoop() {
|
void WifiSelectionActivity::displayTaskLoop() {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (subActivity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (updateRequired) {
|
if (updateRequired) {
|
||||||
updateRequired = false;
|
updateRequired = false;
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
@ -461,9 +460,6 @@ void WifiSelectionActivity::render() const {
|
|||||||
case WifiSelectionState::NETWORK_LIST:
|
case WifiSelectionState::NETWORK_LIST:
|
||||||
renderNetworkList();
|
renderNetworkList();
|
||||||
break;
|
break;
|
||||||
case WifiSelectionState::PASSWORD_ENTRY:
|
|
||||||
renderPasswordEntry();
|
|
||||||
break;
|
|
||||||
case WifiSelectionState::CONNECTING:
|
case WifiSelectionState::CONNECTING:
|
||||||
renderConnecting();
|
renderConnecting();
|
||||||
break;
|
break;
|
||||||
@ -561,23 +557,6 @@ void WifiSelectionActivity::renderNetworkList() const {
|
|||||||
renderer.drawButtonHints(UI_FONT_ID, "« Back", "Connect", "", "");
|
renderer.drawButtonHints(UI_FONT_ID, "« Back", "Connect", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WifiSelectionActivity::renderPasswordEntry() const {
|
|
||||||
// Draw header
|
|
||||||
renderer.drawCenteredText(READER_FONT_ID, 5, "WiFi Password", true, BOLD);
|
|
||||||
|
|
||||||
// Draw network name with good spacing from header
|
|
||||||
std::string networkInfo = "Network: " + selectedSSID;
|
|
||||||
if (networkInfo.length() > 30) {
|
|
||||||
networkInfo.replace(27, networkInfo.length() - 27, "...");
|
|
||||||
}
|
|
||||||
renderer.drawCenteredText(UI_FONT_ID, 38, networkInfo.c_str(), true, REGULAR);
|
|
||||||
|
|
||||||
// Draw keyboard
|
|
||||||
if (subActivity) {
|
|
||||||
static_cast<KeyboardEntryActivity*>(subActivity.get())->render(58);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiSelectionActivity::renderConnecting() const {
|
void WifiSelectionActivity::renderConnecting() const {
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
const auto height = renderer.getLineHeight(UI_FONT_ID);
|
const auto height = renderer.getLineHeight(UI_FONT_ID);
|
||||||
|
|||||||
@ -12,36 +12,50 @@ const char* const KeyboardEntryActivity::keyboard[NUM_ROWS] = {
|
|||||||
const char* const KeyboardEntryActivity::keyboardShift[NUM_ROWS] = {"~!@#$%^&*()_+", "QWERTYUIOP{}|", "ASDFGHJKL:\"",
|
const char* const KeyboardEntryActivity::keyboardShift[NUM_ROWS] = {"~!@#$%^&*()_+", "QWERTYUIOP{}|", "ASDFGHJKL:\"",
|
||||||
"ZXCVBNM<>?", "SPECIAL ROW"};
|
"ZXCVBNM<>?", "SPECIAL ROW"};
|
||||||
|
|
||||||
void KeyboardEntryActivity::setText(const std::string& newText) {
|
void KeyboardEntryActivity::taskTrampoline(void* param) {
|
||||||
text = newText;
|
auto* self = static_cast<KeyboardEntryActivity*>(param);
|
||||||
if (maxLength > 0 && text.length() > maxLength) {
|
self->displayTaskLoop();
|
||||||
text.resize(maxLength);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardEntryActivity::reset(const std::string& newTitle, const std::string& newInitialText) {
|
void KeyboardEntryActivity::displayTaskLoop() {
|
||||||
if (!newTitle.empty()) {
|
while (true) {
|
||||||
title = newTitle;
|
if (updateRequired) {
|
||||||
|
updateRequired = false;
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
render();
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
}
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
text = newInitialText;
|
|
||||||
selectedRow = 0;
|
|
||||||
selectedCol = 0;
|
|
||||||
shiftActive = false;
|
|
||||||
complete = false;
|
|
||||||
cancelled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardEntryActivity::onEnter() {
|
void KeyboardEntryActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
|
|
||||||
// Reset state when entering the activity
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
complete = false;
|
|
||||||
cancelled = false;
|
// Trigger first update
|
||||||
|
updateRequired = true;
|
||||||
|
|
||||||
|
xTaskCreate(&KeyboardEntryActivity::taskTrampoline, "KeyboardEntryActivity",
|
||||||
|
2048, // Stack size
|
||||||
|
this, // Parameters
|
||||||
|
1, // Priority
|
||||||
|
&displayTaskHandle // Task handle
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardEntryActivity::loop() {
|
void KeyboardEntryActivity::onExit() {
|
||||||
handleInput();
|
Activity::onExit();
|
||||||
render(10);
|
|
||||||
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
if (displayTaskHandle) {
|
||||||
|
vTaskDelete(displayTaskHandle);
|
||||||
|
displayTaskHandle = nullptr;
|
||||||
|
}
|
||||||
|
vSemaphoreDelete(renderingMutex);
|
||||||
|
renderingMutex = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeyboardEntryActivity::getRowLength(const int row) const {
|
int KeyboardEntryActivity::getRowLength(const int row) const {
|
||||||
@ -100,7 +114,6 @@ void KeyboardEntryActivity::handleKeyPress() {
|
|||||||
|
|
||||||
if (selectedCol >= DONE_COL) {
|
if (selectedCol >= DONE_COL) {
|
||||||
// Done button
|
// Done button
|
||||||
complete = true;
|
|
||||||
if (onComplete) {
|
if (onComplete) {
|
||||||
onComplete(text);
|
onComplete(text);
|
||||||
}
|
}
|
||||||
@ -123,11 +136,7 @@ void KeyboardEntryActivity::handleKeyPress() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KeyboardEntryActivity::handleInput() {
|
void KeyboardEntryActivity::loop() {
|
||||||
if (complete || cancelled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
if (inputManager.wasPressed(InputManager::BTN_UP)) {
|
if (inputManager.wasPressed(InputManager::BTN_UP)) {
|
||||||
if (selectedRow > 0) {
|
if (selectedRow > 0) {
|
||||||
@ -136,7 +145,7 @@ bool KeyboardEntryActivity::handleInput() {
|
|||||||
const int maxCol = getRowLength(selectedRow) - 1;
|
const int maxCol = getRowLength(selectedRow) - 1;
|
||||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||||
}
|
}
|
||||||
return true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputManager.wasPressed(InputManager::BTN_DOWN)) {
|
if (inputManager.wasPressed(InputManager::BTN_DOWN)) {
|
||||||
@ -145,11 +154,10 @@ bool KeyboardEntryActivity::handleInput() {
|
|||||||
const int maxCol = getRowLength(selectedRow) - 1;
|
const int maxCol = getRowLength(selectedRow) - 1;
|
||||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||||
}
|
}
|
||||||
return true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputManager.wasPressed(InputManager::BTN_LEFT)) {
|
if (inputManager.wasPressed(InputManager::BTN_LEFT)) {
|
||||||
|
|
||||||
// Special bottom row case
|
// Special bottom row case
|
||||||
if (selectedRow == SPECIAL_ROW) {
|
if (selectedRow == SPECIAL_ROW) {
|
||||||
// Bottom row has special key widths
|
// Bottom row has special key widths
|
||||||
@ -165,7 +173,8 @@ bool KeyboardEntryActivity::handleInput() {
|
|||||||
// At done button, move to backspace
|
// At done button, move to backspace
|
||||||
selectedCol = BACKSPACE_COL;
|
selectedCol = BACKSPACE_COL;
|
||||||
}
|
}
|
||||||
return true;
|
updateRequired = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedCol > 0) {
|
if (selectedCol > 0) {
|
||||||
@ -175,7 +184,7 @@ bool KeyboardEntryActivity::handleInput() {
|
|||||||
selectedRow--;
|
selectedRow--;
|
||||||
selectedCol = getRowLength(selectedRow) - 1;
|
selectedCol = getRowLength(selectedRow) - 1;
|
||||||
}
|
}
|
||||||
return true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputManager.wasPressed(InputManager::BTN_RIGHT)) {
|
if (inputManager.wasPressed(InputManager::BTN_RIGHT)) {
|
||||||
@ -196,7 +205,8 @@ bool KeyboardEntryActivity::handleInput() {
|
|||||||
} else if (selectedCol >= DONE_COL) {
|
} else if (selectedCol >= DONE_COL) {
|
||||||
// At done button, do nothing
|
// At done button, do nothing
|
||||||
}
|
}
|
||||||
return true;
|
updateRequired = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedCol < maxCol) {
|
if (selectedCol < maxCol) {
|
||||||
@ -206,30 +216,29 @@ bool KeyboardEntryActivity::handleInput() {
|
|||||||
selectedRow++;
|
selectedRow++;
|
||||||
selectedCol = 0;
|
selectedCol = 0;
|
||||||
}
|
}
|
||||||
return true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selection
|
// Selection
|
||||||
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
|
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
|
||||||
handleKeyPress();
|
handleKeyPress();
|
||||||
return true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel
|
// Cancel
|
||||||
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
|
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
|
||||||
cancelled = true;
|
|
||||||
if (onCancel) {
|
if (onCancel) {
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
return true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardEntryActivity::render(const int startY) const {
|
void KeyboardEntryActivity::render() const {
|
||||||
const auto pageWidth = GfxRenderer::getScreenWidth();
|
const auto pageWidth = GfxRenderer::getScreenWidth();
|
||||||
|
|
||||||
|
renderer.clearScreen();
|
||||||
|
|
||||||
// Draw title
|
// Draw title
|
||||||
renderer.drawCenteredText(UI_FONT_ID, startY, title.c_str(), true, REGULAR);
|
renderer.drawCenteredText(UI_FONT_ID, startY, title.c_str(), true, REGULAR);
|
||||||
|
|
||||||
@ -322,6 +331,7 @@ void KeyboardEntryActivity::render(const int startY) const {
|
|||||||
// Draw help text at absolute bottom of screen (consistent with other screens)
|
// Draw help text at absolute bottom of screen (consistent with other screens)
|
||||||
const auto pageHeight = GfxRenderer::getScreenHeight();
|
const auto pageHeight = GfxRenderer::getScreenHeight();
|
||||||
renderer.drawText(SMALL_FONT_ID, 10, pageHeight - 30, "Navigate: D-pad | Select: OK | Cancel: BACK");
|
renderer.drawText(SMALL_FONT_ID, 10, pageHeight - 30, "Navigate: D-pad | Select: OK | Cancel: BACK");
|
||||||
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardEntryActivity::renderItemWithSelector(const int x, const int y, const char* item,
|
void KeyboardEntryActivity::renderItemWithSelector(const int x, const int y, const char* item,
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <InputManager.h>
|
#include <InputManager.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "../Activity.h"
|
#include "../Activity.h"
|
||||||
|
|
||||||
@ -30,80 +34,44 @@ class KeyboardEntryActivity : public Activity {
|
|||||||
* @param inputManager Reference to InputManager for handling input
|
* @param inputManager Reference to InputManager for handling input
|
||||||
* @param title Title to display above the keyboard
|
* @param title Title to display above the keyboard
|
||||||
* @param initialText Initial text to show in the input field
|
* @param initialText Initial text to show in the input field
|
||||||
|
* @param startY Y position to start rendering the keyboard
|
||||||
* @param maxLength Maximum length of input text (0 for unlimited)
|
* @param maxLength Maximum length of input text (0 for unlimited)
|
||||||
* @param isPassword If true, display asterisks instead of actual characters
|
* @param isPassword If true, display asterisks instead of actual characters
|
||||||
|
* @param onComplete Callback invoked when input is complete
|
||||||
|
* @param onCancel Callback invoked when input is cancelled
|
||||||
*/
|
*/
|
||||||
KeyboardEntryActivity(GfxRenderer& renderer, InputManager& inputManager, const std::string& title = "Enter Text",
|
explicit KeyboardEntryActivity(GfxRenderer& renderer, InputManager& inputManager, std::string title = "Enter Text",
|
||||||
const std::string& initialText = "", const size_t maxLength = 0, const bool isPassword = false)
|
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),
|
: Activity("KeyboardEntry", renderer, inputManager),
|
||||||
title(title),
|
title(std::move(title)),
|
||||||
text(initialText),
|
text(std::move(initialText)),
|
||||||
|
startY(startY),
|
||||||
maxLength(maxLength),
|
maxLength(maxLength),
|
||||||
isPassword(isPassword) {}
|
isPassword(isPassword),
|
||||||
|
onComplete(std::move(onComplete)),
|
||||||
/**
|
onCancel(std::move(onCancel)) {}
|
||||||
* Handle button input. Call this in your main loop.
|
|
||||||
* @return true if input was handled, false otherwise
|
|
||||||
*/
|
|
||||||
bool handleInput();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the keyboard at the specified Y position.
|
|
||||||
* @param startY Y-coordinate where keyboard rendering starts (default 10)
|
|
||||||
*/
|
|
||||||
void render(int startY = 10) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current text entered by the user.
|
|
||||||
*/
|
|
||||||
const std::string& getText() const { return text; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current text.
|
|
||||||
*/
|
|
||||||
void setText(const std::string& newText);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the user has completed text entry (pressed OK on Done).
|
|
||||||
*/
|
|
||||||
bool isComplete() const { return complete; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the user has cancelled text entry.
|
|
||||||
*/
|
|
||||||
bool isCancelled() const { return cancelled; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the keyboard state for reuse.
|
|
||||||
*/
|
|
||||||
void reset(const std::string& newTitle = "", const std::string& newInitialText = "");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set callback for when input is complete.
|
|
||||||
*/
|
|
||||||
void setOnComplete(OnCompleteCallback callback) { onComplete = callback; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set callback for when input is cancelled.
|
|
||||||
*/
|
|
||||||
void setOnCancel(OnCancelCallback callback) { onCancel = callback; }
|
|
||||||
|
|
||||||
// Activity overrides
|
// Activity overrides
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
void onExit() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string title;
|
std::string title;
|
||||||
|
int startY;
|
||||||
std::string text;
|
std::string text;
|
||||||
size_t maxLength;
|
size_t maxLength;
|
||||||
bool isPassword;
|
bool isPassword;
|
||||||
|
TaskHandle_t displayTaskHandle = nullptr;
|
||||||
|
SemaphoreHandle_t renderingMutex = nullptr;
|
||||||
|
bool updateRequired = false;
|
||||||
|
|
||||||
// Keyboard state
|
// Keyboard state
|
||||||
int selectedRow = 0;
|
int selectedRow = 0;
|
||||||
int selectedCol = 0;
|
int selectedCol = 0;
|
||||||
bool shiftActive = false;
|
bool shiftActive = false;
|
||||||
bool complete = false;
|
|
||||||
bool cancelled = false;
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
OnCompleteCallback onComplete;
|
OnCompleteCallback onComplete;
|
||||||
@ -122,8 +90,11 @@ class KeyboardEntryActivity : public Activity {
|
|||||||
static constexpr int BACKSPACE_COL = 7;
|
static constexpr int BACKSPACE_COL = 7;
|
||||||
static constexpr int DONE_COL = 9;
|
static constexpr int DONE_COL = 9;
|
||||||
|
|
||||||
|
static void taskTrampoline(void* param);
|
||||||
|
[[noreturn]] void displayTaskLoop();
|
||||||
char getSelectedChar() const;
|
char getSelectedChar() const;
|
||||||
void handleKeyPress();
|
void handleKeyPress();
|
||||||
int getRowLength(int row) const;
|
int getRowLength(int row) const;
|
||||||
|
void render() const;
|
||||||
void renderItemWithSelector(int x, int y, const char* item, bool isSelected) const;
|
void renderItemWithSelector(int x, int y, const char* item, bool isSelected) const;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user