// SPDX-License-Identifier: MIT // // ES-DE Frontend // GuiScraperSingle.cpp // // Single game scraping user interface. // This interface is triggered from GuiMetaDataEd. // GuiScraperSearch is called from here. // #include "guis/GuiScraperSingle.h" #include "FileData.h" #include "MameNames.h" #include "SystemData.h" #include "components/ButtonComponent.h" #include "components/MenuComponent.h" #include "components/TextComponent.h" #include "utils/LocalizationUtil.h" GuiScraperSingle::GuiScraperSingle(ScraperSearchParams& params, std::function doneFunc, bool& savedMediaAndAborted) : mClose {false} , mRenderer {Renderer::getInstance()} , mBackground {":/graphics/frame.svg"} , mGrid {glm::ivec2 {2, 6}} , mSearchParams {params} , mSavedMediaAndAborted {savedMediaAndAborted} { addChild(&mBackground); addChild(&mGrid); std::string scrapeName; if (Settings::getInstance()->getBool("ScraperSearchMetadataName")) { scrapeName = mSearchParams.game->getName(); } else { if (params.game->isArcadeGame() && Settings::getInstance()->getString("Scraper") == "thegamesdb") scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath()) + " (" + MameNames::getInstance().getCleanName(mSearchParams.game->getCleanName()) + ")"; else scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath()); } mGameName = std::make_shared( scrapeName + ((mSearchParams.game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR : ""), Font::get(FONT_SIZE_LARGE * Utils::Localization::sMenuTitleScaleFactor), mMenuColorPrimary, ALIGN_CENTER); mGameName->setColor(mMenuColorTitle); mGrid.setEntry(mGameName, glm::ivec2 {0, 0}, false, true, glm::ivec2 {2, 2}); mSystemName = std::make_shared( Utils::String::toUpper(mSearchParams.system->getFullName()), Font::get(FONT_SIZE_SMALL), mMenuColorSecondary, ALIGN_CENTER); mGrid.setEntry(mSystemName, glm::ivec2 {0, 2}, false, true, glm::ivec2 {2, 1}); // Row 3 is a spacer. // GuiScraperSearch. mSearch = std::make_shared(GuiScraperSearch::MANUAL_MODE, 1, 8); mGrid.setEntry(mSearch, glm::ivec2 {0, 4}, true, true, glm::ivec2 {2, 1}); mResultList = mSearch->getResultList(); // Set up scroll indicators. mScrollUp = std::make_shared(); mScrollDown = std::make_shared(); mScrollUp->setResize(0.0f, mGameName->getFont()->getLetterHeight() / 2.0f); mScrollUp->setOrigin(0.0f, -0.35f); mScrollDown->setResize(0.0f, mGameName->getFont()->getLetterHeight() / 2.0f); mScrollDown->setOrigin(0.0f, 0.35f); mScrollIndicator = std::make_shared(mResultList, mScrollUp, mScrollDown); mGrid.setEntry(mScrollUp, glm::ivec2 {1, 0}, false, false, glm::ivec2 {1, 1}); mGrid.setEntry(mScrollDown, glm::ivec2 {1, 1}, false, false, glm::ivec2 {1, 1}); // Buttons std::vector> buttons; buttons.push_back( std::make_shared(_("REFINE SEARCH"), _("refine search"), [&] { // Refine the search, unless the result has already been accepted. if (!mSearch->getAcceptedResult()) { // Copy any search refine that may have been previously entered by opening // the input screen using the "Y" button shortcut. mSearchParams.nameOverride = mSearch->getNameOverride(); mSearch->openInputScreen(mSearchParams); mGrid.resetCursor(); } })); buttons.push_back(std::make_shared(_("CANCEL"), _("cancel"), [&] { if (mSearch->getSavedNewMedia()) { // If the user aborted the scraping but there was still some media downloaded, // then flag to GuiMetaDataEd that the image and marquee textures need to be // manually unloaded and that the gamelist needs to be reloaded. Otherwise the // images would not get updated until the user scrolls up and down the gamelist. mSavedMediaAndAborted = true; } delete this; })); mButtonGrid = MenuComponent::makeButtonGrid(buttons); mGrid.setEntry(mButtonGrid, glm::ivec2 {0, 5}, true, false, glm::ivec2 {2, 1}); mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { doneFunc(result); close(); }); mSearch->setCancelCallback([&] { delete this; }); mSearch->setRefineCallback([&] { mScrollUp->setOpacity(0.0f); mScrollDown->setOpacity(0.0f); mResultList->resetScrollIndicatorStatus(); }); // Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is // the 16:9 reference. const float aspectValue {1.778f / Renderer::getScreenAspectRatio()}; const float width {glm::clamp(0.95f * aspectValue, 0.70f, 0.95f) * mRenderer->getScreenWidth()}; const float screenSize {mRenderer->getIsVerticalOrientation() ? mRenderer->getScreenWidth() : mRenderer->getScreenHeight()}; const float height {(mGameName->getFont()->getLetterHeight() + screenSize * 0.0637f) + mSystemName->getFont()->getLetterHeight() + screenSize * 0.04f + mButtonGrid->getSize().y + Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f}; setSize(width, height); setPosition((mRenderer->getScreenWidth() - mSize.x) / 2.0f, (mRenderer->getScreenHeight() - mSize.y) / 2.0f); mGrid.resetCursor(); mSearch->search(params); // Start the search. } void GuiScraperSingle::onSizeChanged() { const float gameNameHeight {mRenderer->getIsVerticalOrientation() ? mRenderer->getScreenWidth() * 0.0637f : mRenderer->getScreenHeight() * 0.0637f}; mGrid.setRowHeightPerc(0, (mGameName->getFont()->getLetterHeight() + gameNameHeight) / mSize.y / 2.0f); mGrid.setRowHeightPerc(1, (mGameName->getFont()->getLetterHeight() + gameNameHeight) / mSize.y / 2.0f); mGrid.setRowHeightPerc(2, mSystemName->getFont()->getLetterHeight() / mSize.y, false); mGrid.setRowHeightPerc(3, 0.04f, false); mGrid.setRowHeightPerc(4, (Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f) / mSize.y, false); if (mRenderer->getIsVerticalOrientation()) mGrid.setColWidthPerc(1, 0.05f); else mGrid.setColWidthPerc(1, 0.04f); mGrid.setSize(mSize); mBackground.fitTo(mSize); // Add some extra margins to the game name. const float newSizeX {mSize.x * 0.96f}; mGameName->setSize(newSizeX, mGameName->getSize().y); mGameName->setPosition((mSize.x - newSizeX) / 2.0f, 0.0f); } bool GuiScraperSingle::input(InputConfig* config, Input input) { if (config->isMappedTo("b", input) && input.value) { if (mSearch->getSavedNewMedia()) { // If the user aborted the scraping but there was still some media downloaded, // then flag to GuiMetaDataEd that the image and marquee textures need to be // manually unloaded and that the gamelist needs to be reloaded. Otherwise the // images would not get updated until the user scrolls up and down the gamelist. mSavedMediaAndAborted = true; } delete this; return true; } return GuiComponent::input(config, input); } void GuiScraperSingle::update(int deltaTime) { GuiComponent::update(deltaTime); if (mClose) delete this; } std::vector GuiScraperSingle::getHelpPrompts() { std::vector prompts {mGrid.getHelpPrompts()}; prompts.push_back(HelpPrompt("b", _("back (cancel)"))); return prompts; } void GuiScraperSingle::close() { // This will cause update() to close the GUI. mClose = true; }