diff --git a/src/components/ScraperSearchComponent.cpp b/src/components/ScraperSearchComponent.cpp index 82c16c989..ce93f7553 100644 --- a/src/components/ScraperSearchComponent.cpp +++ b/src/components/ScraperSearchComponent.cpp @@ -4,10 +4,13 @@ #include "TextComponent.h" #include "ScrollableContainer.h" #include "ImageComponent.h" +#include "RatingComponent.h" +#include "DateTimeComponent.h" #include "ComponentList.h" #include "../HttpReq.h" #include "../Settings.h" #include "../Log.h" +#include "../Util.h" ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) : GuiComponent(window), mGrid(window, Eigen::Vector2i(4, 3)), @@ -36,6 +39,36 @@ ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) mDescContainer->addChild(mResultDesc.get()); mDescContainer->setAutoScroll(2200, 0.015f); + // metadata + auto font = Font::get(FONT_SIZE_SMALL); // this gets replaced in onSizeChanged() so its just a placeholder + const unsigned int mdColor = 0x777777FF; + const unsigned int mdLblColor = 0x666666FF; + mMD_Rating = std::make_shared(mWindow); + mMD_ReleaseDate = std::make_shared(mWindow); + mMD_ReleaseDate->setColor(mdColor); + mMD_Developer = std::make_shared(mWindow, "", font, mdColor); + mMD_Publisher = std::make_shared(mWindow, "", font, mdColor); + mMD_Genre = std::make_shared(mWindow, "", font, mdColor); + mMD_Players = std::make_shared(mWindow, "", font, mdColor); + + mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "RATING:", font, mdLblColor), mMD_Rating)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "GENRE:", font, mdLblColor), mMD_Genre)); + mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "PLAYERS:", font, mdLblColor), mMD_Players)); + + mMD_Grid = std::make_shared(mWindow, Vector2i(2, mMD_Pairs.size())); + unsigned int i = 0; + for(auto it = mMD_Pairs.begin(); it != mMD_Pairs.end(); it++) + { + mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true); + mMD_Grid->setEntry(it->second, Vector2i(1, i), false, true); + i++; + } + + mGrid.setEntry(mMD_Grid, Vector2i(2, 1), false, true); + // result list mResultList = std::make_shared(mWindow); @@ -57,8 +90,36 @@ void ScraperSearchComponent::onSizeChanged() mGrid.setRowHeightPerc(0, fontHeightPerc); // result name mGrid.setRowHeightPerc(2, 0.375f); // description - mResultThumbnail->setMaxSize(mGrid.getColWidth(1), mGrid.getRowHeight(1)); + // limit thumbnail size using setMaxHeight - we do this instead of letting mGrid call setSize because it maintains the aspect ratio + // we also pad a little so it doesn't rub up against the metadata labels + mResultThumbnail->setMaxSize(mGrid.getColWidth(1) - 16, mGrid.getRowHeight(1)); mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container + + // metadata + // (mMD_Grid has already been resized by mGrid) + + const int fontHeight = (int)(mMD_Grid->getSize().y() / mMD_Pairs.size() * 0.8f); + auto fontLbl = Font::get(fontHeight, FONT_PATH_REGULAR); + auto fontComp = Font::get(fontHeight, FONT_PATH_LIGHT); + + // update label fonts + float maxLblWidth = 0; + for(auto it = mMD_Pairs.begin(); it != mMD_Pairs.end(); it++) + { + it->first->setFont(fontLbl); + it->first->setSize(0, 0); + if(it->first->getSize().x() > maxLblWidth) + maxLblWidth = it->first->getSize().x() + 6; + } + + // update component fonts + mMD_ReleaseDate->setFont(fontComp); + mMD_Developer->setFont(fontComp); + mMD_Publisher->setFont(fontComp); + mMD_Genre->setFont(fontComp); + mMD_Players->setFont(fontComp); + + mMD_Grid->setColWidthPerc(0, maxLblWidth / mMD_Grid->getSize().x()); } void ScraperSearchComponent::updateViewStyle() @@ -171,10 +232,27 @@ void ScraperSearchComponent::updateInfoPane() mThumbnailReq = std::unique_ptr(new HttpReq(thumb)); else mThumbnailReq.reset(); + + // metadata + mMD_Rating->setValue(strToUpper(mScraperResults.at(i).mdl.get("rating"))); + mMD_ReleaseDate->setValue(strToUpper(mScraperResults.at(i).mdl.get("releasedate"))); + mMD_Developer->setText(strToUpper(mScraperResults.at(i).mdl.get("developer"))); + mMD_Publisher->setText(strToUpper(mScraperResults.at(i).mdl.get("publisher"))); + mMD_Genre->setText(strToUpper(mScraperResults.at(i).mdl.get("genre"))); + mMD_Players->setText(strToUpper(mScraperResults.at(i).mdl.get("players"))); + }else{ - mResultName->setText(" "); - mResultDesc->setText(" "); + mResultName->setText(""); + mResultDesc->setText(""); mResultThumbnail->setImage(""); + + // metadata + mMD_Rating->setValue(""); + mMD_ReleaseDate->setValue(""); + mMD_Developer->setText(""); + mMD_Publisher->setText(""); + mMD_Genre->setText(""); + mMD_Players->setText(""); } } @@ -207,14 +285,14 @@ void ScraperSearchComponent::render(const Eigen::Affine3f& parentTrans) { Eigen::Affine3f trans = parentTrans * getTransform(); + renderChildren(trans); + if(mBlockAccept) { Renderer::setMatrix(trans); Renderer::drawRect((int)mResultList->getPosition().x(), (int)mResultList->getPosition().y(), (int)mResultList->getSize().x(), (int)mResultList->getSize().y(), 0x00000011); } - - renderChildren(trans); } void ScraperSearchComponent::returnResult(ScraperSearchResult result) @@ -233,6 +311,8 @@ void ScraperSearchComponent::returnResult(ScraperSearchResult result) void ScraperSearchComponent::update(int deltaTime) { + GuiComponent::update(deltaTime); + if(mThumbnailReq && mThumbnailReq->status() != HttpReq::REQ_IN_PROGRESS) { updateThumbnail(); @@ -254,15 +334,14 @@ void ScraperSearchComponent::update(int deltaTime) { if(mMDResolveHandle->status() == ASYNC_DONE) { + // this might end in us being deleted, depending on mAcceptCallback - so make sure this is the last thing we do in update() returnResult(mMDResolveHandle->getResult()); }else if(mMDResolveHandle->status() == ASYNC_ERROR) { onSearchError(mMDResolveHandle->getStatusString()); + mMDResolveHandle.reset(); } - mMDResolveHandle.reset(); } - - GuiComponent::update(deltaTime); } void ScraperSearchComponent::updateThumbnail() diff --git a/src/components/ScraperSearchComponent.h b/src/components/ScraperSearchComponent.h index e3adee360..fa8f1cbf7 100644 --- a/src/components/ScraperSearchComponent.h +++ b/src/components/ScraperSearchComponent.h @@ -8,11 +8,14 @@ #define MAX_SCRAPER_RESULTS 5 class ComponentList; -class TextEditComponent; class ImageComponent; +class RatingComponent; +class TextComponent; +class DateTimeComponent; class ScrollableContainer; class HttpReq; + class ScraperSearchComponent : public GuiComponent { public: @@ -61,6 +64,18 @@ private: std::shared_ptr mResultThumbnail; std::shared_ptr mResultList; + std::shared_ptr mMD_Grid; + std::shared_ptr mMD_Rating; + std::shared_ptr mMD_ReleaseDate; + std::shared_ptr mMD_Developer; + std::shared_ptr mMD_Publisher; + std::shared_ptr mMD_Genre; + std::shared_ptr mMD_Players; + + // label-component pair + typedef std::pair< std::shared_ptr, std::shared_ptr > MetaDataPair; + std::vector mMD_Pairs; + SearchType mSearchType; ScraperSearchParams mLastSearch; std::function mAcceptCallback; diff --git a/src/guis/GuiGameScraper.cpp b/src/guis/GuiGameScraper.cpp index 7ac7dcf6f..8e462f223 100644 --- a/src/guis/GuiGameScraper.cpp +++ b/src/guis/GuiGameScraper.cpp @@ -11,7 +11,8 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::function doneFunc) : GuiComponent(window), mGrid(window, Eigen::Vector2i(1, 3)), mBox(window, ":/frame.png"), - mSearchParams(params) + mSearchParams(params), + mClose(false) { addChild(&mBox); addChild(&mGrid); @@ -45,7 +46,29 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std:: mGrid.setPosition((mSize.x() - mGrid.getSize().x()) / 2, (mSize.y() - mGrid.getSize().y()) / 2); mBox.fitTo(mGrid.getSize(), mGrid.getPosition(), Eigen::Vector2f(-32, -32)); - mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { doneFunc(result); delete this; }); + // we call this->close() instead of just delete this; in the accept callback: + // this is because of how GuiComponent::update works. if it was just delete this, this would happen when the metadata resolver is done: + // GuiGameScraper::update() + // GuiComponent::update() + // it = mChildren.begin(); + // mBox::update() + // it++; + // mSearchComponent::update() + // acceptCallback -> delete this + // it++; // error, mChildren has been deleted because it was part of this + + // so instead we do this: + // GuiGameScraper::update() + // GuiComponent::update() + // it = mChildren.begin(); + // mBox::update() + // it++; + // mSearchComponent::update() + // acceptCallback -> close() -> mClose = true + // it++; // ok + // if(mClose) + // delete this; + mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { doneFunc(result); close(); }); mSearch->setCancelCallback([&] { delete this; }); mGrid.resetCursor(); @@ -63,7 +86,20 @@ bool GuiGameScraper::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } +void GuiGameScraper::update(int deltaTime) +{ + GuiComponent::update(deltaTime); + + if(mClose) + delete this; +} + std::vector GuiGameScraper::getHelpPrompts() { return mGrid.getHelpPrompts(); } + +void GuiGameScraper::close() +{ + mClose = true; +} \ No newline at end of file diff --git a/src/guis/GuiGameScraper.h b/src/guis/GuiGameScraper.h index f87d7f0f8..fa27ecc03 100644 --- a/src/guis/GuiGameScraper.h +++ b/src/guis/GuiGameScraper.h @@ -10,10 +10,13 @@ public: GuiGameScraper(Window* window, ScraperSearchParams params, std::function doneFunc); bool input(InputConfig* config, Input input) override; - + void update(int deltaTime); virtual std::vector getHelpPrompts() override; private: + bool mClose; + void close(); + ComponentGrid mGrid; NinePatchComponent mBox; diff --git a/src/scrapers/Scraper.cpp b/src/scrapers/Scraper.cpp index 3a450852f..85a6d7f15 100644 --- a/src/scrapers/Scraper.cpp +++ b/src/scrapers/Scraper.cpp @@ -62,7 +62,7 @@ void MDResolveHandle::update() setStatus(ASYNC_DONE); } -std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs) +std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs) { return std::unique_ptr(new ImageDownloadHandle(url, saveAs, Settings::getInstance()->getInt("ScraperResizeWidth"), Settings::getInstance()->getInt("ScraperResizeHeight"))); diff --git a/src/scrapers/Scraper.h b/src/scrapers/Scraper.h index 21c7198f1..88422e7bd 100644 --- a/src/scrapers/Scraper.h +++ b/src/scrapers/Scraper.h @@ -84,7 +84,7 @@ private: std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& url); //Will resize according to Settings::getInt("ScraperResizeWidth") and Settings::getInt("ScraperResizeHeight"). -std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs); +std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs); // Resolves all metadata assets that need to be downloaded. std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, const ScraperSearchParams& search);