@ -0,0 +1,8 @@ | |||||
<?xml version="1.0"?> | |||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | |||||
<application xmlns="urn:schemas-microsoft-com:asm.v3"> | |||||
<windowsSettings> | |||||
<ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware> | |||||
</windowsSettings> | |||||
</application> | |||||
</assembly> |
@ -1,124 +0,0 @@ | |||||
/* | |||||
* Copyright (C) 2017 Christopher J. Howard | |||||
* | |||||
* This file is part of Antkeeper Source Code. | |||||
* | |||||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "level-select-state.hpp" | |||||
#include "main-menu-state.hpp" | |||||
#include "../application.hpp" | |||||
#include "../configuration.hpp" | |||||
#include "../camera-controller.hpp" | |||||
LevelSelectState::LevelSelectState(Application* application): | |||||
ApplicationState(application) | |||||
{} | |||||
LevelSelectState::~LevelSelectState() | |||||
{} | |||||
void LevelSelectState::enter() | |||||
{ | |||||
levelRotation = 0.0f; | |||||
for (int i = 0; i < 5; ++i) | |||||
{ | |||||
ModelInstance* surfaceInstance = &application->previewLevelSurfaces[i]; | |||||
ModelInstance* subsurfaceInstance = &application->previewLevelSubsurfaces[i]; | |||||
Quaternion rotation = glm::angleAxis(levelRotation, Vector3(0, 1, 0)); | |||||
surfaceInstance->setRotation(rotation); | |||||
subsurfaceInstance->setRotation(rotation); | |||||
application->defaultLayer->addObject(surfaceInstance); | |||||
application->defaultLayer->addObject(subsurfaceInstance); | |||||
} | |||||
application->defaultLayer->addObject(&application->biomeFloorModelInstance); | |||||
application->levelIDLabel->setVisible(true); | |||||
application->levelNameLabel->setVisible(true); | |||||
application->selectWorld(0); | |||||
application->selectLevel(0); | |||||
// Setup camera controller | |||||
application->surfaceCam->setTargetFocalPoint(Vector3(0.0f)); | |||||
application->surfaceCam->setTargetFocalDistance(350.0f); | |||||
application->surfaceCam->setTargetElevation(glm::radians(85.0f)); | |||||
application->surfaceCam->setTargetAzimuth(0.0f); | |||||
application->surfaceCam->update(0.0f); | |||||
} | |||||
void LevelSelectState::execute() | |||||
{ | |||||
// Navigate menu | |||||
if (application->menuLeft.isTriggered() && !application->menuLeft.wasTriggered()) | |||||
{ | |||||
application->selectPreviousLevel(); | |||||
} | |||||
else if (application->menuRight.isTriggered() && !application->menuRight.wasTriggered()) | |||||
{ | |||||
application->selectNextLevel(); | |||||
} | |||||
if (application->menuDown.isTriggered() && !application->menuDown.wasTriggered()) | |||||
{ | |||||
application->selectPreviousWorld(); | |||||
} | |||||
else if (application->menuUp.isTriggered() && !application->menuUp.wasTriggered()) | |||||
{ | |||||
application->selectNextWorld(); | |||||
} | |||||
if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered()) | |||||
{ | |||||
application->enterSelectedLevel(); | |||||
} | |||||
else if (application->menuCancel.isTriggered() && !application->menuCancel.wasTriggered()) | |||||
{ | |||||
application->changeState(application->mainMenuState); | |||||
} | |||||
// Rotate levels | |||||
levelRotation += glm::radians(5.0f) * application->dt; | |||||
for (int i = 0; i < 5; ++i) | |||||
{ | |||||
ModelInstance* surfaceInstance = &application->previewLevelSurfaces[i]; | |||||
ModelInstance* subsurfaceInstance = &application->previewLevelSubsurfaces[i]; | |||||
Quaternion rotation = glm::angleAxis(levelRotation, Vector3(0, 1, 0)); | |||||
//surfaceInstance->setRotation(rotation); | |||||
//subsurfaceInstance->setRotation(rotation); | |||||
} | |||||
// Update camera | |||||
application->surfaceCam->update(application->dt); | |||||
} | |||||
void LevelSelectState::exit() | |||||
{ | |||||
for (int i = 0; i < 5; ++i) | |||||
{ | |||||
ModelInstance* surfaceInstance = &application->previewLevelSurfaces[i]; | |||||
ModelInstance* subsurfaceInstance = &application->previewLevelSubsurfaces[i]; | |||||
application->defaultLayer->removeObject(surfaceInstance); | |||||
application->defaultLayer->removeObject(subsurfaceInstance); | |||||
} | |||||
application->defaultLayer->removeObject(&application->biomeFloorModelInstance); | |||||
application->levelIDLabel->setVisible(false); | |||||
application->levelNameLabel->setVisible(false); | |||||
} |
@ -1,42 +0,0 @@ | |||||
/* | |||||
* Copyright (C) 2017 Christopher J. Howard | |||||
* | |||||
* This file is part of Antkeeper Source Code. | |||||
* | |||||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef LEVEL_SELECT_STATE_HPP | |||||
#define LEVEL_SELECT_STATE_HPP | |||||
#include "../application-state.hpp" | |||||
#include <emergent/emergent.hpp> | |||||
using namespace Emergent; | |||||
class LevelSelectState: public ApplicationState | |||||
{ | |||||
public: | |||||
LevelSelectState(Application* application); | |||||
virtual ~LevelSelectState(); | |||||
virtual void enter(); | |||||
virtual void execute(); | |||||
virtual void exit(); | |||||
private: | |||||
float levelRotation; | |||||
}; | |||||
#endif // LEVEL_SELECT_STATE_HPP |
@ -1,299 +0,0 @@ | |||||
/* | |||||
* Copyright (C) 2017 Christopher J. Howard | |||||
* | |||||
* This file is part of Antkeeper Source Code. | |||||
* | |||||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "main-menu-state.hpp" | |||||
#include "title-state.hpp" | |||||
#include "../application.hpp" | |||||
#include "../debug.hpp" | |||||
#include "../camera-controller.hpp" | |||||
void drawShaft(LineBatcher* lineBatcher, const Shaft* shaft); | |||||
void drawChamber(LineBatcher* lineBatcher, const Chamber* chamber) | |||||
{ | |||||
float helixAngle = chamber->parent->getHelixAngle(chamber->relativeDepth); | |||||
float minAngle = helixAngle - chamber->centralAngle * 0.5f; | |||||
float maxAngle = helixAngle + chamber->centralAngle * 0.5f; | |||||
// Find position on helix | |||||
Vector3 helixPosition = chamber->parent->getHelixPosition(chamber->relativeDepth); | |||||
helixPosition.y = -helixPosition.y; | |||||
// Move annulus toward helix by the inner radius | |||||
Vector3 helixDirection = glm::normalize(Vector3(std::cos(helixAngle), 0.0f, std::sin(helixAngle))); | |||||
Vector3 offset = helixPosition - helixDirection * (chamber->innerRadius - chamber->parent->shaftRadius); | |||||
int stepCount = 10; | |||||
float angleStep = chamber->centralAngle / (float)stepCount; | |||||
for (int i = 0; i < stepCount; ++i) | |||||
{ | |||||
float angle0 = minAngle + angleStep * (float)i; | |||||
float angle1 = minAngle + angleStep * (float)(i + 1); | |||||
float x0 = std::cos(angle0); | |||||
float z0 = std::sin(angle0); | |||||
float x1 = std::cos(angle1); | |||||
float z1 = std::sin(angle1); | |||||
Vector3 innerStart; | |||||
innerStart.x = x0 * chamber->innerRadius; | |||||
innerStart.y = 0.0f; | |||||
innerStart.z = z0 * chamber->innerRadius; | |||||
Vector3 outerStart; | |||||
outerStart.x = x0 * chamber->outerRadius; | |||||
outerStart.y = 0.0f; | |||||
outerStart.z = z0 * chamber->outerRadius; | |||||
Vector3 innerEnd; | |||||
innerEnd.x = x1 * chamber->innerRadius; | |||||
innerEnd.y = 0.0f; | |||||
innerEnd.z = z1 * chamber->innerRadius; | |||||
Vector3 outerEnd; | |||||
outerEnd.x = x1 * chamber->outerRadius; | |||||
outerEnd.y = 0.0f; | |||||
outerEnd.z = z1 * chamber->outerRadius; | |||||
lineBatcher->draw(offset + innerStart, offset + innerEnd); | |||||
lineBatcher->draw(offset + outerStart, offset + outerEnd); | |||||
} | |||||
Vector3 leftWallStart; | |||||
leftWallStart.x = std::cos(minAngle) * chamber->innerRadius; | |||||
leftWallStart.y = 0.0f; | |||||
leftWallStart.z = std::sin(minAngle) * chamber->innerRadius; | |||||
Vector3 leftWallEnd; | |||||
leftWallEnd.x = std::cos(minAngle) * chamber->outerRadius; | |||||
leftWallEnd.y = 0.0f; | |||||
leftWallEnd.z = std::sin(minAngle) * chamber->outerRadius; | |||||
Vector3 rightWallStart; | |||||
rightWallStart.x = std::cos(maxAngle) * chamber->innerRadius; | |||||
rightWallStart.y = 0.0f; | |||||
rightWallStart.z = std::sin(maxAngle) * chamber->innerRadius; | |||||
Vector3 rightWallEnd; | |||||
rightWallEnd.x = std::cos(maxAngle) * chamber->outerRadius; | |||||
rightWallEnd.y = 0.0f; | |||||
rightWallEnd.z = std::sin(maxAngle) * chamber->outerRadius; | |||||
lineBatcher->draw(offset + leftWallStart, offset + leftWallEnd); | |||||
lineBatcher->draw(offset + rightWallStart, offset + rightWallEnd); | |||||
if (chamber->child != nullptr) | |||||
{ | |||||
drawShaft(lineBatcher, chamber->child); | |||||
} | |||||
} | |||||
void drawShaft(LineBatcher* lineBatcher, const Shaft* shaft) | |||||
{ | |||||
// Draw helix | |||||
int stepCount = 50; | |||||
float depthStep = shaft->shaftDepth / (float)stepCount; | |||||
for (int i = 0; i < stepCount; ++i) | |||||
{ | |||||
Vector3 start = shaft->getHelixPosition((float)i * depthStep); | |||||
Vector3 end = shaft->getHelixPosition((float)(i + 1) * depthStep); | |||||
start.y = -start.y; | |||||
end.y = -end.y; | |||||
lineBatcher->draw(start, end); | |||||
} | |||||
// Draw children | |||||
for (const Chamber* chamber: shaft->children) | |||||
{ | |||||
drawChamber(lineBatcher, chamber); | |||||
} | |||||
} | |||||
void MainMenuState::generateNest() | |||||
{ | |||||
NestParameters params; | |||||
params.randomSeed = std::rand(); | |||||
params.maxShaftGeneration = 2; | |||||
params.minShaftRadius = 0.0f; | |||||
params.maxShaftRadius = 0.0f; | |||||
params.minShaftDepth = 4.0f; | |||||
params.maxShaftDepth = 6.0f; | |||||
params.minShaftHelixRadius = 0.1f; | |||||
params.maxShaftHelixRadius = 1.0f; | |||||
params.minShaftHelixPitch = 0.25f; | |||||
params.maxShaftHelixPitch = 0.75f; | |||||
params.minShaftChamberCount = 1; | |||||
params.maxShaftChamberCount = 5; | |||||
params.minShaftChamberPitch = 0.5f; | |||||
params.maxShaftChamberPitch = 2.0f; | |||||
params.minChamberInnerRadius = 0.2f; | |||||
params.maxChamberInnerRadius = 0.2f; | |||||
params.minChamberOuterRadius = 0.5f; | |||||
params.maxChamberOuterRadius = 0.5f; | |||||
params.minChamberCentralAngle = glm::radians(240.0f); | |||||
params.maxChamberCentralAngle = glm::radians(240.0f); | |||||
nest.setParameters(params); | |||||
nest.generate(); | |||||
// Draw nest | |||||
application->lineBatcher->setColor(Vector4(1.0f)); | |||||
application->lineBatcher->setWidth(0.015f); | |||||
application->lineBatcher->begin(); | |||||
drawShaft(application->lineBatcher, nest.getRootShaft()); | |||||
application->lineBatcher->end(); | |||||
} | |||||
MainMenuState::MainMenuState(Application* application): | |||||
ApplicationState(application) | |||||
{} | |||||
MainMenuState::~MainMenuState() | |||||
{} | |||||
void MainMenuState::enter() | |||||
{ | |||||
// Open main menu | |||||
application->enterMenu(0); | |||||
application->menuSelectorLabel->setVisible(true); | |||||
// Start fade-in | |||||
application->blackoutImage->setVisible(true); | |||||
application->fadeInTween->start(); | |||||
application->backgroundLayer->addObject(&application->bgCamera); | |||||
application->backgroundLayer->addObject(&application->bgBatch); | |||||
// Add nest | |||||
application->defaultLayer->addObject(&application->nestModelInstance); | |||||
application->surfaceCam->setCamera(&application->camera); | |||||
application->surfaceCam->setFocalPoint(Vector3(-10.0f, -13.3f, 0.0f)); | |||||
application->surfaceCam->setFocalDistance(89.5f); | |||||
application->surfaceCam->setElevation(glm::radians(15.0f)); | |||||
application->surfaceCam->setAzimuth(glm::radians(0.0f)); | |||||
application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getFocalPoint()); | |||||
application->surfaceCam->setTargetFocalDistance(application->surfaceCam->getFocalDistance()); | |||||
application->surfaceCam->setTargetElevation(application->surfaceCam->getElevation()); | |||||
application->surfaceCam->setTargetAzimuth(application->surfaceCam->getAzimuth()); | |||||
application->surfaceCam->update(0.0f); | |||||
// 3D camera | |||||
application->camera.setPerspective( | |||||
glm::radians(25.0f), | |||||
(float)application->width / (float)application->height, | |||||
0.1f, | |||||
1000.0f); | |||||
} | |||||
void MainMenuState::execute() | |||||
{ | |||||
// Navigate menu | |||||
if (application->menuDown.isTriggered() && !application->menuDown.wasTriggered()) | |||||
{ | |||||
if (application->selectedMenuItemIndex < application->currentMenu->getItemCount() - 1) | |||||
{ | |||||
application->selectMenuItem(application->selectedMenuItemIndex + 1); | |||||
} | |||||
else | |||||
{ | |||||
application->selectMenuItem(0); | |||||
} | |||||
} | |||||
else if (application->menuUp.isTriggered() && !application->menuUp.wasTriggered()) | |||||
{ | |||||
if (application->selectedMenuItemIndex > 0) | |||||
{ | |||||
application->selectMenuItem(application->selectedMenuItemIndex - 1); | |||||
} | |||||
else | |||||
{ | |||||
application->selectMenuItem(application->currentMenu->getItemCount() - 1); | |||||
} | |||||
} | |||||
if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered()) | |||||
{ | |||||
application->activateMenuItem(application->selectedMenuItemIndex); | |||||
} | |||||
else if (application->menuCancel.isTriggered() && !application->menuCancel.wasTriggered()) | |||||
{ | |||||
application->changeState(application->titleState); | |||||
} | |||||
float lineHeight = application->menuFont->getMetrics().getHeight(); | |||||
const UIContainer* container = application->menuContainers[application->currentMenuIndex]; | |||||
application->menuSelectorLabel->setTranslation( | |||||
Vector2(container->getPosition().x - application->menuSelectorLabel->getDimensions().x * 1.5f, | |||||
container->getPosition().y + lineHeight * 0.5f - application->menuSelectorLabel->getDimensions().y * 0.5f + lineHeight * application->selectedMenuItemIndex)); | |||||
// Move camera | |||||
Vector2 movementVector(0.0f); | |||||
if (application->cameraMoveLeft.isTriggered()) | |||||
movementVector.x -= application->cameraMoveLeft.getCurrentValue(); | |||||
if (application->cameraMoveRight.isTriggered()) | |||||
movementVector.x += application->cameraMoveRight.getCurrentValue(); | |||||
if (application->cameraMoveForward.isTriggered()) | |||||
movementVector.y -= application->cameraMoveForward.getCurrentValue(); | |||||
if (application->cameraMoveBack.isTriggered()) | |||||
movementVector.y += application->cameraMoveBack.getCurrentValue(); | |||||
if (movementVector.x != 0.0f || movementVector.y != 0.0f) | |||||
{ | |||||
movementVector *= 0.005f * application->surfaceCam->getFocalDistance() * application->dt / (1.0f / 60.0f); | |||||
application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getTargetFocalPoint() + Vector3(movementVector.x, -movementVector.y, 0.0f)); | |||||
Vector3 focal = application->surfaceCam->getFocalPoint(); | |||||
std::cout << focal.x << ", " << focal.y << ", " << focal.z << "; " << application->surfaceCam->getFocalDistance() << std::endl; | |||||
} | |||||
// Zoom camera | |||||
float zoomFactor = application->surfaceCam->getFocalDistance() / 20.0f * application->dt / (1.0f / 60.0f); | |||||
if (application->cameraZoomIn.isTriggered()) | |||||
application->surfaceCam->zoom(zoomFactor * application->cameraZoomIn.getCurrentValue()); | |||||
if (application->cameraZoomOut.isTriggered()) | |||||
application->surfaceCam->zoom(-zoomFactor * application->cameraZoomOut.getCurrentValue()); | |||||
application->surfaceCam->update(application->dt); | |||||
application->surfaceCam->setTargetAzimuth(application->surfaceCam->getTargetAzimuth() + glm::radians(2.0f) * application->dt); | |||||
application->surfaceCam->update(application->dt); | |||||
} | |||||
void MainMenuState::exit() | |||||
{ | |||||
// Hide UI | |||||
application->menuSelectorLabel->setVisible(false); | |||||
// Clear scene | |||||
application->defaultLayer->removeObject(&application->nestModelInstance); | |||||
application->backgroundLayer->removeObject(&application->bgCamera); | |||||
application->backgroundLayer->removeObject(&application->bgBatch); | |||||
} | |||||
void MainMenuState::mouseButtonPressed(int button, int x, int y) | |||||
{ | |||||
} | |||||
void MainMenuState::mouseButtonReleased(int button, int x, int y) | |||||
{ | |||||
} |
@ -1,49 +0,0 @@ | |||||
/* | |||||
* Copyright (C) 2017 Christopher J. Howard | |||||
* | |||||
* This file is part of Antkeeper Source Code. | |||||
* | |||||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef MAIN_MENU_STATE_HPP | |||||
#define MAIN_MENU_STATE_HPP | |||||
#include "../application-state.hpp" | |||||
#include "../input.hpp" | |||||
#include "../game/nest.hpp" | |||||
#include <emergent/emergent.hpp> | |||||
using namespace Emergent; | |||||
class MainMenuState: public ApplicationState, public MouseButtonObserver | |||||
{ | |||||
public: | |||||
MainMenuState(Application* application); | |||||
virtual ~MainMenuState(); | |||||
virtual void enter(); | |||||
virtual void execute(); | |||||
virtual void exit(); | |||||
virtual void mouseButtonPressed(int button, int x, int y); | |||||
virtual void mouseButtonReleased(int button, int x, int y); | |||||
private: | |||||
void generateNest(); | |||||
Nest nest; | |||||
}; | |||||
#endif // MAIN_MENU_STATE_HPP |
@ -0,0 +1,247 @@ | |||||
/* | |||||
* Copyright (C) 2017 Christopher J. Howard | |||||
* | |||||
* This file is part of Antkeeper Source Code. | |||||
* | |||||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "menu.hpp" | |||||
#include "ui.hpp" | |||||
#include <algorithm> | |||||
MenuItem::MenuItem(Menu* parent, std::size_t index): | |||||
parent(parent), | |||||
index(index), | |||||
selectedCallback(nullptr), | |||||
deselectedCallback(nullptr), | |||||
activatedCallback(nullptr) | |||||
{ | |||||
label = new UILabel(); | |||||
label->setMouseOverCallback(std::bind(&Menu::select, parent, index)); | |||||
label->setMouseMovedCallback(std::bind(&Menu::select, parent, index)); | |||||
label->setMousePressedCallback(std::bind(&Menu::activate, parent)); | |||||
} | |||||
MenuItem::~MenuItem() | |||||
{ | |||||
delete label; | |||||
} | |||||
void MenuItem::select() | |||||
{ | |||||
if (selectedCallback != nullptr) | |||||
{ | |||||
selectedCallback(); | |||||
} | |||||
} | |||||
void MenuItem::deselect() | |||||
{ | |||||
if (deselectedCallback != nullptr) | |||||
{ | |||||
deselectedCallback(); | |||||
} | |||||
} | |||||
void MenuItem::activate() | |||||
{ | |||||
if (activatedCallback != nullptr) | |||||
{ | |||||
activatedCallback(); | |||||
} | |||||
} | |||||
void MenuItem::setSelectedCallback(std::function<void()> callback) | |||||
{ | |||||
this->selectedCallback = callback = callback; | |||||
} | |||||
void MenuItem::setDeselectedCallback(std::function<void()> callback) | |||||
{ | |||||
this->deselectedCallback = callback; | |||||
} | |||||
void MenuItem::setActivatedCallback(std::function<void()> callback) | |||||
{ | |||||
this->activatedCallback = callback; | |||||
} | |||||
void MenuItem::setLabel(const std::string& text) | |||||
{ | |||||
label->setText(text); | |||||
parent->resize(); | |||||
} | |||||
bool MenuItem::isSelected() const | |||||
{ | |||||
return (parent->getSelectedItem() == this); | |||||
} | |||||
Menu::Menu(): | |||||
selectedItem(nullptr), | |||||
enteredCallback(nullptr), | |||||
exitedCallback(nullptr), | |||||
font(nullptr), | |||||
lineSpacing(1.0f) | |||||
{ | |||||
container = new UIContainer(); | |||||
resize(); | |||||
} | |||||
Menu::~Menu() | |||||
{ | |||||
removeItems(); | |||||
delete container; | |||||
} | |||||
void Menu::enter() | |||||
{ | |||||
if (enteredCallback != nullptr) | |||||
{ | |||||
enteredCallback(); | |||||
} | |||||
} | |||||
void Menu::exit() | |||||
{ | |||||
if (exitedCallback != nullptr) | |||||
{ | |||||
exitedCallback(); | |||||
} | |||||
} | |||||
MenuItem* Menu::addItem() | |||||
{ | |||||
// Allocate item and add to items | |||||
MenuItem* item = new MenuItem(this, items.size()); | |||||
items.push_back(item); | |||||
// Set item label font | |||||
item->label->setFont(font); | |||||
item->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f)); | |||||
// Add item label to UI container | |||||
container->addChild(item->label); | |||||
// Resize UI container | |||||
resize(); | |||||
return item; | |||||
} | |||||
void Menu::removeItems() | |||||
{ | |||||
for (MenuItem* item: items) | |||||
{ | |||||
// Remove label from UI container | |||||
container->removeChild(item->label); | |||||
delete item; | |||||
} | |||||
items.clear(); | |||||
resize(); | |||||
} | |||||
void Menu::setEnteredCallback(std::function<void()> callback) | |||||
{ | |||||
this->enteredCallback = callback; | |||||
} | |||||
void Menu::setExitedCallback(std::function<void()> callback) | |||||
{ | |||||
this->exitedCallback = callback; | |||||
} | |||||
void Menu::setFont(Font* font) | |||||
{ | |||||
this->font = font; | |||||
for (MenuItem* item: items) | |||||
{ | |||||
item->label->setFont(font); | |||||
} | |||||
resize(); | |||||
} | |||||
void Menu::setLineSpacing(float spacing) | |||||
{ | |||||
lineSpacing = spacing; | |||||
resize(); | |||||
} | |||||
void Menu::deselect() | |||||
{ | |||||
if (selectedItem != nullptr) | |||||
{ | |||||
selectedItem->deselect(); | |||||
selectedItem->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f)); | |||||
selectedItem = nullptr; | |||||
} | |||||
} | |||||
void Menu::select(std::size_t index) | |||||
{ | |||||
deselect(); | |||||
MenuItem* item = items[index]; | |||||
item->select(); | |||||
selectedItem = item; | |||||
selectedItem->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f)); | |||||
} | |||||
void Menu::activate() | |||||
{ | |||||
if (selectedItem != nullptr) | |||||
{ | |||||
selectedItem->activate(); | |||||
} | |||||
} | |||||
void Menu::resize() | |||||
{ | |||||
if (!font) | |||||
{ | |||||
container->setDimensions(Vector2(0.0f)); | |||||
} | |||||
else | |||||
{ | |||||
Vector2 dimensions(0.0f); | |||||
for (std::size_t i = 0; i < items.size(); ++i) | |||||
{ | |||||
const MenuItem* item = items[i]; | |||||
item->label->setTranslation(Vector2(0.0f, static_cast<int>(font->getMetrics().getHeight() * lineSpacing * static_cast<float>(i)))); | |||||
dimensions.x = std::max<float>(dimensions.x, item->label->getDimensions().x); | |||||
if (!i) | |||||
{ | |||||
dimensions.y += font->getMetrics().getHeight(); | |||||
} | |||||
else | |||||
{ | |||||
dimensions.y += font->getMetrics().getHeight() * lineSpacing; | |||||
} | |||||
} | |||||
container->setDimensions(dimensions); | |||||
} | |||||
} |
@ -0,0 +1,161 @@ | |||||
/* | |||||
* Copyright (C) 2017 Christopher J. Howard | |||||
* | |||||
* This file is part of Antkeeper Source Code. | |||||
* | |||||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 3 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef MENU_HPP | |||||
#define MENU_HPP | |||||
#include <emergent/emergent.hpp> | |||||
using namespace Emergent; | |||||
#include <functional> | |||||
#include <string> | |||||
#include <vector> | |||||
class Menu; | |||||
class UIContainer; | |||||
class UILabel; | |||||
class MenuItem | |||||
{ | |||||
public: | |||||
void setSelectedCallback(std::function<void()> callback); | |||||
void setDeselectedCallback(std::function<void()> callback); | |||||
void setActivatedCallback(std::function<void()> callback); | |||||
void setValueChangedCallback(std::function<void(std::size_t)> callback); | |||||
void setLabel(const std::string& text); | |||||
std::size_t getIndex() const; | |||||
bool isSelected() const; | |||||
private: | |||||
friend class Menu; | |||||
MenuItem(Menu* parent, std::size_t index); | |||||
~MenuItem(); | |||||
void select(); | |||||
void deselect(); | |||||
void activate(); | |||||
Menu* parent; | |||||
std::size_t index; | |||||
std::function<void()> selectedCallback; | |||||
std::function<void()> deselectedCallback; | |||||
std::function<void()> activatedCallback; | |||||
UILabel* label; | |||||
}; | |||||
inline std::size_t MenuItem::getIndex() const | |||||
{ | |||||
return index; | |||||
} | |||||
class Menu | |||||
{ | |||||
public: | |||||
Menu(); | |||||
~Menu(); | |||||
void enter(); | |||||
void exit(); | |||||
MenuItem* addItem(); | |||||
void removeItems(); | |||||
void setEnteredCallback(std::function<void()> callback); | |||||
void setExitedCallback(std::function<void()> callback); | |||||
void setFont(Font* font); | |||||
void setLineSpacing(float spacing); | |||||
std::size_t getItemCount(); | |||||
const MenuItem* getItem(std::size_t index) const; | |||||
MenuItem* getItem(std::size_t index); | |||||
const MenuItem* getSelectedItem() const; | |||||
MenuItem* getSelectedItem(); | |||||
const UIContainer* getUIContainer() const; | |||||
UIContainer* getUIContainer(); | |||||
/** | |||||
* Deselects the currently selected item (if any) | |||||
*/ | |||||
void deselect(); | |||||
/** | |||||
* Selects the item at the specified index | |||||
*/ | |||||
void select(std::size_t index); | |||||
/** | |||||
* Activates the selected item (if any) | |||||
*/ | |||||
void activate(); | |||||
/** | |||||
* Recalculates the dimensions of the UI container according the dimensions of the menu item labels and the line spacing. | |||||
*/ | |||||
void resize(); | |||||
private: | |||||
friend class MenuItem; | |||||
std::vector<MenuItem*> items; | |||||
MenuItem* selectedItem; | |||||
std::function<void()> enteredCallback; | |||||
std::function<void()> exitedCallback; | |||||
Font* font; | |||||
float lineSpacing; | |||||
UIContainer* container; | |||||
}; | |||||
inline std::size_t Menu::getItemCount() | |||||
{ | |||||
return items.size(); | |||||
} | |||||
inline const MenuItem* Menu::getItem(std::size_t index) const | |||||
{ | |||||
return items[index]; | |||||
} | |||||
inline MenuItem* Menu::getItem(std::size_t index) | |||||
{ | |||||
return items[index]; | |||||
} | |||||
inline const MenuItem* Menu::getSelectedItem() const | |||||
{ | |||||
return selectedItem; | |||||
} | |||||
inline MenuItem* Menu::getSelectedItem() | |||||
{ | |||||
return selectedItem; | |||||
} | |||||
inline const UIContainer* Menu::getUIContainer() const | |||||
{ | |||||
return container; | |||||
} | |||||
inline UIContainer* Menu::getUIContainer() | |||||
{ | |||||
return container; | |||||
} | |||||
#endif // MENU_HPP |