From 06af6467f218053941977c655cb59aff8c0973d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Mr=C3=B3z?= Date: Sat, 2 Jan 2021 14:19:50 +0100 Subject: [PATCH 1/4] Extend ScrolledWindow to stick to the bottom --- examples/ScrolledWindowViewport.cpp | 3 ++ include/SFGUI/Adjustment.hpp | 27 +++++++++++++++--- include/SFGUI/ScrolledWindow.hpp | 16 ++++++++++- src/SFGUI/Adjustment.cpp | 43 +++++++++++++++++++++-------- src/SFGUI/ListBox.cpp | 3 +- src/SFGUI/ScrolledWindow.cpp | 12 +++++++- src/SFGUI/SpinButton.cpp | 2 +- 7 files changed, 86 insertions(+), 20 deletions(-) diff --git a/examples/ScrolledWindowViewport.cpp b/examples/ScrolledWindowViewport.cpp index 48da76e1..db10994c 100644 --- a/examples/ScrolledWindowViewport.cpp +++ b/examples/ScrolledWindowViewport.cpp @@ -38,6 +38,9 @@ int main() { // Set the ScrolledWindow to always show the horizontal scrollbar // and only show the vertical scrollbar when needed. scrolledwindow->SetScrollbarPolicy( sfg::ScrolledWindow::HORIZONTAL_ALWAYS | sfg::ScrolledWindow::VERTICAL_AUTOMATIC ); + + // Set sticking ON + scrolledwindow->SetStick(sfg::ScrolledWindow::ScrollbarSticking::YES); // Add the ScrolledWindow box to the ScrolledWindow // and create a new viewport automatically. diff --git a/include/SFGUI/Adjustment.hpp b/include/SFGUI/Adjustment.hpp index a9cc06ad..035eb3ea 100644 --- a/include/SFGUI/Adjustment.hpp +++ b/include/SFGUI/Adjustment.hpp @@ -25,9 +25,10 @@ class SFGUI_API Adjustment : public Object, public std::enable_shared_from_this< * @param minor_step Minor change value (such as clicking on arrow button). * @param major_step Major change value (such as clicking on the scroll area). * @param page_size Page size (how many entries are visible / slider size). + * @param stick True if the adjustment should stick to the maximum value. * @return Adjustment. */ - static Ptr Create( float value = .0f, float lower = .0f, float upper = .0f, float minor_step = 1.f, float major_step = 5.f, float page_size = .0f ); + static Ptr Create( float value = .0f, float lower = .0f, float upper = .0f, float minor_step = 1.f, float major_step = 5.f, float page_size = .0f, bool stick = false ); /** Assignment operator * @param adjustment Adjustment whose values are to be assigned to this one. @@ -65,6 +66,21 @@ class SFGUI_API Adjustment : public Object, public std::enable_shared_from_this< */ void SetUpper( float new_upper ); + /** Get Adjustment stick setting + * @return Adjustment stick setting + */ + bool GetStick() const; + + /** Set Adjustment stick setting + * @param new_stick new stick setting + */ + void SetStick(bool new_stick); + + /** Check if value is set to maximum + * @return True is Value at Upper + */ + bool IsAtUpper() const; + /** Get Adjustment minor step * @return Adjustment minor step */ @@ -102,8 +118,9 @@ class SFGUI_API Adjustment : public Object, public std::enable_shared_from_this< * @param new_minor_step Minor change value (such as clicking on arrow button). * @param new_major_step Major change value (such as clicking on the scroll area). * @param new_page_size Page size (how many entries are visible / slider size). + * @param stick True if the adjustment should stick to the maximum value. */ - void Configure( float new_value, float new_lower, float new_upper, float new_minor_step, float new_major_step, float new_page_size ); + void Configure( float new_value, float new_lower, float new_upper, float new_minor_step, float new_major_step, float new_page_size, bool new_stick); /** Increment current value */ @@ -132,15 +149,17 @@ class SFGUI_API Adjustment : public Object, public std::enable_shared_from_this< * @param minor_step Minor change value (such as clicking on arrow button). * @param major_step Major change value (such as clicking on the scroll area). * @param page_size Page size (how many entries are visible / slider size). + * @param stick True if the adjustment should stick to the maximum value. */ - Adjustment( float value, float lower, float upper, float minor_step, float major_step, float page_size ); + Adjustment( float value, float lower, float upper, float minor_step, float major_step, float page_size, bool new_stick); float m_value; float m_lower; float m_upper; float m_minor_step; float m_major_step; - float m_page_size; + float m_page_size; + float m_stick; }; } diff --git a/include/SFGUI/ScrolledWindow.hpp b/include/SFGUI/ScrolledWindow.hpp index bf01f393..c55efdbf 100644 --- a/include/SFGUI/ScrolledWindow.hpp +++ b/include/SFGUI/ScrolledWindow.hpp @@ -39,6 +39,14 @@ class SFGUI_API ScrolledWindow : public Container { DEFAULT = HORIZONTAL_ALWAYS | VERTICAL_ALWAYS }; + /** Scrollbar sticking. + */ + enum class ScrollbarSticking : char { + NO = 0, //!< Do not move scrollbar. + YES, //!< Keep scrollbar sticked to the bottom. + DEFAULT = NO + }; + /** Create scrolled window. * @return ScrolledWindow. */ @@ -88,6 +96,11 @@ class SFGUI_API ScrolledWindow : public Container { */ void SetPlacement( Placement placement ); + /** Set the scrollbar behaviour when the bottom child is expanding. + * @param stick new ScrollbarSticking. + */ + void SetStick(ScrollbarSticking stick); + /** Get the allocation of the content area of this Scrolled Window * @return Allocation of the content area of this Scrolled Window */ @@ -146,7 +159,8 @@ class SFGUI_API ScrolledWindow : public Container { std::shared_ptr m_viewport; char m_policy; - Placement m_placement; + Placement m_placement; + ScrollbarSticking m_stick; }; } diff --git a/src/SFGUI/Adjustment.cpp b/src/SFGUI/Adjustment.cpp index 8f27e863..1198ef73 100644 --- a/src/SFGUI/Adjustment.cpp +++ b/src/SFGUI/Adjustment.cpp @@ -5,18 +5,19 @@ namespace sfg { // Signals. Signal::SignalID Adjustment::OnChange = 0; -Adjustment::Adjustment( float value, float lower, float upper, float minor_step, float major_step, float page_size ) : +Adjustment::Adjustment( float value, float lower, float upper, float minor_step, float major_step, float page_size, bool stick ) : m_value( value ), m_lower( lower ), m_upper( upper ), m_minor_step( minor_step ), m_major_step( major_step ), - m_page_size( page_size ) + m_page_size( page_size ), + m_stick ( stick ) { } -Adjustment::Ptr Adjustment::Create( float value, float lower, float upper, float minor_step, float major_step, float page_size ) { - return Adjustment::Ptr( new Adjustment( value, lower, upper, minor_step, major_step, page_size ) ); +Adjustment::Ptr Adjustment::Create( float value, float lower, float upper, float minor_step, float major_step, float page_size, bool stick ) { + return Adjustment::Ptr( new Adjustment( value, lower, upper, minor_step, major_step, page_size, stick) ); } Adjustment& Adjustment::operator=( const Adjustment& adjustment ) { @@ -25,7 +26,8 @@ Adjustment& Adjustment::operator=( const Adjustment& adjustment ) { SetMinorStep( adjustment.m_minor_step ); SetMajorStep( adjustment.m_major_step ); SetPageSize( adjustment.m_page_size ); - SetValue( adjustment.m_value ); + SetValue( adjustment.m_value ); + SetValue( adjustment.m_stick ); return *this; } @@ -51,6 +53,10 @@ void Adjustment::SetValue( float new_value ) { } } +bool Adjustment::IsAtUpper() const { + return m_value + m_page_size >= m_upper; +} + float Adjustment::GetLower() const { return m_lower; } @@ -70,13 +76,25 @@ float Adjustment::GetUpper() const { } void Adjustment::SetUpper( float new_upper ) { - m_upper = new_upper; + if (m_upper != new_upper) + { + bool sticked = m_stick && IsAtUpper(); + m_upper = new_upper; - if( m_upper < m_lower ) { - m_lower = m_upper; - } + if (m_upper < m_lower) { + m_lower = m_upper; + } - SetValue( GetValue() ); + SetValue(sticked ? m_upper : GetValue()); + } +} + +bool Adjustment::GetStick() const { + return m_stick; +} + +void Adjustment::SetStick(bool new_stick) { + m_stick = new_stick; } float Adjustment::GetMinorStep() const { @@ -109,13 +127,14 @@ void Adjustment::SetPageSize( float new_page_size ) { SetValue( GetValue() ); } -void Adjustment::Configure( float new_value, float new_lower, float new_upper, float new_minor_step, float new_major_step, float new_page_size ) { +void Adjustment::Configure( float new_value, float new_lower, float new_upper, float new_minor_step, float new_major_step, float new_page_size, bool new_stick ) { SetValue( new_value ); SetLower( new_lower ); SetUpper( new_upper ); SetMinorStep( new_minor_step ); SetMajorStep( new_major_step ); - SetPageSize( new_page_size ); + SetPageSize(new_page_size); + SetStick(new_stick); } void Adjustment::Increment() { diff --git a/src/SFGUI/ListBox.cpp b/src/SFGUI/ListBox.cpp index 1b4f1670..7433dea2 100644 --- a/src/SFGUI/ListBox.cpp +++ b/src/SFGUI/ListBox.cpp @@ -543,7 +543,8 @@ void ListBox::UpdateScrollbarAdjustment() { static_cast( m_items.size() ), 1.f, static_cast( m_max_displayed_items_count ), - static_cast( m_max_displayed_items_count ) + static_cast( m_max_displayed_items_count ), + false // stick ); } diff --git a/src/SFGUI/ScrolledWindow.cpp b/src/SFGUI/ScrolledWindow.cpp index fb79bf33..d129d643 100644 --- a/src/SFGUI/ScrolledWindow.cpp +++ b/src/SFGUI/ScrolledWindow.cpp @@ -14,7 +14,8 @@ ScrolledWindow::ScrolledWindow( Adjustment::Ptr horizontal_adjustment, Adjustmen m_vertical_scrollbar(), m_viewport(), m_policy( ScrollbarPolicy::DEFAULT ), - m_placement( Placement::DEFAULT ) + m_placement( Placement::DEFAULT ), + m_stick(ScrollbarSticking::DEFAULT) { m_horizontal_scrollbar = Scrollbar::Create( horizontal_adjustment, Scrollbar::Orientation::HORIZONTAL ); m_vertical_scrollbar = Scrollbar::Create( vertical_adjustment, Scrollbar::Orientation::VERTICAL ); @@ -81,6 +82,15 @@ void ScrolledWindow::SetPlacement( Placement placement ) { Invalidate(); } +void ScrolledWindow::SetStick(ScrollbarSticking stick) { + m_stick = stick; + GetHorizontalAdjustment()->SetStick(stick == ScrollbarSticking::YES); + GetVerticalAdjustment()->SetStick(stick == ScrollbarSticking::YES); + + RecalculateContentAllocation(); + Invalidate(); +} + bool ScrolledWindow::IsHorizontalScrollbarVisible() const { if( m_policy & HORIZONTAL_ALWAYS ) { return true; diff --git a/src/SFGUI/SpinButton.cpp b/src/SFGUI/SpinButton.cpp index 4b48117d..8c9cfb66 100644 --- a/src/SFGUI/SpinButton.cpp +++ b/src/SFGUI/SpinButton.cpp @@ -25,7 +25,7 @@ SpinButton::SpinButton() : SpinButton::Ptr SpinButton::Create( float minimum, float maximum, float step ) { auto adjustment = Adjustment::Create(); - adjustment->Configure( minimum, minimum, maximum, step, 0.f, 0.f ); + adjustment->Configure( minimum, minimum, maximum, step, 0.f, 0.f, false /* stick */ ); auto ptr = Ptr( new SpinButton ); ptr->SetAdjustment( adjustment ); From 1e23bf27e9018fc6a6214e972a4ecb0c323e2659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Mr=C3=B3z?= Date: Sat, 2 Jan 2021 14:32:38 +0100 Subject: [PATCH 2/4] Fix compilation on Windows --- include/SFGUI/ResizableImage.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SFGUI/ResizableImage.hpp b/include/SFGUI/ResizableImage.hpp index c8a5427b..8a1cd6e8 100644 --- a/include/SFGUI/ResizableImage.hpp +++ b/include/SFGUI/ResizableImage.hpp @@ -189,7 +189,7 @@ namespace sfg if (m_KeepAspect) { // Use same scale for both sides of the sprite - float lowerScale = std::min(scaleX, scaleY); + float lowerScale = scaleX < scaleY ? scaleX : scaleY; tempSprite.scale(lowerScale, lowerScale); From 3ff1323d6a2502260c15b739749a5004bae9b884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Mr=C3=B3z?= Date: Sat, 2 Jan 2021 14:45:39 +0100 Subject: [PATCH 3/4] Add RichText from https://github.com/Skyrpex/RichText --- examples/CMakeLists.txt | 1 + examples/RichText.cpp | 37 +++ include/SFGUI/RichText.hpp | 288 +++++++++++++++++++++ src/SFGUI/RichText.cpp | 512 +++++++++++++++++++++++++++++++++++++ 4 files changed, 838 insertions(+) create mode 100644 examples/RichText.cpp create mode 100644 include/SFGUI/RichText.hpp create mode 100644 src/SFGUI/RichText.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d7edba36..1cfb8d9d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -39,6 +39,7 @@ build_example( "SpinButton" "SpinButton.cpp" ) build_example( "Canvas" "Canvas.cpp" ) build_example( "CustomWidget" "CustomWidget.cpp" ) build_example( "ListBox" "ListBox.cpp" ) +build_example( "RichText" "RichText.cpp" ) build_example( "SFGUI-Test" "Test.cpp" ) if( SFGUI_BOOST_FILESYSTEM_SUPPORT ) diff --git a/examples/RichText.cpp b/examples/RichText.cpp new file mode 100644 index 00000000..adba5f62 --- /dev/null +++ b/examples/RichText.cpp @@ -0,0 +1,37 @@ +#include +#include "../RichText.hpp" + +int main() +{ + sf::RenderWindow window(sf::VideoMode(800, 600), "sfe::RichText"); + window.setFramerateLimit(30); + + sf::Font font; + font.loadFromFile("FreeMono.ttf"); + + sfe::RichText text(font); + text << sf::Text::Bold << sf::Color::Cyan << "This " + << sf::Text::Italic << sf::Color::White << "is\nan\n" + << sf::Text::Regular << sf::Color::Green << "example" + << sf::Color::White << ".\n" + << sf::Text::Underlined << "It looks good!\n" << sf::Text::StrikeThrough + << sfe::Outline{ sf::Color::Blue, 3.f } << "Really good!"; + + text.setCharacterSize(25); + text.setPosition(400, 300); + text.setOrigin(text.getGlobalBounds().width / 2.f, text.getGlobalBounds().height / 2.f); + + while (window.isOpen()) + { + sf::Event event; + while (window.pollEvent(event)) + { + if (event.type == sf::Event::Closed) + window.close(); + } + + window.clear(); + window.draw(text); + window.display(); + } +} diff --git a/include/SFGUI/RichText.hpp b/include/SFGUI/RichText.hpp new file mode 100644 index 00000000..2b00da80 --- /dev/null +++ b/include/SFGUI/RichText.hpp @@ -0,0 +1,288 @@ +#pragma once + +////////////////////////////////////////////////////////////////////////// +// Headers +////////////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include + +#include + +namespace sf +{ +class Font; +class String; +template class Rect; +typedef Rect FloatRect; +} + +namespace sfe +{ +struct TextStroke +{ + sf::Color fill = sf::Color::White; + sf::Color outline = sf::Color::Transparent; + float thickness = 0.f; +}; + +struct Outline +{ + sf::Color outline = sf::Color::Transparent; + float thickness = 0.f; +}; + +class RichText : public sf::Drawable, public sf::Transformable +{ +public: + ////////////////////////////////////////////////////////////////////////// + // Nested class that represents a single line + ////////////////////////////////////////////////////////////////////////// + class Line : public sf::Transformable, public sf::Drawable + { + public: + ////////////////////////////////////////////////////////////////////// + // Set a character's color. + // NOTE: Attempting to access a character outside of the line bounds + // causes a crash. + ////////////////////////////////////////////////////////////////////// + void setCharacterColor(std::size_t pos, sf::Color color); + + ////////////////////////////////////////////////////////////////////// + // Set a character's style + // NOTE: Attempting to access a character outside of the line bounds + // causes a crash. + ////////////////////////////////////////////////////////////////////// + void setCharacterStyle(std::size_t pos, sf::Text::Style style); + + ////////////////////////////////////////////////////////////////////// + // Set a character + // NOTE: Attempting to access a character outside of the line bounds + // causes a crash. + ////////////////////////////////////////////////////////////////////// + void setCharacter(std::size_t pos, sf::Uint32 character); + + ////////////////////////////////////////////////////////////////////// + // Set character size + ////////////////////////////////////////////////////////////////////// + void setCharacterSize(unsigned int size); + + ////////////////////////////////////////////////////////////////////// + // Set font + ////////////////////////////////////////////////////////////////////// + void setFont(const sf::Font &font); + + ////////////////////////////////////////////////////////////////////// + // Get the length of the line + ////////////////////////////////////////////////////////////////////// + std::size_t getLength() const; + + ////////////////////////////////////////////////////////////////////// + // Get a character's color + // NOTE: Attempting to access a character outside of the line bounds + // causes a crash. + ////////////////////////////////////////////////////////////////////// + sf::Color getCharacterColor(std::size_t pos) const; + + ////////////////////////////////////////////////////////////////////// + // Get a character's style + // NOTE: Attempting to access a character outside of the line bounds + // causes a crash. + ////////////////////////////////////////////////////////////////////// + sf::Uint32 getCharacterStyle(std::size_t pos) const; + + ////////////////////////////////////////////////////////////////////// + // Get a character + // NOTE: Attempting to access a character outside of the line bounds + // causes a crash + ////////////////////////////////////////////////////////////////////// + sf::Uint32 getCharacter(std::size_t pos) const; + + ////////////////////////////////////////////////////////////////////// + // 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: + ////////////////////////////////////////////////////////////////////// + // Get the index of the sf::Text containing the pos'th character. + // Also changes pos to the position of the character in the sf::Text. + ////////////////////////////////////////////////////////////////////// + std::size_t convertLinePosToLocal(std::size_t &pos) const; + + ////////////////////////////////////////////////////////////////////// + // Split a string to isolate the given character + // into a string of its own for individual formatting + ////////////////////////////////////////////////////////////////////// + void isolateCharacter(std::size_t pos); + + ////////////////////////////////////////////////////////////////////// + // Update geometry + ////////////////////////////////////////////////////////////////////// + void updateGeometry() const; + + ////////////////////////////////////////////////////////////////////// + // Update geometry for a given text + ////////////////////////////////////////////////////////////////////// + void updateTextAndGeometry(sf::Text &text) const; + + ////////////////////////////////////////////////////////////////////// + // Member data + ////////////////////////////////////////////////////////////////////// + mutable std::vector m_texts; ///< List of texts + mutable sf::FloatRect m_bounds; ///< Local bounds + }; + + ////////////////////////////////////////////////////////////////////////// + // Constructor + ////////////////////////////////////////////////////////////////////////// + RichText(); + + ////////////////////////////////////////////////////////////////////////// + // Constructor + ////////////////////////////////////////////////////////////////////////// + RichText(const sf::Font &font); + + ////////////////////////////////////////////////////////////////////////// + // Operators + ////////////////////////////////////////////////////////////////////////// + RichText & operator << (const TextStroke &stroke); + RichText & operator << (const Outline &outline); + RichText & operator << (const sf::Color &color); + RichText & operator << (sf::Text::Style style); + RichText & operator << (const sf::String &string); + + ////////////////////////////////////////////////////////////////////////// + // Set the color of a character. + // Attempting to access a character outside of the bounds causes a crash. + ////////////////////////////////////////////////////////////////////////// + void setCharacterColor(std::size_t line, std::size_t pos, sf::Color color); + + ////////////////////////////////////////////////////////////////////////// + // Set the style of a character. + // Attempting to access a character outside of the bounds causes a crash. + ////////////////////////////////////////////////////////////////////////// + void setCharacterStyle(std::size_t line, std::size_t pos, sf::Text::Style style); + + ////////////////////////////////////////////////////////////////////////// + // Set a character + // Attempting to access a character outside of the bounds causes a crash. + ////////////////////////////////////////////////////////////////////////// + void setCharacter(std::size_t line, std::size_t pos, sf::Uint32 character); + + ////////////////////////////////////////////////////////////////////////// + // Set character size + ////////////////////////////////////////////////////////////////////////// + void setCharacterSize(unsigned int size); + + ////////////////////////////////////////////////////////////////////////// + // Set font + ////////////////////////////////////////////////////////////////////////// + void setFont(const sf::Font &font); + + ////////////////////////////////////////////////////////////////////////// + // Clear + ////////////////////////////////////////////////////////////////////////// + void clear(); + + ////////////////////////////////////////////////////////////////////////// + // Get the color of a character. + // Attempting to access a character outside of the bounds causes a crash. + ////////////////////////////////////////////////////////////////////////// + sf::Color getCharacterColor(std::size_t line, std::size_t pos) const; + + ////////////////////////////////////////////////////////////////////////// + // Get the style of a character. + // Attempting to access a character outside of the bounds causes a crash. + ////////////////////////////////////////////////////////////////////////// + sf::Uint32 getCharacterStyle(std::size_t line, std::size_t pos) const; + + ////////////////////////////////////////////////////////////////////////// + // Get a character + // Attempting to access a character outside of the bounds causes a crash. + ////////////////////////////////////////////////////////////////////////// + sf::Uint32 getCharacter(std::size_t line, std::size_t pos) const; + + ////////////////////////////////////////////////////////////////////////// + // Get text list + ////////////////////////////////////////////////////////////////////////// + const std::vector &getLines() const; + + ////////////////////////////////////////////////////////////////////////// + // Get character size + ////////////////////////////////////////////////////////////////////////// + unsigned int getCharacterSize() const; + + ////////////////////////////////////////////////////////////////////////// + // Get font + ////////////////////////////////////////////////////////////////////////// + const sf::Font *getFont() const; + + ////////////////////////////////////////////////////////////////////////// + // Get local bounds + ////////////////////////////////////////////////////////////////////////// + sf::FloatRect getLocalBounds() const; + + ////////////////////////////////////////////////////////////////////////// + // Get global bounds + ////////////////////////////////////////////////////////////////////////// + sf::FloatRect getGlobalBounds() const; + +protected: + ////////////////////////////////////////////////////////////////////////// + // Render + ////////////////////////////////////////////////////////////////////////// + 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 m_lines; ///< List of lines + const sf::Font *m_font; ///< Font + unsigned int m_characterSize; ///< Character size + mutable sf::FloatRect m_bounds; ///< Local bounds + TextStroke m_currentStroke; ///< Last used stroke + sf::Text::Style m_currentStyle; ///< Last style used +}; + +} diff --git a/src/SFGUI/RichText.cpp b/src/SFGUI/RichText.cpp new file mode 100644 index 00000000..3c436718 --- /dev/null +++ b/src/SFGUI/RichText.cpp @@ -0,0 +1,512 @@ +//////////////////////////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////////////////////////// +#include +#include + +#include "RichText.hpp" + +#include +#include +#include + +#include + +namespace sfe +{ + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::setCharacterColor(std::size_t pos, sf::Color color) +{ + assert(pos < getLength()); + isolateCharacter(pos); + std::size_t stringToFormat = convertLinePosToLocal(pos); + m_texts[stringToFormat].setFillColor(color); + updateGeometry(); +} + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::setCharacterStyle(std::size_t pos, sf::Text::Style style) +{ + assert(pos < getLength()); + isolateCharacter(pos); + std::size_t stringToFormat = convertLinePosToLocal(pos); + m_texts[stringToFormat].setStyle(style); + updateGeometry(); +} +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::setCharacter(std::size_t pos, sf::Uint32 character) +{ + assert(pos < getLength()); + sf::Text& text = m_texts[convertLinePosToLocal(pos)]; + sf::String string = text.getString(); + string[pos] = character; + text.setString(string); + updateGeometry(); +} + + +//////////////////////////////////////////////////////////////////////////////// +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(); +} + + +//////////////////////////////////////////////////////////////////////////////// +std::size_t RichText::Line::getLength() const +{ + std::size_t count = 0; + for (sf::Text &text : m_texts) + { + count += text.getString().getSize(); + } + return count; +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::Color RichText::Line::getCharacterColor(std::size_t pos) const +{ + assert(pos < getLength()); + return m_texts[convertLinePosToLocal(pos)].getFillColor(); +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::Uint32 RichText::Line::getCharacterStyle(std::size_t pos) const +{ + assert(pos < getLength()); + return m_texts[convertLinePosToLocal(pos)].getStyle(); +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::Uint32 RichText::Line::getCharacter(std::size_t pos) const +{ + assert(pos < getLength()); + sf::Text& text = m_texts[convertLinePosToLocal(pos)]; + return text.getString()[pos]; +} + + +//////////////////////////////////////////////////////////////////////////////// +const std::vector &RichText::Line::getTexts() const +{ + return m_texts; +} + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::appendText(sf::Text text) +{ + updateTextAndGeometry(text); + m_texts.push_back(std::move(text)); +} + + +//////////////////////////////////////////////////////////////////////////////// +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); +} + + +//////////////////////////////////////////////////////////////////////////////// +std::size_t RichText::Line::convertLinePosToLocal(std::size_t& pos) const +{ + assert(pos < getLength()); + std::size_t arrayIndex = 0; + for (; pos >= m_texts[arrayIndex].getString().getSize(); ++arrayIndex) + { + pos -= m_texts[arrayIndex].getString().getSize(); + } + return arrayIndex; +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::isolateCharacter(std::size_t pos) +{ + std::size_t localPos = pos; + std::size_t index = convertLinePosToLocal(localPos); + sf::Text temp = m_texts[index]; + + sf::String string = temp.getString(); + if (string.getSize() == 1) + return; + + m_texts.erase(m_texts.begin() + index); + if (localPos != string.getSize() - 1) + { + temp.setString(string.substring(localPos+1)); + m_texts.insert(m_texts.begin() + index, temp); + } + + temp.setString(string.substring(localPos, 1)); + m_texts.insert(m_texts.begin() + index, temp); + + if (localPos != 0) + { + temp.setString(string.substring(0, localPos)); + m_texts.insert(m_texts.begin() + index, temp); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::updateGeometry() const +{ + m_bounds = sf::FloatRect(); + + for (sf::Text& text : m_texts) + updateTextAndGeometry(text); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::Line::updateTextAndGeometry(sf::Text& text) const +{ + // Set text offset + text.setPosition(m_bounds.width, 0.f); + + // Update bounds + float lineSpacing = std::floor(text.getFont()->getLineSpacing(text.getCharacterSize())); + m_bounds.height = std::max(m_bounds.height, lineSpacing); + m_bounds.width += text.getGlobalBounds().width; +} + + +//////////////////////////////////////////////////////////////////////////////// +RichText::RichText() + : RichText(nullptr) +{ + +} + + +//////////////////////////////////////////////////////////////////////////////// +RichText::RichText(const sf::Font& font) + : RichText(&font) +{ + +} + +//////////////////////////////////////////////////////////////////////////////// +RichText& RichText::operator << (const TextStroke& stroke) +{ + m_currentStroke = stroke; + return *this; +} + +RichText& RichText::operator << (const Outline& outline) +{ + m_currentStroke.outline = outline.outline; + m_currentStroke.thickness = outline.thickness; + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +RichText & RichText::operator << (const sf::Color& color) +{ + m_currentStroke.fill = color; + return *this; +} + + +//////////////////////////////////////////////////////////////////////////////// +RichText & RichText::operator << (sf::Text::Style style) +{ + m_currentStyle = style; + return *this; +} + + +//////////////////////////////////////////////////////////////////////////////// +std::vector explode(const sf::String& string, sf::Uint32 delimiter) +{ + if (string.isEmpty()) + return std::vector(); + + // For each character in the string + 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 + buffer += character; + } + } + + // 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; +} + + +//////////////////////////////////////////////////////////////////////////////// +RichText & RichText::operator << (const sf::String& string) +{ + // Maybe skip + if (string.isEmpty()) + return *this; + + // 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; +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::setCharacterColor(std::size_t line, std::size_t pos, sf::Color color) +{ + assert(line < m_lines.size()); + m_lines[line].setCharacterColor(pos, color); + updateGeometry(); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::setCharacterStyle(std::size_t line, std::size_t pos, sf::Text::Style style) +{ + assert(line < m_lines.size()); + m_lines[line].setCharacterStyle(pos, style); + updateGeometry(); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::setCharacter(std::size_t line, std::size_t pos, sf::Uint32 character) +{ + assert(line < m_lines.size()); + m_lines[line].setCharacter(pos, character); + updateGeometry(); +} + +//////////////////////////////////////////////////////////////////////////////// +void RichText::setCharacterSize(unsigned int size) +{ + // Maybe skip + if (m_characterSize == size) + return; + + // Update character size + m_characterSize = size; + + // Set texts character size + for (Line &line : m_lines) + line.setCharacterSize(size); + + updateGeometry(); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::setFont(const sf::Font& font) +{ + // Maybe skip + if (m_font == &font) + return; + + // Update font + m_font = &font; + + // Set texts font + for (Line &line : m_lines) + line.setFont(font); + + updateGeometry(); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::clear() +{ + // Clear texts + m_lines.clear(); + + // Reset bounds + m_bounds = sf::FloatRect(); +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::Color RichText::getCharacterColor(std::size_t line, std::size_t pos) const +{ + assert(line < m_lines.size()); + return m_lines[line].getCharacterColor(pos); +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::Uint32 RichText::getCharacterStyle(std::size_t line, std::size_t pos) const +{ + assert(line < m_lines.size()); + return m_lines[line].getCharacterStyle(pos); +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::Uint32 RichText::getCharacter(std::size_t line, std::size_t pos) const +{ + assert(line < m_lines.size()); + return m_lines[line].getCharacter(pos); +} + + +//////////////////////////////////////////////////////////////////////////////// +const std::vector &RichText::getLines() const +{ + return m_lines; +} + + +//////////////////////////////////////////////////////////////////////////////// +unsigned int RichText::getCharacterSize() const +{ + return m_characterSize; +} + + +//////////////////////////////////////////////////////////////////////////////// +const sf::Font *RichText::getFont() const +{ + return m_font; +} + + +//////////////////////////////////////////////////////////// +sf::FloatRect RichText::getLocalBounds() const +{ + return m_bounds; +} + + +//////////////////////////////////////////////////////////// +sf::FloatRect RichText::getGlobalBounds() const +{ + return getTransform().transformRect(getLocalBounds()); +} + + +//////////////////////////////////////////////////////////////////////////////// +void RichText::draw(sf::RenderTarget& target, sf::RenderStates states) const +{ + states.transform *= getTransform(); + + for (const Line &line : m_lines) + target.draw(line, states); +} + + +//////////////////////////////////////////////////////////////////////////////// +RichText::RichText(const sf::Font* font) + : m_font(font), + m_characterSize(30), + m_currentStroke{ sf::Color::White, sf::Color::Transparent }, + m_currentStyle(sf::Text::Regular) +{ + +} + + +//////////////////////////////////////////////////////////////////////////////// +sf::Text RichText::createText(const sf::String& string) const +{ + sf::Text text; + text.setString(string); + text.setFillColor(m_currentStroke.fill); + text.setOutlineColor(m_currentStroke.outline); + text.setOutlineThickness(m_currentStroke.thickness); + text.setStyle(m_currentStyle); + text.setCharacterSize(m_characterSize); + 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); + } +} + +} From a6e082c1fa1c7e0eebad6926347d524e0c272955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Mr=C3=B3z?= Date: Fri, 30 Apr 2021 15:39:44 +0200 Subject: [PATCH 4/4] RichText to Widget transition --- examples/RichText.cpp | 124 ++++++++++++++++++------ include/SFGUI/Engine.hpp | 7 ++ include/SFGUI/Engines/BREW.hpp | 3 +- include/SFGUI/RichText.hpp | 40 ++++++-- include/SFGUI/Widgets.hpp | 1 + src/SFGUI/Engines/BREW.cpp | 3 + src/SFGUI/Engines/BREW/RichText.cpp | 27 ++++++ src/SFGUI/RichText.cpp | 140 ++++++++++++++++++++++------ 8 files changed, 283 insertions(+), 62 deletions(-) create mode 100644 src/SFGUI/Engines/BREW/RichText.cpp diff --git a/examples/RichText.cpp b/examples/RichText.cpp index adba5f62..096f6226 100644 --- a/examples/RichText.cpp +++ b/examples/RichText.cpp @@ -1,37 +1,109 @@ +// Always include the necessary header files. +// Including SFGUI/Widgets.hpp includes everything +// you can possibly need automatically. + +// Always include the necessary header files. +// Including SFGUI/Widgets.hpp includes everything +// you can possibly need automatically. +#include +#include + #include -#include "../RichText.hpp" + int main() { - sf::RenderWindow window(sf::VideoMode(800, 600), "sfe::RichText"); - window.setFramerateLimit(30); + // Create the main SFML window + sf::RenderWindow app_window(sf::VideoMode(800, 600), "SFGUI RichText Example", sf::Style::Titlebar | sf::Style::Close); + + // We have to do this because we don't use SFML to draw. + app_window.resetGLStates(); + + // Create an SFGUI. This is required before doing anything with SFGUI. + sfg::SFGUI sfgui; + + // Create our main SFGUI window + auto window = sfg::Window::Create(); + window->SetTitle("Title"); sf::Font font; - font.loadFromFile("FreeMono.ttf"); - - sfe::RichText text(font); - text << sf::Text::Bold << sf::Color::Cyan << "This " - << sf::Text::Italic << sf::Color::White << "is\nan\n" - << sf::Text::Regular << sf::Color::Green << "example" - << sf::Color::White << ".\n" - << sf::Text::Underlined << "It looks good!\n" << sf::Text::StrikeThrough - << sfe::Outline{ sf::Color::Blue, 3.f } << "Really good!"; - - text.setCharacterSize(25); - text.setPosition(400, 300); - text.setOrigin(text.getGlobalBounds().width / 2.f, text.getGlobalBounds().height / 2.f); - - while (window.isOpen()) + // Load it from a file + if (!font.loadFromFile("D:\\Fafa\\Domki\\SFGUI\\SFGUI++\\build\\examples\\Debug\\sansation.ttf")) { + return -1; + // error... + } + + // Create the richtext. + auto richtext = sfg::RichText::Create(); + richtext->setFont(font); + + // Set the text of the richtext. + *richtext << "Default colors. " << sf::Text::Bold << sf::Color::Cyan << "This " + << sf::Text::Italic << sf::Color::White << "is\nan\n" + << sf::Text::Regular << sf::Color::Green << "example" << " going now very long and booooring" + << sf::Color::White << ".\n" + << sf::Text::Underlined << "It looks good!\n" << sf::Text::StrikeThrough + << sfg::Outline{ sf::Color::Blue, 3.f } << "Really good!" + << sf::Text::Italic << sf::Color::White << "start chatting below...\n"; + + // Create the ScrolledWindow. + auto scrolledwindow = sfg::ScrolledWindow::Create(); + scrolledwindow->SetScrollbarPolicy(sfg::ScrolledWindow::VERTICAL_AUTOMATIC | sfg::ScrolledWindow::HORIZONTAL_AUTOMATIC); + scrolledwindow->AddWithViewport(richtext); + scrolledwindow->SetRequisition(sf::Vector2f(50.f, 50.f)); + scrolledwindow->SetStick(sfg::ScrolledWindow::ScrollbarSticking::YES); + + // Create label. + auto label = sfg::Label::Create("Type chat and enter:"); + label->SetAlignment(sf::Vector2f(0,0)); + + // Create the Entry for adding text to richtext. + auto entry = sfg::Entry::Create(""); + entry->GetSignal(sfg::Entry::OnReturnPressed).Connect([&richtext, &entry] { + *richtext << sf::Text::Bold << sf::Color::Green << "Fafa87:" << sf::Text::Regular << sf::Color::White << entry->GetText() << "\n"; + entry->SetText(""); + }); + entry->SetRequisition(sf::Vector2f(80.f, 20.f)); + + // Pack both into a box. + auto box = sfg::Box::Create(sfg::Box::Orientation::VERTICAL); + box->SetSpacing(10); + box->Pack(scrolledwindow); + box->Pack(sfg::Separator::Create(), false); + box->Pack(label, false); + box->Pack(entry, false); + + window->Add(box); + + // Start the game loop + while (app_window.isOpen()) { + // Process events sf::Event event; - while (window.pollEvent(event)) - { - if (event.type == sf::Event::Closed) - window.close(); + + while (app_window.pollEvent(event)) { + // Handle events + window->HandleEvent(event); + + // Close window : exit + if (event.type == sf::Event::Closed) { + return EXIT_SUCCESS; + } } - window.clear(); - window.draw(text); - window.display(); + // Update the GUI, note that you shouldn't normally + // pass 0 seconds to the update method. + window->Update(0.f); + + // Clear screen + app_window.clear(); + + // Draw the GUI + sfgui.Display(app_window); + + // Update the window + app_window.display(); } -} + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/include/SFGUI/Engine.hpp b/include/SFGUI/Engine.hpp index b6375439..293bf56d 100644 --- a/include/SFGUI/Engine.hpp +++ b/include/SFGUI/Engine.hpp @@ -41,6 +41,7 @@ class Spinner; class ComboBox; class SpinButton; class ListBox; +class RichText; class Selector; class RenderQueue; @@ -171,6 +172,12 @@ class SFGUI_API Engine { */ virtual std::unique_ptr CreateListBoxDrawable( std::shared_ptr listbox ) const = 0; + /** Create drawable for richtext widgets. + * @param richtext Widget. + * @return New drawable object (unmanaged memory!). + */ + virtual std::unique_ptr CreateRichTextDrawable(std::shared_ptr listbox) const = 0; + /** Get maximum line height. * @param font Font. * @param font_size Font size. diff --git a/include/SFGUI/Engines/BREW.hpp b/include/SFGUI/Engines/BREW.hpp index 0d5ef5b2..0be89b7b 100644 --- a/include/SFGUI/Engines/BREW.hpp +++ b/include/SFGUI/Engines/BREW.hpp @@ -43,7 +43,8 @@ class SFGUI_API BREW : public Engine { std::unique_ptr CreateSpinnerDrawable( std::shared_ptr spinner ) const override; std::unique_ptr CreateComboBoxDrawable( std::shared_ptr combo_box ) const override; std::unique_ptr CreateSpinButtonDrawable( std::shared_ptr spinbutton ) const override; - std::unique_ptr CreateListBoxDrawable( std::shared_ptr listbox ) const override; + std::unique_ptr CreateListBoxDrawable( std::shared_ptr listbox ) const override; + std::unique_ptr CreateRichTextDrawable( std::shared_ptr richtext ) const override; private: static std::unique_ptr CreateBorder( const sf::FloatRect& rect, float border_width, const sf::Color& light_color, const sf::Color& dark_color ); diff --git a/include/SFGUI/RichText.hpp b/include/SFGUI/RichText.hpp index 2b00da80..a6cb303f 100644 --- a/include/SFGUI/RichText.hpp +++ b/include/SFGUI/RichText.hpp @@ -12,6 +12,8 @@ #include +#include + namespace sf { class Font; @@ -20,7 +22,7 @@ template class Rect; typedef Rect FloatRect; } -namespace sfe +namespace sfg { struct TextStroke { @@ -35,13 +37,13 @@ struct Outline float thickness = 0.f; }; -class RichText : public sf::Drawable, public sf::Transformable +class SFGUI_API RichText : public Widget, public sf::Drawable, public sf::Transformable { public: ////////////////////////////////////////////////////////////////////////// // Nested class that represents a single line ////////////////////////////////////////////////////////////////////////// - class Line : public sf::Transformable, public sf::Drawable + class RichLine : public sf::Transformable, public sf::Drawable { public: ////////////////////////////////////////////////////////////////////// @@ -121,6 +123,11 @@ class RichText : public sf::Drawable, public sf::Transformable ////////////////////////////////////////////////////////////////////// sf::FloatRect getGlobalBounds() const; + ////////////////////////////////////////////////////////////////////// + // Set top for all texts + ////////////////////////////////////////////////////////////////////// + void updateTop() const; + protected: ////////////////////////////////////////////////////////////////////// // Draw @@ -167,6 +174,24 @@ class RichText : public sf::Drawable, public sf::Transformable ////////////////////////////////////////////////////////////////////////// RichText(const sf::Font &font); + + ////////////////////////////////////////////////////////////////////////// + // Please be a widget + ////////////////////////////////////////////////////////////////////////// + typedef std::shared_ptr Ptr; //!< Shared pointer. + typedef std::shared_ptr PtrConst; //!< Shared pointer. + /** Create label. + * @param text Text. + * @return Label. + */ + static Ptr Create(const sf::String& text = L""); + + const std::string& GetName() const override; + + sf::Vector2f CalculateRequisition() override; + void HandleRequisitionChange() override; + void HandleSizeChange() override; + ////////////////////////////////////////////////////////////////////////// // Operators ////////////////////////////////////////////////////////////////////////// @@ -230,7 +255,7 @@ class RichText : public sf::Drawable, public sf::Transformable ////////////////////////////////////////////////////////////////////////// // Get text list ////////////////////////////////////////////////////////////////////////// - const std::vector &getLines() const; + const std::vector &getLines() const; ////////////////////////////////////////////////////////////////////////// // Get character size @@ -258,6 +283,9 @@ class RichText : public sf::Drawable, public sf::Transformable ////////////////////////////////////////////////////////////////////////// void draw(sf::RenderTarget &target, sf::RenderStates states) const override; + + std::unique_ptr InvalidateImpl() const override; + private: ////////////////////////////////////////////////////////////////////////// // Delegate constructor @@ -272,12 +300,12 @@ class RichText : public sf::Drawable, public sf::Transformable ////////////////////////////////////////////////////////////////////////// // Update geometry ////////////////////////////////////////////////////////////////////////// - void updateGeometry() const; + void updateGeometry(); ////////////////////////////////////////////////////////////////////////// // Member data ////////////////////////////////////////////////////////////////////////// - mutable std::vector m_lines; ///< List of lines + 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 diff --git a/include/SFGUI/Widgets.hpp b/include/SFGUI/Widgets.hpp index 7470a600..a127edce 100644 --- a/include/SFGUI/Widgets.hpp +++ b/include/SFGUI/Widgets.hpp @@ -37,3 +37,4 @@ #include #include #include +#include diff --git a/src/SFGUI/Engines/BREW.cpp b/src/SFGUI/Engines/BREW.cpp index 50c5d8d9..4b94c785 100644 --- a/src/SFGUI/Engines/BREW.cpp +++ b/src/SFGUI/Engines/BREW.cpp @@ -162,6 +162,9 @@ void BREW::ResetProperties() { SetProperty( "ListBox", "HighlightedColor", sf::Color( 0x65, 0x67, 0x62 ) ); SetProperty( "ListBox", "SelectedColor", sf::Color( 0x5a, 0x6a, 0x50 ) ); + // RichText-specific. + // None as it does not use the properties for formatting. + // (Re)Enable automatic widget refreshing after we are done setting all these properties. SetAutoRefresh( true ); } diff --git a/src/SFGUI/Engines/BREW/RichText.cpp b/src/SFGUI/Engines/BREW/RichText.cpp new file mode 100644 index 00000000..0a176c34 --- /dev/null +++ b/src/SFGUI/Engines/BREW/RichText.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +#include + +namespace sfg { + namespace eng { + std::unique_ptr BREW::CreateRichTextDrawable(std::shared_ptr richtext) const { + std::unique_ptr queue(new RenderQueue); + + for (const auto &line : richtext->getLines()) + { + float pos_line_y = line.getPosition().y; + line.updateTop(); + for (auto &text : line.getTexts()) + { + queue->Add(Renderer::Get().CreateText(text)); + } + } + + return queue; + } + + } +} diff --git a/src/SFGUI/RichText.cpp b/src/SFGUI/RichText.cpp index 3c436718..d591e920 100644 --- a/src/SFGUI/RichText.cpp +++ b/src/SFGUI/RichText.cpp @@ -4,19 +4,23 @@ #include #include -#include "RichText.hpp" +#include #include #include #include +#include +#include +#include + #include -namespace sfe +namespace sfg { //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::setCharacterColor(std::size_t pos, sf::Color color) +void RichText::RichLine::setCharacterColor(std::size_t pos, sf::Color color) { assert(pos < getLength()); isolateCharacter(pos); @@ -26,7 +30,7 @@ void RichText::Line::setCharacterColor(std::size_t pos, sf::Color color) } //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::setCharacterStyle(std::size_t pos, sf::Text::Style style) +void RichText::RichLine::setCharacterStyle(std::size_t pos, sf::Text::Style style) { assert(pos < getLength()); isolateCharacter(pos); @@ -35,7 +39,7 @@ void RichText::Line::setCharacterStyle(std::size_t pos, sf::Text::Style style) updateGeometry(); } //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::setCharacter(std::size_t pos, sf::Uint32 character) +void RichText::RichLine::setCharacter(std::size_t pos, sf::Uint32 character) { assert(pos < getLength()); sf::Text& text = m_texts[convertLinePosToLocal(pos)]; @@ -47,7 +51,7 @@ void RichText::Line::setCharacter(std::size_t pos, sf::Uint32 character) //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::setCharacterSize(unsigned int size) +void RichText::RichLine::setCharacterSize(unsigned int size) { for (sf::Text &text : m_texts) text.setCharacterSize(size); @@ -57,7 +61,7 @@ void RichText::Line::setCharacterSize(unsigned int size) //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::setFont(const sf::Font &font) +void RichText::RichLine::setFont(const sf::Font &font) { for (sf::Text &text : m_texts) text.setFont(font); @@ -67,7 +71,7 @@ void RichText::Line::setFont(const sf::Font &font) //////////////////////////////////////////////////////////////////////////////// -std::size_t RichText::Line::getLength() const +std::size_t RichText::RichLine::getLength() const { std::size_t count = 0; for (sf::Text &text : m_texts) @@ -79,7 +83,7 @@ std::size_t RichText::Line::getLength() const //////////////////////////////////////////////////////////////////////////////// -sf::Color RichText::Line::getCharacterColor(std::size_t pos) const +sf::Color RichText::RichLine::getCharacterColor(std::size_t pos) const { assert(pos < getLength()); return m_texts[convertLinePosToLocal(pos)].getFillColor(); @@ -87,7 +91,7 @@ sf::Color RichText::Line::getCharacterColor(std::size_t pos) const //////////////////////////////////////////////////////////////////////////////// -sf::Uint32 RichText::Line::getCharacterStyle(std::size_t pos) const +sf::Uint32 RichText::RichLine::getCharacterStyle(std::size_t pos) const { assert(pos < getLength()); return m_texts[convertLinePosToLocal(pos)].getStyle(); @@ -95,7 +99,7 @@ sf::Uint32 RichText::Line::getCharacterStyle(std::size_t pos) const //////////////////////////////////////////////////////////////////////////////// -sf::Uint32 RichText::Line::getCharacter(std::size_t pos) const +sf::Uint32 RichText::RichLine::getCharacter(std::size_t pos) const { assert(pos < getLength()); sf::Text& text = m_texts[convertLinePosToLocal(pos)]; @@ -104,13 +108,13 @@ sf::Uint32 RichText::Line::getCharacter(std::size_t pos) const //////////////////////////////////////////////////////////////////////////////// -const std::vector &RichText::Line::getTexts() const +const std::vector &RichText::RichLine::getTexts() const { return m_texts; } //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::appendText(sf::Text text) +void RichText::RichLine::appendText(sf::Text text) { updateTextAndGeometry(text); m_texts.push_back(std::move(text)); @@ -118,21 +122,21 @@ void RichText::Line::appendText(sf::Text text) //////////////////////////////////////////////////////////////////////////////// -sf::FloatRect RichText::Line::getLocalBounds() const +sf::FloatRect RichText::RichLine::getLocalBounds() const { return m_bounds; } //////////////////////////////////////////////////////////////////////////////// -sf::FloatRect RichText::Line::getGlobalBounds() const +sf::FloatRect RichText::RichLine::getGlobalBounds() const { return getTransform().transformRect(getLocalBounds()); } //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::draw(sf::RenderTarget &target, sf::RenderStates states) const +void RichText::RichLine::draw(sf::RenderTarget &target, sf::RenderStates states) const { states.transform *= getTransform(); @@ -142,7 +146,7 @@ void RichText::Line::draw(sf::RenderTarget &target, sf::RenderStates states) con //////////////////////////////////////////////////////////////////////////////// -std::size_t RichText::Line::convertLinePosToLocal(std::size_t& pos) const +std::size_t RichText::RichLine::convertLinePosToLocal(std::size_t& pos) const { assert(pos < getLength()); std::size_t arrayIndex = 0; @@ -155,7 +159,7 @@ std::size_t RichText::Line::convertLinePosToLocal(std::size_t& pos) const //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::isolateCharacter(std::size_t pos) +void RichText::RichLine::isolateCharacter(std::size_t pos) { std::size_t localPos = pos; std::size_t index = convertLinePosToLocal(localPos); @@ -184,7 +188,7 @@ void RichText::Line::isolateCharacter(std::size_t pos) //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::updateGeometry() const +void RichText::RichLine::updateGeometry() const { m_bounds = sf::FloatRect(); @@ -192,15 +196,26 @@ void RichText::Line::updateGeometry() const updateTextAndGeometry(text); } +//////////////////////////////////////////////////////////////////////////////// +void RichText::RichLine::updateTop() const +{ + for (sf::Text& text : m_texts) + { + text.setPosition(text.getPosition().x, this->getPosition().y); + } +} + //////////////////////////////////////////////////////////////////////////////// -void RichText::Line::updateTextAndGeometry(sf::Text& text) const +void RichText::RichLine::updateTextAndGeometry(sf::Text& text) const { // Set text offset text.setPosition(m_bounds.width, 0.f); // Update bounds - float lineSpacing = std::floor(text.getFont()->getLineSpacing(text.getCharacterSize())); + auto character_size = text.getCharacterSize(); + auto line_spacing = text.getFont()->getLineSpacing(character_size); + float lineSpacing = std::floor(line_spacing); m_bounds.height = std::max(m_bounds.height, lineSpacing); m_bounds.width += text.getGlobalBounds().width; } @@ -221,6 +236,70 @@ RichText::RichText(const sf::Font& font) } + +//////////////////////////////////////////////////////////////////////////////// +RichText::Ptr RichText::Create(const sf::String& text) { + Ptr label(new RichText()); + *label << text; + label->RequestResize(); + return label; +} + + +//////////////////////////////////////////////////////////////////////////////// + +std::unique_ptr RichText::InvalidateImpl() const { + return Context::Get().GetEngine().CreateRichTextDrawable(std::dynamic_pointer_cast(shared_from_this())); +} + + +sf::Vector2f RichText::CalculateRequisition() { + auto tmp_bounds = sf::FloatRect(); + + for (RichLine &line : m_lines) { + line.setPosition(0.f, tmp_bounds.height); + + tmp_bounds.height += line.getGlobalBounds().height; + tmp_bounds.width = std::max(tmp_bounds.width, line.getGlobalBounds().width); + } + + return sf::Vector2f(tmp_bounds.width, tmp_bounds.height); +} + + +void RichText::HandleRequisitionChange() { + static auto calculate_y_requisition = false; + + if (!calculate_y_requisition) { + calculate_y_requisition = true; + RequestResize(); + } + else { + calculate_y_requisition = false; + } +} + +void RichText::HandleSizeChange() { + static auto calculate_y_requisition = false; + + if (!calculate_y_requisition) { + calculate_y_requisition = true; + RequestResize(); + } + else { + calculate_y_requisition = false; + } +} + + + + +//////////////////////////////////////////////////////////////////////////////// +const std::string& RichText::GetName() const { + static const std::string name("RichText"); + return name; +} + //////////////////////////////////////////////////////////////////////////////// RichText& RichText::operator << (const TextStroke& stroke) { @@ -298,7 +377,7 @@ RichText & RichText::operator << (const sf::String& string) m_lines.resize(1); // Remove last line's height - Line &line = m_lines.back(); + RichLine &line = m_lines.back(); m_bounds.height -= line.getGlobalBounds().height; // Append text @@ -311,7 +390,7 @@ RichText & RichText::operator << (const sf::String& string) // Append the rest of substrings as new lines while (++it != subStrings.end()) { - Line line; + RichLine line; line.setPosition(0.f, m_bounds.height); line.appendText(createText(*it)); m_lines.push_back(std::move(line)); @@ -321,6 +400,7 @@ RichText & RichText::operator << (const sf::String& string) m_bounds.width = std::max(m_bounds.width, line.getGlobalBounds().width); } + updateGeometry(); // Return return *this; } @@ -363,7 +443,7 @@ void RichText::setCharacterSize(unsigned int size) m_characterSize = size; // Set texts character size - for (Line &line : m_lines) + for (RichLine &line : m_lines) line.setCharacterSize(size); updateGeometry(); @@ -381,7 +461,7 @@ void RichText::setFont(const sf::Font& font) m_font = &font; // Set texts font - for (Line &line : m_lines) + for (RichLine &line : m_lines) line.setFont(font); updateGeometry(); @@ -424,7 +504,7 @@ sf::Uint32 RichText::getCharacter(std::size_t line, std::size_t pos) const //////////////////////////////////////////////////////////////////////////////// -const std::vector &RichText::getLines() const +const std::vector &RichText::getLines() const { return m_lines; } @@ -463,7 +543,7 @@ void RichText::draw(sf::RenderTarget& target, sf::RenderStates states) const { states.transform *= getTransform(); - for (const Line &line : m_lines) + for (const RichLine &line : m_lines) target.draw(line, states); } @@ -497,16 +577,18 @@ sf::Text RichText::createText(const sf::String& string) const //////////////////////////////////////////////////////////////////////////////// -void RichText::updateGeometry() const +void RichText::updateGeometry() { m_bounds = sf::FloatRect(); - for (Line &line : m_lines) { + for (RichLine &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); } + RequestResize(); + Invalidate(); } }