From 1534cec8656c7f4a3de778b219089a4fc1333d6d Mon Sep 17 00:00:00 2001 From: Aloshi Date: Tue, 18 Jun 2013 20:12:30 -0500 Subject: [PATCH] Added ComponentListComponent for laying out elements in a grid and navigating through them. Added SliderComponent for selecting from a range of values. Added SwitchComponent for selecting an "ON" or "OFF" value. --- CMakeLists.txt | 8 + src/GuiComponent.cpp | 7 +- src/GuiComponent.h | 3 +- src/components/ComponentListComponent.cpp | 312 ++++++++++++++++++++++ src/components/ComponentListComponent.h | 129 +++++++++ src/components/GuiMenu.cpp | 8 + src/components/GuiSettingsMenu.cpp | 70 +++++ src/components/GuiSettingsMenu.h | 29 ++ src/components/SliderComponent.cpp | 77 ++++++ src/components/SliderComponent.h | 25 ++ src/components/SwitchComponent.cpp | 43 +++ src/components/SwitchComponent.h | 18 ++ src/components/TextComponent.cpp | 33 ++- src/components/TextComponent.h | 5 +- src/components/TextListComponent.h | 1 - 15 files changed, 760 insertions(+), 8 deletions(-) create mode 100644 src/components/ComponentListComponent.cpp create mode 100644 src/components/ComponentListComponent.h create mode 100644 src/components/GuiSettingsMenu.cpp create mode 100644 src/components/GuiSettingsMenu.h create mode 100644 src/components/SliderComponent.cpp create mode 100644 src/components/SliderComponent.h create mode 100644 src/components/SwitchComponent.cpp create mode 100644 src/components/SwitchComponent.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 08026db09..ab88f5c13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,10 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextListComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ThemeComponent.h @@ -143,6 +146,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.h ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp ${CMAKE_CURRENT_SOURCE_DIR}/data/Resources.h @@ -169,7 +173,10 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ThemeComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiBox.cpp @@ -178,6 +185,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/logo/ES_logo_16.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/logo/ES_logo_32.cpp diff --git a/src/GuiComponent.cpp b/src/GuiComponent.cpp index eef473de7..4a5ee0195 100644 --- a/src/GuiComponent.cpp +++ b/src/GuiComponent.cpp @@ -13,6 +13,9 @@ GuiComponent::~GuiComponent() if(mParent) mParent->removeChild(this); + + for(unsigned int i = 0; i < getChildCount(); i++) + getChild(i)->setParent(NULL); } bool GuiComponent::input(InputConfig* config, Input input) @@ -83,13 +86,15 @@ Vector2i GuiComponent::getOffset() void GuiComponent::setOffset(Vector2i offset) { - mOffset = offset; + setOffset(offset.x, offset.y); + onOffsetChanged(); } void GuiComponent::setOffset(int x, int y) { mOffset.x = x; mOffset.y = y; + onOffsetChanged(); } Vector2u GuiComponent::getSize() diff --git a/src/GuiComponent.h b/src/GuiComponent.h index 14ae66868..b7f36af64 100644 --- a/src/GuiComponent.h +++ b/src/GuiComponent.h @@ -29,10 +29,11 @@ public: //Called when the Renderer deinitializes. Passes to children. virtual void deinit(); - Vector2i getGlobalOffset(); + virtual Vector2i getGlobalOffset(); Vector2i getOffset(); void setOffset(Vector2i offset); void setOffset(int x, int y); + virtual void onOffsetChanged() {}; Vector2u getSize(); diff --git a/src/components/ComponentListComponent.cpp b/src/components/ComponentListComponent.cpp new file mode 100644 index 000000000..acc97632a --- /dev/null +++ b/src/components/ComponentListComponent.cpp @@ -0,0 +1,312 @@ +#include "ComponentListComponent.h" +#include "../Log.h" +#include "../Renderer.h" + +#define INITIAL_CELL_SIZE 12 + +ComponentListComponent::ComponentListComponent(Window* window, Vector2u gridDimensions) : GuiComponent(window), mGrid(NULL), mColumnWidths(NULL), mRowHeights(NULL) +{ + mEntries.reserve(gridDimensions.x*gridDimensions.y); + makeCells(gridDimensions); +} + +void ComponentListComponent::makeCells(Vector2u size) +{ + if(mGrid) + delete[] mGrid; + if(mColumnWidths) + delete[] mColumnWidths; + if(mRowHeights) + delete[] mRowHeights; + + mGridSize = size; + mGrid = new ComponentEntry*[size.x * size.y]; + std::fill(mGrid, mGrid + (size.x * size.y), (ComponentEntry*)NULL); + + mColumnWidths = new unsigned int[size.x]; + std::fill(mColumnWidths, mColumnWidths + size.x, INITIAL_CELL_SIZE); + + mRowHeights = new unsigned int[size.y]; + std::fill(mRowHeights, mRowHeights + size.y, INITIAL_CELL_SIZE); + + updateSize(); + resetCursor(); +} + +void ComponentListComponent::setEntry(Vector2u pos, Vector2u size, GuiComponent* component, bool canFocus, AlignmentType align, + Vector2 autoFit, UpdateBehavior updateType) +{ + if(pos.x > mGridSize.x || pos.y > mGridSize.y) + { + LOG(LogError) << "Tried to set entry beyond grid size!"; + return; + } + + if(component == NULL) + { + LOG(LogError) << "Tried to add NULL component to ComponentList!"; + return; + } + + ComponentEntry entry(Rect(pos.x, pos.y, size.x, size.y), component, updateType, canFocus, align); + + mEntries.push_back(entry); + + for(unsigned int y = pos.y; y < pos.y + size.y; y++) + { + for(unsigned int x = pos.x; x < pos.x + size.x; x++) + { + setCell(x, y, &mEntries.back()); + } + } + + if(component->getParent() != NULL) + LOG(LogError) << "ComponentListComponent ruining an existing parent-child relationship! Call a social worker!"; + component->setParent(this); + + if(!cursorValid() && canFocus) + mCursor = (Vector2i)pos; + + //update the column width and row height + if(autoFit.x && getColumnWidth(pos.x) < component->getSize().x) + setColumnWidth(pos.x, component->getSize().x); + if(autoFit.y && getRowHeight(pos.y) < component->getSize().y) + setRowHeight(pos.y, component->getSize().y); + + component->setOffset(getCellOffset(pos)); +} + +void ComponentListComponent::setRowHeight(int row, unsigned int size) +{ + mRowHeights[row] = size; + updateSize(); +} + +void ComponentListComponent::setColumnWidth(int col, unsigned int size) +{ + mColumnWidths[col] = size; + updateSize(); +} + +unsigned int ComponentListComponent::getRowHeight(int row) { return mRowHeights[row]; } +unsigned int ComponentListComponent::getColumnWidth(int col) { return mColumnWidths[col]; } + +Vector2i ComponentListComponent::getCellOffset(Vector2u pos) +{ + Vector2i offset; + + for(unsigned int y = 0; y < pos.y; y++) + offset.y += getRowHeight(y); + + for(unsigned int x = 0; x < pos.x; x++) + offset.x += getColumnWidth(x); + + ComponentEntry* entry = getCell(pos.x, pos.y); + + Vector2u gridSize; + for(unsigned int x = pos.x; x < pos.x + entry->box.size.x; x++) + gridSize.x += getColumnWidth(x); + for(unsigned int y = pos.y; y < pos.y + entry->box.size.y; y++) + gridSize.y += getRowHeight(y); + + //if AlignCenter, add half of cell width - half of control width + if(entry->alignment == AlignCenter) + offset.x += gridSize.x / 2 - entry->component->getSize().x / 2; + + //if AlignRight, add cell width - control width + if(entry->alignment == AlignRight) + offset.x += gridSize.x - entry->component->getSize().x; + + //always center on the Y axis + offset.y += gridSize.y / 2 - entry->component->getSize().y / 2; + + return offset; +} + +void ComponentListComponent::setCell(unsigned int x, unsigned int y, ComponentEntry* entry) +{ + if(x < 0 || y < 0 || x >= mGridSize.x || y >= mGridSize.y) + { + LOG(LogError) << "Invalid setCell - position " << x << ", " << y << " out of bounds!"; + return; + } + + mGrid[y * mGridSize.x + x] = entry; +} + +ComponentListComponent::ComponentEntry* ComponentListComponent::getCell(unsigned int x, unsigned int y) +{ + if(x < 0 || y < 0 || x >= mGridSize.x || y >= mGridSize.y) + { + LOG(LogError) << "Invalid getCell - position " << x << ", " << y << " out of bounds!"; + return NULL; + } + + return mGrid[y * mGridSize.x + x]; +} + +void ComponentListComponent::updateSize() +{ + mSize = Vector2u(0, 0); + for(unsigned int x = 0; x < mGridSize.x; x++) + mSize.x += getColumnWidth(x); + for(unsigned int y = 0; y < mGridSize.y; y++) + mSize.y += getRowHeight(y); +} + +void ComponentListComponent::updateComponentOffsets() +{ + for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) + { + iter->component->setOffset(getCellOffset((Vector2u)iter->box.pos)); + } +} + +bool ComponentListComponent::input(InputConfig* config, Input input) +{ + if(cursorValid() && getCell(mCursor.x, mCursor.y)->component->input(config, input)) + return true; + + if(!input.value) + return false; + + if(config->isMappedTo("down", input)) + { + moveCursor(Vector2i(0, 1)); + return true; + } + if(config->isMappedTo("up", input)) + { + moveCursor(Vector2i(0, -1)); + return true; + } + + return false; +} + +void ComponentListComponent::resetCursor() +{ + if(mEntries.size() == 0) + { + mCursor = Vector2i(-1, -1); + return; + } + + mCursor = mEntries.at(0).box.pos; +} + +void ComponentListComponent::moveCursor(Vector2i dir) +{ + if(dir.x != 0 && dir.y != 0) + { + LOG(LogError) << "Invalid cursor move dir!"; + return; + } + + if(!cursorValid()) + { + resetCursor(); + if(!cursorValid()) + return; + } + + Vector2i origCursor = mCursor; + + Vector2i searchAxis(dir.x == 0, dir.y == 0); + + while(mCursor.x >= 0 && mCursor.y >= 0 && mCursor.x < (int)mGridSize.x && mCursor.y < (int)mGridSize.y) + { + mCursor = mCursor + dir; + + Vector2i curDirPos = mCursor; + + //spread out on search axis+ + while(mCursor.x < (int)mGridSize.x && mCursor.y < (int)mGridSize.y) + { + if(cursorValid() && getCell(mCursor.x, mCursor.y)->canFocus) + return; + + mCursor += searchAxis; + } + + //now again on search axis- + mCursor = curDirPos; + while(mCursor.x >= 0 && mCursor.y >= 0) + { + if(cursorValid() && getCell(mCursor.x, mCursor.y)->canFocus) + return; + + mCursor -= searchAxis; + } + } + + //failed to find another focusable element in this direction + mCursor = origCursor; +} + +bool ComponentListComponent::cursorValid() +{ + if(mCursor.x < 0 || mCursor.y < 0 || mCursor.x >= (int)mGridSize.x || mCursor.y >= (int)mGridSize.y) + return false; + + return getCell(mCursor.x, mCursor.y) != NULL; +} + +void ComponentListComponent::update(int deltaTime) +{ + for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) + { + if(iter->updateType == UpdateAlways) + { + iter->component->update(deltaTime); + continue; + } + + if(iter->updateType == UpdateFocused && cursorValid() && getCell(mCursor.x, mCursor.y)->component == iter->component) + { + iter->component->update(deltaTime); + continue; + } + } +} + +void ComponentListComponent::onRender() +{ + Renderer::drawRect(0, 0, getSize().x, getSize().y, 0xFFFFFFAA); + + for(auto iter = mEntries.begin(); iter != mEntries.end(); iter++) + { + iter->component->render(); + } + + //draw cell outlines + /*Vector2i pos; + for(unsigned int x = 0; x < mGridSize.x; x++) + { + for(unsigned int y = 0; y < mGridSize.y; y++) + { + Renderer::drawRect(pos.x, pos.y, getColumnWidth(x), 2, 0x000000AA); + Renderer::drawRect(pos.x, pos.y, 2, getRowHeight(y), 0x000000AA); + Renderer::drawRect(pos.x + getColumnWidth(x), pos.y, 2, getRowHeight(y), 0x000000AA); + Renderer::drawRect(pos.x, pos.y + getRowHeight(y) - 2, getColumnWidth(x), 2, 0x000000AA); + + pos.y += getRowHeight(y); + } + + pos.y = 0; + pos.x += getColumnWidth(x); + }*/ + + //draw cursor + if(cursorValid()) + { + ComponentEntry* entry = getCell(mCursor.x, mCursor.y); + Renderer::drawRect(entry->component->getOffset().x, entry->component->getOffset().y, 4, 4, 0xFF0000FF); + Renderer::drawRect(entry->component->getOffset().x, entry->component->getOffset().y, entry->component->getSize().x, entry->component->getSize().y, 0x0000AA88); + } +} + +void ComponentListComponent::onOffsetChanged() +{ + updateComponentOffsets(); +} diff --git a/src/components/ComponentListComponent.h b/src/components/ComponentListComponent.h new file mode 100644 index 000000000..95faf527b --- /dev/null +++ b/src/components/ComponentListComponent.h @@ -0,0 +1,129 @@ +#pragma once + +#include "../GuiComponent.h" + +class ComponentListComponent : public GuiComponent +{ +public: + ComponentListComponent(Window* window, Vector2u gridDimensions); + + enum UpdateBehavior + { + UpdateAlways, UpdateFocused + }; + + enum AlignmentType + { + AlignLeft, AlignRight, AlignCenter + }; + + void setEntry(Vector2u pos, Vector2u size, GuiComponent* component, bool canFocus, AlignmentType align, Vector2 autoFit, UpdateBehavior updateType = UpdateAlways); + + void onOffsetChanged() override; + + bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; + void onRender() override; + + void setColumnWidth(int col, unsigned int size); + void setRowHeight(int row, unsigned int size); + + void resetCursor(); + bool cursorValid(); + +private: + class ComponentEntry + { + public: + Rect box; + GuiComponent* component; + UpdateBehavior updateType; + AlignmentType alignment; + bool canFocus; + + ComponentEntry() : component(NULL), updateType(UpdateAlways), canFocus(true), alignment(AlignCenter) {}; + ComponentEntry(Rect b, GuiComponent* comp, UpdateBehavior update, bool focus, AlignmentType align) : box(b), component(comp), updateType(update), canFocus(focus), alignment(align) {}; + + operator bool() const + { + return component != NULL; + } + }; + + //Offset we render components by (for scrolling). + Vector2i mComponentOffset; + + Vector2u mGridSize; + ComponentEntry** mGrid; + std::vector mEntries; + void makeCells(Vector2u size); + void setCell(unsigned int x, unsigned int y, ComponentEntry* entry); + ComponentEntry* getCell(unsigned int x, unsigned int y); + + Vector2u mSelectedCellIndex; + + unsigned int getColumnWidth(int col); + unsigned int getRowHeight(int row); + + unsigned int* mColumnWidths; + unsigned int* mRowHeights; + + Vector2i getCellOffset(Vector2u gridPos); + void updateSize(); + + void moveCursor(Vector2i dir); + Vector2i mCursor; + + void updateComponentOffsets(); +}; + +//ability to define a list of components in terms of a grid + +//input +//pass to selected component +// if returns true, stop +// else, process: +// if input == up/down +// scroll to prev/next selectable component in grid Y +// if input == left/right +// scroll to prev/next selectable component in grid X +// if input == accept +// call registered function? + +//entry struct/class +// GuiComponent* component - component to work with +// bool canFocus - can we pass input to this? (necessary for labels to not be selectable) +// Function* selectFunc? +// UpdateBehavior update - how to handle updates (all the time or only when focused) + +//update +//animate component offset to display selected component within the bounds +//pass update to all entries with appropriate update behavior + +//render +//clip rect to our size +//render a "selected" effect behind component with focus somehow +// an edge filter would be cool, but we can't really do that without shader support +// a transparent rect will work for now, but it's kind of ugly...maybe a GuiBox +//glTranslatef by our render offset +// doesn't handle getGlobalOffset for our components...would need parenting for that + +//methods +//List::setEntry(Vector2i gridPos, GuiComponent* component, bool canFocus, AlignmentType align, +// Function* selectFunc = NULL, UpdateBehavior updateType = UpdateAlways); + +//example of setting up the SettingsMenu list: +//ComponentListComponent list; +//int row = 0; +//TextComponent* label = new TextComponent(Vector2i(0, 0), "Debug:", font, lblColor, etc); +// +//list.setEntry(Vector2i(-1, row), label, false, AlignRight); +//list.setEntry(Vector2i(0, row++), &mDebugSwitch, true, AlignLeft); +//... +//list.setEntry(Rect(-1, row, 2, 1), &mSaveButton, true, AlignCenter); + +//example of setting up GameGrid list: +//ComponentListComponent list; +//for(int y = 0; y < yMax; y++) +// for(int x = 0; x < xMax; x++) +// list.setEntry(Vector2i(x, y), getGameImage(x, y), true, AlignCenter, &this->onSelectGame); diff --git a/src/components/GuiMenu.cpp b/src/components/GuiMenu.cpp index a0bd43fa6..4e678d527 100644 --- a/src/components/GuiMenu.cpp +++ b/src/components/GuiMenu.cpp @@ -5,6 +5,7 @@ #include "../SystemData.h" #include "GuiGameList.h" #include "../Settings.h" +#include "GuiSettingsMenu.h" GuiMenu::GuiMenu(Window* window, GuiGameList* parent) : GuiComponent(window) { @@ -52,6 +53,10 @@ void GuiMenu::executeCommand(std::string command) //reload the game list SystemData::loadConfig(); mParent->setSystemId(0); + }else if(command == "es_settings") + { + mWindow->pushGui(new GuiSettingsMenu(mWindow)); + delete this; }else{ if(system(command.c_str()) != 0) { @@ -69,6 +74,9 @@ void GuiMenu::populateList() //the method is GuiList::addObject(std::string displayString, std::string commandString, unsigned int displayHexColor); //the list will automatically adjust as items are added to it, this should be the only area you need to change //if you want to do something special within ES, override your command in the executeComand() method + + mList->addObject("Settings", "es_settings", 0x0000FFFF); + mList->addObject("Restart", "sudo shutdown -r now", 0x0000FFFF); mList->addObject("Shutdown", "sudo shutdown -h now", 0x0000FFFF); diff --git a/src/components/GuiSettingsMenu.cpp b/src/components/GuiSettingsMenu.cpp new file mode 100644 index 000000000..3e31d7fc0 --- /dev/null +++ b/src/components/GuiSettingsMenu.cpp @@ -0,0 +1,70 @@ +#include "GuiSettingsMenu.h" +#include "../Renderer.h" +#include "../Settings.h" +#include "SliderComponent.h" + +GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(window), + mList(window, Vector2u(2, 3)), + mDrawFramerateSwitch(window) +{ + addChild(&mList); + + mList.setOffset(Renderer::getScreenWidth() / 4, 0); + + TextComponent* label = new TextComponent(mWindow); + label->setText("Draw Framerate: "); + label->setColor(0x0000FFFF); + mList.setEntry(Vector2u(0, 0), Vector2u(1, 1), label, false, ComponentListComponent::AlignRight, Vector2(true, true)); + mLabels.push_back(label); + + //drawFramerate switch + mList.setEntry(Vector2u(1, 0), Vector2u(1, 1), &mDrawFramerateSwitch, true, ComponentListComponent::AlignCenter, Vector2(true, true)); + + label = new TextComponent(mWindow); + label->setText("Volume: "); + label->setColor(0x0000FFFF); + mList.setEntry(Vector2u(0, 1), Vector2u(1, 1), label, false, ComponentListComponent::AlignRight, Vector2(true, true)); + + //volume slider + SliderComponent* slider = new SliderComponent(mWindow, 0, 1); + mList.setEntry(Vector2u(1, 1), Vector2u(1, 1), slider, true, ComponentListComponent::AlignCenter, Vector2(true, true)); + + label = new TextComponent(mWindow); + label->setText("B TO CLOSE"); + label->setColor(0x00FF00FF); + mList.setEntry(Vector2u(0, 2), Vector2u(2, 1), label, true, ComponentListComponent::AlignCenter, Vector2(false, true)); + mLabels.push_back(label); + + mList.setOffset(Renderer::getScreenWidth() / 2 - mList.getSize().x / 2, 0); + + loadStates(); +} + +GuiSettingsMenu::~GuiSettingsMenu() +{ + for(auto iter = mLabels.begin(); iter != mLabels.end(); iter++) + { + delete *iter; + } +} + +bool GuiSettingsMenu::input(InputConfig* config, Input input) +{ + //let our children (read: list) go first + if(GuiComponent::input(config, input)) + return true; + + if(config->isMappedTo("b", input) && input.value) + { + delete this; + return true; + } + + return false; +} + +void GuiSettingsMenu::loadStates() +{ + Settings* s = Settings::getInstance(); + mDrawFramerateSwitch.setState(s->getBool("DRAWFRAMERATE")); +} diff --git a/src/components/GuiSettingsMenu.h b/src/components/GuiSettingsMenu.h new file mode 100644 index 000000000..8f123a335 --- /dev/null +++ b/src/components/GuiSettingsMenu.h @@ -0,0 +1,29 @@ +#ifndef _SETTINGSMENU_H_ +#define _SETTINGSMENU_H_ + +#include "../GuiComponent.h" +#include "ComponentListComponent.h" +#include +#include "SwitchComponent.h" +#include "TextComponent.h" + +class GuiSettingsMenu : public GuiComponent +{ +public: + GuiSettingsMenu(Window* window); + ~GuiSettingsMenu(); + + bool input(InputConfig* config, Input input) override; + +private: + void loadStates(); + void applyStates(); + + ComponentListComponent mList; + + SwitchComponent mDrawFramerateSwitch; + + std::vector mLabels; +}; + +#endif diff --git a/src/components/SliderComponent.cpp b/src/components/SliderComponent.cpp new file mode 100644 index 000000000..117d84600 --- /dev/null +++ b/src/components/SliderComponent.cpp @@ -0,0 +1,77 @@ +#include "SliderComponent.h" +#include +#include "../Renderer.h" + +SliderComponent::SliderComponent(Window* window, float min, float max) : GuiComponent(window), + mMin(min), mMax(max), mMoveRate(0) +{ + assert((min - max) != 0); + + mValue = (max + min) / 2; + + //calculate move scale + mMoveScale = (max - min) * 0.0007f; + + setSize(Vector2u(128, 32)); +} + +bool SliderComponent::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("left", input)) + { + if(input.value) + mMoveRate = -1; + else + mMoveRate = 0; + + return true; + } + if(config->isMappedTo("right", input)) + { + if(input.value) + mMoveRate = 1; + else + mMoveRate = 0; + + return true; + } + + return GuiComponent::input(config, input); +} + +void SliderComponent::update(int deltaTime) +{ + mValue += mMoveRate * deltaTime * mMoveScale; + + if(mValue < mMin) + mValue = mMin; + if(mValue > mMax) + mValue = mMax; + + GuiComponent::update(deltaTime); +} + +void SliderComponent::onRender() +{ + //render line + const int lineWidth = 2; + Renderer::drawRect(0, mSize.y / 2 - lineWidth / 2, mSize.x, lineWidth, 0x000000CC); + + //render left end + const int capWidth = (int)(mSize.x * 0.03f); + Renderer::drawRect(0, 0, capWidth, mSize.y, 0x000000CC); + + //render right end + Renderer::drawRect(mSize.x - capWidth, 0, capWidth, mSize.y, 0x000000CC); + + //render our value + const int lineLength = mSize.x - capWidth; + Renderer::drawRect((int)(((mValue + mMin) / mMax) * lineLength), 0, capWidth, mSize.y, 0x0000FFFF); + + GuiComponent::onRender(); +} + +void SliderComponent::setSize(Vector2u size) +{ + mSize = size; +} diff --git a/src/components/SliderComponent.h b/src/components/SliderComponent.h new file mode 100644 index 000000000..4550cf652 --- /dev/null +++ b/src/components/SliderComponent.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../GuiComponent.h" + +class SliderComponent : public GuiComponent +{ +public: + SliderComponent(Window* window, float min, float max); + + void setValue(float val); + float getValue(); + + bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; + void onRender() override; + + void setSize(Vector2u size); + +private: + float mMin, mMax; + float mValue; + float mMoveScale; + + float mMoveRate; +}; diff --git a/src/components/SwitchComponent.cpp b/src/components/SwitchComponent.cpp new file mode 100644 index 000000000..ac78c810e --- /dev/null +++ b/src/components/SwitchComponent.cpp @@ -0,0 +1,43 @@ +#include "SwitchComponent.h" +#include "../Renderer.h" +#include "../Font.h" + +SwitchComponent::SwitchComponent(Window* window, bool state) : GuiComponent(window), mState(state) +{ + //mSize = Vector2u((unsigned int)(Renderer::getScreenWidth() * 0.05), + // (unsigned int)(Renderer::getScreenHeight() * 0.05)); + + Renderer::getDefaultFont(Renderer::MEDIUM)->sizeText("OFF", (int*)&mSize.x, (int*)&mSize.y); +} + +bool SwitchComponent::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("a", input) && input.value) + { + mState = !mState; + return true; + } + + return false; +} + +void SwitchComponent::onRender() +{ + Renderer::pushClipRect(getGlobalOffset(), getSize()); + + Renderer::drawText(mState ? "ON" : "OFF", 0, 0, mState ? 0x00FF00FF : 0xFF0000FF, Renderer::getDefaultFont(Renderer::MEDIUM)); + + Renderer::popClipRect(); + + GuiComponent::onRender(); +} + +bool SwitchComponent::getState() +{ + return mState; +} + +void SwitchComponent::setState(bool state) +{ + mState = state; +} diff --git a/src/components/SwitchComponent.h b/src/components/SwitchComponent.h new file mode 100644 index 000000000..485c4d7e9 --- /dev/null +++ b/src/components/SwitchComponent.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../GuiComponent.h" + +class SwitchComponent : public GuiComponent +{ +public: + SwitchComponent(Window* window, bool state = false); + + bool input(InputConfig* config, Input input) override; + void onRender() override; + + bool getState(); + void setState(bool state); + +private: + bool mState; +}; diff --git a/src/components/TextComponent.cpp b/src/components/TextComponent.cpp index af707ec21..76312e80c 100644 --- a/src/components/TextComponent.cpp +++ b/src/components/TextComponent.cpp @@ -2,12 +2,12 @@ #include "../Renderer.h" #include "../Log.h" -TextComponent::TextComponent(Window* window) : GuiComponent(window), mFont(NULL), mColor(0x000000FF) +TextComponent::TextComponent(Window* window) : GuiComponent(window), mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true) { } TextComponent::TextComponent(Window* window, const std::string& text, Font* font, Vector2i pos, Vector2u size) : GuiComponent(window), - mFont(NULL), mColor(0x000000FF) + mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true) { setText(text); setFont(font); @@ -22,12 +22,22 @@ void TextComponent::setBox(Vector2i pos, Vector2u size) void TextComponent::setExtent(Vector2u size) { - mSize = size; + if(size == Vector2u(0, 0)) + { + mAutoCalcExtent = true; + calculateExtent(); + }else{ + mAutoCalcExtent = false; + mSize = size; + } } void TextComponent::setFont(Font* font) { mFont = font; + + if(mAutoCalcExtent) + calculateExtent(); } void TextComponent::setColor(unsigned int color) @@ -38,6 +48,9 @@ void TextComponent::setColor(unsigned int color) void TextComponent::setText(const std::string& text) { mText = text; + + if(mAutoCalcExtent) + calculateExtent(); } void TextComponent::onRender() @@ -49,7 +62,7 @@ void TextComponent::onRender() return; } - Renderer::pushClipRect(getOffset(), getSize()); + Renderer::pushClipRect(getGlobalOffset(), getSize()); Renderer::drawWrappedText(mText, 0, 0, mSize.x, mColor, font); @@ -57,3 +70,15 @@ void TextComponent::onRender() GuiComponent::onRender(); } + +void TextComponent::calculateExtent() +{ + Font* font = (mFont ? mFont : Renderer::getDefaultFont(Renderer::MEDIUM)); + if(font == NULL) + { + LOG(LogError) << "TextComponent can't get a valid font!"; + return; + } + + font->sizeText(mText, (int*)&mSize.x, (int*)&mSize.y); +} diff --git a/src/components/TextComponent.h b/src/components/TextComponent.h index c66e59ac0..67a6f9b5d 100644 --- a/src/components/TextComponent.h +++ b/src/components/TextComponent.h @@ -12,15 +12,18 @@ public: void setFont(Font* font); void setBox(Vector2i pos, Vector2u size); - void setExtent(Vector2u size); + void setExtent(Vector2u size); //Use Vector2u(0, 0) to automatically generate extent. void setText(const std::string& text); void setColor(unsigned int color); void onRender(); private: + void calculateExtent(); + unsigned int mColor; Font* mFont; + bool mAutoCalcExtent; std::string mText; }; diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index c73edea64..0aea6cea4 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -15,7 +15,6 @@ #define MARQUEE_RATE 3 //A graphical list. Supports multiple colors for rows and scrolling. -//TODO - add truncation to text rendering if name exceeds a maximum width (a trailing elipses, perhaps). Marquee would be nice too. template class TextListComponent : public GuiComponent {