minor fix

This commit is contained in:
2025-11-22 10:59:47 +01:00
parent ec2bb1bb1e
commit 0ffd801743
8 changed files with 105 additions and 32 deletions

View File

@ -122,7 +122,8 @@ void GameRenderer::renderPlayingState(
float logicalScale, float logicalScale,
float winW, float winW,
float winH, float winH,
bool showExitConfirmPopup bool showExitConfirmPopup,
int exitPopupSelectedButton
) { ) {
if (!game || !pixelFont) return; if (!game || !pixelFont) return;
@ -460,6 +461,10 @@ void GameRenderer::renderPlayingState(
float btnY = popupY + popupH - 60.0f; float btnY = popupY + popupH - 60.0f;
// YES button // 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 - 2.0f, btnY - 2.0f, btnW + 4.0f, btnH + 4.0f, {100, 120, 140, 255});
drawRectWithOffset(yesX, btnY, btnW, btnH, {200, 60, 60, 255}); drawRectWithOffset(yesX, btnY, btnW, btnH, {200, 60, 60, 255});
const std::string yes = "YES"; const std::string yes = "YES";
@ -469,6 +474,10 @@ void GameRenderer::renderPlayingState(
yes, 1.0f, {255, 255, 255, 255}); yes, 1.0f, {255, 255, 255, 255});
// NO button // 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 - 2.0f, btnY - 2.0f, btnW + 4.0f, btnH + 4.0f, {100, 120, 140, 255});
drawRectWithOffset(noX, btnY, btnW, btnH, {80, 140, 80, 255}); drawRectWithOffset(noX, btnY, btnW, btnH, {80, 140, 80, 255});
const std::string no = "NO"; const std::string no = "NO";

View File

@ -26,7 +26,8 @@ public:
float logicalScale, float logicalScale,
float winW, float winW,
float winH, float winH,
bool showExitConfirmPopup bool showExitConfirmPopup,
int exitPopupSelectedButton = 1 // 0=YES, 1=NO
); );
private: private:

View File

@ -193,6 +193,8 @@ void LevelSelectorState::handleEvent(const SDL_Event& e) {
// convert mouse to logical coords (viewport is already centered) // 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 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); 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); SDL_FRect panel = DrawPanel(nullptr, LOGICAL_W, LOGICAL_H, /*draw=*/false, 0.f, 0.f);
Grid g = MakeGrid(panel); Grid g = MakeGrid(panel);
int hit = HitTest(g, int(lx), int(ly)); 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) // 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 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); 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); SDL_FRect panel = DrawPanel(nullptr, LOGICAL_W, LOGICAL_H, /*draw=*/false, 0.f, 0.f);
Grid g = MakeGrid(panel); Grid g = MakeGrid(panel);
hoveredLevel = HitTest(g, int(lx), int(ly)); hoveredLevel = HitTest(g, int(lx), int(ly));
@ -225,28 +229,30 @@ void LevelSelectorState::render(SDL_Renderer* renderer, float logicalScale, SDL_
// Cache for input conversion // Cache for input conversion
lastLogicalScale = logicalScale; lastLogicalScale = logicalScale;
lastLogicalVP = logicalVP; 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; if (!renderer) return;
// Get dynamic logical dimensions // Use fixed logical dimensions to match main.cpp and ensure consistent layout
const int LOGICAL_W = GlobalState::instance().getLogicalWidth(); const float LOGICAL_W = 1200.f;
const int LOGICAL_H = GlobalState::instance().getLogicalHeight(); 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 // Panel and title strip (in logical space) - centered properly with offsets
// The viewport (LOGICAL_W x LOGICAL_H) is already centered within the window SDL_FRect panel = DrawPanel(renderer, LOGICAL_W, LOGICAL_H, /*draw=*/true, contentOffsetX, contentOffsetY);
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);
// Title text - prefer pixelFont for a blocky title if available, fallback to regular font // Title text - prefer pixelFont for a blocky title if available, fallback to regular font
FontAtlas* titleFont = ctx.pixelFont ? ctx.pixelFont : ctx.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 of levels
Grid g = MakeGrid(panel); Grid g = MakeGrid(panel);
@ -256,10 +262,10 @@ void LevelSelectorState::drawLevelSelectionPopup(SDL_Renderer* renderer) {
DrawCell(renderer, rc, i, hoveredLevel == i, selectedLevel == i); 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; FontAtlas* footerFont = ctx.pixelFont ? ctx.pixelFont : ctx.font;
DrawText(renderer, footerFont, "CLICK A LEVEL TO SELECT \u2022 ESC = CANCEL", DrawText(renderer, footerFont, "CLICK A LEVEL TO SELECT ESC = CANCEL",
vw / 2.f + offX, vh - 56.f, 1.0f, COL_FOOTER, true, true); 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) { bool LevelSelectorState::isMouseInPopup(float mouseX, float mouseY, float& popupX, float& popupY, float& popupW, float& popupH) {

View File

@ -19,7 +19,7 @@ private:
SDL_Rect lastLogicalVP{0,0,0,0}; SDL_Rect lastLogicalVP{0,0,0,0};
// Helper functions // 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); 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); int getLevelFromMouse(float mouseX, float mouseY, float popupX, float popupY, float popupW, float popupH);
void updateHoverFromMouse(float mouseX, float mouseY); void updateHoverFromMouse(float mouseX, float mouseY);

View File

@ -2,6 +2,7 @@
#include "persistence/Scores.h" #include "persistence/Scores.h"
#include "graphics/Font.h" #include "graphics/Font.h"
#include "../core/GlobalState.h" #include "../core/GlobalState.h"
#include "../core/state/StateManager.h"
#include "../audio/Audio.h" #include "../audio/Audio.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <cstdio> #include <cstdio>
@ -27,8 +28,37 @@ void MenuState::onExit() {
} }
void MenuState::handleEvent(const SDL_Event& e) { void MenuState::handleEvent(const SDL_Event& e) {
// Key-specific handling (allow main to handle global keys) // Keyboard navigation for menu buttons
(void)e; 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) { 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; int startLevel = ctx.startLevelSelection ? *ctx.startLevelSelection : 0;
std::snprintf(levelBtnText, sizeof(levelBtnText), "LEVEL %d", startLevel); std::snprintf(levelBtnText, sizeof(levelBtnText), "LEVEL %d", startLevel);
// Draw simple styled buttons (replicating menu_drawMenuButton) // 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; 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_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_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); 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+2, ty+2, label, textScale, SDL_Color{0,0,0,180});
font.draw(r, tx, ty, label, textScale, SDL_Color{255,255,255,255}); 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); } FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render after draw LEVEL button\n"); fclose(f); }
} }

View File

@ -10,4 +10,7 @@ public:
void handleEvent(const SDL_Event& e) override; void handleEvent(const SDL_Event& e) override;
void update(double frameMs) override; void update(double frameMs) override;
void render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) override; void render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) override;
private:
int selectedButton = 0; // 0 = PLAY, 1 = LEVEL
}; };

View File

@ -33,15 +33,30 @@ void PlayingState::handleEvent(const SDL_Event& e) {
// If exit-confirm popup is visible, handle shortcuts here // If exit-confirm popup is visible, handle shortcuts here
if (ctx.showExitConfirmPopup && *ctx.showExitConfirmPopup) { if (ctx.showExitConfirmPopup && *ctx.showExitConfirmPopup) {
// Confirm with Enter (main or keypad) // Navigate between YES (0) and NO (1) buttons
if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER) { if (e.key.scancode == SDL_SCANCODE_LEFT || e.key.scancode == SDL_SCANCODE_UP) {
*ctx.showExitConfirmPopup = false; exitPopupSelectedButton = 0; // YES
// Reset game and return to menu
ctx.game->reset(false);
if (ctx.stateManager) ctx.stateManager->setState(AppState::Menu);
return; 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) { if (e.key.scancode == SDL_SCANCODE_ESCAPE) {
*ctx.showExitConfirmPopup = false; *ctx.showExitConfirmPopup = false;
ctx.game->setPaused(false); ctx.game->setPaused(false);

View File

@ -14,4 +14,5 @@ public:
private: private:
// Local per-state variables if needed // Local per-state variables if needed
bool localPaused = false; bool localPaused = false;
int exitPopupSelectedButton = 1; // 0 = YES, 1 = NO (default to NO for safety)
}; };