diff --git a/src/graphics/renderers/UIRenderer.cpp b/src/graphics/renderers/UIRenderer.cpp index d30a36f..3007b0c 100644 --- a/src/graphics/renderers/UIRenderer.cpp +++ b/src/graphics/renderers/UIRenderer.cpp @@ -66,7 +66,7 @@ void UIRenderer::drawButton(SDL_Renderer* renderer, FontAtlas* font, float cx, f if (!textOnly) { // Adjust colors based on state if (isSelected) { - bgColor = {160, 190, 255, 255}; + // 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); @@ -134,17 +134,20 @@ void UIRenderer::drawButton(SDL_Renderer* renderer, FontAtlas* font, float cx, f SDL_SetTextureColorMod(icon, 255, 255, 255); SDL_SetTextureAlphaMod(icon, 255); } else if (font) { - // Draw text (smaller scale for tighter buttons) + // 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; - // Adjust vertical position for better alignment with background buttons - // Vertically center text precisely within the button - // Vertically center text precisely within the button, then nudge down slightly - // to improve optical balance relative to icons and button art. - const float textNudge = 3.0f; // tweak this value to move labels up/down - float ty = y + (h - static_cast(textH)) * 0.5f + textNudge; + // 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 diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index 285b0a9..9cece0a 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -147,44 +147,6 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, }; auto rects = ui::computeMenuButtonRects(params); - // Draw a compact translucent panel behind the button row for readability. - // Keep it tight so the main_screen art stays visible. - { - float left = rects[0].x; - float right = rects[0].x + rects[0].w; - float top = rects[0].y; - float bottom = rects[0].y + rects[0].h; - for (int i = 1; i < MENU_BTN_COUNT; ++i) { - left = std::min(left, rects[i].x); - right = std::max(right, rects[i].x + rects[i].w); - top = std::min(top, rects[i].y); - bottom = std::max(bottom, rects[i].y + rects[i].h); - } - - const float padX = 16.0f; - const float padY = 12.0f; - SDL_FRect panel{ left - padX, top - padY, (right - left) + padX * 2.0f, (bottom - top) + padY * 2.0f }; - - SDL_BlendMode prevBlend = SDL_BLENDMODE_NONE; - SDL_GetRenderDrawBlendMode(renderer, &prevBlend); - SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); - - // Soft shadow (dark, low alpha) - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 80); - SDL_FRect shadow{ panel.x + 3.0f, panel.y + 5.0f, panel.w, panel.h }; - SDL_RenderFillRect(renderer, &shadow); - - // Bright translucent fill (use existing cyan family used elsewhere in UI) - SDL_SetRenderDrawColor(renderer, 180, 235, 255, 46); - SDL_RenderFillRect(renderer, &panel); - - // Border - SDL_SetRenderDrawColor(renderer, 120, 220, 255, 120); - SDL_RenderRect(renderer, &panel); - - SDL_SetRenderDrawBlendMode(renderer, prevBlend); - } - // Compose same button definition used in render() char levelBtnText[32]; int startLevel = ctx.startLevelSelection ? *ctx.startLevelSelection : 0; @@ -201,7 +163,8 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, std::array icons = { playIcon, levelIcon, optionsIcon, helpIcon, exitIcon }; - // Draw all five buttons on top of the main_screen art (text/icon only). + + // Draw PLAY as a real glowing button, and the four bottom items as HUD buttons. for (int i = 0; i < 5; ++i) { const SDL_FRect& r = rects[i]; float cxCenter = r.x + r.w * 0.5f; @@ -212,15 +175,38 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, const bool isHovered = (ctx.hoveredButton && *ctx.hoveredButton == i); const bool isSelected = (selectedButton == i); - // Apply group alpha and transient flash to button colors double aMul = std::clamp(buttonGroupAlpha + buttonFlash * buttonFlashAmount, 0.0, 1.0); - SDL_Color bgCol = buttons[i].bg; - SDL_Color bdCol = buttons[i].border; - bgCol.a = static_cast(std::round(aMul * static_cast(bgCol.a))); - bdCol.a = static_cast(std::round(aMul * static_cast(bdCol.a))); - UIRenderer::drawButton(renderer, ctx.pixelFont, cxCenter, cyCenter, btnW, btnH, - buttons[i].label, isHovered, isSelected, - bgCol, bdCol, true, icons[i]); + + if (i == 0) { + SDL_Color bgCol{ 18, 22, 28, static_cast(std::round(180.0 * aMul)) }; + SDL_Color bdCol{ 255, 200, 70, static_cast(std::round(220.0 * aMul)) }; + UIRenderer::drawButton(renderer, ctx.pixelFont, cxCenter, cyCenter, btnW, btnH, + buttons[i].label, isHovered, isSelected, + bgCol, bdCol, false, nullptr); + } else { + SDL_Color bgCol{ 20, 30, 42, static_cast(std::round(160.0 * aMul)) }; + SDL_Color bdCol{ 120, 220, 255, static_cast(std::round(200.0 * aMul)) }; + UIRenderer::drawButton(renderer, ctx.pixelFont, cxCenter, cyCenter, btnW, btnH, + buttons[i].label, isHovered, isSelected, + bgCol, bdCol, true, nullptr); + } + } + + // Draw small '+' separators between the bottom HUD buttons (matches the reference vibe). + { + SDL_BlendMode prevBlend = SDL_BLENDMODE_NONE; + SDL_GetRenderDrawBlendMode(renderer, &prevBlend); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, 120, 220, 255, 180); + + float y = rects[1].y + rects[1].h * 0.5f; + for (int i = 1; i < 4; ++i) { + float x = (rects[i].x + rects[i].w + rects[i + 1].x) * 0.5f; + SDL_RenderLine(renderer, x - 4.0f, y, x + 4.0f, y); + SDL_RenderLine(renderer, x, y - 4.0f, x, y + 4.0f); + } + + SDL_SetRenderDrawBlendMode(renderer, prevBlend); } } diff --git a/src/ui/MenuLayout.cpp b/src/ui/MenuLayout.cpp index c99ef95..15c9a5c 100644 --- a/src/ui/MenuLayout.cpp +++ b/src/ui/MenuLayout.cpp @@ -8,42 +8,51 @@ namespace ui { std::array computeMenuButtonRects(const MenuLayoutParams& p) { const float LOGICAL_W = static_cast(p.logicalW); const float LOGICAL_H = static_cast(p.logicalH); - bool isSmall = ((LOGICAL_W * p.logicalScale) < MENU_SMALL_THRESHOLD); - float btnW = isSmall ? (LOGICAL_W * MENU_BTN_WIDTH_SMALL_FACTOR) : MENU_BTN_WIDTH_LARGE; - float btnH = isSmall ? MENU_BTN_HEIGHT_SMALL : MENU_BTN_HEIGHT_LARGE; float contentOffsetX = (p.winW - LOGICAL_W * p.logicalScale) * 0.5f / p.logicalScale; float contentOffsetY = (p.winH - LOGICAL_H * p.logicalScale) * 0.5f / p.logicalScale; - float btnCX = LOGICAL_W * 0.5f + contentOffsetX; - float btnCY = LOGICAL_H * 0.86f + contentOffsetY + MENU_BTN_Y_OFFSET; - float spacing = isSmall ? btnW * MENU_BTN_SPACING_FACTOR_SMALL : btnW * MENU_BTN_SPACING_FACTOR_LARGE; - // Guarantee the full 5-button group fits within the logical width so no options - // disappear in windowed mode. We shrink width + spacing proportionally when needed. - const float margin = std::max(18.0f, LOGICAL_W * 0.02f); - const float availableW = std::max(100.0f, LOGICAL_W - margin * 2.0f); - float totalW = btnW + (MENU_BTN_COUNT - 1) * spacing; - if (totalW > availableW) { - float scale = availableW / totalW; - btnW *= scale; - btnH *= scale; - spacing *= scale; - totalW = btnW + (MENU_BTN_COUNT - 1) * spacing; + // Cockpit HUD layout (matches main_screen art): + // - A big centered PLAY button + // - A second row of 4 smaller buttons: LEVEL / OPTIONS / HELP / EXIT + const float marginX = std::max(24.0f, LOGICAL_W * 0.03f); + const float marginBottom = std::max(26.0f, LOGICAL_H * 0.03f); + const float availableW = std::max(120.0f, LOGICAL_W - marginX * 2.0f); + + float playW = std::min(230.0f, availableW * 0.27f); + float playH = 35.0f; + float smallW = std::min(220.0f, availableW * 0.23f); + float smallH = 34.0f; + float smallSpacing = 28.0f; + + // Scale down for narrow windows so nothing goes offscreen. + float smallTotal = smallW * 4.0f + smallSpacing * 3.0f; + if (smallTotal > availableW) { + float s = availableW / smallTotal; + smallW *= s; + smallH *= s; + smallSpacing *= s; + playW = std::min(playW, availableW); + playH *= std::max(0.75f, s); } - // Keep the group centered but ensure left/right edges respect margins. - float groupLeft = btnCX - totalW * 0.5f; - float minLeft = contentOffsetX + margin; - float maxRight = contentOffsetX + LOGICAL_W - margin; - float groupRight = groupLeft + totalW; - if (groupLeft < minLeft) { - btnCX += (minLeft - groupLeft); - } else if (groupRight > maxRight) { - btnCX -= (groupRight - maxRight); - } + float centerX = LOGICAL_W * 0.5f + contentOffsetX; + float bottomY = LOGICAL_H + contentOffsetY - marginBottom; + float smallCY = bottomY - smallH * 0.5f; + float playCY = smallCY - smallH * 0.5f - 16.0f - playH * 0.5f; + std::array rects{}; - for (int i = 0; i < MENU_BTN_COUNT; ++i) { - float center = btnCX + (static_cast(i) - MENU_BTN_CENTER) * spacing; - rects[i] = SDL_FRect{center - btnW / 2.0f, btnCY - btnH / 2.0f, btnW, btnH}; + rects[0] = SDL_FRect{ centerX - playW * 0.5f, playCY - playH * 0.5f, playW, playH }; + + float rowW = smallW * 4.0f + smallSpacing * 3.0f; + float left = centerX - rowW * 0.5f; + float minLeft = contentOffsetX + marginX; + float maxRight = contentOffsetX + LOGICAL_W - marginX; + if (left < minLeft) left = minLeft; + if (left + rowW > maxRight) left = std::max(minLeft, maxRight - rowW); + + for (int i = 0; i < 4; ++i) { + float x = left + i * (smallW + smallSpacing); + rects[i + 1] = SDL_FRect{ x, smallCY - smallH * 0.5f, smallW, smallH }; } return rects; }