Change menu text from gray to white

This commit is contained in:
NeilClack
2026-08-13 14:34:49 -05:00
parent c35f928c85
commit 4abcfa473f
2 changed files with 249 additions and 151 deletions
+123 -71
View File
@@ -16,7 +16,7 @@
#include <algorithm> #include <algorithm>
#ifndef APP_VERSION #ifndef APP_VERSION
#define APP_VERSION "unknown" #define APP_VERSION "dev"
#endif #endif
static TextLine renderText(SDL_Renderer *renderer, TTF_Font *font, const char *text, SDL_Color color) static TextLine renderText(SDL_Renderer *renderer, TTF_Font *font, const char *text, SDL_Color color)
@@ -47,12 +47,10 @@ void Home::onPush(AppState &state)
refreshStatus(); refreshStatus();
state.licenseTracker.refresh(); state.licenseTracker.refresh();
state.keybinds.bind(SDLK_UP, "menu_up", [this]() { state.keybinds.bind(SDLK_UP, "menu_up", [this]()
state_->currentMenuItem = (state_->currentMenuItem + 1) % 2; { state_->currentMenuItem = (state_->currentMenuItem + 1) % 2; });
}); state.keybinds.bind(SDLK_DOWN, "menu_down", [this]()
state.keybinds.bind(SDLK_DOWN, "menu_down", [this]() { { state_->currentMenuItem = (state_->currentMenuItem + 1) % 2; });
state_->currentMenuItem = (state_->currentMenuItem + 1) % 2;
});
} }
void Home::onResume(AppState &state) void Home::onResume(AppState &state)
@@ -63,12 +61,10 @@ void Home::onResume(AppState &state)
refreshStatus(); refreshStatus();
state.licenseTracker.refresh(); state.licenseTracker.refresh();
state.keybinds.bind(SDLK_UP, "menu_up", [this]() { state.keybinds.bind(SDLK_UP, "menu_up", [this]()
state_->currentMenuItem = (state_->currentMenuItem + 1) % 2; { state_->currentMenuItem = (state_->currentMenuItem + 1) % 2; });
}); state.keybinds.bind(SDLK_DOWN, "menu_down", [this]()
state.keybinds.bind(SDLK_DOWN, "menu_down", [this]() { { state_->currentMenuItem = (state_->currentMenuItem + 1) % 2; });
state_->currentMenuItem = (state_->currentMenuItem + 1) % 2;
});
} }
void Home::onPause(AppState &state) void Home::onPause(AppState &state)
@@ -85,15 +81,22 @@ void Home::onPop(AppState &state)
void Home::handleEvent(const SDL_Event &event) void Home::handleEvent(const SDL_Event &event)
{ {
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_RETURN) { if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_RETURN)
if (state_->currentMenuItem == 0) { {
if (state_->currentMenuItem == 0)
{
// Only a confirmed-valid license unlocks the scroller. // Only a confirmed-valid license unlocks the scroller.
if (state_->licenseTracker.isValid()) { if (state_->licenseTracker.isValid())
{
state_->pages.push(*state_, std::make_unique<ScrollerPage>()); state_->pages.push(*state_, std::make_unique<ScrollerPage>());
} else { }
else
{
state_->pages.push(*state_, std::make_unique<LicenseBlockedPage>()); state_->pages.push(*state_, std::make_unique<LicenseBlockedPage>());
} }
} else if (state_->currentMenuItem == 1) { }
else if (state_->currentMenuItem == 1)
{
state_->pages.replace(*state_, std::make_unique<Settings>()); state_->pages.replace(*state_, std::make_unique<Settings>());
} }
} }
@@ -102,7 +105,8 @@ void Home::handleEvent(const SDL_Event &event)
void Home::update(float dt) void Home::update(float dt)
{ {
internetRefreshTimer_ -= dt; internetRefreshTimer_ -= dt;
if (internetRefreshTimer_ <= 0.0f) { if (internetRefreshTimer_ <= 0.0f)
{
internetRefreshTimer_ = 5.0f; internetRefreshTimer_ = 5.0f;
refreshStatus(); refreshStatus();
} }
@@ -133,7 +137,8 @@ void Home::render(SDL_Renderer *renderer)
SDL_FRect overlay{0, 0, fw, fh}; SDL_FRect overlay{0, 0, fw, fh};
SDL_RenderFillRect(renderer, &overlay); SDL_RenderFillRect(renderer, &overlay);
if (!title_.texture) return; if (!title_.texture)
return;
float titleX = (fw - title_.width) / 2.0f; float titleX = (fw - title_.width) / 2.0f;
float titleY = fh / 3.0f - 60.0f; float titleY = fh / 3.0f - 60.0f;
@@ -194,30 +199,31 @@ void Home::render(SDL_Renderer *renderer)
std::string licenseValue; std::string licenseValue;
SDL_Color licenseColor = footerColor; SDL_Color licenseColor = footerColor;
bool licenseBold = false; bool licenseBold = false;
switch (licenseStatus) { switch (licenseStatus)
case radiantwave::license::Status::Valid: {
licenseValue = "OK"; case radiantwave::license::Status::Valid:
licenseColor = okGreen; licenseValue = "OK";
break; licenseColor = okGreen;
case radiantwave::license::Status::Invalid: break;
licenseValue = "Invalid"; case radiantwave::license::Status::Invalid:
licenseColor = badRed; licenseValue = "Invalid";
licenseBold = true; licenseColor = badRed;
break; licenseBold = true;
case radiantwave::license::Status::Checking: break;
licenseValue = "Checking..."; case radiantwave::license::Status::Checking:
break; licenseValue = "Checking...";
case radiantwave::license::Status::Unknown: break;
licenseValue = "Unknown"; case radiantwave::license::Status::Unknown:
break; licenseValue = "Unknown";
break;
} }
int subscriptionLevelId = state_ int subscriptionLevelId = state_
? radiantwave::license::getStoredSubscriptionLevelId(state_->db) ? radiantwave::license::getStoredSubscriptionLevelId(state_->db)
: 0; : 0;
std::string subscriptionValue = subscriptionLevelId > 0 std::string subscriptionValue = subscriptionLevelId > 0
? radiantwave::license::subscriptionLevelName(subscriptionLevelId) ? radiantwave::license::subscriptionLevelName(subscriptionLevelId)
: std::string("Not set"); : std::string("Not set");
time_t now = std::time(nullptr); time_t now = std::time(nullptr);
std::vector<std::string> names = { std::vector<std::string> names = {
@@ -250,23 +256,32 @@ void Home::render(SDL_Renderer *renderer)
valueColors[1] = licenseColor; valueColors[1] = licenseColor;
valueBold[1] = licenseBold; valueBold[1] = licenseBold;
if (footerNames_.size() != names.size()) footerNames_.resize(names.size()); if (footerNames_.size() != names.size())
if (footerValues_.size() != values.size()) footerValues_.resize(values.size()); footerNames_.resize(names.size());
for (size_t i = 0; i < names.size(); ++i) { if (footerValues_.size() != values.size())
footerValues_.resize(values.size());
for (size_t i = 0; i < names.size(); ++i)
{
footerNames_[i].ensure(renderer, footerFont_, names[i], footerColor); footerNames_[i].ensure(renderer, footerFont_, names[i], footerColor);
} }
for (size_t i = 0; i < values.size(); ++i) { for (size_t i = 0; i < values.size(); ++i)
if (valueBold[i]) TTF_SetFontStyle(footerFont_, TTF_STYLE_BOLD); {
if (valueBold[i])
TTF_SetFontStyle(footerFont_, TTF_STYLE_BOLD);
footerValues_[i].ensure(renderer, footerFont_, values[i], valueColors[i], footerValues_[i].ensure(renderer, footerFont_, values[i], valueColors[i],
valueBold[i] ? TTF_STYLE_BOLD : TTF_STYLE_NORMAL); valueBold[i] ? TTF_STYLE_BOLD : TTF_STYLE_NORMAL);
if (valueBold[i]) TTF_SetFontStyle(footerFont_, TTF_STYLE_NORMAL); if (valueBold[i])
TTF_SetFontStyle(footerFont_, TTF_STYLE_NORMAL);
} }
float maxNameW = 0.0f; float maxNameW = 0.0f;
float maxValueW = 0.0f; float maxValueW = 0.0f;
for (size_t i = 0; i < footerNames_.size(); ++i) { for (size_t i = 0; i < footerNames_.size(); ++i)
if (footerNames_[i].width > maxNameW) maxNameW = footerNames_[i].width; {
if (footerValues_[i].width > maxValueW) maxValueW = footerValues_[i].width; if (footerNames_[i].width > maxNameW)
maxNameW = footerNames_[i].width;
if (footerValues_[i].width > maxValueW)
maxValueW = footerValues_[i].width;
} }
CachedText fourSpaces; CachedText fourSpaces;
@@ -280,14 +295,16 @@ void Home::render(SDL_Renderer *renderer)
float lineY = fh - margin; float lineY = fh - margin;
const float lineGap = 6.0f; const float lineGap = 6.0f;
for (size_t i = footerNames_.size(); i-- > 0; ) for (size_t i = footerNames_.size(); i-- > 0;)
{ {
float h = std::max(footerNames_[i].height, footerValues_[i].height); float h = std::max(footerNames_[i].height, footerValues_[i].height);
if (footerNames_[i].texture) { if (footerNames_[i].texture)
{
SDL_FRect dst{rectLeft, lineY - h, footerNames_[i].width, footerNames_[i].height}; SDL_FRect dst{rectLeft, lineY - h, footerNames_[i].width, footerNames_[i].height};
SDL_RenderTexture(renderer, footerNames_[i].texture, nullptr, &dst); SDL_RenderTexture(renderer, footerNames_[i].texture, nullptr, &dst);
} }
if (footerValues_[i].texture) { if (footerValues_[i].texture)
{
SDL_FRect dst{rectRight - footerValues_[i].width, lineY - h, footerValues_[i].width, footerValues_[i].height}; SDL_FRect dst{rectRight - footerValues_[i].width, lineY - h, footerValues_[i].width, footerValues_[i].height};
SDL_RenderTexture(renderer, footerValues_[i].texture, nullptr, &dst); SDL_RenderTexture(renderer, footerValues_[i].texture, nullptr, &dst);
} }
@@ -309,7 +326,8 @@ void Home::createTextures(AppState &state)
TextLine textWidthRef = renderText(state.renderer, font, "Radiant Wave", white); TextLine textWidthRef = renderText(state.renderer, font, "Radiant Wave", white);
float targetWidth = textWidthRef.width * 1.5f; float targetWidth = textWidthRef.width * 1.5f;
if (textWidthRef.texture) SDL_DestroyTexture(textWidthRef.texture); if (textWidthRef.texture)
SDL_DestroyTexture(textWidthRef.texture);
TTF_CloseFont(font); TTF_CloseFont(font);
@@ -338,9 +356,8 @@ void Home::createTextures(AppState &state)
return; return;
} }
SDL_Color gray = {180, 180, 180, 255}; play_ = renderText(state.renderer, font, "Play", white);
play_ = renderText(state.renderer, font, "Play", gray); settings_ = renderText(state.renderer, font, "Settings", white);
settings_ = renderText(state.renderer, font, "Settings", gray);
TTF_CloseFont(font); TTF_CloseFont(font);
@@ -356,16 +373,26 @@ void Home::createTextures(AppState &state)
footerFont_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 20); footerFont_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 20);
const std::vector<std::string> footerNames = { const std::vector<std::string> footerNames = {
"Version:", "License:", "Key:", "Email:", "Subscription:", "WiFi:", "Version:",
"Internet:", "Audio:", "Date:", "Time:", "License:",
"Key:",
"Email:",
"Subscription:",
"WiFi:",
"Internet:",
"Audio:",
"Date:",
"Time:",
}; };
footerNames_.clear(); footerNames_.clear();
footerValues_.clear(); footerValues_.clear();
footerNames_.resize(footerNames.size()); footerNames_.resize(footerNames.size());
footerValues_.resize(footerNames.size()); footerValues_.resize(footerNames.size());
SDL_Color footerColor = {200, 200, 200, 255}; SDL_Color footerColor = {200, 200, 200, 255};
if (footerFont_) { if (footerFont_)
for (size_t i = 0; i < footerNames.size(); ++i) { {
for (size_t i = 0; i < footerNames.size(); ++i)
{
footerNames_[i].ensure(state.renderer, footerFont_, footerNames[i], footerColor); footerNames_[i].ensure(state.renderer, footerFont_, footerNames[i], footerColor);
} }
} }
@@ -373,20 +400,43 @@ void Home::createTextures(AppState &state)
void Home::destroyTextures() void Home::destroyTextures()
{ {
if (bg_.texture) { SDL_DestroyTexture(bg_.texture); bg_.texture = nullptr; } if (bg_.texture)
if (title_.texture) { SDL_DestroyTexture(title_.texture); title_.texture = nullptr; } {
if (play_.texture) { SDL_DestroyTexture(play_.texture); play_.texture = nullptr; } SDL_DestroyTexture(bg_.texture);
if (settings_.texture) { SDL_DestroyTexture(settings_.texture); settings_.texture = nullptr; } bg_.texture = nullptr;
for (auto& t : footerNames_) t.destroy(); }
for (auto& t : footerValues_) t.destroy(); if (title_.texture)
{
SDL_DestroyTexture(title_.texture);
title_.texture = nullptr;
}
if (play_.texture)
{
SDL_DestroyTexture(play_.texture);
play_.texture = nullptr;
}
if (settings_.texture)
{
SDL_DestroyTexture(settings_.texture);
settings_.texture = nullptr;
}
for (auto &t : footerNames_)
t.destroy();
for (auto &t : footerValues_)
t.destroy();
footerNames_.clear(); footerNames_.clear();
footerValues_.clear(); footerValues_.clear();
if (footerFont_) { TTF_CloseFont(footerFont_); footerFont_ = nullptr; } if (footerFont_)
{
TTF_CloseFont(footerFont_);
footerFont_ = nullptr;
}
} }
void Home::refreshStatus() void Home::refreshStatus()
{ {
if (!state_) return; if (!state_)
return;
email_ = state_->db.getKV(radiantwave::db::KV_EMAIL_ADDRESS); email_ = state_->db.getKV(radiantwave::db::KV_EMAIL_ADDRESS);
license_ = state_->db.getKV(radiantwave::db::KV_LICENSE_KEY); license_ = state_->db.getKV(radiantwave::db::KV_LICENSE_KEY);
dateFormat_ = state_->db.getKV("date_format", radiantwave::datetime::DEFAULT_DATE_FORMAT); dateFormat_ = state_->db.getKV("date_format", radiantwave::datetime::DEFAULT_DATE_FORMAT);
@@ -401,7 +451,8 @@ void Home::syncProclamationsIfEmpty(AppState &state)
// Proclamation options. Skipped once the database has content so a // Proclamation options. Skipped once the database has content so a
// blocking sync never runs on every Home push. The API call is also // blocking sync never runs on every Home push. The API call is also
// skipped when the database is closed (fallback path in provisioning). // skipped when the database is closed (fallback path in provisioning).
if (state.db.isOpen() && state.db.getActiveProclamations().empty()) { if (state.db.isOpen() && state.db.getActiveProclamations().empty())
{
// TODO(portal #4/#5): once the portal exposes color palettes, sync them // TODO(portal #4/#5): once the portal exposes color palettes, sync them
// alongside proclamations here (sync::syncColorPalettesFromApi) so Play's // alongside proclamations here (sync::syncColorPalettesFromApi) so Play's
// scroller has portal colors from the very first run. // scroller has portal colors from the very first run.
@@ -411,7 +462,8 @@ void Home::syncProclamationsIfEmpty(AppState &state)
std::string Home::audioStatus() const std::string Home::audioStatus() const
{ {
if (!state_) return "Idle"; if (!state_)
return "Idle";
std::string status = state_->audioDownloader.statusString(); std::string status = state_->audioDownloader.statusString();
return status.empty() ? "Idle" : status; return status.empty() ? "Idle" : status;
} }
+126 -80
View File
@@ -14,88 +14,109 @@
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <algorithm> #include <algorithm>
namespace { namespace
{
constexpr int MENU_ITEM_COUNT = 9; constexpr int MENU_ITEM_COUNT = 9;
// 0 = WiFi Setup, 1 = Audio Options, 2 = Time Zone Setup, 3 = Proclamations, // 0 = WiFi Setup, 1 = Audio Options, 2 = Time Zone Setup, 3 = Proclamations,
// 4 = Email Address, 5 = License Key, 6 = Date Format, // 4 = Email Address, 5 = License Key, 6 = Date Format,
// 7 = View System Logs, 8 = Back // 7 = View System Logs, 8 = Back
constexpr const char* MENU_LABELS[MENU_ITEM_COUNT] = { constexpr const char *MENU_LABELS[MENU_ITEM_COUNT] = {
"WiFi Setup", "WiFi Setup",
"Audio Options", "Audio Options",
"Time Zone Setup", "Time Zone Setup",
"Proclamations", "Proclamations",
"Email Address", "Email Address",
"License Key", "License Key",
"Date Format", "Date Format",
"View System Logs", "View System Logs",
"Back to Home", "Back to Home",
}; };
} // anonymous namespace } // anonymous namespace
void Settings::onPush(AppState& state) void Settings::onPush(AppState &state)
{ {
state_ = &state; state_ = &state;
TTF_Init(); TTF_Init();
createTextures(state); createTextures(state);
state.keybinds.bind(SDLK_UP, "menu_up", [this]() { state.keybinds.bind(SDLK_UP, "menu_up", [this]()
selectedItem_ = (selectedItem_ - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT; { selectedItem_ = (selectedItem_ - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT; });
}); state.keybinds.bind(SDLK_DOWN, "menu_down", [this]()
state.keybinds.bind(SDLK_DOWN, "menu_down", [this]() { { selectedItem_ = (selectedItem_ + 1) % MENU_ITEM_COUNT; });
selectedItem_ = (selectedItem_ + 1) % MENU_ITEM_COUNT;
});
} }
void Settings::onResume(AppState& state) void Settings::onResume(AppState &state)
{ {
state_ = &state; state_ = &state;
createTextures(state); createTextures(state);
state.keybinds.bind(SDLK_UP, "menu_up", [this]() { state.keybinds.bind(SDLK_UP, "menu_up", [this]()
selectedItem_ = (selectedItem_ - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT; { selectedItem_ = (selectedItem_ - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT; });
}); state.keybinds.bind(SDLK_DOWN, "menu_down", [this]()
state.keybinds.bind(SDLK_DOWN, "menu_down", [this]() { { selectedItem_ = (selectedItem_ + 1) % MENU_ITEM_COUNT; });
selectedItem_ = (selectedItem_ + 1) % MENU_ITEM_COUNT;
});
} }
void Settings::onPause(AppState& state) void Settings::onPause(AppState &state)
{ {
state.keybinds.unbindAll(); state.keybinds.unbindAll();
destroyTextures(); destroyTextures();
} }
void Settings::onPop(AppState& state) void Settings::onPop(AppState &state)
{ {
destroyTextures(); destroyTextures();
TTF_Quit(); TTF_Quit();
} }
void Settings::handleEvent(const SDL_Event& event) void Settings::handleEvent(const SDL_Event &event)
{ {
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_RETURN) { 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; switch (selectedItem_)
case 1: state_->pages.push(*state_, std::make_unique<AudioSettingsPage>()); break; {
case 2: state_->pages.push(*state_, std::make_unique<TimeZonePage>()); break; case 0:
case 3: state_->pages.push(*state_, std::make_unique<WiFiSetupPage>());
// The Proclamation list is license-gated like Play. break;
if (state_->licenseTracker.isValid()) { case 1:
state_->pages.push(*state_, std::make_unique<ProclamationOptionsPage>()); state_->pages.push(*state_, std::make_unique<AudioSettingsPage>());
} else { break;
state_->pages.push(*state_, std::make_unique<LicenseBlockedPage>()); case 2:
} state_->pages.push(*state_, std::make_unique<TimeZonePage>());
break; break;
case 4: state_->pages.push(*state_, std::make_unique<EmailAddressPage>()); break; case 3:
case 5: state_->pages.push(*state_, std::make_unique<LicenseKeyPage>()); break; // The Proclamation list is license-gated like Play.
case 6: state_->pages.push(*state_, std::make_unique<DateFormatPage>()); break; if (state_->licenseTracker.isValid())
case 7: state_->pages.push(*state_, std::make_unique<LogViewerPage>()); break; {
case 8: state_->pages.replace(*state_, std::make_unique<Home>()); break; state_->pages.push(*state_, std::make_unique<ProclamationOptionsPage>());
default: break; }
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)) { }
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>()); state_->pages.replace(*state_, std::make_unique<Home>());
} }
} }
@@ -104,7 +125,7 @@ void Settings::update(float dt)
{ {
} }
void Settings::render(SDL_Renderer* renderer) void Settings::render(SDL_Renderer *renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH);
@@ -112,7 +133,8 @@ void Settings::render(SDL_Renderer* renderer)
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
// 1. Logo Background Image // 1. Logo Background Image
if (bgTexture_) { if (bgTexture_)
{
float scaleX = fw / bgWidth_; float scaleX = fw / bgWidth_;
float scaleY = fh / bgHeight_; float scaleY = fh / bgHeight_;
float scale = std::min(scaleX, scaleY); float scale = std::min(scaleX, scaleY);
@@ -132,7 +154,8 @@ void Settings::render(SDL_Renderer* renderer)
// 3. Title Text // 3. Title Text
float titleY = 60.0f; float titleY = 60.0f;
if (titleTexture_) { if (titleTexture_)
{
float x = (fw - titleWidth_) / 2.0f; float x = (fw - titleWidth_) / 2.0f;
SDL_FRect dst{x, titleY, titleWidth_, titleHeight_}; SDL_FRect dst{x, titleY, titleWidth_, titleHeight_};
SDL_RenderTexture(renderer, titleTexture_, nullptr, &dst); SDL_RenderTexture(renderer, titleTexture_, nullptr, &dst);
@@ -140,7 +163,8 @@ void Settings::render(SDL_Renderer* renderer)
// Compute bounding box for all menu items // Compute bounding box for all menu items
float maxWidth = 0.0f; float maxWidth = 0.0f;
for (const auto& item : items_) { for (const auto &item : items_)
{
maxWidth = std::max(maxWidth, item.width); maxWidth = std::max(maxWidth, item.width);
} }
float margin = 12.0f; // 12px vertical margin rule float margin = 12.0f; // 12px vertical margin rule
@@ -151,9 +175,11 @@ void Settings::render(SDL_Renderer* renderer)
// Start menu list exactly 24 pixels below title text // Start menu list exactly 24 pixels below title text
float currentY = titleY + titleHeight_ + 24.0f; float currentY = titleY + titleHeight_ + 24.0f;
for (auto& item : items_) { for (auto &item : items_)
{
item.y = currentY; item.y = currentY;
if (item.texture) { if (item.texture)
{
SDL_FRect dst{sharedX, item.y, item.width, item.height}; SDL_FRect dst{sharedX, item.y, item.width, item.height};
SDL_RenderTexture(renderer, item.texture, nullptr, &dst); SDL_RenderTexture(renderer, item.texture, nullptr, &dst);
} }
@@ -161,9 +187,11 @@ void Settings::render(SDL_Renderer* renderer)
} }
// 6px Underline for selected item occupying half of the 12px margin space // 6px Underline for selected item occupying half of the 12px margin space
if (selectedItem_ >= 0 && selectedItem_ < static_cast<int>(items_.size())) { if (selectedItem_ >= 0 && selectedItem_ < static_cast<int>(items_.size()))
const auto& item = items_[selectedItem_]; {
if (item.texture) { const auto &item = items_[selectedItem_];
if (item.texture)
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_FRect line{sharedX, item.y + item.height + 2.0f, item.width, 6.0f}; SDL_FRect line{sharedX, item.y + item.height + 2.0f, item.width, 6.0f};
SDL_RenderFillRect(renderer, &line); SDL_RenderFillRect(renderer, &line);
@@ -171,13 +199,14 @@ void Settings::render(SDL_Renderer* renderer)
} }
} }
void Settings::createTextures(AppState& state) void Settings::createTextures(AppState &state)
{ {
destroyTextures(); destroyTextures();
// Background Image // Background Image
SDL_Surface* bgSurface = IMG_Load("assets/images/RWT-Circular-Color-ForOnDark-NoText.png"); SDL_Surface *bgSurface = IMG_Load("assets/images/RWT-Circular-Color-ForOnDark-NoText.png");
if (bgSurface) { if (bgSurface)
{
bgTexture_ = SDL_CreateTextureFromSurface(state.renderer, bgSurface); bgTexture_ = SDL_CreateTextureFromSurface(state.renderer, bgSurface);
bgWidth_ = static_cast<float>(bgSurface->w); bgWidth_ = static_cast<float>(bgSurface->w);
bgHeight_ = static_cast<float>(bgSurface->h); bgHeight_ = static_cast<float>(bgSurface->h);
@@ -185,12 +214,14 @@ void Settings::createTextures(AppState& state)
SDL_SetTextureAlphaMod(bgTexture_, 204); SDL_SetTextureAlphaMod(bgTexture_, 204);
} }
SDL_Color white = {255, 255, 255, 255};
// Title Font // Title Font
TTF_Font* fontLarge = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 64); TTF_Font *fontLarge = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 64);
if (fontLarge) { if (fontLarge)
SDL_Color white = {255, 255, 255, 255}; {
SDL_Surface* surface = TTF_RenderText_Blended(fontLarge, "Settings", 0, white); SDL_Surface *surface = TTF_RenderText_Blended(fontLarge, "Settings", 0, white);
if (surface) { if (surface)
{
titleTexture_ = SDL_CreateTextureFromSurface(state.renderer, surface); titleTexture_ = SDL_CreateTextureFromSurface(state.renderer, surface);
titleWidth_ = static_cast<float>(surface->w); titleWidth_ = static_cast<float>(surface->w);
titleHeight_ = static_cast<float>(surface->h); titleHeight_ = static_cast<float>(surface->h);
@@ -200,18 +231,20 @@ void Settings::createTextures(AppState& state)
} }
// Item Font // Item Font
TTF_Font* fontItem = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 36); TTF_Font *fontItem = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 36);
if (!fontItem) return; if (!fontItem)
return;
SDL_Color gray = {200, 200, 200, 255};
items_.reserve(MENU_ITEM_COUNT); items_.reserve(MENU_ITEM_COUNT);
for (int i = 0; i < MENU_ITEM_COUNT; ++i) { for (int i = 0; i < MENU_ITEM_COUNT; ++i)
{
MenuItem item; MenuItem item;
item.label = MENU_LABELS[i]; item.label = MENU_LABELS[i];
SDL_Surface* surface = TTF_RenderText_Blended(fontItem, MENU_LABELS[i], 0, gray); SDL_Surface *surface = TTF_RenderText_Blended(fontItem, MENU_LABELS[i], 0, white);
if (surface) { if (surface)
{
item.texture = SDL_CreateTextureFromSurface(state.renderer, surface); item.texture = SDL_CreateTextureFromSurface(state.renderer, surface);
item.width = static_cast<float>(surface->w); item.width = static_cast<float>(surface->w);
item.height = static_cast<float>(surface->h); item.height = static_cast<float>(surface->h);
@@ -225,10 +258,23 @@ void Settings::createTextures(AppState& state)
void Settings::destroyTextures() void Settings::destroyTextures()
{ {
if (bgTexture_) { SDL_DestroyTexture(bgTexture_); bgTexture_ = nullptr; } if (bgTexture_)
if (titleTexture_) { SDL_DestroyTexture(titleTexture_); titleTexture_ = nullptr; } {
for (auto& item : items_) { SDL_DestroyTexture(bgTexture_);
if (item.texture) { SDL_DestroyTexture(item.texture); item.texture = nullptr; } 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(); items_.clear();
} }