Add home screen (#42)
Some checks are pending
CI / build (push) Waiting to run

## Summary

* Add home screen
* Sits as new root screen, allows for navigation to settings or file
list
This commit is contained in:
Dave Allie 2025-12-17 20:47:43 +11:00 committed by GitHub
parent 973d372521
commit 11f01d3a41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 151 additions and 15 deletions

View File

@ -21,6 +21,7 @@
#include "screens/EpubReaderScreen.h"
#include "screens/FileSelectionScreen.h"
#include "screens/FullScreenMessageScreen.h"
#include "screens/HomeScreen.h"
#include "screens/SettingsScreen.h"
#include "screens/SleepScreen.h"
@ -150,6 +151,7 @@ void enterDeepSleep() {
}
void onGoHome();
void onGoToFileSelection();
void onSelectEpubFile(const std::string& path) {
exitScreen();
enterNewScreen(new FullScreenMessageScreen(renderer, inputManager, "Loading..."));
@ -159,16 +161,21 @@ void onSelectEpubFile(const std::string& path) {
appState.openEpubPath = path;
appState.saveToFile();
exitScreen();
enterNewScreen(new EpubReaderScreen(renderer, inputManager, std::move(epub), onGoHome));
enterNewScreen(new EpubReaderScreen(renderer, inputManager, std::move(epub), onGoToFileSelection));
} else {
exitScreen();
enterNewScreen(
new FullScreenMessageScreen(renderer, inputManager, "Failed to load epub", REGULAR, EInkDisplay::HALF_REFRESH));
delay(2000);
onGoHome();
onGoToFileSelection();
}
}
void onGoToFileSelection() {
exitScreen();
enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile, onGoHome));
}
void onGoToSettings() {
exitScreen();
enterNewScreen(new SettingsScreen(renderer, inputManager, onGoHome));
@ -176,7 +183,7 @@ void onGoToSettings() {
void onGoHome() {
exitScreen();
enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile, onGoToSettings));
enterNewScreen(new HomeScreen(renderer, inputManager, onGoToFileSelection, onGoToSettings));
}
void setup() {
@ -221,8 +228,7 @@ void setup() {
}
}
exitScreen();
enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile, onGoToSettings));
onGoHome();
// Ensure we're not still holding the power button before leaving setup
waitForPowerRelease();

View File

@ -98,7 +98,7 @@ void EpubReaderScreen::handleInput() {
}
if (inputManager.wasPressed(InputManager::BTN_BACK)) {
onGoHome();
onGoBack();
return;
}

View File

@ -17,7 +17,7 @@ class EpubReaderScreen final : public Screen {
int nextPageNumber = 0;
int pagesUntilFullRefresh = 0;
bool updateRequired = false;
const std::function<void()> onGoHome;
const std::function<void()> onGoBack;
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
@ -27,8 +27,8 @@ class EpubReaderScreen final : public Screen {
public:
explicit EpubReaderScreen(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr<Epub> epub,
const std::function<void()>& onGoHome)
: Screen(renderer, inputManager), epub(std::move(epub)), onGoHome(onGoHome) {}
const std::function<void()>& onGoBack)
: Screen(renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {}
void onEnter() override;
void onExit() override;
void handleInput() override;

View File

@ -98,8 +98,8 @@ void FileSelectionScreen::handleInput() {
loadFiles();
updateRequired = true;
} else {
// At root level, go to settings
onSettingsOpen();
// At root level, go back home
onGoHome();
}
} else if (prevPressed) {
selectorIndex = (selectorIndex + files.size() - 1) % files.size();
@ -129,7 +129,7 @@ void FileSelectionScreen::render() const {
renderer.drawCenteredText(READER_FONT_ID, 10, "CrossPoint Reader", true, BOLD);
// Help text
renderer.drawText(SMALL_FONT_ID, 20, GfxRenderer::getScreenHeight() - 30, "Press BACK for Settings");
renderer.drawText(SMALL_FONT_ID, 20, GfxRenderer::getScreenHeight() - 30, "Press BACK for Home");
if (files.empty()) {
renderer.drawText(UI_FONT_ID, 20, 60, "No EPUBs found");

View File

@ -17,7 +17,7 @@ class FileSelectionScreen final : public Screen {
int selectorIndex = 0;
bool updateRequired = false;
const std::function<void(const std::string&)> onSelect;
const std::function<void()> onSettingsOpen;
const std::function<void()> onGoHome;
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
@ -27,8 +27,8 @@ class FileSelectionScreen final : public Screen {
public:
explicit FileSelectionScreen(GfxRenderer& renderer, InputManager& inputManager,
const std::function<void(const std::string&)>& onSelect,
const std::function<void()>& onSettingsOpen)
: Screen(renderer, inputManager), onSelect(onSelect), onSettingsOpen(onSettingsOpen) {}
const std::function<void()>& onGoHome)
: Screen(renderer, inputManager), onSelect(onSelect), onGoHome(onGoHome) {}
void onEnter() override;
void onExit() override;
void handleInput() override;

View File

@ -0,0 +1,99 @@
#include "HomeScreen.h"
#include <GfxRenderer.h>
#include <SD.h>
#include "config.h"
void HomeScreen::taskTrampoline(void* param) {
auto* self = static_cast<HomeScreen*>(param);
self->displayTaskLoop();
}
void HomeScreen::onEnter() {
renderingMutex = xSemaphoreCreateMutex();
selectorIndex = 0;
// Trigger first update
updateRequired = true;
xTaskCreate(&HomeScreen::taskTrampoline, "HomeScreenTask",
2048, // Stack size
this, // Parameters
1, // Priority
&displayTaskHandle // Task handle
);
}
void HomeScreen::onExit() {
// 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;
}
void HomeScreen::handleInput() {
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);
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (selectorIndex == 0) {
onFileSelectionOpen();
} else if (selectorIndex == 1) {
onSettingsOpen();
}
} else if (prevPressed) {
selectorIndex = (selectorIndex + menuItemCount - 1) % menuItemCount;
updateRequired = true;
} else if (nextPressed) {
selectorIndex = (selectorIndex + 1) % menuItemCount;
updateRequired = true;
}
}
void HomeScreen::displayTaskLoop() {
while (true) {
if (updateRequired) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
render();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void HomeScreen::render() const {
renderer.clearScreen();
const auto pageWidth = GfxRenderer::getScreenWidth();
const auto pageHeight = GfxRenderer::getScreenHeight();
renderer.drawCenteredText(READER_FONT_ID, 10, "CrossPoint Reader", true, BOLD);
// Draw selection
renderer.fillRect(0, 60 + selectorIndex * 30 + 2, pageWidth - 1, 30);
renderer.drawText(UI_FONT_ID, 20, 60, "Read", selectorIndex != 0);
renderer.drawText(UI_FONT_ID, 20, 90, "Settings", selectorIndex != 1);
renderer.drawRect(25, pageHeight - 40, 106, 40);
renderer.drawText(UI_FONT_ID, 25 + (105 - renderer.getTextWidth(UI_FONT_ID, "Back")) / 2, pageHeight - 35, "Back");
renderer.drawRect(130, pageHeight - 40, 106, 40);
renderer.drawText(UI_FONT_ID, 130 + (105 - renderer.getTextWidth(UI_FONT_ID, "Confirm")) / 2, pageHeight - 35,
"Confirm");
renderer.drawRect(245, pageHeight - 40, 106, 40);
renderer.drawText(UI_FONT_ID, 245 + (105 - renderer.getTextWidth(UI_FONT_ID, "Left")) / 2, pageHeight - 35, "Left");
renderer.drawRect(350, pageHeight - 40, 106, 40);
renderer.drawText(UI_FONT_ID, 350 + (105 - renderer.getTextWidth(UI_FONT_ID, "Right")) / 2, pageHeight - 35, "Right");
renderer.displayBuffer();
}

31
src/screens/HomeScreen.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include "Screen.h"
class HomeScreen final : public Screen {
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
int selectorIndex = 0;
bool updateRequired = false;
const std::function<void()> onFileSelectionOpen;
const std::function<void()> onSettingsOpen;
static constexpr int menuItemCount = 2;
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void render() const;
public:
explicit HomeScreen(GfxRenderer& renderer, InputManager& inputManager,
const std::function<void()>& onFileSelectionOpen, const std::function<void()>& onSettingsOpen)
: Screen(renderer, inputManager), onFileSelectionOpen(onFileSelectionOpen), onSettingsOpen(onSettingsOpen) {}
void onEnter() override;
void onExit() override;
void handleInput() override;
};