diff --git a/ChangeLog.md b/ChangeLog.md index 156a02777..c4562182f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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) diff --git a/include/Nazara/Utility/RichTextDrawer.hpp b/include/Nazara/Utility/RichTextDrawer.hpp index 67f5ec0de..25bd8dcf6 100644 --- a/include/Nazara/Utility/RichTextDrawer.hpp +++ b/include/Nazara/Utility/RichTextDrawer.hpp @@ -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); diff --git a/include/Nazara/Utility/RichTextDrawer.inl b/include/Nazara/Utility/RichTextDrawer.inl index ef49a0457..38f220198 100644 --- a/include/Nazara/Utility/RichTextDrawer.inl +++ b/include/Nazara/Utility/RichTextDrawer.inl @@ -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. diff --git a/src/Nazara/Utility/RichTextDrawer.cpp b/src/Nazara/Utility/RichTextDrawer.cpp index 75745c543..b744d1949 100644 --- a/src/Nazara/Utility/RichTextDrawer.cpp +++ b/src/Nazara/Utility/RichTextDrawer.cpp @@ -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(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