Files
radiantwave/src/system_name_page.cpp
T
NeilClack 1bfe64580c Kiosk: System Name entry in System Settings
Adds a System Name text-entry page (hint 'e.g. Living Room', 1-40 char
validation) and a data-driven System Settings row for it. Saving persists
locally and nudges the background sync; conflict/invalid results surface
the message and restore the previous name. Extends the settings-menu
tests.
2026-09-29 09:58:08 -05:00

227 lines
6.6 KiB
C++

#include "system_name_page.h"
#include "app_state.h"
#include "network/system_name.h"
#include "logical_size.h"
#include <SDL3/SDL.h>
namespace {
constexpr size_t MAX_INPUT_LENGTH = 80;
} // anonymous namespace
void SystemNamePage::onPush(AppState& state)
{
state_ = &state;
TTF_Init();
createFonts();
storedValue_ = radiantwave::systemname::storedName(state.db);
input_ = storedValue_;
statusMessage_ = "Enter a System Name, then press ENTER";
statusIsError_ = false;
if (state.window) {
SDL_StartTextInput(state.window);
}
}
void SystemNamePage::onResume(AppState& state)
{
state_ = &state;
createFonts();
storedValue_ = radiantwave::systemname::storedName(state.db);
input_ = storedValue_;
if (state.window) {
SDL_StartTextInput(state.window);
}
}
void SystemNamePage::onPause(AppState& state)
{
state.keybinds.unbindAll();
if (state.window) {
SDL_StopTextInput(state.window);
}
destroyFonts();
destroyTextures();
}
void SystemNamePage::onPop(AppState& state)
{
if (state.window) {
SDL_StopTextInput(state.window);
}
destroyFonts();
destroyTextures();
TTF_Quit();
}
void SystemNamePage::createFonts()
{
destroyFonts();
fontTitle_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 52);
fontHeader_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 28);
fontBody_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 32);
fontSmall_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 22);
}
void SystemNamePage::destroyFonts()
{
if (fontTitle_) { TTF_CloseFont(fontTitle_); fontTitle_ = nullptr; }
if (fontHeader_) { TTF_CloseFont(fontHeader_); fontHeader_ = nullptr; }
if (fontBody_) { TTF_CloseFont(fontBody_); fontBody_ = nullptr; }
if (fontSmall_) { TTF_CloseFont(fontSmall_); fontSmall_ = nullptr; }
}
void SystemNamePage::destroyTextures()
{
titleText_.destroy();
storedText_.destroy();
valueText_.destroy();
statusText_.destroy();
hintText_.destroy();
}
void SystemNamePage::submit()
{
if (!state_) return;
// Empty/whitespace is invalid and never clears the stored name.
std::string error;
if (!radiantwave::systemname::storeLocalName(state_->db, input_, &error)) {
statusMessage_ = error;
statusIsError_ = true;
return;
}
storedValue_ = radiantwave::systemname::storedName(state_->db);
input_ = storedValue_;
statusMessage_ = "Saved. Syncing with the portal...";
statusIsError_ = false;
// Never block the UI: the background service performs the PUT and retries.
state_->systemNameSync.requestSync();
}
void SystemNamePage::pollSyncResult()
{
if (!state_) return;
const std::string message = radiantwave::systemname::consumeMessage(state_->db);
if (message.empty()) return;
statusMessage_ = message;
statusIsError_ = true;
storedValue_ = radiantwave::systemname::storedName(state_->db);
input_ = storedValue_;
}
void SystemNamePage::handleEvent(const SDL_Event& event)
{
if (!state_) return;
if (event.type == SDL_EVENT_TEXT_INPUT) {
if (input_.size() < MAX_INPUT_LENGTH) {
input_ += event.text.text;
}
return;
}
if (event.type == SDL_EVENT_KEY_DOWN) {
SDL_Keycode key = event.key.key;
if (key == SDLK_BACKSPACE) {
if (!input_.empty()) {
input_.pop_back();
}
} else if (key == SDLK_RETURN || key == SDLK_KP_ENTER) {
submit();
} else if (key == SDLK_ESCAPE) {
state_->pages.pop(*state_);
}
}
}
void SystemNamePage::update(float dt)
{
pollSyncResult();
}
void SystemNamePage::render(SDL_Renderer* renderer)
{
int winW, winH;
radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 20, 20, 30, 255);
SDL_FRect bgRect{0, 0, fw, fh};
SDL_RenderFillRect(renderer, &bgRect);
if (!fontTitle_ || !fontHeader_ || !fontBody_ || !fontSmall_) return;
SDL_Color white = {255, 255, 255, 255};
SDL_Color yellow = {255, 255, 0, 255};
SDL_Color cyan = {100, 220, 255, 255};
SDL_Color gray = {180, 180, 180, 255};
SDL_Color red = {240, 100, 100, 255};
float currentY = 30.0f;
float boxW = 700.0f;
float boxH = 70.0f;
float boxX = (fw - boxW) / 2.0f;
titleText_.ensure(renderer, fontTitle_, "System Name", white);
if (titleText_.texture) {
float x = (fw - titleText_.width) / 2.0f;
SDL_FRect dst{x, currentY, titleText_.width, titleText_.height};
SDL_RenderTexture(renderer, titleText_.texture, nullptr, &dst);
currentY += titleText_.height + 30.0f;
}
std::string storedStr = storedValue_.empty() ? "Not set" : storedValue_;
storedText_.ensure(renderer, fontHeader_, "Saved: " + storedStr,
storedValue_.empty() ? gray : yellow);
if (storedText_.texture) {
SDL_FRect dst{boxX, currentY, storedText_.width, storedText_.height};
SDL_RenderTexture(renderer, storedText_.texture, nullptr, &dst);
}
currentY += 50.0f;
SDL_SetRenderDrawColor(renderer, 35, 40, 60, 255);
SDL_FRect boxRect{boxX, currentY, boxW, boxH};
SDL_RenderFillRect(renderer, &boxRect);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderRect(renderer, &boxRect);
std::string display = input_ + "_";
valueText_.ensure(renderer, fontBody_, display, cyan);
if (valueText_.texture) {
SDL_FRect dst{boxX + 20.0f, currentY + (boxH - valueText_.height) / 2.0f,
valueText_.width, valueText_.height};
SDL_RenderTexture(renderer, valueText_.texture, nullptr, &dst);
}
currentY += boxH + 30.0f;
if (!statusMessage_.empty()) {
statusText_.ensure(renderer, fontHeader_, statusMessage_,
statusIsError_ ? red : gray);
if (statusText_.texture) {
SDL_FRect dst{boxX, currentY, statusText_.width, statusText_.height};
SDL_RenderTexture(renderer, statusText_.texture, nullptr, &dst);
}
currentY += 50.0f;
}
hintText_.ensure(renderer, fontSmall_,
"e.g. Living Room [Type] Enter [ENTER] Save [BACKSPACE] Delete [ESC] Back", gray);
if (hintText_.texture) {
float x = (fw - hintText_.width) / 2.0f;
SDL_FRect dst{x, fh - 40.0f, hintText_.width, hintText_.height};
SDL_RenderTexture(renderer, hintText_.texture, nullptr, &dst);
}
}