minor fix
This commit is contained in:
@ -122,7 +122,8 @@ void GameRenderer::renderPlayingState(
|
||||
float logicalScale,
|
||||
float winW,
|
||||
float winH,
|
||||
bool showExitConfirmPopup
|
||||
bool showExitConfirmPopup,
|
||||
int exitPopupSelectedButton
|
||||
) {
|
||||
if (!game || !pixelFont) return;
|
||||
|
||||
@ -460,6 +461,10 @@ void GameRenderer::renderPlayingState(
|
||||
float btnY = popupY + popupH - 60.0f;
|
||||
|
||||
// YES button
|
||||
if (exitPopupSelectedButton == 0) {
|
||||
// Draw glow for selected YES button
|
||||
drawRectWithOffset(yesX - 6.0f, btnY - 6.0f, btnW + 12.0f, btnH + 12.0f, {255, 220, 0, 100});
|
||||
}
|
||||
drawRectWithOffset(yesX - 2.0f, btnY - 2.0f, btnW + 4.0f, btnH + 4.0f, {100, 120, 140, 255});
|
||||
drawRectWithOffset(yesX, btnY, btnW, btnH, {200, 60, 60, 255});
|
||||
const std::string yes = "YES";
|
||||
@ -469,6 +474,10 @@ void GameRenderer::renderPlayingState(
|
||||
yes, 1.0f, {255, 255, 255, 255});
|
||||
|
||||
// NO button
|
||||
if (exitPopupSelectedButton == 1) {
|
||||
// Draw glow for selected NO button
|
||||
drawRectWithOffset(noX - 6.0f, btnY - 6.0f, btnW + 12.0f, btnH + 12.0f, {255, 220, 0, 100});
|
||||
}
|
||||
drawRectWithOffset(noX - 2.0f, btnY - 2.0f, btnW + 4.0f, btnH + 4.0f, {100, 120, 140, 255});
|
||||
drawRectWithOffset(noX, btnY, btnW, btnH, {80, 140, 80, 255});
|
||||
const std::string no = "NO";
|
||||
|
||||
@ -26,7 +26,8 @@ public:
|
||||
float logicalScale,
|
||||
float winW,
|
||||
float winH,
|
||||
bool showExitConfirmPopup
|
||||
bool showExitConfirmPopup,
|
||||
int exitPopupSelectedButton = 1 // 0=YES, 1=NO
|
||||
);
|
||||
|
||||
private:
|
||||
|
||||
@ -193,6 +193,8 @@ void LevelSelectorState::handleEvent(const SDL_Event& e) {
|
||||
// convert mouse to logical coords (viewport is already centered)
|
||||
float lx = (float(e.button.x) - float(lastLogicalVP.x)) / (lastLogicalScale > 0.f ? lastLogicalScale : 1.f);
|
||||
float ly = (float(e.button.y) - float(lastLogicalVP.y)) / (lastLogicalScale > 0.f ? lastLogicalScale : 1.f);
|
||||
|
||||
// Use same panel calculation as render (centered)
|
||||
SDL_FRect panel = DrawPanel(nullptr, LOGICAL_W, LOGICAL_H, /*draw=*/false, 0.f, 0.f);
|
||||
Grid g = MakeGrid(panel);
|
||||
int hit = HitTest(g, int(lx), int(ly));
|
||||
@ -210,6 +212,8 @@ void LevelSelectorState::handleEvent(const SDL_Event& e) {
|
||||
// convert mouse to logical coords (viewport is already centered)
|
||||
float lx = (float(e.motion.x) - float(lastLogicalVP.x)) / (lastLogicalScale > 0.f ? lastLogicalScale : 1.f);
|
||||
float ly = (float(e.motion.y) - float(lastLogicalVP.y)) / (lastLogicalScale > 0.f ? lastLogicalScale : 1.f);
|
||||
|
||||
// Use same panel calculation as render (centered)
|
||||
SDL_FRect panel = DrawPanel(nullptr, LOGICAL_W, LOGICAL_H, /*draw=*/false, 0.f, 0.f);
|
||||
Grid g = MakeGrid(panel);
|
||||
hoveredLevel = HitTest(g, int(lx), int(ly));
|
||||
@ -225,28 +229,30 @@ void LevelSelectorState::render(SDL_Renderer* renderer, float logicalScale, SDL_
|
||||
// Cache for input conversion
|
||||
lastLogicalScale = logicalScale;
|
||||
lastLogicalVP = logicalVP;
|
||||
drawLevelSelectionPopup(renderer);
|
||||
drawLevelSelectionPopup(renderer, logicalScale, logicalVP);
|
||||
}
|
||||
|
||||
void LevelSelectorState::drawLevelSelectionPopup(SDL_Renderer* renderer) {
|
||||
void LevelSelectorState::drawLevelSelectionPopup(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) {
|
||||
if (!renderer) return;
|
||||
|
||||
// Get dynamic logical dimensions
|
||||
const int LOGICAL_W = GlobalState::instance().getLogicalWidth();
|
||||
const int LOGICAL_H = GlobalState::instance().getLogicalHeight();
|
||||
// Use fixed logical dimensions to match main.cpp and ensure consistent layout
|
||||
const float LOGICAL_W = 1200.f;
|
||||
const float LOGICAL_H = 1000.f;
|
||||
|
||||
// Compute content offsets (same approach as MenuState for proper centering)
|
||||
float winW = (float)logicalVP.w;
|
||||
float winH = (float)logicalVP.h;
|
||||
float contentW = LOGICAL_W * logicalScale;
|
||||
float contentH = LOGICAL_H * logicalScale;
|
||||
float contentOffsetX = (winW - contentW) * 0.5f / logicalScale;
|
||||
float contentOffsetY = (winH - contentH) * 0.5f / logicalScale;
|
||||
|
||||
// Since ApplicationManager sets up a centered viewport, we draw directly in logical coordinates
|
||||
// The viewport (LOGICAL_W x LOGICAL_H) is already centered within the window
|
||||
float vw = float(LOGICAL_W);
|
||||
float vh = float(LOGICAL_H);
|
||||
float offX = 0.f; // No offset needed since viewport is centered
|
||||
|
||||
// Panel and title strip (in logical space)
|
||||
SDL_FRect panel = DrawPanel(renderer, vw, vh-140.0f, /*draw=*/true, offX, 0.f);
|
||||
// Panel and title strip (in logical space) - centered properly with offsets
|
||||
SDL_FRect panel = DrawPanel(renderer, LOGICAL_W, LOGICAL_H, /*draw=*/true, contentOffsetX, contentOffsetY);
|
||||
|
||||
// Title text - prefer pixelFont for a blocky title if available, fallback to regular font
|
||||
FontAtlas* titleFont = ctx.pixelFont ? ctx.pixelFont : ctx.font;
|
||||
DrawText(renderer, titleFont, "SELECT STARTING LEVEL", vw / 2.f + offX, panel.y + 20.f, 1.2f, COL_TITLE, true, true);
|
||||
DrawText(renderer, titleFont, "SELECT STARTING LEVEL", LOGICAL_W / 2.f + contentOffsetX, panel.y + 20.f, 1.2f, COL_TITLE, true, true);
|
||||
|
||||
// Grid of levels
|
||||
Grid g = MakeGrid(panel);
|
||||
@ -256,10 +262,10 @@ void LevelSelectorState::drawLevelSelectionPopup(SDL_Renderer* renderer) {
|
||||
DrawCell(renderer, rc, i, hoveredLevel == i, selectedLevel == i);
|
||||
}
|
||||
|
||||
// Footer/instructions - use regular TTF font for readability
|
||||
// Footer/instructions - use regular TTF font for readability, centered and higher
|
||||
FontAtlas* footerFont = ctx.pixelFont ? ctx.pixelFont : ctx.font;
|
||||
DrawText(renderer, footerFont, "CLICK A LEVEL TO SELECT \u2022 ESC = CANCEL",
|
||||
vw / 2.f + offX, vh - 56.f, 1.0f, COL_FOOTER, true, true);
|
||||
DrawText(renderer, footerFont, "CLICK A LEVEL TO SELECT • ESC = CANCEL",
|
||||
LOGICAL_W / 2.f + contentOffsetX, panel.y + panel.h + 30.f, 1.0f, COL_FOOTER, true, true);
|
||||
}
|
||||
|
||||
bool LevelSelectorState::isMouseInPopup(float mouseX, float mouseY, float& popupX, float& popupY, float& popupW, float& popupH) {
|
||||
|
||||
@ -19,7 +19,7 @@ private:
|
||||
SDL_Rect lastLogicalVP{0,0,0,0};
|
||||
|
||||
// Helper functions
|
||||
void drawLevelSelectionPopup(SDL_Renderer* renderer);
|
||||
void drawLevelSelectionPopup(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP);
|
||||
bool isMouseInPopup(float mouseX, float mouseY, float& popupX, float& popupY, float& popupW, float& popupH);
|
||||
int getLevelFromMouse(float mouseX, float mouseY, float popupX, float popupY, float popupW, float popupH);
|
||||
void updateHoverFromMouse(float mouseX, float mouseY);
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "persistence/Scores.h"
|
||||
#include "graphics/Font.h"
|
||||
#include "../core/GlobalState.h"
|
||||
#include "../core/state/StateManager.h"
|
||||
#include "../audio/Audio.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <cstdio>
|
||||
@ -27,8 +28,37 @@ void MenuState::onExit() {
|
||||
}
|
||||
|
||||
void MenuState::handleEvent(const SDL_Event& e) {
|
||||
// Key-specific handling (allow main to handle global keys)
|
||||
(void)e;
|
||||
// Keyboard navigation for menu buttons
|
||||
if (e.type == SDL_EVENT_KEY_DOWN && !e.key.repeat) {
|
||||
switch (e.key.scancode) {
|
||||
case SDL_SCANCODE_LEFT:
|
||||
case SDL_SCANCODE_UP:
|
||||
selectedButton = 0; // PLAY
|
||||
break;
|
||||
case SDL_SCANCODE_RIGHT:
|
||||
case SDL_SCANCODE_DOWN:
|
||||
selectedButton = 1; // LEVEL
|
||||
break;
|
||||
case SDL_SCANCODE_RETURN:
|
||||
case SDL_SCANCODE_KP_ENTER:
|
||||
case SDL_SCANCODE_SPACE:
|
||||
// Activate selected button
|
||||
if (selectedButton == 0) {
|
||||
// PLAY button - transition to Playing state
|
||||
if (ctx.stateManager) {
|
||||
ctx.stateManager->setState(AppState::Playing);
|
||||
}
|
||||
} else {
|
||||
// LEVEL button - transition to LevelSelector state
|
||||
if (ctx.stateManager) {
|
||||
ctx.stateManager->setState(AppState::LevelSelector);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuState::update(double frameMs) {
|
||||
@ -175,8 +205,16 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
||||
int startLevel = ctx.startLevelSelection ? *ctx.startLevelSelection : 0;
|
||||
std::snprintf(levelBtnText, sizeof(levelBtnText), "LEVEL %d", startLevel);
|
||||
// Draw simple styled buttons (replicating menu_drawMenuButton)
|
||||
auto drawMenuButtonLocal = [&](SDL_Renderer* r, FontAtlas& font, float cx, float cy, float w, float h, const std::string& label, SDL_Color bg, SDL_Color border){
|
||||
auto drawMenuButtonLocal = [&](SDL_Renderer* r, FontAtlas& font, float cx, float cy, float w, float h, const std::string& label, SDL_Color bg, SDL_Color border, bool selected){
|
||||
float x = cx - w/2; float y = cy - h/2;
|
||||
|
||||
// If selected, draw a glow effect
|
||||
if (selected) {
|
||||
SDL_SetRenderDrawColor(r, 255, 220, 0, 100);
|
||||
SDL_FRect glow{ x-10, y-10, w+20, h+20 };
|
||||
SDL_RenderFillRect(r, &glow);
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(r, border.r, border.g, border.b, border.a);
|
||||
SDL_FRect br{ x-6, y-6, w+12, h+12 }; SDL_RenderFillRect(r, &br);
|
||||
SDL_SetRenderDrawColor(r, 255,255,255,255); SDL_FRect br2{ x-4, y-4, w+8, h+8 }; SDL_RenderFillRect(r, &br2);
|
||||
@ -185,10 +223,10 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
||||
font.draw(r, tx+2, ty+2, label, textScale, SDL_Color{0,0,0,180});
|
||||
font.draw(r, tx, ty, label, textScale, SDL_Color{255,255,255,255});
|
||||
};
|
||||
drawMenuButtonLocal(renderer, *ctx.pixelFont, btnX - btnW * 0.6f, btnY, btnW, btnH, std::string("PLAY"), SDL_Color{60,180,80,255}, SDL_Color{30,120,40,255});
|
||||
drawMenuButtonLocal(renderer, *ctx.pixelFont, btnX - btnW * 0.6f, btnY, btnW, btnH, std::string("PLAY"), SDL_Color{60,180,80,255}, SDL_Color{30,120,40,255}, selectedButton == 0);
|
||||
{
|
||||
}
|
||||
drawMenuButtonLocal(renderer, *ctx.pixelFont, btnX + btnW * 0.6f, btnY, btnW, btnH, std::string(levelBtnText), SDL_Color{40,140,240,255}, SDL_Color{20,100,200,255});
|
||||
drawMenuButtonLocal(renderer, *ctx.pixelFont, btnX + btnW * 0.6f, btnY, btnW, btnH, std::string(levelBtnText), SDL_Color{40,140,240,255}, SDL_Color{20,100,200,255}, selectedButton == 1);
|
||||
{
|
||||
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render after draw LEVEL button\n"); fclose(f); }
|
||||
}
|
||||
|
||||
@ -10,4 +10,7 @@ public:
|
||||
void handleEvent(const SDL_Event& e) override;
|
||||
void update(double frameMs) override;
|
||||
void render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) override;
|
||||
|
||||
private:
|
||||
int selectedButton = 0; // 0 = PLAY, 1 = LEVEL
|
||||
};
|
||||
|
||||
@ -33,15 +33,30 @@ void PlayingState::handleEvent(const SDL_Event& e) {
|
||||
|
||||
// If exit-confirm popup is visible, handle shortcuts here
|
||||
if (ctx.showExitConfirmPopup && *ctx.showExitConfirmPopup) {
|
||||
// Confirm with Enter (main or keypad)
|
||||
if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER) {
|
||||
*ctx.showExitConfirmPopup = false;
|
||||
// Reset game and return to menu
|
||||
ctx.game->reset(false);
|
||||
if (ctx.stateManager) ctx.stateManager->setState(AppState::Menu);
|
||||
// Navigate between YES (0) and NO (1) buttons
|
||||
if (e.key.scancode == SDL_SCANCODE_LEFT || e.key.scancode == SDL_SCANCODE_UP) {
|
||||
exitPopupSelectedButton = 0; // YES
|
||||
return;
|
||||
}
|
||||
// Cancel with Esc
|
||||
if (e.key.scancode == SDL_SCANCODE_RIGHT || e.key.scancode == SDL_SCANCODE_DOWN) {
|
||||
exitPopupSelectedButton = 1; // NO
|
||||
return;
|
||||
}
|
||||
|
||||
// Activate selected button with Enter or Space
|
||||
if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER || e.key.scancode == SDL_SCANCODE_SPACE) {
|
||||
*ctx.showExitConfirmPopup = false;
|
||||
if (exitPopupSelectedButton == 0) {
|
||||
// YES - Reset game and return to menu
|
||||
ctx.game->reset(false);
|
||||
if (ctx.stateManager) ctx.stateManager->setState(AppState::Menu);
|
||||
} else {
|
||||
// NO - Just close popup and resume
|
||||
ctx.game->setPaused(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Cancel with Esc (same as NO)
|
||||
if (e.key.scancode == SDL_SCANCODE_ESCAPE) {
|
||||
*ctx.showExitConfirmPopup = false;
|
||||
ctx.game->setPaused(false);
|
||||
|
||||
@ -14,4 +14,5 @@ public:
|
||||
private:
|
||||
// Local per-state variables if needed
|
||||
bool localPaused = false;
|
||||
int exitPopupSelectedButton = 1; // 0 = YES, 1 = NO (default to NO for safety)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user