Search for box hooked up.

Display thumbnails for results.
Still need to resolve boxart.
This commit is contained in:
Aloshi 2013-09-20 18:55:05 -05:00
parent 9ce511cc71
commit a3a4636fd5
13 changed files with 166 additions and 7 deletions

View file

@ -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)

View file

@ -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);

View file

@ -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"},

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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();

View file

@ -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)

View file

@ -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);

View file

@ -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);
}