238 lines
9.6 KiB
C++
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});
|
|
}
|