Bottom menu reorganized
This commit is contained in:
@ -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<float>(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<float>(textH)) * 0.5f + textNudge;
|
||||
// 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
|
||||
|
||||
@ -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<SDL_Texture*,5> 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<Uint8>(std::round(aMul * static_cast<double>(bgCol.a)));
|
||||
bdCol.a = static_cast<Uint8>(std::round(aMul * static_cast<double>(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<Uint8>(std::round(180.0 * aMul)) };
|
||||
SDL_Color bdCol{ 255, 200, 70, static_cast<Uint8>(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<Uint8>(std::round(160.0 * aMul)) };
|
||||
SDL_Color bdCol{ 120, 220, 255, static_cast<Uint8>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,42 +8,51 @@ namespace ui {
|
||||
std::array<SDL_FRect, 5> computeMenuButtonRects(const MenuLayoutParams& p) {
|
||||
const float LOGICAL_W = static_cast<float>(p.logicalW);
|
||||
const float LOGICAL_H = static_cast<float>(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<SDL_FRect, MENU_BTN_COUNT> rects{};
|
||||
for (int i = 0; i < MENU_BTN_COUNT; ++i) {
|
||||
float center = btnCX + (static_cast<float>(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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user