From e785a2dfe001c580be48653d745a314a8bace6c0 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Tue, 2 Jul 2013 02:04:52 -0500 Subject: [PATCH] You can now mix and match game list detail levels. Basically, only games that have a gamelist.xml will use the detailed view. --- README.md | 2 +- src/components/GuiGameList.cpp | 193 ++++++++++++------------------ src/components/GuiGameList.h | 15 +-- src/components/ThemeComponent.cpp | 11 +- src/components/ThemeComponent.h | 5 +- 5 files changed, 92 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 5a1700639..d96a20257 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ The gamelist.xml for a system defines metadata for a system's games. This metada **Making a gamelist.xml by hand sucks, so a cool guy named Pendor made a python script which automatically generates a gamelist.xml for you, with boxart automatically downloaded. It can be found here:** https://github.com/elpendor/ES-scraper -If a file named gamelist.xml is found in the root of a system's search directory OR within `~/.emulationstation/%NAME%/`, it will be parsed and the detailed GuiGameList will be used. This means you can define images, descriptions, and different names for files. Note that only standard ASCII characters are supported (if you see a weird [X] symbol, you're probably using unicode!). +If a file named gamelist.xml is found in the root of a system's search directory OR within `~/.emulationstation/%NAME%/`, game metadata will be loaded from it. This allows you to define images, descriptions, and different names for files. Note that only standard ASCII characters are supported for text (if you see a weird [X] symbol, you're probably using unicode!). Images will be automatically resized to fit within the left column of the screen. Smaller images will load faster, so try to keep your resolution low. An example gamelist.xml: ``` diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index d6b2cd60d..0b66c944e 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -12,31 +12,23 @@ Vector2i GuiGameList::getImagePos() return Vector2i((int)(Renderer::getScreenWidth() * mTheme->getFloat("gameImageOffsetX")), (int)(Renderer::getScreenHeight() * mTheme->getFloat("gameImageOffsetY"))); } -GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window), - mDescription(window), mTransitionImage(window, 0, 0, "", Renderer::getScreenWidth(), Renderer::getScreenHeight(), true) +bool GuiGameList::isDetailed() const { - mDetailed = useDetail; + if(mSystem == NULL) + return false; - mTheme = new ThemeComponent(mWindow, mDetailed); + return mSystem->hasGamelist(); +} - mScreenshot = new ImageComponent(mWindow, getImagePos().x, getImagePos().y, "", (unsigned int)mTheme->getFloat("gameImageWidth"), (unsigned int)mTheme->getFloat("gameImageHeight"), false); +GuiGameList::GuiGameList(Window* window) : GuiComponent(window), + mTheme(new ThemeComponent(mWindow)), + mList(window, 0, 0, Renderer::getDefaultFont(Renderer::MEDIUM)), + mScreenshot(window), + mDescription(window), + mTransitionImage(window, 0, 0, "", Renderer::getScreenWidth(), Renderer::getScreenHeight(), true) +{ + mImageAnimation.addChild(&mScreenshot); - //The GuiGameList can use the older, simple game list if so desired. - //The old view only shows a list in the center of the screen; the new view can display an image and description. - //Those with smaller displays may prefer the older view. - if(mDetailed) - { - mList = new TextListComponent(mWindow, (int)(Renderer::getScreenWidth() * mTheme->getFloat("listOffsetX")), Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); - - mImageAnimation = new AnimationComponent(); - mImageAnimation->addChild(mScreenshot); - }else{ - mList = new TextListComponent(mWindow, 0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, Renderer::getDefaultFont(Renderer::MEDIUM)); - } - - mScreenshot->setOrigin(mTheme->getFloat("gameImageOriginX"), mTheme->getFloat("gameImageOriginY")); - - mDescription.setOffset(Vector2i((int)(Renderer::getScreenWidth() * 0.03), getImagePos().y + mScreenshot->getSize().y + 12)); //scale delay with screen width (higher width = more text per line) //the scroll speed is automatically scrolled by component size mDescription.setAutoScroll((int)(1500 + (Renderer::getScreenWidth() * 0.5)), 0.025f); @@ -49,7 +41,7 @@ GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window), //the list depends on knowing it's final window coordinates (getGlobalOffset), which requires knowing the where the GuiGameList is. //the GuiGameList now moves during screen transitions, so we have to let it know somehow. //this should be removed in favor of using real children soon. - mList->setParent(this); + mList.setParent(this); setSystemId(0); } @@ -57,15 +49,8 @@ GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window), GuiGameList::~GuiGameList() { //undo the parenting hack because otherwise it's not really a child and will try to remove itself on delete - mList->setParent(NULL); - delete mList; - delete mScreenshot; - - if(mDetailed) - { - delete mImageAnimation; - } - + mList.setParent(NULL); + delete mTheme; } @@ -112,22 +97,17 @@ void GuiGameList::render() if(!mTheme->getBool("hideHeader")) Renderer::drawCenteredText(mSystem->getDescName(), 0, 1, 0xFF0000FF, Renderer::getDefaultFont(Renderer::LARGE)); - if(mDetailed) + if(isDetailed()) { //divider if(!mTheme->getBool("hideDividers")) Renderer::drawRect((int)(Renderer::getScreenWidth() * mTheme->getFloat("listOffsetX")) - 4, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2, 8, Renderer::getScreenHeight(), 0x0000FFFF); - mScreenshot->render(); - - //if we're not scrolling and we have selected a non-folder - if(!mList->isScrolling() && !mList->getSelectedObject()->isFolder()) - { - mDescription.render(); - } + mScreenshot.render(); + mDescription.render(); } - mList->render(); + mList.render(); Renderer::translate(-mOffset); @@ -136,14 +116,14 @@ void GuiGameList::render() bool GuiGameList::input(InputConfig* config, Input input) { - mList->input(config, input); + mList.input(config, input); if(config->isMappedTo("a", input) && mFolder->getFileCount() > 0 && input.value != 0) { //play select sound mTheme->getSound("menuSelect")->play(); - FileData* file = mList->getSelectedObject(); + FileData* file = mList.getSelectedObject(); if(file->isFolder()) //if you selected a folder, add this directory to the stack, and use the selected one { mFolderStack.push(mFolder); @@ -152,7 +132,7 @@ bool GuiGameList::input(InputConfig* config, Input input) updateDetailData(); return true; }else{ - mList->stopScrolling(); + mList.stopScrolling(); //wait for the sound to finish or we'll never hear it... while(mTheme->getSound("menuSelect")->isPlaying()); @@ -203,11 +183,11 @@ bool GuiGameList::input(InputConfig* config, Input input) //open the fast select menu if(config->isMappedTo("select", input) && input.value != 0) { - mWindow->pushGui(new GuiFastSelect(mWindow, this, mList, mList->getSelectedObject()->getName()[0], mTheme->getBoxData(), mTheme->getColor("fastSelect"), mTheme->getSound("menuScroll"), mTheme->getFastSelectFont())); + mWindow->pushGui(new GuiFastSelect(mWindow, this, &mList, mList.getSelectedObject()->getName()[0], mTheme->getBoxData(), mTheme->getColor("fastSelect"), mTheme->getSound("menuScroll"), mTheme->getFastSelectFont())); return true; } - if(mDetailed) + if(isDetailed()) { if(config->isMappedTo("up", input) || config->isMappedTo("down", input) || config->isMappedTo("pageup", input) || config->isMappedTo("pagedown", input)) { @@ -224,19 +204,16 @@ bool GuiGameList::input(InputConfig* config, Input input) void GuiGameList::updateList() { - if(mDetailed) - mScreenshot->setImage(""); - - mList->clear(); + mList.clear(); for(unsigned int i = 0; i < mFolder->getFileCount(); i++) { FileData* file = mFolder->getFile(i); if(file->isFolder()) - mList->addObject(file->getName(), file, mTheme->getColor("secondary")); + mList.addObject(file->getName(), file, mTheme->getColor("secondary")); else - mList->addObject(file->getName(), file, mTheme->getColor("primary")); + mList.addObject(file->getName(), file, mTheme->getColor("primary")); } } @@ -263,65 +240,72 @@ std::string GuiGameList::getThemeFile() void GuiGameList::updateTheme() { - if(!mTheme) - return; + mTheme->readXML(getThemeFile(), isDetailed()); - mTheme->readXML( getThemeFile() ); + mList.setSelectorColor(mTheme->getColor("selector")); + mList.setSelectedTextColor(mTheme->getColor("selected")); + mList.setScrollSound(mTheme->getSound("menuScroll")); - mList->setSelectorColor(mTheme->getColor("selector")); - mList->setSelectedTextColor(mTheme->getColor("selected")); - mList->setScrollSound(mTheme->getSound("menuScroll")); + mList.setFont(mTheme->getListFont()); + mList.setOffset(0, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2); - //fonts - mList->setFont(mTheme->getListFont()); - - if(mDetailed) + if(isDetailed()) { - mList->setCentered(mTheme->getBool("listCentered")); + mList.setCentered(mTheme->getBool("listCentered")); - mList->setOffset((int)(mTheme->getFloat("listOffsetX") * Renderer::getScreenWidth()), mList->getOffset().y); - mList->setTextOffsetX((int)(mTheme->getFloat("listTextOffsetX") * Renderer::getScreenWidth())); + mList.setOffset((int)(mTheme->getFloat("listOffsetX") * Renderer::getScreenWidth()), mList.getOffset().y); + mList.setTextOffsetX((int)(mTheme->getFloat("listTextOffsetX") * Renderer::getScreenWidth())); - mScreenshot->setOffset((int)(mTheme->getFloat("gameImageOffsetX") * Renderer::getScreenWidth()), (int)(mTheme->getFloat("gameImageOffsetY") * Renderer::getScreenHeight())); - mScreenshot->setOrigin(mTheme->getFloat("gameImageOriginX"), mTheme->getFloat("gameImageOriginY")); - mScreenshot->setResize((int)mTheme->getFloat("gameImageWidth"), (int)mTheme->getFloat("gameImageHeight"), false); + mScreenshot.setOffset((int)(mTheme->getFloat("gameImageOffsetX") * Renderer::getScreenWidth()), (int)(mTheme->getFloat("gameImageOffsetY") * Renderer::getScreenHeight())); + mScreenshot.setOrigin(mTheme->getFloat("gameImageOriginX"), mTheme->getFloat("gameImageOriginY")); + mScreenshot.setResize((int)mTheme->getFloat("gameImageWidth"), (int)mTheme->getFloat("gameImageHeight"), false); mDescription.setColor(mTheme->getColor("description")); mDescription.setFont(mTheme->getDescriptionFont()); + }else{ + mList.setCentered(true); + mList.setOffset(0, mList.getOffset().y); } } void GuiGameList::updateDetailData() { - if(!mDetailed) - return; - - if(mList->getSelectedObject() && !mList->getSelectedObject()->isFolder()) + if(!isDetailed()) { - if(((GameData*)mList->getSelectedObject())->getImagePath().empty()) - mScreenshot->setImage(mTheme->getString("imageNotFoundPath")); - else - mScreenshot->setImage(((GameData*)mList->getSelectedObject())->getImagePath()); - - Vector2i imgOffset = Vector2i((int)(Renderer::getScreenWidth() * 0.10f), 0); - mScreenshot->setOffset(getImagePos() - imgOffset); - - mImageAnimation->fadeIn(35); - mImageAnimation->move(imgOffset.x, imgOffset.y, 20); - - mDescription.setOffset(Vector2i((int)(Renderer::getScreenWidth() * 0.03), getImagePos().y + mScreenshot->getSize().y + 12)); - mDescription.setExtent(Vector2u((int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), Renderer::getScreenHeight() - mDescription.getOffset().y)); - mDescription.setText(((GameData*)mList->getSelectedObject())->getDescription()); + mScreenshot.setImage(""); + mDescription.setText(""); }else{ - mScreenshot->setImage(""); + //if we've selected a game + if(mList.getSelectedObject() && !mList.getSelectedObject()->isFolder()) + { + //set image to either "not found" image or metadata image + if(((GameData*)mList.getSelectedObject())->getImagePath().empty()) + mScreenshot.setImage(mTheme->getString("imageNotFoundPath")); + else + mScreenshot.setImage(((GameData*)mList.getSelectedObject())->getImagePath()); + + Vector2i imgOffset = Vector2i((int)(Renderer::getScreenWidth() * 0.10f), 0); + mScreenshot.setOffset(getImagePos() - imgOffset); + + mImageAnimation.fadeIn(35); + mImageAnimation.move(imgOffset.x, imgOffset.y, 20); + + mDescription.setOffset(Vector2i((int)(Renderer::getScreenWidth() * 0.03), getImagePos().y + mScreenshot.getSize().y + 12)); + mDescription.setExtent(Vector2u((int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), Renderer::getScreenHeight() - mDescription.getOffset().y)); + mDescription.setText(((GameData*)mList.getSelectedObject())->getDescription()); + }else{ + mScreenshot.setImage(""); + mDescription.setText(""); + } } } void GuiGameList::clearDetailData() { - if(mDetailed) + if(isDetailed()) { - mImageAnimation->fadeOut(35); + mImageAnimation.fadeOut(35); + mDescription.setText(""); } } @@ -329,11 +313,8 @@ void GuiGameList::clearDetailData() //we have to manually call init/deinit on mTheme because it is not our child void GuiGameList::deinit() { - if(mDetailed) - { - mScreenshot->deinit(); - } - + mScreenshot.deinit(); + mTheme->deinit(); } @@ -341,41 +322,23 @@ void GuiGameList::init() { mTheme->init(); - if(mDetailed) - { - mScreenshot->init(); - } + mScreenshot.init(); } GuiGameList* GuiGameList::create(Window* window) { - bool detailed = false; - - if(!Settings::getInstance()->getBool("IGNOREGAMELIST")) - { - for(unsigned int i = 0; i < SystemData::sSystemVector.size(); i++) - { - if(SystemData::sSystemVector.at(i)->hasGamelist()) - { - detailed = true; - break; - } - } - } - - GuiGameList* list = new GuiGameList(window, detailed); + GuiGameList* list = new GuiGameList(window); window->pushGui(list); return list; } void GuiGameList::update(int deltaTime) { - if(mDetailed) - mImageAnimation->update(deltaTime); + mImageAnimation.update(deltaTime); mTransitionAnimation.update(deltaTime); - mList->update(deltaTime); + mList.update(deltaTime); mDescription.update(deltaTime); } diff --git a/src/components/GuiGameList.h b/src/components/GuiGameList.h index 7f51a0f15..51e581bbe 100644 --- a/src/components/GuiGameList.h +++ b/src/components/GuiGameList.h @@ -18,13 +18,13 @@ class GuiGameList : public GuiComponent { public: - GuiGameList(Window* window, bool useDetail = false); + GuiGameList(Window* window); virtual ~GuiGameList(); void setSystemId(int id); - bool input(InputConfig* config, Input input); - void update(int deltaTime); + bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; void render(); void init(); @@ -34,6 +34,8 @@ public: static GuiGameList* create(Window* window); + bool isDetailed() const; + static const float sInfoWidth; private: void updateList(); @@ -47,12 +49,11 @@ private: FolderData* mFolder; std::stack mFolderStack; int mSystemId; - bool mDetailed; - TextListComponent* mList; - ImageComponent* mScreenshot; + TextListComponent mList; + ImageComponent mScreenshot; TextComponent mDescription; - AnimationComponent* mImageAnimation; + AnimationComponent mImageAnimation; ThemeComponent* mTheme; ImageComponent mTransitionImage; diff --git a/src/components/ThemeComponent.cpp b/src/components/ThemeComponent.cpp index 56a5d42e1..0e54caa81 100644 --- a/src/components/ThemeComponent.cpp +++ b/src/components/ThemeComponent.cpp @@ -59,10 +59,8 @@ Font* ThemeComponent::getFastSelectFont() return mFastSelectFont; } -ThemeComponent::ThemeComponent(Window* window, bool detailed, std::string path) : GuiComponent(window) +ThemeComponent::ThemeComponent(Window* window) : GuiComponent(window) { - mDetailed = detailed; - mSoundMap["menuScroll"] = std::shared_ptr(new Sound); mSoundMap["menuSelect"] = std::shared_ptr(new Sound); mSoundMap["menuBack"] = std::shared_ptr(new Sound); @@ -79,9 +77,6 @@ ThemeComponent::ThemeComponent(Window* window, bool detailed, std::string path) mFastSelectFont = NULL; setDefaults(); - - if(!path.empty()) - readXML(path); } ThemeComponent::~ThemeComponent() @@ -159,7 +154,7 @@ void ThemeComponent::deleteComponents() -void ThemeComponent::readXML(std::string path) +void ThemeComponent::readXML(std::string path, bool detailed) { if(mPath == path) return; @@ -185,7 +180,7 @@ void ThemeComponent::readXML(std::string path) pugi::xml_node root; - if(!mDetailed) + if(!detailed) { //if we're using the basic view, check if there's a basic version of the theme root = doc.child("basicTheme"); diff --git a/src/components/ThemeComponent.h b/src/components/ThemeComponent.h index 8b711c8ec..c6ec05f4e 100644 --- a/src/components/ThemeComponent.h +++ b/src/components/ThemeComponent.h @@ -13,10 +13,10 @@ class ThemeComponent : public GuiComponent { public: - ThemeComponent(Window* window, bool detailed, std::string path = ""); + ThemeComponent(Window* window); virtual ~ThemeComponent(); - void readXML(std::string path); + void readXML(std::string path, bool detailed); GuiBoxData getBoxData(); @@ -48,7 +48,6 @@ private: Font* resolveFont(pugi::xml_node node, std::string defaultPath, unsigned int defaultSize); std::string mPath; - bool mDetailed; std::map mColorMap; std::map mBoolMap;