Work on new OptionListComponent.

This commit is contained in:
Aloshi 2014-03-05 19:49:32 -06:00
parent 8928ce49ec
commit d0dfe480fa
4 changed files with 135 additions and 294 deletions

View file

@ -104,6 +104,9 @@ void ComponentList::onCursorChanged(const CursorState& state)
void ComponentList::render(const Eigen::Affine3f& parentTrans) void ComponentList::render(const Eigen::Affine3f& parentTrans)
{ {
if(!size())
return;
Eigen::Affine3f trans = parentTrans * getTransform(); Eigen::Affine3f trans = parentTrans * getTransform();
// clip everything to be inside our bounds // clip everything to be inside our bounds

View file

@ -2,35 +2,90 @@
#include "../GuiComponent.h" #include "../GuiComponent.h"
#include "../resources/Font.h" #include "../resources/Font.h"
#include <vector>
#include <functional>
#include "../Renderer.h" #include "../Renderer.h"
#include "NinePatchComponent.h"
#include "../Window.h" #include "../Window.h"
#include "TextComponent.h"
#include "MenuComponent.h"
#include <sstream>
//Used to display a list of options. //Used to display a list of options.
//Can select one or multiple options. //Can select one or multiple options.
// if !multiSelect
// * <- curEntry ->
// always
// * press a -> open full list
template<typename T> template<typename T>
class OptionListComponent : public GuiComponent class OptionListComponent : public GuiComponent
{ {
public: private:
OptionListComponent(Window* window, bool multiSelect = false) : GuiComponent(window), struct OptionListData
mMultiSelect(multiSelect)
{ {
if(multiSelect) std::string name;
setSize(getFont()->sizeText("0 selected"));
else
setSize(getFont()->sizeText("Not set"));
}
struct ListEntry
{
std::string text;
bool selected;
T object; T object;
bool selected;
}; };
class OptionListPopup : public GuiComponent
{
private:
MenuComponent mMenu;
OptionListComponent<T>* mParent;
public:
OptionListPopup(Window* window, OptionListComponent<T>* parent) : GuiComponent(window),
mMenu(window, "optionlist"), mParent(parent)
{
auto font = Font::get(FONT_SIZE_MEDIUM);
ComponentListRow row;
for(auto it = mParent->mEntries.begin(); it != mParent->mEntries.end(); it++)
{
row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, it->name, font, 0x777777FF), true);
mMenu.addRow(row);
}
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, (Renderer::getScreenHeight() - mMenu.getSize().y()) / 2);
addChild(&mMenu);
}
bool input(InputConfig* config, Input input) override
{
if(config->isMappedTo("b", input) && input.value != 0)
{
delete this;
return true;
}
return GuiComponent::input(config, input);
}
virtual ~OptionListPopup()
{
// commit changes
}
};
public:
OptionListComponent(Window* window, bool multiSelect = false) : GuiComponent(window), mText(window), mMultiSelect(multiSelect)
{
auto font = Font::get(FONT_SIZE_MEDIUM);
mText.setFont(font);
mText.setColor(0x777777FF);
mText.setCentered(true);
addChild(&mText);
setSize(Renderer::getScreenWidth() * 0.2f, (float)font->getHeight());
}
void onSizeChanged() override
{
mText.setSize(mSize);
}
bool input(InputConfig* config, Input input) override bool input(InputConfig* config, Input input) override
{ {
@ -41,90 +96,18 @@ public:
open(); open();
return true; return true;
} }
} if(!mMultiSelect)
return GuiComponent::input(config, input);
}
void render(const Eigen::Affine3f& parentTrans)
{
std::shared_ptr<Font> font = getFont();
Renderer::setMatrix(parentTrans * getTransform());
unsigned int color = 0x000000FF;
if(mMultiSelect)
{
//draw "# selected"
unsigned int selectedCount = 0;
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
{ {
if(it->selected) if(config->isMappedTo("left", input))
selectedCount++;
}
std::stringstream ss;
ss << selectedCount << " selected";
font->drawText(ss.str(), Eigen::Vector2f(0, 0), color);
}else{
//draw selected option
bool found = false;
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
{
if(it->selected)
{ {
font->drawText(it->text, Eigen::Vector2f(0, 0), color); // move selection to previous
found = true; }else if(config->isMappedTo("right", input))
break; {
// move selection to next
} }
} }
if(!found)
font->drawText("Not set", Eigen::Vector2f(0, 0), color);
} }
return GuiComponent::input(config, input);
renderChildren(parentTrans * getTransform());
}
ListEntry makeEntry(const std::string& name, T obj, bool selected = false) const
{
ListEntry e;
e.text = name;
e.object = obj;
e.selected = selected;
return e;
}
void populate(std::vector<T>& vec, std::function<ListEntry(const T&)> selector)
{
for(auto it = vec.begin(); it != vec.end(); it++)
{
ListEntry e = selector(*it);
if(!e.text.empty())
addEntry(e);
}
}
void addEntry(ListEntry e)
{
mEntries.push_back(e);
Eigen::Vector2f size = getFont()->sizeText(e.text);
if(size.x() > mSize.x())
setSize(size.x(), mSize.y());
}
std::vector<const ListEntry*> getSelected()
{
std::vector<const ListEntry*> ret;
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
{
if((*it).selected)
ret.push_back(&(*it));
}
return ret;
} }
std::vector<T> getSelectedObjects() std::vector<T> getSelectedObjects()
@ -132,206 +115,63 @@ public:
std::vector<T> ret; std::vector<T> ret;
for(auto it = mEntries.begin(); it != mEntries.end(); it++) for(auto it = mEntries.begin(); it != mEntries.end(); it++)
{ {
if((*it).selected) if(it->selected)
ret.push_back(it->object); ret.push_back(it->object);
} }
return ret; return ret;
} }
T getSelected()
{
assert(mMultiSelect == false);
auto selected = getSelectedObjects();
assert(selected.size() == 1);
return selected.at(0);
}
void add(const std::string& name, const T& obj, bool selected)
{
OptionListData e;
e.name = name;
e.object = obj;
e.selected = selected;
mEntries.push_back(e);
onSelectedChanged();
}
private: private:
void open() void open()
{ {
mWindow->pushGui(new OptionListPopup(mWindow, *this)); mWindow->pushGui(new OptionListPopup(mWindow, this));
} }
void select(unsigned int i) void onSelectedChanged()
{ {
if(i >= mEntries.size()) if(mMultiSelect)
return; {
// display # selected
if(!mMultiSelect) std::stringstream ss;
ss << getSelectedObjects().size() << " selected";
mText.setText(ss.str());
}else{
// display currently selected + l/r cursors
for(auto it = mEntries.begin(); it != mEntries.end(); it++) for(auto it = mEntries.begin(); it != mEntries.end(); it++)
it->selected = false; {
if(it->selected)
mEntries.at(i).selected = !mEntries.at(i).selected; {
mText.setText(it->name);
break;
}
}
}
} }
std::shared_ptr<Font> getFont()
{
return Font::get(FONT_SIZE_MEDIUM);
}
class OptionListPopup : public GuiComponent
{
public:
OptionListPopup(Window* window, OptionListComponent<T>& optList) : GuiComponent(window),
mOptList(optList), mBox(window, ":/textbox.png"), mCursor(0), mScrollOffset(0), mCursorTimer(0)
{
//find global position
GuiComponent* p = &mOptList;
do {
mPosition += p->getPosition();
} while(p = p->getParent());
mSize = mOptList.getSize();
updateTextCaches();
}
void render(const Eigen::Affine3f& parentTrans) override
{
Eigen::Affine3f trans = parentTrans * getTransform();
std::shared_ptr<Font> font = mOptList.getFont();
unsigned int renderCount = getPageSize();
if(renderCount + mScrollOffset > mTextCaches.size())
renderCount = mTextCaches.size() - mScrollOffset;
unsigned int renderTo = mScrollOffset + renderCount;
float height = (float)renderCount * font->getHeight();
trans.translate(Eigen::Vector3f(0, -height / 2 + font->getHeight() * 0.5f, 0));
mBox.fitTo(Eigen::Vector2f(mSize.x(), height));
mBox.render(trans);
Renderer::setMatrix(trans);
Renderer::drawRect(0, 0, (int)getSize().x(), (int)height, 0xFFFFFFFF);
for(unsigned int i = mScrollOffset; i < renderTo; i++)
{
Renderer::setMatrix(trans);
char rectOpacity = 0x00;
if(i == mCursor)
rectOpacity += 0x22;
if(mOptList.mEntries.at(i).selected)
rectOpacity += 0x44;
Renderer::drawRect(0, 0, (int)mSize.x(), font->getHeight(), 0x00000000 | rectOpacity);
Renderer::setMatrix(trans);
font->renderTextCache(mTextCaches.at(i));
trans = trans.translate(Eigen::Vector3f(0, (float)font->getHeight(), 0));
}
}
bool input(InputConfig* config, Input input)
{
if(input.value != 0)
{
if(config->isMappedTo("b", input))
{
close();
return true;
}
if(config->isMappedTo("a", input))
{
mOptList.select(mCursor);
if(!mOptList.mMultiSelect)
close();
return true;
}
if(config->isMappedTo("up", input))
{
mCursorDir = -1;
mCursorTimer = -350;
moveCursor();
return true;
}
if(config->isMappedTo("down", input))
{
mCursorDir = 1;
mCursorTimer = -350;
moveCursor();
return true;
}
}else{
if(config->isMappedTo("up", input) || config->isMappedTo("down", input))
mCursorDir = 0;
}
return GuiComponent::input(config, input);
}
void update(int deltaTime)
{
if(mCursorDir != 0)
{
mCursorTimer += deltaTime;
while(mCursorTimer >= 100)
{
moveCursor();
mCursorTimer -= 100;
}
}
}
private:
void moveCursor()
{
if(mOptList.mEntries.size() == 0)
return;
if(mCursorDir < 0) //scroll up
{
if(mCursor > 0)
mCursor--;
if(mCursor < mScrollOffset)
mScrollOffset--;
}else if(mCursorDir > 0) //scroll down
{
if(mCursor < mOptList.mEntries.size() - 1)
mCursor++;
if(mCursor - mScrollOffset >= getPageSize())
mScrollOffset++;
}
}
void close()
{
delete this;
}
void updateTextCaches()
{
for(auto it = mTextCaches.begin(); it != mTextCaches.end(); it++)
{
delete *it;
}
mTextCaches.clear();
TextCache* cache;
std::shared_ptr<Font> font = mOptList.getFont();
for(unsigned int i = 0; i < mOptList.mEntries.size(); i++)
{
cache = font->buildTextCache(mOptList.mEntries.at(i).text, 0, 0, 0x000000FF);
mTextCaches.push_back(cache);
}
}
unsigned int getPageSize()
{
return 5;
}
OptionListComponent<T>& mOptList;
NinePatchComponent mBox;
unsigned int mCursor;
int mCursorDir;
int mCursorTimer;
unsigned int mScrollOffset;
std::vector<TextCache*> mTextCaches;
};
bool mMultiSelect; bool mMultiSelect;
std::vector<ListEntry> mEntries; TextComponent mText;
std::vector<OptionListData> mEntries;
}; };

View file

@ -23,20 +23,18 @@ GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window),
using namespace Eigen; using namespace Eigen;
//add filters (with first one selected) //add filters (with first one selected)
mFiltersOpt.addEntry(mFiltersOpt.makeEntry("All Games", mFiltersOpt.add("All Games",
[](SystemData*, FileData*) -> bool { return true; }, true)); [](SystemData*, FileData*) -> bool { return true; }, true);
mFiltersOpt.addEntry(mFiltersOpt.makeEntry("Missing Image", mFiltersOpt.add("Missing Image",
[](SystemData*, FileData* g) -> bool { return g->metadata.get("image").empty(); })); [](SystemData*, FileData* g) -> bool { return g->metadata.get("image").empty(); }, false);
mList.setEntry(Vector2i(0, 0), Vector2i(1, 1), &mFilterLabel, false, ComponentGrid::AlignRight); mList.setEntry(Vector2i(0, 0), Vector2i(1, 1), &mFilterLabel, false, ComponentGrid::AlignRight);
mList.setEntry(Vector2i(1, 0), Vector2i(1, 1), &mFiltersOpt, true, ComponentGrid::AlignLeft); mList.setEntry(Vector2i(1, 0), Vector2i(1, 1), &mFiltersOpt, true, ComponentGrid::AlignLeft);
//add systems (all with a platformid specified selected) //add systems (all with a platformid specified selected)
std::vector<SystemData*> sys = SystemData::sSystemVector; std::vector<SystemData*> sys = SystemData::sSystemVector;
mSystemsOpt.populate(sys, for(auto it = sys.begin(); it != sys.end(); it++)
[&](SystemData* s) { mSystemsOpt.add((*it)->getFullName(), *it, (*it)->getPlatformId() != PlatformIds::PLATFORM_UNKNOWN);
return mSystemsOpt.makeEntry(s->getName(), s, s->getPlatformId() != PlatformIds::PLATFORM_UNKNOWN);
});
mList.setEntry(Vector2i(0, 1), Vector2i(1, 1), &mSystemsLabel, false, ComponentGrid::AlignRight); mList.setEntry(Vector2i(0, 1), Vector2i(1, 1), &mSystemsLabel, false, ComponentGrid::AlignRight);
mList.setEntry(Vector2i(1, 1), Vector2i(1, 1), &mSystemsOpt, true, ComponentGrid::AlignLeft); mList.setEntry(Vector2i(1, 1), Vector2i(1, 1), &mSystemsOpt, true, ComponentGrid::AlignLeft);

View file

@ -41,13 +41,13 @@ GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(window),
std::vector< std::shared_ptr<Scraper> > scrapers; std::vector< std::shared_ptr<Scraper> > scrapers;
scrapers.push_back(std::make_shared<GamesDBScraper>()); scrapers.push_back(std::make_shared<GamesDBScraper>());
scrapers.push_back(std::make_shared<TheArchiveScraper>()); scrapers.push_back(std::make_shared<TheArchiveScraper>());
scraper_list->populate(scrapers, [&] (const std::shared_ptr<Scraper>& sc) {
return scraper_list->makeEntry(sc->getName(), sc, sc->getName() == Settings::getInstance()->getScraper()->getName()); for(auto it = scrapers.begin(); it != scrapers.end(); it++)
}); scraper_list->add((*it)->getName(), *it, (*it)->getName() == Settings::getInstance()->getScraper()->getName());
addSetting("Scraper:", scraper_list, addSetting("Scraper:", scraper_list,
[scraper_list] { [scraper_list] {
if(scraper_list->getSelected().size() > 0) Settings::getInstance()->setScraper(scraper_list->getSelected());
Settings::getInstance()->setScraper(scraper_list->getSelected()[0]->object);
}); });
// scrape ratings // scrape ratings