Browse Source

Make options menu functional

master
C. J. Howard 7 years ago
parent
commit
d3dd81a6a5
6 changed files with 587 additions and 170 deletions
  1. +364
    -122
      src/application.cpp
  2. +23
    -11
      src/application.hpp
  3. +33
    -6
      src/states/game-state.cpp
  4. +23
    -5
      src/states/title-state.cpp
  5. +109
    -22
      src/ui/menu.cpp
  6. +35
    -4
      src/ui/menu.hpp

+ 364
- 122
src/application.cpp View File

@ -43,6 +43,8 @@
#define OPENGL_VERSION_MAJOR 3 #define OPENGL_VERSION_MAJOR 3
#define OPENGL_VERSION_MINOR 3 #define OPENGL_VERSION_MINOR 3
#undef max
Application::Application(int argc, char* argv[]): Application::Application(int argc, char* argv[]):
state(nullptr), state(nullptr),
nextState(nullptr), nextState(nullptr),
@ -110,10 +112,6 @@ Application::Application(int argc, char* argv[]):
// Get values of required settings // Get values of required settings
settings.get("fullscreen", &fullscreen); settings.get("fullscreen", &fullscreen);
settings.get("fullscreen_width", &fullscreenWidth);
settings.get("fullscreen_height", &fullscreenHeight);
settings.get("windowed_width", &windowedWidth);
settings.get("windowed_height", &windowedHeight);
settings.get("swap_interval", &swapInterval); settings.get("swap_interval", &swapInterval);
// Load strings // Load strings
@ -141,61 +139,83 @@ Application::Application(int argc, char* argv[]):
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
// Check desktop display mode
SDL_DisplayMode displayMode;
if (SDL_GetDesktopDisplayMode(0, &displayMode) != 0)
{
std::cerr << "Failed to get desktop display mode: \"" << SDL_GetError() << "\"" << std::endl;
close(EXIT_FAILURE);
return;
}
/*
// Check (usable?) display bounds
SDL_Rect displayBounds;
if (SDL_GetDisplayBounds(0, &displayBounds) != 0)
// Get all possible display modes for the default display
int displayModeCount = SDL_GetNumDisplayModes(0);
for (int i = displayModeCount - 1; i >= 0; --i)
{ {
std::cerr << "Failed to get display bounds: \"" << SDL_GetError() << "\"" << std::endl;
close(EXIT_FAILURE);
return;
SDL_DisplayMode displayMode;
if (SDL_GetDisplayMode(0, i, &displayMode) != 0)
{
std::cerr << "Failed to get display mode: \"" << SDL_GetError() << "\"" << std::endl;
close(EXIT_FAILURE);
return;
}
resolutions.push_back(Vector2(displayMode.w, displayMode.h));
} }
*/
/*
SDL_DisplayMode displayMode;
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0)
// Read requested windowed and fullscreen resolutions from settings
Vector2 requestedWindowedResolution;
Vector2 requestedFullscreenResolution;
settings.get("windowed_width", &requestedWindowedResolution.x);
settings.get("windowed_height", &requestedWindowedResolution.y);
settings.get("fullscreen_width", &requestedFullscreenResolution.x);
settings.get("fullscreen_height", &requestedFullscreenResolution.y);
// Determine desktop resolution
SDL_DisplayMode desktopDisplayMode;
if (SDL_GetDesktopDisplayMode(0, &desktopDisplayMode) != 0)
{ {
std::cerr << "Failed to get display mode: \"" << SDL_GetError() << "\"" << std::endl;
std::cerr << "Failed to get desktop display mode: \"" << SDL_GetError() << "\"" << std::endl;
close(EXIT_FAILURE); close(EXIT_FAILURE);
return; return;
} }
*/
// Use display resolution if settings request
if (windowedWidth == -1 || windowedHeight == -1)
{
windowedWidth = displayMode.w;
windowedHeight = displayMode.h;
}
if (fullscreenWidth == -1 || fullscreenHeight == -1)
Vector2 desktopResolution;
desktopResolution.x = static_cast<float>(desktopDisplayMode.w);
desktopResolution.y = static_cast<float>(desktopDisplayMode.h);
// Replace requested resolutions of -1 with native resolution
requestedWindowedResolution.x = (requestedWindowedResolution.x == -1.0f) ? desktopResolution.x : requestedWindowedResolution.x;
requestedWindowedResolution.y = (requestedWindowedResolution.y == -1.0f) ? desktopResolution.y : requestedWindowedResolution.y;
requestedFullscreenResolution.x = (requestedFullscreenResolution.x == -1.0f) ? desktopResolution.x : requestedFullscreenResolution.x;
requestedFullscreenResolution.y = (requestedFullscreenResolution.y == -1.0f) ? desktopResolution.y : requestedFullscreenResolution.y;
// Find indices of closest resolutions to requested windowed and fullscreen resolutions
windowedResolutionIndex = 0;
fullscreenResolutionIndex = 0;
float minWindowedResolutionDistance = std::numeric_limits<float>::max();
float minFullscreenResolutionDistance = std::numeric_limits<float>::max();
for (std::size_t i = 0; i < resolutions.size(); ++i)
{ {
fullscreenWidth = displayMode.w;
fullscreenHeight = displayMode.h;
Vector2 windowedResolutionDifference = resolutions[i] - requestedWindowedResolution;
float windowedResolutionDistance = glm::dot(windowedResolutionDifference, windowedResolutionDifference);
if (windowedResolutionDistance <= minWindowedResolutionDistance)
{
minWindowedResolutionDistance = windowedResolutionDistance;
windowedResolutionIndex = i;
}
Vector2 fullscreenResolutionDifference = resolutions[i] - requestedFullscreenResolution;
float fullscreenResolutionDistance = glm::dot(fullscreenResolutionDifference, fullscreenResolutionDifference);
if (fullscreenResolutionDistance <= minFullscreenResolutionDistance)
{
minFullscreenResolutionDistance = fullscreenResolutionDistance;
fullscreenResolutionIndex = i;
}
} }
// Determine window parameters
// Determine window parameters and current resolution
Uint32 windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI; Uint32 windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI;
if (fullscreen) if (fullscreen)
{ {
width = fullscreenWidth;
height = fullscreenHeight;
resolution = resolutions[fullscreenResolutionIndex];
windowFlags |= SDL_WINDOW_FULLSCREEN; windowFlags |= SDL_WINDOW_FULLSCREEN;
} }
else else
{ {
width = windowedWidth;
height = windowedHeight;
resolution = resolutions[windowedResolutionIndex];
} }
// Get window title string // Get window title string
@ -203,10 +223,10 @@ Application::Application(int argc, char* argv[]):
strings.get("title", &title); strings.get("title", &title);
// Create window // Create window
std::cout << "Creating a " << width << "x" << height;
std::cout << "Creating a " << resolution.x << "x" << resolution.y;
std::cout << ((fullscreen) ? " fullscreen" : " windowed"); std::cout << ((fullscreen) ? " fullscreen" : " windowed");
std::cout << " window... "; std::cout << " window... ";
window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, windowFlags);
window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast<int>(resolution.x), static_cast<int>(resolution.y), windowFlags);
if (window == nullptr) if (window == nullptr)
{ {
std::cout << "failed: \"" << SDL_GetError() << "\"" << std::endl; std::cout << "failed: \"" << SDL_GetError() << "\"" << std::endl;
@ -218,19 +238,6 @@ Application::Application(int argc, char* argv[]):
std::cout << "success" << std::endl; std::cout << "success" << std::endl;
} }
// Get actual window size
SDL_GetWindowSize(window, &width, &height);
if (fullscreen)
{
fullscreenWidth = width;
fullscreenHeight = height;
}
else
{
windowedWidth = width;
windowedHeight = height;
}
// Print video driver // Print video driver
const char* videoDriver = SDL_GetCurrentVideoDriver(); const char* videoDriver = SDL_GetCurrentVideoDriver();
if (!videoDriver) if (!videoDriver)
@ -428,6 +435,10 @@ int Application::execute()
// Reset frame timer to counteract frames eaten by state exit() and enter() functions // Reset frame timer to counteract frames eaten by state exit() and enter() functions
frameTimer.reset(); frameTimer.reset();
} }
else
{
break;
}
} }
// Update input // Update input
@ -507,10 +518,9 @@ void Application::changeFullscreen()
if (fullscreen) if (fullscreen)
{ {
width = fullscreenWidth;
height = fullscreenHeight;
resolution = resolutions[fullscreenResolutionIndex];
SDL_SetWindowSize(window, width, height);
SDL_SetWindowSize(window, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) != 0) if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) != 0)
{ {
std::cerr << "Failed to set fullscreen mode: \"" << SDL_GetError() << "\"" << std::endl; std::cerr << "Failed to set fullscreen mode: \"" << SDL_GetError() << "\"" << std::endl;
@ -519,9 +529,8 @@ void Application::changeFullscreen()
} }
else else
{ {
width = windowedWidth;
height = windowedHeight;
resolution = resolutions[windowedResolutionIndex];
if (SDL_SetWindowFullscreen(window, 0) != 0) if (SDL_SetWindowFullscreen(window, 0) != 0)
{ {
std::cerr << "Failed to set windowed mode: \"" << SDL_GetError() << "\"" << std::endl; std::cerr << "Failed to set windowed mode: \"" << SDL_GetError() << "\"" << std::endl;
@ -529,22 +538,19 @@ void Application::changeFullscreen()
} }
else else
{ {
SDL_SetWindowSize(window, width, height);
SDL_SetWindowSize(window, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
} }
} }
// Get actual window size
SDL_GetWindowSize(window, &width, &height);
// Print mode and resolution // Print mode and resolution
if (fullscreen) if (fullscreen)
{ {
std::cout << "Changed to fullscreen mode at resolution " << width << "x" << height << std::endl;
std::cout << "Changed to fullscreen mode at resolution " << resolution.x << "x" << resolution.y << std::endl;
} }
else else
{ {
std::cout << "Changed to windowed mode at resolution " << width << "x" << height << std::endl;
std::cout << "Changed to windowed mode at resolution " << resolution.x << "x" << resolution.y << std::endl;
} }
// Save settings // Save settings
@ -765,6 +771,12 @@ bool Application::loadUI()
std::cerr << "Failed to load copyright font" << std::endl; std::cerr << "Failed to load copyright font" << std::endl;
} }
levelNameFont = new Font(512, 512);
if (!fontLoader->load("data/fonts/Vollkorn-Regular.ttf", static_cast<int>(fontSizePX * 2.0f + 0.5f), levelNameFont))
{
std::cerr << "Failed to load level name font" << std::endl;
}
delete fontLoader; delete fontLoader;
// Load UI textures // Load UI textures
@ -848,13 +860,13 @@ bool Application::loadUI()
// Setup root UI element // Setup root UI element
uiRootElement = new UIContainer(); uiRootElement = new UIContainer();
uiRootElement->setDimensions(Vector2(width, height));
uiRootElement->setDimensions(resolution);
mouse->addMouseMotionObserver(uiRootElement); mouse->addMouseMotionObserver(uiRootElement);
mouse->addMouseButtonObserver(uiRootElement); mouse->addMouseButtonObserver(uiRootElement);
// Create blackout element (for screen transitions) // Create blackout element (for screen transitions)
blackoutImage = new UIImage(); blackoutImage = new UIImage();
blackoutImage->setDimensions(Vector2(width, height));
blackoutImage->setDimensions(resolution);
blackoutImage->setLayerOffset(ANTKEEPER_UI_LAYER_BLACKOUT); blackoutImage->setLayerOffset(ANTKEEPER_UI_LAYER_BLACKOUT);
blackoutImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f)); blackoutImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f));
blackoutImage->setVisible(false); blackoutImage->setVisible(false);
@ -862,7 +874,7 @@ bool Application::loadUI()
// Create darken element (for darkening title screen) // Create darken element (for darkening title screen)
darkenImage = new UIImage(); darkenImage = new UIImage();
darkenImage->setDimensions(Vector2(width, height));
darkenImage->setDimensions(resolution);
darkenImage->setLayerOffset(ANTKEEPER_UI_LAYER_DARKEN); darkenImage->setLayerOffset(ANTKEEPER_UI_LAYER_DARKEN);
darkenImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 0.35f)); darkenImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 0.35f));
darkenImage->setVisible(false); darkenImage->setVisible(false);
@ -870,7 +882,7 @@ bool Application::loadUI()
// Create splash screen background element // Create splash screen background element
splashBackgroundImage = new UIImage(); splashBackgroundImage = new UIImage();
splashBackgroundImage->setDimensions(Vector2(width, height));
splashBackgroundImage->setDimensions(resolution);
splashBackgroundImage->setLayerOffset(-1); splashBackgroundImage->setLayerOffset(-1);
splashBackgroundImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f)); splashBackgroundImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f));
splashBackgroundImage->setVisible(false); splashBackgroundImage->setVisible(false);
@ -888,7 +900,7 @@ bool Application::loadUI()
titleImage = new UIImage(); titleImage = new UIImage();
titleImage->setAnchor(Vector2(0.5f, 0.0f)); titleImage->setAnchor(Vector2(0.5f, 0.0f));
titleImage->setDimensions(Vector2(titleTexture->getWidth(), titleTexture->getHeight())); titleImage->setDimensions(Vector2(titleTexture->getWidth(), titleTexture->getHeight()));
titleImage->setTranslation(Vector2(0.0f, (int)(height * (1.0f / 4.0f) - titleTexture->getHeight() * 0.5f)));
titleImage->setTranslation(Vector2(0.0f, (int)(resolution.y * (1.0f / 4.0f) - titleTexture->getHeight() * 0.5f)));
titleImage->setTexture(titleTexture); titleImage->setTexture(titleTexture);
titleImage->setVisible(false); titleImage->setVisible(false);
titleImage->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); titleImage->setLayerOffset(ANTKEEPER_UI_LAYER_MENU);
@ -912,7 +924,7 @@ bool Application::loadUI()
anyKeyLabel = new UILabel(); anyKeyLabel = new UILabel();
anyKeyLabel->setAnchor(Vector2(0.5f, 1.0f)); anyKeyLabel->setAnchor(Vector2(0.5f, 1.0f));
anyKeyLabel->setFont(menuFont); anyKeyLabel->setFont(menuFont);
anyKeyLabel->setTranslation(Vector2(0.0f, (int)(-height * (1.0f / 4.0f) - menuFont->getMetrics().getHeight() * 0.5f)));
anyKeyLabel->setTranslation(Vector2(0.0f, (int)(-resolution.y * (1.0f / 4.0f) - menuFont->getMetrics().getHeight() * 0.5f)));
anyKeyLabel->setText(pressAnyKeyString); anyKeyLabel->setText(pressAnyKeyString);
anyKeyLabel->setVisible(false); anyKeyLabel->setVisible(false);
uiRootElement->addChild(anyKeyLabel); uiRootElement->addChild(anyKeyLabel);
@ -949,22 +961,12 @@ bool Application::loadUI()
depthTextureImage->setVisible(false); depthTextureImage->setVisible(false);
uiRootElement->addChild(depthTextureImage); uiRootElement->addChild(depthTextureImage);
// Create level ID
levelIDLabel = new UILabel();
levelIDLabel->setAnchor(Vector2(0.5f, 0.0f));
levelIDLabel->setFont(menuFont);
levelIDLabel->setTranslation(Vector2(0.0f, (int)(height * (1.0f / 4.0f) - menuFont->getMetrics().getHeight() * 1.5f)));
levelIDLabel->setText("");
levelIDLabel->setVisible(false);
uiRootElement->addChild(levelIDLabel);
// Create level name label // Create level name label
levelNameLabel = new UILabel(); levelNameLabel = new UILabel();
levelNameLabel->setAnchor(Vector2(0.5f, 0.0f));
levelNameLabel->setFont(menuFont);
levelNameLabel->setTranslation(Vector2(0.0f, (int)(height * (1.0f / 4.0f) - menuFont->getMetrics().getHeight() * 0.5f)));
levelNameLabel->setText("");
levelNameLabel->setAnchor(Vector2(0.5f, 0.5f));
levelNameLabel->setFont(levelNameFont);
levelNameLabel->setVisible(false); levelNameLabel->setVisible(false);
levelNameLabel->setLayerOffset(ANTKEEPER_UI_LAYER_HUD);
uiRootElement->addChild(levelNameLabel); uiRootElement->addChild(levelNameLabel);
// Create toolbar // Create toolbar
@ -979,7 +981,7 @@ bool Application::loadUI()
toolbar->addButton(toolForcepsTexture, std::bind(&std::printf, "2\n"), std::bind(&std::printf, "2\n")); toolbar->addButton(toolForcepsTexture, std::bind(&std::printf, "2\n"), std::bind(&std::printf, "2\n"));
toolbar->addButton(toolTrowelTexture, std::bind(&std::printf, "3\n"), std::bind(&std::printf, "3\n")); toolbar->addButton(toolTrowelTexture, std::bind(&std::printf, "3\n"), std::bind(&std::printf, "3\n"));
toolbar->resize(); toolbar->resize();
uiRootElement->addChild(toolbar->getContainer());
//uiRootElement->addChild(toolbar->getContainer());
toolbar->getContainer()->setVisible(false); toolbar->getContainer()->setVisible(false);
toolbar->getContainer()->setActive(false); toolbar->getContainer()->setActive(false);
@ -1046,7 +1048,7 @@ bool Application::loadUI()
Vector4 menuFadeOutDeltaColor = Vector4(0.0f, 0.0f, 0.0f, -1.0f); Vector4 menuFadeOutDeltaColor = Vector4(0.0f, 0.0f, 0.0f, -1.0f);
float menuSlideInDuration = 0.35f; float menuSlideInDuration = 0.35f;
Vector2 menuSlideInStartTranslation = Vector2(-64.0f, 0.0f); Vector2 menuSlideInStartTranslation = Vector2(-64.0f, 0.0f);
Vector2 menuSlideInDeltaTranslation = Vector2((int)(64.0f + width / 8.0f), 0.0f);
Vector2 menuSlideInDeltaTranslation = Vector2((int)(64.0f + resolution.x / 8.0f), 0.0f);
// Setup main menu tween // Setup main menu tween
menuFadeInTween = new Tween<Vector4>(EaseFunction::OUT_QUINT, 0.0f, menuFadeInDuration, menuFadeInStartColor, menuFadeInDeltaColor); menuFadeInTween = new Tween<Vector4>(EaseFunction::OUT_QUINT, 0.0f, menuFadeInDuration, menuFadeInStartColor, menuFadeInDeltaColor);
@ -1083,27 +1085,27 @@ bool Application::loadUI()
MenuItem* continueItem = mainMenu->addItem(); MenuItem* continueItem = mainMenu->addItem();
continueItem->setActivatedCallback(std::bind(&Application::continueGame, this)); continueItem->setActivatedCallback(std::bind(&Application::continueGame, this));
continueItem->setLabel(continueString);
MenuItem* newGameItem = mainMenu->addItem();
newGameItem->setActivatedCallback(std::bind(&Application::newGame, this));
newGameItem->setLabel(newGameString);
continueItem->setName(continueString);
MenuItem* levelsItem = mainMenu->addItem(); MenuItem* levelsItem = mainMenu->addItem();
levelsItem->setActivatedCallback(std::bind(&Application::openMenu, this, levelsMenu)); levelsItem->setActivatedCallback(std::bind(&Application::openMenu, this, levelsMenu));
levelsItem->setLabel(levelsString);
levelsItem->setName(levelsString);
MenuItem* newGameItem = mainMenu->addItem();
newGameItem->setActivatedCallback(std::bind(&Application::newGame, this));
newGameItem->setName(newGameString);
MenuItem* sandboxItem = mainMenu->addItem(); MenuItem* sandboxItem = mainMenu->addItem();
sandboxItem->setActivatedCallback(std::bind(&std::printf, "1\n")); sandboxItem->setActivatedCallback(std::bind(&std::printf, "1\n"));
sandboxItem->setLabel(sandboxString);
sandboxItem->setName(sandboxString);
MenuItem* optionsItem = mainMenu->addItem(); MenuItem* optionsItem = mainMenu->addItem();
optionsItem->setActivatedCallback(std::bind(&Application::openMenu, this, optionsMenu)); optionsItem->setActivatedCallback(std::bind(&Application::openMenu, this, optionsMenu));
optionsItem->setLabel(optionsString);
optionsItem->setName(optionsString);
MenuItem* exitItem = mainMenu->addItem(); MenuItem* exitItem = mainMenu->addItem();
exitItem->setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS)); exitItem->setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS));
exitItem->setLabel(exitString);
exitItem->setName(exitString);
mainMenu->getUIContainer()->setActive(false); mainMenu->getUIContainer()->setActive(false);
mainMenu->getUIContainer()->setVisible(false); mainMenu->getUIContainer()->setVisible(false);
@ -1138,13 +1140,23 @@ bool Application::loadUI()
( (
[this, world, level]() [this, world, level]()
{ {
closeMenu();
loadWorld(world); loadWorld(world);
loadLevel(level); loadLevel(level);
changeState(gameState);
// Close levels menu
closeMenu();
// Begin title fade-out
titleFadeOutTween->reset();
titleFadeOutTween->start();
// Begin fade-out
fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState));
fadeOutTween->reset();
fadeOutTween->start();
} }
); );
levelItem->setLabel(label);
levelItem->setName(label);
} }
} }
@ -1156,7 +1168,7 @@ bool Application::loadUI()
openMenu(previousActiveMenu); openMenu(previousActiveMenu);
} }
); );
backItem->setLabel(backString);
backItem->setName(backString);
levelsMenu->getUIContainer()->setActive(false); levelsMenu->getUIContainer()->setActive(false);
levelsMenu->getUIContainer()->setVisible(false); levelsMenu->getUIContainer()->setVisible(false);
@ -1169,12 +1181,51 @@ bool Application::loadUI()
optionsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f)); optionsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f));
optionsMenu->getUIContainer()->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); optionsMenu->getUIContainer()->setLayerOffset(ANTKEEPER_UI_LAYER_MENU);
optionsMenu->setLineSpacing(1.0f); optionsMenu->setLineSpacing(1.0f);
optionsMenu->setColumnMargin(menuFont->getWidth("MM"));
MenuItem* resolutionItem = optionsMenu->addItem();
resolutionItem->setLabel("Resolution");
MenuItem* windowedResolutionItem = optionsMenu->addItem();
windowedResolutionItem->setName("Windowed Resolution");
MenuItem* fullscreenResolutionItem = optionsMenu->addItem();
fullscreenResolutionItem->setName("Fullscreen Resolution");
for (const Vector2& resolution: resolutions)
{
std::stringstream stream;
stream << resolution.x << "x" << resolution.y;
windowedResolutionItem->addValue(stream.str());
fullscreenResolutionItem->addValue(stream.str());
}
windowedResolutionItem->setValueIndex(windowedResolutionIndex);
windowedResolutionItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this));
windowedResolutionItem->setValueChangedCallback(std::bind(&Application::selectWindowedResolution, this, std::placeholders::_1));
fullscreenResolutionItem->setValueIndex(fullscreenResolutionIndex);
fullscreenResolutionItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this));
fullscreenResolutionItem->setValueChangedCallback(std::bind(&Application::selectFullscreenResolution, this, std::placeholders::_1));
MenuItem* fullscreenItem = optionsMenu->addItem(); MenuItem* fullscreenItem = optionsMenu->addItem();
fullscreenItem->setLabel("Fullscreen");
fullscreenItem->setName("Fullscreen");
fullscreenItem->addValue("Off");
fullscreenItem->addValue("On");
fullscreenItem->setValueIndex((fullscreen == 0) ? 0 : 1);
fullscreenItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this));
fullscreenItem->setValueChangedCallback(std::bind(&Application::selectFullscreenMode, this, std::placeholders::_1));
MenuItem* vsyncItem = optionsMenu->addItem();
vsyncItem->setName("VSync");
vsyncItem->addValue("Off");
vsyncItem->addValue("On");
vsyncItem->setValueIndex((swapInterval == 0) ? 0 : 1);
vsyncItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this));
vsyncItem->setValueChangedCallback(std::bind(&Application::selectVSyncMode, this, std::placeholders::_1));
MenuItem* languageItem = optionsMenu->addItem();
languageItem->setName("Language");
languageItem->addValue("English");
languageItem->addValue("Chinese");
languageItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this));
MenuItem* controlsItem = optionsMenu->addItem();
controlsItem->setName("Controls");
MenuItem* backItem = optionsMenu->addItem(); MenuItem* backItem = optionsMenu->addItem();
backItem->setActivatedCallback backItem->setActivatedCallback
@ -1184,7 +1235,7 @@ bool Application::loadUI()
openMenu(previousActiveMenu); openMenu(previousActiveMenu);
} }
); );
backItem->setLabel(backString);
backItem->setName(backString);
optionsMenu->getUIContainer()->setActive(false); optionsMenu->getUIContainer()->setActive(false);
optionsMenu->getUIContainer()->setVisible(false); optionsMenu->getUIContainer()->setVisible(false);
@ -1200,15 +1251,15 @@ bool Application::loadUI()
MenuItem* resumeItem = pauseMenu->addItem(); MenuItem* resumeItem = pauseMenu->addItem();
resumeItem->setActivatedCallback(std::bind(&Application::unpauseSimulation, this)); resumeItem->setActivatedCallback(std::bind(&Application::unpauseSimulation, this));
resumeItem->setLabel("Resume");
resumeItem->setName("Resume");
MenuItem* levelsItem = pauseMenu->addItem(); MenuItem* levelsItem = pauseMenu->addItem();
levelsItem->setActivatedCallback(std::bind(&Application::openMenu, this, levelsMenu)); levelsItem->setActivatedCallback(std::bind(&Application::openMenu, this, levelsMenu));
levelsItem->setLabel(levelsString);
levelsItem->setName(levelsString);
MenuItem* optionsItem = pauseMenu->addItem(); MenuItem* optionsItem = pauseMenu->addItem();
optionsItem->setActivatedCallback(std::bind(&Application::openMenu, this, optionsMenu)); optionsItem->setActivatedCallback(std::bind(&Application::openMenu, this, optionsMenu));
optionsItem->setLabel(optionsString);
optionsItem->setName(optionsString);
MenuItem* mainMenuItem = pauseMenu->addItem(); MenuItem* mainMenuItem = pauseMenu->addItem();
mainMenuItem->setActivatedCallback mainMenuItem->setActivatedCallback
@ -1224,11 +1275,11 @@ bool Application::loadUI()
fadeOutTween->start(); fadeOutTween->start();
} }
); );
mainMenuItem->setLabel("Main Menu");
mainMenuItem->setName("Main Menu");
MenuItem* exitItem = pauseMenu->addItem(); MenuItem* exitItem = pauseMenu->addItem();
exitItem->setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS)); exitItem->setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS));
exitItem->setLabel(exitString);
exitItem->setName(exitString);
pauseMenu->getUIContainer()->setActive(false); pauseMenu->getUIContainer()->setActive(false);
pauseMenu->getUIContainer()->setVisible(false); pauseMenu->getUIContainer()->setVisible(false);
@ -1254,8 +1305,8 @@ bool Application::loadUI()
uiLayer->addObject(uiBatch); uiLayer->addObject(uiBatch);
uiLayer->addObject(&uiCamera); uiLayer->addObject(&uiCamera);
defaultRenderTarget.width = width;
defaultRenderTarget.height = height;
defaultRenderTarget.width = static_cast<int>(resolution.x);
defaultRenderTarget.height = static_cast<int>(resolution.y);
defaultRenderTarget.framebuffer = 0; defaultRenderTarget.framebuffer = 0;
resizeUI(); resizeUI();
@ -1371,11 +1422,11 @@ bool Application::loadGame()
void Application::resizeUI() void Application::resizeUI()
{ {
// Adjust UI dimensions // Adjust UI dimensions
uiRootElement->setDimensions(Vector2(width, height));
uiRootElement->setDimensions(resolution);
uiRootElement->update(); uiRootElement->update();
// Adjust UI camera projection // Adjust UI camera projection
uiCamera.setOrthographic(0.0f, static_cast<float>(width), static_cast<float>(height), 0.0f, -1.0f, 1.0f);
uiCamera.setOrthographic(0.0f, resolution.x, resolution.y, 0.0f, -1.0f, 1.0f);
} }
void Application::openMenu(Menu* menu) void Application::openMenu(Menu* menu)
@ -1423,6 +1474,43 @@ void Application::activateMenuItem()
} }
} }
void Application::incrementMenuItem()
{
if (activeMenu != nullptr)
{
MenuItem* item = activeMenu->getSelectedItem();
if (item != nullptr)
{
if (item->getValueCount() != 0)
{
item->setValueIndex((item->getValueIndex() + 1) % item->getValueCount());
}
}
}
}
void Application::decrementMenuItem()
{
if (activeMenu != nullptr)
{
MenuItem* item = activeMenu->getSelectedItem();
if (item != nullptr)
{
if (item->getValueCount() != 0)
{
if (!item->getValueIndex())
{
item->setValueIndex(item->getValueCount() - 1);
}
else
{
item->setValueIndex(item->getValueIndex() - 1);
}
}
}
}
}
void Application::continueGame() void Application::continueGame()
{ {
closeMenu(); closeMenu();
@ -1441,8 +1529,15 @@ void Application::continueGame()
{ {
loadLevel(level); loadLevel(level);
} }
// Begin title fade-out
titleFadeOutTween->reset();
titleFadeOutTween->start();
changeState(gameState);
// Begin fade-out
fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState));
fadeOutTween->reset();
fadeOutTween->start();
} }
void Application::newGame() void Application::newGame()
@ -1459,11 +1554,6 @@ void Application::newGame()
// Select first level of the first world // Select first level of the first world
currentWorldIndex = 0; currentWorldIndex = 0;
currentLevelIndex = 0; currentLevelIndex = 0;
// Save continue world and level indices
settings.set("continue_world", currentWorldIndex);
settings.set("continue_level", currentLevelIndex);
saveUserSettings();
// Begin fade-out // Begin fade-out
fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState)); fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState));
@ -1583,4 +1673,156 @@ void Application::setDisplayDebugInfo(bool display)
frameTimeLabel->setVisible(displayDebugInfo); frameTimeLabel->setVisible(displayDebugInfo);
depthTextureImage->setVisible(displayDebugInfo); depthTextureImage->setVisible(displayDebugInfo);
}
}
std::string Application::getLevelName(std::size_t world, std::size_t level) const
{
// Form level ID string
char levelIDBuffer[6];
std::sprintf(levelIDBuffer, "%02d-%02d", static_cast<int>(world + 1), static_cast<int>(level + 1));
std::string levelID(levelIDBuffer);
// Look up level name
std::string levelName;
strings.get(levelIDBuffer, &levelName);
return levelName;
}
void Application::selectWindowedResolution(std::size_t index)
{
windowedResolutionIndex = index;
if (!fullscreen)
{
// Select resolution
resolution = resolutions[windowedResolutionIndex];
// Resize window
SDL_SetWindowSize(window, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
// Resize UI
resizeUI();
// Notify window observers
inputManager->update();
}
// Save settings
settings.set("windowed_width", resolutions[windowedResolutionIndex].x);
settings.set("windowed_height", resolutions[windowedResolutionIndex].y);
saveUserSettings();
}
void Application::selectFullscreenResolution(std::size_t index)
{
fullscreenResolutionIndex = index;
if (fullscreen)
{
// Select resolution
resolution = resolutions[fullscreenResolutionIndex];
// Resize window
SDL_SetWindowSize(window, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
// Resize UI
resizeUI();
// Notify window observers
inputManager->update();
}
// Save settings
settings.set("fullscreen_width", resolutions[fullscreenResolutionIndex].x);
settings.set("fullscreen_height", resolutions[fullscreenResolutionIndex].y);
saveUserSettings();
}
void Application::selectFullscreenMode(std::size_t index)
{
fullscreen = (index == 1);
if (fullscreen)
{
resolution = resolutions[fullscreenResolutionIndex];
SDL_SetWindowSize(window, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) != 0)
{
std::cerr << "Failed to set fullscreen mode: \"" << SDL_GetError() << "\"" << std::endl;
fullscreen = false;
}
}
else
{
resolution = resolutions[windowedResolutionIndex];
if (SDL_SetWindowFullscreen(window, 0) != 0)
{
std::cerr << "Failed to set windowed mode: \"" << SDL_GetError() << "\"" << std::endl;
fullscreen = true;
}
else
{
SDL_SetWindowSize(window, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
}
// Print mode and resolution
if (fullscreen)
{
std::cout << "Changed to fullscreen mode at resolution " << resolution.x << "x" << resolution.y << std::endl;
}
else
{
std::cout << "Changed to windowed mode at resolution " << resolution.x << "x" << resolution.y << std::endl;
}
// Save settings
settings.set("fullscreen", fullscreen);
saveUserSettings();
// Resize UI
resizeUI();
// Notify window observers
inputManager->update();
}
// index: 0 = off, 1 = on
void Application::selectVSyncMode(std::size_t index)
{
swapInterval = (index == 0) ? 0 : 1;
if (swapInterval == 1)
{
std::cout << "Enabling vertical sync... ";
}
else
{
std::cout << "Disabling vertical sync... ";
}
if (SDL_GL_SetSwapInterval(swapInterval) != 0)
{
std::cout << "failed: \"" << SDL_GetError() << "\"" << std::endl;
swapInterval = SDL_GL_GetSwapInterval();
}
else
{
std::cout << "success" << std::endl;
}
// Save settings
settings.set("swap_interval", swapInterval);
saveUserSettings();
}
void Application::selectLanguage(std::size_t index)
{
}

+ 23
- 11
src/application.hpp View File

@ -92,6 +92,8 @@ public:
void closeMenu(); void closeMenu();
void selectMenuItem(std::size_t index); void selectMenuItem(std::size_t index);
void activateMenuItem(); void activateMenuItem();
void incrementMenuItem();
void decrementMenuItem();
void loadWorld(std::size_t index); void loadWorld(std::size_t index);
void loadLevel(std::size_t index); void loadLevel(std::size_t index);
@ -108,6 +110,15 @@ public:
void setDisplayDebugInfo(bool display); void setDisplayDebugInfo(bool display);
std::string getLevelName(std::size_t world, std::size_t level) const;
// Options menu functions
void selectWindowedResolution(std::size_t index);
void selectFullscreenResolution(std::size_t index);
void selectFullscreenMode(std::size_t index);
void selectVSyncMode(std::size_t index);
void selectLanguage(std::size_t index);
private: private:
ApplicationState* state; ApplicationState* state;
ApplicationState* nextState; ApplicationState* nextState;
@ -127,16 +138,6 @@ public:
// Settings // Settings
ParameterDict settings; ParameterDict settings;
// Window
bool fullscreen;
int fullscreenWidth;
int fullscreenHeight;
int windowedWidth;
int windowedHeight;
int swapInterval;
int width;
int height;
// State machine // State machine
LoadingState* loadingState; LoadingState* loadingState;
SplashState* splashState; SplashState* splashState;
@ -234,6 +235,7 @@ public:
float fontSizePX; float fontSizePX;
Font* menuFont; Font* menuFont;
Font* copyrightFont; Font* copyrightFont;
Font* levelNameFont;
// UI textures // UI textures
Texture* splashTexture; Texture* splashTexture;
@ -276,7 +278,6 @@ public:
UIImage* contextButtonImage0; UIImage* contextButtonImage0;
UIImage* contextButtonImage1; UIImage* contextButtonImage1;
UIImage* depthTextureImage; UIImage* depthTextureImage;
UILabel* levelIDLabel;
UILabel* levelNameLabel; UILabel* levelNameLabel;
Toolbar* toolbar; Toolbar* toolbar;
PieMenu* pieMenu; PieMenu* pieMenu;
@ -337,6 +338,17 @@ public:
// Debug // Debug
LineBatcher* lineBatcher; LineBatcher* lineBatcher;
bool displayDebugInfo; bool displayDebugInfo;
// Options menu values
bool fullscreen;
int swapInterval;
Vector2 resolution;
std::vector<Vector2> resolutions;
std::size_t windowedResolutionIndex;
std::size_t fullscreenResolutionIndex;
int* fullscreenModes;
int* vsyncModes;
std::string* languages;
}; };
#endif // APPLICATION_HPP #endif // APPLICATION_HPP

+ 33
- 6
src/states/game-state.cpp View File

@ -38,6 +38,19 @@ GameState::~GameState()
void GameState::enter() void GameState::enter()
{ {
int continueWorld = -1;
int continueLevel = -1;
application->settings.get("continue_world", &continueWorld);
application->settings.get("continue_level", &continueLevel);
if (continueWorld != application->currentWorldIndex || continueLevel != application->currentLevelIndex)
{
// Save continue world and level indices
application->settings.set("continue_world", application->currentWorldIndex);
application->settings.set("continue_level", application->currentLevelIndex);
application->saveUserSettings();
}
// Setup HUD // Setup HUD
application->rectangularPaletteImage->setVisible(true); application->rectangularPaletteImage->setVisible(true);
application->rectangularPaletteImage->setActive(true); application->rectangularPaletteImage->setActive(true);
@ -97,6 +110,11 @@ void GameState::enter()
// Position options menu // Position options menu
application->optionsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.5f)); application->optionsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.5f));
application->levelsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.5f));
// Show level name
application->levelNameLabel->setText(application->getLevelName(application->currentWorldIndex, application->currentLevelIndex));
//application->levelNameLabel->setVisible(true);
// Begin fade-in // Begin fade-in
application->fadeInTween->start(); application->fadeInTween->start();
@ -128,9 +146,9 @@ void GameState::execute()
{ {
if (selectedItem != nullptr) if (selectedItem != nullptr)
{ {
if (selectedItem->getIndex() < application->activeMenu->getItemCount() - 1)
if (selectedItem->getItemIndex() < application->activeMenu->getItemCount() - 1)
{ {
application->selectMenuItem(selectedItem->getIndex() + 1);
application->selectMenuItem(selectedItem->getItemIndex() + 1);
} }
else else
{ {
@ -146,9 +164,9 @@ void GameState::execute()
{ {
if (selectedItem != nullptr) if (selectedItem != nullptr)
{ {
if (selectedItem->getIndex() > 0)
if (selectedItem->getItemIndex() > 0)
{ {
application->selectMenuItem(selectedItem->getIndex() - 1);
application->selectMenuItem(selectedItem->getItemIndex() - 1);
} }
else else
{ {
@ -161,6 +179,15 @@ void GameState::execute()
} }
} }
if (application->menuLeft.isTriggered() && !application->menuLeft.wasTriggered())
{
application->decrementMenuItem();
}
else if (application->menuRight.isTriggered() && !application->menuRight.wasTriggered())
{
application->incrementMenuItem();
}
if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered()) if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered())
{ {
application->activateMenuItem(); application->activateMenuItem();
@ -223,8 +250,8 @@ void GameState::execute()
// Picking // Picking
glm::ivec2 mousePosition = application->mouse->getCurrentPosition(); glm::ivec2 mousePosition = application->mouse->getCurrentPosition();
mousePosition.y = application->height - mousePosition.y;
Vector4 viewport(0.0f, 0.0f, application->width, application->height);
mousePosition.y = application->resolution.y - mousePosition.y;
Vector4 viewport(0.0f, 0.0f, application->resolution.x, application->resolution.y);
Vector3 mouseNear = application->camera.unproject(Vector3(mousePosition.x, mousePosition.y, 0.0f), viewport); Vector3 mouseNear = application->camera.unproject(Vector3(mousePosition.x, mousePosition.y, 0.0f), viewport);
Vector3 mouseFar = application->camera.unproject(Vector3(mousePosition.x, mousePosition.y, 1.0f), viewport); Vector3 mouseFar = application->camera.unproject(Vector3(mousePosition.x, mousePosition.y, 1.0f), viewport);

+ 23
- 5
src/states/title-state.cpp View File

@ -37,7 +37,13 @@ void TitleState::enter()
application->backgroundLayer->addObject(&application->bgBatch); application->backgroundLayer->addObject(&application->bgBatch);
application->inputManager->addWindowObserver(this); application->inputManager->addWindowObserver(this);
windowResized(application->width, application->height);
windowResized(application->resolution.x, application->resolution.y);
application->camera.setPerspective(
glm::radians(25.0f),
application->resolution.x / application->resolution.y,
0.1f,
1000.0f);
// Setup camera controller // Setup camera controller
application->surfaceCam->setCamera(&application->camera); application->surfaceCam->setCamera(&application->camera);
@ -63,6 +69,7 @@ void TitleState::enter()
// Position options menu // Position options menu
application->optionsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f)); application->optionsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f));
application->levelsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f));
// Setup fade-in // Setup fade-in
application->blackoutImage->setVisible(true); application->blackoutImage->setVisible(true);
@ -80,9 +87,9 @@ void TitleState::execute()
{ {
if (selectedItem != nullptr) if (selectedItem != nullptr)
{ {
if (selectedItem->getIndex() < application->activeMenu->getItemCount() - 1)
if (selectedItem->getItemIndex() < application->activeMenu->getItemCount() - 1)
{ {
application->selectMenuItem(selectedItem->getIndex() + 1);
application->selectMenuItem(selectedItem->getItemIndex() + 1);
} }
else else
{ {
@ -98,9 +105,9 @@ void TitleState::execute()
{ {
if (selectedItem != nullptr) if (selectedItem != nullptr)
{ {
if (selectedItem->getIndex() > 0)
if (selectedItem->getItemIndex() > 0)
{ {
application->selectMenuItem(selectedItem->getIndex() - 1);
application->selectMenuItem(selectedItem->getItemIndex() - 1);
} }
else else
{ {
@ -113,6 +120,15 @@ void TitleState::execute()
} }
} }
if (application->menuLeft.isTriggered() && !application->menuLeft.wasTriggered())
{
application->decrementMenuItem();
}
else if (application->menuRight.isTriggered() && !application->menuRight.wasTriggered())
{
application->incrementMenuItem();
}
if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered()) if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered())
{ {
application->activateMenuItem(); application->activateMenuItem();
@ -158,6 +174,7 @@ void TitleState::windowClosed()
void TitleState::windowResized(int width, int height) void TitleState::windowResized(int width, int height)
{ {
/*
// Update application dimensions // Update application dimensions
application->width = width; application->width = width;
application->height = height; application->height = height;
@ -185,4 +202,5 @@ void TitleState::windowResized(int width, int height)
(float)application->width / (float)application->height, (float)application->width / (float)application->height,
0.1f, 0.1f,
1000.0f); 1000.0f);
*/
} }

+ 109
- 22
src/ui/menu.cpp View File

@ -27,18 +27,34 @@ MenuItem::MenuItem(Menu* parent, std::size_t index):
index(index), index(index),
selectedCallback(nullptr), selectedCallback(nullptr),
deselectedCallback(nullptr), deselectedCallback(nullptr),
activatedCallback(nullptr)
activatedCallback(nullptr),
valueChangedCallback(nullptr),
valueIndex(0),
nameLabel(nullptr),
valueLabel(nullptr),
rowContainer(nullptr)
{ {
label = new UILabel();
nameLabel = new UILabel();
nameLabel->setAnchor(Vector2(0.0f, 0.0f));
valueLabel = new UILabel();
valueLabel->setAnchor(Vector2(1.0f, 0.0f));
rowContainer = new UIContainer();
rowContainer->addChild(nameLabel);
rowContainer->addChild(valueLabel);
label->setMouseOverCallback(std::bind(&Menu::select, parent, index));
label->setMouseMovedCallback(std::bind(&Menu::select, parent, index));
label->setMousePressedCallback(std::bind(&Menu::activate, parent));
rowContainer->setMouseOverCallback(std::bind(&Menu::select, parent, index));
rowContainer->setMouseMovedCallback(std::bind(&Menu::select, parent, index));
rowContainer->setMousePressedCallback(std::bind(&Menu::activate, parent));
} }
MenuItem::~MenuItem() MenuItem::~MenuItem()
{ {
delete label;
delete nameLabel;
delete valueLabel;
delete rowContainer;
} }
void MenuItem::select() void MenuItem::select()
@ -80,12 +96,46 @@ void MenuItem::setActivatedCallback(std::function callback)
this->activatedCallback = callback; this->activatedCallback = callback;
} }
void MenuItem::setLabel(const std::string& text)
void MenuItem::setValueChangedCallback(std::function<void(std::size_t)> callback)
{
this->valueChangedCallback = callback;
}
void MenuItem::setName(const std::string& text)
{
nameLabel->setText(text);
parent->resize();
}
void MenuItem::addValue(const std::string& text)
{ {
label->setText(text);
values.push_back(text);
valueLabel->setText(values[valueIndex]);
parent->resize(); parent->resize();
} }
void MenuItem::removeValues()
{
values.clear();
valueIndex = 0;
valueLabel->setText(std::string());
parent->resize();
}
void MenuItem::setValueIndex(std::size_t index)
{
if (valueIndex != index)
{
valueIndex = index;
valueLabel->setText(values[index]);
if (valueChangedCallback != nullptr)
{
valueChangedCallback(index);
}
}
}
bool MenuItem::isSelected() const bool MenuItem::isSelected() const
{ {
return (parent->getSelectedItem() == this); return (parent->getSelectedItem() == this);
@ -96,7 +146,8 @@ Menu::Menu():
enteredCallback(nullptr), enteredCallback(nullptr),
exitedCallback(nullptr), exitedCallback(nullptr),
font(nullptr), font(nullptr),
lineSpacing(1.0f)
lineSpacing(1.0f),
columnMargin(0.0f)
{ {
container = new UIContainer(); container = new UIContainer();
resize(); resize();
@ -130,12 +181,16 @@ MenuItem* Menu::addItem()
MenuItem* item = new MenuItem(this, items.size()); MenuItem* item = new MenuItem(this, items.size());
items.push_back(item); items.push_back(item);
// Set item label font
item->label->setFont(font);
item->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
// Set item fonts
item->nameLabel->setFont(font);
item->valueLabel->setFont(font);
// Add item label to UI container
container->addChild(item->label);
// Set item colors
item->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
item->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
// Add item labels to UI container
container->addChild(item->rowContainer);
// Resize UI container // Resize UI container
resize(); resize();
@ -147,8 +202,8 @@ void Menu::removeItems()
{ {
for (MenuItem* item: items) for (MenuItem* item: items)
{ {
// Remove label from UI container
container->removeChild(item->label);
// Remove labels from UI container
container->removeChild(item->rowContainer);
delete item; delete item;
} }
@ -172,7 +227,8 @@ void Menu::setFont(Font* font)
this->font = font; this->font = font;
for (MenuItem* item: items) for (MenuItem* item: items)
{ {
item->label->setFont(font);
item->nameLabel->setFont(font);
item->valueLabel->setFont(font);
} }
resize(); resize();
@ -184,13 +240,20 @@ void Menu::setLineSpacing(float spacing)
resize(); resize();
} }
void Menu::setColumnMargin(float margin)
{
columnMargin = margin;
resize();
}
void Menu::deselect() void Menu::deselect()
{ {
if (selectedItem != nullptr) if (selectedItem != nullptr)
{ {
selectedItem->deselect(); selectedItem->deselect();
selectedItem->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
selectedItem = nullptr; selectedItem = nullptr;
} }
@ -204,7 +267,8 @@ void Menu::select(std::size_t index)
item->select(); item->select();
selectedItem = item; selectedItem = item;
selectedItem->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
} }
void Menu::activate() void Menu::activate()
@ -223,14 +287,37 @@ void Menu::resize()
} }
else else
{ {
Vector2 dimensions(0.0f);
// Determine maximum width of menu
float menuWidth = 0.0f;
// For each menu item
for (std::size_t i = 0; i < items.size(); ++i)
{
const MenuItem* item = items[i];
// Calculate width of item name label
float nameLabelWidth = item->nameLabel->getDimensions().x;
// For each item value
for (std::size_t j = 0; j < item->getValueCount(); ++j)
{
// Calculate width of item value label
float valueLabelWidth = font->getWidth(item->getValue(j).c_str());
menuWidth = std::max<float>(menuWidth, nameLabelWidth + columnMargin + valueLabelWidth);
}
menuWidth = std::max<float>(menuWidth, nameLabelWidth);
}
Vector2 dimensions(menuWidth, 0.0f);
for (std::size_t i = 0; i < items.size(); ++i) for (std::size_t i = 0; i < items.size(); ++i)
{ {
const MenuItem* item = items[i]; const MenuItem* item = items[i];
item->label->setTranslation(Vector2(0.0f, static_cast<int>(font->getMetrics().getHeight() * lineSpacing * static_cast<float>(i))));
float translationY = static_cast<float>(static_cast<int>(font->getMetrics().getHeight() * lineSpacing * static_cast<float>(i)));
dimensions.x = std::max<float>(dimensions.x, item->label->getDimensions().x);
item->rowContainer->setDimensions(Vector2(menuWidth, font->getMetrics().getHeight()));
item->rowContainer->setTranslation(Vector2(0.0f, translationY));
if (!i) if (!i)
{ {

+ 35
- 4
src/ui/menu.hpp View File

@ -38,9 +38,18 @@ public:
void setDeselectedCallback(std::function<void()> callback); void setDeselectedCallback(std::function<void()> callback);
void setActivatedCallback(std::function<void()> callback); void setActivatedCallback(std::function<void()> callback);
void setValueChangedCallback(std::function<void(std::size_t)> callback); void setValueChangedCallback(std::function<void(std::size_t)> callback);
void setLabel(const std::string& text);
std::size_t getIndex() const;
void setName(const std::string& text);
void addValue(const std::string& text);
void removeValues();
void setValueIndex(std::size_t index);
std::size_t getValueCount() const;
const std::string& getValue(std::size_t index) const;
std::size_t getValueIndex() const;
std::size_t getItemIndex() const;
bool isSelected() const; bool isSelected() const;
private: private:
@ -57,14 +66,34 @@ private:
std::function<void()> selectedCallback; std::function<void()> selectedCallback;
std::function<void()> deselectedCallback; std::function<void()> deselectedCallback;
std::function<void()> activatedCallback; std::function<void()> activatedCallback;
UILabel* label;
std::function<void(std::size_t)> valueChangedCallback;
std::vector<std::string> values;
std::size_t valueIndex;
UILabel* nameLabel;
UILabel* valueLabel;
UIContainer* rowContainer;
}; };
inline std::size_t MenuItem::getIndex() const
inline std::size_t MenuItem::getItemIndex() const
{ {
return index; return index;
} }
inline std::size_t MenuItem::getValueCount() const
{
return values.size();
}
inline const std::string& MenuItem::getValue(std::size_t index) const
{
return values[index];
}
inline std::size_t MenuItem::getValueIndex() const
{
return valueIndex;
}
class Menu class Menu
{ {
public: public:
@ -81,6 +110,7 @@ public:
void setExitedCallback(std::function<void()> callback); void setExitedCallback(std::function<void()> callback);
void setFont(Font* font); void setFont(Font* font);
void setLineSpacing(float spacing); void setLineSpacing(float spacing);
void setColumnMargin(float margin);
std::size_t getItemCount(); std::size_t getItemCount();
const MenuItem* getItem(std::size_t index) const; const MenuItem* getItem(std::size_t index) const;
@ -120,6 +150,7 @@ private:
std::function<void()> exitedCallback; std::function<void()> exitedCallback;
Font* font; Font* font;
float lineSpacing; float lineSpacing;
float columnMargin;
UIContainer* container; UIContainer* container;
}; };

Loading…
Cancel
Save