Grid updates. Animate scrolling option, autoLayout option, center selection option, scroll loop option, image source option

This commit is contained in:
John Rassa 2019-07-06 10:50:50 -04:00
parent cb0db38f54
commit 961571655b
12 changed files with 651 additions and 193 deletions

View file

@ -538,6 +538,7 @@ Reference
## Types of properties: ## 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_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`. * 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`. * 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). * 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. * `pos` - type: NORMALIZED_PAIR.
* `size` - 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. - 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. * `gameImage` - type: PATH.
- The default image used for games which doesn't have an image. - The default image used for games which doesn't have an image.
* `folderImage` - type: PATH. * `folderImage` - type: PATH.
- The default image used for folders which doesn't have an image. - 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. * `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. - `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 #### gridtile

View file

@ -144,7 +144,7 @@ void CollectionSystemManager::loadCollectionSystems()
void CollectionSystemManager::loadEnabledListFromSettings() void CollectionSystemManager::loadEnabledListFromSettings()
{ {
// we parse the auto collection settings list // 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 // iterate the map
for(std::map<std::string, CollectionSystemData>::iterator it = mAutoCollectionSystemsData.begin() ; it != mAutoCollectionSystemsData.end() ; it++ ) 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 // 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 // iterate the map
for(std::map<std::string, CollectionSystemData>::iterator it = mCustomCollectionSystemsData.begin() ; it != mCustomCollectionSystemsData.end() ; it++ ) for(std::map<std::string, CollectionSystemData>::iterator it = mCustomCollectionSystemsData.begin() ; it != mCustomCollectionSystemsData.end() ; it++ )

View file

@ -110,6 +110,18 @@ bool GridGameListView::input(InputConfig* config, Input input)
return ISimpleGameListView::input(config, 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) void GridGameListView::populateList(const std::vector<FileData*>& files)
{ {
mGrid.clear(); mGrid.clear();
@ -118,7 +130,7 @@ void GridGameListView::populateList(const std::vector<FileData*>& files)
{ {
for (auto it = files.cbegin(); it != files.cend(); it++) for (auto it = files.cbegin(); it != files.cend(); it++)
{ {
mGrid.add((*it)->getName(), (*it)->getThumbnailPath(), *it); mGrid.add((*it)->getName(), getImagePath(*it), *it);
} }
} }
else else

View file

@ -36,6 +36,7 @@ protected:
private: private:
void updateInfoPanel(); void updateInfoPanel();
const std::string getImagePath(FileData* file);
void initMDLabels(); void initMDLabels();
void initMDValues(); void initMDValues();

View file

@ -3,6 +3,7 @@
#include "components/ImageComponent.h" #include "components/ImageComponent.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
#include "Log.h" #include "Log.h"
#include "platform.h" #include "platform.h"
#include "Settings.h" #include "Settings.h"
@ -32,9 +33,16 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>> The
{ "pos", NORMALIZED_PAIR }, { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR }, { "size", NORMALIZED_PAIR },
{ "margin", NORMALIZED_PAIR }, { "margin", NORMALIZED_PAIR },
{ "padding", NORMALIZED_RECT },
{ "autoLayout", NORMALIZED_PAIR },
{ "autoLayoutSelectedZoom", FLOAT },
{ "gameImage", PATH }, { "gameImage", PATH },
{ "folderImage", PATH }, { "folderImage", PATH },
{ "scrollDirection", STRING } } }, { "imageSource", STRING },
{ "scrollDirection", STRING },
{ "centerSelection", BOOLEAN },
{ "scrollLoop", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "gridtile", { { "gridtile", {
{ "size", NORMALIZED_PAIR }, { "size", NORMALIZED_PAIR },
{ "padding", 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) 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: case NORMALIZED_PAIR:
{ {
size_t divider = str.find(' '); size_t divider = str.find(' ');

View file

@ -3,6 +3,7 @@
#define ES_CORE_THEME_DATA_H #define ES_CORE_THEME_DATA_H
#include "math/Vector2f.h" #include "math/Vector2f.h"
#include "math/Vector4f.h"
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
#include <deque> #include <deque>
#include <map> #include <map>
@ -94,12 +95,14 @@ public:
struct Property 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 Vector2f& value) { v = value; }
void operator= (const std::string& value) { s = value; } void operator= (const std::string& value) { s = value; }
void operator= (const unsigned int& value) { i = value; } void operator= (const unsigned int& value) { i = value; }
void operator= (const float& value) { f = value; } void operator= (const float& value) { f = value; }
void operator= (const bool& value) { b = value; } void operator= (const bool& value) { b = value; }
Vector4f r;
Vector2f v; Vector2f v;
std::string s; std::string s;
unsigned int i; 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, 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, 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, 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(); return T();
} }
@ -140,6 +144,7 @@ public:
enum ElementPropertyType enum ElementPropertyType
{ {
NORMALIZED_RECT,
NORMALIZED_PAIR, NORMALIZED_PAIR,
PATH, PATH,
STRING, STRING,

View file

@ -1,5 +1,6 @@
#include "GridTileComponent.h" #include "GridTileComponent.h"
#include "animations/LambdaAnimation.h"
#include "resources/TextureResource.h" #include "resources/TextureResource.h"
#include "ThemeData.h" #include "ThemeData.h"
@ -29,7 +30,9 @@ GridTileComponent::GridTileComponent(Window* window) : GuiComponent(window), mBa
addChild(&mBackground); addChild(&mBackground);
addChild(&(*mImage)); addChild(&(*mImage));
setSelected(false); mSelectedZoomPercent = 0;
setSelected(false, false);
setVisible(true); setVisible(true);
} }
@ -42,19 +45,53 @@ void GridTileComponent::render(const Transform4x4f& parentTrans)
} }
// Update all the tile properties to the new status (selected or default) // 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.setImagePath(mCurrentProperties.mBackgroundImage);
mBackground.setCenterColor(currentProperties.mBackgroundCenterColor);
mBackground.setEdgeColor(currentProperties.mBackgroundEdgeColor); mImage->setColorShift(mCurrentProperties.mImageColor);
mBackground.setCenterColor(mCurrentProperties.mBackgroundCenterColor);
mBackground.setEdgeColor(mCurrentProperties.mBackgroundEdgeColor);
resize(); 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*/) 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()); 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 // Apply theme to the default gridtile
const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile"); const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile");
if (elem) if (elem)
{ applyThemeToProperties(elem, mDefaultProperties);
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");
}
// Apply theme to the selected gridtile // Apply theme to the selected gridtile
// NOTE that some of the default gridtile properties influence on the selected gridtile properties // NOTE that some of the default gridtile properties influence on the selected gridtile properties
// See THEMES.md for more informations // See THEMES.md for more informations
elem = theme->getElement(view, "selected", "gridtile"); elem = theme->getElement(view, "selected", "gridtile");
mSelectedProperties.mSize = elem && elem->has("size") ? mSelectedProperties.mSize = getSelectedTileSize();
elem->get<Vector2f>("size") * screen : mSelectedProperties.mPadding = mDefaultProperties.mPadding;
getSelectedTileSize(); mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage;
mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize;
mSelectedProperties.mPadding = elem && elem->has("padding") ? if (elem)
elem->get<Vector2f>("padding") : applyThemeToProperties(elem, mSelectedProperties);
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");
} }
// Made this a static function because the ImageGridComponent need to know the default tile size // 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; return mSelected;
} }
void GridTileComponent::reset()
{
setImage("");
}
void GridTileComponent::setImage(const std::string& path) void GridTileComponent::setImage(const std::string& path)
{ {
mImage->setImage(path); mImage->setImage(path);
@ -163,9 +155,80 @@ void GridTileComponent::setImage(const std::shared_ptr<TextureResource>& texture
resize(); resize();
} }
void GridTileComponent::setSelected(bool selected) void GridTileComponent::setSelected(bool selected, bool allowAnimation, Vector3f* pPosition, bool force)
{ {
if (mSelected == selected && !force)
{
return;
}
mSelected = selected; 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) void GridTileComponent::setVisible(bool visible)
@ -175,14 +238,86 @@ void GridTileComponent::setVisible(bool visible)
void GridTileComponent::resize() void GridTileComponent::resize()
{ {
const GridTileProperties& currentProperties = getCurrentProperties(); calcCurrentProperties();
mImage->setMaxSize(currentProperties.mSize - currentProperties.mPadding * 2); mImage->setMaxSize(mCurrentProperties.mSize - mCurrentProperties.mPadding * 2);
mBackground.setCornerSize(currentProperties.mBackgroundCornerSize); mBackground.setCornerSize(mCurrentProperties.mBackgroundCornerSize);
mBackground.fitTo(currentProperties.mSize - mBackground.getCornerSize() * 2); 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;
} }

View file

@ -22,7 +22,6 @@ public:
GridTileComponent(Window* window); GridTileComponent(Window* window);
void render(const Transform4x4f& parentTrans) override; 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); 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 // Made this a static function because the ImageGridComponent need to know the default tile max size
@ -31,23 +30,38 @@ public:
Vector2f getSelectedTileSize() const; Vector2f getSelectedTileSize() const;
bool isSelected() const; bool isSelected() const;
void reset();
void setImage(const std::string& path); void setImage(const std::string& path);
void setImage(const std::shared_ptr<TextureResource>& texture); 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 setVisible(bool visible);
void forceSize(Vector2f size, float selectedZoom);
Vector3f getBackgroundPosition();
virtual void update(int deltaTime);
std::shared_ptr<TextureResource> getTexture();
private: private:
void resize(); void resize();
const GridTileProperties& getCurrentProperties() const; void calcCurrentProperties();
void setSelectedZoom(float percent);
std::shared_ptr<ImageComponent> mImage; std::shared_ptr<ImageComponent> mImage;
NinePatchComponent mBackground; NinePatchComponent mBackground;
GridTileProperties mDefaultProperties; GridTileProperties mDefaultProperties;
GridTileProperties mSelectedProperties; GridTileProperties mSelectedProperties;
GridTileProperties mCurrentProperties;
float mSelectedZoomPercent;
bool mSelected; bool mSelected;
bool mVisible; bool mVisible;
Vector3f mAnimPosition;
}; };
#endif // ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H #endif // ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H

View file

@ -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 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; virtual std::vector<HelpPrompt> getHelpPrompts() override;
std::shared_ptr<TextureResource> getTexture() { return mTexture; };
private: private:
Vector2f mTargetSize; Vector2f mTargetSize;

View file

@ -3,16 +3,26 @@
#define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H #define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
#include "Log.h" #include "Log.h"
#include "animations/LambdaAnimation.h"
#include "components/IList.h" #include "components/IList.h"
#include "resources/TextureResource.h" #include "resources/TextureResource.h"
#include "GridTileComponent.h" #include "GridTileComponent.h"
#define EXTRAITEMS 2
enum ScrollDirection enum ScrollDirection
{ {
SCROLL_VERTICALLY, SCROLL_VERTICALLY,
SCROLL_HORIZONTALLY SCROLL_HORIZONTALLY
}; };
enum ImageSource
{
THUMBNAIL,
IMAGE,
MARQUEE
};
struct ImageGridData struct ImageGridData
{ {
std::string texturePath; std::string texturePath;
@ -50,20 +60,23 @@ public:
void onSizeChanged() override; void onSizeChanged() override;
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; } inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
ImageSource getImageSource() { return mImageSource; };
protected: protected:
virtual void onCursorChanged(const CursorState& state) override; virtual void onCursorChanged(const CursorState& state) override;
private: private:
// TILES // TILES
void buildTiles(); void buildTiles();
void updateTiles(); void updateTiles(bool ascending = true, bool allowAnimation = true, bool updateSelectedState = true);
void updateTileAtPos(int tilePos, int imgPos, int bufferTop, int bufferBot); void updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState);
int getStartPosition() const;
void calcGridDimension(); void calcGridDimension();
bool isScrollLoop();
bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; };
// IMAGES & ENTRIES // IMAGES & ENTRIES
const int texBuffersBehind[4] = { 1, 1, 1, 1 };
const int texBuffersForward[4] = { 1, 2, 3, 3 };
bool mEntriesDirty; bool mEntriesDirty;
int mLastCursor; int mLastCursor;
std::string mDefaultGameTexture; std::string mDefaultGameTexture;
@ -71,14 +84,27 @@ private:
// TILES // TILES
bool mLastRowPartial; bool mLastRowPartial;
Vector2f mAutoLayout;
float mAutoLayoutZoom;
Vector4f mPadding;
Vector2f mMargin; Vector2f mMargin;
Vector2f mTileSize; Vector2f mTileSize;
Vector2i mGridDimension; Vector2i mGridDimension;
std::shared_ptr<ThemeData> mTheme; std::shared_ptr<ThemeData> mTheme;
std::vector< std::shared_ptr<GridTileComponent> > mTiles; std::vector< std::shared_ptr<GridTileComponent> > mTiles;
int mStartPosition;
float mCamera;
float mCameraDirection;
// MISCELLANEOUS // MISCELLANEOUS
bool mAnimate;
bool mCenterSelection;
bool mScrollLoop;
ScrollDirection mScrollDirection; ScrollDirection mScrollDirection;
ImageSource mImageSource;
std::function<void(CursorState state)> mCursorChangedCallback; 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()); 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; mEntriesDirty = true;
mLastCursor = 0; mLastCursor = 0;
mDefaultGameTexture = ":/cartridge.svg"; mDefaultGameTexture = ":/cartridge.svg";
@ -94,9 +128,14 @@ ImageGridComponent<T>::ImageGridComponent(Window* window) : IList<ImageGridData,
mSize = screen * 0.80f; mSize = screen * 0.80f;
mMargin = screen * 0.07f; mMargin = screen * 0.07f;
mPadding = Vector4f::Zero();
mTileSize = GridTileComponent::getDefaultTileSize(); mTileSize = GridTileComponent::getDefaultTileSize();
mAnimate = true;
mCenterSelection = false;
mScrollLoop = false;
mScrollDirection = SCROLL_VERTICALLY; mScrollDirection = SCROLL_VERTICALLY;
mImageSource = THUMBNAIL;
} }
template<typename T> template<typename T>
@ -116,19 +155,24 @@ bool ImageGridComponent<T>::input(InputConfig* config, Input input)
{ {
if(input.value != 0) if(input.value != 0)
{ {
int idx = isVertical() ? 0 : 1;
Vector2i dir = Vector2i::Zero(); Vector2i dir = Vector2i::Zero();
if(config->isMappedLike("up", input)) if(config->isMappedLike("up", input))
dir[1 ^ mScrollDirection] = -1; dir[1 ^ idx] = -1;
else if(config->isMappedLike("down", input)) else if(config->isMappedLike("down", input))
dir[1 ^ mScrollDirection] = 1; dir[1 ^ idx] = 1;
else if(config->isMappedLike("left", input)) else if(config->isMappedLike("left", input))
dir[0 ^ mScrollDirection] = -1; dir[0 ^ idx] = -1;
else if(config->isMappedLike("right", input)) else if(config->isMappedLike("right", input))
dir[0 ^ mScrollDirection] = 1; dir[0 ^ idx] = 1;
if(dir != Vector2i::Zero()) if(dir != Vector2i::Zero())
{ {
if (isVertical())
listInput(dir.x() + dir.y() * mGridDimension.x()); listInput(dir.x() + dir.y() * mGridDimension.x());
else
listInput(dir.x() + dir.y() * mGridDimension.y());
return true; return true;
} }
}else{ }else{
@ -144,16 +188,23 @@ bool ImageGridComponent<T>::input(InputConfig* config, Input input)
template<typename T> template<typename T>
void ImageGridComponent<T>::update(int deltaTime) void ImageGridComponent<T>::update(int deltaTime)
{ {
GuiComponent::update(deltaTime);
listUpdate(deltaTime); listUpdate(deltaTime);
for(auto it = mTiles.begin(); it != mTiles.end(); it++) for(auto it = mTiles.begin(); it != mTiles.end(); it++)
(*it)->update(); (*it)->update(deltaTime);
} }
template<typename T> template<typename T>
void ImageGridComponent<T>::render(const Transform4x4f& parentTrans) void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
{ {
Transform4x4f trans = getTransform() * 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) if(mEntriesDirty)
{ {
@ -180,14 +231,14 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
if(tile->isSelected()) if(tile->isSelected())
selectedTile = tile; selectedTile = tile;
else else
tile->render(trans); tile->render(tileTrans);
} }
Renderer::popClipRect(); Renderer::popClipRect();
// Render the selected image on top of the others // Render the selected image on top of the others
if (selectedTile != NULL) if (selectedTile != NULL)
selectedTile->render(trans); selectedTile->render(tileTrans);
listRenderTitleOverlay(trans); listRenderTitleOverlay(trans);
@ -211,9 +262,44 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (elem->has("margin")) if (elem->has("margin"))
mMargin = elem->get<Vector2f>("margin") * screen; 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")) if (elem->has("scrollDirection"))
mScrollDirection = (ScrollDirection)(elem->get<std::string>("scrollDirection") == "horizontal"); 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")) if (elem->has("gameImage"))
{ {
std::string path = elem->get<std::string>("gameImage"); std::string path = elem->get<std::string>("gameImage");
@ -283,39 +369,191 @@ void ImageGridComponent<T>::onSizeChanged()
template<typename T> template<typename T>
void ImageGridComponent<T>::onCursorChanged(const CursorState& state) void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
{ {
updateTiles(); if (mLastCursor == mCursor)
{
if(mCursorChangedCallback) if (state == CURSOR_STOPPED && mCursorChangedCallback)
mCursorChangedCallback(state); 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) // Create and position tiles (mTiles)
template<typename T> template<typename T>
void ImageGridComponent<T>::buildTiles() void ImageGridComponent<T>::buildTiles()
{ {
mStartPosition = 0;
mTiles.clear(); mTiles.clear();
calcGridDimension(); calcGridDimension();
if (mCenterSelection)
{
int dimScrollable = (isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS;
mStartPosition -= (int) Math::floorf(dimScrollable / 2.0f);
}
Vector2f tileDistance = mTileSize + mMargin; Vector2f tileDistance = mTileSize + mMargin;
Vector2f bufferSize = Vector2f(mScrollDirection == SCROLL_HORIZONTALLY ? tileDistance.x() * texBuffersForward[3] : 0,
mScrollDirection == SCROLL_VERTICALLY ? tileDistance.y() * texBuffersForward[3] : 0); if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
Vector2f startPosition = mTileSize / 2 - bufferSize; {
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; int X, Y;
// Layout tile size and position // 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 // Create tiles
auto tile = std::make_shared<GridTileComponent>(mWindow); auto tile = std::make_shared<GridTileComponent>(mWindow);
// In Vertical mod, tiles are ordered from left to right, then from top to bottom // 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 // In Horizontal mod, tiles are ordered from top to bottom, then from left to right
X = mScrollDirection == SCROLL_VERTICALLY ? x : y; X = vert ? x : y - EXTRAITEMS;
Y = mScrollDirection == SCROLL_VERTICALLY ? y : x; Y = vert ? y - EXTRAITEMS : x;
tile->setPosition(X * tileDistance.x() + startPosition.x(), Y * tileDistance.y() + startPosition.y()); tile->setPosition(X * tileDistance.x() + startPosition.x(), Y * tileDistance.y() + startPosition.y());
tile->setOrigin(0.5f, 0.5f); tile->setOrigin(0.5f, 0.5f);
@ -324,13 +562,16 @@ void ImageGridComponent<T>::buildTiles()
if (mTheme) if (mTheme)
tile->applyTheme(mTheme, "grid", "gridtile", ThemeFlags::ALL); tile->applyTheme(mTheme, "grid", "gridtile", ThemeFlags::ALL);
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
tile->forceSize(mTileSize, mAutoLayoutZoom);
mTiles.push_back(tile); mTiles.push_back(tile);
} }
} }
} }
template<typename T> template<typename T>
void ImageGridComponent<T>::updateTiles() void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation, bool updateSelectedState)
{ {
if (!mTiles.size()) if (!mTiles.size())
return; return;
@ -349,104 +590,91 @@ void ImageGridComponent<T>::updateTiles()
return; return;
} }
// 1 if scrolling down, -1 if scrolling up // Temporary store previous texture so they can't be unloaded
int scrollDirection = mCursor >= mLastCursor ? 1 : -1; 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 down, update from top to bottom
// If going up, update from bottom to top // If going up, update from bottom to top
int ti = scrollDirection == 1 ? 0 : (int)mTiles.size() - 1; int scrollDirection = ascending ? 1 : -1;
int end = scrollDirection == 1 ? (int)mTiles.size() : -1; int ti = ascending ? 0 : (int)mTiles.size() - 1;
int end = ascending ? (int)mTiles.size() : -1;
int img = mStartPosition + ti;
int img = getStartPosition(); img -= EXTRAITEMS * (isVertical() ? mGridDimension.x() : mGridDimension.y());
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;
// Update the tiles // Update the tiles
while (ti != end) while (ti != end)
{ {
updateTileAtPos(ti, img, bufferTop, bufferBot); updateTileAtPos(ti, img, allowAnimation, updateSelectedState);
ti += scrollDirection; ti += scrollDirection;
img += scrollDirection; img += scrollDirection;
} }
if (updateSelectedState)
mLastCursor = mCursor;
mLastCursor = mCursor; mLastCursor = mCursor;
} }
template<typename T> 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); std::shared_ptr<GridTileComponent> tile = mTiles.at(tilePos);
// If we have more tiles than we have to display images on screen, hide them if(isScrollLoop())
if(imgPos < 0 || imgPos >= size()
|| tilePos < bufferTop || tilePos >= (int)mTiles.size() - bufferBot) // Same for tiles out of the buffer
{ {
tile->setSelected(false); if (imgPos < 0)
tile->setImage(""); 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); tile->setVisible(false);
} }
else else
{ {
tile->setSelected(imgPos == mCursor);
tile->setVisible(true); tile->setVisible(true);
std::string imagePath = mEntries.at(imgPos).data.texturePath; std::string imagePath = mEntries.at(imgPos).data.texturePath;
if (ResourceManager::getInstance()->fileExists(imagePath)) if (ResourceManager::getInstance()->fileExists(imagePath))
{
tile->setImage(imagePath); tile->setImage(imagePath);
} else if (mEntries.at(imgPos).object->getType() == 2)
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)
tile->setImage(mDefaultFolderTexture); tile->setImage(mDefaultFolderTexture);
else else
tile->setImage(mDefaultGameTexture); tile->setImage(mDefaultGameTexture);
}
}
}
// Return the starting position (the number of the game which will be displayed on top left of the screen) if (updateSelectedState)
template<typename T>
int ImageGridComponent<T>::getStartPosition() const
{
// 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 we are at the end put the row as close as we can and no higher, using the following formula if (imgPos == mCursor && mCursor != mLastCursor)
// 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;
}
if(start < -bufferSize)
{ {
start = -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);
} }
return start; }
} }
// Calculate how much tiles of size mTileSize we can fit in a grid of size mSize using a margin of size mMargin // 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) // <=> COLUMNS = (GRID_SIZE + MARGIN) / (TILE_SIZE + MARGIN)
Vector2f gridDimension = (mSize + mMargin) / (mTileSize + mMargin); Vector2f gridDimension = (mSize + mMargin) / (mTileSize + mMargin);
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
gridDimension = mAutoLayout;
mLastRowPartial = Math::floorf(gridDimension.y()) != gridDimension.y(); mLastRowPartial = Math::floorf(gridDimension.y()) != gridDimension.y();
// Ceil y dim so we can display partial last row // Ceil y dim so we can display partial last row
mGridDimension = Vector2i(gridDimension.x(), Math::ceilf(gridDimension.y())); 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 // Grid dimension validation
if (mGridDimension.x() < 1) if (mGridDimension.x() < 1)
LOG(LogError) << "Theme defined grid X dimension below 1"; LOG(LogError) << "Theme defined grid X dimension below 1";
if (mGridDimension.y() < 1) if (mGridDimension.y() < 1)
LOG(LogError) << "Theme defined grid Y dimension below 1"; LOG(LogError) << "Theme defined grid Y dimension below 1";
// Add extra tiles to both side depending on max texture buffer // Add extra tiles to both sides : Add EXTRAITEMS before, EXTRAITEMS after
mGridDimension.y() += texBuffersForward[3] * 2; 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 #endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H

View file

@ -222,24 +222,30 @@ namespace Utils
} // removeParenthesis } // removeParenthesis
stringVector commaStringToVector(const std::string& _string) stringVector delimitedStringToVector(const std::string& _string, const std::string& _delimiter, bool sort)
{ {
stringVector vector; stringVector vector;
size_t start = 0; size_t start = 0;
size_t comma = _string.find(","); size_t comma = _string.find(_delimiter);
while(comma != std::string::npos) while(comma != std::string::npos)
{ {
vector.push_back(_string.substr(start, comma - start)); vector.push_back(_string.substr(start, comma - start));
start = comma + 1; start = comma + 1;
comma = _string.find(",", start); comma = _string.find(_delimiter, start);
} }
vector.push_back(_string.substr(start)); vector.push_back(_string.substr(start));
if (sort)
std::sort(vector.begin(), vector.end()); std::sort(vector.begin(), vector.end());
return vector; return vector;
} // delimitedStringToVector
stringVector commaStringToVector(const std::string& _string, bool sort)
{
return delimitedStringToVector(_string, ",", sort);
} // commaStringToVector } // commaStringToVector
std::string vectorToCommaString(stringVector _vector) std::string vectorToCommaString(stringVector _vector)

View file

@ -23,8 +23,9 @@ namespace Utils
bool startsWith (const std::string& _string, const std::string& _start); bool startsWith (const std::string& _string, const std::string& _start);
bool endsWith (const std::string& _string, const std::string& _end); bool endsWith (const std::string& _string, const std::string& _end);
std::string removeParenthesis (const std::string& _string); 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);
std::string vectorToCommaString(stringVector _vector); stringVector commaStringToVector (const std::string& _string, bool sort = false);
std::string vectorToCommaString (stringVector _vector);
std::string format (const char* _string, ...); std::string format (const char* _string, ...);
std::string scramble (const std::string& _input, const std::string& key); std::string scramble (const std::string& _input, const std::string& key);