2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-28 16:39:18 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-28 16:39:18 +00:00
|
|
|
// TextComponent.cpp
|
|
|
|
//
|
|
|
|
// Displays text.
|
|
|
|
//
|
|
|
|
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "components/TextComponent.h"
|
2017-03-13 21:11:07 +00:00
|
|
|
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "Log.h"
|
|
|
|
#include "Settings.h"
|
2021-07-07 18:31:46 +00:00
|
|
|
#include "utils/StringUtil.h"
|
2013-06-14 15:48:13 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
TextComponent::TextComponent(Window* window)
|
|
|
|
: GuiComponent(window)
|
|
|
|
, mFont(Font::get(FONT_SIZE_MEDIUM))
|
|
|
|
, mUppercase(false)
|
|
|
|
, mColor(0x000000FF)
|
|
|
|
, mAutoCalcExtent(true, true)
|
|
|
|
, mHorizontalAlignment(ALIGN_LEFT)
|
|
|
|
, mVerticalAlignment(ALIGN_CENTER)
|
|
|
|
, mLineSpacing(1.5f)
|
|
|
|
, mNoTopMargin(false)
|
|
|
|
, mBgColor(0)
|
|
|
|
, mMargin(0.0f)
|
|
|
|
, mRenderBackground(false)
|
2013-06-14 15:48:13 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
TextComponent::TextComponent(Window* window,
|
|
|
|
const std::string& text,
|
|
|
|
const std::shared_ptr<Font>& font,
|
|
|
|
unsigned int color,
|
|
|
|
Alignment align,
|
|
|
|
Vector3f pos,
|
|
|
|
Vector2f size,
|
|
|
|
unsigned int bgcolor,
|
|
|
|
float margin)
|
|
|
|
: GuiComponent(window)
|
|
|
|
, mFont(nullptr)
|
|
|
|
, mUppercase(false)
|
|
|
|
, mColor(0x000000FF)
|
|
|
|
, mAutoCalcExtent(true, true)
|
|
|
|
, mHorizontalAlignment(align)
|
|
|
|
, mVerticalAlignment(ALIGN_CENTER)
|
|
|
|
, mLineSpacing(1.5f)
|
|
|
|
, mNoTopMargin(false)
|
|
|
|
, mBgColor(0)
|
|
|
|
, mMargin(margin)
|
|
|
|
, mRenderBackground(false)
|
2013-06-14 15:48:13 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
setFont(font);
|
|
|
|
setColor(color);
|
|
|
|
setBackgroundColor(bgcolor);
|
|
|
|
setText(text);
|
|
|
|
setPosition(pos);
|
|
|
|
setSize(size);
|
2013-06-14 15:48:13 +00:00
|
|
|
}
|
|
|
|
|
2013-07-10 11:29:43 +00:00
|
|
|
void TextComponent::onSizeChanged()
|
2013-06-14 15:48:13 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mAutoCalcExtent = Vector2i((getSize().x() == 0), (getSize().y() == 0));
|
|
|
|
onTextChanged();
|
2013-06-14 15:48:13 +00:00
|
|
|
}
|
|
|
|
|
2014-03-08 01:35:16 +00:00
|
|
|
void TextComponent::setFont(const std::shared_ptr<Font>& font)
|
2013-06-14 15:48:13 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mFont = font;
|
|
|
|
onTextChanged();
|
2013-06-14 15:48:13 +00:00
|
|
|
}
|
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
// Set the color of the font/text.
|
2013-06-14 15:48:13 +00:00
|
|
|
void TextComponent::setColor(unsigned int color)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mColor = color;
|
|
|
|
mColorOpacity = mColor & 0x000000FF;
|
|
|
|
onColorChanged();
|
2013-10-10 21:14:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
// Set the color of the background box.
|
2017-03-13 21:11:07 +00:00
|
|
|
void TextComponent::setBackgroundColor(unsigned int color)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mBgColor = color;
|
|
|
|
mBgColorOpacity = mBgColor & 0x000000FF;
|
2017-03-13 21:11:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
// Scale the opacity.
|
2013-10-10 21:14:33 +00:00
|
|
|
void TextComponent::setOpacity(unsigned char opacity)
|
|
|
|
{
|
2021-07-07 18:31:46 +00:00
|
|
|
// This function is mostly called to do fade in and fade out of the text component element.
|
|
|
|
// Therefore we assume here that opacity is a fractional value (expressed as an unsigned
|
|
|
|
// char 0 - 255) of the opacity originally set with setColor() or setBackgroundColor().
|
|
|
|
unsigned char o = static_cast<unsigned char>(static_cast<float>(opacity) / 255.0f *
|
|
|
|
static_cast<float>(mColorOpacity));
|
2020-11-17 22:06:54 +00:00
|
|
|
mColor = (mColor & 0xFFFFFF00) | static_cast<unsigned char>(o);
|
2017-05-01 02:54:27 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
unsigned char bgo = static_cast<unsigned char>(static_cast<float>(opacity) / 255.0f *
|
|
|
|
static_cast<float>(mBgColorOpacity));
|
2020-11-17 22:06:54 +00:00
|
|
|
mBgColor = (mBgColor & 0xFFFFFF00) | static_cast<unsigned char>(bgo);
|
2013-10-10 21:14:33 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
onColorChanged();
|
|
|
|
GuiComponent::setOpacity(opacity);
|
2013-10-10 21:14:33 +00:00
|
|
|
}
|
|
|
|
|
2013-06-14 15:48:13 +00:00
|
|
|
void TextComponent::setText(const std::string& text)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mText = text;
|
|
|
|
onTextChanged();
|
2013-07-02 05:57:31 +00:00
|
|
|
}
|
|
|
|
|
2014-05-03 19:51:50 +00:00
|
|
|
void TextComponent::setUppercase(bool uppercase)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mUppercase = uppercase;
|
|
|
|
onTextChanged();
|
2014-05-03 19:51:50 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
void TextComponent::render(const Transform4x4f& parentTrans)
|
2013-06-14 15:48:13 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (!isVisible())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Transform4x4f trans = parentTrans * getTransform();
|
|
|
|
|
|
|
|
if (mRenderBackground) {
|
|
|
|
Renderer::setMatrix(trans);
|
|
|
|
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), mBgColor, mBgColor);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mTextCache) {
|
|
|
|
const Vector2f& textSize = mTextCache->metrics.size;
|
|
|
|
float yOff = 0;
|
|
|
|
switch (mVerticalAlignment) {
|
2021-07-07 18:31:46 +00:00
|
|
|
case ALIGN_TOP: {
|
2020-06-28 16:39:18 +00:00
|
|
|
yOff = 0;
|
|
|
|
break;
|
2021-07-07 18:31:46 +00:00
|
|
|
}
|
|
|
|
case ALIGN_BOTTOM: {
|
2020-06-28 16:39:18 +00:00
|
|
|
yOff = (getSize().y() - textSize.y());
|
|
|
|
break;
|
2021-07-07 18:31:46 +00:00
|
|
|
}
|
|
|
|
case ALIGN_CENTER: {
|
2020-06-28 16:39:18 +00:00
|
|
|
yOff = (getSize().y() - textSize.y()) / 2.0f;
|
|
|
|
break;
|
2021-07-07 18:31:46 +00:00
|
|
|
}
|
|
|
|
default: {
|
2020-06-28 16:39:18 +00:00
|
|
|
break;
|
2021-07-07 18:31:46 +00:00
|
|
|
}
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
Vector3f off(0, yOff, 0);
|
|
|
|
|
|
|
|
if (Settings::getInstance()->getBool("DebugText")) {
|
|
|
|
// Draw the "textbox" area, what we are aligned within.
|
|
|
|
Renderer::setMatrix(trans);
|
2021-03-22 16:56:38 +00:00
|
|
|
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x0000FF33, 0x0000FF33);
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
trans.translate(off);
|
|
|
|
Renderer::setMatrix(trans);
|
|
|
|
|
|
|
|
// Draw the text area, where the text actually is located.
|
|
|
|
if (Settings::getInstance()->getBool("DebugText")) {
|
|
|
|
switch (mHorizontalAlignment) {
|
2021-07-07 18:31:46 +00:00
|
|
|
case ALIGN_LEFT: {
|
|
|
|
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
|
|
|
|
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ALIGN_CENTER: {
|
|
|
|
Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f,
|
|
|
|
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
|
|
|
|
0x00000033, 0x00000033);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ALIGN_RIGHT: {
|
|
|
|
Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f,
|
|
|
|
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
|
|
|
|
0x00000033, 0x00000033);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
break;
|
|
|
|
}
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
mFont->renderTextCache(mTextCache.get());
|
|
|
|
}
|
2013-06-14 15:48:13 +00:00
|
|
|
}
|
2013-06-19 01:12:30 +00:00
|
|
|
|
|
|
|
void TextComponent::calculateExtent()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (mAutoCalcExtent.x()) {
|
|
|
|
mSize = mFont->sizeText(mUppercase ? Utils::String::toUpper(mText) : mText, mLineSpacing);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (mAutoCalcExtent.y())
|
2021-07-07 18:31:46 +00:00
|
|
|
mSize[1] = mFont
|
|
|
|
->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText,
|
|
|
|
getSize().x(), mLineSpacing)
|
|
|
|
.y();
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
2013-07-02 05:57:31 +00:00
|
|
|
}
|
2013-08-14 12:16:49 +00:00
|
|
|
|
2013-08-22 01:08:36 +00:00
|
|
|
void TextComponent::onTextChanged()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
calculateExtent();
|
|
|
|
|
|
|
|
if (!mFont || mText.empty()) {
|
|
|
|
mTextCache.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string text = mUppercase ? Utils::String::toUpper(mText) : mText;
|
|
|
|
|
|
|
|
std::shared_ptr<Font> f = mFont;
|
2021-01-16 18:22:23 +00:00
|
|
|
const bool isMultiline = (mSize.y() == 0 || mSize.y() > f->getHeight() * 1.2f);
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
bool addAbbrev = false;
|
|
|
|
if (!isMultiline) {
|
|
|
|
size_t newline = text.find('\n');
|
|
|
|
// Single line of text - stop at the first newline since it'll mess everything up.
|
|
|
|
text = text.substr(0, newline);
|
|
|
|
addAbbrev = newline != std::string::npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector2f size = f->sizeText(text);
|
2021-01-13 18:49:35 +00:00
|
|
|
if (!isMultiline && mSize.x() > 0.0f && text.size() && (size.x() > mSize.x() || addAbbrev)) {
|
2020-06-28 16:39:18 +00:00
|
|
|
// Abbreviate text.
|
|
|
|
const std::string abbrev = "...";
|
|
|
|
Vector2f abbrevSize = f->sizeText(abbrev);
|
2021-01-05 15:52:39 +00:00
|
|
|
// mMargin adds a margin around the text if it's abbreviated.
|
|
|
|
float marginAdjustedSize = mSize.x() - (mSize.x() * mMargin);
|
2020-06-28 16:39:18 +00:00
|
|
|
|
2021-01-05 15:52:39 +00:00
|
|
|
while (text.size() && size.x() + abbrevSize.x() > marginAdjustedSize) {
|
2020-06-28 16:39:18 +00:00
|
|
|
size_t newSize = Utils::String::prevCursor(text, text.size());
|
|
|
|
text.erase(newSize, text.size() - newSize);
|
|
|
|
size = f->sizeText(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
text.append(abbrev);
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
mTextCache = std::shared_ptr<TextCache>(
|
|
|
|
f->buildTextCache(text, Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(),
|
|
|
|
mHorizontalAlignment, mLineSpacing, mNoTopMargin));
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-07-07 18:31:46 +00:00
|
|
|
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(
|
|
|
|
f->wrapText(text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(),
|
|
|
|
mHorizontalAlignment, mLineSpacing, mNoTopMargin));
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
2013-08-22 01:08:36 +00:00
|
|
|
}
|
|
|
|
|
2013-10-10 21:14:33 +00:00
|
|
|
void TextComponent::onColorChanged()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (mTextCache)
|
|
|
|
mTextCache->setColor(mColor);
|
2013-10-10 21:14:33 +00:00
|
|
|
}
|
|
|
|
|
2017-08-15 02:34:34 +00:00
|
|
|
void TextComponent::setHorizontalAlignment(Alignment align)
|
2014-05-14 22:01:40 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mHorizontalAlignment = align;
|
|
|
|
onTextChanged();
|
2014-05-14 22:01:40 +00:00
|
|
|
}
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
void TextComponent::setVerticalAlignment(Alignment align) { mVerticalAlignment = align; }
|
2017-08-15 02:34:34 +00:00
|
|
|
|
2014-05-14 22:01:40 +00:00
|
|
|
void TextComponent::setLineSpacing(float spacing)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mLineSpacing = spacing;
|
|
|
|
onTextChanged();
|
2014-05-14 22:01:40 +00:00
|
|
|
}
|
|
|
|
|
2021-03-13 15:52:15 +00:00
|
|
|
void TextComponent::setNoTopMargin(bool margin)
|
|
|
|
{
|
|
|
|
mNoTopMargin = margin;
|
|
|
|
onTextChanged();
|
|
|
|
}
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
|
|
|
const std::string& view,
|
|
|
|
const std::string& element,
|
|
|
|
unsigned int properties)
|
2014-01-01 05:39:22 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
GuiComponent::applyTheme(theme, view, element, properties);
|
|
|
|
|
|
|
|
using namespace ThemeFlags;
|
|
|
|
|
|
|
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "text");
|
|
|
|
if (!elem)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (properties & COLOR && elem->has("color"))
|
|
|
|
setColor(elem->get<unsigned int>("color"));
|
|
|
|
|
|
|
|
setRenderBackground(false);
|
|
|
|
if (properties & COLOR && elem->has("backgroundColor")) {
|
|
|
|
setBackgroundColor(elem->get<unsigned int>("backgroundColor"));
|
|
|
|
setRenderBackground(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (properties & ALIGNMENT && elem->has("alignment")) {
|
|
|
|
std::string str = elem->get<std::string>("alignment");
|
|
|
|
if (str == "left")
|
|
|
|
setHorizontalAlignment(ALIGN_LEFT);
|
|
|
|
else if (str == "center")
|
|
|
|
setHorizontalAlignment(ALIGN_CENTER);
|
|
|
|
else if (str == "right")
|
|
|
|
setHorizontalAlignment(ALIGN_RIGHT);
|
|
|
|
else
|
|
|
|
LOG(LogError) << "Unknown text alignment string: " << str;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (properties & TEXT && elem->has("text"))
|
|
|
|
setText(elem->get<std::string>("text"));
|
|
|
|
|
|
|
|
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
|
|
|
setUppercase(elem->get<bool>("forceUppercase"));
|
|
|
|
|
|
|
|
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
|
|
|
setLineSpacing(elem->get<float>("lineSpacing"));
|
|
|
|
|
|
|
|
setFont(Font::getFromTheme(elem, properties, mFont));
|
2014-01-01 05:39:22 +00:00
|
|
|
}
|