Utility/RichTextDrawer: Add character & line spacing offsets

This commit is contained in:
Lynix 2020-01-26 16:57:56 +01:00
parent 977044f59e
commit 8c7301f649
4 changed files with 162 additions and 33 deletions

View File

@ -219,7 +219,7 @@ Nazara Engine:
- On Windows, Thread::Set(Current)Name now uses `SetThreadDescription` Win32 function if possible instead of triggering a debugger exception. MinGW builds will use this if available too.
- ⚠ Removed Texture(const Image\*) constructor, use Texture::LoadFromImage instead
- ⚠ TextDrawers now use floating-point internally and to exposes their Bounds (AbstractTextDrawer::GetBounds() now returns a Rectf)
- Added SimpleTextDrawer character and line spacing offset properties
- Added [SimpleTextDrawer|RichTextDrawer] character and line spacing offset properties
Nazara Development Kit:
- Added ImageWidget (#139)

View File

@ -33,10 +33,13 @@ namespace Nz
inline std::size_t FindBlock(std::size_t glyphIndex) const;
inline unsigned int GetBlockCharacterSize(std::size_t index) const;
inline float GetBlockCharacterSpacingOffset(std::size_t index) const;
inline const Color& GetBlockColor(std::size_t index) const;
inline std::size_t GetBlockCount() const;
inline std::size_t GetBlockFirstGlyphIndex(std::size_t index) const;
inline const FontRef& GetBlockFont(std::size_t index) const;
inline float GetBlockLineHeight(std::size_t index) const;
inline float GetBlockLineSpacingOffset(std::size_t index) const;
inline const Color& GetBlockOutlineColor(std::size_t index) const;
inline float GetBlockOutlineThickness(std::size_t index) const;
inline TextStyleFlags GetBlockStyle(std::size_t index) const;
@ -45,8 +48,10 @@ namespace Nz
inline BlockRef GetBlock(std::size_t index);
const Rectf& GetBounds() const override;
inline unsigned int GetDefaultCharacterSize() const;
inline float GetDefaultCharacterSpacingOffset() const;
inline const Color& GetDefaultColor() const;
inline const FontRef& GetDefaultFont() const;
inline float GetDefaultLineSpacingOffset() const;
inline const Color& GetDefaultOutlineColor() const;
inline float GetDefaultOutlineThickness() const;
inline TextStyleFlags GetDefaultStyle() const;
@ -65,16 +70,20 @@ namespace Nz
void RemoveBlock(std::size_t index);
inline void SetBlockCharacterSize(std::size_t index, unsigned int characterSize);
inline void SetBlockCharacterSpacingOffset(std::size_t index, float offset);
inline void SetBlockColor(std::size_t index, const Color& color);
inline void SetBlockFont(std::size_t index, FontRef font);
inline void SetBlockLineSpacingOffset(std::size_t index, float offset);
inline void SetBlockOutlineColor(std::size_t index, const Color& color);
inline void SetBlockOutlineThickness(std::size_t index, float thickness);
inline void SetBlockStyle(std::size_t index, TextStyleFlags style);
inline void SetBlockText(std::size_t index, String str);
inline void SetDefaultCharacterSize(unsigned int characterSize);
inline void SetDefaultCharacterSpacingOffset(float offset);
inline void SetDefaultColor(const Color& color);
inline void SetDefaultFont(const FontRef& font);
inline void SetDefaultLineSpacingOffset(float offset);
inline void SetDefaultOutlineColor(const Color& color);
inline void SetDefaultOutlineThickness(float thickness);
inline void SetDefaultStyle(TextStyleFlags style);
@ -89,13 +98,15 @@ namespace Nz
private:
struct Block;
inline void AppendNewLine(const Font* font, unsigned int characterSize) const;
void AppendNewLine(const Font* font, unsigned int characterSize, std::size_t glyphIndex, float glyphPosition) const;
inline void AppendNewLine(const Font* font, unsigned int characterSize, float lineSpacingOffset) const;
void AppendNewLine(const Font* font, unsigned int characterSize, float lineSpacingOffset, std::size_t glyphIndex, float glyphPosition) const;
inline void ClearGlyphs() const;
inline void ConnectFontSlots();
inline void DisconnectFontSlots();
bool GenerateGlyph(Glyph& glyph, char32_t character, float outlineThickness, bool lineWrap, const Font* font, const Color& color, TextStyleFlags style, unsigned int characterSize, int renderOrder, int* advance) const;
void GenerateGlyphs(const Font* font, const Color& color, TextStyleFlags style, unsigned int characterSize, const Color& outlineColor, float outlineThickness, const String& text) const;
bool GenerateGlyph(Glyph& glyph, char32_t character, float outlineThickness, bool lineWrap, const Font* font, const Color& color, TextStyleFlags style, float lineSpacingOffset, unsigned int characterSize, int renderOrder, int* advance) const;
void GenerateGlyphs(const Font* font, const Color& color, TextStyleFlags style, unsigned int characterSize, const Color& outlineColor, float characterSpacingOffset, float lineSpacingOffset, float outlineThickness, const String& text) const;
inline float GetLineHeight(const Block& block) const;
inline float GetLineHeight(float lineSpacingOffset, const Font::SizeInfo& sizeInfo) const;
inline std::size_t HandleFontAddition(const FontRef& font);
inline void InvalidateGlyphs();
inline void ReleaseFont(std::size_t fontIndex);
@ -117,6 +128,8 @@ namespace Nz
Color outlineColor;
String text;
TextStyleFlags style;
float characterSpacingOffset;
float lineSpacingOffset;
float outlineThickness;
unsigned int characterSize;
};
@ -145,6 +158,8 @@ namespace Nz
mutable Rectf m_bounds;
mutable Vector2f m_drawPos;
mutable bool m_glyphUpdated;
float m_defaultCharacterSpacingOffset;
float m_defaultLineSpacingOffset;
float m_defaultOutlineThickness;
float m_maxLineWidth;
unsigned int m_defaultCharacterSize;
@ -160,18 +175,22 @@ namespace Nz
BlockRef(BlockRef&&) = default;
~BlockRef() = default;
inline float GetCharacterSpacingOffset() const;
inline unsigned int GetCharacterSize() const;
inline Color GetColor() const;
inline std::size_t GetFirstGlyphIndex() const;
inline const FontRef& GetFont() const;
inline float GetLineSpacingOffset() const;
inline Color GetOutlineColor() const;
inline float GetOutlineThickness() const;
inline TextStyleFlags GetStyle() const;
inline const String& GetText() const;
inline void SetCharacterSpacingOffset(float offset);
inline void SetCharacterSize(unsigned int size);
inline void SetColor(Color color);
inline void SetFont(FontRef font);
inline void SetLineSpacingOffset(float offset);
inline void SetOutlineColor(Color color);
inline void SetOutlineThickness(float thickness);
inline void SetStyle(TextStyleFlags style);

View File

@ -57,6 +57,12 @@ namespace Nz
return m_blocks[index].characterSize;
}
inline float RichTextDrawer::GetBlockCharacterSpacingOffset(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].characterSpacingOffset;
}
inline const Color& RichTextDrawer::GetBlockColor(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
@ -82,6 +88,18 @@ namespace Nz
return m_fonts[fontIndex].font;
}
inline float RichTextDrawer::GetBlockLineHeight(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].lineSpacingOffset;
}
inline float RichTextDrawer::GetBlockLineSpacingOffset(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].lineSpacingOffset;
}
inline const Color& RichTextDrawer::GetBlockOutlineColor(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
@ -111,6 +129,11 @@ namespace Nz
return m_defaultCharacterSize;
}
inline float RichTextDrawer::GetDefaultCharacterSpacingOffset() const
{
return m_defaultCharacterSpacingOffset;
}
inline const Color& RichTextDrawer::GetDefaultColor() const
{
return m_defaultColor;
@ -121,6 +144,11 @@ namespace Nz
return m_defaultFont;
}
inline float RichTextDrawer::GetDefaultLineSpacingOffset() const
{
return m_defaultLineSpacingOffset;
}
inline const Color& RichTextDrawer::GetDefaultOutlineColor() const
{
return m_defaultOutlineColor;
@ -136,9 +164,9 @@ namespace Nz
return m_defaultStyle;
}
inline void RichTextDrawer::AppendNewLine(const Font* font, unsigned int characterSize) const
inline void RichTextDrawer::AppendNewLine(const Font* font, unsigned int characterSize, float lineSpacingOffset) const
{
AppendNewLine(font, characterSize, InvalidGlyph, 0);
AppendNewLine(font, characterSize, lineSpacingOffset, InvalidGlyph, 0);
}
inline void RichTextDrawer::ClearGlyphs() const
@ -173,6 +201,19 @@ namespace Nz
}
}
inline float RichTextDrawer::GetLineHeight(const Block& block) const
{
assert(block.fontIndex < m_fonts.size());
const FontData& fontData = m_fonts[block.fontIndex];
return GetLineHeight(block.lineSpacingOffset, fontData.font->GetSizeInfo(block.characterSize));
}
inline float RichTextDrawer::GetLineHeight(float lineSpacingOffset, const Font::SizeInfo& sizeInfo) const
{
return float(sizeInfo.lineHeight) + lineSpacingOffset;
}
inline std::size_t RichTextDrawer::HandleFontAddition(const FontRef& font)
{
auto it = m_fontIndexes.find(font);
@ -204,10 +245,10 @@ namespace Nz
{
// Shift font indexes
m_fontIndexes.erase(fontData.font);
for (auto it = m_fontIndexes.begin(); it != m_fontIndexes.end(); ++it)
for (auto& fontIndexe : m_fontIndexes)
{
if (it->second > fontIndex)
it->second--;
if (fontIndexe.second > fontIndex)
fontIndexe.second--;
}
m_fonts.erase(m_fonts.begin() + fontIndex);
@ -235,6 +276,14 @@ namespace Nz
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockCharacterSpacingOffset(std::size_t index, float offset)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].characterSpacingOffset = offset;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockColor(std::size_t index, const Color& color)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
@ -260,6 +309,14 @@ namespace Nz
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockLineSpacingOffset(std::size_t index, float offset)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].lineSpacingOffset = offset;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockOutlineColor(std::size_t index, const Color& color)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
@ -308,6 +365,11 @@ namespace Nz
m_defaultCharacterSize = characterSize;
}
inline void RichTextDrawer::SetDefaultCharacterSpacingOffset(float offset)
{
m_defaultCharacterSpacingOffset = offset;
}
inline void RichTextDrawer::SetDefaultColor(const Color& color)
{
m_defaultColor = color;
@ -318,6 +380,11 @@ namespace Nz
m_defaultFont = font;
}
inline void RichTextDrawer::SetDefaultLineSpacingOffset(float offset)
{
m_defaultLineSpacingOffset = offset;
}
inline void RichTextDrawer::SetDefaultOutlineColor(const Color& color)
{
m_defaultOutlineColor = color;
@ -351,6 +418,17 @@ namespace Nz
{
}
/*!
* Returns the character spacing offset used for the characters of the referenced block
* \return The referenced block character size
*
* \see GetColor, GetFont, GetStyle, GetText, SetCharacterSize
*/
inline float RichTextDrawer::BlockRef::GetCharacterSpacingOffset() const
{
return m_drawer.GetBlockCharacterSpacingOffset(m_blockIndex);
}
/*!
* Returns the character size used for the characters of the referenced block
* \return The referenced block character size
@ -384,6 +462,17 @@ namespace Nz
return m_drawer.GetBlockFont(m_blockIndex);
}
/*!
* Returns the line spacing offset used for the characters of the referenced block
* \return The referenced block character size
*
* \see GetColor, GetFont, GetStyle, GetText, SetCharacterSize
*/
inline float RichTextDrawer::BlockRef::GetLineSpacingOffset() const
{
return m_drawer.GetBlockLineSpacingOffset(m_blockIndex);
}
/*!
* Returns the outline color used for the characters of the referenced block
* \return The referenced block outline color
@ -439,6 +528,17 @@ namespace Nz
return m_drawer.GetBlockText(m_blockIndex);
}
/*!
* Changes the character spacing offset of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetCharacterSpacingOffset, SetColor, SetFont, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetCharacterSpacingOffset(float offset)
{
m_drawer.SetBlockCharacterSpacingOffset(m_blockIndex, offset);
}
/*!
* Changes the character size of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
@ -472,6 +572,17 @@ namespace Nz
m_drawer.SetBlockFont(m_blockIndex, std::move(font));
}
/*!
* Changes the line spacing offset of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetLineSpacingOffset, SetColor, SetFont, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetLineSpacingOffset(float offset)
{
m_drawer.SetBlockLineSpacingOffset(m_blockIndex, offset);
}
/*!
* Changes the outline color of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.

View File

@ -267,7 +267,7 @@ namespace Nz
return *this;
}
void RichTextDrawer::AppendNewLine(const Font* font, unsigned int characterSize, std::size_t glyphIndex, float glyphPosition) const
void RichTextDrawer::AppendNewLine(const Font* font, unsigned int characterSize, float lineSpacingOffset, std::size_t glyphIndex, float glyphPosition) const
{
// Ensure we're appending from last line
Line& lastLine = m_lines.back();
@ -276,13 +276,15 @@ namespace Nz
float previousDrawPos = m_drawPos.x;
float lineHeight = GetLineHeight(lineSpacingOffset, sizeInfo);
// Reset cursor
m_drawPos.x = 0;
m_drawPos.y += sizeInfo.lineHeight;
m_drawPos.y += lineHeight;
m_lastSeparatorGlyph = InvalidGlyph;
m_bounds.ExtendTo(lastLine.bounds);
m_lines.emplace_back(Line{ Rectf(0.f, float(sizeInfo.lineHeight * m_lines.size()), 0.f, float(sizeInfo.lineHeight)), m_glyphs.size() + 1 });
m_lines.emplace_back(Line{ Rectf(0.f, lineHeight * m_lines.size(), 0.f, lineHeight), m_glyphs.size() + 1 });
if (glyphIndex != InvalidGlyph && glyphIndex > lastLine.glyphIndex)
{
@ -293,12 +295,12 @@ namespace Nz
{
Glyph& glyph = m_glyphs[i];
glyph.bounds.x -= glyphPosition;
glyph.bounds.y += sizeInfo.lineHeight;
glyph.bounds.y += lineHeight;
for (auto& corner : glyph.corners)
{
corner.x -= glyphPosition;
corner.y += sizeInfo.lineHeight;
corner.y += lineHeight;
}
newLine.bounds.ExtendTo(glyph.bounds);
@ -316,7 +318,7 @@ namespace Nz
}
}
bool RichTextDrawer::GenerateGlyph(Glyph& glyph, char32_t character, float outlineThickness, bool lineWrap, const Font* font, const Color& color, TextStyleFlags style, unsigned int characterSize, int renderOrder, int* advance) const
bool RichTextDrawer::GenerateGlyph(Glyph& glyph, char32_t character, float outlineThickness, bool lineWrap, const Font* font, const Color& color, TextStyleFlags style, float lineSpacingOffset, unsigned int characterSize, int renderOrder, int* advance) const
{
const Font::Glyph& fontGlyph = font->GetGlyph(characterSize, style, outlineThickness, character);
if (fontGlyph.valid && fontGlyph.fauxOutlineThickness <= 0.f)
@ -330,7 +332,7 @@ namespace Nz
glyph.bounds.Set(fontGlyph.aabb);
if (lineWrap && ShouldLineWrap(glyph.bounds.width))
AppendNewLine(font, characterSize, m_lastSeparatorGlyph, m_lastSeparatorPosition);
AppendNewLine(font, characterSize, lineSpacingOffset, m_lastSeparatorGlyph, m_lastSeparatorPosition);
glyph.bounds.x += m_drawPos.x;
glyph.bounds.y += m_drawPos.y;
@ -356,7 +358,7 @@ namespace Nz
return false;
};
void RichTextDrawer::GenerateGlyphs(const Font* font, const Color& color, TextStyleFlags style, unsigned int characterSize, const Color& outlineColor, float outlineThickness, const String& text) const
void RichTextDrawer::GenerateGlyphs(const Font* font, const Color& color, TextStyleFlags style, unsigned int characterSize, const Color& outlineColor, float characterSpacingOffset, float lineSpacingOffset, float outlineThickness, const String& text) const
{
if (text.IsEmpty())
return;
@ -372,8 +374,9 @@ namespace Nz
char32_t previousCharacter = 0;
const Font::SizeInfo& sizeInfo = font->GetSizeInfo(characterSize);
float lineHeight = GetLineHeight(lineSpacingOffset, sizeInfo);
float heightDifference = sizeInfo.lineHeight - m_lines.back().bounds.height;
float heightDifference = lineHeight - m_lines.back().bounds.height;
if (heightDifference > 0.f)
{
for (std::size_t glyphIndex = m_lines.back().glyphIndex; glyphIndex < m_glyphs.size(); ++glyphIndex)
@ -388,10 +391,6 @@ namespace Nz
m_drawPos.y += static_cast<unsigned int>(heightDifference);
m_lines.back().bounds.height += heightDifference;
}
/*if (firstFont.font)
m_lines.emplace_back(Line{ Rectf(0.f, 0.f, 0.f, float(font->GetSizeInfo(firstBlock.characterSize).lineHeight)), 0 });
else
m_lines.emplace_back(Line{ Rectf::Zero(), 0 });*/
m_glyphs.reserve(m_glyphs.size() + characters.size() * ((outlineThickness > 0.f) ? 2 : 1));
for (char32_t character : characters)
@ -402,16 +401,16 @@ namespace Nz
previousCharacter = character;
bool whitespace = true;
float advance = 0.f;
float advance = characterSpacingOffset;
switch (character)
{
case ' ':
case '\n':
advance = float(sizeInfo.spaceAdvance);
advance += float(sizeInfo.spaceAdvance);
break;
case '\t':
advance = float(sizeInfo.spaceAdvance) * 4.f;
advance += float(sizeInfo.spaceAdvance) * 4.f;
break;
default:
@ -423,22 +422,22 @@ namespace Nz
if (!whitespace)
{
int iAdvance;
if (!GenerateGlyph(glyph, character, 0.f, true, font, color, style, characterSize, 0, &iAdvance))
if (!GenerateGlyph(glyph, character, 0.f, true, font, color, style, lineSpacingOffset, characterSize, 0, &iAdvance))
continue; // Glyph failed to load, just skip it (can't do much)
advance = float(iAdvance);
advance += float(iAdvance);
if (outlineThickness > 0.f)
{
Glyph outlineGlyph;
if (GenerateGlyph(outlineGlyph, character, outlineThickness, false, font, outlineColor, style, characterSize, -1, nullptr))
if (GenerateGlyph(outlineGlyph, character, outlineThickness, false, font, outlineColor, style, lineSpacingOffset, characterSize, -1, nullptr))
m_glyphs.push_back(outlineGlyph);
}
}
else
{
if (ShouldLineWrap(advance))
AppendNewLine(font, characterSize, m_lastSeparatorGlyph, m_lastSeparatorPosition);
AppendNewLine(font, characterSize, lineSpacingOffset, m_lastSeparatorGlyph, m_lastSeparatorPosition);
glyph.atlas = nullptr;
glyph.bounds.Set(m_drawPos.x, m_lines.back().bounds.y, advance, float(sizeInfo.lineHeight));
@ -455,7 +454,7 @@ namespace Nz
{
case '\n':
{
AppendNewLine(font, characterSize);
AppendNewLine(font, characterSize, lineSpacingOffset);
break;
}
@ -545,7 +544,7 @@ namespace Nz
const auto& firstFont = m_fonts[firstBlock.fontIndex];
if (firstFont.font)
m_lines.emplace_back(Line{ Rectf(0.f, 0.f, 0.f, float(firstFont.font->GetSizeInfo(firstBlock.characterSize).lineHeight)), 0 });
m_lines.emplace_back(Line{ Rectf(0.f, 0.f, 0.f, GetLineHeight(firstBlock)), 0 });
else
m_lines.emplace_back(Line{ Rectf::Zero(), 0 });
@ -556,7 +555,7 @@ namespace Nz
assert(block.fontIndex < m_fonts.size());
const auto& fontData = m_fonts[block.fontIndex];
GenerateGlyphs(fontData.font, block.color, block.style, block.characterSize, block.outlineColor, block.outlineThickness, block.text);
GenerateGlyphs(fontData.font, block.color, block.style, block.characterSize, block.outlineColor, block.outlineThickness, block.characterSpacingOffset, block.lineSpacingOffset, block.text);
}
}
else