Files
spacetris/src/graphics/renderers/UIRenderer.cpp
2025-12-17 19:23:16 +01:00

238 lines
9.6 KiB
C++

#include "UIRenderer.h"
#include "../ui/Font.h"
#include <algorithm>
#include <cmath>
void UIRenderer::drawSciFiPanel(SDL_Renderer* renderer, const SDL_FRect& rect, float alpha) {
if (!renderer) return;
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
Uint8 alphaUint = static_cast<Uint8>(std::clamp(alpha * 255.0f, 0.0f, 255.0f));
// Drop shadow
SDL_FRect shadow{rect.x + 6.0f, rect.y + 10.0f, rect.w, rect.h};
SDL_SetRenderDrawColor(renderer, 0, 0, 0, static_cast<Uint8>(120.0f * alpha));
SDL_RenderFillRect(renderer, &shadow);
// Glow aura
for (int i = 0; i < 5; ++i) {
SDL_FRect glow{rect.x - float(i * 2), rect.y - float(i * 2), rect.w + float(i * 4), rect.h + float(i * 4)};
Uint8 glowAlpha = static_cast<Uint8>((42 - i * 8) * alpha);
SDL_SetRenderDrawColor(renderer, 0, 180, 255, glowAlpha);
SDL_RenderRect(renderer, &glow);
}
// Body
SDL_SetRenderDrawColor(renderer, 18, 30, 52, alphaUint);
SDL_RenderFillRect(renderer, &rect);
// Border
SDL_SetRenderDrawColor(renderer, 70, 120, 210, alphaUint);
SDL_RenderRect(renderer, &rect);
}
void UIRenderer::drawButton(SDL_Renderer* renderer, FontAtlas* font, float cx, float cy, float w, float h,
const std::string& label, bool isHovered, bool isSelected,
SDL_Color bgColor, SDL_Color borderColor, bool textOnly, SDL_Texture* icon) {
if (!renderer) return;
float x = cx - w * 0.5f;
float y = cy - h * 0.5f;
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
// In "textOnly" mode we don't draw a full button body (the art may be in the background image),
// but we still add a subtle highlight so hover/selection feels intentional.
if (textOnly && (isHovered || isSelected)) {
Uint8 outlineA = isSelected ? 170 : 110;
Uint8 fillA = isSelected ? 60 : 32;
SDL_Color hl = borderColor;
hl.a = outlineA;
SDL_SetRenderDrawColor(renderer, hl.r, hl.g, hl.b, hl.a);
SDL_FRect o1{x - 3.0f, y - 3.0f, w + 6.0f, h + 6.0f};
SDL_RenderRect(renderer, &o1);
SDL_FRect o2{x - 6.0f, y - 6.0f, w + 12.0f, h + 12.0f};
SDL_SetRenderDrawColor(renderer, hl.r, hl.g, hl.b, static_cast<Uint8>(std::max(0, (int)hl.a - 60)));
SDL_RenderRect(renderer, &o2);
SDL_Color fill = bgColor;
fill.a = fillA;
SDL_SetRenderDrawColor(renderer, fill.r, fill.g, fill.b, fill.a);
SDL_FRect f{x, y, w, h};
SDL_RenderFillRect(renderer, &f);
}
if (!textOnly) {
// Adjust colors based on state
if (isSelected) {
// Keep caller-provided colors; just add a stronger glow.
SDL_SetRenderDrawColor(renderer, 255, 220, 0, 110);
SDL_FRect glow{x - 10, y - 10, w + 20, h + 20};
SDL_RenderFillRect(renderer, &glow);
} else if (isHovered) {
bgColor = {static_cast<Uint8>(std::min(255, bgColor.r + 40)),
static_cast<Uint8>(std::min(255, bgColor.g + 40)),
static_cast<Uint8>(std::min(255, bgColor.b + 40)),
bgColor.a};
}
// Neon glow aura around the button to increase visibility (subtle)
for (int gi = 0; gi < 3; ++gi) {
float grow = 6.0f + gi * 3.0f;
Uint8 glowA = static_cast<Uint8>(std::max(0, (int)borderColor.a / (3 - gi)));
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, glowA);
SDL_FRect glowRect{x - grow, y - grow, w + grow * 2.0f, h + grow * 2.0f};
SDL_RenderRect(renderer, &glowRect);
}
// Draw button background with border
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, borderColor.a);
SDL_FRect borderRect{x - 2, y - 2, w + 4, h + 4};
SDL_RenderFillRect(renderer, &borderRect);
SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, bgColor.a);
SDL_FRect bgRect{x, y, w, h};
SDL_RenderFillRect(renderer, &bgRect);
}
// Draw icon if provided, otherwise draw text
if (icon) {
// Get icon dimensions
float iconW = 0.0f, iconH = 0.0f;
SDL_GetTextureSize(icon, &iconW, &iconH);
// Scale icon to fit nicely in button (60% of button height)
float maxIconH = h * 0.6f;
float scale = maxIconH / iconH;
float scaledW = iconW * scale;
float scaledH = iconH * scale;
// Center icon in button
float iconX = cx - scaledW * 0.5f;
float iconY = cy - scaledH * 0.5f;
SDL_FRect iconRect{iconX, iconY, scaledW, scaledH};
// Soft icon shadow for readability over busy backgrounds
SDL_SetTextureBlendMode(icon, SDL_BLENDMODE_BLEND);
SDL_SetTextureColorMod(icon, 0, 0, 0);
SDL_SetTextureAlphaMod(icon, 150);
SDL_FRect shadowRect{iconX + 2.0f, iconY + 2.0f, scaledW, scaledH};
SDL_RenderTexture(renderer, icon, nullptr, &shadowRect);
// Main icon (yellow tint when selected)
if (isSelected) {
SDL_SetTextureColorMod(icon, 255, 220, 0);
} else {
SDL_SetTextureColorMod(icon, 255, 255, 255);
}
SDL_SetTextureAlphaMod(icon, 255);
SDL_RenderTexture(renderer, icon, nullptr, &iconRect);
// Reset
SDL_SetTextureColorMod(icon, 255, 255, 255);
SDL_SetTextureAlphaMod(icon, 255);
} else if (font) {
// Draw text with scale based on button height.
float textScale = 1.2f;
if (h <= 40.0f) {
textScale = 0.90f;
} else if (h <= 54.0f) {
textScale = 1.00f;
} else if (h <= 70.0f) {
textScale = 1.10f;
}
int textW = 0, textH = 0;
font->measure(label, textScale, textW, textH);
float tx = x + (w - static_cast<float>(textW)) * 0.5f;
// Vertically center text within the button.
float ty = y + (h - static_cast<float>(textH)) * 0.5f;
// Choose text color based on selection state
SDL_Color textColor = {255, 255, 255, 255}; // Default white
if (isSelected) {
textColor = {255, 220, 0, 255}; // Yellow when selected
}
// Text shadow
font->draw(renderer, tx + 2.0f, ty + 2.0f, label, textScale, {0, 0, 0, 200});
// Text
font->draw(renderer, tx, ty, label, textScale, textColor);
}
}
void UIRenderer::computeContentOffsets(float winW, float winH, float logicalW, float logicalH, float logicalScale, float& outOffsetX, float& outOffsetY) {
float contentW = logicalW * logicalScale;
float contentH = logicalH * logicalScale;
outOffsetX = (winW - contentW) * 0.5f / logicalScale;
outOffsetY = (winH - contentH) * 0.5f / logicalScale;
}
void UIRenderer::drawLogo(SDL_Renderer* renderer, SDL_Texture* logoTex, float logicalW, float logicalH, float contentOffsetX, float contentOffsetY, int texW, int texH) {
if (!renderer || !logoTex) return;
float w = 0.0f;
float h = 0.0f;
if (texW > 0 && texH > 0) {
w = static_cast<float>(texW);
h = static_cast<float>(texH);
} else {
SDL_GetTextureSize(logoTex, &w, &h);
}
if (w > 0.0f && h > 0.0f) {
float maxWidth = logicalW * 0.6f;
float scale = std::min(1.0f, maxWidth / w);
float dw = w * scale;
float dh = h * scale;
float logoX = (logicalW - dw) * 0.5f + contentOffsetX;
float logoY = logicalH * 0.05f + contentOffsetY;
SDL_FRect dst{logoX, logoY, dw, dh};
SDL_RenderTexture(renderer, logoTex, nullptr, &dst);
}
}
void UIRenderer::drawSettingsPopup(SDL_Renderer* renderer, FontAtlas* font, float logicalW, float logicalH, bool musicEnabled, bool soundEnabled) {
if (!renderer || !font) return;
float popupW = 350, popupH = 260;
float popupX = (logicalW - popupW) / 2;
float popupY = (logicalH - popupH) / 2;
// Semi-transparent overlay
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 128);
SDL_FRect overlay{0, 0, logicalW, logicalH};
SDL_RenderFillRect(renderer, &overlay);
// Popup background
SDL_SetRenderDrawColor(renderer, 100, 120, 160, 255);
SDL_FRect bord{popupX-4, popupY-4, popupW+8, popupH+8};
SDL_RenderFillRect(renderer, &bord);
SDL_SetRenderDrawColor(renderer, 40, 50, 70, 255);
SDL_FRect body{popupX, popupY, popupW, popupH};
SDL_RenderFillRect(renderer, &body);
// Title
font->draw(renderer, popupX + 20, popupY + 20, "SETTINGS", 2.0f, {255, 220, 0, 255});
// Music toggle
font->draw(renderer, popupX + 20, popupY + 70, "MUSIC:", 1.5f, {255, 255, 255, 255});
const char* musicStatus = musicEnabled ? "ON" : "OFF";
SDL_Color musicColor = musicEnabled ? SDL_Color{0, 255, 0, 255} : SDL_Color{255, 0, 0, 255};
font->draw(renderer, popupX + 120, popupY + 70, musicStatus, 1.5f, musicColor);
// Sound effects toggle
font->draw(renderer, popupX + 20, popupY + 100, "SOUND FX:", 1.5f, {255, 255, 255, 255});
const char* soundStatus = soundEnabled ? "ON" : "OFF";
SDL_Color soundColor = soundEnabled ? SDL_Color{0, 255, 0, 255} : SDL_Color{255, 0, 0, 255};
font->draw(renderer, popupX + 140, popupY + 100, soundStatus, 1.5f, soundColor);
// Instructions
font->draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, {200, 200, 220, 255});
font->draw(renderer, popupX + 20, popupY + 170, "S = TOGGLE SOUND FX", 1.0f, {200, 200, 220, 255});
font->draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, {200, 200, 220, 255});
}