mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-18 07:05:39 +00:00
Greatly improved text sizing and rendering.
This commit is contained in:
parent
c5098a62d5
commit
84f019680d
|
@ -206,7 +206,7 @@ void GuiComponent::setDimming(float dimming)
|
||||||
const glm::mat4& GuiComponent::getTransform()
|
const glm::mat4& GuiComponent::getTransform()
|
||||||
{
|
{
|
||||||
mTransform = Renderer::getIdentity();
|
mTransform = Renderer::getIdentity();
|
||||||
mTransform = glm::translate(mTransform, glm::round(mPosition));
|
mTransform = glm::translate(mTransform, mPosition);
|
||||||
|
|
||||||
if (mScale != 1.0f)
|
if (mScale != 1.0f)
|
||||||
mTransform = glm::scale(mTransform, glm::vec3 {mScale});
|
mTransform = glm::scale(mTransform, glm::vec3 {mScale});
|
||||||
|
|
|
@ -160,6 +160,8 @@ public:
|
||||||
mComponentThemeFlags ^= ComponentThemeFlags::METADATA_ELEMENT;
|
mComponentThemeFlags ^= ComponentThemeFlags::METADATA_ELEMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual int getTextCacheGlyphHeight() { return 0; }
|
||||||
|
|
||||||
// Returns the center point of the image (takes origin into account).
|
// Returns the center point of the image (takes origin into account).
|
||||||
const glm::vec2 getCenter() const;
|
const glm::vec2 getCenter() const;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ HelpStyle::HelpStyle()
|
||||||
, opacity {1.0f}
|
, opacity {1.0f}
|
||||||
, letterCase {"uppercase"}
|
, letterCase {"uppercase"}
|
||||||
{
|
{
|
||||||
|
|
||||||
if (FONT_SIZE_SMALL != 0)
|
if (FONT_SIZE_SMALL != 0)
|
||||||
font = Font::get(FONT_SIZE_SMALL);
|
font = Font::get(FONT_SIZE_SMALL);
|
||||||
else
|
else
|
||||||
|
@ -61,7 +60,7 @@ void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::s
|
||||||
iconColorDimmed = iconColor;
|
iconColorDimmed = iconColor;
|
||||||
|
|
||||||
if (elem->has("fontPath") || elem->has("fontSize"))
|
if (elem->has("fontPath") || elem->has("fontSize"))
|
||||||
font = Font::getFromTheme(elem, ThemeFlags::ALL, font);
|
font = Font::getFromTheme(elem, ThemeFlags::ALL, font, 0.0f, theme->isLegacyTheme());
|
||||||
|
|
||||||
if (elem->has("entrySpacing"))
|
if (elem->has("entrySpacing"))
|
||||||
entrySpacing = glm::clamp(elem->get<float>("entrySpacing"), 0.0f, 0.04f);
|
entrySpacing = glm::clamp(elem->get<float>("entrySpacing"), 0.0f, 0.04f);
|
||||||
|
|
|
@ -224,5 +224,5 @@ void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
||||||
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight));
|
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, theme->isLegacyTheme()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,10 +66,15 @@ void ScrollableContainer::reset()
|
||||||
mAutoScrollResetAccumulator = 0;
|
mAutoScrollResetAccumulator = 0;
|
||||||
mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed;
|
mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed;
|
||||||
mAtEnd = false;
|
mAtEnd = false;
|
||||||
// This is needed to resize to the designated area when the backgrund image gets invalidated.
|
// This is needed to resize to the designated area when the background image gets invalidated.
|
||||||
if (!mChildren.empty()) {
|
if (!mChildren.empty()) {
|
||||||
float combinedHeight {
|
float combinedHeight {0.0f};
|
||||||
mChildren.front()->getFont()->getHeight(mChildren.front()->getLineSpacing())};
|
const float cacheGlyphHeight {
|
||||||
|
static_cast<float>(mChildren.front()->getTextCacheGlyphHeight())};
|
||||||
|
if (cacheGlyphHeight > 0.0f)
|
||||||
|
combinedHeight = cacheGlyphHeight * mChildren.front()->getLineSpacing();
|
||||||
|
else
|
||||||
|
return;
|
||||||
if (mChildren.front()->getSize().y > mSize.y) {
|
if (mChildren.front()->getSize().y > mSize.y) {
|
||||||
if (mVerticalSnap) {
|
if (mVerticalSnap) {
|
||||||
float numLines {std::floor(mSize.y / combinedHeight)};
|
float numLines {std::floor(mSize.y / combinedHeight)};
|
||||||
|
@ -121,8 +126,13 @@ void ScrollableContainer::update(int deltaTime)
|
||||||
const glm::vec2 contentSize {glm::round(mChildren.front()->getSize())};
|
const glm::vec2 contentSize {glm::round(mChildren.front()->getSize())};
|
||||||
float rowModifier {1.0f};
|
float rowModifier {1.0f};
|
||||||
|
|
||||||
float lineSpacing {mChildren.front()->getLineSpacing()};
|
const float lineSpacing {mChildren.front()->getLineSpacing()};
|
||||||
float combinedHeight {mChildren.front()->getFont()->getHeight(lineSpacing)};
|
float combinedHeight {0.0f};
|
||||||
|
const float cacheGlyphHeight {static_cast<float>(mChildren.front()->getTextCacheGlyphHeight())};
|
||||||
|
if (cacheGlyphHeight > 0.0f)
|
||||||
|
combinedHeight = cacheGlyphHeight * lineSpacing;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
// Calculate the spacing which will be used to clip the container.
|
// Calculate the spacing which will be used to clip the container.
|
||||||
if (lineSpacing > 1.2f && mClipSpacing == 0.0f) {
|
if (lineSpacing > 1.2f && mClipSpacing == 0.0f) {
|
||||||
|
@ -175,7 +185,7 @@ void ScrollableContainer::update(int deltaTime)
|
||||||
mAutoScrollAccumulator += deltaTime;
|
mAutoScrollAccumulator += deltaTime;
|
||||||
while (mAutoScrollAccumulator >=
|
while (mAutoScrollAccumulator >=
|
||||||
static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed))) {
|
static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed))) {
|
||||||
if (contentSize.y > mAdjustedHeight)
|
if (!mAtEnd && contentSize.y > mAdjustedHeight)
|
||||||
mScrollPos += mScrollDir;
|
mScrollPos += mScrollDir;
|
||||||
mAutoScrollAccumulator -=
|
mAutoScrollAccumulator -=
|
||||||
static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed));
|
static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed));
|
||||||
|
@ -193,13 +203,10 @@ void ScrollableContainer::update(int deltaTime)
|
||||||
mAtEnd = true;
|
mAtEnd = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentSize.y < mAdjustedHeight) {
|
if (contentSize.y < mAdjustedHeight)
|
||||||
mScrollPos.y = 0.0f;
|
mScrollPos.y = 0.0f;
|
||||||
}
|
else if (mScrollPos.y + mAdjustedHeight > contentSize.y)
|
||||||
else if (mScrollPos.y + mAdjustedHeight > contentSize.y) {
|
|
||||||
mScrollPos.y = contentSize.y - mAdjustedHeight;
|
|
||||||
mAtEnd = true;
|
mAtEnd = true;
|
||||||
}
|
|
||||||
|
|
||||||
if (mAtEnd) {
|
if (mAtEnd) {
|
||||||
mAutoScrollResetAccumulator += deltaTime;
|
mAutoScrollResetAccumulator += deltaTime;
|
||||||
|
@ -237,8 +244,8 @@ void ScrollableContainer::render(const glm::mat4& parentTrans)
|
||||||
dimScaled.x = std::fabs(trans[3].x + mSize.x);
|
dimScaled.x = std::fabs(trans[3].x + mSize.x);
|
||||||
dimScaled.y = std::fabs(trans[3].y + mAdjustedHeight);
|
dimScaled.y = std::fabs(trans[3].y + mAdjustedHeight);
|
||||||
|
|
||||||
glm::ivec2 clipDim {static_cast<int>(ceilf(dimScaled.x - trans[3].x)),
|
glm::ivec2 clipDim {static_cast<int>(dimScaled.x - trans[3].x),
|
||||||
static_cast<int>(ceilf(dimScaled.y - trans[3].y))};
|
static_cast<int>(dimScaled.y - trans[3].y)};
|
||||||
|
|
||||||
// By effectively clipping the upper and lower boundaries of the container we mostly avoid
|
// By effectively clipping the upper and lower boundaries of the container we mostly avoid
|
||||||
// scrolling outside the vertical starting and ending positions.
|
// scrolling outside the vertical starting and ending positions.
|
||||||
|
@ -247,7 +254,7 @@ void ScrollableContainer::render(const glm::mat4& parentTrans)
|
||||||
|
|
||||||
mRenderer->pushClipRect(clipPos, clipDim);
|
mRenderer->pushClipRect(clipPos, clipDim);
|
||||||
|
|
||||||
trans = glm::translate(trans, glm::round(-glm::vec3 {mScrollPos.x, mScrollPos.y, 0.0f}));
|
trans = glm::translate(trans, -glm::vec3 {mScrollPos.x, mScrollPos.y, 0.0f});
|
||||||
mRenderer->setMatrix(trans);
|
mRenderer->setMatrix(trans);
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("DebugText"))
|
if (Settings::getInstance()->getBool("DebugText"))
|
||||||
|
|
|
@ -30,6 +30,7 @@ TextComponent::TextComponent()
|
||||||
, mNoTopMargin {false}
|
, mNoTopMargin {false}
|
||||||
, mSelectable {false}
|
, mSelectable {false}
|
||||||
, mVerticalAutoSizing {false}
|
, mVerticalAutoSizing {false}
|
||||||
|
, mLegacyTheme {false}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ TextComponent::TextComponent(const std::string& text,
|
||||||
, mNoTopMargin {false}
|
, mNoTopMargin {false}
|
||||||
, mSelectable {false}
|
, mSelectable {false}
|
||||||
, mVerticalAutoSizing {false}
|
, mVerticalAutoSizing {false}
|
||||||
|
, mLegacyTheme {false}
|
||||||
{
|
{
|
||||||
setFont(font);
|
setFont(font);
|
||||||
setColor(color);
|
setColor(color);
|
||||||
|
@ -180,11 +182,11 @@ void TextComponent::render(const glm::mat4& parentTrans)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ALIGN_BOTTOM: {
|
case ALIGN_BOTTOM: {
|
||||||
yOff = (getSize().y - textSize.y);
|
yOff = mSize.y - textSize.y;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ALIGN_CENTER: {
|
case ALIGN_CENTER: {
|
||||||
yOff = std::round((getSize().y - textSize.y) / 2.0f);
|
yOff = (mSize.y - textSize.y) / 2.0f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -194,7 +196,7 @@ void TextComponent::render(const glm::mat4& parentTrans)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If height is smaller than the font height, then always center vertically.
|
// If height is smaller than the font height, then always center vertically.
|
||||||
yOff = std::round((getSize().y - textSize.y) / 2.0f);
|
yOff = (mSize.y - textSize.y) / 2.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the overall textbox area. If we're inside a scrollable container then this
|
// Draw the overall textbox area. If we're inside a scrollable container then this
|
||||||
|
@ -264,10 +266,23 @@ void TextComponent::onTextChanged()
|
||||||
if (!mFont || text.empty() || mSize.x < 0.0f)
|
if (!mFont || text.empty() || mSize.x < 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::shared_ptr<Font> font {mFont};
|
float lineHeight {0.0f};
|
||||||
const float lineHeight {font->getHeight(mLineSpacing)};
|
|
||||||
const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y > lineHeight};
|
|
||||||
const bool isScrollable {mParent && mParent->isScrollable()};
|
const bool isScrollable {mParent && mParent->isScrollable()};
|
||||||
|
std::shared_ptr<Font> font {mFont};
|
||||||
|
|
||||||
|
if (mLegacyTheme && !isScrollable && (mVerticalAutoSizing || mAutoCalcExtent.x)) {
|
||||||
|
// This is needed to retain a bug from the legacy theme engine where lineSpacing
|
||||||
|
// is not sized correctly when using automatic text element sizing. This is only
|
||||||
|
// applied to legacy themes for backward compatibility reasons.
|
||||||
|
font->useLegacyMaxGlyphHeight();
|
||||||
|
lineHeight = font->getHeight(mLineSpacing);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Used to initialize all glyphs, which is needed to populate mMaxGlyphHeight.
|
||||||
|
lineHeight = mFont->loadGlyphs(text + "\n") * mLineSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y > lineHeight};
|
||||||
|
|
||||||
if (isMultiline && !isScrollable) {
|
if (isMultiline && !isScrollable) {
|
||||||
const std::string wrappedText {
|
const std::string wrappedText {
|
||||||
|
@ -284,7 +299,7 @@ void TextComponent::onTextChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAutoCalcExtent.y)
|
if (mAutoCalcExtent.y)
|
||||||
mSize.y = font->getTextSize().y;
|
mSize.y = mTextCache->metrics.size.y;
|
||||||
|
|
||||||
if (mOpacity != 1.0f || mThemeOpacity != 1.0f)
|
if (mOpacity != 1.0f || mThemeOpacity != 1.0f)
|
||||||
setOpacity(mOpacity);
|
setOpacity(mOpacity);
|
||||||
|
@ -333,6 +348,8 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
GuiComponent::applyTheme(theme, view, element, properties);
|
GuiComponent::applyTheme(theme, view, element, properties);
|
||||||
|
|
||||||
|
mLegacyTheme = theme->isLegacyTheme();
|
||||||
|
|
||||||
std::string elementType {"text"};
|
std::string elementType {"text"};
|
||||||
std::string componentName {"TextComponent"};
|
std::string componentName {"TextComponent"};
|
||||||
|
|
||||||
|
@ -472,5 +489,5 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
||||||
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight));
|
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, theme->isLegacyTheme()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,11 @@ public:
|
||||||
Alignment getHorizontalAlignment() { return mHorizontalAlignment; }
|
Alignment getHorizontalAlignment() { return mHorizontalAlignment; }
|
||||||
Alignment getVerticalAlignment() { return mVerticalAlignment; }
|
Alignment getVerticalAlignment() { return mVerticalAlignment; }
|
||||||
|
|
||||||
|
int getTextCacheGlyphHeight() override
|
||||||
|
{
|
||||||
|
return (mTextCache == nullptr ? 0 : mTextCache->metrics.maxGlyphHeight);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onTextChanged();
|
virtual void onTextChanged();
|
||||||
|
|
||||||
|
@ -114,6 +119,7 @@ private:
|
||||||
bool mNoTopMargin;
|
bool mNoTopMargin;
|
||||||
bool mSelectable;
|
bool mSelectable;
|
||||||
bool mVerticalAutoSizing;
|
bool mVerticalAutoSizing;
|
||||||
|
bool mLegacyTheme;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
||||||
|
|
|
@ -183,8 +183,8 @@ CarouselComponent<T>::CarouselComponent()
|
||||||
, mMaxItemCount {3.0f}
|
, mMaxItemCount {3.0f}
|
||||||
, mItemsBeforeCenter {8}
|
, mItemsBeforeCenter {8}
|
||||||
, mItemsAfterCenter {8}
|
, mItemsAfterCenter {8}
|
||||||
, mItemSize {glm::round(
|
, mItemSize {glm::vec2 {Renderer::getScreenWidth() * 0.25f,
|
||||||
glm::vec2 {Renderer::getScreenWidth() * 0.25f, Renderer::getScreenHeight() * 0.155f})}
|
Renderer::getScreenHeight() * 0.155f}}
|
||||||
, mLinearInterpolation {false}
|
, mLinearInterpolation {false}
|
||||||
, mInstantItemTransitions {false}
|
, mInstantItemTransitions {false}
|
||||||
, mItemAxisHorizontal {false}
|
, mItemAxisHorizontal {false}
|
||||||
|
@ -568,11 +568,10 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
|
|
||||||
glm::mat4 carouselTrans {parentTrans};
|
glm::mat4 carouselTrans {parentTrans};
|
||||||
carouselTrans = glm::translate(
|
carouselTrans = glm::translate(
|
||||||
carouselTrans,
|
carouselTrans, glm::vec3 {GuiComponent::mPosition.x, GuiComponent::mPosition.y, 0.0f});
|
||||||
glm::round(glm::vec3 {GuiComponent::mPosition.x, GuiComponent::mPosition.y, 0.0f}));
|
carouselTrans =
|
||||||
carouselTrans = glm::translate(
|
glm::translate(carouselTrans, glm::vec3 {GuiComponent::mOrigin.x * mSize.x * -1.0f,
|
||||||
carouselTrans, glm::round(glm::vec3 {GuiComponent::mOrigin.x * mSize.x * -1.0f,
|
GuiComponent::mOrigin.y * mSize.y * -1.0f, 0.0f});
|
||||||
GuiComponent::mOrigin.y * mSize.y * -1.0f, 0.0f}));
|
|
||||||
|
|
||||||
if (carouselTrans[3].x + mSize.x <= 0.0f || carouselTrans[3].y + mSize.y <= 0.0f)
|
if (carouselTrans[3].x + mSize.x <= 0.0f || carouselTrans[3].y + mSize.y <= 0.0f)
|
||||||
return;
|
return;
|
||||||
|
@ -650,8 +649,7 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mType == CarouselType::VERTICAL) {
|
else if (mType == CarouselType::VERTICAL) {
|
||||||
itemSpacing.y =
|
itemSpacing.y = ((mSize.y - (mItemSize.y * mMaxItemCount)) / mMaxItemCount) + mItemSize.y;
|
||||||
std::round(((mSize.y - (mItemSize.y * mMaxItemCount)) / mMaxItemCount) + mItemSize.y);
|
|
||||||
yOff = (mSize.y - mItemSize.y) / 2.0f - (camOffset * itemSpacing.y);
|
yOff = (mSize.y - mItemSize.y) / 2.0f - (camOffset * itemSpacing.y);
|
||||||
if (mItemHorizontalAlignment == ALIGN_LEFT) {
|
if (mItemHorizontalAlignment == ALIGN_LEFT) {
|
||||||
if (mLegacyMode)
|
if (mLegacyMode)
|
||||||
|
@ -670,8 +668,7 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // HORIZONTAL.
|
else { // HORIZONTAL.
|
||||||
itemSpacing.x =
|
itemSpacing.x = ((mSize.x - (mItemSize.x * mMaxItemCount)) / mMaxItemCount) + mItemSize.x;
|
||||||
std::round(((mSize.x - (mItemSize.x * mMaxItemCount)) / mMaxItemCount) + mItemSize.x);
|
|
||||||
xOff = (mSize.x - mItemSize.x) / 2.0f - (camOffset * itemSpacing.x);
|
xOff = (mSize.x - mItemSize.x) / 2.0f - (camOffset * itemSpacing.x);
|
||||||
if (mItemVerticalAlignment == ALIGN_TOP) {
|
if (mItemVerticalAlignment == ALIGN_TOP) {
|
||||||
if (mLegacyMode)
|
if (mLegacyMode)
|
||||||
|
@ -780,9 +777,8 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
if (singleEntry)
|
if (singleEntry)
|
||||||
itemTrans = glm::translate(carouselTrans, glm::vec3 {xOff, yOff, 0.0f});
|
itemTrans = glm::translate(carouselTrans, glm::vec3 {xOff, yOff, 0.0f});
|
||||||
else
|
else
|
||||||
itemTrans =
|
itemTrans = glm::translate(itemTrans, glm::vec3 {(i * itemSpacing.x) + xOff,
|
||||||
glm::translate(itemTrans, glm::vec3 {std::round(i * itemSpacing.x + xOff),
|
(i * itemSpacing.y) + yOff, 0.0f});
|
||||||
std::round(i * itemSpacing.y + yOff), 0.0f});
|
|
||||||
|
|
||||||
float opacity {0.0f};
|
float opacity {0.0f};
|
||||||
|
|
||||||
|
@ -1011,8 +1007,8 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
|
|
||||||
if (elem->has("itemSize")) {
|
if (elem->has("itemSize")) {
|
||||||
const glm::vec2 itemSize {glm::clamp(elem->get<glm::vec2>("itemSize"), 0.05f, 1.0f)};
|
const glm::vec2 itemSize {glm::clamp(elem->get<glm::vec2>("itemSize"), 0.05f, 1.0f)};
|
||||||
mItemSize = glm::round(
|
mItemSize =
|
||||||
itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight()));
|
itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem->has("itemScale"))
|
if (elem->has("itemScale"))
|
||||||
|
@ -1164,8 +1160,8 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
itemSize.x = glm::clamp(itemSize.x, 0.005f, 1.0f);
|
itemSize.x = glm::clamp(itemSize.x, 0.005f, 1.0f);
|
||||||
itemSize.y = glm::clamp(itemSize.y, 0.005f, 1.0f);
|
itemSize.y = glm::clamp(itemSize.y, 0.005f, 1.0f);
|
||||||
}
|
}
|
||||||
mItemSize = glm::round(
|
mItemSize =
|
||||||
itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight()));
|
itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem->has("maxLogoCount")) {
|
if (elem->has("maxLogoCount")) {
|
||||||
|
@ -1210,7 +1206,7 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mFont = Font::getFromTheme(elem, properties, mFont);
|
mFont = Font::getFromTheme(elem, properties, mFont, 0.0f, mLegacyMode);
|
||||||
|
|
||||||
if (elem->has("textColor"))
|
if (elem->has("textColor"))
|
||||||
mTextColor = elem->get<unsigned int>("textColor");
|
mTextColor = elem->get<unsigned int>("textColor");
|
||||||
|
|
|
@ -350,10 +350,19 @@ template <typename T> void TextListComponent<T>::render(const glm::mat4& parentT
|
||||||
int screenCount {0};
|
int screenCount {0};
|
||||||
float y {0.0f};
|
float y {0.0f};
|
||||||
|
|
||||||
const float entrySize {
|
float entrySize {0.0f};
|
||||||
std::max(floorf(font->getHeight(1.0f)), floorf(static_cast<float>(font->getSize()))) *
|
float lineSpacingHeight {0.0f};
|
||||||
mLineSpacing};
|
|
||||||
const float lineSpacingHeight {floorf(font->getHeight(mLineSpacing) - font->getHeight(1.0f))};
|
// The vertical spacing between rows for legacy themes is very inaccurate and will look
|
||||||
|
// different depending on the resolution, but it's done for maximum backward compatibility.
|
||||||
|
if (mLegacyMode) {
|
||||||
|
entrySize = std::floor(font->getSize()) * mLineSpacing;
|
||||||
|
lineSpacingHeight = std::floor(font->getSize()) * mLineSpacing - font->getSize() * 1.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entrySize = font->getSize() * mLineSpacing;
|
||||||
|
lineSpacingHeight = font->getSize() * mLineSpacing - font->getSize() * 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
if (mLegacyMode) {
|
if (mLegacyMode) {
|
||||||
// This extra vertical margin is technically incorrect, but it adds a little extra leeway
|
// This extra vertical margin is technically incorrect, but it adds a little extra leeway
|
||||||
|
@ -366,7 +375,9 @@ template <typename T> void TextListComponent<T>::render(const glm::mat4& parentT
|
||||||
floorf((mSize.y + lineSpacingHeight / 2.0f + extraMargin) / entrySize));
|
floorf((mSize.y + lineSpacingHeight / 2.0f + extraMargin) / entrySize));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
screenCount = static_cast<int>(floorf((mSize.y + lineSpacingHeight / 2.0f) / entrySize));
|
// Number of entries that can fit on the screen simultaneously.
|
||||||
|
screenCount =
|
||||||
|
static_cast<int>(std::floor((mSize.y + lineSpacingHeight / 2.0f) / entrySize));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size() >= screenCount) {
|
if (size() >= screenCount) {
|
||||||
|
@ -555,9 +566,8 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
setColor(1, elem->get<unsigned int>("secondaryColor"));
|
setColor(1, elem->get<unsigned int>("secondaryColor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont));
|
setFont(Font::getFromTheme(elem, properties, mFont, 0.0f, mLegacyMode));
|
||||||
const float selectorHeight {
|
const float selectorHeight {mFont->getSize() * mLineSpacing};
|
||||||
std::max(mFont->getHeight(1.0), static_cast<float>(mFont->getSize())) * mLineSpacing};
|
|
||||||
setSelectorHeight(selectorHeight);
|
setSelectorHeight(selectorHeight);
|
||||||
|
|
||||||
if (properties & ALIGNMENT) {
|
if (properties & ALIGNMENT) {
|
||||||
|
|
|
@ -15,19 +15,20 @@
|
||||||
#include "utils/PlatformUtil.h"
|
#include "utils/PlatformUtil.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
Font::Font(int size, const std::string& path)
|
Font::Font(float size, const std::string& path)
|
||||||
: mRenderer {Renderer::getInstance()}
|
: mRenderer {Renderer::getInstance()}
|
||||||
, mPath(path)
|
, mPath(path)
|
||||||
, mTextSize {0.0f, 0.0f}
|
, mFontSize {size}
|
||||||
, mFontSize(size)
|
, mLetterHeight {0.0f}
|
||||||
, mMaxGlyphHeight {0}
|
, mMaxGlyphHeight {static_cast<int>(std::round(size))}
|
||||||
|
, mLegacyMaxGlyphHeight {0}
|
||||||
{
|
{
|
||||||
if (mFontSize < 3) {
|
if (mFontSize < 3.0f) {
|
||||||
mFontSize = 3;
|
mFontSize = 3.0f;
|
||||||
LOG(LogWarning) << "Requested font size too small, changing to minimum supported size";
|
LOG(LogWarning) << "Requested font size too small, changing to minimum supported size";
|
||||||
}
|
}
|
||||||
else if (mFontSize > Renderer::getScreenHeight() * 1.5f) {
|
else if (mFontSize > Renderer::getScreenHeight() * 1.5f) {
|
||||||
mFontSize = static_cast<int>(Renderer::getScreenHeight() * 1.5f);
|
mFontSize = Renderer::getScreenHeight() * 1.5f;
|
||||||
LOG(LogWarning) << "Requested font size too large, changing to maximum supported size";
|
LOG(LogWarning) << "Requested font size too large, changing to maximum supported size";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ Font::Font(int size, const std::string& path)
|
||||||
initLibrary();
|
initLibrary();
|
||||||
|
|
||||||
// Always initialize ASCII characters.
|
// Always initialize ASCII characters.
|
||||||
for (unsigned int i = 32; i < 128; ++i)
|
for (unsigned int i = 32; i < 127; ++i)
|
||||||
getGlyph(i);
|
getGlyph(i);
|
||||||
|
|
||||||
clearFaceCache();
|
clearFaceCache();
|
||||||
|
@ -45,7 +46,7 @@ Font::~Font()
|
||||||
{
|
{
|
||||||
unload(ResourceManager::getInstance());
|
unload(ResourceManager::getInstance());
|
||||||
|
|
||||||
auto fontEntry = sFontMap.find(std::pair<std::string, int>(mPath, mFontSize));
|
auto fontEntry = sFontMap.find(std::pair<std::string, float>(mPath, mFontSize));
|
||||||
|
|
||||||
if (fontEntry != sFontMap.cend())
|
if (fontEntry != sFontMap.cend())
|
||||||
sFontMap.erase(fontEntry);
|
sFontMap.erase(fontEntry);
|
||||||
|
@ -56,11 +57,11 @@ Font::~Font()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Font> Font::get(int size, const std::string& path)
|
std::shared_ptr<Font> Font::get(float size, const std::string& path)
|
||||||
{
|
{
|
||||||
const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)};
|
const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)};
|
||||||
std::pair<std::string, int> def {canonicalPath.empty() ? getDefaultPath() : canonicalPath,
|
const std::pair<std::string, float> def {
|
||||||
size};
|
canonicalPath.empty() ? getDefaultPath() : canonicalPath, size};
|
||||||
|
|
||||||
auto foundFont = sFontMap.find(def);
|
auto foundFont = sFontMap.find(def);
|
||||||
if (foundFont != sFontMap.cend()) {
|
if (foundFont != sFontMap.cend()) {
|
||||||
|
@ -76,11 +77,9 @@ std::shared_ptr<Font> Font::get(int size, const std::string& path)
|
||||||
|
|
||||||
glm::vec2 Font::sizeText(std::string text, float lineSpacing)
|
glm::vec2 Font::sizeText(std::string text, float lineSpacing)
|
||||||
{
|
{
|
||||||
|
const float lineHeight {getHeight(lineSpacing)};
|
||||||
float lineWidth {0.0f};
|
float lineWidth {0.0f};
|
||||||
float highestWidth {0.0f};
|
float highestWidth {0.0f};
|
||||||
|
|
||||||
const float lineHeight {getHeight(lineSpacing)};
|
|
||||||
|
|
||||||
float y {lineHeight};
|
float y {lineHeight};
|
||||||
|
|
||||||
size_t i {0};
|
size_t i {0};
|
||||||
|
@ -106,6 +105,20 @@ glm::vec2 Font::sizeText(std::string text, float lineSpacing)
|
||||||
return glm::vec2 {highestWidth, y};
|
return glm::vec2 {highestWidth, y};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Font::loadGlyphs(const std::string& text)
|
||||||
|
{
|
||||||
|
mMaxGlyphHeight = static_cast<int>(std::round(mFontSize));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < text.length();) {
|
||||||
|
unsigned int character {Utils::String::chars2Unicode(text, i)}; // Advances i.
|
||||||
|
Glyph* glyph {getGlyph(character)};
|
||||||
|
|
||||||
|
if (glyph->rows > mMaxGlyphHeight)
|
||||||
|
mMaxGlyphHeight = glyph->rows;
|
||||||
|
}
|
||||||
|
return mMaxGlyphHeight;
|
||||||
|
}
|
||||||
|
|
||||||
TextCache* Font::buildTextCache(const std::string& text,
|
TextCache* Font::buildTextCache(const std::string& text,
|
||||||
float offsetX,
|
float offsetX,
|
||||||
float offsetY,
|
float offsetY,
|
||||||
|
@ -134,6 +147,9 @@ TextCache* Font::buildTextCache(const std::string& text,
|
||||||
yBot = getHeight(1.5);
|
yBot = getHeight(1.5);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// TODO: This is lacking some precision which is especially visible at higher resolutions
|
||||||
|
// like 4K where the text is not always placed entirely correctly vertically. Try to find
|
||||||
|
// a way to improve on this.
|
||||||
yTop = getGlyph('S')->bearing.y;
|
yTop = getGlyph('S')->bearing.y;
|
||||||
yBot = getHeight(lineSpacing);
|
yBot = getHeight(lineSpacing);
|
||||||
}
|
}
|
||||||
|
@ -203,7 +219,8 @@ TextCache* Font::buildTextCache(const std::string& text,
|
||||||
|
|
||||||
TextCache* cache {new TextCache()};
|
TextCache* cache {new TextCache()};
|
||||||
cache->vertexLists.resize(vertMap.size());
|
cache->vertexLists.resize(vertMap.size());
|
||||||
cache->metrics = {sizeText(text, lineSpacing)};
|
cache->metrics.size = {sizeText(text, lineSpacing)};
|
||||||
|
cache->metrics.maxGlyphHeight = mMaxGlyphHeight;
|
||||||
|
|
||||||
size_t i {0};
|
size_t i {0};
|
||||||
for (auto it = vertMap.cbegin(); it != vertMap.cend(); ++it) {
|
for (auto it = vertMap.cbegin(); it != vertMap.cend(); ++it) {
|
||||||
|
@ -366,7 +383,6 @@ std::string Font::wrapText(const std::string& text,
|
||||||
wrappedText.append("...");
|
wrappedText.append("...");
|
||||||
}
|
}
|
||||||
|
|
||||||
mTextSize = {maxLength, accumHeight};
|
|
||||||
return wrappedText;
|
return wrappedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,31 +412,36 @@ glm::vec2 Font::getWrappedTextCursorOffset(const std::string& wrappedText,
|
||||||
|
|
||||||
float Font::getLetterHeight()
|
float Font::getLetterHeight()
|
||||||
{
|
{
|
||||||
Glyph* glyph {getGlyph('S')};
|
if (mLetterHeight == 0.0f)
|
||||||
assert(glyph);
|
return mFontSize * 0.737; // Only needed if face does not contain the letter 'S'.
|
||||||
return glyph->texSize.y * glyph->texture->textureSize.y;
|
else
|
||||||
|
return mLetterHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem,
|
std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem,
|
||||||
unsigned int properties,
|
unsigned int properties,
|
||||||
const std::shared_ptr<Font>& orig,
|
const std::shared_ptr<Font>& orig,
|
||||||
const float maxHeight)
|
const float maxHeight,
|
||||||
|
const bool legacyTheme)
|
||||||
{
|
{
|
||||||
|
mLegacyTheme = legacyTheme;
|
||||||
|
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
if (!(properties & FONT_PATH) && !(properties & FONT_SIZE))
|
if (!(properties & FONT_PATH) && !(properties & FONT_SIZE))
|
||||||
return orig;
|
return orig;
|
||||||
|
|
||||||
int size {static_cast<int>(orig ? orig->mFontSize : FONT_SIZE_MEDIUM)};
|
float size {static_cast<float>(orig ? orig->mFontSize : FONT_SIZE_MEDIUM)};
|
||||||
std::string path {orig ? orig->mPath : getDefaultPath()};
|
std::string path {orig ? orig->mPath : getDefaultPath()};
|
||||||
|
|
||||||
float screenHeight {static_cast<float>(Renderer::getScreenHeight())};
|
float screenHeight {static_cast<float>(Renderer::getScreenHeight())};
|
||||||
|
|
||||||
if (properties & FONT_SIZE && elem->has("fontSize"))
|
if (properties & FONT_SIZE && elem->has("fontSize")) {
|
||||||
size = static_cast<int>(glm::clamp(screenHeight * elem->get<float>("fontSize"),
|
size = glm::clamp(screenHeight * elem->get<float>("fontSize"), screenHeight * 0.001f,
|
||||||
screenHeight * 0.001f, screenHeight * 1.5f));
|
screenHeight * 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
if (maxHeight != 0.0f && static_cast<float>(size) > maxHeight)
|
if (maxHeight != 0.0f && size > maxHeight)
|
||||||
size = static_cast<int>(maxHeight);
|
size = maxHeight;
|
||||||
|
|
||||||
if (properties & FONT_PATH && elem->has("fontPath"))
|
if (properties & FONT_PATH && elem->has("fontPath"))
|
||||||
path = elem->get<std::string>("fontPath");
|
path = elem->get<std::string>("fontPath");
|
||||||
|
@ -433,6 +454,9 @@ std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem,
|
||||||
path = getDefaultPath();
|
path = getDefaultPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mLegacyTheme)
|
||||||
|
return get(std::floor(size), path);
|
||||||
|
else
|
||||||
return get(size, path);
|
return get(size, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,7 +582,7 @@ void Font::FontTexture::deinitTexture()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::FontFace::FontFace(ResourceData&& d, int size, const std::string& path)
|
Font::FontFace::FontFace(ResourceData&& d, float size, const std::string& path)
|
||||||
: data {d}
|
: data {d}
|
||||||
{
|
{
|
||||||
if (FT_New_Memory_Face(sLibrary, d.ptr.get(), static_cast<FT_Long>(d.length), 0, &face) != 0) {
|
if (FT_New_Memory_Face(sLibrary, d.ptr.get(), static_cast<FT_Long>(d.length), 0, &face) != 0) {
|
||||||
|
@ -566,7 +590,10 @@ Font::FontFace::FontFace(ResourceData&& d, int size, const std::string& path)
|
||||||
Utils::Platform::emergencyShutdown();
|
Utils::Platform::emergencyShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
FT_Set_Pixel_Sizes(face, 0, size);
|
// Even though a fractional font size can be requested, the glyphs will always be rounded
|
||||||
|
// to integers. It's not useless to call FT_Set_Char_Size() instead of FT_Set_Pixel_Sizes()
|
||||||
|
// though as the glyphs will still be much more evenely sized across different resolutions.
|
||||||
|
FT_Set_Char_Size(face, 0.0f, size * 64.0f, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::FontFace::~FontFace()
|
Font::FontFace::~FontFace()
|
||||||
|
@ -712,6 +739,12 @@ Font::Glyph* Font::getGlyph(const unsigned int id)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the letter 'S' as a size reference.
|
||||||
|
if (mLetterHeight == 0 && id == 'S') {
|
||||||
|
const float ratio {static_cast<float>(glyphSize.y) / std::round(mFontSize)};
|
||||||
|
mLetterHeight = mFontSize * ratio;
|
||||||
|
}
|
||||||
|
|
||||||
// Create glyph.
|
// Create glyph.
|
||||||
Glyph& glyph {mGlyphMap[id]};
|
Glyph& glyph {mGlyphMap[id]};
|
||||||
|
|
||||||
|
@ -720,18 +753,18 @@ Font::Glyph* Font::getGlyph(const unsigned int id)
|
||||||
cursor.y / static_cast<float>(tex->textureSize.y)};
|
cursor.y / static_cast<float>(tex->textureSize.y)};
|
||||||
glyph.texSize = glm::vec2 {glyphSize.x / static_cast<float>(tex->textureSize.x),
|
glyph.texSize = glm::vec2 {glyphSize.x / static_cast<float>(tex->textureSize.x),
|
||||||
glyphSize.y / static_cast<float>(tex->textureSize.y)};
|
glyphSize.y / static_cast<float>(tex->textureSize.y)};
|
||||||
|
|
||||||
glyph.advance = glm::vec2 {static_cast<float>(glyphSlot->metrics.horiAdvance) / 64.0f,
|
glyph.advance = glm::vec2 {static_cast<float>(glyphSlot->metrics.horiAdvance) / 64.0f,
|
||||||
static_cast<float>(glyphSlot->metrics.vertAdvance) / 64.0f};
|
static_cast<float>(glyphSlot->metrics.vertAdvance) / 64.0f};
|
||||||
glyph.bearing = glm::vec2 {static_cast<float>(glyphSlot->metrics.horiBearingX) / 64.0f,
|
glyph.bearing = glm::vec2 {static_cast<float>(glyphSlot->metrics.horiBearingX) / 64.0f,
|
||||||
static_cast<float>(glyphSlot->metrics.horiBearingY) / 64.0f};
|
static_cast<float>(glyphSlot->metrics.horiBearingY) / 64.0f};
|
||||||
|
glyph.rows = glyphSlot->bitmap.rows;
|
||||||
|
|
||||||
// Upload glyph bitmap to texture.
|
// Upload glyph bitmap to texture.
|
||||||
mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y,
|
mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y,
|
||||||
glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
|
glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
|
||||||
|
|
||||||
if (glyphSize.y > mMaxGlyphHeight)
|
if (glyphSize.y > mLegacyMaxGlyphHeight)
|
||||||
mMaxGlyphHeight = glyphSize.y;
|
mLegacyMaxGlyphHeight = glyphSize.y;
|
||||||
|
|
||||||
return &glyph;
|
return &glyph;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,10 @@
|
||||||
|
|
||||||
class TextCache;
|
class TextCache;
|
||||||
|
|
||||||
// clang-format off
|
#define FONT_SIZE_MINI 0.030f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
|
||||||
#define FONT_SIZE_MINI (static_cast<unsigned int>(0.030f * \
|
#define FONT_SIZE_SMALL 0.035f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
|
||||||
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))
|
#define FONT_SIZE_MEDIUM 0.045f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
|
||||||
#define FONT_SIZE_SMALL (static_cast<unsigned int>(0.035f * \
|
#define FONT_SIZE_LARGE 0.085f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
|
||||||
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))
|
|
||||||
#define FONT_SIZE_MEDIUM (static_cast<unsigned int>(0.045f * \
|
|
||||||
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))
|
|
||||||
#define FONT_SIZE_LARGE (static_cast<unsigned int>(0.085f * \
|
|
||||||
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
#define FONT_PATH_LIGHT ":/fonts/Akrobat-Regular.ttf"
|
#define FONT_PATH_LIGHT ":/fonts/Akrobat-Regular.ttf"
|
||||||
#define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf"
|
#define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf"
|
||||||
|
@ -42,13 +36,19 @@ class Font : public IReloadable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Font();
|
virtual ~Font();
|
||||||
static std::shared_ptr<Font> get(int size, const std::string& path = getDefaultPath());
|
static std::shared_ptr<Font> get(float size, const std::string& path = getDefaultPath());
|
||||||
|
|
||||||
// Returns the expected size of a string when rendered. Extra spacing is applied to the Y axis.
|
// Returns the expected size of a string when rendered. Extra spacing is applied to the Y axis.
|
||||||
glm::vec2 sizeText(std::string text, float lineSpacing = 1.5f);
|
glm::vec2 sizeText(std::string text, float lineSpacing = 1.5f);
|
||||||
|
|
||||||
// Returns the size of the overall text area.
|
// Used to determine mMaxGlyphHeight upfront which is needed for accurate text sizing by
|
||||||
const glm::vec2 getTextSize() { return mTextSize; }
|
// wrapText and buildTextCache. This is required as the requested font height is not
|
||||||
|
// guaranteed and can be exceeded by a few pixels for some glyphs.
|
||||||
|
int loadGlyphs(const std::string& text);
|
||||||
|
|
||||||
|
// This is needed to retain a bug from the legacy theme engine where lineSpacing is not
|
||||||
|
// sized correctly when using automatic text element sizing.
|
||||||
|
void useLegacyMaxGlyphHeight() { mMaxGlyphHeight = mLegacyMaxGlyphHeight; }
|
||||||
|
|
||||||
TextCache* buildTextCache(const std::string& text,
|
TextCache* buildTextCache(const std::string& text,
|
||||||
float offsetX,
|
float offsetX,
|
||||||
|
@ -86,14 +86,15 @@ public:
|
||||||
void reload(ResourceManager& rm) override { rebuildTextures(); }
|
void reload(ResourceManager& rm) override { rebuildTextures(); }
|
||||||
void unload(ResourceManager& rm) override { unloadTextures(); }
|
void unload(ResourceManager& rm) override { unloadTextures(); }
|
||||||
|
|
||||||
int getSize() const { return mFontSize; }
|
const float getSize() const { return mFontSize; }
|
||||||
const std::string& getPath() const { return mPath; }
|
const std::string& getPath() const { return mPath; }
|
||||||
static std::string getDefaultPath() { return FONT_PATH_REGULAR; }
|
static std::string getDefaultPath() { return FONT_PATH_REGULAR; }
|
||||||
|
|
||||||
static std::shared_ptr<Font> getFromTheme(const ThemeData::ThemeElement* elem,
|
static std::shared_ptr<Font> getFromTheme(const ThemeData::ThemeElement* elem,
|
||||||
unsigned int properties,
|
unsigned int properties,
|
||||||
const std::shared_ptr<Font>& orig,
|
const std::shared_ptr<Font>& orig,
|
||||||
const float maxHeight = 0.0f);
|
const float maxHeight = 0.0f,
|
||||||
|
const bool legacyTheme = false);
|
||||||
|
|
||||||
// Returns an approximation of VRAM used by this font's texture (in bytes).
|
// Returns an approximation of VRAM used by this font's texture (in bytes).
|
||||||
size_t getMemUsage() const;
|
size_t getMemUsage() const;
|
||||||
|
@ -101,7 +102,7 @@ public:
|
||||||
static size_t getTotalMemUsage();
|
static size_t getTotalMemUsage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Font(int size, const std::string& path);
|
Font(float size, const std::string& path);
|
||||||
static void initLibrary();
|
static void initLibrary();
|
||||||
|
|
||||||
struct FontTexture {
|
struct FontTexture {
|
||||||
|
@ -127,7 +128,7 @@ private:
|
||||||
const ResourceData data;
|
const ResourceData data;
|
||||||
FT_Face face;
|
FT_Face face;
|
||||||
|
|
||||||
FontFace(ResourceData&& d, int size, const std::string& path);
|
FontFace(ResourceData&& d, float size, const std::string& path);
|
||||||
virtual ~FontFace();
|
virtual ~FontFace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,6 +138,7 @@ private:
|
||||||
glm::vec2 texSize; // In texels.
|
glm::vec2 texSize; // In texels.
|
||||||
glm::vec2 advance;
|
glm::vec2 advance;
|
||||||
glm::vec2 bearing;
|
glm::vec2 bearing;
|
||||||
|
int rows;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Completely recreate the texture data for all textures based on mGlyphs information.
|
// Completely recreate the texture data for all textures based on mGlyphs information.
|
||||||
|
@ -159,7 +161,8 @@ private:
|
||||||
void clearFaceCache() { mFaceCache.clear(); }
|
void clearFaceCache() { mFaceCache.clear(); }
|
||||||
|
|
||||||
static inline FT_Library sLibrary {nullptr};
|
static inline FT_Library sLibrary {nullptr};
|
||||||
static inline std::map<std::pair<std::string, int>, std::weak_ptr<Font>> sFontMap;
|
static inline std::map<std::pair<std::string, float>, std::weak_ptr<Font>> sFontMap;
|
||||||
|
static inline bool mLegacyTheme {false};
|
||||||
|
|
||||||
Renderer* mRenderer;
|
Renderer* mRenderer;
|
||||||
std::vector<std::unique_ptr<FontTexture>> mTextures;
|
std::vector<std::unique_ptr<FontTexture>> mTextures;
|
||||||
|
@ -167,9 +170,10 @@ private:
|
||||||
std::map<unsigned int, Glyph> mGlyphMap;
|
std::map<unsigned int, Glyph> mGlyphMap;
|
||||||
|
|
||||||
const std::string mPath;
|
const std::string mPath;
|
||||||
glm::vec2 mTextSize;
|
float mFontSize;
|
||||||
int mFontSize;
|
float mLetterHeight;
|
||||||
int mMaxGlyphHeight;
|
int mMaxGlyphHeight;
|
||||||
|
int mLegacyMaxGlyphHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used to store a sort of "pre-rendered" string.
|
// Used to store a sort of "pre-rendered" string.
|
||||||
|
@ -183,6 +187,7 @@ class TextCache
|
||||||
public:
|
public:
|
||||||
struct CacheMetrics {
|
struct CacheMetrics {
|
||||||
glm::vec2 size;
|
glm::vec2 size;
|
||||||
|
int maxGlyphHeight;
|
||||||
} metrics;
|
} metrics;
|
||||||
|
|
||||||
void setColor(unsigned int color);
|
void setColor(unsigned int color);
|
||||||
|
|
Loading…
Reference in a new issue