mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-29 19:55:37 +00:00
Merge remote-tracking branch 'upstream/master'
had to hand-merge GuiGameList.cpp
This commit is contained in:
commit
f72e80b629
|
@ -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:
|
||||
```
|
||||
|
|
145
src/Font.cpp
145
src/Font.cpp
|
@ -5,7 +5,6 @@
|
|||
#include "Renderer.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "Log.h"
|
||||
#include "Vector2.h"
|
||||
|
||||
FT_Library Font::sLibrary;
|
||||
bool Font::libraryInitialized = false;
|
||||
|
@ -220,13 +219,14 @@ Font::~Font()
|
|||
}
|
||||
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
Vector2<GLfloat> pos;
|
||||
Vector2<GLfloat> tex;
|
||||
};
|
||||
|
||||
void Font::drawText(std::string text, int startx, int starty, int color)
|
||||
{
|
||||
TextCache* cache = buildTextCache(text, startx, starty, color);
|
||||
renderTextCache(cache);
|
||||
delete cache;
|
||||
}
|
||||
|
||||
void Font::renderTextCache(TextCache* cache)
|
||||
{
|
||||
if(!textureID)
|
||||
{
|
||||
|
@ -234,24 +234,92 @@ void Font::drawText(std::string text, int startx, int starty, int color)
|
|||
return;
|
||||
}
|
||||
|
||||
const int triCount = text.length() * 2;
|
||||
Vertex* vert = new Vertex[triCount * 3];
|
||||
GLubyte* colors = new GLubyte[triCount * 3 * 4];
|
||||
if(cache == NULL)
|
||||
{
|
||||
LOG(LogError) << "Attempted to draw NULL TextCache!";
|
||||
return;
|
||||
}
|
||||
|
||||
if(cache->sourceFont != this)
|
||||
{
|
||||
LOG(LogError) << "Attempted to draw TextCache with font other than its source!";
|
||||
return;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(TextCache::Vertex), &cache->verts[0].pos);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(TextCache::Vertex), &cache->verts[0].tex);
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, 0, cache->colors);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, cache->vertCount);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void Font::sizeText(std::string text, int* w, int* h)
|
||||
{
|
||||
float cwidth = 0.0f;
|
||||
for(unsigned int i = 0; i < text.length(); i++)
|
||||
{
|
||||
unsigned char letter = text[i];
|
||||
if(letter < 32 || letter >= 128)
|
||||
letter = 127;
|
||||
|
||||
cwidth += charData[letter].advX * fontScale;
|
||||
}
|
||||
|
||||
if(w != NULL)
|
||||
*w = (int)cwidth;
|
||||
|
||||
if(h != NULL)
|
||||
*h = getHeight();
|
||||
}
|
||||
|
||||
int Font::getHeight()
|
||||
{
|
||||
return (int)(mMaxGlyphHeight * 1.5f * fontScale);
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================================================
|
||||
//TextCache
|
||||
//=============================================================================================================
|
||||
|
||||
TextCache* Font::buildTextCache(const std::string& text, int offsetX, int offsetY, unsigned int color)
|
||||
{
|
||||
if(!textureID)
|
||||
{
|
||||
LOG(LogError) << "Error - tried to build TextCache with Font that has no texture loaded!";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int triCount = text.length() * 2;
|
||||
const int vertCount = triCount * 3;
|
||||
TextCache::Vertex* vert = new TextCache::Vertex[vertCount];
|
||||
GLubyte* colors = new GLubyte[vertCount * 4];
|
||||
|
||||
//texture atlas width/height
|
||||
float tw = (float)textureWidth;
|
||||
float th = (float)textureHeight;
|
||||
|
||||
float x = (float)startx;
|
||||
float y = starty + mMaxGlyphHeight * 1.1f * fontScale; //padding (another 0.5% is added to the bottom through the sizeText function)
|
||||
float x = (float)offsetX;
|
||||
float y = offsetY + mMaxGlyphHeight * 1.1f * fontScale; //padding (another 0.5% is added to the bottom through the sizeText function)
|
||||
|
||||
int charNum = 0;
|
||||
for(int i = 0; i < triCount * 3; i += 6, charNum++)
|
||||
for(int i = 0; i < vertCount; i += 6, charNum++)
|
||||
{
|
||||
unsigned char letter = text[charNum];
|
||||
|
||||
|
@ -286,49 +354,24 @@ void Font::drawText(std::string text, int startx, int starty, int color)
|
|||
x += charData[letter].advX * fontScale;
|
||||
}
|
||||
|
||||
Renderer::buildGLColorArray(colors, color, triCount * 3);
|
||||
TextCache* cache = new TextCache(vertCount, vert, colors, this);
|
||||
if(color != 0x00000000)
|
||||
cache->setColor(color);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
return cache;
|
||||
}
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vert[0].pos);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vert[0].tex);
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
|
||||
TextCache::TextCache(int verts, Vertex* v, GLubyte* c, Font* f) : vertCount(verts), verts(v), colors(c), sourceFont(f)
|
||||
{
|
||||
}
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, triCount * 3);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
delete[] vert;
|
||||
TextCache::~TextCache()
|
||||
{
|
||||
delete[] verts;
|
||||
delete[] colors;
|
||||
}
|
||||
|
||||
void Font::sizeText(std::string text, int* w, int* h)
|
||||
void TextCache::setColor(unsigned int color)
|
||||
{
|
||||
float cwidth = 0.0f;
|
||||
for(unsigned int i = 0; i < text.length(); i++)
|
||||
{
|
||||
unsigned char letter = text[i];
|
||||
if(letter < 32 || letter >= 128)
|
||||
letter = 127;
|
||||
|
||||
cwidth += charData[letter].advX * fontScale;
|
||||
}
|
||||
|
||||
if(w != NULL)
|
||||
*w = (int)cwidth;
|
||||
|
||||
if(h != NULL)
|
||||
*h = getHeight();
|
||||
}
|
||||
|
||||
int Font::getHeight()
|
||||
{
|
||||
return (int)(mMaxGlyphHeight * 1.5f * fontScale);
|
||||
Renderer::buildGLColorArray(const_cast<GLubyte*>(colors), color, vertCount);
|
||||
}
|
||||
|
|
31
src/Font.h
31
src/Font.h
|
@ -6,6 +6,9 @@
|
|||
#include GLHEADER
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include "Vector2.h"
|
||||
|
||||
class TextCache;
|
||||
|
||||
//A TrueType Font renderer that uses FreeType and OpenGL.
|
||||
//The library is automatically initialized when it's needed.
|
||||
|
@ -37,8 +40,12 @@ public:
|
|||
|
||||
GLuint textureID;
|
||||
|
||||
void drawText(std::string text, int startx, int starty, int color); //Render some text using this font.
|
||||
void sizeText(std::string text, int* w, int* h); //Sets the width and height of a given string to given pointers. Skipped if pointer is NULL.
|
||||
TextCache* buildTextCache(const std::string& text, int offsetX, int offsetY, unsigned int color);
|
||||
void renderTextCache(TextCache* cache);
|
||||
|
||||
//Create a TextCache, render with it, then delete it. Best used for short text or text that changes frequently.
|
||||
void drawText(std::string text, int startx, int starty, int color);
|
||||
void sizeText(std::string text, int* w, int* h); //Sets the width and height of a given string to supplied pointers. A dimension is skipped if its pointer is NULL.
|
||||
int getHeight();
|
||||
|
||||
void init();
|
||||
|
@ -65,4 +72,24 @@ private:
|
|||
int mSize;
|
||||
};
|
||||
|
||||
class TextCache
|
||||
{
|
||||
public:
|
||||
struct Vertex
|
||||
{
|
||||
Vector2<GLfloat> pos;
|
||||
Vector2<GLfloat> tex;
|
||||
};
|
||||
|
||||
void setColor(unsigned int color);
|
||||
|
||||
TextCache(int verts, Vertex* v, GLubyte* c, Font* f);
|
||||
~TextCache();
|
||||
|
||||
const int vertCount;
|
||||
const Vertex* verts;
|
||||
const GLubyte* colors;
|
||||
const Font* sourceFont;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace Renderer
|
|||
void drawText(std::string text, int x, int y, unsigned int color, Font* font);
|
||||
void drawCenteredText(std::string text, int xOffset, int y, unsigned int color, Font* font);
|
||||
void drawWrappedText(std::string text, int xStart, int yStart, int xLen, unsigned int color, Font* font);
|
||||
void sizeWrappedText(std::string text, int xLen, Font* font, int* xOut, int* yOut);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -252,4 +252,61 @@ namespace Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
void sizeWrappedText(std::string text, int xLen, Font* font, int* xOut, int* yOut)
|
||||
{
|
||||
*xOut = xLen;
|
||||
|
||||
int y = 0;
|
||||
|
||||
std::string line, word, temp;
|
||||
int w, h;
|
||||
size_t space, newline;
|
||||
|
||||
while(text.length() > 0 || !line.empty()) //while there's text or we still have text to render
|
||||
{
|
||||
space = text.find(' ', 0);
|
||||
if(space == std::string::npos)
|
||||
space = text.length() - 1;
|
||||
|
||||
word = text.substr(0, space + 1);
|
||||
|
||||
//check if the next word contains a newline
|
||||
newline = word.find('\n', 0);
|
||||
if(newline != std::string::npos)
|
||||
{
|
||||
word = word.substr(0, newline);
|
||||
text.erase(0, newline + 1);
|
||||
}else{
|
||||
text.erase(0, space + 1);
|
||||
}
|
||||
|
||||
temp = line + word;
|
||||
|
||||
font->sizeText(temp, &w, &h);
|
||||
|
||||
//if we're on the last word and it'll fit on the line, just add it to the line
|
||||
if((w <= xLen && text.length() == 0) || newline != std::string::npos)
|
||||
{
|
||||
line = temp;
|
||||
word = "";
|
||||
}
|
||||
|
||||
//if the next line will be too long or we're on the last of the text, render it
|
||||
if(w > xLen || text.length() == 0 || newline != std::string::npos)
|
||||
{
|
||||
//increment y by height and some extra padding for the next line
|
||||
y += h + 4;
|
||||
|
||||
//move the word we skipped to the next line
|
||||
line = word;
|
||||
}else{
|
||||
//there's still space, continue building the line
|
||||
line = temp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*yOut = y;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -93,6 +93,7 @@ bool operator !=(const Vector2<T>& left, const Vector2<T>& right)
|
|||
typedef Vector2<int> Vector2i;
|
||||
typedef Vector2<unsigned int> Vector2u;
|
||||
typedef Vector2<float> Vector2f;
|
||||
typedef Vector2<double> Vector2d;
|
||||
|
||||
class Rect
|
||||
{
|
||||
|
|
|
@ -16,8 +16,21 @@ 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), mDetailed(useDetail), sortStateIndex(0)
|
||||
bool GuiGameList::isDetailed() const
|
||||
{
|
||||
if(mSystem == NULL)
|
||||
return false;
|
||||
|
||||
return mSystem->hasGamelist();
|
||||
}
|
||||
|
||||
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),
|
||||
sortStateIndex(0)
|
||||
{
|
||||
//first object initializes the vector
|
||||
if (sortStates.empty()) {
|
||||
|
@ -33,28 +46,12 @@ GuiGameList::GuiGameList(Window* window, bool useDetail) : GuiComponent(window),
|
|||
sortStates.push_back(FolderData::SortState(FolderData::compareLastPlayed, false));
|
||||
}
|
||||
|
||||
mTheme = new ThemeComponent(mWindow, mDetailed);
|
||||
mImageAnimation.addChild(&mScreenshot);
|
||||
|
||||
mScreenshot = new ImageComponent(mWindow, getImagePos().x, getImagePos().y, "", (unsigned int)mTheme->getFloat("gameImageWidth"), (unsigned int)mTheme->getFloat("gameImageHeight"), false);
|
||||
//scale delay with screen width (higher width = more text per line)
|
||||
//the scroll speed is automatically scaled by component size
|
||||
mDescription.setAutoScroll((int)(1500 + (Renderer::getScreenWidth() * 0.5)), 0.025f);
|
||||
|
||||
//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<FileData*>(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<FileData*>(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), mScreenshot->getOffset().y + mScreenshot->getSize().y + 12));
|
||||
mDescription.setExtent(Vector2u((int)(Renderer::getScreenWidth() * (mTheme->getFloat("listOffsetX") - 0.03)), 0));
|
||||
|
||||
mTransitionImage.setOffset(Renderer::getScreenWidth(), 0);
|
||||
mTransitionImage.setOrigin(0, 0);
|
||||
mTransitionAnimation.addChild(&mTransitionImage);
|
||||
|
@ -63,7 +60,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);
|
||||
}
|
||||
|
@ -71,15 +68,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;
|
||||
}
|
||||
|
||||
|
@ -126,22 +116,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);
|
||||
|
||||
|
@ -150,14 +135,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);
|
||||
|
@ -166,7 +151,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());
|
||||
|
@ -227,11 +212,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))
|
||||
{
|
||||
|
@ -287,19 +272,16 @@ void GuiGameList::sort(FolderData::ComparisonFunction & comparisonFunction, bool
|
|||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,64 +308,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), mScreenshot->getOffset().y + mScreenshot->getSize().y + 12));
|
||||
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("");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,11 +381,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();
|
||||
}
|
||||
|
||||
|
@ -403,41 +390,25 @@ 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);
|
||||
}
|
||||
|
||||
void GuiGameList::doTransition(int dir)
|
||||
|
|
|
@ -21,13 +21,13 @@ class GuiGameList : public GuiComponent
|
|||
size_t sortStateIndex;
|
||||
|
||||
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();
|
||||
|
@ -42,6 +42,8 @@ public:
|
|||
|
||||
static GuiGameList* create(Window* window);
|
||||
|
||||
bool isDetailed() const;
|
||||
|
||||
static const float sInfoWidth;
|
||||
private:
|
||||
void updateList();
|
||||
|
@ -55,12 +57,11 @@ private:
|
|||
FolderData* mFolder;
|
||||
std::stack<FolderData*> mFolderStack;
|
||||
int mSystemId;
|
||||
bool mDetailed;
|
||||
|
||||
TextListComponent<FileData*>* mList;
|
||||
ImageComponent* mScreenshot;
|
||||
TextListComponent<FileData*> mList;
|
||||
ImageComponent mScreenshot;
|
||||
TextComponent mDescription;
|
||||
AnimationComponent* mImageAnimation;
|
||||
AnimationComponent mImageAnimation;
|
||||
ThemeComponent* mTheme;
|
||||
|
||||
ImageComponent mTransitionImage;
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
#include "../Renderer.h"
|
||||
#include "../Log.h"
|
||||
|
||||
TextComponent::TextComponent(Window* window) : GuiComponent(window), mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true)
|
||||
TextComponent::TextComponent(Window* window) : GuiComponent(window),
|
||||
mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true), mAutoScrollDelay(0), mAutoScrollSpeed(0), mAutoScrollTimer(0)
|
||||
{
|
||||
}
|
||||
|
||||
TextComponent::TextComponent(Window* window, const std::string& text, Font* font, Vector2i pos, Vector2u size) : GuiComponent(window),
|
||||
mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true)
|
||||
mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true), mAutoScrollDelay(0), mAutoScrollSpeed(0), mAutoScrollTimer(0)
|
||||
{
|
||||
setText(text);
|
||||
setFont(font);
|
||||
|
@ -51,11 +52,20 @@ void TextComponent::setText(const std::string& text)
|
|||
|
||||
if(mAutoCalcExtent)
|
||||
calculateExtent();
|
||||
|
||||
mScrollPos = Vector2d(0, 0);
|
||||
mScrollDir = Vector2d(0, 0);
|
||||
resetAutoScrollTimer();
|
||||
}
|
||||
|
||||
Font* TextComponent::getFont() const
|
||||
{
|
||||
return (mFont ? mFont : Renderer::getDefaultFont(Renderer::MEDIUM));;
|
||||
}
|
||||
|
||||
void TextComponent::onRender()
|
||||
{
|
||||
Font* font = (mFont ? mFont : Renderer::getDefaultFont(Renderer::MEDIUM));
|
||||
Font* font = getFont();
|
||||
if(font == NULL)
|
||||
{
|
||||
LOG(LogError) << "TextComponent can't get a valid font!";
|
||||
|
@ -64,7 +74,7 @@ void TextComponent::onRender()
|
|||
|
||||
Renderer::pushClipRect(getGlobalOffset(), getSize());
|
||||
|
||||
Renderer::drawWrappedText(mText, 0, 0, mSize.x, mColor, font);
|
||||
Renderer::drawWrappedText(mText, (int)-mScrollPos.x, (int)-mScrollPos.y, mSize.x, mColor, font);
|
||||
|
||||
Renderer::popClipRect();
|
||||
|
||||
|
@ -73,7 +83,7 @@ void TextComponent::onRender()
|
|||
|
||||
void TextComponent::calculateExtent()
|
||||
{
|
||||
Font* font = (mFont ? mFont : Renderer::getDefaultFont(Renderer::MEDIUM));
|
||||
Font* font = getFont();
|
||||
if(font == NULL)
|
||||
{
|
||||
LOG(LogError) << "TextComponent can't get a valid font!";
|
||||
|
@ -82,3 +92,73 @@ void TextComponent::calculateExtent()
|
|||
|
||||
font->sizeText(mText, (int*)&mSize.x, (int*)&mSize.y);
|
||||
}
|
||||
|
||||
void TextComponent::setAutoScroll(int delay, double speed)
|
||||
{
|
||||
mAutoScrollDelay = delay;
|
||||
mAutoScrollSpeed = speed;
|
||||
resetAutoScrollTimer();
|
||||
}
|
||||
|
||||
void TextComponent::resetAutoScrollTimer()
|
||||
{
|
||||
mAutoScrollTimer = 0;
|
||||
}
|
||||
|
||||
Vector2d TextComponent::getScrollPos() const
|
||||
{
|
||||
return mScrollPos;
|
||||
}
|
||||
|
||||
void TextComponent::setScrollPos(const Vector2d& pos)
|
||||
{
|
||||
mScrollPos = pos;
|
||||
}
|
||||
|
||||
void TextComponent::update(int deltaTime)
|
||||
{
|
||||
double scrollAmt = (double)deltaTime;
|
||||
|
||||
if(mAutoScrollSpeed != 0)
|
||||
{
|
||||
mAutoScrollTimer += deltaTime;
|
||||
|
||||
scrollAmt = (float)(mAutoScrollTimer - mAutoScrollDelay);
|
||||
|
||||
if(scrollAmt > 0)
|
||||
{
|
||||
//scroll the amount of time left over from the delay
|
||||
mAutoScrollTimer = mAutoScrollDelay;
|
||||
|
||||
//scale speed by our width! more text per line = slower scrolling
|
||||
const double widthMod = (680.0 / getSize().x);
|
||||
mScrollDir = Vector2d(0, mAutoScrollSpeed * widthMod);
|
||||
}else{
|
||||
//not enough to pass the delay, do nothing
|
||||
scrollAmt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2d scroll = mScrollDir * scrollAmt;
|
||||
mScrollPos += scroll;
|
||||
|
||||
//clip scrolling within bounds
|
||||
if(mScrollPos.x < 0)
|
||||
mScrollPos.x = 0;
|
||||
if(mScrollPos.y < 0)
|
||||
mScrollPos.y = 0;
|
||||
|
||||
Font* font = getFont();
|
||||
if(font != NULL)
|
||||
{
|
||||
Vector2i textSize;
|
||||
Renderer::sizeWrappedText(mText, getSize().x, getFont(), &textSize.x, &textSize.y);
|
||||
|
||||
if(mScrollPos.x + getSize().x > textSize.x)
|
||||
mScrollPos.x = (double)textSize.x - getSize().x;
|
||||
if(mScrollPos.y + getSize().y > textSize.y)
|
||||
mScrollPos.y = (double)textSize.y - getSize().y;
|
||||
}
|
||||
|
||||
GuiComponent::update(deltaTime);
|
||||
}
|
||||
|
|
|
@ -16,15 +16,29 @@ public:
|
|||
void setText(const std::string& text);
|
||||
void setColor(unsigned int color);
|
||||
|
||||
void onRender();
|
||||
Vector2d getScrollPos() const;
|
||||
void setScrollPos(const Vector2d& pos);
|
||||
void setAutoScroll(int delay, double speed); //Use 0 for speed to disable.
|
||||
void resetAutoScrollTimer();
|
||||
|
||||
void update(int deltaTime) override;
|
||||
void onRender() override;
|
||||
|
||||
private:
|
||||
Font* getFont() const;
|
||||
void calculateExtent();
|
||||
|
||||
unsigned int mColor;
|
||||
Font* mFont;
|
||||
bool mAutoCalcExtent;
|
||||
std::string mText;
|
||||
|
||||
//scrolling
|
||||
Vector2d mScrollPos;
|
||||
Vector2d mScrollDir;
|
||||
int mAutoScrollDelay;
|
||||
double mAutoScrollSpeed;
|
||||
int mAutoScrollTimer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<Sound>(new Sound);
|
||||
mSoundMap["menuSelect"] = std::shared_ptr<Sound>(new Sound);
|
||||
mSoundMap["menuBack"] = std::shared_ptr<Sound>(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");
|
||||
|
|
|
@ -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<std::string, unsigned int> mColorMap;
|
||||
std::map<std::string, bool> mBoolMap;
|
||||
|
|
Loading…
Reference in a new issue