#include "UIRenderer.h" #include "../ui/Font.h" #include #include 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(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(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((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(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(std::min(255, bgColor.r + 40)), static_cast(std::min(255, bgColor.g + 40)), static_cast(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(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(textW)) * 0.5f; // Vertically center text within the button. float ty = y + (h - static_cast(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(texW); h = static_cast(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}); }