SFML community forums

Help => Graphics => Topic started by: Nexus on March 02, 2012, 09:04:57 pm

Title: sf::Text::GetLocalBounds()
Post by: Nexus on March 02, 2012, 09:04:57 pm
Not sure if this is better suited in feature requests. Actually, the topic doesn't concern a new feature, so I put it here. And since it isn't only connected to the whitespace problem, I think it deserves an own thread.

It would be nice if sf::Text::GetLocalBounds() behaved more intuitively. At the moment, the returned height is different depending on the characters in the text (e.g. 'y' is higher than 'a'). While this seems standing to reason, it makes simple use cases like aligning multiple texts to an edge impossible. Additionally, spaces at the end are not considered in the size calculation.

A proposal to compute the size in local coordinates:
sf::Vector2f GetLocalSize(const sf::Text& text)
{
        const sf::String str = text.GetString() + '\n';

        float maxLineWidth = 0.f;
        float lineWidth = 0.f;
        unsigned int lines = 0;

        for (sf::String::ConstIterator itr = str.Begin(); itr != str.End(); ++itr)
        {
                if (*itr == '\n')
                {
                        ++lines;
                        maxLineWidth = std::max(maxLineWidth, lineWidth);
                        lineWidth = 0.f;
                }
                else
                {
                        lineWidth += text.GetFont().GetGlyph(*itr, text.GetCharacterSize(), text.GetStyle() & sf::Text::Bold).Advance;
                }
        }

        const float lineHeight = static_cast<float>(text.GetFont().GetLineSpacing(text.GetCharacterSize()));
        return sf::Vector2f(maxLineWidth, lines * lineHeight);
}
Some operations might be expensive, however a sf::Text could calculate them upon first call and batch them if necessary. And maybe it is appropriate to expand the bounding rect a little bit on the side and to move it towards the bottom, since it exceeds the text at the top. Or just decrease the height at the top ;)

A simple test application:
void DrawText(sf::RenderWindow& window, const char* str, float x, float y)
{
        sf::Text text(str);
        text.SetPosition(x, y);

        sf::Vector2f size = GetLocalSize(text);

        sf::RectangleShape shape(size);
        shape.SetPosition(text.GetPosition());
        shape.SetFillColor(sf::Color::Transparent);
        shape.SetOutlineColor(sf::Color::Yellow);
        shape.SetOutlineThickness(1.f);

        window.Draw(text);
        window.Draw(shape);
}

int main()
{
        sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Application");
        window.SetFramerateLimit(20);

        while (window.IsOpen())
        {
                sf::Event event;
                while (window.PollEvent(event))
                {
                        if (event.Type == sf::Event::KeyPressed || event.Type == sf::Event::Closed)
                                return 0;
                }

                window.Clear();

                DrawText(window, "hello", 10.f, 10.f);
                DrawText(window, "hello ", 10.f, 60.f);
                DrawText(window, "hello d", 10.f, 110.f);

                DrawText(window, "contains some spaces", 150.f, 10.f);
                DrawText(window, "and a tab\t", 150.f, 60.f);

                DrawText(window, "multiline\ntext", 10.f, 200.f);
                DrawText(window, "text\nmultiline", 150.f, 200.f);

                DrawText(window, "some\nreally long text\neven\nspread across\nmultiple lines.", 350.f, 200.f);

                window.Display();
        }
}
Title: sf::Text::GetLocalBounds()
Post by: Laurent on March 02, 2012, 09:14:47 pm
Thanks, I'll definitely test it and consider your solution.

My todo list is currently full so don't expect feedback from me soon, though.
Title: sf::Text::GetLocalBounds()
Post by: Nexus on March 02, 2012, 09:21:36 pm
Thanks for the response!

Yes, it's not a high-priority task, and it can also be fixed without touching the API. So focus on the important tasks for SFML 2.0 ;)