From 39a1ce02dcc4d57cfcf448217e994289d853f356 Mon Sep 17 00:00:00 2001 From: Skyrpex Date: Mon, 24 Feb 2014 00:07:04 +0100 Subject: [PATCH 1/2] Fixed newline error Fixed code style Uses C++11 features Added Qt project file and example --- RichText.cpp | 240 ++++++++++++++++++++++++--------------------------- RichText.hpp | 135 ++++++++++++++--------------- RichText.pro | 16 ++++ main.cpp | 36 ++++++++ 4 files changed, 230 insertions(+), 197 deletions(-) create mode 100644 RichText.pro create mode 100644 main.cpp diff --git a/RichText.cpp b/RichText.cpp index 803cd6f..36b8087 100644 --- a/RichText.cpp +++ b/RichText.cpp @@ -8,11 +8,11 @@ namespace sfe { -RichText::RichText() - : myCurrentColor(sf::Color::White), - myCurrentStyle(sf::Text::Regular), - mySizeUpdated(false), - myPositionUpdated(false) +RichText::RichText(const sf::Font &font) + : myFont(&font), + myCurrentColor(sf::Color::White), + myCurrentStyle(sf::Text::Regular), + mySizeUpdated(false) { } @@ -22,8 +22,8 @@ RichText::RichText() //////////////////////////////////////////////////////////////////////////////// RichText & RichText::operator << (const sf::Color &color) { - myCurrentColor = color; - return *this; + myCurrentColor = color; + return *this; } //////////////////////////////////////////////////////////////////////////////// @@ -31,42 +31,68 @@ RichText & RichText::operator << (const sf::Color &color) //////////////////////////////////////////////////////////////////////////////// RichText & RichText::operator << (sf::Text::Style style) { - myCurrentStyle = style; - return *this; + myCurrentStyle = style; + return *this; +} + +//std::vector explode(const std::string &string, char delim) +//{ +// std::vector result; +// std::istringstream iss(string); + +// for (std::string token; std::getline(iss, token, delim); ) { +// result.push_back(std::move(token)); +// } + +// return result; +//} + +std::vector explode(const std::string &string, char delimiter) { + std::string next; + std::vector result; + + // For each character in the string + for (auto it = string.begin(); it != string.end(); it++) { + // If we've hit the terminal character + if (*it == delimiter) { + // If we have some characters accumulated + if (!next.empty()) { + // Add them to the result vector + result.push_back(next); + next.clear(); + } + } else { + // Accumulate the next character into the sequence + next += *it; + } + } + + if (!next.empty()) + result.push_back(next); + + return result; } //////////////////////////////////////////////////////////////////////////////// // Operator << sf::String //////////////////////////////////////////////////////////////////////////////// -/* - ** Must parse the strings to look for '\n' characters. If found, we break - ** the string into two pieces. - */ RichText & RichText::operator << (const sf::String &string) { - // It is not updated - mySizeUpdated = false; - myPositionUpdated = false; + // It is not updated + mySizeUpdated = false; - // Find \n characters (assert) - //assert(string.Find('\n') == std::string::npos); - if(string.find('\n') != std::string::npos) - std::cerr << "sfe::RichtText: Oops, character \n found." - "You will get visual errors." << std::endl; - - //String cannot be void - //assert(string != ""); - - // Add string - myTexts.push_back(sf::Text(string)); + // Add lines + std::vector lines = explode(string, '\n'); + for (const std::string &line : lines) { + sf::Text text(line, *myFont); + text.setFont(*myFont); + text.setColor(myCurrentColor); + text.setStyle(myCurrentStyle); + myTexts.push_back(std::move(text)); + } - // Setup string - sf::Text &text = myTexts.back(); - text.setColor(myCurrentColor); - text.setStyle(myCurrentStyle); - - // Return - return *this; + // Return + return *this; } //////////////////////////////////////////////////////////////////////////////// @@ -74,16 +100,13 @@ RichText & RichText::operator << (const sf::String &string) //////////////////////////////////////////////////////////////////////////////// void RichText::setCharacterSize(unsigned int size) { - // Set character size - for(collection_type::iterator it = myTexts.begin(); - it != myTexts.end(); ++it) - { - it->setCharacterSize(size); - } + // Set character size + for (auto it = myTexts.begin(); it != myTexts.end(); ++it) { + it->setCharacterSize(size); + } - // It is not updated - mySizeUpdated = false; - myPositionUpdated = false; + // It is not updated + mySizeUpdated = false; } //////////////////////////////////////////////////////////////////////////////// @@ -91,16 +114,15 @@ void RichText::setCharacterSize(unsigned int size) //////////////////////////////////////////////////////////////////////////////// void RichText::setFont(const sf::Font &font) { - // Set character size - for(collection_type::iterator it = myTexts.begin(); - it != myTexts.end(); ++it) - { - it->setFont(font); - } + myFont = &font; - // It is not updated - mySizeUpdated = false; - myPositionUpdated = false; + // Set character size + for (auto it = myTexts.begin(); it != myTexts.end(); ++it) { + it->setFont(font); + } + + // It is not updated + mySizeUpdated = false; } //////////////////////////////////////////////////////////////////////////////// @@ -108,23 +130,22 @@ void RichText::setFont(const sf::Font &font) //////////////////////////////////////////////////////////////////////////////// void RichText::clear() { - // Clear text list - myTexts.clear(); + // Clear text list + myTexts.clear(); - // Reset size - mySize = sf::Vector2f(0.f, 0.f); + // Reset size + mySize = sf::Vector2f(0.f, 0.f); - // It is updated - mySizeUpdated = true; - myPositionUpdated = true; + // It is updated + mySizeUpdated = true; } //////////////////////////////////////////////////////////////////////////////// // Get text list //////////////////////////////////////////////////////////////////////////////// -const RichText::collection_type & RichText::getTextList() const +const std::vector & RichText::getTextList() const { - return myTexts; + return myTexts; } //////////////////////////////////////////////////////////////////////////////// @@ -132,17 +153,17 @@ const RichText::collection_type & RichText::getTextList() const //////////////////////////////////////////////////////////////////////////////// unsigned int RichText::getCharacterSize() const { - if(myTexts.size()) return myTexts.begin()->getCharacterSize(); - return 0; + if (myTexts.size()) + return myTexts.begin()->getCharacterSize(); + return 0; } //////////////////////////////////////////////////////////////////////////////// // Get font //////////////////////////////////////////////////////////////////////////////// -const sf::Font & RichText::getFont() const +const sf::Font *RichText::getFont() const { - if(myTexts.size()) return myTexts.begin()->getFont(); - return sf::Font::getDefaultFont(); + return myFont; } //////////////////////////////////////////////////////////////////////////////// @@ -150,8 +171,8 @@ const sf::Font & RichText::getFont() const //////////////////////////////////////////////////////////////////////////////// float RichText::getWidth() const { - updateSize(); - return mySize.x; + updateSize(); + return mySize.x; } //////////////////////////////////////////////////////////////////////////////// @@ -159,8 +180,8 @@ float RichText::getWidth() const //////////////////////////////////////////////////////////////////////////////// float RichText::getHeight() const { - updateSize(); - return mySize.y; + updateSize(); + return mySize.y; } //////////////////////////////////////////////////////////////////////////////// @@ -168,21 +189,14 @@ float RichText::getHeight() const //////////////////////////////////////////////////////////////////////////////// void RichText::draw(sf::RenderTarget& target, sf::RenderStates states) const { - // Update position - updatePosition(); + states.transform *= getTransform(); - states.transform *= getTransform(); + for (const sf::Text &text : myTexts) { + int height = text.getGlobalBounds().height; + target.draw(text, states); - // Draw - for(collection_type::const_iterator it = myTexts.begin(); - it != myTexts.end(); ++it) - { - // Add transformation - //it->setT - - // Draw text - target.draw(*it, states); - } + states.transform.translate(0, height); + } } //////////////////////////////////////////////////////////////////////////////// @@ -190,54 +204,24 @@ void RichText::draw(sf::RenderTarget& target, sf::RenderStates states) const //////////////////////////////////////////////////////////////////////////////// void RichText::updateSize() const { - // Return if updated - if(mySizeUpdated) return; + // Return if updated + if (mySizeUpdated) + return; - // Return if empty - if(myTexts.begin() == myTexts.end()) return; + // Return if empty + if (myTexts.begin() == myTexts.end()) + return; - // It is updated - mySizeUpdated = true; + // It is updated + mySizeUpdated = true; - // Sum all sizes (height not implemented) - mySize.x = 0.f; - mySize.y = myTexts.begin()->getGlobalBounds().height; - for(collection_type::const_iterator it = myTexts.begin(); - it != myTexts.end(); ++it) - { - // Update width - mySize.x += it->getGlobalBounds().width; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Update position -//////////////////////////////////////////////////////////////////////////////// -void RichText::updatePosition() const -{ - // Return if updated - if(myPositionUpdated) return; - - // Return if empty - if(myTexts.begin() == myTexts.end()) return; - - // It is updated - myPositionUpdated = true; - - // Get starting position - sf::Vector2f offset; - - // Draw - for(collection_type::iterator it = myTexts.begin(); - it != myTexts.end(); ++it) - { - // Set all the origins to the first one - it->setOrigin(it->getPosition() - myTexts.begin()->getPosition() - offset); - - // Set offset - const sf::FloatRect rect = it->getGlobalBounds(); - offset.x += rect.width; - } + // Sum all sizes (height not implemented) + mySize.x = 0.f; + mySize.y = myTexts.begin()->getGlobalBounds().height; + for (auto it = myTexts.begin(); it != myTexts.end(); ++it) { + // Update width + mySize.x += it->getGlobalBounds().width; + } } } diff --git a/RichText.hpp b/RichText.hpp index 68d4428..60d230e 100644 --- a/RichText.hpp +++ b/RichText.hpp @@ -8,6 +8,7 @@ #include #include #include + #include namespace sfe @@ -16,89 +17,85 @@ namespace sfe class RichText : public sf::Drawable, public sf::Transformable { public: - ////////////////////////////////////////////////////////////////////////// - // Typedef for collection type - ////////////////////////////////////////////////////////////////////////// - typedef std::vector collection_type; + ////////////////////////////////////////////////////////////////////////// + // Typedef for collection type + ////////////////////////////////////////////////////////////////////////// + typedef std::vector Collection; + typedef Collection::iterator Iterator; + typedef Collection::const_iterator ConstIterator; - ////////////////////////////////////////////////////////////////////////// - // Constructor - ////////////////////////////////////////////////////////////////////////// - RichText(); + ////////////////////////////////////////////////////////////////////////// + // Constructor + ////////////////////////////////////////////////////////////////////////// + RichText(const sf::Font &font); - ////////////////////////////////////////////////////////////////////////// - // Operators - ////////////////////////////////////////////////////////////////////////// - RichText & operator << (const sf::Color &color); - RichText & operator << (sf::Text::Style style); - RichText & operator << (const sf::String &string); + ////////////////////////////////////////////////////////////////////////// + // Operators + ////////////////////////////////////////////////////////////////////////// + RichText & operator << (const sf::Color &color); + RichText & operator << (sf::Text::Style style); + RichText & operator << (const sf::String &string); - ////////////////////////////////////////////////////////////////////////// - // Set character size - ////////////////////////////////////////////////////////////////////////// - void setCharacterSize(unsigned int size); + ////////////////////////////////////////////////////////////////////////// + // Set character size + ////////////////////////////////////////////////////////////////////////// + void setCharacterSize(unsigned int size); - ////////////////////////////////////////////////////////////////////////// - // Set font - ////////////////////////////////////////////////////////////////////////// - void setFont(const sf::Font &font); + ////////////////////////////////////////////////////////////////////////// + // Set font + ////////////////////////////////////////////////////////////////////////// + void setFont(const sf::Font &font); - ////////////////////////////////////////////////////////////////////////// - // Clear - ////////////////////////////////////////////////////////////////////////// - void clear(); + ////////////////////////////////////////////////////////////////////////// + // Clear + ////////////////////////////////////////////////////////////////////////// + void clear(); - ////////////////////////////////////////////////////////////////////////// - // Get text list - ////////////////////////////////////////////////////////////////////////// - const collection_type &getTextList() const; + ////////////////////////////////////////////////////////////////////////// + // Get text list + ////////////////////////////////////////////////////////////////////////// + const std::vector &getTextList() const; - ////////////////////////////////////////////////////////////////////////// - // Get character size - ////////////////////////////////////////////////////////////////////////// - unsigned int getCharacterSize() const; + ////////////////////////////////////////////////////////////////////////// + // Get character size + ////////////////////////////////////////////////////////////////////////// + unsigned int getCharacterSize() const; - ////////////////////////////////////////////////////////////////////////// - // Get font - ////////////////////////////////////////////////////////////////////////// - const sf::Font & getFont() const; + ////////////////////////////////////////////////////////////////////////// + // Get font + ////////////////////////////////////////////////////////////////////////// + const sf::Font *getFont() const; - ////////////////////////////////////////////////////////////////////////// - // Get width - ////////////////////////////////////////////////////////////////////////// - float getWidth() const; + ////////////////////////////////////////////////////////////////////////// + // Get width + ////////////////////////////////////////////////////////////////////////// + float getWidth() const; - ////////////////////////////////////////////////////////////////////////// - // Get height - ////////////////////////////////////////////////////////////////////////// - float getHeight() const; + ////////////////////////////////////////////////////////////////////////// + // Get height + ////////////////////////////////////////////////////////////////////////// + float getHeight() const; private: - ////////////////////////////////////////////////////////////////////////// - // Update size - ////////////////////////////////////////////////////////////////////////// - void updateSize() const; + ////////////////////////////////////////////////////////////////////////// + // Update size + ////////////////////////////////////////////////////////////////////////// + void updateSize() const; - ////////////////////////////////////////////////////////////////////////// - // Update position - ////////////////////////////////////////////////////////////////////////// - void updatePosition() const; + ////////////////////////////////////////////////////////////////////////// + // Render + ////////////////////////////////////////////////////////////////////////// + void draw(sf::RenderTarget& target, sf::RenderStates states) const; - ////////////////////////////////////////////////////////////////////////// - // Render - ////////////////////////////////////////////////////////////////////////// - void draw(sf::RenderTarget& target, sf::RenderStates states) const; - - ////////////////////////////////////////////////////////////////////////// - // Member data - ////////////////////////////////////////////////////////////////////////// - mutable collection_type myTexts; ///< List of texts - sf::Color myCurrentColor; ///< Last used color - sf::Text::Style myCurrentStyle; ///< Last style used - mutable sf::Vector2f mySize; ///< Size of the text - mutable bool mySizeUpdated; ///< Do we need to recompute the size? - mutable bool myPositionUpdated; ///< Do we need to recompute the - ///< position? + ////////////////////////////////////////////////////////////////////////// + // Member data + ////////////////////////////////////////////////////////////////////////// + mutable std::vector myTexts; ///< List of texts + const sf::Font *myFont; ///< Font + sf::Color myCurrentColor; ///< Last used color + sf::Text::Style myCurrentStyle; ///< Last style used + mutable sf::Vector2f mySize; ///< Size of the text + mutable bool mySizeUpdated; ///< Do we need to recompute the size? }; } diff --git a/RichText.pro b/RichText.pro new file mode 100644 index 0000000..4d397cc --- /dev/null +++ b/RichText.pro @@ -0,0 +1,16 @@ +TEMPLATE = app +CONFIG -= qt +CONFIG -= app_bundle +CONFIG -= console +CONFIG += C++11 + +INCLUDEPATH += SFML + +LIBS += -lsfml-system-d -lsfml-window-d -lsfml-graphics-d -ljpeg -lGLEW + +SOURCES += main.cpp \ + RichText.cpp + +HEADERS += \ + RichText.hpp + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..31232db --- /dev/null +++ b/main.cpp @@ -0,0 +1,36 @@ +#include + +#include "RichText.hpp" + +int main(int, char *[]) +{ + sf::RenderWindow window; + window.create(sf::VideoMode(800, 600), "Test"); + window.setVerticalSyncEnabled(true); + + sf::Font font; + font.loadFromFile("/usr/share/fonts/truetype/freefont/FreeMono.ttf"); + sfe::RichText text(font); + text << sf::Color::Red << "Test\nbla" << sf::Color::Blue << "Lol"; + + while (window.isOpen()) { + sf::Event event; + while (window.pollEvent(event)) { + switch (event.type) { + case sf::Event::Closed: + window.close(); + break; + + default: + break; + } + } + + window.clear(); + window.draw(text); + window.display(); + } + + return 0; +} + From 838094855e1d44e7152fa78666d500d3312e471a Mon Sep 17 00:00:00 2001 From: Skyrpex Date: Mon, 24 Feb 2014 19:19:16 +0100 Subject: [PATCH 2/2] Major rewrite of the class --- RichText.cpp | 352 +++++++++++++++++++++++++++++++++------------------ RichText.hpp | 127 +++++++++++++++---- 2 files changed, 327 insertions(+), 152 deletions(-) diff --git a/RichText.cpp b/RichText.cpp index 36b8087..7a0f641 100644 --- a/RichText.cpp +++ b/RichText.cpp @@ -2,225 +2,329 @@ // Headers //////////////////////////////////////////////////////////////////////////////// #include "RichText.hpp" + +#include +#include #include -#include + +#include namespace sfe { -RichText::RichText(const sf::Font &font) - : myFont(&font), - myCurrentColor(sf::Color::White), - myCurrentStyle(sf::Text::Regular), - mySizeUpdated(false) +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::setCharacterSize(unsigned int size) +{ + for (sf::Text &text : m_texts) + text.setCharacterSize(size); + + updateGeometry(); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::setFont(const sf::Font &font) +{ + for (sf::Text &text : m_texts) + text.setFont(font); + + updateGeometry(); +} + + +//////////////////////////////////////////////////////////////////////////////// +const std::vector &RichText::Line::getTexts() const +{ + return m_texts; +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::appendText(sf::Text text) +{ + // Set text offset + text.setPosition(m_bounds.width, 0.f); + + // Push back + m_texts.push_back(std::move(text)); + + // Update bounds + m_bounds.height = std::max(m_bounds.height, text.getGlobalBounds().height); + m_bounds.width += text.getGlobalBounds().width; +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::FloatRect RichText::Line::getLocalBounds() const +{ + return m_bounds; +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::FloatRect RichText::Line::getGlobalBounds() const +{ + return getTransform().transformRect(getLocalBounds()); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::draw(sf::RenderTarget &target, sf::RenderStates states) const +{ + states.transform *= getTransform(); + + for (const sf::Text &text : m_texts) + target.draw(text, states); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::updateGeometry() const +{ + m_bounds = sf::FloatRect(); + + for (sf::Text &text : m_texts) { + text.setPosition(m_bounds.width, 0.f); + + m_bounds.height = std::max(m_bounds.height, text.getGlobalBounds().height); + m_bounds.width += text.getGlobalBounds().height; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +RichText::RichText() + : RichText(nullptr) { } + //////////////////////////////////////////////////////////////////////////////// -// Operator << sf::Color +RichText::RichText(const sf::Font &font) + : RichText(&font) +{ + +} + + //////////////////////////////////////////////////////////////////////////////// RichText & RichText::operator << (const sf::Color &color) { - myCurrentColor = color; + m_currentColor = color; return *this; } -//////////////////////////////////////////////////////////////////////////////// -// Operator << sf::Text::Style + //////////////////////////////////////////////////////////////////////////////// RichText & RichText::operator << (sf::Text::Style style) { - myCurrentStyle = style; + m_currentStyle = style; return *this; } -//std::vector explode(const std::string &string, char delim) -//{ -// std::vector result; -// std::istringstream iss(string); -// for (std::string token; std::getline(iss, token, delim); ) { -// result.push_back(std::move(token)); -// } - -// return result; -//} - -std::vector explode(const std::string &string, char delimiter) { - std::string next; - std::vector result; +//////////////////////////////////////////////////////////////////////////////// +std::vector explode(const sf::String &string, sf::Uint32 delimiter) { + if (string.isEmpty()) + return std::vector(); // For each character in the string - for (auto it = string.begin(); it != string.end(); it++) { - // If we've hit the terminal character - if (*it == delimiter) { - // If we have some characters accumulated - if (!next.empty()) { - // Add them to the result vector - result.push_back(next); - next.clear(); - } + std::vector result; + sf::String buffer; + for (sf::Uint32 character : string) { + // If we've hit the delimiter character + if (character == delimiter) { + // Add them to the result vector + result.push_back(buffer); + buffer.clear(); } else { // Accumulate the next character into the sequence - next += *it; + buffer += character; } } - if (!next.empty()) - result.push_back(next); + // Add to the result if buffer still has contents or if the last character is a delimiter + if (!buffer.isEmpty() || string[string.getSize() - 1] == delimiter) + result.push_back(buffer); return result; } -//////////////////////////////////////////////////////////////////////////////// -// Operator << sf::String + //////////////////////////////////////////////////////////////////////////////// RichText & RichText::operator << (const sf::String &string) { - // It is not updated - mySizeUpdated = false; + // Maybe skip + if (string.isEmpty()) + return *this; - // Add lines - std::vector lines = explode(string, '\n'); - for (const std::string &line : lines) { - sf::Text text(line, *myFont); - text.setFont(*myFont); - text.setColor(myCurrentColor); - text.setStyle(myCurrentStyle); - myTexts.push_back(std::move(text)); + // Explode into substrings + std::vector subStrings = explode(string, '\n'); + + // Append first substring using the last line + auto it = subStrings.begin(); + if (it != subStrings.end()) { + // If there isn't any line, just create it + if (m_lines.empty()) + m_lines.resize(1); + + // Remove last line's height + Line &line = m_lines.back(); + m_bounds.height -= line.getGlobalBounds().height; + + // Append text + line.appendText(createText(*it)); + + // Update bounds + m_bounds.height += line.getGlobalBounds().height; + m_bounds.width = std::max(m_bounds.width, line.getGlobalBounds().width); + } + + // Append the rest of substrings as new lines + while (++it != subStrings.end()) { + Line line; + line.setPosition(0.f, m_bounds.height); + line.appendText(createText(*it)); + m_lines.push_back(std::move(line)); + + // Update bounds + m_bounds.height += line.getGlobalBounds().height; + m_bounds.width = std::max(m_bounds.width, line.getGlobalBounds().width); } // Return return *this; } -//////////////////////////////////////////////////////////////////////////////// -// Set size + //////////////////////////////////////////////////////////////////////////////// void RichText::setCharacterSize(unsigned int size) { - // Set character size - for (auto it = myTexts.begin(); it != myTexts.end(); ++it) { - it->setCharacterSize(size); - } + // Maybe skip + if (m_characterSize == size) + return; - // It is not updated - mySizeUpdated = false; + // Update character size + m_characterSize = size; + + // Set texts character size + for (Line &line : m_lines) + line.setCharacterSize(size); + + updateGeometry(); } -//////////////////////////////////////////////////////////////////////////////// -// Set font + //////////////////////////////////////////////////////////////////////////////// void RichText::setFont(const sf::Font &font) { - myFont = &font; + // Maybe skip + if (m_font == &font) + return; - // Set character size - for (auto it = myTexts.begin(); it != myTexts.end(); ++it) { - it->setFont(font); - } + // Update font + m_font = &font; - // It is not updated - mySizeUpdated = false; + // Set texts font + for (Line &line : m_lines) + line.setFont(font); + + updateGeometry(); } -//////////////////////////////////////////////////////////////////////////////// -// Clear + //////////////////////////////////////////////////////////////////////////////// void RichText::clear() { - // Clear text list - myTexts.clear(); + // Clear texts + m_lines.clear(); - // Reset size - mySize = sf::Vector2f(0.f, 0.f); - - // It is updated - mySizeUpdated = true; + // Reset bounds + m_bounds = sf::FloatRect(); } + //////////////////////////////////////////////////////////////////////////////// -// Get text list -//////////////////////////////////////////////////////////////////////////////// -const std::vector & RichText::getTextList() const +const std::vector &RichText::getLines() const { - return myTexts; + return m_lines; } -//////////////////////////////////////////////////////////////////////////////// -// Get character size + //////////////////////////////////////////////////////////////////////////////// unsigned int RichText::getCharacterSize() const { - if (myTexts.size()) - return myTexts.begin()->getCharacterSize(); - return 0; + return m_characterSize; } -//////////////////////////////////////////////////////////////////////////////// -// Get font + //////////////////////////////////////////////////////////////////////////////// const sf::Font *RichText::getFont() const { - return myFont; + return m_font; } -//////////////////////////////////////////////////////////////////////////////// -// Get width -//////////////////////////////////////////////////////////////////////////////// -float RichText::getWidth() const + +//////////////////////////////////////////////////////////// +sf::FloatRect RichText::getLocalBounds() const { - updateSize(); - return mySize.x; + return m_bounds; } -//////////////////////////////////////////////////////////////////////////////// -// Get height -//////////////////////////////////////////////////////////////////////////////// -float RichText::getHeight() const + +//////////////////////////////////////////////////////////// +sf::FloatRect RichText::getGlobalBounds() const { - updateSize(); - return mySize.y; + return getTransform().transformRect(getLocalBounds()); } -//////////////////////////////////////////////////////////////////////////////// -// Render + //////////////////////////////////////////////////////////////////////////////// void RichText::draw(sf::RenderTarget& target, sf::RenderStates states) const { states.transform *= getTransform(); - for (const sf::Text &text : myTexts) { - int height = text.getGlobalBounds().height; - target.draw(text, states); - - states.transform.translate(0, height); - } + for (const Line &line : m_lines) + target.draw(line, states); } + //////////////////////////////////////////////////////////////////////////////// -// Update size -//////////////////////////////////////////////////////////////////////////////// -void RichText::updateSize() const +RichText::RichText(const sf::Font *font) + : m_font(font), + m_characterSize(30), + m_currentColor(sf::Color::White), + m_currentStyle(sf::Text::Regular) { - // Return if updated - if (mySizeUpdated) - return; - // Return if empty - if (myTexts.begin() == myTexts.end()) - return; +} - // It is updated - mySizeUpdated = true; - // Sum all sizes (height not implemented) - mySize.x = 0.f; - mySize.y = myTexts.begin()->getGlobalBounds().height; - for (auto it = myTexts.begin(); it != myTexts.end(); ++it) { - // Update width - mySize.x += it->getGlobalBounds().width; +//////////////////////////////////////////////////////////////////////////////// +sf::Text RichText::createText(const sf::String &string) const +{ + sf::Text text; + text.setString(string); + text.setColor(m_currentColor); + text.setStyle(m_currentStyle); + if (m_font) + text.setFont(*m_font); + + return text; +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::updateGeometry() const +{ + m_bounds = sf::FloatRect(); + + for (Line &line : m_lines) { + line.setPosition(0.f, m_bounds.height); + + m_bounds.height += line.getGlobalBounds().height; + m_bounds.width = std::max(m_bounds.width, line.getGlobalBounds().width); } } diff --git a/RichText.hpp b/RichText.hpp index 60d230e..d355eb1 100644 --- a/RichText.hpp +++ b/RichText.hpp @@ -1,15 +1,24 @@ -#ifndef RICHTEXT_HPP -#define RICHTEXT_HPP +#pragma once ////////////////////////////////////////////////////////////////////////// // Headers ////////////////////////////////////////////////////////////////////////// +#include + +#include #include #include #include -#include -#include +#include + +namespace sf +{ +class Font; +class String; +template class Rect; +typedef Rect FloatRect; +} namespace sfe { @@ -18,11 +27,64 @@ class RichText : public sf::Drawable, public sf::Transformable { public: ////////////////////////////////////////////////////////////////////////// - // Typedef for collection type + // Nested class that represents a single line ////////////////////////////////////////////////////////////////////////// - typedef std::vector Collection; - typedef Collection::iterator Iterator; - typedef Collection::const_iterator ConstIterator; + class Line : public sf::Transformable, public sf::Drawable + { + public: + ////////////////////////////////////////////////////////////////////// + // Set character size + ////////////////////////////////////////////////////////////////////// + void setCharacterSize(unsigned int size); + + ////////////////////////////////////////////////////////////////////// + // Set font + ////////////////////////////////////////////////////////////////////// + void setFont(const sf::Font &font); + + ////////////////////////////////////////////////////////////////////// + // Get texts + ////////////////////////////////////////////////////////////////////// + const std::vector &getTexts() const; + + ////////////////////////////////////////////////////////////////////// + // Append text + ////////////////////////////////////////////////////////////////////// + void appendText(sf::Text text); + + ////////////////////////////////////////////////////////////////////// + // Get local bounds + ////////////////////////////////////////////////////////////////////// + sf::FloatRect getLocalBounds() const; + + ////////////////////////////////////////////////////////////////////// + // Get global bounds + ////////////////////////////////////////////////////////////////////// + sf::FloatRect getGlobalBounds() const; + + protected: + ////////////////////////////////////////////////////////////////////// + // Draw + ////////////////////////////////////////////////////////////////////// + void draw(sf::RenderTarget &target, sf::RenderStates states) const override; + + private: + ////////////////////////////////////////////////////////////////////// + // Update geometry + ////////////////////////////////////////////////////////////////////// + void updateGeometry() const; + + ////////////////////////////////////////////////////////////////////// + // Member data + ////////////////////////////////////////////////////////////////////// + mutable std::vector m_texts; ///< List of texts + mutable sf::FloatRect m_bounds; ///< Local bounds + }; + + ////////////////////////////////////////////////////////////////////////// + // Constructor + ////////////////////////////////////////////////////////////////////////// + RichText(); ////////////////////////////////////////////////////////////////////////// // Constructor @@ -54,7 +116,7 @@ public: ////////////////////////////////////////////////////////////////////////// // Get text list ////////////////////////////////////////////////////////////////////////// - const std::vector &getTextList() const; + const std::vector &getLines() const; ////////////////////////////////////////////////////////////////////////// // Get character size @@ -67,37 +129,46 @@ public: const sf::Font *getFont() const; ////////////////////////////////////////////////////////////////////////// - // Get width + // Get local bounds ////////////////////////////////////////////////////////////////////////// - float getWidth() const; + sf::FloatRect getLocalBounds() const; ////////////////////////////////////////////////////////////////////////// - // Get height + // Get global bounds ////////////////////////////////////////////////////////////////////////// - float getHeight() const; - -private: - ////////////////////////////////////////////////////////////////////////// - // Update size - ////////////////////////////////////////////////////////////////////////// - void updateSize() const; + sf::FloatRect getGlobalBounds() const; +protected: ////////////////////////////////////////////////////////////////////////// // Render ////////////////////////////////////////////////////////////////////////// - void draw(sf::RenderTarget& target, sf::RenderStates states) const; + void draw(sf::RenderTarget &target, sf::RenderStates states) const override; + +private: + ////////////////////////////////////////////////////////////////////////// + // Delegate constructor + ////////////////////////////////////////////////////////////////////////// + RichText(const sf::Font *font); + + ////////////////////////////////////////////////////////////////////////// + // Creates a sf::Text instance using the current styles + ////////////////////////////////////////////////////////////////////////// + sf::Text createText(const sf::String &string) const; + + ////////////////////////////////////////////////////////////////////////// + // Update geometry + ////////////////////////////////////////////////////////////////////////// + void updateGeometry() const; ////////////////////////////////////////////////////////////////////////// // Member data ////////////////////////////////////////////////////////////////////////// - mutable std::vector myTexts; ///< List of texts - const sf::Font *myFont; ///< Font - sf::Color myCurrentColor; ///< Last used color - sf::Text::Style myCurrentStyle; ///< Last style used - mutable sf::Vector2f mySize; ///< Size of the text - mutable bool mySizeUpdated; ///< Do we need to recompute the size? + mutable std::vector m_lines; ///< List of lines + const sf::Font *m_font; ///< Font + unsigned int m_characterSize; ///< Character size + mutable sf::FloatRect m_bounds; ///< Local bounds + sf::Color m_currentColor; ///< Last used color + sf::Text::Style m_currentStyle; ///< Last style used }; } - -#endif // RICHTEXT_HPP