Files
radiantwave/src/settings.cpp
T

281 lines
8.1 KiB
C++

#include "settings.h"
#include "app_state.h"
#include "home.h"
#include "license_blocked_page.h"
#include "wifi_setup_page.h"
#include "audio_settings_page.h"
#include "timezone_page.h"
#include "log_viewer_page.h"
#include "proclamation_options_page.h"
#include "email_address_page.h"
#include "license_key_page.h"
#include "date_format_page.h"
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <algorithm>
namespace
{
constexpr int MENU_ITEM_COUNT = 9;
// 0 = WiFi Setup, 1 = Audio Options, 2 = Time Zone Setup, 3 = Proclamations,
// 4 = Email Address, 5 = License Key, 6 = Date Format,
// 7 = View System Logs, 8 = Back
constexpr const char *MENU_LABELS[MENU_ITEM_COUNT] = {
"WiFi Setup",
"Audio Options",
"Time Zone Setup",
"Proclamations",
"Email Address",
"License Key",
"Date Format",
"View System Logs",
"Back to Home",
};
} // anonymous namespace
void Settings::onPush(AppState &state)
{
state_ = &state;
TTF_Init();
createTextures(state);
state.keybinds.bind(SDLK_UP, "menu_up", [this]()
{ selectedItem_ = (selectedItem_ - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT; });
state.keybinds.bind(SDLK_DOWN, "menu_down", [this]()
{ selectedItem_ = (selectedItem_ + 1) % MENU_ITEM_COUNT; });
}
void Settings::onResume(AppState &state)
{
state_ = &state;
createTextures(state);
state.keybinds.bind(SDLK_UP, "menu_up", [this]()
{ selectedItem_ = (selectedItem_ - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT; });
state.keybinds.bind(SDLK_DOWN, "menu_down", [this]()
{ selectedItem_ = (selectedItem_ + 1) % MENU_ITEM_COUNT; });
}
void Settings::onPause(AppState &state)
{
state.keybinds.unbindAll();
destroyTextures();
}
void Settings::onPop(AppState &state)
{
destroyTextures();
TTF_Quit();
}
void Settings::handleEvent(const SDL_Event &event)
{
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_RETURN)
{
switch (selectedItem_)
{
case 0:
state_->pages.push(*state_, std::make_unique<WiFiSetupPage>());
break;
case 1:
state_->pages.push(*state_, std::make_unique<AudioSettingsPage>());
break;
case 2:
state_->pages.push(*state_, std::make_unique<TimeZonePage>());
break;
case 3:
// The Proclamation list is license-gated like Play.
if (state_->licenseTracker.isValid())
{
state_->pages.push(*state_, std::make_unique<ProclamationOptionsPage>());
}
else
{
state_->pages.push(*state_, std::make_unique<LicenseBlockedPage>());
}
break;
case 4:
state_->pages.push(*state_, std::make_unique<EmailAddressPage>());
break;
case 5:
state_->pages.push(*state_, std::make_unique<LicenseKeyPage>());
break;
case 6:
state_->pages.push(*state_, std::make_unique<DateFormatPage>());
break;
case 7:
state_->pages.push(*state_, std::make_unique<LogViewerPage>());
break;
case 8:
state_->pages.replace(*state_, std::make_unique<Home>());
break;
default:
break;
}
}
else if (event.type == SDL_EVENT_KEY_DOWN && (event.key.key == SDLK_ESCAPE || event.key.key == SDLK_TAB))
{
state_->pages.replace(*state_, std::make_unique<Home>());
}
}
void Settings::update(float dt)
{
}
void Settings::render(SDL_Renderer *renderer)
{
int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH);
float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH);
// 1. Logo Background Image
if (bgTexture_)
{
float scaleX = fw / bgWidth_;
float scaleY = fh / bgHeight_;
float scale = std::min(scaleX, scaleY);
float dw = bgWidth_ * scale;
float dh = bgHeight_ * scale;
float dx = (fw - dw) / 2.0f;
float dy = (fh - dh) / 2.0f;
SDL_FRect bgDst{dx, dy, dw, dh};
SDL_RenderTexture(renderer, bgTexture_, nullptr, &bgDst);
}
// 2. Dark Overlay
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 160);
SDL_FRect overlayRect{0, 0, fw, fh};
SDL_RenderFillRect(renderer, &overlayRect);
// 3. Title Text
float titleY = 60.0f;
if (titleTexture_)
{
float x = (fw - titleWidth_) / 2.0f;
SDL_FRect dst{x, titleY, titleWidth_, titleHeight_};
SDL_RenderTexture(renderer, titleTexture_, nullptr, &dst);
}
// Compute bounding box for all menu items
float maxWidth = 0.0f;
for (const auto &item : items_)
{
maxWidth = std::max(maxWidth, item.width);
}
float margin = 12.0f; // 12px vertical margin rule
// Shared left edge for left-aligned items with centered bounding box
float sharedX = (fw - maxWidth) / 2.0f;
// Start menu list exactly 24 pixels below title text
float currentY = titleY + titleHeight_ + 24.0f;
for (auto &item : items_)
{
item.y = currentY;
if (item.texture)
{
SDL_FRect dst{sharedX, item.y, item.width, item.height};
SDL_RenderTexture(renderer, item.texture, nullptr, &dst);
}
currentY += item.height + margin;
}
// 6px Underline for selected item occupying half of the 12px margin space
if (selectedItem_ >= 0 && selectedItem_ < static_cast<int>(items_.size()))
{
const auto &item = items_[selectedItem_];
if (item.texture)
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_FRect line{sharedX, item.y + item.height + 2.0f, item.width, 6.0f};
SDL_RenderFillRect(renderer, &line);
}
}
}
void Settings::createTextures(AppState &state)
{
destroyTextures();
// Background Image
SDL_Surface *bgSurface = IMG_Load("assets/images/RWT-Circular-Color-ForOnDark-NoText.png");
if (bgSurface)
{
bgTexture_ = SDL_CreateTextureFromSurface(state.renderer, bgSurface);
bgWidth_ = static_cast<float>(bgSurface->w);
bgHeight_ = static_cast<float>(bgSurface->h);
SDL_DestroySurface(bgSurface);
SDL_SetTextureAlphaMod(bgTexture_, 204);
}
SDL_Color white = {255, 255, 255, 255};
// Title Font
TTF_Font *fontLarge = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 64);
if (fontLarge)
{
SDL_Surface *surface = TTF_RenderText_Blended(fontLarge, "Settings", 0, white);
if (surface)
{
titleTexture_ = SDL_CreateTextureFromSurface(state.renderer, surface);
titleWidth_ = static_cast<float>(surface->w);
titleHeight_ = static_cast<float>(surface->h);
SDL_DestroySurface(surface);
}
TTF_CloseFont(fontLarge);
}
// Item Font
TTF_Font *fontItem = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 36);
if (!fontItem)
return;
items_.reserve(MENU_ITEM_COUNT);
for (int i = 0; i < MENU_ITEM_COUNT; ++i)
{
MenuItem item;
item.label = MENU_LABELS[i];
SDL_Surface *surface = TTF_RenderText_Blended(fontItem, MENU_LABELS[i], 0, white);
if (surface)
{
item.texture = SDL_CreateTextureFromSurface(state.renderer, surface);
item.width = static_cast<float>(surface->w);
item.height = static_cast<float>(surface->h);
SDL_DestroySurface(surface);
}
items_.push_back(std::move(item));
}
TTF_CloseFont(fontItem);
}
void Settings::destroyTextures()
{
if (bgTexture_)
{
SDL_DestroyTexture(bgTexture_);
bgTexture_ = nullptr;
}
if (titleTexture_)
{
SDL_DestroyTexture(titleTexture_);
titleTexture_ = nullptr;
}
for (auto &item : items_)
{
if (item.texture)
{
SDL_DestroyTexture(item.texture);
item.texture = nullptr;
}
}
items_.clear();
}