Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Request: Weaponized Text Wrapping Example  (Read 6806 times)

0 Members and 1 Guest are viewing this topic.

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Request: Weaponized Text Wrapping Example
« on: May 20, 2016, 01:20:59 am »
I see many posts of people asking for this, and almost all of them are people linking to other (sometimes dead) threads or people saying "yahoo I got it" but not posting examples. Laurent says to:

"Otherwise, short answer: compute the position of every character (text.findCharacterPos) and insert a line break when the position exceeds the width of the container."

However I'm extremely new to coding. I'm trying to port my visual novel homebrew game over from 3DS (written in C), but the library on there has a very simple function where you define a rectangle size, position, and character limit and it just kinda does the rest for you.

I was wondering if anyone could take the time to throw together a working example of text wrapping (and maybe as a bonus changing the rendered string with a button press or something?) for all of us beginners @.@ It's pretty much the only thing I'm stuck at currently to finish porting over my game...

Recoil

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Re: Request: Weaponized Text Wrapping Example
« Reply #1 on: May 20, 2016, 02:38:08 am »
I do not understand you "weaponized" request. But are you asking for an example of text wrapping in a region?  What language are you coding in?

I can post an example if that is what you are looking for, but the best I can do would be to convert it to C# and you try to take it from there.  However, it does not leverage SFML methods for proper text wrapping by the width of the font.

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #2 on: May 20, 2016, 03:25:52 am »
Basically it means "fully functional"

I'm coding in C/C++ on Windows using Visual Studio.

I'm using a font too, so it would be handy to have it look at the text/pixel length when conforming it to the rectangle...

Recoil

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Re: Request: Weaponized Text Wrapping Example
« Reply #3 on: May 20, 2016, 04:47:49 am »
I'm a VB.NET programmer, so this is probably not close to what you are looking after.  However, just in case no one comes along and posts fully functional code, maybe this can give you an idea of what you may be able to do...I used an online converter for this BTW.  This has been generically modified as best as I could while trying to remain functional, and with C++ there is probably a much more efficient method of achieving the results you are looking for.

Regardless, here I have a list of strings, the length of the string, and what I want to set to split the words with so it doesn't cut them off at the end:
private List<string> _allTextLines { get; set; }

const int _wordWrapLength = 50;

private readonly char[] _splitChars = new char[] {
        ' ',
        '-',
        ControlChars.Tab
};
 

You entire text string is going to pass here to populate the list with strings of words:
public void AddText(string msg)
{

        _allTextLines = new List<string>();

        foreach (string str in WordWrap(msg, _wordWrapLength)) {
                _allTextLines.Add(str);
        }

}
 

Here is where it is wrapping the lines to be put in sections so the lines aren't split in the middle of a word, then returning it to the method above:
public List<string> WordWrap(string str, int width)
{

        string[] words = Explode(str, _splitChars);
        int curLineLength = 0;
        StringBuilder strBuilder = new StringBuilder();
        int i = 0;
        List<string> rtnString = new List<string>();

        while (i < words.Length) {
                string word = words(i);

                // If adding the new word to the current line would be too long,
                // then put it on a new line (and split it up if it's too long).

                if (curLineLength + word.Length > width) {
                        // Only move down to a new line if we have text on the current line.
                        // Avoids situation where wrapped whitespace causes emptylines in text.
                        if (curLineLength > 0) {
                                strBuilder.Append("|");
                                curLineLength = 0;
                        }

                        // If the current word is too long to fit on a line even on it's own then
                        // split the word up.
                        while (word.Length > width) {
                                strBuilder.Append(word.Substring(0, width - 1) + "-");
                                word = word.Substring(width - 1);
                                strBuilder.Append("|");
                        }

                        // Remove leading whitespace from the word so the new line starts flush to the left.
                        word = word.TrimStart();

                }

                strBuilder.Append(word);
                curLineLength += word.Length;
                i += 1;
        }

        string[] lines = strBuilder.ToString.Split("|");
        foreach (string line in lines) {
                rtnString.Add(line.Replace("|", ""));
                // & vbNewLine)
        }

        return rtnString;

}
 

And here is where it is splitting the words at a space or hyphen, returning it to WordWrap:
public string[] Explode(string str, char[] splitChars)
{
        string[] functionReturnValue = null;

        List<string> parts = new List<string>();
        int startIndex = 0;
        functionReturnValue = null;
        while (true) {
                int index = str.IndexOfAny(splitChars, startIndex);

                if (index == -1) {
                        parts.Add(str.Substring(startIndex));
                        return parts.ToArray();
                }

                string word = str.Substring(startIndex, index - startIndex);
                char nextChar = str.Substring(index, 1)(0);
                // Dashes and the likes should stick to the word occuring before it. Whitespace doesn't have to.
                if (char.IsWhiteSpace(nextChar)) {
                        parts.Add(word);
                        parts.Add(nextChar.ToString());
                } else {
                        parts.Add(word + nextChar);
                }

                startIndex = index + 1;
        }
        return functionReturnValue;

}
 

I'm not going to post what you need to do to draw the text in your list of strings, because the code I have doing that is not yet ready to be presented because of how I am drawing the strings in a pseudo-control multiline textbox.  However, you would do something like this pseudo code:

AddText(a_whole_lot_of_text)

For each textLine As String in _allTextLines
RenderWindow.Draw(textLine)
//offset Y axis here
Next //textLine
 

I'm 99% sure there is a way to get the width of the font so you can set the limit of the _wordWrapLength, but I have been too busy with other things to look into that.  If you happen across it feel free to share.  I hope this helps in some way ;)

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #4 on: May 20, 2016, 07:13:20 am »
Yikes, that's a lot to take in :o Thank you for the effort, I really appreciate it, but I don't think I'm to the point yet where I can take all this and adapt it >_> I'll tinker with it but it seems a bit daunting going from

sftd_calc_bounding_box(&textWidth, &textHeight, font, 15, 375, gameGlobals.sceneText);
sftd_draw_text_wrap(font, 30, 165, RGBA8(255, 255, 255, 255), 15, 375, gameGlobals.sceneText);
 

in my 3DS C code to all of that xD If I had a clear concise example of it working with SFML font sizes and put to use in an example, it would help a ton. I really learn fastest by reversing and toying with working examples made my competent coders.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #5 on: May 20, 2016, 08:08:38 am »
Recoil, I think your code does something slightly different from what is requested. It works in character units, instead of pixel units + using SFML functions to compute real text metrics. And I'm not sure if advanced word wrapping was requested, usually people don't care where the text wraps.

If I translate what I said:
Quote
Otherwise, short answer: compute the position of every character (text.findCharacterPos) and insert a line break when the position exceeds the width of the container.
... to some (untested) code:

const int containerWidth = ...;
for (auto i = 0; i < text.getString().size(); ++i)
{
    if (text.findCharacterPos(i).x > containerWidth)
    {
        auto str = text.getString();
        str.insert(i, "\n");
        text.setString(str);
    }
}
Laurent Gomila - SFML developer

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #6 on: May 23, 2016, 02:26:45 am »
I'm still having a bit of trouble, here is the code:

                                if (event.key.code == sf::Keyboard::F4)
                                {
                                        // Lets try this instead
                                        const int containerWidth = 20;
                                        for (auto i = 0u; i < textline1.getString().getSize(); ++i)
                                        {
                                                if (textline1.findCharacterPos(i).x > containerWidth)
                                                {
                                                        auto str = textline1.getString();
                                                        str.insert(i, "\n");
                                                        textline1.setString(str);
                                                        break;
                                                }
                                        }
                                }

I had to change size to getSize (I hope that's ok) and I changed text to textline1 (which is my sf::text name). The game "crashes" if I press F4 because I think it's not evaluating anything and getting stuck in the FOR loop? I threw break; in there to see if I could just get one line break maybe but it doesn't affect the text in any way. In my game loop I have it refresh the text string:

      textline1.setString(gameGlobals.sceneText);

As you can see the actual char string is a global char "std::string sceneText;". Should I be passing that to this function instead? VS doesn't seem to like me putting a global string in the place of the sf::string... I hope I made sense :P

Edit: Oh I also made i = 0u because the compiler was whining about signage so I put in the 'u'.
« Last Edit: May 23, 2016, 05:38:11 am by SonyUSA »

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #7 on: May 23, 2016, 10:03:15 am »
Also tried this way with no luck:

                                        // Let's wrap the text
                                                        sf::String str("blahblah");

                                                        textline1.setString(str);

                                                        float width = 30.f;
                                        // Wrap the text
                                                        for (auto i = 0u; i < str.getSize(); ++i)
                                                        {
                                                                auto position = textline1.findCharacterPos(i);
                                                                if (position.x > width)
                                                                {
                                                                        //insert a newline in string at i;
                                                                        str.insert(i, "\n");
                                                                        textline1.setString(str);
                                                                        break;
                                                                }
                                                        }

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #8 on: May 23, 2016, 10:11:22 am »
Can you give a more precise description of what happens? Have you tried to debug the code step by step to figure out what it does?
Laurent Gomila - SFML developer

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #9 on: May 24, 2016, 12:48:15 am »
The debugger kinda flips out about missing syswow files. The code does nothing at all, I don't see any changes happening to my text displayed on screen :/

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: Request: Weaponized Text Wrapping Example
« Reply #10 on: May 24, 2016, 01:24:00 pm »
i have created minimal example to test what Laurent suggested. it works fine here. i think you need to modify string gameGlobals.sceneText instead of textline1, like so

#include <SFML/Graphics.hpp>
#include <iostream>
#include <string>

int main()
{
        sf::RenderWindow window({200, 600}, "SFML works!");

    sf::Font font;
    if (!font.loadFromFile("arial.ttf"))
    {
        std::cout << "can't load font\n";
    }

    std::string str = "hello world";

    sf::Text text;//(str, font, 100);
    text.setFont(font);
    text.setCharacterSize(100);

        while (window.isOpen())
        {
                sf::Event event;
                while (window.pollEvent(event))
                {
                        if (event.type == sf::Event::Closed)
                                window.close();

            if(event.type == sf::Event::KeyPressed)
            {
                if (event.key.code == sf::Keyboard::F1)
                {
                    const auto containerWidth = text.getCharacterSize();
                    for (auto i = 0u; i < text.getString().getSize(); ++i)
                    {
                        if (text.findCharacterPos(i).x >= containerWidth)
                        {
                            str.insert(i, "\n");
                            text.setString(str);
                        }
                    }
                }
            }
                }
               
        text.setString(str);

                window.clear();
                window.draw(text);
                window.display();
        }
}
« Last Edit: May 24, 2016, 01:51:37 pm by MORTAL »

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #11 on: May 28, 2016, 03:46:23 am »
Can I ask what <string> and <iostream> are for?

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #12 on: May 28, 2016, 04:19:56 am »
OK, I have this:

                                if (event.key.code == sf::Keyboard::F4)
                                {
                                        // Lets try this instead
                                                const auto containerWidth = textline1.getCharacterSize();
                                                for (auto i = 0u; i < textline1.getString().getSize(); ++i)
                                                {
                                                        if (textline1.findCharacterPos(i).x >= containerWidth)
                                                        {
                                                                gameGlobals.sceneText.insert(i, " \n");
                                                                //textline1.setString(gameGlobals.sceneText);
                                                        }
                                                }
                                }

If I un-comment the //textline1.setString(...); the game crashes when I press F4, but I have that exact same line in my main game loop and it works fine. When I press F4, the text box clears itself and has nothing inside of it... Not sure what I'm doing wrong :/

Edit: Update! I see why it's "crashing". I put in a break; after the textline1.setString(...); and it moved the ENTIRE text line down one spacing... o.o So basically it was just inserting newlines until the end of eternity...

Also, a newline is actually farther down than I want it... any way to scootch/change that spacing? :3

---

New New Update!

I did this:

const auto containerWidth = 750;

and it wraps properly now, but it cuts off the words mid-letter, any way to stop that from happening?

---

New New New Update ;_;

I can't put this in the main loop because the game crashes
« Last Edit: May 28, 2016, 04:32:02 am by SonyUSA »

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: Request: Weaponized Text Wrapping Example
« Reply #13 on: May 28, 2016, 01:00:22 pm »
I can't put this in the main loop because the game crashes
you can, actually sfml provides two ways to handle the input, you already tried one way but there is another way to do it. from what you are trying to achieve is better to consider real time input

SonyUSA

  • Newbie
  • *
  • Posts: 14
    • View Profile
    • Email
Re: Request: Weaponized Text Wrapping Example
« Reply #14 on: May 28, 2016, 07:38:21 pm »
I can't put this in the main loop because the game crashes
you can, actually sfml provides two ways to handle the input, you already tried one way but there is another way to do it. from what you are trying to achieve is better to consider real time input

I think we had a miscommunication, it works fine when I have it as just a key press, but I want it to automatically wrap the text. If I move this code out of key-press and into the main loops so it evaluates the string on every loop, the game crashes.