ES-DE/es-core/src/components/OptionListComponent.h

343 lines
8.6 KiB
C
Raw Normal View History

2013-10-01 21:52:30 +00:00
#pragma once
#ifndef ES_CORE_COMPONENTS_OPTION_LIST_COMPONENT_H
#define ES_CORE_COMPONENTS_OPTION_LIST_COMPONENT_H
2013-10-01 21:52:30 +00:00
#include "GuiComponent.h"
#include "resources/Font.h"
#include "Renderer.h"
#include "Window.h"
#include "components/TextComponent.h"
#include "components/ImageComponent.h"
#include "components/MenuComponent.h"
2014-03-06 01:49:32 +00:00
#include <sstream>
#include "Log.h"
2013-10-01 21:52:30 +00:00
//Used to display a list of options.
//Can select one or multiple options.
2014-03-06 01:49:32 +00:00
// if !multiSelect
// * <- curEntry ->
// always
// * press a -> open full list
#define CHECKED_PATH ":/checkbox_checked.svg"
#define UNCHECKED_PATH ":/checkbox_unchecked.svg"
2013-10-03 20:58:09 +00:00
template<typename T>
2013-10-01 21:52:30 +00:00
class OptionListComponent : public GuiComponent
{
2014-03-06 01:49:32 +00:00
private:
struct OptionListData
2013-10-03 20:58:09 +00:00
{
2014-03-06 01:49:32 +00:00
std::string name;
2013-10-03 20:58:09 +00:00
T object;
2014-03-06 01:49:32 +00:00
bool selected;
2013-10-03 20:58:09 +00:00
};
2014-03-06 01:49:32 +00:00
class OptionListPopup : public GuiComponent
2013-10-03 20:58:09 +00:00
{
2014-03-06 01:49:32 +00:00
private:
MenuComponent mMenu;
OptionListComponent<T>* mParent;
2014-03-06 01:49:32 +00:00
public:
OptionListPopup(Window* window, OptionListComponent<T>* parent, const std::string& title) : GuiComponent(window),
mMenu(window, title.c_str()), mParent(parent)
{
2014-03-06 01:49:32 +00:00
auto font = Font::get(FONT_SIZE_MEDIUM);
ComponentListRow row;
// for select all/none
std::vector<ImageComponent*> checkboxes;
2014-03-06 01:49:32 +00:00
for(auto it = mParent->mEntries.begin(); it != mParent->mEntries.end(); it++)
{
2014-03-06 01:49:32 +00:00
row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, strToUpper(it->name), font, 0x777777FF), true);
2014-03-06 01:49:32 +00:00
OptionListData& e = *it;
if(mParent->mMultiSelect)
{
// add checkbox
auto checkbox = std::make_shared<ImageComponent>(mWindow);
checkbox->setImage(it->selected ? CHECKED_PATH : UNCHECKED_PATH);
2014-03-22 21:02:25 +00:00
checkbox->setResize(0, font->getLetterHeight());
row.addElement(checkbox, false);
// input handler
// update checkbox state & selected value
row.makeAcceptInputHandler([this, &e, checkbox]
{
e.selected = !e.selected;
checkbox->setImage(e.selected ? CHECKED_PATH : UNCHECKED_PATH);
mParent->onSelectedChanged();
});
// for select all/none
checkboxes.push_back(checkbox.get());
}else{
// input handler for non-multiselect
// update selected value and close
row.makeAcceptInputHandler([this, &e]
{
mParent->mEntries.at(mParent->getSelectedId()).selected = false;
e.selected = true;
mParent->onSelectedChanged();
delete this;
});
}
// also set cursor to this row if we're not multi-select and this row is selected
mMenu.addRow(row, (!mParent->mMultiSelect && it->selected));
}
2013-10-01 21:52:30 +00:00
mMenu.addButton("BACK", "accept", [this] { delete this; });
if(mParent->mMultiSelect)
{
mMenu.addButton("SELECT ALL", "select all", [this, checkboxes] {
for(unsigned int i = 0; i < mParent->mEntries.size(); i++)
{
mParent->mEntries.at(i).selected = true;
checkboxes.at(i)->setImage(CHECKED_PATH);
}
mParent->onSelectedChanged();
});
mMenu.addButton("SELECT NONE", "select none", [this, checkboxes] {
for(unsigned int i = 0; i < mParent->mEntries.size(); i++)
{
mParent->mEntries.at(i).selected = false;
checkboxes.at(i)->setImage(UNCHECKED_PATH);
}
mParent->onSelectedChanged();
});
}
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f);
2014-03-06 01:49:32 +00:00
addChild(&mMenu);
}
2013-10-01 21:52:30 +00:00
2014-03-06 01:49:32 +00:00
bool input(InputConfig* config, Input input) override
{
if(config->isMappedTo("b", input) && input.value != 0)
{
2014-03-06 01:49:32 +00:00
delete this;
return true;
}
2014-03-06 01:49:32 +00:00
return GuiComponent::input(config, input);
}
std::vector<HelpPrompt> getHelpPrompts() override
{
auto prompts = mMenu.getHelpPrompts();
prompts.push_back(HelpPrompt("b", "back"));
return prompts;
}
2014-03-06 01:49:32 +00:00
};
2014-03-06 01:49:32 +00:00
public:
OptionListComponent(Window* window, const std::string& name, bool multiSelect = false) : GuiComponent(window), mMultiSelect(multiSelect), mName(name),
mText(window), mLeftArrow(window), mRightArrow(window)
2013-10-03 20:58:09 +00:00
{
auto font = Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT);
2014-03-06 01:49:32 +00:00
mText.setFont(font);
mText.setColor(0x777777FF);
2017-08-15 02:34:34 +00:00
mText.setHorizontalAlignment(ALIGN_CENTER);
2014-03-06 01:49:32 +00:00
addChild(&mText);
2013-10-03 20:58:09 +00:00
if(mMultiSelect)
{
2014-03-22 18:04:14 +00:00
mRightArrow.setImage(":/arrow.svg");
addChild(&mRightArrow);
}else{
2014-03-22 18:04:14 +00:00
mLeftArrow.setImage(":/option_arrow.svg");
mLeftArrow.setFlipX(true);
addChild(&mLeftArrow);
2014-03-22 18:04:14 +00:00
mRightArrow.setImage(":/option_arrow.svg");
addChild(&mRightArrow);
}
2014-03-22 18:04:14 +00:00
setSize(mLeftArrow.getSize().x() + mRightArrow.getSize().x(), font->getHeight());
2013-10-03 20:58:09 +00:00
}
// handles positioning/resizing of text and arrows
2014-03-06 01:49:32 +00:00
void onSizeChanged() override
2013-10-03 20:58:09 +00:00
{
2014-03-22 21:02:25 +00:00
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
if(mSize.x() < (mLeftArrow.getSize().x() + mRightArrow.getSize().x()))
LOG(LogWarning) << "OptionListComponent too narrow!";
2014-03-22 18:04:14 +00:00
mText.setSize(mSize.x() - mLeftArrow.getSize().x() - mRightArrow.getSize().x(), mText.getFont()->getHeight());
// position
mLeftArrow.setPosition(0, (mSize.y() - mLeftArrow.getSize().y()) / 2);
mText.setPosition(mLeftArrow.getPosition().x() + mLeftArrow.getSize().x(), (mSize.y() - mText.getSize().y()) / 2);
mRightArrow.setPosition(mText.getPosition().x() + mText.getSize().x(), (mSize.y() - mRightArrow.getSize().y()) / 2);
2013-10-03 20:58:09 +00:00
}
2014-03-06 01:49:32 +00:00
bool input(InputConfig* config, Input input) override
2013-10-03 20:58:09 +00:00
{
2014-03-06 01:49:32 +00:00
if(input.value != 0)
2013-10-03 20:58:09 +00:00
{
2014-03-06 01:49:32 +00:00
if(config->isMappedTo("a", input))
{
open();
return true;
}
if(!mMultiSelect)
{
if(config->isMappedTo("left", input))
{
// move selection to previous
unsigned int i = getSelectedId();
int next = (int)i - 1;
if(next < 0)
next += mEntries.size();
mEntries.at(i).selected = false;
mEntries.at(next).selected = true;
onSelectedChanged();
return true;
2014-03-06 01:49:32 +00:00
}else if(config->isMappedTo("right", input))
{
// move selection to next
unsigned int i = getSelectedId();
int next = (i + 1) % mEntries.size();
mEntries.at(i).selected = false;
mEntries.at(next).selected = true;
onSelectedChanged();
return true;
2014-03-06 01:49:32 +00:00
}
}
2013-10-03 20:58:09 +00:00
}
2014-03-06 01:49:32 +00:00
return GuiComponent::input(config, input);
2013-10-03 20:58:09 +00:00
}
2013-10-11 00:55:57 +00:00
std::vector<T> getSelectedObjects()
{
std::vector<T> ret;
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
{
2014-03-06 01:49:32 +00:00
if(it->selected)
2013-10-11 00:55:57 +00:00
ret.push_back(it->object);
}
return ret;
}
2014-03-06 01:49:32 +00:00
T getSelected()
{
2014-03-06 01:49:32 +00:00
assert(mMultiSelect == false);
auto selected = getSelectedObjects();
assert(selected.size() == 1);
return selected.at(0);
}
2014-03-06 01:49:32 +00:00
void add(const std::string& name, const T& obj, bool selected)
2013-10-03 20:58:09 +00:00
{
2014-03-06 01:49:32 +00:00
OptionListData e;
e.name = name;
e.object = obj;
e.selected = selected;
2014-03-06 01:49:32 +00:00
mEntries.push_back(e);
onSelectedChanged();
2013-10-03 20:58:09 +00:00
}
void selectAll()
{
for(unsigned int i = 0; i < mEntries.size(); i++)
{
mEntries.at(i).selected = true;
}
onSelectedChanged();
}
void selectNone()
{
for(unsigned int i = 0; i < mEntries.size(); i++)
{
mEntries.at(i).selected = false;
}
onSelectedChanged();
}
2014-03-06 01:49:32 +00:00
private:
unsigned int getSelectedId()
{
assert(mMultiSelect == false);
for(unsigned int i = 0; i < mEntries.size(); i++)
{
if(mEntries.at(i).selected)
return i;
}
LOG(LogWarning) << "OptionListComponent::getSelectedId() - no selected element found, defaulting to 0";
return 0;
}
2014-03-06 01:49:32 +00:00
void open()
2013-10-03 20:58:09 +00:00
{
mWindow->pushGui(new OptionListPopup(mWindow, this, mName));
}
2014-03-06 01:49:32 +00:00
void onSelectedChanged()
2013-10-03 20:58:09 +00:00
{
2014-03-06 01:49:32 +00:00
if(mMultiSelect)
{
2014-03-06 01:49:32 +00:00
// display # selected
std::stringstream ss;
ss << getSelectedObjects().size() << " SELECTED";
2014-03-06 01:49:32 +00:00
mText.setText(ss.str());
mText.setSize(0, mText.getSize().y());
setSize(mText.getSize().x() + mRightArrow.getSize().x() + 24, mText.getSize().y());
if(mParent) // hack since theres no "on child size changed" callback atm...
mParent->onSizeChanged();
2014-03-06 01:49:32 +00:00
}else{
// display currently selected + l/r cursors
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
{
2014-03-06 01:49:32 +00:00
if(it->selected)
{
mText.setText(strToUpper(it->name));
mText.setSize(0, mText.getSize().y());
setSize(mText.getSize().x() + mLeftArrow.getSize().x() + mRightArrow.getSize().x() + 24, mText.getSize().y());
if(mParent) // hack since theres no "on child size changed" callback atm...
mParent->onSizeChanged();
2014-03-06 01:49:32 +00:00
break;
}
}
}
2014-03-06 01:49:32 +00:00
}
2013-10-01 21:52:30 +00:00
std::vector<HelpPrompt> getHelpPrompts() override
{
std::vector<HelpPrompt> prompts;
if(!mMultiSelect)
prompts.push_back(HelpPrompt("left/right", "change"));
prompts.push_back(HelpPrompt("a", "select"));
return prompts;
}
2014-03-06 01:49:32 +00:00
bool mMultiSelect;
std::string mName;
2014-03-06 01:49:32 +00:00
TextComponent mText;
ImageComponent mLeftArrow;
ImageComponent mRightArrow;
2014-03-06 01:49:32 +00:00
std::vector<OptionListData> mEntries;
2013-10-01 21:52:30 +00:00
};
#endif // ES_CORE_COMPONENTS_OPTION_LIST_COMPONENT_H