2013-12-01 01:04:46 +00:00
|
|
|
#pragma once
|
2017-10-31 17:12:50 +00:00
|
|
|
#ifndef ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
|
|
|
#define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "components/IList.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "resources/TextureResource.h"
|
2018-04-07 19:23:10 +00:00
|
|
|
#include "GridTileComponent.h"
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2014-02-08 03:45:28 +00:00
|
|
|
struct ImageGridData
|
|
|
|
{
|
|
|
|
std::shared_ptr<TextureResource> texture;
|
|
|
|
};
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
template<typename T>
|
2014-02-13 23:10:28 +00:00
|
|
|
class ImageGridComponent : public IList<ImageGridData, T>
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2014-02-17 17:40:31 +00:00
|
|
|
protected:
|
|
|
|
using IList<ImageGridData, T>::mEntries;
|
|
|
|
using IList<ImageGridData, T>::listUpdate;
|
|
|
|
using IList<ImageGridData, T>::listInput;
|
|
|
|
using IList<ImageGridData, T>::listRenderTitleOverlay;
|
|
|
|
using IList<ImageGridData, T>::getTransform;
|
|
|
|
using IList<ImageGridData, T>::mSize;
|
|
|
|
using IList<ImageGridData, T>::mCursor;
|
|
|
|
using IList<ImageGridData, T>::Entry;
|
|
|
|
using IList<ImageGridData, T>::mWindow;
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
public:
|
2014-02-17 17:40:31 +00:00
|
|
|
using IList<ImageGridData, T>::size;
|
|
|
|
using IList<ImageGridData, T>::isScrolling;
|
|
|
|
using IList<ImageGridData, T>::stopScrolling;
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
ImageGridComponent(Window* window);
|
|
|
|
|
2014-02-08 03:45:28 +00:00
|
|
|
void add(const std::string& name, const std::string& imagePath, const T& obj);
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
void onSizeChanged() override;
|
|
|
|
|
|
|
|
bool input(InputConfig* config, Input input) override;
|
|
|
|
void update(int deltaTime) override;
|
2017-10-28 20:24:35 +00:00
|
|
|
void render(const Transform4x4f& parentTrans) override;
|
2018-03-31 13:59:14 +00:00
|
|
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-03-22 12:52:13 +00:00
|
|
|
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
private:
|
2018-04-07 19:23:10 +00:00
|
|
|
// Calculate how much tiles of size mTileSize we can fit in a grid of size mSize using a margin of size mMargin
|
|
|
|
void calcGridDimension()
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-04-07 19:23:10 +00:00
|
|
|
// GRID_SIZE = COLUMNS * TILE_SIZE + (COLUMNS - 1) * MARGIN
|
|
|
|
// <=> COLUMNS = (GRID_SIZE + MARGIN) / (TILE_SIZE + MARGIN)
|
|
|
|
Vector2f gridDimension = (mSize + mMargin) / (mTileSize + mMargin);
|
|
|
|
|
|
|
|
mGridDimension = Vector2i(gridDimension.x(), gridDimension.y());
|
2013-12-01 01:04:46 +00:00
|
|
|
};
|
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
int getStartPosition();
|
2013-12-01 01:04:46 +00:00
|
|
|
void buildImages();
|
|
|
|
void updateImages();
|
|
|
|
|
2014-02-08 03:45:28 +00:00
|
|
|
virtual void onCursorChanged(const CursorState& state);
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-03-22 12:52:13 +00:00
|
|
|
std::function<void(CursorState state)> mCursorChangedCallback;
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
bool mEntriesDirty;
|
|
|
|
|
2018-03-22 07:03:12 +00:00
|
|
|
Vector2f mMargin;
|
2018-04-07 19:23:10 +00:00
|
|
|
Vector2f mTileSize;
|
|
|
|
Vector2i mGridDimension;
|
|
|
|
|
|
|
|
std::vector< std::shared_ptr<GridTileComponent> > mTiles;
|
2018-03-22 07:03:12 +00:00
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
std::shared_ptr<ThemeData> mTheme;
|
2013-12-01 01:04:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
2014-02-17 17:40:31 +00:00
|
|
|
ImageGridComponent<T>::ImageGridComponent(Window* window) : IList<ImageGridData, T>(window)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-03-22 07:03:12 +00:00
|
|
|
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
mEntriesDirty = true;
|
2018-03-22 07:03:12 +00:00
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
mSize = screen * 0.80f;
|
|
|
|
mMargin = screen * 0.07f;
|
|
|
|
mTileSize = GridTileComponent::getDefaultTileSize();
|
|
|
|
|
|
|
|
calcGridDimension();
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
2014-02-08 03:45:28 +00:00
|
|
|
void ImageGridComponent<T>::add(const std::string& name, const std::string& imagePath, const T& obj)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2014-02-17 17:40:31 +00:00
|
|
|
typename IList<ImageGridData, T>::Entry entry;
|
2014-02-08 03:45:28 +00:00
|
|
|
entry.name = name;
|
|
|
|
entry.object = obj;
|
|
|
|
entry.data.texture = ResourceManager::getInstance()->fileExists(imagePath) ? TextureResource::get(imagePath) : TextureResource::get(":/button.png");
|
|
|
|
static_cast<IList< ImageGridData, T >*>(this)->add(entry);
|
2013-12-01 01:04:46 +00:00
|
|
|
mEntriesDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
bool ImageGridComponent<T>::input(InputConfig* config, Input input)
|
|
|
|
{
|
|
|
|
if(input.value != 0)
|
|
|
|
{
|
2017-10-28 20:24:35 +00:00
|
|
|
Vector2i dir = Vector2i::Zero();
|
2013-12-01 01:04:46 +00:00
|
|
|
if(config->isMappedTo("up", input))
|
|
|
|
dir[1] = -1;
|
|
|
|
else if(config->isMappedTo("down", input))
|
|
|
|
dir[1] = 1;
|
|
|
|
else if(config->isMappedTo("left", input))
|
|
|
|
dir[0] = -1;
|
|
|
|
else if(config->isMappedTo("right", input))
|
|
|
|
dir[0] = 1;
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
if(dir != Vector2i::Zero())
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-04-07 19:23:10 +00:00
|
|
|
listInput(dir.x() + dir.y() * mGridDimension.x());
|
2013-12-01 01:04:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(config->isMappedTo("up", input) || config->isMappedTo("down", input) || config->isMappedTo("left", input) || config->isMappedTo("right", input))
|
|
|
|
{
|
2014-02-08 02:15:48 +00:00
|
|
|
stopScrolling();
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return GuiComponent::input(config, input);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void ImageGridComponent<T>::update(int deltaTime)
|
|
|
|
{
|
2014-02-08 02:15:48 +00:00
|
|
|
listUpdate(deltaTime);
|
2018-04-07 19:23:10 +00:00
|
|
|
|
|
|
|
for(auto it = mTiles.begin(); it != mTiles.end(); it++)
|
|
|
|
(*it)->update();
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
2017-10-28 20:24:35 +00:00
|
|
|
void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2017-10-28 20:24:35 +00:00
|
|
|
Transform4x4f trans = getTransform() * parentTrans;
|
2013-12-01 01:04:46 +00:00
|
|
|
|
|
|
|
if(mEntriesDirty)
|
|
|
|
{
|
|
|
|
buildImages();
|
|
|
|
updateImages();
|
|
|
|
mEntriesDirty = false;
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
std::shared_ptr<GridTileComponent> selectedTile = NULL;
|
|
|
|
for(auto it = mTiles.begin(); it != mTiles.end(); it++)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-03-22 07:03:12 +00:00
|
|
|
// If it's the selected image, keep it for later, otherwise render it now
|
2018-04-07 19:23:10 +00:00
|
|
|
std::shared_ptr<GridTileComponent> tile = (*it);
|
|
|
|
if(tile->isSelected())
|
|
|
|
selectedTile = tile;
|
2018-03-22 07:03:12 +00:00
|
|
|
else
|
2018-04-07 19:23:10 +00:00
|
|
|
tile->render(trans);
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 07:03:12 +00:00
|
|
|
// Render the selected image on top of the others
|
2018-04-07 19:23:10 +00:00
|
|
|
if (selectedTile != NULL)
|
|
|
|
selectedTile->render(trans);
|
2018-03-22 07:03:12 +00:00
|
|
|
|
2014-02-17 17:40:31 +00:00
|
|
|
GuiComponent::renderChildren(trans);
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
|
2018-03-31 13:59:14 +00:00
|
|
|
template<typename T>
|
|
|
|
void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
|
|
|
{
|
|
|
|
GuiComponent::applyTheme(theme, view, element, properties);
|
2018-04-07 19:23:10 +00:00
|
|
|
|
|
|
|
// Keep the theme pointer to apply it on the tiles later on
|
|
|
|
mTheme = theme;
|
|
|
|
|
|
|
|
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
|
|
|
|
|
|
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "imagegrid");
|
|
|
|
|
|
|
|
if (elem && elem->has("margin"))
|
|
|
|
mMargin = elem->get<Vector2f>("margin") * screen;
|
|
|
|
|
|
|
|
// We still need to manually get the grid tile size here,
|
|
|
|
// so we can recalculate the new grid dimension, and THEN (re)build the tiles
|
|
|
|
elem = theme->getElement(view, "default", "gridtile");
|
|
|
|
|
|
|
|
mTileSize = elem && elem->has("size") ?
|
|
|
|
elem->get<Vector2f>("size") * screen :
|
|
|
|
GridTileComponent::getDefaultTileSize();
|
|
|
|
|
|
|
|
// Recalculate grid dimension after theme changed
|
|
|
|
calcGridDimension();
|
2018-03-31 13:59:14 +00:00
|
|
|
}
|
|
|
|
|
2013-12-01 01:04:46 +00:00
|
|
|
template<typename T>
|
2018-03-22 12:52:13 +00:00
|
|
|
void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
|
|
|
updateImages();
|
2018-03-22 12:52:13 +00:00
|
|
|
|
|
|
|
if(mCursorChangedCallback)
|
|
|
|
mCursorChangedCallback(state);
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void ImageGridComponent<T>::onSizeChanged()
|
|
|
|
{
|
|
|
|
buildImages();
|
|
|
|
updateImages();
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
// Create and position tiles (mTiles)
|
2013-12-01 01:04:46 +00:00
|
|
|
template<typename T>
|
|
|
|
void ImageGridComponent<T>::buildImages()
|
|
|
|
{
|
2018-04-07 19:23:10 +00:00
|
|
|
mTiles.clear();
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
Vector2f startPosition = mTileSize / 2;
|
|
|
|
Vector2f tileDistance = mTileSize + mMargin;
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-03-22 07:03:12 +00:00
|
|
|
// Layout tile size and position
|
2018-04-07 19:23:10 +00:00
|
|
|
for(int y = 0; y < mGridDimension.y(); y++)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-04-07 19:23:10 +00:00
|
|
|
for(int x = 0; x < mGridDimension.x(); x++)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-03-22 07:03:12 +00:00
|
|
|
// Create tiles
|
2018-04-07 19:23:10 +00:00
|
|
|
auto tile = std::make_shared<GridTileComponent>(mWindow);
|
|
|
|
|
|
|
|
tile->setPosition(x * tileDistance.x() + startPosition.x(), y * tileDistance.y() + startPosition.y());
|
|
|
|
tile->setOrigin(0.5f, 0.5f);
|
|
|
|
tile->setImage("");
|
|
|
|
|
|
|
|
if (mTheme)
|
|
|
|
tile->applyTheme(mTheme, "grid", "gridtile", ThemeFlags::ALL);
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
mTiles.push_back(tile);
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
// Return the starting position (the number of the game which will be displayed on top left of the screen)
|
2013-12-01 01:04:46 +00:00
|
|
|
template<typename T>
|
2018-04-07 19:23:10 +00:00
|
|
|
int ImageGridComponent<T>::getStartPosition()
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-04-07 19:23:10 +00:00
|
|
|
int cursorRow = mCursor / mGridDimension.x();
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
int start = (cursorRow - (mGridDimension.y() / 2)) * mGridDimension.x();
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-04-02 13:28:33 +00:00
|
|
|
// 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)
|
2018-04-07 19:23:10 +00:00
|
|
|
if(start + (mGridDimension.x() * mGridDimension.y()) >= (int)mEntries.size())
|
|
|
|
start = mGridDimension.x() * (((int)mEntries.size() - 1) / mGridDimension.x() - mGridDimension.y() + 1);
|
2013-12-01 01:04:46 +00:00
|
|
|
|
|
|
|
if(start < 0)
|
|
|
|
start = 0;
|
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void ImageGridComponent<T>::updateImages()
|
|
|
|
{
|
|
|
|
if(mTiles.empty())
|
|
|
|
buildImages();
|
|
|
|
|
|
|
|
int pos = getStartPosition();
|
|
|
|
|
|
|
|
for(int img = 0; img < mTiles.size(); img++)
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-04-07 19:23:10 +00:00
|
|
|
std::shared_ptr<GridTileComponent> tile = mTiles.at(img);
|
|
|
|
|
|
|
|
// If we have more tiles than we have to display images on screen, hide them
|
|
|
|
if(pos >= size())
|
2013-12-01 01:04:46 +00:00
|
|
|
{
|
2018-04-07 19:23:10 +00:00
|
|
|
tile->setSelected(false);
|
|
|
|
tile->setImage("");
|
|
|
|
tile->setVisible(false);
|
2013-12-01 01:04:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
tile->setSelected(pos == mCursor);
|
|
|
|
tile->setImage(mEntries.at(pos).data.texture);
|
|
|
|
tile->setVisible(true);
|
2013-12-01 01:04:46 +00:00
|
|
|
|
2018-04-07 19:23:10 +00:00
|
|
|
pos++;
|
2013-12-01 01:04:46 +00:00
|
|
|
}
|
|
|
|
}
|
2017-10-31 17:12:50 +00:00
|
|
|
|
|
|
|
#endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|