minor fix
This commit is contained in:
@ -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";
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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); }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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)
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user