diff --git a/src/components/ComponentList.cpp b/src/components/ComponentList.cpp index 23f0414d4..c6f389583 100644 --- a/src/components/ComponentList.cpp +++ b/src/components/ComponentList.cpp @@ -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) diff --git a/src/components/ComponentList.h b/src/components/ComponentList.h index 1168ec8db..f5547eea7 100644 --- a/src/components/ComponentList.h +++ b/src/components/ComponentList.h @@ -14,12 +14,30 @@ struct ComponentListElement struct ComponentListRow { std::vector 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 input_handler; inline void addElement(const std::shared_ptr& 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& 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 diff --git a/src/guis/GuiMenu.cpp b/src/guis/GuiMenu.cpp index 10857508e..6b002c4ad 100644 --- a/src/guis/GuiMenu.cpp +++ b/src/guis/GuiMenu.cpp @@ -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 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 f) : name(n), color(c), add_arrow(a), func(f) {}; - }; - - std::vector 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)); } ); - - 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("SCRAPE NOW", 0x777777FF, true, + [this] { mWindow->pushGui(new GuiScraperStart(mWindow)); } + ); + + 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); + } ); } + + addChild(&mMenu); +} - setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2); - +void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func) +{ std::shared_ptr font = Font::get(FONT_SIZE_LARGE); // populate the list ComponentListRow row; + row.addElement(std::make_shared(mWindow, name, font, color), true); - for(unsigned int i = 0; i < entries.size(); i++) - { - row.elements.clear(); - row.addElement(std::make_shared(mWindow, entries[i].name, font, entries[i].color), true); + if(add_arrow) + row.addElement(std::make_shared(mWindow, ">", font, color), false); - if(entries[i].add_arrow) - row.addElement(std::make_shared(mWindow, ">", font, entries[i].color), false); + row.makeAcceptInputHandler(func); - std::function& 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) diff --git a/src/guis/GuiMenu.h b/src/guis/GuiMenu.h index 2c79af3d5..8dc13d636 100644 --- a/src/guis/GuiMenu.h +++ b/src/guis/GuiMenu.h @@ -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& func); + MenuComponent mMenu; }; diff --git a/src/guis/GuiSettingsMenu.cpp b/src/guis/GuiSettingsMenu.cpp index 7788838da..a4f9b07a6 100644 --- a/src/guis/GuiSettingsMenu.cpp +++ b/src/guis/GuiSettingsMenu.cpp @@ -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) +GuiSettingsMenu::GuiSettingsMenu(Window* window) : GuiComponent(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(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(true, true)); + // framerate + auto framerate = std::make_shared(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(true, true)); + // volume + auto volume = std::make_shared(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(true, true)); + // disable sounds + auto sound_disable = std::make_shared(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(true, true)); - - mList.setEntry(Vector2i(1, 2), Vector2i(1, 1), &mDisableSoundsSwitch, true, ComponentGrid::AlignCenter, Matrix(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 > >(mWindow, false); std::vector< std::shared_ptr > scrapers; - scrapers.push_back(std::shared_ptr(new GamesDBScraper())); - scrapers.push_back(std::shared_ptr(new TheArchiveScraper())); - mScraperOptList.populate(scrapers, [&] (const std::shared_ptr& s) { - return mScraperOptList.makeEntry(s->getName(), s, s->getName() == Settings::getInstance()->getScraper()->getName()); - } ); + scrapers.push_back(std::make_shared()); + scrapers.push_back(std::make_shared()); + scraper_list->populate(scrapers, [&] (const std::shared_ptr& 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(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); - - mList.setEntry(Vector2i(1, 4), Vector2i(1, 1), &mScrapeRatingsSwitch, true, ComponentGrid::AlignCenter); + // dim time + auto dim_time = std::make_shared(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)); }); - // 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); + // disable help + auto disable_help = std::make_shared(mWindow); + disable_help->setState(s->getBool("DISABLEHELP")); + addSetting("Disable help:", disable_help, + [disable_help] { Settings::getInstance()->setBool("DISABLEHELP", disable_help->getState()); }); - // 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 + ComponentListRow row; + row.addElement(std::make_shared(mWindow, "SAVE", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.makeAcceptInputHandler(std::bind(&GuiSettingsMenu::save, this)); + mMenu.addRow(row); - //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(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& comp, const std::function& saveFunc) { - for(auto iter = mLabels.begin(); iter != mLabels.end(); iter++) - { - delete *iter; - } + ComponentListRow row; + row.addElement(std::make_shared(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 GuiSettingsMenu::getHelpPrompts() { - std::vector prompts = mList.getHelpPrompts(); + std::vector prompts = mMenu.getHelpPrompts(); prompts.push_back(HelpPrompt("b", "discard changes")); return prompts; } diff --git a/src/guis/GuiSettingsMenu.h b/src/guis/GuiSettingsMenu.h index ee865be17..6f542dd75 100644 --- a/src/guis/GuiSettingsMenu.h +++ b/src/guis/GuiSettingsMenu.h @@ -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 getHelpPrompts() override; private: - void loadStates(); - void applyStates(); + void addSetting(const char* label, const std::shared_ptr& comp, const std::function& saveFunc); + void save(); - ComponentGrid mList; + std::vector< std::function > mApplyFuncs; - NinePatchComponent mBox; - - SwitchComponent mDrawFramerateSwitch; - SliderComponent mVolumeSlider; - SwitchComponent mDisableSoundsSwitch; - OptionListComponent< std::shared_ptr > mScraperOptList; - SwitchComponent mScrapeRatingsSwitch; - SliderComponent mDimSlider; - SwitchComponent mDisableHelpSwitch; - ButtonComponent mSaveButton; - - std::vector mLabels; + MenuComponent mMenu; };