Updated bottom menu

This commit is contained in:
2025-12-17 20:12:06 +01:00
parent 122de2b36f
commit fe6c5e3c8a
7 changed files with 240 additions and 83 deletions

126
src/ui/BottomMenu.cpp Normal file
View File

@ -0,0 +1,126 @@
#include "ui/BottomMenu.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include "graphics/renderers/UIRenderer.h"
#include "graphics/Font.h"
namespace ui {
static bool pointInRect(const SDL_FRect& r, float x, float y) {
return x >= r.x && x <= (r.x + r.w) && y >= r.y && y <= (r.y + r.h);
}
BottomMenu buildBottomMenu(const MenuLayoutParams& params, int startLevel) {
BottomMenu menu{};
auto rects = computeMenuButtonRects(params);
char levelBtnText[32];
std::snprintf(levelBtnText, sizeof(levelBtnText), "LEVEL %d", startLevel);
menu.buttons[0] = Button{ BottomMenuItem::Play, rects[0], "PLAY", false };
menu.buttons[1] = Button{ BottomMenuItem::Level, rects[1], levelBtnText, true };
menu.buttons[2] = Button{ BottomMenuItem::Options, rects[2], "OPTIONS", true };
menu.buttons[3] = Button{ BottomMenuItem::Help, rects[3], "HELP", true };
menu.buttons[4] = Button{ BottomMenuItem::Exit, rects[4], "EXIT", true };
return menu;
}
void renderBottomMenu(SDL_Renderer* renderer,
FontAtlas* font,
const BottomMenu& menu,
int hoveredIndex,
int selectedIndex,
double baseAlphaMul,
double flashAddMul) {
if (!renderer || !font) return;
const double baseMul = std::clamp(baseAlphaMul, 0.0, 1.0);
const double flashMul = flashAddMul;
const int focusIndex = (hoveredIndex != -1) ? hoveredIndex : selectedIndex;
for (int i = 0; i < MENU_BTN_COUNT; ++i) {
const Button& b = menu.buttons[i];
const SDL_FRect& r = b.rect;
float cx = r.x + r.w * 0.5f;
float cy = r.y + r.h * 0.5f;
bool isHovered = (hoveredIndex == i);
bool isSelected = (selectedIndex == i);
// Requested behavior: flash only the PLAY button, and only when it's the active/focused item.
const bool playIsActive = (i == 0) && (focusIndex == 0);
const double aMul = std::clamp(baseMul + (playIsActive ? flashMul : 0.0), 0.0, 1.0);
if (!b.textOnly) {
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, font, cx, cy, r.w, r.h,
b.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, font, cx, cy, r.w, r.h,
b.label, isHovered, isSelected,
bgCol, bdCol, true, nullptr);
}
}
// '+' separators between the bottom HUD buttons (indices 1..4)
{
SDL_BlendMode prevBlend = SDL_BLENDMODE_NONE;
SDL_GetRenderDrawBlendMode(renderer, &prevBlend);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 120, 220, 255, static_cast<Uint8>(std::round(180.0 * baseMul)));
float y = menu.buttons[1].rect.y + menu.buttons[1].rect.h * 0.5f;
for (int i = 1; i < 4; ++i) {
float x = (menu.buttons[i].rect.x + menu.buttons[i].rect.w + menu.buttons[i + 1].rect.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);
}
}
BottomMenuInputResult handleBottomMenuInput(const MenuLayoutParams& params,
const SDL_Event& e,
float x,
float y,
int prevHoveredIndex,
bool inputEnabled) {
BottomMenuInputResult result{};
result.hoveredIndex = prevHoveredIndex;
if (!inputEnabled) {
return result;
}
if (e.type == SDL_EVENT_MOUSE_MOTION) {
result.hoveredIndex = hitTestMenuButtons(params, x, y);
return result;
}
if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
auto rects = computeMenuButtonRects(params);
for (int i = 0; i < MENU_BTN_COUNT; ++i) {
if (pointInRect(rects[i], x, y)) {
result.activated = static_cast<BottomMenuItem>(i);
result.hoveredIndex = i;
break;
}
}
}
return result;
}
} // namespace ui

63
src/ui/BottomMenu.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <array>
#include <optional>
#include <string>
#include <SDL3/SDL.h>
#include "ui/MenuLayout.h"
#include "ui/UIConstants.h"
struct FontAtlas;
namespace ui {
enum class BottomMenuItem : int {
Play = 0,
Level = 1,
Options = 2,
Help = 3,
Exit = 4,
};
struct Button {
BottomMenuItem item{};
SDL_FRect rect{};
std::string label;
bool textOnly = true;
};
struct BottomMenu {
std::array<Button, MENU_BTN_COUNT> buttons{};
};
BottomMenu buildBottomMenu(const MenuLayoutParams& params, int startLevel);
// Draws the cockpit HUD menu (PLAY + 4 bottom items) using existing UIRenderer primitives.
// hoveredIndex: -1..4
// selectedIndex: 0..4 (keyboard selection)
// alphaMul: 0..1 (overall group alpha)
void renderBottomMenu(SDL_Renderer* renderer,
FontAtlas* font,
const BottomMenu& menu,
int hoveredIndex,
int selectedIndex,
double baseAlphaMul,
double flashAddMul);
struct BottomMenuInputResult {
int hoveredIndex = -1;
std::optional<BottomMenuItem> activated;
};
// Interprets mouse motion/button input for the bottom menu.
// Expects x/y in the same logical coordinate space used by MenuLayout (the current main loop already provides this).
BottomMenuInputResult handleBottomMenuInput(const MenuLayoutParams& params,
const SDL_Event& e,
float x,
float y,
int prevHoveredIndex,
bool inputEnabled);
} // namespace ui

View File

@ -38,7 +38,9 @@ std::array<SDL_FRect, 5> computeMenuButtonRects(const MenuLayoutParams& p) {
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;
// Extra breathing room between PLAY and the bottom row (requested).
const float rowGap = 34.0f;
float playCY = smallCY - smallH * 0.5f - rowGap - playH * 0.5f;
std::array<SDL_FRect, MENU_BTN_COUNT> rects{};
rects[0] = SDL_FRect{ centerX - playW * 0.5f, playCY - playH * 0.5f, playW, playH };