mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 14:15:38 +00:00
New game image theming tags! See THEMES.md for details.
Also, some minor fixes all around (game descriptions no longer appear while scrolling, woo!).
This commit is contained in:
parent
65701c58c1
commit
61c9b10abd
21
THEMES.md
21
THEMES.md
|
@ -5,8 +5,7 @@ EmulationStation allows each system to have its own "theme." A theme is a collec
|
|||
|
||||
ES will check two places for a theme: first, the system's search directory for theme.xml. Then, if that's not found, $HOME/.emulationstation/es_theme.xml.
|
||||
|
||||
Almost all positions, dimensions, etc. work in percentages - that is, they are a decimal between 0 and 1, representing the percentage of the screen on that axis to use.
|
||||
This ensures that themes look similar at every resolution.
|
||||
Almost all positions, dimensions, origins, etc. work in percentages - that is, they are a decimal between 0 and 1, representing the percentage of the screen on that axis to use. This ensures that themes look similar at every resolution.
|
||||
|
||||
|
||||
Example
|
||||
|
@ -61,13 +60,13 @@ Display tags must be at the root of the <theme> tree - for example, they can't b
|
|||
|
||||
`<descColor>` - the hex font color to use for the description on the GuiGameList.
|
||||
|
||||
`<listSelectorColor>` - the hex color to use for the "selector bar" on the GuiGameList.
|
||||
`<listSelectorColor>` - the hex color to use for the "selector bar" on the GuiGameList. Default is `000000`.
|
||||
|
||||
`<listSelectedColor>` - the hex color to use for selected text on the GuiGameList. Default is -1, which will not change the color.
|
||||
|
||||
`<listLeftAlign />` - if present, the games list names will be left aligned to the value of `<listOffsetX>` (default 0.5).
|
||||
|
||||
`<hideHeader />` - if present, the system name header won't be displayed (useful for replacing it with an image).
|
||||
`<hideHeader />` - if present, the system name header won't be displayed (useful for replacing it with an image). If you're making a complete custom theme, you probably want to use this.
|
||||
|
||||
`<hideDividers />` - if present, the divider between games on the detailed GuiGameList won't be displayed.
|
||||
|
||||
|
@ -75,11 +74,17 @@ Display tags must be at the root of the <theme> tree - for example, they can't b
|
|||
|
||||
`<listTextOffsetX>` - the percentage to offset the text in the list by. Default is 0.005. Only works in combination with `<listLeftAlign />`.
|
||||
|
||||
`<gameImageOffsetY>` - the percentage to offset the displayed game image by. Default is the height of the header font.
|
||||
~~`<gameImageOffsetY>` - the percentage to offset the displayed game image by. Default is the height of the header font.~~
|
||||
|
||||
`<gameImagePos>` - two values for the position of the game art, in the form of `[x] [y]`, as a percentage. Default is `$infoWidth/2 $headerHeight`.
|
||||
|
||||
`<gameImageDim>' - two values for the dimensions of the game art, in the form of `[width] [height]`, as a percentage of the screen. Default is `0 0` (not resized). The image will only be resized if at least one axis is nonzero *and* exceeded by the image's size. You should always leave at least one axis as zero to preserve the aspect ratio.
|
||||
|
||||
`<gameImageOrigin>` two values for the origin of the game art, in the form of `[x] [y]`, as a percentage. Default is `0.5 0`.
|
||||
|
||||
|
||||
|
||||
The Fast Select box can be themed with these tags:
|
||||
**The Fast Select box can be themed with these tags:**
|
||||
|
||||
`<fastSelectColor>` - the hex color to use for the letter display on the Fast Select box.
|
||||
|
||||
|
@ -117,9 +122,11 @@ List of variables
|
|||
|
||||
Variables can be used in position and dimension definitions. They can be added, subtracted, multiplied, and divided. Parenthesis are valid. They are a percentage of the screen.
|
||||
|
||||
For example, if you wanted to place an image that covered the left half of the screen, up to the game list, you could use `<dim>$infoWidth 1</dim>`.
|
||||
|
||||
`$headerHeight` - height of the system name header.
|
||||
|
||||
`$infoWidth` - where the center of the horizontal divider is drawn.
|
||||
`$infoWidth` - where the left of the game list begins. Will follow `<listOffsetX>`.
|
||||
|
||||
|
||||
-Aloshi
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
October 13
|
||||
-Added sound support through SDL_mixer.
|
||||
-Added new theme tags for defining menu sounds. See THEMES.md for details.
|
||||
-Added new theme tags for defining game art information. See THEMES.md for details.
|
||||
|
||||
October 10
|
||||
-Added a theming tag for the Fast Select box's text.
|
||||
|
|
|
@ -109,7 +109,7 @@ void MathExp::doNextOperation()
|
|||
{
|
||||
val = mOperands.top();
|
||||
mOperands.pop();
|
||||
val -= mOperands.top();
|
||||
val = mOperands.top() - val;
|
||||
mOperands.pop();
|
||||
}
|
||||
if(top == *"*")
|
||||
|
@ -123,7 +123,7 @@ void MathExp::doNextOperation()
|
|||
{
|
||||
val = mOperands.top();
|
||||
mOperands.pop();
|
||||
val /= mOperands.top();
|
||||
val = mOperands.top() / val;
|
||||
mOperands.pop();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "GuiFastSelect.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
|
||||
//this is just a default value; the true value is in mTheme->getListOffsetX();
|
||||
const float GuiGameList::sInfoWidth = 0.5;
|
||||
|
||||
|
||||
|
@ -23,8 +23,8 @@ GuiGameList::GuiGameList(bool useDetail)
|
|||
{
|
||||
mList = new GuiList<FileData*>(Renderer::getScreenWidth() * sInfoWidth, Renderer::getFontHeight(Renderer::LARGE) + 2);
|
||||
|
||||
mScreenshot = new GuiImage(Renderer::getScreenWidth() * sInfoWidth * 0.5, Renderer::getScreenHeight() * mTheme->getGameImageOffsetY(), "", Renderer::getScreenWidth() * sInfoWidth * 0.7);
|
||||
mScreenshot->setOrigin(0.5, 0.0);
|
||||
mScreenshot = new GuiImage(Renderer::getScreenWidth() * sInfoWidth * 0.5, Renderer::getScreenHeight() * mTheme->getGameImageOffsetY(), "", Renderer::getScreenWidth() * sInfoWidth * 0.7, 0, false);
|
||||
mScreenshot->setOrigin(mTheme->getGameImageOriginX(), mTheme->getGameImageOriginY());
|
||||
addChild(mScreenshot);
|
||||
}else{
|
||||
mList = new GuiList<FileData*>(0, Renderer::getFontHeight(Renderer::LARGE) + 2);
|
||||
|
@ -70,7 +70,7 @@ void GuiGameList::setSystemId(int id)
|
|||
mSystemId = id;
|
||||
mSystem = SystemData::sSystemVector.at(mSystemId);
|
||||
|
||||
//clear the folder stack (I can't look up the proper method right now)
|
||||
//clear the folder stack
|
||||
while(mFolderStack.size()){ mFolderStack.pop(); }
|
||||
|
||||
mFolder = mSystem->getRootFolder();
|
||||
|
@ -93,7 +93,7 @@ void GuiGameList::onRender()
|
|||
{
|
||||
//divider
|
||||
if(!mTheme->getDividersHidden())
|
||||
Renderer::drawRect(Renderer::getScreenWidth() * sInfoWidth - 4, Renderer::getFontHeight(Renderer::LARGE) + 2, 8, Renderer::getScreenHeight(), 0x0000FF);
|
||||
Renderer::drawRect(Renderer::getScreenWidth() * mTheme->getListOffsetX() - 4, Renderer::getFontHeight(Renderer::LARGE) + 2, 8, Renderer::getScreenHeight(), 0x0000FF);
|
||||
|
||||
//if we're not scrolling and we have selected a non-folder
|
||||
if(!mList->isScrolling() && mList->getSelectedObject() && !mList->getSelectedObject()->isFolder())
|
||||
|
@ -102,7 +102,7 @@ void GuiGameList::onRender()
|
|||
|
||||
std::string desc = game->getDescription();
|
||||
if(!desc.empty())
|
||||
Renderer::drawWrappedText(desc, Renderer::getScreenWidth() * 0.03, mScreenshot->getOffsetY() + mScreenshot->getHeight() + 8, Renderer::getScreenWidth() * (sInfoWidth - 0.03), mTheme->getDescColor(), Renderer::SMALL);
|
||||
Renderer::drawWrappedText(desc, Renderer::getScreenWidth() * 0.03, mScreenshot->getOffsetY() + mScreenshot->getHeight() + 12, Renderer::getScreenWidth() * (mTheme->getListOffsetX() - 0.03), mTheme->getDescColor(), Renderer::SMALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,10 @@ void GuiGameList::updateTheme()
|
|||
{
|
||||
mList->setOffsetX(mTheme->getListOffsetX() * Renderer::getScreenWidth());
|
||||
mList->setTextOffsetX(mTheme->getListTextOffsetX() * Renderer::getScreenWidth());
|
||||
|
||||
mScreenshot->setOffsetX(mTheme->getGameImageOffsetX() * Renderer::getScreenWidth());
|
||||
mScreenshot->setOffsetY(mTheme->getGameImageOffsetY() * Renderer::getScreenHeight());
|
||||
mScreenshot->setOrigin(mTheme->getGameImageOriginX(), mTheme->getGameImageOriginY());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ int GuiList<listType>::getSelection()
|
|||
template <typename listType>
|
||||
bool GuiList<listType>::isScrolling()
|
||||
{
|
||||
return mScrolling;
|
||||
return mScrollDir != 0;
|
||||
}
|
||||
|
||||
template <typename listType>
|
||||
|
|
|
@ -15,9 +15,7 @@ int GuiTheme::getFastSelectColor() { return mFastSelectColor; }
|
|||
bool GuiTheme::getHeaderHidden() { return mHideHeader; }
|
||||
bool GuiTheme::getDividersHidden() { return mHideDividers; }
|
||||
bool GuiTheme::getListCentered() { return mListCentered; }
|
||||
|
||||
float GuiTheme::getListOffsetX() { return mListOffsetX; }
|
||||
float GuiTheme::getGameImageOffsetY() { return mGameImageOffsetY; }
|
||||
float GuiTheme::getListTextOffsetX() { return mListTextOffsetX; }
|
||||
|
||||
int GuiTheme::getSelectedTextColor() { return mListSelectedColor; }
|
||||
|
@ -29,6 +27,13 @@ Sound* GuiTheme::getMenuSelectSound() { return &mMenuSelectSound; }
|
|||
Sound* GuiTheme::getMenuBackSound() { return &mMenuBackSound; }
|
||||
Sound* GuiTheme::getMenuOpenSound() { return &mMenuOpenSound; }
|
||||
|
||||
float GuiTheme::getGameImageOffsetX() { return mGameImageOffsetX; }
|
||||
float GuiTheme::getGameImageOffsetY() { return mGameImageOffsetY; }
|
||||
float GuiTheme::getGameImageWidth() { return mGameImageWidth; }
|
||||
float GuiTheme::getGameImageHeight() { return mGameImageHeight; }
|
||||
float GuiTheme::getGameImageOriginX() { return mGameImageOriginX; }
|
||||
float GuiTheme::getGameImageOriginY() { return mGameImageOriginY; }
|
||||
|
||||
GuiTheme::GuiTheme(std::string path)
|
||||
{
|
||||
setDefaults();
|
||||
|
@ -56,7 +61,13 @@ void GuiTheme::setDefaults()
|
|||
|
||||
mListOffsetX = 0.5;
|
||||
mListTextOffsetX = 0.005;
|
||||
|
||||
mGameImageOriginX = 0.5;
|
||||
mGameImageOriginY = 0;
|
||||
mGameImageOffsetX = mListOffsetX / 2;
|
||||
mGameImageOffsetY = (float)Renderer::getFontHeight(Renderer::LARGE) / Renderer::getScreenHeight();
|
||||
mGameImageWidth = 0;
|
||||
mGameImageHeight = 0;
|
||||
|
||||
mBoxData.backgroundPath = "";
|
||||
mBoxData.backgroundTiled = false;
|
||||
|
@ -122,7 +133,6 @@ void GuiTheme::readXML(std::string path)
|
|||
mFastSelectColor = resolveColor(root.child("fastSelectColor").text().get(), mFastSelectColor);
|
||||
mHideHeader = root.child("hideHeader");
|
||||
mHideDividers = root.child("hideDividers");
|
||||
mListCentered = !root.child("listLeftAlign");
|
||||
|
||||
//GuiBox theming data
|
||||
mBoxData.backgroundPath = expandPath(root.child("boxBackground").text().get());
|
||||
|
@ -133,20 +143,40 @@ void GuiTheme::readXML(std::string path)
|
|||
mBoxData.verticalTiled = root.child("boxVerticalTiled");
|
||||
mBoxData.cornerPath = expandPath(root.child("boxCorner").text().get());
|
||||
|
||||
//list stuff
|
||||
mListCentered = !root.child("listLeftAlign");
|
||||
mListOffsetX = strToFloat(root.child("listOffsetX").text().get(), mListOffsetX);
|
||||
mGameImageOffsetY = strToFloat(root.child("gameImageOffsetY").text().get(), mGameImageOffsetY);
|
||||
mListTextOffsetX = strToFloat(root.child("listTextOffsetX").text().get(), mListTextOffsetX);
|
||||
|
||||
|
||||
//game image stuff
|
||||
std::string artPos = root.child("gameImagePos").text().get();
|
||||
std::string artDim = root.child("gameImageDim").text().get();
|
||||
std::string artOrigin = root.child("gameImageOrigin").text().get();
|
||||
|
||||
std::string artPosX, artPosY, artWidth, artHeight, artOriginX, artOriginY;
|
||||
splitString(artPos, ' ', &artPosX, &artPosY);
|
||||
splitString(artDim, ' ', &artWidth, &artHeight);
|
||||
splitString(artOrigin, ' ', &artOriginX, &artOriginY);
|
||||
|
||||
mGameImageOffsetX = resolveExp(artPosX, mGameImageOffsetX);
|
||||
mGameImageOffsetY = resolveExp(artPosY, mGameImageOffsetY);
|
||||
mGameImageWidth = resolveExp(artWidth, mGameImageWidth);
|
||||
mGameImageHeight = resolveExp(artHeight, mGameImageHeight);
|
||||
mGameImageOriginX = resolveExp(artOriginX, mGameImageOriginX);
|
||||
mGameImageOriginY = resolveExp(artOriginY, mGameImageOriginY);
|
||||
|
||||
//sounds
|
||||
mMenuScrollSound.loadFile(expandPath(root.child("menuScrollSound").text().get()));
|
||||
mMenuSelectSound.loadFile(expandPath(root.child("menuSelectSound").text().get()));
|
||||
mMenuBackSound.loadFile(expandPath(root.child("menuBackSound").text().get()));
|
||||
mMenuOpenSound.loadFile(expandPath(root.child("menuOpenSound").text().get()));
|
||||
|
||||
//recursively create children for all <components> with proper parenting
|
||||
//actually read the components
|
||||
createComponentChildren(root, this);
|
||||
}
|
||||
|
||||
//recursively creates components (with proper parenting)
|
||||
void GuiTheme::createComponentChildren(pugi::xml_node node, GuiComponent* parent)
|
||||
{
|
||||
for(pugi::xml_node data = node.child("component"); data; data = data.next_sibling("component"))
|
||||
|
@ -158,6 +188,7 @@ void GuiTheme::createComponentChildren(pugi::xml_node node, GuiComponent* parent
|
|||
}
|
||||
}
|
||||
|
||||
//takes an XML element definition and creates an object from it
|
||||
GuiComponent* GuiTheme::createElement(pugi::xml_node data, GuiComponent* parent)
|
||||
{
|
||||
std::string type = data.child("type").text().get();
|
||||
|
@ -188,8 +219,6 @@ GuiComponent* GuiTheme::createElement(pugi::xml_node data, GuiComponent* parent)
|
|||
std::string originX, originY;
|
||||
splitString(origin, ' ', &originX, &originY);
|
||||
|
||||
//std::cout << "image, x: " << posX << " y: " << posY << " w: " << dimW << " h: " << dimH << " ox: " << originX << " oy: " << originY << " tiled: " << tiled << "\n";
|
||||
|
||||
//resolve to pixels from percentages/variables
|
||||
int x = resolveExp(posX) * Renderer::getScreenWidth();
|
||||
int y = resolveExp(posY) * Renderer::getScreenHeight();
|
||||
|
@ -210,10 +239,11 @@ GuiComponent* GuiTheme::createElement(pugi::xml_node data, GuiComponent* parent)
|
|||
}
|
||||
|
||||
|
||||
std::cerr << "Type \"" << type << "\" unknown!\n";
|
||||
std::cerr << "Theme component type \"" << type << "\" unknown!\n";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//expands a file path (./ becomes the directory of this theme file, ~/ becomes $HOME/)
|
||||
std::string GuiTheme::expandPath(std::string path)
|
||||
{
|
||||
if(path[0] == '~')
|
||||
|
@ -224,18 +254,23 @@ std::string GuiTheme::expandPath(std::string path)
|
|||
return path;
|
||||
}
|
||||
|
||||
float GuiTheme::resolveExp(std::string str)
|
||||
//takes a string containing a mathematical expression (possibly including variables) and resolves it to a float value
|
||||
float GuiTheme::resolveExp(std::string str, float defaultVal)
|
||||
{
|
||||
if(str.empty())
|
||||
return defaultVal;
|
||||
|
||||
MathExp exp;
|
||||
exp.setExpression(str);
|
||||
|
||||
//set variables
|
||||
exp.setVariable("headerHeight", Renderer::getFontHeight(Renderer::LARGE) / Renderer::getScreenHeight());
|
||||
exp.setVariable("infoWidth", GuiGameList::sInfoWidth);
|
||||
exp.setVariable("infoWidth", mListOffsetX);
|
||||
|
||||
return exp.eval();
|
||||
}
|
||||
|
||||
//takes a string of hex and resolves it to an integer
|
||||
int GuiTheme::resolveColor(std::string str, int defaultColor)
|
||||
{
|
||||
if(str.empty())
|
||||
|
@ -249,16 +284,23 @@ int GuiTheme::resolveColor(std::string str, int defaultColor)
|
|||
return ret;
|
||||
}
|
||||
|
||||
//splits a string in two at the first instance of the delimiter
|
||||
void GuiTheme::splitString(std::string str, char delim, std::string* before, std::string* after)
|
||||
{
|
||||
if(str.empty())
|
||||
return;
|
||||
|
||||
size_t split = str.find(delim);
|
||||
*before = str.substr(0, split);
|
||||
*after = str.substr(split + 1, str.length() - split - 1);
|
||||
if(split != std::string::npos)
|
||||
{
|
||||
*before = str.substr(0, split);
|
||||
*after = str.substr(split + 1, str.length() - split - 1);
|
||||
}else{
|
||||
std::cerr << " Error: tried to splt string \"" << str << "\" with delimiter '" << delim << "', but delimiter was not found!\n";
|
||||
}
|
||||
}
|
||||
|
||||
//converts a string to a float
|
||||
float GuiTheme::strToFloat(std::string str, float defaultVal)
|
||||
{
|
||||
if(str.empty())
|
||||
|
|
|
@ -27,7 +27,13 @@ public:
|
|||
|
||||
float getListOffsetX();
|
||||
float getListTextOffsetX();
|
||||
|
||||
float getGameImageOffsetX();
|
||||
float getGameImageOffsetY();
|
||||
float getGameImageWidth();
|
||||
float getGameImageHeight();
|
||||
float getGameImageOriginX();
|
||||
float getGameImageOriginY();
|
||||
|
||||
GuiBoxData getBoxData();
|
||||
|
||||
|
@ -43,7 +49,7 @@ private:
|
|||
|
||||
//utility functions
|
||||
std::string expandPath(std::string path);
|
||||
float resolveExp(std::string str);
|
||||
float resolveExp(std::string str, float defaultVal = 0.0);
|
||||
int resolveColor(std::string str, int defaultColor = 0x000000);
|
||||
void splitString(std::string str, char delim, std::string* before, std::string* after);
|
||||
float strToFloat(std::string str, float defaultVal = 0.0f);
|
||||
|
@ -53,7 +59,7 @@ private:
|
|||
int mListPrimaryColor, mListSecondaryColor, mListSelectorColor, mListSelectedColor, mDescColor, mFastSelectColor;
|
||||
bool mHideHeader, mHideDividers, mListCentered;
|
||||
|
||||
float mListOffsetX, mGameImageOffsetY, mListTextOffsetX;
|
||||
float mListOffsetX, mListTextOffsetX, mGameImageOffsetX, mGameImageOffsetY, mGameImageWidth, mGameImageHeight, mGameImageOriginX, mGameImageOriginY;
|
||||
|
||||
GuiBoxData mBoxData;
|
||||
|
||||
|
|
Loading…
Reference in a new issue