mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-17 22:55:38 +00:00
Search for box hooked up.
Display thumbnails for results. Still need to resolve boxart.
This commit is contained in:
parent
9ce511cc71
commit
a3a4636fd5
|
@ -7,6 +7,38 @@ boost::asio::io_service HttpReq::io_service;
|
|||
|
||||
HttpReq::HttpReq(const std::string& server, const std::string& path)
|
||||
: mResolver(io_service), mSocket(io_service), mStatus(REQ_IN_PROGRESS)
|
||||
{
|
||||
start(server, path);
|
||||
}
|
||||
|
||||
HttpReq::HttpReq(const std::string& url)
|
||||
: mResolver(io_service), mSocket(io_service), mStatus(REQ_IN_PROGRESS)
|
||||
{
|
||||
size_t startpos = 0;
|
||||
|
||||
if(url.substr(startpos, 7) == "http://")
|
||||
startpos = 7;
|
||||
else if(url.substr(0, 8) == "https://")
|
||||
startpos = 8;
|
||||
|
||||
if(url.substr(startpos, 4) == "www.")
|
||||
startpos += 4;
|
||||
|
||||
size_t pathStart = url.find('/', startpos);
|
||||
std::string server = url.substr(startpos, pathStart - startpos);
|
||||
std::string path = url.substr(pathStart, std::string::npos);
|
||||
|
||||
start(server, path);
|
||||
}
|
||||
|
||||
HttpReq::~HttpReq()
|
||||
{
|
||||
mResolver.cancel();
|
||||
mSocket.close();
|
||||
while(status() == REQ_IN_PROGRESS); //otherwise you get really weird heap-allocation-related crashes
|
||||
}
|
||||
|
||||
void HttpReq::start(const std::string& server, const std::string& path)
|
||||
{
|
||||
std::ostream req_str(&mRequest);
|
||||
req_str << "GET " << path << " HTTP/1.0\r\n";
|
||||
|
@ -21,7 +53,6 @@ HttpReq::HttpReq(const std::string& server, const std::string& path)
|
|||
boost::asio::placeholders::iterator));
|
||||
}
|
||||
|
||||
|
||||
void HttpReq::handleResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator)
|
||||
{
|
||||
if (!err)
|
||||
|
|
|
@ -27,6 +27,9 @@ class HttpReq
|
|||
{
|
||||
public:
|
||||
HttpReq(const std::string& server, const std::string& path);
|
||||
HttpReq(const std::string& url);
|
||||
|
||||
~HttpReq();
|
||||
|
||||
enum Status
|
||||
{
|
||||
|
@ -47,6 +50,7 @@ public:
|
|||
private:
|
||||
static boost::asio::io_service io_service;
|
||||
|
||||
void start(const std::string& server, const std::string& path);
|
||||
void handleResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator);
|
||||
void handleConnect(const boost::system::error_code& err);
|
||||
void handleWriteRequest(const boost::system::error_code& err);
|
||||
|
|
|
@ -19,6 +19,7 @@ std::vector<MetaDataDecl> MetaDataList::getDefaultGameMDD()
|
|||
{"name", MD_STRING, ""},
|
||||
{"desc", MD_MULTILINE_STRING, ""},
|
||||
{"image", MD_IMAGE_PATH, ""},
|
||||
{"thumbnail", MD_IMAGE_PATH, ""},
|
||||
{"rating", MD_RATING, "0"},
|
||||
{"userrating", MD_RATING, "0"},
|
||||
{"playcount", MD_INT, "0"},
|
||||
|
|
|
@ -104,6 +104,9 @@ void ComponentListComponent::removeEntriesIn(Eigen::Vector2i pos, Eigen::Vector2
|
|||
if((*iter)->pos.x() >= pos.x() && (*iter)->pos.x() < pos.x() + size.x()
|
||||
&& (*iter)->pos.y() >= pos.y() && (*iter)->pos.y() < pos.y() + size.y())
|
||||
{
|
||||
if((*iter)->component->getParent() == this)
|
||||
(*iter)->component->setParent(NULL);
|
||||
|
||||
delete *iter;
|
||||
iter = mEntries.erase(iter);
|
||||
}else{
|
||||
|
@ -305,14 +308,22 @@ bool ComponentListComponent::input(InputConfig* config, Input input)
|
|||
|
||||
void ComponentListComponent::resetCursor()
|
||||
{
|
||||
if(mEntries.size() == 0)
|
||||
auto iter = mEntries.begin();
|
||||
while(iter != mEntries.end())
|
||||
{
|
||||
if((*iter)->canFocus)
|
||||
break;
|
||||
iter++;
|
||||
}
|
||||
|
||||
if(iter == mEntries.end())
|
||||
{
|
||||
mCursor = Eigen::Vector2i(-1, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
const Eigen::Vector2i origCursor = mCursor;
|
||||
mCursor << mEntries.at(0)->pos[0], mEntries.at(0)->pos[1];
|
||||
mCursor << (*iter)->pos[0], (*iter)->pos[1];
|
||||
onCursorMoved(origCursor, mCursor);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::
|
|||
mResultName(window, "", Font::get(*window->getResourceManager(), Font::getDefaultPath(), FONT_SIZE_MEDIUM)),
|
||||
mResultInfo(window),
|
||||
mResultDesc(window, "", Font::get(*window->getResourceManager(), Font::getDefaultPath(), FONT_SIZE_SMALL)),
|
||||
mResultThumbnail(window),
|
||||
|
||||
mSearchLabel(window, "Search for: ", Font::get(*window->getResourceManager(), Font::getDefaultPath(), FONT_SIZE_SMALL)),
|
||||
mSearchText(window),
|
||||
|
@ -57,13 +58,16 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::
|
|||
mResultName.setColor(0x3B56CCFF);
|
||||
mList.setEntry(Vector2i(0, 1), Vector2i(1, 1), &mResultName, false, ComponentListComponent::AlignLeft);
|
||||
|
||||
|
||||
mResultDesc.setText(params.game->metadata()->get("desc"));
|
||||
mResultDesc.setSize(colWidth, 0);
|
||||
mResultInfo.addChild(&mResultDesc);
|
||||
mResultInfo.setSize(mResultDesc.getSize().x(), mResultDesc.getFont()->getHeight() * 3.0f);
|
||||
mList.setEntry(Vector2i(0, 2), Vector2i(1, 1), &mResultInfo, false, ComponentListComponent::AlignLeft);
|
||||
|
||||
mResultThumbnail.setOrigin(0.5f, 0.5f);
|
||||
mResultThumbnail.setResize(colWidth, mResultInfo.getSize().y(), false);
|
||||
mList.setEntry(Vector2i(1, 2), Vector2i(1, 1), &mResultThumbnail, false, ComponentListComponent::AlignCenter);
|
||||
|
||||
//y = 3 is a spacer row
|
||||
|
||||
mList.setEntry(Vector2i(0, 4), Vector2i(1, 1), &mSearchLabel, false, ComponentListComponent::AlignLeft);
|
||||
|
@ -87,6 +91,8 @@ GuiGameScraper::GuiGameScraper(Window* window, ScraperSearchParams params, std::
|
|||
mBox.fitTo(mList.getSize(), mList.getPosition());
|
||||
|
||||
mResultInfo.setAutoScroll(2200, 0.015f);
|
||||
|
||||
mList.resetCursor();
|
||||
}
|
||||
|
||||
void GuiGameScraper::search()
|
||||
|
@ -135,7 +141,7 @@ bool GuiGameScraper::input(InputConfig* config, Input input)
|
|||
if(config->isMappedTo("a", input) && input.value != 0)
|
||||
{
|
||||
//if you're on a result
|
||||
if(getSelectedIndex())
|
||||
if(getSelectedIndex() != -1)
|
||||
{
|
||||
mDoneFunc(mScraperResults.at(getSelectedIndex()));
|
||||
delete this;
|
||||
|
@ -149,9 +155,10 @@ bool GuiGameScraper::input(InputConfig* config, Input input)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool wasEditing = mSearchText.isEditing();
|
||||
bool ret = GuiComponent::input(config, input);
|
||||
|
||||
if(config->isMappedTo("up", input) || config->isMappedTo("down", input))
|
||||
if(config->isMappedTo("up", input) || config->isMappedTo("down", input) && input.value != 0)
|
||||
{
|
||||
//update game info pane
|
||||
int i = getSelectedIndex();
|
||||
|
@ -161,8 +168,46 @@ bool GuiGameScraper::input(InputConfig* config, Input input)
|
|||
mResultDesc.setText(mScraperResults.at(i).get("desc"));
|
||||
mResultInfo.setScrollPos(Eigen::Vector2d(0, 0));
|
||||
mResultInfo.resetAutoScrollTimer();
|
||||
|
||||
std::string thumb = mScraperResults.at(i).get("thumbnail");
|
||||
mResultThumbnail.setImage("");
|
||||
if(!thumb.empty())
|
||||
mThumbnailReq = std::unique_ptr<HttpReq>(new HttpReq(thumb));
|
||||
else
|
||||
mThumbnailReq.reset();
|
||||
}
|
||||
}
|
||||
|
||||
//stopped editing
|
||||
if(wasEditing && !mSearchText.isEditing())
|
||||
{
|
||||
//update results
|
||||
search();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GuiGameScraper::update(int deltaTime)
|
||||
{
|
||||
if(mThumbnailReq && mThumbnailReq->status() != HttpReq::REQ_IN_PROGRESS)
|
||||
{
|
||||
updateThumbnail();
|
||||
}
|
||||
|
||||
GuiComponent::update(deltaTime);
|
||||
}
|
||||
|
||||
void GuiGameScraper::updateThumbnail()
|
||||
{
|
||||
if(mThumbnailReq && mThumbnailReq->status() == HttpReq::REQ_SUCCESS)
|
||||
{
|
||||
std::string content = mThumbnailReq->getContent();
|
||||
mResultThumbnail.setImage(content.data(), content.length());
|
||||
}else{
|
||||
LOG(LogWarning) << "thumbnail req failed: " << mThumbnailReq->getErrorMsg();
|
||||
mResultThumbnail.setImage("");
|
||||
}
|
||||
|
||||
mThumbnailReq.reset();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "TextEditComponent.h"
|
||||
#include "NinePatchComponent.h"
|
||||
#include "../Settings.h"
|
||||
#include "../HttpReq.h"
|
||||
#include "ImageComponent.h"
|
||||
|
||||
class GuiGameScraper : public GuiComponent
|
||||
{
|
||||
|
@ -15,11 +17,13 @@ public:
|
|||
GuiGameScraper(Window* window, ScraperSearchParams params, std::function<void(MetaDataList)> doneFunc, std::function<void()> skipFunc = NULL);
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void update(int deltaTime) override;
|
||||
|
||||
void search();
|
||||
private:
|
||||
int getSelectedIndex();
|
||||
void onSearchDone(std::vector<MetaDataList> results);
|
||||
void updateThumbnail();
|
||||
|
||||
ComponentListComponent mList;
|
||||
NinePatchComponent mBox;
|
||||
|
@ -29,6 +33,7 @@ private:
|
|||
TextComponent mResultName;
|
||||
ScrollableContainer mResultInfo;
|
||||
TextComponent mResultDesc;
|
||||
ImageComponent mResultThumbnail;
|
||||
|
||||
TextComponent mSearchLabel;
|
||||
TextEditComponent mSearchText;
|
||||
|
@ -41,4 +46,6 @@ private:
|
|||
|
||||
std::function<void(MetaDataList)> mDoneFunc;
|
||||
std::function<void()> mSkipFunc;
|
||||
|
||||
std::unique_ptr<HttpReq> mThumbnailReq;
|
||||
};
|
||||
|
|
|
@ -81,6 +81,16 @@ void ImageComponent::setImage(std::string path)
|
|||
resize();
|
||||
}
|
||||
|
||||
void ImageComponent::setImage(const char* path, size_t length)
|
||||
{
|
||||
mTexture.reset();
|
||||
|
||||
mTexture = TextureResource::get(*mWindow->getResourceManager(), "");
|
||||
mTexture->initFromMemory(path, length);
|
||||
|
||||
resize();
|
||||
}
|
||||
|
||||
void ImageComponent::setOrigin(float originX, float originY)
|
||||
{
|
||||
mOrigin << originX, originY;
|
||||
|
|
|
@ -20,6 +20,7 @@ public:
|
|||
|
||||
void copyScreen(); //Copy the entire screen into a texture for us to use.
|
||||
void setImage(std::string path); //Loads the image at the given filepath.
|
||||
void setImage(const char* image, size_t length); //Loads image from memory.
|
||||
void setOrigin(float originX, float originY); //Sets the origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center)
|
||||
void setTiling(bool tile); //Enables or disables tiling. Must be called before loading an image or resizing will be weird.
|
||||
void setResize(float width, float height, bool allowUpscale);
|
||||
|
|
|
@ -137,6 +137,9 @@ void TextEditComponent::onTextChanged()
|
|||
|
||||
std::string wrappedText = (isMultiline() ? f->wrapText(mText, mSize.x()) : mText);
|
||||
mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(wrappedText, 0, 0, 0x00000000 | getOpacity()));
|
||||
|
||||
if(mCursor > (int)mText.length())
|
||||
mCursor = mText.length();
|
||||
}
|
||||
|
||||
void TextEditComponent::onCursorChanged()
|
||||
|
@ -215,3 +218,7 @@ bool TextEditComponent::isMultiline()
|
|||
return (getSize().y() > (float)getFont()->getHeight());
|
||||
}
|
||||
|
||||
bool TextEditComponent::isEditing() const
|
||||
{
|
||||
return mEditing;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ public:
|
|||
void setValue(const std::string& val) override;
|
||||
std::string getValue() const override;
|
||||
|
||||
bool isEditing() const;
|
||||
|
||||
private:
|
||||
void onTextChanged();
|
||||
void onCursorChanged();
|
||||
|
|
|
@ -79,6 +79,34 @@ void TextureResource::initFromScreen()
|
|||
mTextureSize[1] = height;
|
||||
}
|
||||
|
||||
void TextureResource::initFromMemory(const char* data, size_t length)
|
||||
{
|
||||
deinit();
|
||||
|
||||
size_t width, height;
|
||||
std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32((const unsigned char*)(data), length, width, height);
|
||||
|
||||
if(imageRGBA.size() == 0)
|
||||
{
|
||||
LOG(LogError) << "Could not initialize texture from memory (invalid data)!";
|
||||
return;
|
||||
}
|
||||
|
||||
//now for the openGL texture stuff
|
||||
glGenTextures(1, &mTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageRGBA.data());
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
mTextureSize << width, height;
|
||||
}
|
||||
|
||||
void TextureResource::deinit()
|
||||
{
|
||||
if(mTextureID != 0)
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
void bind() const;
|
||||
|
||||
void initFromScreen();
|
||||
void initFromMemory(const char* image, size_t length);
|
||||
|
||||
private:
|
||||
TextureResource(const ResourceManager& rm, const std::string& path);
|
||||
|
|
|
@ -54,6 +54,18 @@ std::vector<MetaDataList> GamesDBScraper::parseReq(ScraperSearchParams params, s
|
|||
mdl.push_back(MetaDataList(params.system->getGameMDD()));
|
||||
mdl.back().set("name", game.child("GameTitle").text().get());
|
||||
mdl.back().set("desc", game.child("Overview").text().get());
|
||||
pugi::xml_node images = game.child("Images");
|
||||
|
||||
if(images)
|
||||
{
|
||||
pugi::xml_node art = images.find_child_by_attribute("boxart", "side", "front");
|
||||
|
||||
if(art)
|
||||
{
|
||||
mdl.back().set("thumbnail", baseImageUrl + art.attribute("thumb").as_string());
|
||||
mdl.back().set("image", baseImageUrl + art.text().get());
|
||||
}
|
||||
}
|
||||
|
||||
resultNum++;
|
||||
game = game.next_sibling("Game");
|
||||
|
@ -75,4 +87,3 @@ void GamesDBScraper::getResultsAsync(ScraperSearchParams params, Window* window,
|
|||
|
||||
window->pushGui(req);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue