ES-DE/src/components/TextListComponent.h

358 lines
9.1 KiB
C
Raw Normal View History

2014-02-08 02:15:48 +00:00
#pragma once
2013-06-02 15:08:32 +00:00
2014-02-08 02:15:48 +00:00
#include "IList.h"
2013-06-02 15:08:32 +00:00
#include "../Renderer.h"
#include "../resources/Font.h"
2013-06-02 15:08:32 +00:00
#include "../InputManager.h"
#include <vector>
#include <string>
#include <memory>
#include "../Sound.h"
#include "../Log.h"
#include "../ThemeData.h"
#include <functional>
2013-06-02 15:08:32 +00:00
struct TextListData
{
unsigned int colorId;
std::shared_ptr<TextCache> textCache;
};
2013-06-02 15:08:32 +00:00
//A graphical list. Supports multiple colors for rows and scrolling.
template <typename T>
class TextListComponent : public IList<TextListData, T>
2013-06-02 15:08:32 +00:00
{
protected:
using IList<TextListData, T>::mEntries;
using IList<TextListData, T>::listUpdate;
using IList<TextListData, T>::listInput;
using IList<TextListData, T>::listRenderTitleOverlay;
using IList<TextListData, T>::getTransform;
using IList<TextListData, T>::mSize;
using IList<TextListData, T>::mCursor;
using IList<TextListData, T>::Entry;
2013-06-02 15:08:32 +00:00
public:
using IList<TextListData, T>::size;
using IList<TextListData, T>::isScrolling;
using IList<TextListData, T>::stopScrolling;
TextListComponent(Window* window);
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Eigen::Affine3f& parentTrans) override;
void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
void add(const std::string& name, const T& obj, unsigned int colorId);
2014-02-08 02:15:48 +00:00
enum Alignment
{
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
};
inline void setAlignment(Alignment align) { mAlignment = align; }
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
2013-06-02 15:08:32 +00:00
inline void setFont(const std::shared_ptr<Font>& font)
{
mFont = font;
this->mTitleOverlayFont = Font::get(FONT_SIZE_LARGE, mFont->getPath());
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
it->data.textCache.reset();
}
inline void setSelectorColor(unsigned int color) { mSelectorColor = color; }
inline void setSelectedColor(unsigned int color) { mSelectedColor = color; }
inline void setScrollSound(const std::shared_ptr<Sound>& sound) { mScrollSound = sound; }
inline void setColor(unsigned int id, unsigned int color) { mColors[id] = color; }
inline void setSound(const std::shared_ptr<Sound>& sound) { mScrollSound = sound; }
2014-02-08 02:15:48 +00:00
protected:
virtual void onScroll(int amt) { if(mScrollSound) mScrollSound->play(); }
virtual void onCursorChanged(const CursorState& state);
2014-02-08 02:15:48 +00:00
2013-06-02 15:08:32 +00:00
private:
static const int MARQUEE_DELAY = 900;
static const int MARQUEE_SPEED = 16;
static const int MARQUEE_RATE = 3;
int mMarqueeOffset;
int mMarqueeTime;
Alignment mAlignment;
float mHorizontalMargin;
2013-06-02 15:08:32 +00:00
std::function<void(CursorState state)> mCursorChangedCallback;
std::shared_ptr<Font> mFont;
unsigned int mSelectorColor;
unsigned int mSelectedColor;
std::shared_ptr<Sound> mScrollSound;
static const unsigned int COLOR_ID_COUNT = 2;
unsigned int mColors[COLOR_ID_COUNT];
2013-06-02 15:08:32 +00:00
};
template <typename T>
TextListComponent<T>::TextListComponent(Window* window) :
IList<TextListData, T>(window)
2013-06-02 15:08:32 +00:00
{
mMarqueeOffset = 0;
mMarqueeTime = -MARQUEE_DELAY;
2013-06-02 15:08:32 +00:00
mHorizontalMargin = 0;
mAlignment = ALIGN_CENTER;
2013-06-02 15:08:32 +00:00
mFont = Font::get(FONT_SIZE_MEDIUM);
mSelectorColor = 0x000000FF;
mSelectedColor = 0;
mColors[0] = 0x0000FFFF;
mColors[1] = 0x00FF00FF;
}
2013-06-02 15:08:32 +00:00
template <typename T>
void TextListComponent<T>::render(const Eigen::Affine3f& parentTrans)
2013-06-02 15:08:32 +00:00
{
Eigen::Affine3f trans = parentTrans * getTransform();
std::shared_ptr<Font>& font = mFont;
if(size() == 0)
{
Renderer::setMatrix(trans);
font->drawText("The list is empty.", Eigen::Vector2f(0, 0), 0xFF0000FF);
return;
}
const int cutoff = 0;
const int entrySize = font->getHeight() + 5;
2013-06-02 15:08:32 +00:00
int startEntry = 0;
//number of entries that can fit on the screen simultaniously
int screenCount = (int)(mSize.y() / entrySize + 0.5f);
if(size() >= screenCount)
2013-06-02 15:08:32 +00:00
{
startEntry = mCursor - (int)(screenCount * 0.5);
2013-06-02 15:08:32 +00:00
if(startEntry < 0)
startEntry = 0;
if(startEntry >= size() - screenCount)
startEntry = size() - screenCount;
2013-06-02 15:08:32 +00:00
}
float y = (float)cutoff;
2013-06-02 15:08:32 +00:00
int listCutoff = startEntry + screenCount;
if(listCutoff > size())
listCutoff = size();
2013-06-02 15:08:32 +00:00
Eigen::Vector3f dim(mSize.x(), mSize.y(), 0);
2013-08-06 13:15:20 +00:00
dim = trans * dim - trans.translation();
Renderer::pushClipRect(Eigen::Vector2i((int)trans.translation().x(), (int)trans.translation().y()), Eigen::Vector2i((int)dim.x(), (int)dim.y()));
2013-06-02 15:08:32 +00:00
for(int i = startEntry; i < listCutoff; i++)
{
//draw selector bar
if(mCursor == i)
2013-06-02 15:08:32 +00:00
{
Renderer::setMatrix(trans);
Renderer::drawRect(0, (int)y, (int)mSize.x(), font->getHeight(), mSelectorColor);
2013-06-02 15:08:32 +00:00
}
typename IList<TextListData, T>::Entry& entry = mEntries.at((unsigned int)i);
2013-06-02 15:08:32 +00:00
unsigned int color;
if(mCursor == i && mSelectedColor)
color = mSelectedColor;
else
color = mColors[entry.data.colorId];
if(!entry.data.textCache)
entry.data.textCache = std::unique_ptr<TextCache>(font->buildTextCache(entry.name, 0, 0, 0x000000FF));
entry.data.textCache->setColor(color);
Eigen::Vector3f offset(0, y, 0);
switch(mAlignment)
{
case ALIGN_LEFT:
offset[0] = mHorizontalMargin;
break;
case ALIGN_CENTER:
offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x()) / 2;
if(offset[0] < 0)
offset[0] = 0;
break;
case ALIGN_RIGHT:
offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x());
offset[0] -= mHorizontalMargin;
if(offset[0] < 0)
offset[0] = 0;
break;
}
if(mCursor == i)
offset[0] -= mMarqueeOffset;
Eigen::Affine3f drawTrans = trans;
drawTrans.translate(offset);
Renderer::setMatrix(drawTrans);
font->renderTextCache(entry.data.textCache.get());
2013-06-02 15:08:32 +00:00
y += entrySize;
}
Renderer::popClipRect();
listRenderTitleOverlay(trans);
GuiComponent::renderChildren(trans);
2013-06-02 15:08:32 +00:00
}
template <typename T>
bool TextListComponent<T>::input(InputConfig* config, Input input)
{
if(size() > 0)
2013-06-02 15:08:32 +00:00
{
if(input.value != 0)
{
if(config->isMappedTo("down", input))
{
2014-02-08 02:15:48 +00:00
listInput(1);
2013-06-02 15:08:32 +00:00
return true;
}
if(config->isMappedTo("up", input))
{
2014-02-08 02:15:48 +00:00
listInput(-1);
2013-06-02 15:08:32 +00:00
return true;
}
if(config->isMappedTo("pagedown", input))
{
2014-02-08 02:15:48 +00:00
listInput(10);
2013-06-02 15:08:32 +00:00
return true;
}
if(config->isMappedTo("pageup", input))
{
2014-02-08 02:15:48 +00:00
listInput(-10);
2013-06-02 15:08:32 +00:00
return true;
}
}else{
if(config->isMappedTo("down", input) || config->isMappedTo("up", input) ||
config->isMappedTo("pagedown", input) || config->isMappedTo("pageup", input))
2013-06-02 15:08:32 +00:00
{
stopScrolling();
}
}
}
return GuiComponent::input(config, input);
2013-06-02 15:08:32 +00:00
}
template <typename T>
void TextListComponent<T>::update(int deltaTime)
{
2014-02-08 02:15:48 +00:00
listUpdate(deltaTime);
if(!isScrolling() && size() > 0)
2013-06-02 15:08:32 +00:00
{
//if we're not scrolling and this object's text goes outside our size, marquee it!
const std::string& text = mEntries.at((unsigned int)mCursor).name;
Eigen::Vector2f textSize = mFont->sizeText(text);
//it's long enough to marquee
if(textSize.x() - mMarqueeOffset > mSize.x() - 12 - (mAlignment != ALIGN_CENTER ? mHorizontalMargin : 0))
{
mMarqueeTime += deltaTime;
while(mMarqueeTime > MARQUEE_SPEED)
{
mMarqueeOffset += MARQUEE_RATE;
mMarqueeTime -= MARQUEE_SPEED;
}
}
2013-06-02 15:08:32 +00:00
}
GuiComponent::update(deltaTime);
2013-06-02 15:08:32 +00:00
}
//list management stuff
template <typename T>
void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned int color)
2013-06-02 15:08:32 +00:00
{
assert(color < COLOR_ID_COUNT);
typename IList<TextListData, T>::Entry entry;
entry.name = name;
entry.object = obj;
entry.data.colorId = color;
static_cast<IList< TextListData, T >*>(this)->add(entry);
2013-06-02 15:08:32 +00:00
}
template <typename T>
void TextListComponent<T>::onCursorChanged(const CursorState& state)
2013-06-02 15:08:32 +00:00
{
2014-02-08 02:15:48 +00:00
mMarqueeOffset = 0;
mMarqueeTime = -MARQUEE_DELAY;
if(mCursorChangedCallback)
mCursorChangedCallback(state);
2013-06-02 15:08:32 +00:00
}
template <typename T>
void TextListComponent<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);
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "textlist");
if(!elem)
return;
using namespace ThemeFlags;
if(properties & COLOR)
{
if(elem->has("selectorColor"))
setSelectorColor(elem->get<unsigned int>("selectorColor"));
if(elem->has("selectedColor"))
setSelectedColor(elem->get<unsigned int>("selectedColor"));
if(elem->has("primaryColor"))
setColor(0, elem->get<unsigned int>("primaryColor"));
if(elem->has("secondaryColor"))
setColor(1, elem->get<unsigned int>("secondaryColor"));
}
setFont(Font::getFromTheme(elem, properties, mFont));
if(properties & SOUND && elem->has("scrollSound"))
setSound(Sound::get(elem->get<std::string>("scrollSound")));
if(properties & ALIGNMENT)
{
if(elem->has("alignment"))
{
const std::string& str = elem->get<std::string>("alignment");
if(str == "left")
setAlignment(ALIGN_LEFT);
else if(str == "center")
setAlignment(ALIGN_CENTER);
else if(str == "right")
setAlignment(ALIGN_RIGHT);
else
LOG(LogError) << "Unknown TextListComponent alignment \"" << str << "\"!";
}
if(elem->has("horizontalMargin"))
{
mHorizontalMargin = elem->get<float>("horizontalMargin") * (this->mParent ? this->mParent->getSize().x() : (float)Renderer::getScreenWidth());
}
}
}