ComponentList now only scrolls if content > size.

ComponentList now has a default input handler behavior (forward to
rightmost component in row).
ComponentList now updates the currently selected row.
GuiSettingsMenu has been redone to use the new
MenuComponent/ComponentList scheme.
GuiMenu refactored slightly to be less needlessly ridiculous.
This commit is contained in:
Aloshi 2014-03-02 12:36:23 -06:00
parent fdbbf96d5e
commit e97dd8ff36
6 changed files with 167 additions and 220 deletions

View file

@ -31,6 +31,8 @@ void ComponentList::onSizeChanged()
updateElementSize(it->data);
updateElementPosition(it->data);
}
onCursorChanged(mScrollVelocity != 0 ? CURSOR_SCROLLING : CURSOR_STOPPED);
}
bool ComponentList::input(InputConfig* config, Input input)
@ -39,8 +41,19 @@ bool ComponentList::input(InputConfig* config, Input input)
return false;
// give it to the current row's input handler
if(mEntries.at(mCursor).data.input_handler && mEntries.at(mCursor).data.input_handler(config, input))
return true;
if(mEntries.at(mCursor).data.input_handler)
{
if(mEntries.at(mCursor).data.input_handler(config, input))
return true;
}else{
// no input handler assigned, do the default, which is to give it to the rightmost element in the row
auto& row = mEntries.at(mCursor).data;
if(row.elements.size())
{
if(row.elements.back().component->input(config, input))
return true;
}
}
// input handler didn't consume the input - try to scroll
if(config->isMappedTo("up", input))
@ -57,6 +70,13 @@ bool ComponentList::input(InputConfig* config, Input input)
void ComponentList::update(int deltaTime)
{
listUpdate(deltaTime);
if(size())
{
// update our currently selected row
for(auto it = mEntries.at(mCursor).data.elements.begin(); it != mEntries.at(mCursor).data.elements.end(); it++)
it->component->update(deltaTime);
}
}
void ComponentList::onCursorChanged(const CursorState& state)
@ -69,12 +89,17 @@ void ComponentList::onCursorChanged(const CursorState& state)
mSelectorBarOffset += getRowHeight(mEntries.at(i).data);
}
mCameraOffset = mSelectorBarOffset - (mSize.y() / 2);
// move the camera to scroll
const float totalHeight = getTotalRowHeight();
if(totalHeight > mSize.y())
{
mCameraOffset = mSelectorBarOffset - (mSize.y() / 2);
if(mCameraOffset < 0)
mCameraOffset = 0;
else if(mCameraOffset + mSize.y() > getTotalRowHeight())
mCameraOffset = getTotalRowHeight() - mSize.y();
if(mCameraOffset < 0)
mCameraOffset = 0;
else if(mCameraOffset + mSize.y() > totalHeight)
mCameraOffset = totalHeight - mSize.y();
}
}
void ComponentList::render(const Eigen::Affine3f& parentTrans)

View file

@ -14,12 +14,30 @@ struct ComponentListElement
struct ComponentListRow
{
std::vector<ComponentListElement> elements;
// The input handler is called when the user enters any input while this row is highlighted (including up/down).
// Return false to let the list try to use it or true if the input has been consumed.
// If no input handler is supplied (input_handler == nullptr), the default behavior is to forward the input to
// the rightmost element in the currently selected row.
std::function<bool(InputConfig*, Input)> input_handler;
inline void addElement(const std::shared_ptr<GuiComponent>& component, bool resize_width)
{
elements.push_back(ComponentListElement(component, resize_width));
}
// Utility method for making an input handler for "when the users presses A on this, do func."
inline void makeAcceptInputHandler(const std::function<void()>& func)
{
input_handler = [func](InputConfig* config, Input input) -> bool {
if(config->isMappedTo("a", input) && input.value != 0)
{
func();
return true;
}
return false;
};
}
};
class ComponentList : public IList<ComponentListRow, void*>

View file

@ -10,95 +10,66 @@
GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "Main Menu")
{
struct MenuEntry
{
const char* name;
unsigned int color;
bool add_arrow;
std::function<void()> func;
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2);
MenuEntry(const char* n, unsigned int c, bool a, std::function<void()> f) : name(n), color(c), add_arrow(a), func(f) {};
};
std::vector<MenuEntry> entries;
entries.push_back(
MenuEntry("GENERAL SETTINGS", 0x777777FF, true,
[this] { mWindow->pushGui(new GuiSettingsMenu(mWindow)); }
)
);
entries.push_back(
MenuEntry("SCRAPE NOW", 0x777777FF, true,
[this] { mWindow->pushGui(new GuiScraperStart(mWindow)); }
)
// populate our list
addEntry("GENERAL SETTINGS", 0x777777FF, true,
[this] { mWindow->pushGui(new GuiSettingsMenu(mWindow)); }
);
addEntry("SCRAPE NOW", 0x777777FF, true,
[this] { mWindow->pushGui(new GuiScraperStart(mWindow)); }
);
entries.push_back(
MenuEntry("RESTART SYSTEM", 0x990000FF, false,
[this] {
mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Do you really want to restart the system?",
[] {
if(system("sudo shutdown -r now") != 0)
LOG(LogWarning) << "Restart terminated with non-zero result!";
}));
addEntry("RESTART SYSTEM", 0x990000FF, false,
[this] {
mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Do you really want to restart the system?",
[] {
if(system("sudo shutdown -r now") != 0)
LOG(LogWarning) << "Restart terminated with non-zero result!";
})
);}
);
entries.push_back(
MenuEntry("SHUTDOWN SYSTEM", 0x990000FF, false,
[this] {
mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Do you really want to shutdown the system?",
[] {
if(system("sudo shutdown -h now") != 0)
LOG(LogWarning) << "Shutdown terminated with non-zero result!";
}));
})
addEntry("SHUTDOWN SYSTEM", 0x990000FF, false,
[this] {
mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Do you really want to shutdown the system?",
[] {
if(system("sudo shutdown -h now") != 0)
LOG(LogWarning) << "Shutdown terminated with non-zero result!";
}));
}
);
if(!Settings::getInstance()->getBool("DONTSHOWEXIT"))
{
entries.push_back(
MenuEntry("EXIT EMULATIONSTATION", 0x990000FF, false,
[] {
SDL_Event ev;
ev.type = SDL_QUIT;
SDL_PushEvent(&ev);
})
addEntry("EXIT EMULATIONSTATION", 0x990000FF, false,
[] {
SDL_Event ev;
ev.type = SDL_QUIT;
SDL_PushEvent(&ev);
}
);
}
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2);
addChild(&mMenu);
}
void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func)
{
std::shared_ptr<Font> font = Font::get(FONT_SIZE_LARGE);
// populate the list
ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, name, font, color), true);
for(unsigned int i = 0; i < entries.size(); i++)
{
row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, entries[i].name, font, entries[i].color), true);
if(add_arrow)
row.addElement(std::make_shared<TextComponent>(mWindow, ">", font, color), false);
if(entries[i].add_arrow)
row.addElement(std::make_shared<TextComponent>(mWindow, ">", font, entries[i].color), false);
row.makeAcceptInputHandler(func);
std::function<void()>& execFunc = entries[i].func;
row.input_handler = [execFunc](InputConfig* config, Input input) -> bool
{
if(config->isMappedTo("a", input) && input.value != 0)
{
execFunc();
return true;
}
return false;
};
mMenu.addRow(row);
}
addChild(&mMenu);
mMenu.addRow(row);
}
bool GuiMenu::input(InputConfig* config, Input input)

View file

@ -12,5 +12,7 @@ public:
bool input(InputConfig* config, Input input) override;
private:
void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func);
MenuComponent mMenu;
};

View file

@ -2,120 +2,88 @@
#include "../Renderer.h"
#include "../Settings.h"
#include "../VolumeControl.h"
#include "../Log.h"
#include "../scrapers/TheArchiveScraper.h"
#include "../scrapers/GamesDBScraper.h"
GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(window),
mList(window, Eigen::Vector2i(2, 8)),
mBox(mWindow, ":/frame.png", 0x444444FF),
mDrawFramerateSwitch(window),
mVolumeSlider(window, 0, 100, 1, "%"),
mDisableSoundsSwitch(window, false),
mScraperOptList(window),
mScrapeRatingsSwitch(window),
mDimSlider(window, 0, 60, 1, "s"),
mDisableHelpSwitch(window),
mSaveButton(window)
mMenu(mWindow, "Settings")
{
loadStates();
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
addChild(&mBox);
addChild(&mList);
mList.setPosition(Renderer::getScreenWidth() / 4.0f, 0);
// center menu
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2);
using namespace Eigen;
//drawFramerate label
TextComponent* label = new TextComponent(mWindow);
label->setText("Draw Framerate: ");
label->setColor(0x0000FFFF);
mList.setEntry(Vector2i(0, 0), Vector2i(1, 1), label, false, ComponentGrid::AlignRight, Matrix<bool, 1, 2>(true, true));
mLabels.push_back(label);
Settings* s = Settings::getInstance();
//drawFramerate switch
mList.setEntry(Vector2i(1, 0), Vector2i(1, 1), &mDrawFramerateSwitch, true, ComponentGrid::AlignCenter, Matrix<bool, 1, 2>(true, true));
// framerate
auto framerate = std::make_shared<SwitchComponent>(mWindow);
framerate->setState(s->getBool("DRAWFRAMERATE"));
addSetting("Draw framerate:", framerate,
[framerate] { Settings::getInstance()->setBool("DRAWFRAMERATE", framerate->getState()); });
//volume label
label = new TextComponent(mWindow);
label->setText("System volume: ");
label->setColor(0x0000FFFF);
mLabels.push_back(label);
mList.setEntry(Vector2i(0, 1), Vector2i(1, 1), label, false, ComponentGrid::AlignRight, Matrix<bool, 1, 2>(true, true));
// volume
auto volume = std::make_shared<SliderComponent>(mWindow, 0.f, 100.f, 1.f, "%");
volume->setValue((float)VolumeControl::getInstance()->getVolume());
addSetting("System volume:", volume,
[volume] { VolumeControl::getInstance()->setVolume((int)volume->getValue()); });
//volume slider
mList.setEntry(Vector2i(1, 1), Vector2i(1, 1), &mVolumeSlider, true, ComponentGrid::AlignCenter, Matrix<bool, 1, 2>(true, true));
// disable sounds
auto sound_disable = std::make_shared<SwitchComponent>(mWindow);
sound_disable->setState(s->getBool("DISABLESOUNDS"));
addSetting("Disable sound:", sound_disable,
[sound_disable] { Settings::getInstance()->setBool("DISABLESOUNDS", sound_disable->getState()); });
//disable sounds
label = new TextComponent(mWindow);
label->setText("Disable sounds: ");
label->setColor(0x0000FFFF);
mLabels.push_back(label);
mList.setEntry(Vector2i(0, 2), Vector2i(1, 1), label, false, ComponentGrid::AlignRight, Matrix<bool, 1, 2>(true, true));
mList.setEntry(Vector2i(1, 2), Vector2i(1, 1), &mDisableSoundsSwitch, true, ComponentGrid::AlignCenter, Matrix<bool, 1, 2>(true, true));
//scraper label
label = new TextComponent(mWindow);
label->setText("Scraper: ");
label->setColor(0x0000FFFF);
mLabels.push_back(label);
mList.setEntry(Vector2i(0, 3), Vector2i(1, 1), label, false, ComponentGrid::AlignRight);
//fill scraper list
// scraper
auto scraper_list = std::make_shared< OptionListComponent< std::shared_ptr<Scraper> > >(mWindow, false);
std::vector< std::shared_ptr<Scraper> > scrapers;
scrapers.push_back(std::shared_ptr<Scraper>(new GamesDBScraper()));
scrapers.push_back(std::shared_ptr<Scraper>(new TheArchiveScraper()));
mScraperOptList.populate(scrapers, [&] (const std::shared_ptr<Scraper>& s) {
return mScraperOptList.makeEntry(s->getName(), s, s->getName() == Settings::getInstance()->getScraper()->getName());
} );
scrapers.push_back(std::make_shared<GamesDBScraper>());
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());
});
addSetting("Scraper:", scraper_list,
[scraper_list] {
if(scraper_list->getSelected().size() > 0)
Settings::getInstance()->setScraper(scraper_list->getSelected()[0]->object);
});
mList.setEntry(Vector2i(1, 3), Vector2i(1, 1), &mScraperOptList, true, ComponentGrid::AlignCenter);
// scrape ratings
auto scrape_ratings = std::make_shared<SwitchComponent>(mWindow);
scrape_ratings->setState(s->getBool("ScrapeRatings"));
addSetting("Scrape ratings?", scrape_ratings,
[scrape_ratings] { Settings::getInstance()->setBool("ScrapeRatings", scrape_ratings->getState()); });
//scrape ratings label
label = new TextComponent(mWindow);
label->setText("Scrape ratings? ");
label->setColor(0x0000FFFF);
mLabels.push_back(label);
mList.setEntry(Vector2i(0, 4), Vector2i(1, 1), label, false, ComponentGrid::AlignRight);
// dim time
auto dim_time = std::make_shared<SliderComponent>(mWindow, 0.f, 1200.f, 30.f, "s");
dim_time->setValue((float)(s->getInt("DIMTIME") / 1000));
addSetting("Dim screen after:", dim_time,
[dim_time] { Settings::getInstance()->setInt("DIMTIME", (int)(dim_time->getValue() * 1000)); });
mList.setEntry(Vector2i(1, 4), Vector2i(1, 1), &mScrapeRatingsSwitch, true, ComponentGrid::AlignCenter);
// disable help
auto disable_help = std::make_shared<SwitchComponent>(mWindow);
disable_help->setState(s->getBool("DISABLEHELP"));
addSetting("Disable help:", disable_help,
[disable_help] { Settings::getInstance()->setBool("DISABLEHELP", disable_help->getState()); });
// dim time label
label = new TextComponent(mWindow);
label->setText("Dim after: ");
label->setColor(0x0000FFFF);
mLabels.push_back(label);
mList.setEntry(Vector2i(0, 5), Vector2i(1, 1), label, false, ComponentGrid::AlignRight);
mList.setEntry(Vector2i(1, 5), Vector2i(1, 1), &mDimSlider, true, ComponentGrid::AlignCenter);
// save button
ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, "SAVE", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.makeAcceptInputHandler(std::bind(&GuiSettingsMenu::save, this));
mMenu.addRow(row);
// disable help switch
label = new TextComponent(mWindow);
label->setText("Disable help: ");
label->setColor(0x0000FFFF);
mLabels.push_back(label);
mList.setEntry(Vector2i(0, 6), Vector2i(1, 1), label, false, ComponentGrid::AlignRight);
mList.setEntry(Vector2i(1, 6), Vector2i(1, 1), &mDisableHelpSwitch, true, ComponentGrid::AlignCenter);
//save button
mSaveButton.setText("SAVE", "apply & save", 0x00FF00FF);
mSaveButton.setPressedFunc([this] () { applyStates(); delete this; });
mList.setEntry(Vector2i(0, 7), Vector2i(2, 1), &mSaveButton, true, ComponentGrid::AlignCenter, Matrix<bool, 1, 2>(false, true));
//center list
mList.setPosition(Renderer::getScreenWidth() / 2 - mList.getSize().x() / 2, Renderer::getScreenHeight() / 2 - mList.getSize().y() / 2);
//set up borders/background
mBox.fitTo(mList.getSize(), mList.getPosition(), Eigen::Vector2f(8, 8));
addChild(&mMenu);
}
GuiSettingsMenu::~GuiSettingsMenu()
void GuiSettingsMenu::addSetting(const char* label, const std::shared_ptr<GuiComponent>& comp, const std::function<void()>& saveFunc)
{
for(auto iter = mLabels.begin(); iter != mLabels.end(); iter++)
{
delete *iter;
}
ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.addElement(comp, false);
mApplyFuncs.push_back(saveFunc);
mMenu.addRow(row);
}
bool GuiSettingsMenu::input(InputConfig* config, Input input)
@ -125,7 +93,7 @@ bool GuiSettingsMenu::input(InputConfig* config, Input input)
return true;
//cancel if b is pressed
if(config->isMappedTo("b", input) && input.value)
if(config->isMappedTo("b", input) && input.value != 0)
{
delete this;
return true;
@ -134,46 +102,21 @@ bool GuiSettingsMenu::input(InputConfig* config, Input input)
return false;
}
void GuiSettingsMenu::loadStates()
void GuiSettingsMenu::save()
{
Settings* s = Settings::getInstance();
mDrawFramerateSwitch.setState(s->getBool("DRAWFRAMERATE"));
LOG(LogInfo) << "saving";
mVolumeSlider.setValue((float)VolumeControl::getInstance()->getVolume());
for(auto it = mApplyFuncs.begin(); it != mApplyFuncs.end(); it++)
(*it)();
mDisableSoundsSwitch.setState(s->getBool("DISABLESOUNDS"));
Settings::getInstance()->saveFile();
mScrapeRatingsSwitch.setState(s->getBool("ScrapeRatings"));
mDimSlider.setValue((float)(s->getInt("DIMTIME") / 1000));
mDisableHelpSwitch.setState(s->getBool("DISABLEHELP"));
}
void GuiSettingsMenu::applyStates()
{
Settings* s = Settings::getInstance();
s->setBool("DRAWFRAMERATE", mDrawFramerateSwitch.getState());
VolumeControl::getInstance()->setVolume((int)mVolumeSlider.getValue());
s->setBool("DISABLESOUNDS", mDisableSoundsSwitch.getState());
if(mScraperOptList.getSelected().size() > 0)
s->setScraper(mScraperOptList.getSelected()[0]->object);
s->setBool("ScrapeRatings", mScrapeRatingsSwitch.getState());
s->setInt("DIMTIME", (int)(mDimSlider.getValue() * 1000));
s->setBool("DISABLEHELP", mDisableHelpSwitch.getState());
s->saveFile();
delete this;
}
std::vector<HelpPrompt> GuiSettingsMenu::getHelpPrompts()
{
std::vector<HelpPrompt> prompts = mList.getHelpPrompts();
std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
prompts.push_back(HelpPrompt("b", "discard changes"));
return prompts;
}

View file

@ -1,7 +1,7 @@
#pragma once
#include "../GuiComponent.h"
#include "../components/ComponentGrid.h"
#include "../components/MenuComponent.h"
#include "../components/SwitchComponent.h"
#include "../components/SliderComponent.h"
#include "../components/TextComponent.h"
@ -14,28 +14,16 @@ class GuiSettingsMenu : public GuiComponent
{
public:
GuiSettingsMenu(Window* window);
~GuiSettingsMenu();
bool input(InputConfig* config, Input input) override;
std::vector<HelpPrompt> getHelpPrompts() override;
private:
void loadStates();
void applyStates();
void addSetting(const char* label, const std::shared_ptr<GuiComponent>& comp, const std::function<void()>& saveFunc);
void save();
ComponentGrid mList;
std::vector< std::function<void()> > mApplyFuncs;
NinePatchComponent mBox;
SwitchComponent mDrawFramerateSwitch;
SliderComponent mVolumeSlider;
SwitchComponent mDisableSoundsSwitch;
OptionListComponent< std::shared_ptr<Scraper> > mScraperOptList;
SwitchComponent mScrapeRatingsSwitch;
SliderComponent mDimSlider;
SwitchComponent mDisableHelpSwitch;
ButtonComponent mSaveButton;
std::vector<GuiComponent*> mLabels;
MenuComponent mMenu;
};