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

Author Topic: What is a good way to draw a really long list of text?  (Read 1503 times)

0 Members and 1 Guest are viewing this topic.

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
What is a good way to draw a really long list of text?
« on: December 24, 2023, 01:30:49 pm »
I have a game, and in its starting menu I want to display all filenames from specific folders (so that the user can choose a filename to load and thus start the game). Something along the lines of:



Only Each folder/category would take it's own screen (there's a LOT of filenames)

I want each text object to be in its own "width bounds" so that it's evenly spaced.
Any filename that would exceed this width bound would be ignored.

And that is where I'm stuck. Basically, I don't know how can I know the local bounds that the string would have just from looking at it (and the intended font size). For example, the string "A" with font size 30 gives me width of 15 (when checking with getLocalBounds), but the string "P" with same font size gives width of 19. Same goes with height - it just seems unpredictable.

Thanks in advance, and if you have a better idea on who I should implement this, I would like to hear it  :)

« Last Edit: December 24, 2023, 01:33:46 pm by abcd1234567 »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: What is a good way to draw a really long list of text?
« Reply #1 on: December 24, 2023, 03:24:09 pm »
Every character can have a wildly separate width, that's true. It's mainly dependent on the font but should be considered in code for any font.

You "clip" a text to a specific size, you can set its string and see how wide it is without drawing it.
You can then remove a character one at a time until its width becomes within range.

That's the simplest method. However, you can use sf::Text's findCharacterPos method to see how far along the x axis each character is and then just cut the string to match the result.

One you've cut all the lines to how you want them, don't forget you can have a text with multiple lines by adding the newline character ('\n') between them.
You can even specify the distant between the lines to help aid with layout using setLineSpacing.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: What is a good way to draw a really long list of text?
« Reply #2 on: December 24, 2023, 05:46:09 pm »
Every character can have a wildly separate width, that's true. It's mainly dependent on the font but should be considered in code for any font.

You "clip" a text to a specific size, you can set its string and see how wide it is without drawing it.
You can then remove a character one at a time until its width becomes within range.

That's the simplest method. However, you can use sf::Text's findCharacterPos method to see how far along the x axis each character is and then just cut the string to match the result.

One you've cut all the lines to how you want them, don't forget you can have a text with multiple lines by adding the newline character ('\n') between them.
You can even specify the distant between the lines to help aid with layout using setLineSpacing.

Thanks a lot!
If you don't mind, I'd be happy if you could help me with another issue:
I've decided to move to simply a scrollable list of items that looks like this:


Basically it's just one screen that you can scroll to the bottom.
Now, I want to be able to color the menu item when the user hovers above it, and want to be able to register that item when the user has pressed on it.

Problem is: as we've seen, every Text object has a different height.
So when I register a click, I have no way of knowing if it was inside a Text object or not,
outside than to run through all of them with the Contains function.

Of course, if we want to implement hover functionality, which we check for in every iteration of the loop, then it's just too slow to check every single object every iteration; since the amount of Text objects can reach the hundreds in my program.

Is there a better way of doing it?




Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: What is a good way to draw a really long list of text?
« Reply #3 on: December 24, 2023, 06:16:23 pm »
As soon as you have hundreds of texts, you should be considering options to reduce this number.

If you have static texts (that don't change the content very often), consider pre-rendering to a render texture. You can still move them around but they will move together.

Aside from that, and more specifically to your scenario, the first thing I would suggest is to work out which texts are not on screen. You can use this for two significant improvements:
1) you can only check those ones for "collision" with visible user interface points (such as a mouse position)
2) you can draw only those ones. this can significantly decrease frame time (improve frame rate) if you have so many draw calls.

I suspect you are still using a single text object per line due to the fact that you seem to be centring them and you cannot do this to a multi-line block in SFML. (not yet, anyway, but I have proposed an implementation of this already ;) )

So, if you have a long list of things, draw them all (or in groups) to a render texture (maybe a few) and then move and draw just the render texture(s) once.

In addition, to check if your mouse is over a specific text, you could consider approximate rectangles. These would be around the text but not "pixel-exact". This might even be prefered for usability due to the ease of being able to select something without having to exactly on it. You could, for example, break up the list into rectangles that are about the height of the difference in y positions (basically this means that you would always be over one of the texts) and equal widths. Equal widths are often used in these sorts of scenarios. Consider thinking about it like a table of cells. Then, you can simply check if the mouse is inside the cell rather than deal with the text graphic itself.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: What is a good way to draw a really long list of text?
« Reply #4 on: December 24, 2023, 08:16:45 pm »
As soon as you have hundreds of texts, you should be considering options to reduce this number.

If you have static texts (that don't change the content very often), consider pre-rendering to a render texture. You can still move them around but they will move together.

Aside from that, and more specifically to your scenario, the first thing I would suggest is to work out which texts are not on screen. You can use this for two significant improvements:
1) you can only check those ones for "collision" with visible user interface points (such as a mouse position)
2) you can draw only those ones. this can significantly decrease frame time (improve frame rate) if you have so many draw calls.

I suspect you are still using a single text object per line due to the fact that you seem to be centring them and you cannot do this to a multi-line block in SFML. (not yet, anyway, but I have proposed an implementation of this already ;) )

So, if you have a long list of things, draw them all (or in groups) to a render texture (maybe a few) and then move and draw just the render texture(s) once.

In addition, to check if your mouse is over a specific text, you could consider approximate rectangles. These would be around the text but not "pixel-exact". This might even be prefered for usability due to the ease of being able to select something without having to exactly on it. You could, for example, break up the list into rectangles that are about the height of the difference in y positions (basically this means that you would always be over one of the texts) and equal widths. Equal widths are often used in these sorts of scenarios. Consider thinking about it like a table of cells. Then, you can simply check if the mouse is inside the cell rather than deal with the text graphic itself.

The menu doesn't change at all. The user clicks on an option and we move to a different menu. Basically, you're saying that drawing a RenderTexture (which to itself I drew all the texts) in my window is faster than drawing those texts directly to the window? Is that because I'm esentially just drawing a "picture" of those texts, and the objects themselves?

Thanks for the tips, I'll be sure to implement it :)

I don't mind the text being left-oriented, as long as it helps with performance ;) If I understand correctly, the multi line solution is similar to the RenderTexture, no? Draw just a single object instead all of them. Although since I want to have hover functionality, how can it work when I have just one Text object or one RenderTexture object? For that reason I also don't think I could draw only once

Oh I wish it was like a table of cells  :P But the widths would be different, since the filenames are of different lengths. You can see how that affects the hover functionality

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: What is a good way to draw a really long list of text?
« Reply #5 on: December 24, 2023, 08:49:42 pm »
Multi-line does allow fewer draw calls and that can help with performance.

My point about cells was that you can implement it as if it was a table of cells regardless of the width of each text/item. This means that you would consider it "hovering" an item if its within that "cell" rectangle even if it isn't over the actual text itself.
If you think of it as a table of cells that is wide enough to contain all of the text lengths and each of the smaller ones would just be inside a wider cell.

With the render texture idea, you would need to track which the rectangles when drawing them.
The process for render texture, by the way, would be to draw them all to the render texture once. Then, use that texture to draw a quad/rectangle that displays it (and all those texts). You can draw that simple rectangle every frame but you shouldn't need to update the texts in the render texture at all if they don't change (so don't draw them every frame).

With multi-line, again you could keep a separate track of rectangles. Or, you could divide its entire height by its "line spacing". That should give you a decent approximation of where lines are.
Or, you could also try this:
https://github.com/SFML/SFML/wiki/Source%3A-Get-Character-At-Coord
(it tells you which character in a text is at a specific position) but the longer the text, the longer it might take to search (if further in the text). It shouldn't be too significant a delay but you can try it and see if it's what you might be looking for.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: What is a good way to draw a really long list of text?
« Reply #6 on: December 24, 2023, 09:30:49 pm »
Multi-line does allow fewer draw calls and that can help with performance.

My point about cells was that you can implement it as if it was a table of cells regardless of the width of each text/item. This means that you would consider it "hovering" an item if its within that "cell" rectangle even if it isn't over the actual text itself.
If you think of it as a table of cells that is wide enough to contain all of the text lengths and each of the smaller ones would just be inside a wider cell.

With the render texture idea, you would need to track which the rectangles when drawing them.
The process for render texture, by the way, would be to draw them all to the render texture once. Then, use that texture to draw a quad/rectangle that displays it (and all those texts). You can draw that simple rectangle every frame but you shouldn't need to update the texts in the render texture at all if they don't change (so don't draw them every frame).

With multi-line, again you could keep a separate track of rectangles. Or, you could divide its entire height by its "line spacing". That should give you a decent approximation of where lines are.
Or, you could also try this:
https://github.com/SFML/SFML/wiki/Source%3A-Get-Character-At-Coord
(it tells you which character in a text is at a specific position) but the longer the text, the longer it might take to search (if further in the text). It shouldn't be too significant a delay but you can try it and see if it's what you might be looking for.

Amazing, thanks a lot!