diff --git a/CMakeLists.txt b/CMakeLists.txt index 61bdad719..b5d41510d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,8 +184,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiFastSelect.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBoxOk.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBoxYesNo.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGameScraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInputConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.h @@ -264,8 +263,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiFastSelect.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBoxOk.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBoxYesNo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGameScraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInputConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.cpp diff --git a/src/components/ComponentGrid.cpp b/src/components/ComponentGrid.cpp index f73257876..e73b45629 100644 --- a/src/components/ComponentGrid.cpp +++ b/src/components/ComponentGrid.cpp @@ -88,7 +88,11 @@ void ComponentGrid::setEntry(const std::shared_ptr& comp, const Ei addChild(comp.get()); if(!cursorValid() && canFocus) + { + auto origCursor = mCursor; mCursor = pos; + onCursorMoved(origCursor, mCursor); + } updateCellComponent(mCells.back()); updateSeparators(); @@ -327,7 +331,8 @@ void ComponentGrid::onFocusGained() bool ComponentGrid::cursorValid() { - return getCellAt(mCursor) != NULL; + GridEntry* e = getCellAt(mCursor); + return (e != NULL && e->canFocus); } void ComponentGrid::update(int deltaTime) diff --git a/src/components/MenuComponent.cpp b/src/components/MenuComponent.cpp index 3bc1eac68..2ea74dd65 100644 --- a/src/components/MenuComponent.cpp +++ b/src/components/MenuComponent.cpp @@ -61,17 +61,8 @@ void MenuComponent::updateGrid() if(mButtons.size()) { - mButtonGrid = std::make_shared(mWindow, Vector2i(mButtons.size(), 1)); + mButtonGrid = makeButtonGrid(mWindow, mButtons); - float buttonGridWidth = 16.0f * mButtons.size(); // initialize to padding - for(int i = 0; i < (int)mButtons.size(); i++) - { - mButtonGrid->setEntry(mButtons.at(i), Vector2i(i, 0), true, false); - buttonGridWidth += mButtons.at(i)->getSize().x(); - } - - mButtonGrid->setSize(buttonGridWidth, mButtons.at(0)->getSize().y()); - mGrid.setEntry(mButtonGrid, Vector2i(0, 2), true, false); }else{ mButtonGrid.reset(); @@ -82,3 +73,23 @@ std::vector MenuComponent::getHelpPrompts() { return mGrid.getHelpPrompts(); } + +std::shared_ptr makeButtonGrid(Window* window, const std::vector< std::shared_ptr >& buttons) +{ + std::shared_ptr buttonGrid = std::make_shared(window, Vector2i(buttons.size(), 1)); + + float buttonGridWidth = 16.0f * buttons.size(); // initialize to padding + for(int i = 0; i < (int)buttons.size(); i++) + { + buttonGrid->setEntry(buttons.at(i), Vector2i(i, 0), true, false); + buttonGridWidth += buttons.at(i)->getSize().x(); + } + for(unsigned int i = 0; i < buttons.size(); i++) + { + buttonGrid->setColWidthPerc(i, (buttons.at(i)->getSize().x() + 16) / buttonGridWidth); + } + + buttonGrid->setSize(buttonGridWidth, buttons.at(0)->getSize().y()); + + return buttonGrid; +} diff --git a/src/components/MenuComponent.h b/src/components/MenuComponent.h index a39bfef33..c1068908f 100644 --- a/src/components/MenuComponent.h +++ b/src/components/MenuComponent.h @@ -8,6 +8,8 @@ class ButtonComponent; +std::shared_ptr makeButtonGrid(Window* window, const std::vector< std::shared_ptr >& buttons); + class MenuComponent : public GuiComponent { public: diff --git a/src/guis/GuiMenu.cpp b/src/guis/GuiMenu.cpp index e0c731260..5a7f1981e 100644 --- a/src/guis/GuiMenu.cpp +++ b/src/guis/GuiMenu.cpp @@ -3,7 +3,7 @@ #include "../Sound.h" #include "../Log.h" #include "../Settings.h" -#include "GuiMsgBoxYesNo.h" +#include "GuiMsgBox.h" #include "GuiSettings.h" #include "GuiScraperStart.h" @@ -128,22 +128,22 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN ComponentListRow row; row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBoxYesNo(window, "REALLY RESTART?", + window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", [] { if(system("sudo shutdown -r now") != 0) LOG(LogWarning) << "Restart terminated with non-zero result!"; - })); + }, "NO", nullptr)); }); row.addElement(std::make_shared(window, "RESTART SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); s->addRow(row); row.elements.clear(); row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBoxYesNo(window, "REALLY SHUTDOWN?", + window->pushGui(new GuiMsgBox(window, "REALLY SHUTDOWN?", "YES", [] { if(system("sudo shutdown -h now") != 0) LOG(LogWarning) << "Shutdown terminated with non-zero result!"; - })); + }, "NO", nullptr)); }); row.addElement(std::make_shared(window, "SHUTDOWN SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); s->addRow(row); @@ -152,12 +152,12 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN { row.elements.clear(); row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBoxYesNo(window, "REALLY QUIT?", + window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES", [] { SDL_Event ev; ev.type = SDL_QUIT; SDL_PushEvent(&ev); - })); + }, "NO", nullptr)); }); row.addElement(std::make_shared(window, "QUIT EMULATIONSTATION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); s->addRow(row); diff --git a/src/guis/GuiMetaDataEd.cpp b/src/guis/GuiMetaDataEd.cpp index 451064414..b59915dbe 100644 --- a/src/guis/GuiMetaDataEd.cpp +++ b/src/guis/GuiMetaDataEd.cpp @@ -4,7 +4,7 @@ #include "../components/AsyncReqComponent.h" #include "../Settings.h" #include "GuiGameScraper.h" -#include "GuiMsgBoxYesNo.h" +#include "GuiMsgBox.h" #include GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector& mdd, ScraperSearchParams scraperParams, @@ -35,7 +35,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector if(mDeleteFunc) { auto deleteFileAndSelf = [&] { mDeleteFunc(); delete this; }; - auto deleteBtnFunc = [this, deleteFileAndSelf] { mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "This will delete a file!\nAre you sure?", deleteFileAndSelf)); }; + auto deleteBtnFunc = [this, deleteFileAndSelf] { mWindow->pushGui(new GuiMsgBox(mWindow, "This will delete a file!\nAre you sure?", "YES", deleteFileAndSelf, "NO", nullptr)); }; mMenu.addButton("DELETE", "delete this game on disk", deleteBtnFunc); } diff --git a/src/guis/GuiMsgBox.cpp b/src/guis/GuiMsgBox.cpp new file mode 100644 index 000000000..3db5a3ff8 --- /dev/null +++ b/src/guis/GuiMsgBox.cpp @@ -0,0 +1,69 @@ +#include "GuiMsgBox.h" +#include "../Renderer.h" +#include "../components/TextComponent.h" +#include "../components/ButtonComponent.h" +#include "../components/MenuComponent.h" // for makeButtonGrid + +#define BUTTON_VERT_PADDING 16.0f + +GuiMsgBox::GuiMsgBox(Window* window, const std::string& text, + const std::string& name1, const std::function& func1, + const std::string& name2, const std::function& func2, + const std::string& name3, const std::function& func3) : GuiComponent(window), + mBackground(window, ":/frame.png"), mGrid(window, Eigen::Vector2i(1, 2)) +{ + float width = Renderer::getScreenWidth() * 0.6f; // max width + float minWidth = Renderer::getScreenWidth() * 0.3f; // minimum width + + mMsg = std::make_shared(mWindow, text, Font::get(FONT_SIZE_MEDIUM), 0x777777FF, true); + + // create the buttons + mButtons.push_back(std::make_shared(mWindow, name1, name1, std::bind(&GuiMsgBox::deleteMeAndCall, this, func1))); + if(!name2.empty()) + mButtons.push_back(std::make_shared(mWindow, name2, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func2))); + if(!name3.empty()) + mButtons.push_back(std::make_shared(mWindow, name3, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func3))); + + // put the buttons into a ComponentGrid + mButtonGrid = makeButtonGrid(mWindow, mButtons); + + mGrid.setEntry(mMsg, Eigen::Vector2i(0, 0), false, true); + mGrid.setEntry(mButtonGrid, Eigen::Vector2i(0, 1), true, false); + + if(mMsg->getSize().x() > width) + { + mMsg->setSize(width, 0); + }else{ + // mMsg is narrower than width + // are buttons? + if(mButtonGrid->getSize().x() < width) + { + width = std::max(mButtonGrid->getSize().x(), mMsg->getSize().x()); + width = std::max(width, minWidth); + } + } + + setSize(width, mMsg->getSize().y() + mButtonGrid->getSize().y() + BUTTON_VERT_PADDING); + + // center for good measure + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, (Renderer::getScreenHeight() - mSize.y()) / 2.0f); + + addChild(&mBackground); + addChild(&mGrid); +} + +void GuiMsgBox::onSizeChanged() +{ + mGrid.setSize(mSize); + mBackground.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(-16, -32)); + + mGrid.setRowHeightPerc(1, (mButtonGrid->getSize().y() + BUTTON_VERT_PADDING) / mSize.y()); +} + +void GuiMsgBox::deleteMeAndCall(const std::function& func) +{ + if(func) + func(); + + delete this; +} diff --git a/src/guis/GuiMsgBox.h b/src/guis/GuiMsgBox.h new file mode 100644 index 000000000..148d3ca04 --- /dev/null +++ b/src/guis/GuiMsgBox.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../GuiComponent.h" +#include "../components/NinePatchComponent.h" +#include "../components/ComponentGrid.h" + +class TextComponent; +class ButtonComponent; + +class GuiMsgBox : public GuiComponent +{ +public: + GuiMsgBox(Window* window, const std::string& text, + const std::string& name1 = "OK", const std::function& func1 = nullptr, + const std::string& name2 = "", const std::function& func2 = nullptr, + const std::string& name3 = "", const std::function& func3 = nullptr); + + void onSizeChanged() override; + +private: + void deleteMeAndCall(const std::function& func); + + NinePatchComponent mBackground; + ComponentGrid mGrid; + + + std::shared_ptr mMsg; + std::vector< std::shared_ptr > mButtons; + std::shared_ptr mButtonGrid; + +}; diff --git a/src/guis/GuiMsgBoxOk.cpp b/src/guis/GuiMsgBoxOk.cpp deleted file mode 100644 index 5f99238f1..000000000 --- a/src/guis/GuiMsgBoxOk.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "GuiMsgBoxOk.h" -#include "../Renderer.h" - -#define MSG_WIDTH 0.8f -#define MSG_PADDING ((1 - MSG_WIDTH) / 2) - -GuiMsgBoxOk::GuiMsgBoxOk(Window* window, const std::string& text, std::function callback) : GuiComponent(window), - mCallback(callback), - mBackground(window), - mText(window), - mOkText(window) -{ - mText.setCentered(true); - mText.setColor(0x00BB00FF); - mText.setSize(Renderer::getScreenWidth() * MSG_WIDTH, 0); - mText.setText(text); - - mOkText.setCentered(true); - mOkText.setColor(0x0044BBFF); - mOkText.setFont(Font::get(FONT_SIZE_SMALL)); - mOkText.setSize(Renderer::getScreenWidth() * MSG_WIDTH, 0); - mOkText.setText("[A]"); - - mText.setPosition(Renderer::getScreenWidth() * MSG_PADDING, (Renderer::getScreenHeight() - mText.getSize().y() - mOkText.getSize().y()) / 2); - mOkText.setPosition(Renderer::getScreenWidth() * MSG_PADDING, mText.getPosition().y() + mText.getSize().y()); -} - -bool GuiMsgBoxOk::input(InputConfig* config, Input input) -{ - if(input.value != 0 && - (config->isMappedTo("a", input) || config->isMappedTo("b", input))) - { - if(mCallback) - mCallback(); - - delete this; - return true; - } - - return false; -} - -void GuiMsgBoxOk::render(const Eigen::Affine3f& parentTrans) -{ - float height = mText.getSize().y() + mOkText.getSize().y(); - Renderer::setMatrix(parentTrans); - Renderer::drawRect(0, (int)((Renderer::getScreenHeight() - height) / 2), Renderer::getScreenWidth(), (int)height, 0x111111FF); - mText.render(parentTrans); - mOkText.render(parentTrans); -} diff --git a/src/guis/GuiMsgBoxOk.h b/src/guis/GuiMsgBoxOk.h deleted file mode 100644 index 6df26b7e0..000000000 --- a/src/guis/GuiMsgBoxOk.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "../GuiComponent.h" -#include "../components/TextComponent.h" -#include "../components/NinePatchComponent.h" -#include - -//A simple popup message box with callbacks for when the user dismisses it. -//Make sure you remember to push it onto the window! -class GuiMsgBoxOk : public GuiComponent -{ -public: - GuiMsgBoxOk(Window* window, const std::string& msg, std::function okCallback = nullptr); - - bool input(InputConfig* config, Input input) override; - void render(const Eigen::Affine3f& parentTrans) override; - -private: - std::function mCallback; - - NinePatchComponent mBackground; - - TextComponent mText; - TextComponent mOkText; -}; diff --git a/src/guis/GuiMsgBoxYesNo.cpp b/src/guis/GuiMsgBoxYesNo.cpp deleted file mode 100644 index 60bbba0a0..000000000 --- a/src/guis/GuiMsgBoxYesNo.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "GuiMsgBoxYesNo.h" -#include "../Renderer.h" - -GuiMsgBoxYesNo::GuiMsgBoxYesNo(Window* window, const std::string& text, std::function yesCallback, std::function noCallback) : GuiComponent(window), - mYesCallback(yesCallback), - mNoCallback(noCallback), - mBackground(window), - mText(window), - mInputText(window) -{ - const float paddingX = 32; - const float paddingY = 16; - - const float maxWidth = Renderer::getScreenWidth() * 0.7f; - - float width = mText.getFont()->sizeText(text).x() + paddingX; - if(width > maxWidth) - width = maxWidth; - - mText.setCentered(true); - mText.setColor(0x777777FF); - mText.setPosition(paddingX / 2, paddingY / 2); - mText.setSize(width - paddingX, 0); - mText.setText(text); - - mInputText.setCentered(true); - mInputText.setColor(0x0044BBFF); - mInputText.setFont(Font::get(FONT_SIZE_SMALL)); - mInputText.setPosition(paddingX / 2, mText.getPosition().y() + mText.getSize().y()); - mInputText.setSize(width - paddingX, 0); - mInputText.setText("[A - yes] [B - no]"); - - setSize(width, mInputText.getPosition().y() + mInputText.getSize().y() + paddingY/2); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); - - mBackground.setImagePath(":/frame.png"); - mBackground.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(-32, -32)); -} - -bool GuiMsgBoxYesNo::input(InputConfig* config, Input input) -{ - if(input.value != 0) - { - if(config->isMappedTo("a", input)) - { - if(mYesCallback) - mYesCallback(); - - delete this; - return true; - }else if(config->isMappedTo("b", input)) - { - if(mNoCallback) - mNoCallback(); - - delete this; - return true; - } - } - - return false; -} - -void GuiMsgBoxYesNo::render(const Eigen::Affine3f& parentTrans) -{ - Eigen::Affine3f trans = parentTrans * getTransform(); - - mBackground.render(trans); - - Renderer::setMatrix(trans); - Renderer::drawRect(0, (int)(mText.getPosition().y() + mText.getSize().y()), (int)mSize.x(), 1, 0xC6C7C6FF); - - mText.render(trans); - mInputText.render(trans); - - renderChildren(trans); -} diff --git a/src/guis/GuiMsgBoxYesNo.h b/src/guis/GuiMsgBoxYesNo.h deleted file mode 100644 index e2e118895..000000000 --- a/src/guis/GuiMsgBoxYesNo.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "../GuiComponent.h" -#include "../components/TextComponent.h" -#include "../components/NinePatchComponent.h" -#include - -//A simple "yes or no" popup box with callbacks for yes or no. -//Make sure you remember to push it onto the window! -class GuiMsgBoxYesNo : public GuiComponent -{ -public: - GuiMsgBoxYesNo(Window* window, const std::string& msg, std::function yesCallback = nullptr, std::function noCallback = nullptr); - - bool input(InputConfig* config, Input input) override; - void render(const Eigen::Affine3f& trans) override; - -private: - std::function mYesCallback, mNoCallback; - - NinePatchComponent mBackground; - - TextComponent mText; - TextComponent mInputText; -}; diff --git a/src/guis/GuiScraperStart.cpp b/src/guis/GuiScraperStart.cpp index 8275d7b93..e20cf4cb5 100644 --- a/src/guis/GuiScraperStart.cpp +++ b/src/guis/GuiScraperStart.cpp @@ -1,6 +1,6 @@ #include "GuiScraperStart.h" #include "GuiScraperLog.h" -#include "GuiMsgBoxYesNo.h" +#include "GuiMsgBox.h" #include "../components/TextComponent.h" #include "../components/OptionListComponent.h" @@ -41,8 +41,10 @@ void GuiScraperStart::pressedStart() { if((*it)->getPlatformId() == PlatformIds::PLATFORM_UNKNOWN) { - mWindow->pushGui(new GuiMsgBoxYesNo(mWindow, "Warning: some of your selected systems do not have a platform ID set. Results may be even more inaccurate than usual!\nContinue anyway?", - std::bind(&GuiScraperStart::start, this))); + mWindow->pushGui(new GuiMsgBox(mWindow, + "Warning: some of your selected systems do not have a platform ID set. Results may be even more inaccurate than usual!\nContinue anyway?", + "YES", std::bind(&GuiScraperStart::start, this), + "NO", nullptr)); return; } }