#pragma once #ifndef ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H #define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H #include "components/IList.h" #include "resources/TextureResource.h" struct ImageGridData { std::shared_ptr texture; }; template class ImageGridComponent : public IList { protected: using IList::mEntries; using IList::listUpdate; using IList::listInput; using IList::listRenderTitleOverlay; using IList::getTransform; using IList::mSize; using IList::mCursor; using IList::Entry; using IList::mWindow; public: using IList::size; using IList::isScrolling; using IList::stopScrolling; ImageGridComponent(Window* window); void add(const std::string& name, const std::string& imagePath, const T& obj); void onSizeChanged() override; bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; void render(const Transform4x4f& parentTrans) override; private: Vector2f getSquareSize(std::shared_ptr tex = nullptr) const { Vector2f aspect(1, 1); if(tex) { const Vector2i& texSize = tex->getSize(); if(texSize.x() > texSize.y()) aspect[0] = (float)texSize.x() / texSize.y(); else aspect[1] = (float)texSize.y() / texSize.x(); } return Vector2f(156 * aspect.x(), 156 * aspect.y()); }; Vector2f getMaxSquareSize() const { Vector2f squareSize(32, 32); // calc biggest square size for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) { Vector2f chkSize = getSquareSize(it->data.texture); if(chkSize.x() > squareSize.x()) squareSize[0] = chkSize[0]; if(chkSize.y() > squareSize.y()) squareSize[1] = chkSize[1]; } return squareSize; }; Vector2i getGridSize() const { Vector2f squareSize = getMaxSquareSize(); Vector2i gridSize(mSize.x() / (squareSize.x() + getPadding().x()), mSize.y() / (squareSize.y() + getPadding().y())); return gridSize; }; Vector2f getPadding() const { return Vector2f(24, 24); } void buildImages(); void updateImages(); virtual void onCursorChanged(const CursorState& state); bool mEntriesDirty; std::vector mImages; }; template ImageGridComponent::ImageGridComponent(Window* window) : IList(window) { mEntriesDirty = true; } template void ImageGridComponent::add(const std::string& name, const std::string& imagePath, const T& obj) { typename IList::Entry entry; entry.name = name; entry.object = obj; entry.data.texture = ResourceManager::getInstance()->fileExists(imagePath) ? TextureResource::get(imagePath) : TextureResource::get(":/button.png"); static_cast*>(this)->add(entry); mEntriesDirty = true; } template bool ImageGridComponent::input(InputConfig* config, Input input) { if(input.value != 0) { Vector2i dir = Vector2i::Zero(); 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; if(dir != Vector2i::Zero()) { listInput(dir.x() + dir.y() * getGridSize().x()); return true; } }else{ if(config->isMappedTo("up", input) || config->isMappedTo("down", input) || config->isMappedTo("left", input) || config->isMappedTo("right", input)) { stopScrolling(); } } return GuiComponent::input(config, input); } template void ImageGridComponent::update(int deltaTime) { listUpdate(deltaTime); } template void ImageGridComponent::render(const Transform4x4f& parentTrans) { Transform4x4f trans = getTransform() * parentTrans; if(mEntriesDirty) { buildImages(); updateImages(); mEntriesDirty = false; } for(auto it = mImages.begin(); it != mImages.end(); it++) { it->render(trans); } GuiComponent::renderChildren(trans); } template void ImageGridComponent::onCursorChanged(const CursorState& state) { updateImages(); } template void ImageGridComponent::onSizeChanged() { buildImages(); updateImages(); } // create and position imagecomponents (mImages) template void ImageGridComponent::buildImages() { mImages.clear(); Vector2i gridSize = getGridSize(); Vector2f squareSize = getMaxSquareSize(); Vector2f padding = getPadding(); // attempt to center within our size Vector2f totalSize(gridSize.x() * (squareSize.x() + padding.x()), gridSize.y() * (squareSize.y() + padding.y())); Vector2f offset(mSize.x() - totalSize.x(), mSize.y() - totalSize.y()); offset /= 2; for(int y = 0; y < gridSize.y(); y++) { for(int x = 0; x < gridSize.x(); x++) { mImages.push_back(ImageComponent(mWindow)); ImageComponent& image = mImages.at(y * gridSize.x() + x); image.setPosition((squareSize.x() + padding.x()) * (x + 0.5f) + offset.x(), (squareSize.y() + padding.y()) * (y + 0.5f) + offset.y()); image.setOrigin(0.5f, 0.5f); image.setResize(squareSize.x(), squareSize.y()); image.setImage(""); } } } template void ImageGridComponent::updateImages() { if(mImages.empty()) buildImages(); Vector2i gridSize = getGridSize(); int cursorRow = mCursor / gridSize.x(); int start = (cursorRow - (gridSize.y() / 2)) * gridSize.x(); //if we're at the end put the row as close as we can and no higher if(start + (gridSize.x() * gridSize.y()) >= (int)mEntries.size()) start = gridSize.x() * ((int)mEntries.size()/gridSize.x() - gridSize.y() + 1); if(start < 0) start = 0; unsigned int i = (unsigned int)start; for(unsigned int img = 0; img < mImages.size(); img++) { ImageComponent& image = mImages.at(img); if(i >= (unsigned int)size()) { image.setImage(""); continue; } Vector2f squareSize = getSquareSize(mEntries.at(i).data.texture); if(i == (unsigned int)mCursor) { image.setColorShift(0xFFFFFFFF); image.setResize(squareSize.x() + getPadding().x() * 0.95f, squareSize.y() + getPadding().y() * 0.95f); }else{ image.setColorShift(0xAAAAAABB); image.setResize(squareSize.x(), squareSize.y()); } image.setImage(mEntries.at(i).data.texture); i++; } } #endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H