mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-18 07:05:39 +00:00
Work on new OptionListComponent.
This commit is contained in:
parent
8928ce49ec
commit
d0dfe480fa
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue