mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 14:15:38 +00:00
Grid updates. Animate scrolling option, autoLayout option, center selection option, scroll loop option, image source option
This commit is contained in:
parent
cb0db38f54
commit
961571655b
19
THEMES.md
19
THEMES.md
|
@ -538,6 +538,7 @@ Reference
|
|||
## Types of properties:
|
||||
|
||||
* NORMALIZED_PAIR - two decimals, in the range [0..1], delimited by a space. For example, `0.25 0.5`. Most commonly used for position (x and y coordinates) and size (width and height).
|
||||
* NORMALIZED_RECT - four decimals, in the range [0..1], delimited by a space. For example, `0.25 0.5 0.10 0.30`. Most commonly used for padding to store top, left, bottom and right coordinates.
|
||||
* PATH - a path. If the first character is a `~`, it will be expanded into the environment variable for the home path (`$HOME` for Linux or `%HOMEPATH%` for Windows). If the first character is a `.`, it will be expanded to the theme file's directory, allowing you to specify resources relative to the theme file, like so: `./../general_art/myfont.ttf`.
|
||||
* BOOLEAN - `true`/`1` or `false`/`0`.
|
||||
* COLOR - a hexidecimal RGB or RGBA color (6 or 8 digits). If 6 digits, will assume the alpha channel is `FF` (not transparent).
|
||||
|
@ -586,13 +587,29 @@ Can be created as an extra.
|
|||
* `pos` - type: NORMALIZED_PAIR.
|
||||
* `size` - type: NORMALIZED_PAIR.
|
||||
- The size of the grid. Take care the selected tile can go out of the grid size, so don't position the grid too close to another element or the screen border.
|
||||
* `margin` - type: NORMALIZED_PAIR.
|
||||
* `margin` - type: NORMALIZED_PAIR. Margin between tiles.
|
||||
* `padding` - type: NORMALIZED_RECT.
|
||||
- NEW : Padding for displaying tiles.
|
||||
* `autoLayout` - type: NORMALIZED_PAIR.
|
||||
- NEW : Number of column and rows in the grid (integer values).
|
||||
* `autoLayoutSelectedZoom` - type: FLOAT.
|
||||
- NEW : Zoom factor to apply when a tile is selected.
|
||||
* `gameImage` - type: PATH.
|
||||
- The default image used for games which doesn't have an image.
|
||||
* `folderImage` - type: PATH.
|
||||
- The default image used for folders which doesn't have an image.
|
||||
* `imageSource` - type: STRING.
|
||||
- Selects the image to display. `thumbnail` by default, can also be set to `image` or `marquee`.
|
||||
* `scrollDirection` - type: STRING.
|
||||
- `vertical` by default, can also be set to `horizontal`. Not that in `horizontal` mod, the tiles are ordered from top to bottom, then from left to right.
|
||||
* `centerSelection` - type: BOOLEAN.
|
||||
- `false` by default, when `true` the selected tile will be locked to the center of the grid.
|
||||
* `scrollLoop` - type: BOOLEAN.
|
||||
- `false` by default, when `true` the grid will seamlessly loop around when scrolling reaches the end of the list. Only works when `centerSelection` is `true`.
|
||||
* `animate` - type : BOOLEAN.
|
||||
- `true` by default, when `false` the grid scrolling will not be animated.
|
||||
* `zIndex` - type: FLOAT.
|
||||
- z-index value for component. Components will be rendered in order of z-index value from low to high.
|
||||
|
||||
#### gridtile
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ void CollectionSystemManager::loadCollectionSystems()
|
|||
void CollectionSystemManager::loadEnabledListFromSettings()
|
||||
{
|
||||
// we parse the auto collection settings list
|
||||
std::vector<std::string> autoSelected = Utils::String::commaStringToVector(Settings::getInstance()->getString("CollectionSystemsAuto"));
|
||||
std::vector<std::string> autoSelected = Utils::String::commaStringToVector(Settings::getInstance()->getString("CollectionSystemsAuto"), true);
|
||||
|
||||
// iterate the map
|
||||
for(std::map<std::string, CollectionSystemData>::iterator it = mAutoCollectionSystemsData.begin() ; it != mAutoCollectionSystemsData.end() ; it++ )
|
||||
|
@ -153,7 +153,7 @@ void CollectionSystemManager::loadEnabledListFromSettings()
|
|||
}
|
||||
|
||||
// we parse the custom collection settings list
|
||||
std::vector<std::string> customSelected = Utils::String::commaStringToVector(Settings::getInstance()->getString("CollectionSystemsCustom"));
|
||||
std::vector<std::string> customSelected = Utils::String::commaStringToVector(Settings::getInstance()->getString("CollectionSystemsCustom"), true);
|
||||
|
||||
// iterate the map
|
||||
for(std::map<std::string, CollectionSystemData>::iterator it = mCustomCollectionSystemsData.begin() ; it != mCustomCollectionSystemsData.end() ; it++ )
|
||||
|
|
|
@ -110,6 +110,18 @@ bool GridGameListView::input(InputConfig* config, Input input)
|
|||
return ISimpleGameListView::input(config, input);
|
||||
}
|
||||
|
||||
const std::string GridGameListView::getImagePath(FileData* file)
|
||||
{
|
||||
ImageSource src = mGrid.getImageSource();
|
||||
|
||||
if (src == ImageSource::IMAGE)
|
||||
return file->getImagePath();
|
||||
else if (src == ImageSource::MARQUEE)
|
||||
return file->getMarqueePath();
|
||||
|
||||
return file->getThumbnailPath();
|
||||
}
|
||||
|
||||
void GridGameListView::populateList(const std::vector<FileData*>& files)
|
||||
{
|
||||
mGrid.clear();
|
||||
|
@ -118,7 +130,7 @@ void GridGameListView::populateList(const std::vector<FileData*>& files)
|
|||
{
|
||||
for (auto it = files.cbegin(); it != files.cend(); it++)
|
||||
{
|
||||
mGrid.add((*it)->getName(), (*it)->getThumbnailPath(), *it);
|
||||
mGrid.add((*it)->getName(), getImagePath(*it), *it);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -36,6 +36,7 @@ protected:
|
|||
|
||||
private:
|
||||
void updateInfoPanel();
|
||||
const std::string getImagePath(FileData* file);
|
||||
|
||||
void initMDLabels();
|
||||
void initMDValues();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "components/ImageComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "utils/StringUtil.h"
|
||||
#include "Log.h"
|
||||
#include "platform.h"
|
||||
#include "Settings.h"
|
||||
|
@ -32,9 +33,16 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>> The
|
|||
{ "pos", NORMALIZED_PAIR },
|
||||
{ "size", NORMALIZED_PAIR },
|
||||
{ "margin", NORMALIZED_PAIR },
|
||||
{ "padding", NORMALIZED_RECT },
|
||||
{ "autoLayout", NORMALIZED_PAIR },
|
||||
{ "autoLayoutSelectedZoom", FLOAT },
|
||||
{ "gameImage", PATH },
|
||||
{ "folderImage", PATH },
|
||||
{ "scrollDirection", STRING } } },
|
||||
{ "imageSource", STRING },
|
||||
{ "scrollDirection", STRING },
|
||||
{ "centerSelection", BOOLEAN },
|
||||
{ "scrollLoop", BOOLEAN },
|
||||
{ "zIndex", FLOAT } } },
|
||||
{ "gridtile", {
|
||||
{ "size", NORMALIZED_PAIR },
|
||||
{ "padding", NORMALIZED_PAIR },
|
||||
|
@ -410,6 +418,25 @@ void ThemeData::parseElement(const pugi::xml_node& root, const std::map<std::str
|
|||
|
||||
switch(typeIt->second)
|
||||
{
|
||||
case NORMALIZED_RECT:
|
||||
{
|
||||
Vector4f val;
|
||||
|
||||
auto splits = Utils::String::delimitedStringToVector(str, " ");
|
||||
if (splits.size() == 2)
|
||||
{
|
||||
val = Vector4f((float)atof(splits.at(0).c_str()), (float)atof(splits.at(1).c_str()),
|
||||
(float)atof(splits.at(0).c_str()), (float)atof(splits.at(1).c_str()));
|
||||
}
|
||||
else if (splits.size() == 4)
|
||||
{
|
||||
val = Vector4f((float)atof(splits.at(0).c_str()), (float)atof(splits.at(1).c_str()),
|
||||
(float)atof(splits.at(2).c_str()), (float)atof(splits.at(3).c_str()));
|
||||
}
|
||||
|
||||
element.properties[node.name()] = val;
|
||||
break;
|
||||
}
|
||||
case NORMALIZED_PAIR:
|
||||
{
|
||||
size_t divider = str.find(' ');
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#define ES_CORE_THEME_DATA_H
|
||||
|
||||
#include "math/Vector2f.h"
|
||||
#include "math/Vector4f.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
@ -94,12 +95,14 @@ public:
|
|||
|
||||
struct Property
|
||||
{
|
||||
void operator= (const Vector4f& value) { r = value; v = Vector2f(value.x(), value.y()); }
|
||||
void operator= (const Vector2f& value) { v = value; }
|
||||
void operator= (const std::string& value) { s = value; }
|
||||
void operator= (const unsigned int& value) { i = value; }
|
||||
void operator= (const float& value) { f = value; }
|
||||
void operator= (const bool& value) { b = value; }
|
||||
|
||||
Vector4f r;
|
||||
Vector2f v;
|
||||
std::string s;
|
||||
unsigned int i;
|
||||
|
@ -117,6 +120,7 @@ public:
|
|||
else if(std::is_same<T, unsigned int>::value) return *(const T*)&properties.at(prop).i;
|
||||
else if(std::is_same<T, float>::value) return *(const T*)&properties.at(prop).f;
|
||||
else if(std::is_same<T, bool>::value) return *(const T*)&properties.at(prop).b;
|
||||
else if(std::is_same<T, Vector4f>::value) return *(const T*)&properties.at(prop).r;
|
||||
return T();
|
||||
}
|
||||
|
||||
|
@ -140,6 +144,7 @@ public:
|
|||
|
||||
enum ElementPropertyType
|
||||
{
|
||||
NORMALIZED_RECT,
|
||||
NORMALIZED_PAIR,
|
||||
PATH,
|
||||
STRING,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "GridTileComponent.h"
|
||||
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "resources/TextureResource.h"
|
||||
#include "ThemeData.h"
|
||||
|
||||
|
@ -29,7 +30,9 @@ GridTileComponent::GridTileComponent(Window* window) : GuiComponent(window), mBa
|
|||
addChild(&mBackground);
|
||||
addChild(&(*mImage));
|
||||
|
||||
setSelected(false);
|
||||
mSelectedZoomPercent = 0;
|
||||
|
||||
setSelected(false, false);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
|
@ -42,19 +45,53 @@ void GridTileComponent::render(const Transform4x4f& parentTrans)
|
|||
}
|
||||
|
||||
// Update all the tile properties to the new status (selected or default)
|
||||
void GridTileComponent::update()
|
||||
void GridTileComponent::update(int deltaTime)
|
||||
{
|
||||
const GridTileProperties& currentProperties = getCurrentProperties();
|
||||
GuiComponent::update(deltaTime);
|
||||
|
||||
mBackground.setImagePath(currentProperties.mBackgroundImage);
|
||||
calcCurrentProperties();
|
||||
|
||||
mImage->setColorShift(currentProperties.mImageColor);
|
||||
mBackground.setCenterColor(currentProperties.mBackgroundCenterColor);
|
||||
mBackground.setEdgeColor(currentProperties.mBackgroundEdgeColor);
|
||||
mBackground.setImagePath(mCurrentProperties.mBackgroundImage);
|
||||
|
||||
mImage->setColorShift(mCurrentProperties.mImageColor);
|
||||
mBackground.setCenterColor(mCurrentProperties.mBackgroundCenterColor);
|
||||
mBackground.setEdgeColor(mCurrentProperties.mBackgroundEdgeColor);
|
||||
|
||||
resize();
|
||||
}
|
||||
|
||||
void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties properties)
|
||||
{
|
||||
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
|
||||
if (elem->has("size"))
|
||||
properties.mSize = elem->get<Vector2f>("size") * screen;
|
||||
|
||||
if (elem->has("padding"))
|
||||
properties.mPadding = elem->get<Vector2f>("padding");
|
||||
|
||||
if (elem->has("imageColor"))
|
||||
properties.mImageColor = elem->get<unsigned int>("imageColor");
|
||||
|
||||
if (elem->has("backgroundImage"))
|
||||
properties.mBackgroundImage = elem->get<std::string>("backgroundImage");
|
||||
|
||||
if (elem->has("backgroundCornerSize"))
|
||||
properties.mBackgroundCornerSize = elem->get<Vector2f>("backgroundCornerSize");
|
||||
|
||||
if (elem->has("backgroundColor"))
|
||||
{
|
||||
properties.mBackgroundCenterColor = elem->get<unsigned int>("backgroundColor");
|
||||
properties.mBackgroundEdgeColor = elem->get<unsigned int>("backgroundColor");
|
||||
}
|
||||
|
||||
if (elem->has("backgroundCenterColor"))
|
||||
properties.mBackgroundCenterColor = elem->get<unsigned int>("backgroundCenterColor");
|
||||
|
||||
if (elem->has("backgroundEdgeColor"))
|
||||
properties.mBackgroundEdgeColor = elem->get<unsigned int>("backgroundEdgeColor");
|
||||
}
|
||||
|
||||
void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& /*element*/, unsigned int /*properties*/)
|
||||
{
|
||||
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
|
@ -62,70 +99,20 @@ void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, cons
|
|||
// Apply theme to the default gridtile
|
||||
const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile");
|
||||
if (elem)
|
||||
{
|
||||
if (elem->has("size"))
|
||||
mDefaultProperties.mSize = elem->get<Vector2f>("size") * screen;
|
||||
|
||||
if (elem->has("padding"))
|
||||
mDefaultProperties.mPadding = elem->get<Vector2f>("padding");
|
||||
|
||||
if (elem->has("imageColor"))
|
||||
mDefaultProperties.mImageColor = elem->get<unsigned int>("imageColor");
|
||||
|
||||
if (elem->has("backgroundImage"))
|
||||
mDefaultProperties.mBackgroundImage = elem->get<std::string>("backgroundImage");
|
||||
|
||||
if (elem->has("backgroundCornerSize"))
|
||||
mDefaultProperties.mBackgroundCornerSize = elem->get<Vector2f>("backgroundCornerSize");
|
||||
|
||||
if (elem->has("backgroundColor"))
|
||||
{
|
||||
mDefaultProperties.mBackgroundCenterColor = elem->get<unsigned int>("backgroundColor");
|
||||
mDefaultProperties.mBackgroundEdgeColor = elem->get<unsigned int>("backgroundColor");
|
||||
}
|
||||
|
||||
if (elem->has("backgroundCenterColor"))
|
||||
mDefaultProperties.mBackgroundCenterColor = elem->get<unsigned int>("backgroundCenterColor");
|
||||
|
||||
if (elem->has("backgroundEdgeColor"))
|
||||
mDefaultProperties.mBackgroundEdgeColor = elem->get<unsigned int>("backgroundEdgeColor");
|
||||
}
|
||||
applyThemeToProperties(elem, mDefaultProperties);
|
||||
|
||||
// Apply theme to the selected gridtile
|
||||
// NOTE that some of the default gridtile properties influence on the selected gridtile properties
|
||||
// See THEMES.md for more informations
|
||||
elem = theme->getElement(view, "selected", "gridtile");
|
||||
|
||||
mSelectedProperties.mSize = elem && elem->has("size") ?
|
||||
elem->get<Vector2f>("size") * screen :
|
||||
getSelectedTileSize();
|
||||
mSelectedProperties.mSize = getSelectedTileSize();
|
||||
mSelectedProperties.mPadding = mDefaultProperties.mPadding;
|
||||
mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage;
|
||||
mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize;
|
||||
|
||||
mSelectedProperties.mPadding = elem && elem->has("padding") ?
|
||||
elem->get<Vector2f>("padding") :
|
||||
mDefaultProperties.mPadding;
|
||||
|
||||
if (elem && elem->has("imageColor"))
|
||||
mSelectedProperties.mImageColor = elem->get<unsigned int>("imageColor");
|
||||
|
||||
mSelectedProperties.mBackgroundImage = elem && elem->has("backgroundImage") ?
|
||||
elem->get<std::string>("backgroundImage") :
|
||||
mDefaultProperties.mBackgroundImage;
|
||||
|
||||
mSelectedProperties.mBackgroundCornerSize = elem && elem->has("backgroundCornerSize") ?
|
||||
elem->get<Vector2f>("backgroundCornerSize") :
|
||||
mDefaultProperties.mBackgroundCornerSize;
|
||||
|
||||
if (elem && elem->has("backgroundColor"))
|
||||
{
|
||||
mSelectedProperties.mBackgroundCenterColor = elem->get<unsigned int>("backgroundColor");
|
||||
mSelectedProperties.mBackgroundEdgeColor = elem->get<unsigned int>("backgroundColor");
|
||||
}
|
||||
|
||||
if (elem && elem->has("backgroundCenterColor"))
|
||||
mSelectedProperties.mBackgroundCenterColor = elem->get<unsigned int>("backgroundCenterColor");
|
||||
|
||||
if (elem && elem->has("backgroundEdgeColor"))
|
||||
mSelectedProperties.mBackgroundEdgeColor = elem->get<unsigned int>("backgroundEdgeColor");
|
||||
if (elem)
|
||||
applyThemeToProperties(elem, mSelectedProperties);
|
||||
}
|
||||
|
||||
// Made this a static function because the ImageGridComponent need to know the default tile size
|
||||
|
@ -147,6 +134,11 @@ bool GridTileComponent::isSelected() const
|
|||
return mSelected;
|
||||
}
|
||||
|
||||
void GridTileComponent::reset()
|
||||
{
|
||||
setImage("");
|
||||
}
|
||||
|
||||
void GridTileComponent::setImage(const std::string& path)
|
||||
{
|
||||
mImage->setImage(path);
|
||||
|
@ -163,9 +155,80 @@ void GridTileComponent::setImage(const std::shared_ptr<TextureResource>& texture
|
|||
resize();
|
||||
}
|
||||
|
||||
void GridTileComponent::setSelected(bool selected)
|
||||
void GridTileComponent::setSelected(bool selected, bool allowAnimation, Vector3f* pPosition, bool force)
|
||||
{
|
||||
if (mSelected == selected && !force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mSelected = selected;
|
||||
|
||||
if (selected)
|
||||
{
|
||||
if (pPosition == NULL || !allowAnimation)
|
||||
{
|
||||
cancelAnimation(3);
|
||||
|
||||
this->setSelectedZoom(1);
|
||||
mAnimPosition = Vector3f(0, 0, 0);
|
||||
|
||||
resize();
|
||||
}
|
||||
else
|
||||
{
|
||||
mAnimPosition = Vector3f(pPosition->x(), pPosition->y(), pPosition->z());
|
||||
|
||||
auto func = [this](float t)
|
||||
{
|
||||
t -= 1; // cubic ease out
|
||||
float pct = Math::lerp(0, 1, t*t*t + 1);
|
||||
|
||||
this->setSelectedZoom(pct);
|
||||
};
|
||||
|
||||
cancelAnimation(3);
|
||||
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
||||
this->setSelectedZoom(1);
|
||||
mAnimPosition = Vector3f(0, 0, 0);
|
||||
}, false, 3);
|
||||
}
|
||||
}
|
||||
else // if (!selected)
|
||||
{
|
||||
if (!allowAnimation)
|
||||
{
|
||||
cancelAnimation(3);
|
||||
this->setSelectedZoom(0);
|
||||
|
||||
resize();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setSelectedZoom(1);
|
||||
|
||||
auto func = [this](float t)
|
||||
{
|
||||
t -= 1; // cubic ease out
|
||||
float pct = Math::lerp(0, 1, t*t*t + 1);
|
||||
this->setSelectedZoom(1.0 - pct);
|
||||
};
|
||||
|
||||
cancelAnimation(3);
|
||||
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
||||
this->setSelectedZoom(0);
|
||||
}, false, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridTileComponent::setSelectedZoom(float percent)
|
||||
{
|
||||
if (mSelectedZoomPercent == percent)
|
||||
return;
|
||||
|
||||
mSelectedZoomPercent = percent;
|
||||
resize();
|
||||
}
|
||||
|
||||
void GridTileComponent::setVisible(bool visible)
|
||||
|
@ -175,14 +238,86 @@ void GridTileComponent::setVisible(bool visible)
|
|||
|
||||
void GridTileComponent::resize()
|
||||
{
|
||||
const GridTileProperties& currentProperties = getCurrentProperties();
|
||||
calcCurrentProperties();
|
||||
|
||||
mImage->setMaxSize(currentProperties.mSize - currentProperties.mPadding * 2);
|
||||
mBackground.setCornerSize(currentProperties.mBackgroundCornerSize);
|
||||
mBackground.fitTo(currentProperties.mSize - mBackground.getCornerSize() * 2);
|
||||
mImage->setMaxSize(mCurrentProperties.mSize - mCurrentProperties.mPadding * 2);
|
||||
mBackground.setCornerSize(mCurrentProperties.mBackgroundCornerSize);
|
||||
mBackground.fitTo(mCurrentProperties.mSize - mBackground.getCornerSize() * 2);
|
||||
}
|
||||
|
||||
const GridTileProperties& GridTileComponent::getCurrentProperties() const
|
||||
unsigned int mixColors(unsigned int first, unsigned int second, float percent)
|
||||
{
|
||||
return mSelected ? mSelectedProperties : mDefaultProperties;
|
||||
unsigned char alpha0 = (first >> 24) & 0xFF;
|
||||
unsigned char blue0 = (first >> 16) & 0xFF;
|
||||
unsigned char green0 = (first >> 8) & 0xFF;
|
||||
unsigned char red0 = first & 0xFF;
|
||||
|
||||
unsigned char alpha1 = (second >> 24) & 0xFF;
|
||||
unsigned char blue1 = (second >> 16) & 0xFF;
|
||||
unsigned char green1 = (second >> 8) & 0xFF;
|
||||
unsigned char red1 = second & 0xFF;
|
||||
|
||||
unsigned char alpha = (unsigned char)(alpha0 * (1.0 - percent) + alpha1 * percent);
|
||||
unsigned char blue = (unsigned char)(blue0 * (1.0 - percent) + blue1 * percent);
|
||||
unsigned char green = (unsigned char)(green0 * (1.0 - percent) + green1 * percent);
|
||||
unsigned char red = (unsigned char)(red0 * (1.0 - percent) + red1 * percent);
|
||||
|
||||
return (alpha << 24) | (blue << 16) | (green << 8) | red;
|
||||
}
|
||||
|
||||
void GridTileComponent::calcCurrentProperties()
|
||||
{
|
||||
mCurrentProperties = mSelected ? mSelectedProperties : mDefaultProperties;
|
||||
|
||||
float zoomPercentInverse = 1.0 - mSelectedZoomPercent;
|
||||
|
||||
if (mSelectedZoomPercent != 0.0f && mSelectedZoomPercent != 1.0f) {
|
||||
if (mDefaultProperties.mSize != mSelectedProperties.mSize) {
|
||||
mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse + mSelectedProperties.mSize * mSelectedZoomPercent;
|
||||
}
|
||||
|
||||
if (mDefaultProperties.mPadding != mSelectedProperties.mPadding)
|
||||
{
|
||||
mCurrentProperties.mPadding = mDefaultProperties.mPadding * zoomPercentInverse + mSelectedProperties.mPadding * mSelectedZoomPercent;
|
||||
}
|
||||
|
||||
if (mDefaultProperties.mImageColor != mSelectedProperties.mImageColor)
|
||||
{
|
||||
mCurrentProperties.mImageColor = mixColors(mDefaultProperties.mImageColor, mSelectedProperties.mImageColor, mSelectedZoomPercent);
|
||||
}
|
||||
|
||||
if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize)
|
||||
{
|
||||
mCurrentProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize * zoomPercentInverse + mSelectedProperties.mBackgroundCornerSize * mSelectedZoomPercent;
|
||||
}
|
||||
|
||||
if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor)
|
||||
{
|
||||
mCurrentProperties.mBackgroundCenterColor = mixColors(mDefaultProperties.mBackgroundCenterColor, mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent);
|
||||
}
|
||||
|
||||
if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor)
|
||||
{
|
||||
mCurrentProperties.mBackgroundEdgeColor = mixColors(mDefaultProperties.mBackgroundEdgeColor, mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3f GridTileComponent::getBackgroundPosition()
|
||||
{
|
||||
return mBackground.getPosition() + mPosition;
|
||||
}
|
||||
|
||||
std::shared_ptr<TextureResource> GridTileComponent::getTexture()
|
||||
{
|
||||
if (mImage != nullptr)
|
||||
return mImage->getTexture();
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
void GridTileComponent::forceSize(Vector2f size, float selectedZoom)
|
||||
{
|
||||
mDefaultProperties.mSize = size;
|
||||
mSelectedProperties.mSize = size * selectedZoom;
|
||||
}
|
|
@ -22,7 +22,6 @@ public:
|
|||
GridTileComponent(Window* window);
|
||||
|
||||
void render(const Transform4x4f& parentTrans) override;
|
||||
void update();
|
||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties);
|
||||
|
||||
// Made this a static function because the ImageGridComponent need to know the default tile max size
|
||||
|
@ -31,23 +30,38 @@ public:
|
|||
Vector2f getSelectedTileSize() const;
|
||||
bool isSelected() const;
|
||||
|
||||
void reset();
|
||||
|
||||
void setImage(const std::string& path);
|
||||
void setImage(const std::shared_ptr<TextureResource>& texture);
|
||||
void setSelected(bool selected);
|
||||
void setSelected(bool selected, bool allowAnimation = true, Vector3f* pPosition = NULL, bool force=false);
|
||||
void setVisible(bool visible);
|
||||
|
||||
void forceSize(Vector2f size, float selectedZoom);
|
||||
|
||||
Vector3f getBackgroundPosition();
|
||||
|
||||
virtual void update(int deltaTime);
|
||||
|
||||
std::shared_ptr<TextureResource> getTexture();
|
||||
|
||||
private:
|
||||
void resize();
|
||||
const GridTileProperties& getCurrentProperties() const;
|
||||
void calcCurrentProperties();
|
||||
void setSelectedZoom(float percent);
|
||||
|
||||
std::shared_ptr<ImageComponent> mImage;
|
||||
NinePatchComponent mBackground;
|
||||
|
||||
GridTileProperties mDefaultProperties;
|
||||
GridTileProperties mSelectedProperties;
|
||||
GridTileProperties mCurrentProperties;
|
||||
|
||||
float mSelectedZoomPercent;
|
||||
bool mSelected;
|
||||
bool mVisible;
|
||||
|
||||
Vector3f mAnimPosition;
|
||||
};
|
||||
|
||||
#endif // ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
|
||||
|
|
|
@ -75,6 +75,8 @@ public:
|
|||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
|
||||
|
||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||
|
||||
std::shared_ptr<TextureResource> getTexture() { return mTexture; };
|
||||
private:
|
||||
Vector2f mTargetSize;
|
||||
|
||||
|
|
|
@ -3,16 +3,26 @@
|
|||
#define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
||||
|
||||
#include "Log.h"
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "components/IList.h"
|
||||
#include "resources/TextureResource.h"
|
||||
#include "GridTileComponent.h"
|
||||
|
||||
#define EXTRAITEMS 2
|
||||
|
||||
enum ScrollDirection
|
||||
{
|
||||
SCROLL_VERTICALLY,
|
||||
SCROLL_HORIZONTALLY
|
||||
};
|
||||
|
||||
enum ImageSource
|
||||
{
|
||||
THUMBNAIL,
|
||||
IMAGE,
|
||||
MARQUEE
|
||||
};
|
||||
|
||||
struct ImageGridData
|
||||
{
|
||||
std::string texturePath;
|
||||
|
@ -50,20 +60,23 @@ public:
|
|||
void onSizeChanged() override;
|
||||
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
|
||||
|
||||
ImageSource getImageSource() { return mImageSource; };
|
||||
|
||||
protected:
|
||||
virtual void onCursorChanged(const CursorState& state) override;
|
||||
|
||||
private:
|
||||
// TILES
|
||||
void buildTiles();
|
||||
void updateTiles();
|
||||
void updateTileAtPos(int tilePos, int imgPos, int bufferTop, int bufferBot);
|
||||
int getStartPosition() const;
|
||||
void updateTiles(bool ascending = true, bool allowAnimation = true, bool updateSelectedState = true);
|
||||
void updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState);
|
||||
void calcGridDimension();
|
||||
bool isScrollLoop();
|
||||
|
||||
bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; };
|
||||
|
||||
|
||||
// IMAGES & ENTRIES
|
||||
const int texBuffersBehind[4] = { 1, 1, 1, 1 };
|
||||
const int texBuffersForward[4] = { 1, 2, 3, 3 };
|
||||
bool mEntriesDirty;
|
||||
int mLastCursor;
|
||||
std::string mDefaultGameTexture;
|
||||
|
@ -71,14 +84,27 @@ private:
|
|||
|
||||
// TILES
|
||||
bool mLastRowPartial;
|
||||
Vector2f mAutoLayout;
|
||||
float mAutoLayoutZoom;
|
||||
|
||||
Vector4f mPadding;
|
||||
Vector2f mMargin;
|
||||
Vector2f mTileSize;
|
||||
Vector2i mGridDimension;
|
||||
std::shared_ptr<ThemeData> mTheme;
|
||||
std::vector< std::shared_ptr<GridTileComponent> > mTiles;
|
||||
|
||||
int mStartPosition;
|
||||
|
||||
float mCamera;
|
||||
float mCameraDirection;
|
||||
|
||||
// MISCELLANEOUS
|
||||
bool mAnimate;
|
||||
bool mCenterSelection;
|
||||
bool mScrollLoop;
|
||||
ScrollDirection mScrollDirection;
|
||||
ImageSource mImageSource;
|
||||
std::function<void(CursorState state)> mCursorChangedCallback;
|
||||
};
|
||||
|
||||
|
@ -87,6 +113,14 @@ ImageGridComponent<T>::ImageGridComponent(Window* window) : IList<ImageGridData,
|
|||
{
|
||||
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
|
||||
mCamera = 0.0;
|
||||
mCameraDirection = 1.0;
|
||||
|
||||
mAutoLayout = Vector2f::Zero();
|
||||
mAutoLayoutZoom = 1.0;
|
||||
|
||||
mStartPosition = 0;
|
||||
|
||||
mEntriesDirty = true;
|
||||
mLastCursor = 0;
|
||||
mDefaultGameTexture = ":/cartridge.svg";
|
||||
|
@ -94,9 +128,14 @@ ImageGridComponent<T>::ImageGridComponent(Window* window) : IList<ImageGridData,
|
|||
|
||||
mSize = screen * 0.80f;
|
||||
mMargin = screen * 0.07f;
|
||||
mPadding = Vector4f::Zero();
|
||||
mTileSize = GridTileComponent::getDefaultTileSize();
|
||||
|
||||
mAnimate = true;
|
||||
mCenterSelection = false;
|
||||
mScrollLoop = false;
|
||||
mScrollDirection = SCROLL_VERTICALLY;
|
||||
mImageSource = THUMBNAIL;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -116,19 +155,24 @@ bool ImageGridComponent<T>::input(InputConfig* config, Input input)
|
|||
{
|
||||
if(input.value != 0)
|
||||
{
|
||||
int idx = isVertical() ? 0 : 1;
|
||||
|
||||
Vector2i dir = Vector2i::Zero();
|
||||
if(config->isMappedLike("up", input))
|
||||
dir[1 ^ mScrollDirection] = -1;
|
||||
dir[1 ^ idx] = -1;
|
||||
else if(config->isMappedLike("down", input))
|
||||
dir[1 ^ mScrollDirection] = 1;
|
||||
dir[1 ^ idx] = 1;
|
||||
else if(config->isMappedLike("left", input))
|
||||
dir[0 ^ mScrollDirection] = -1;
|
||||
dir[0 ^ idx] = -1;
|
||||
else if(config->isMappedLike("right", input))
|
||||
dir[0 ^ mScrollDirection] = 1;
|
||||
dir[0 ^ idx] = 1;
|
||||
|
||||
if(dir != Vector2i::Zero())
|
||||
{
|
||||
if (isVertical())
|
||||
listInput(dir.x() + dir.y() * mGridDimension.x());
|
||||
else
|
||||
listInput(dir.x() + dir.y() * mGridDimension.y());
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
|
@ -144,16 +188,23 @@ bool ImageGridComponent<T>::input(InputConfig* config, Input input)
|
|||
template<typename T>
|
||||
void ImageGridComponent<T>::update(int deltaTime)
|
||||
{
|
||||
GuiComponent::update(deltaTime);
|
||||
listUpdate(deltaTime);
|
||||
|
||||
for(auto it = mTiles.begin(); it != mTiles.end(); it++)
|
||||
(*it)->update();
|
||||
(*it)->update(deltaTime);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
||||
{
|
||||
Transform4x4f trans = getTransform() * parentTrans;
|
||||
Transform4x4f tileTrans = trans;
|
||||
|
||||
float offsetX = isVertical() ? 0.0f : mCamera * mCameraDirection * (mTileSize.x() + mMargin.x());
|
||||
float offsetY = isVertical() ? mCamera * mCameraDirection * (mTileSize.y() + mMargin.y()) : 0.0f;
|
||||
|
||||
tileTrans.translate(Vector3f(offsetX, offsetY, 0.0));
|
||||
|
||||
if(mEntriesDirty)
|
||||
{
|
||||
|
@ -180,14 +231,14 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
|||
if(tile->isSelected())
|
||||
selectedTile = tile;
|
||||
else
|
||||
tile->render(trans);
|
||||
tile->render(tileTrans);
|
||||
}
|
||||
|
||||
Renderer::popClipRect();
|
||||
|
||||
// Render the selected image on top of the others
|
||||
if (selectedTile != NULL)
|
||||
selectedTile->render(trans);
|
||||
selectedTile->render(tileTrans);
|
||||
|
||||
listRenderTitleOverlay(trans);
|
||||
|
||||
|
@ -211,9 +262,44 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
|||
if (elem->has("margin"))
|
||||
mMargin = elem->get<Vector2f>("margin") * screen;
|
||||
|
||||
if (elem->has("padding"))
|
||||
mPadding = elem->get<Vector4f>("padding") * Vector4f(screen.x(), screen.y(), screen.x(), screen.y());
|
||||
|
||||
if (elem->has("autoLayout"))
|
||||
mAutoLayout = elem->get<Vector2f>("autoLayout");
|
||||
|
||||
if (elem->has("autoLayoutSelectedZoom"))
|
||||
mAutoLayoutZoom = elem->get<float>("autoLayoutSelectedZoom");
|
||||
|
||||
if (elem->has("imageSource"))
|
||||
{
|
||||
auto direction = elem->get<std::string>("imageSource");
|
||||
if (direction == "image")
|
||||
mImageSource = IMAGE;
|
||||
else if (direction == "marquee")
|
||||
mImageSource = MARQUEE;
|
||||
else
|
||||
mImageSource = THUMBNAIL;
|
||||
}
|
||||
else
|
||||
mImageSource = THUMBNAIL;
|
||||
|
||||
if (elem->has("scrollDirection"))
|
||||
mScrollDirection = (ScrollDirection)(elem->get<std::string>("scrollDirection") == "horizontal");
|
||||
|
||||
if (elem->has("centerSelection"))
|
||||
{
|
||||
mCenterSelection = (elem->get<bool>("centerSelection"));
|
||||
|
||||
if (elem->has("scrollLoop"))
|
||||
mScrollLoop = (elem->get<bool>("scrollLoop"));
|
||||
}
|
||||
|
||||
if (elem->has("animate"))
|
||||
mAnimate = (elem->get<bool>("animate"));
|
||||
else
|
||||
mAnimate = true;
|
||||
|
||||
if (elem->has("gameImage"))
|
||||
{
|
||||
std::string path = elem->get<std::string>("gameImage");
|
||||
|
@ -283,39 +369,191 @@ void ImageGridComponent<T>::onSizeChanged()
|
|||
template<typename T>
|
||||
void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
|
||||
{
|
||||
updateTiles();
|
||||
if (mLastCursor == mCursor)
|
||||
{
|
||||
if (state == CURSOR_STOPPED && mCursorChangedCallback)
|
||||
mCursorChangedCallback(state);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool direction = mCursor >= mLastCursor;
|
||||
int diff = direction ? mCursor - mLastCursor : mLastCursor - mCursor;
|
||||
if (isScrollLoop() && diff == mEntries.size() - 1)
|
||||
{
|
||||
direction = !direction;
|
||||
}
|
||||
|
||||
int oldStart = mStartPosition;
|
||||
|
||||
int dimScrollable = (isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS;
|
||||
int dimOpposite = isVertical() ? mGridDimension.x() : mGridDimension.y();
|
||||
|
||||
int centralCol = (int)(dimScrollable - 0.5) / 2;
|
||||
int maxCentralCol = dimScrollable / 2;
|
||||
|
||||
int oldCol = (mLastCursor / dimOpposite);
|
||||
int col = (mCursor / dimOpposite);
|
||||
|
||||
int lastCol = ((mEntries.size() - 1) / dimOpposite);
|
||||
|
||||
int lastScroll = std::max(0, (lastCol + 1 - dimScrollable));
|
||||
|
||||
float startPos = 0;
|
||||
float endPos = 1;
|
||||
|
||||
if (((GuiComponent*)this)->isAnimationPlaying(2))
|
||||
{
|
||||
startPos = 0;
|
||||
((GuiComponent*)this)->cancelAnimation(2);
|
||||
updateTiles(direction, false, false);
|
||||
}
|
||||
|
||||
if (mAnimate) {
|
||||
|
||||
std::shared_ptr<GridTileComponent> oldTile = nullptr;
|
||||
std::shared_ptr<GridTileComponent> newTile = nullptr;
|
||||
|
||||
int oldIdx = mLastCursor - mStartPosition + (dimOpposite * EXTRAITEMS);
|
||||
if (oldIdx >= 0 && oldIdx < mTiles.size())
|
||||
oldTile = mTiles[oldIdx];
|
||||
|
||||
int newIdx = mCursor - mStartPosition + (dimOpposite * EXTRAITEMS);
|
||||
if (isScrollLoop()) {
|
||||
if (newIdx < 0)
|
||||
newIdx += mEntries.size();
|
||||
else if (newIdx >= mTiles.size())
|
||||
newIdx -= mEntries.size();
|
||||
}
|
||||
|
||||
if (newIdx >= 0 && newIdx < mTiles.size())
|
||||
newTile = mTiles[newIdx];
|
||||
|
||||
for (auto it = mTiles.begin(); it != mTiles.end(); it++) {
|
||||
if ((*it)->isSelected() && *it != oldTile && *it != newTile) {
|
||||
startPos = 0;
|
||||
(*it)->setSelected(false, false, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3f oldPos = Vector3f::Zero();
|
||||
|
||||
if (oldTile != nullptr && oldTile != newTile) {
|
||||
oldPos = oldTile->getBackgroundPosition();
|
||||
oldTile->setSelected(false, true, nullptr, true);
|
||||
}
|
||||
|
||||
if (newTile != nullptr)
|
||||
newTile->setSelected(true, true, oldPos == Vector3f::Zero() ? nullptr : &oldPos, true);
|
||||
}
|
||||
|
||||
int firstVisibleCol = mStartPosition / dimOpposite;
|
||||
|
||||
if ((col < centralCol || (col == 0 && col == centralCol)) && !mCenterSelection)
|
||||
mStartPosition = 0;
|
||||
else if ((col - centralCol) > lastScroll && !mCenterSelection && !isScrollLoop())
|
||||
mStartPosition = lastScroll * dimOpposite;
|
||||
else if ((maxCentralCol != centralCol && col == firstVisibleCol + maxCentralCol) || col == firstVisibleCol + centralCol)
|
||||
{
|
||||
if (col == firstVisibleCol + maxCentralCol)
|
||||
mStartPosition = (col - maxCentralCol) * dimOpposite;
|
||||
else
|
||||
mStartPosition = (col - centralCol) * dimOpposite;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oldCol == firstVisibleCol + maxCentralCol)
|
||||
mStartPosition = (col - maxCentralCol) * dimOpposite;
|
||||
else
|
||||
mStartPosition = (col - centralCol) * dimOpposite;
|
||||
}
|
||||
|
||||
auto lastCursor = mLastCursor;
|
||||
mLastCursor = mCursor;
|
||||
|
||||
mCameraDirection = direction ? -1.0f : 1.0f;
|
||||
mCamera = 0;
|
||||
|
||||
if (lastCursor < 0 || !mAnimate)
|
||||
{
|
||||
updateTiles(direction, mAnimate && (lastCursor >= 0 || isScrollLoop()));
|
||||
|
||||
if (mCursorChangedCallback)
|
||||
mCursorChangedCallback(state);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCursorChangedCallback)
|
||||
mCursorChangedCallback(state);
|
||||
|
||||
bool moveCamera = (oldStart != mStartPosition);
|
||||
|
||||
auto func = [this, startPos, endPos, moveCamera](float t)
|
||||
{
|
||||
if (!moveCamera)
|
||||
return;
|
||||
|
||||
t -= 1; // cubic ease out
|
||||
float pct = Math::lerp(0, 1, t*t*t + 1);
|
||||
t = startPos * (1.0f - pct) + endPos * pct;
|
||||
|
||||
mCamera = t;
|
||||
};
|
||||
|
||||
((GuiComponent*)this)->setAnimation(new LambdaAnimation(func, 250), 0, [this, direction] {
|
||||
mCamera = 0;
|
||||
updateTiles(direction, false);
|
||||
}, false, 2);
|
||||
}
|
||||
|
||||
|
||||
// Create and position tiles (mTiles)
|
||||
template<typename T>
|
||||
void ImageGridComponent<T>::buildTiles()
|
||||
{
|
||||
mStartPosition = 0;
|
||||
mTiles.clear();
|
||||
|
||||
calcGridDimension();
|
||||
|
||||
if (mCenterSelection)
|
||||
{
|
||||
int dimScrollable = (isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS;
|
||||
mStartPosition -= (int) Math::floorf(dimScrollable / 2.0f);
|
||||
}
|
||||
|
||||
Vector2f tileDistance = mTileSize + mMargin;
|
||||
Vector2f bufferSize = Vector2f(mScrollDirection == SCROLL_HORIZONTALLY ? tileDistance.x() * texBuffersForward[3] : 0,
|
||||
mScrollDirection == SCROLL_VERTICALLY ? tileDistance.y() * texBuffersForward[3] : 0);
|
||||
Vector2f startPosition = mTileSize / 2 - bufferSize;
|
||||
|
||||
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
|
||||
{
|
||||
auto x = (mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1)) - mPadding.x() - mPadding.z()) / (int) mAutoLayout.x();
|
||||
auto y = (mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1)) - mPadding.y() - mPadding.w()) / (int) mAutoLayout.y();
|
||||
|
||||
mTileSize = Vector2f(x, y);
|
||||
tileDistance = mTileSize + mMargin;
|
||||
}
|
||||
|
||||
bool vert = isVertical();
|
||||
|
||||
Vector2f startPosition = mTileSize / 2;
|
||||
|
||||
startPosition += mPadding.v2();
|
||||
|
||||
int X, Y;
|
||||
|
||||
// Layout tile size and position
|
||||
for(int y = 0; y < mGridDimension.y(); y++)
|
||||
for (int y = 0; y < (vert ? mGridDimension.y() : mGridDimension.x()); y++)
|
||||
{
|
||||
for(int x = 0; x < mGridDimension.x(); x++)
|
||||
for (int x = 0; x < (vert ? mGridDimension.x() : mGridDimension.y()); x++)
|
||||
{
|
||||
// Create tiles
|
||||
auto tile = std::make_shared<GridTileComponent>(mWindow);
|
||||
|
||||
// In Vertical mod, tiles are ordered from left to right, then from top to bottom
|
||||
// In Horizontal mod, tiles are ordered from top to bottom, then from left to right
|
||||
X = mScrollDirection == SCROLL_VERTICALLY ? x : y;
|
||||
Y = mScrollDirection == SCROLL_VERTICALLY ? y : x;
|
||||
X = vert ? x : y - EXTRAITEMS;
|
||||
Y = vert ? y - EXTRAITEMS : x;
|
||||
|
||||
tile->setPosition(X * tileDistance.x() + startPosition.x(), Y * tileDistance.y() + startPosition.y());
|
||||
tile->setOrigin(0.5f, 0.5f);
|
||||
|
@ -324,13 +562,16 @@ void ImageGridComponent<T>::buildTiles()
|
|||
if (mTheme)
|
||||
tile->applyTheme(mTheme, "grid", "gridtile", ThemeFlags::ALL);
|
||||
|
||||
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
|
||||
tile->forceSize(mTileSize, mAutoLayoutZoom);
|
||||
|
||||
mTiles.push_back(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ImageGridComponent<T>::updateTiles()
|
||||
void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation, bool updateSelectedState)
|
||||
{
|
||||
if (!mTiles.size())
|
||||
return;
|
||||
|
@ -349,104 +590,91 @@ void ImageGridComponent<T>::updateTiles()
|
|||
return;
|
||||
}
|
||||
|
||||
// 1 if scrolling down, -1 if scrolling up
|
||||
int scrollDirection = mCursor >= mLastCursor ? 1 : -1;
|
||||
// Temporary store previous texture so they can't be unloaded
|
||||
std::vector<std::shared_ptr<TextureResource>> previousTextures;
|
||||
for (int ti = 0; ti < (int)mTiles.size(); ti++)
|
||||
{
|
||||
std::shared_ptr<GridTileComponent> tile = mTiles.at(ti);
|
||||
previousTextures.push_back(tile->getTexture());
|
||||
}
|
||||
|
||||
// If going down, update from top to bottom
|
||||
// If going up, update from bottom to top
|
||||
int ti = scrollDirection == 1 ? 0 : (int)mTiles.size() - 1;
|
||||
int end = scrollDirection == 1 ? (int)mTiles.size() : -1;
|
||||
int scrollDirection = ascending ? 1 : -1;
|
||||
int ti = ascending ? 0 : (int)mTiles.size() - 1;
|
||||
int end = ascending ? (int)mTiles.size() : -1;
|
||||
int img = mStartPosition + ti;
|
||||
|
||||
int img = getStartPosition();
|
||||
if (scrollDirection == -1)
|
||||
img += (int)mTiles.size() - 1;
|
||||
|
||||
// Calculate buffer size depending on scroll speed and direction
|
||||
int bufferBehind = (texBuffersForward[3] - texBuffersBehind[mScrollTier]) * mGridDimension.x();
|
||||
int bufferForward = (texBuffersForward[3] - texBuffersForward[mScrollTier]) * mGridDimension.x();
|
||||
|
||||
int bufferTop = scrollDirection == 1 ? bufferBehind : bufferForward;
|
||||
int bufferBot = scrollDirection == 1 ? bufferForward : bufferBehind;
|
||||
img -= EXTRAITEMS * (isVertical() ? mGridDimension.x() : mGridDimension.y());
|
||||
|
||||
// Update the tiles
|
||||
while (ti != end)
|
||||
{
|
||||
updateTileAtPos(ti, img, bufferTop, bufferBot);
|
||||
updateTileAtPos(ti, img, allowAnimation, updateSelectedState);
|
||||
|
||||
ti += scrollDirection;
|
||||
img += scrollDirection;
|
||||
}
|
||||
|
||||
if (updateSelectedState)
|
||||
mLastCursor = mCursor;
|
||||
|
||||
mLastCursor = mCursor;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos, int bufferTop, int bufferBot)
|
||||
void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState)
|
||||
{
|
||||
std::shared_ptr<GridTileComponent> tile = mTiles.at(tilePos);
|
||||
|
||||
// If we have more tiles than we have to display images on screen, hide them
|
||||
if(imgPos < 0 || imgPos >= size()
|
||||
|| tilePos < bufferTop || tilePos >= (int)mTiles.size() - bufferBot) // Same for tiles out of the buffer
|
||||
if(isScrollLoop())
|
||||
{
|
||||
tile->setSelected(false);
|
||||
tile->setImage("");
|
||||
if (imgPos < 0)
|
||||
imgPos += mEntries.size();
|
||||
else if (imgPos >= size())
|
||||
imgPos -= mEntries.size();
|
||||
}
|
||||
|
||||
// If we have more tiles than we have to display images on screen, hide them
|
||||
if(imgPos < 0 || imgPos >= size() || tilePos < 0 || tilePos >= (int) mTiles.size()) // Same for tiles out of the buffer
|
||||
{
|
||||
if (updateSelectedState)
|
||||
tile->setSelected(false, allowAnimation);
|
||||
|
||||
tile->reset();
|
||||
tile->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
tile->setSelected(imgPos == mCursor);
|
||||
tile->setVisible(true);
|
||||
|
||||
std::string imagePath = mEntries.at(imgPos).data.texturePath;
|
||||
|
||||
if (ResourceManager::getInstance()->fileExists(imagePath))
|
||||
{
|
||||
tile->setImage(imagePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FileType::FOLDER = 2, but FileData is our template parameter T,
|
||||
// so we don't want to bring that dependence to FileData here
|
||||
if (mEntries.at(imgPos).object->getType() == 2)
|
||||
else if (mEntries.at(imgPos).object->getType() == 2)
|
||||
tile->setImage(mDefaultFolderTexture);
|
||||
else
|
||||
tile->setImage(mDefaultGameTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the starting position (the number of the game which will be displayed on top left of the screen)
|
||||
template<typename T>
|
||||
int ImageGridComponent<T>::getStartPosition() const
|
||||
if (updateSelectedState)
|
||||
{
|
||||
// The "partialRow" variable exist because we want to keep the same positioning behavior in both
|
||||
// case, whenever we have an integer number of rows or not (the last partial row is ignored when
|
||||
// calculating position and the cursor shouldn't end up in this row when close to the end)
|
||||
int partialRow = (int)mLastRowPartial;
|
||||
|
||||
int cursorRow = mCursor / mGridDimension.x();
|
||||
|
||||
int start = (cursorRow - ((mGridDimension.y() - partialRow) / 2)) * mGridDimension.x();
|
||||
|
||||
// Number of tiles which are just used as a buffer for texture loading
|
||||
int bufferSize = texBuffersForward[3] * mGridDimension.x();
|
||||
|
||||
if(start + (mGridDimension.x() * (mGridDimension.y() - partialRow)) >= (int)mEntries.size() + bufferSize)
|
||||
if (imgPos == mCursor && mCursor != mLastCursor)
|
||||
{
|
||||
// If we are at the end put the row as close as we can and no higher, using the following formula
|
||||
// Where E is the nb of entries, X the grid x dim (nb of column), Y the grid y dim (nb of line)
|
||||
// start = first tile of last row - nb column * (nb line - 1)
|
||||
// = (E - 1) / X * X - X * (Y - 1)
|
||||
// = X * ((E - 1) / X - Y + 1)
|
||||
start = mGridDimension.x() * (((int)mEntries.size() - 1) / mGridDimension.x() - mGridDimension.y() + 1 + partialRow) + bufferSize;
|
||||
int dif = mCursor - tilePos;
|
||||
int idx = mLastCursor - dif;
|
||||
|
||||
if (idx < 0 || idx >= mTiles.size())
|
||||
idx = 0;
|
||||
|
||||
Vector3f pos = mTiles.at(idx)->getBackgroundPosition();
|
||||
tile->setSelected(true, allowAnimation, &pos);
|
||||
}
|
||||
else
|
||||
tile->setSelected(imgPos == mCursor, allowAnimation);
|
||||
}
|
||||
|
||||
if(start < -bufferSize)
|
||||
{
|
||||
start = -bufferSize;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
// Calculate how much tiles of size mTileSize we can fit in a grid of size mSize using a margin of size mMargin
|
||||
|
@ -457,24 +685,34 @@ void ImageGridComponent<T>::calcGridDimension()
|
|||
// <=> COLUMNS = (GRID_SIZE + MARGIN) / (TILE_SIZE + MARGIN)
|
||||
Vector2f gridDimension = (mSize + mMargin) / (mTileSize + mMargin);
|
||||
|
||||
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
|
||||
gridDimension = mAutoLayout;
|
||||
|
||||
mLastRowPartial = Math::floorf(gridDimension.y()) != gridDimension.y();
|
||||
|
||||
// Ceil y dim so we can display partial last row
|
||||
mGridDimension = Vector2i(gridDimension.x(), Math::ceilf(gridDimension.y()));
|
||||
|
||||
// Invert dimensions for horizontally scrolling grid
|
||||
if (mScrollDirection == SCROLL_HORIZONTALLY)
|
||||
mGridDimension = Vector2i(mGridDimension.y(), mGridDimension.x());
|
||||
|
||||
// Grid dimension validation
|
||||
if (mGridDimension.x() < 1)
|
||||
LOG(LogError) << "Theme defined grid X dimension below 1";
|
||||
if (mGridDimension.y() < 1)
|
||||
LOG(LogError) << "Theme defined grid Y dimension below 1";
|
||||
|
||||
// Add extra tiles to both side depending on max texture buffer
|
||||
mGridDimension.y() += texBuffersForward[3] * 2;
|
||||
// Add extra tiles to both sides : Add EXTRAITEMS before, EXTRAITEMS after
|
||||
if (isVertical())
|
||||
mGridDimension.y() += 2 * EXTRAITEMS;
|
||||
else
|
||||
mGridDimension.x() += 2 * EXTRAITEMS;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ImageGridComponent<T>::isScrollLoop() {
|
||||
if (!mScrollLoop)
|
||||
return false;
|
||||
if (isVertical())
|
||||
return (mGridDimension.x() * (mGridDimension.y() - 2 * EXTRAITEMS)) <= mEntries.size();
|
||||
return (mGridDimension.y() * (mGridDimension.x() - 2 * EXTRAITEMS)) <= mEntries.size();
|
||||
};
|
||||
|
||||
|
||||
#endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
||||
|
|
|
@ -222,24 +222,30 @@ namespace Utils
|
|||
|
||||
} // removeParenthesis
|
||||
|
||||
stringVector commaStringToVector(const std::string& _string)
|
||||
stringVector delimitedStringToVector(const std::string& _string, const std::string& _delimiter, bool sort)
|
||||
{
|
||||
stringVector vector;
|
||||
size_t start = 0;
|
||||
size_t comma = _string.find(",");
|
||||
size_t comma = _string.find(_delimiter);
|
||||
|
||||
while(comma != std::string::npos)
|
||||
{
|
||||
vector.push_back(_string.substr(start, comma - start));
|
||||
start = comma + 1;
|
||||
comma = _string.find(",", start);
|
||||
comma = _string.find(_delimiter, start);
|
||||
}
|
||||
|
||||
vector.push_back(_string.substr(start));
|
||||
if (sort)
|
||||
std::sort(vector.begin(), vector.end());
|
||||
|
||||
return vector;
|
||||
|
||||
} // delimitedStringToVector
|
||||
|
||||
stringVector commaStringToVector(const std::string& _string, bool sort)
|
||||
{
|
||||
return delimitedStringToVector(_string, ",", sort);
|
||||
} // commaStringToVector
|
||||
|
||||
std::string vectorToCommaString(stringVector _vector)
|
||||
|
|
|
@ -23,7 +23,8 @@ namespace Utils
|
|||
bool startsWith (const std::string& _string, const std::string& _start);
|
||||
bool endsWith (const std::string& _string, const std::string& _end);
|
||||
std::string removeParenthesis (const std::string& _string);
|
||||
stringVector commaStringToVector(const std::string& _string);
|
||||
stringVector delimitedStringToVector(const std::string& _string, const std::string& _delimiter, bool sort = false);
|
||||
stringVector commaStringToVector (const std::string& _string, bool sort = false);
|
||||
std::string vectorToCommaString (stringVector _vector);
|
||||
std::string format (const char* _string, ...);
|
||||
std::string scramble (const std::string& _input, const std::string& key);
|
||||
|
|
Loading…
Reference in a new issue