love2d / love

LÖVE is an awesome 2D game framework for Lua.
https://love2d.org
Other
4.92k stars 392 forks source link

An easier way to draw unwrapped right-aligned or centered text #1331

Closed slime73 closed 2 years ago

slime73 commented 7 years ago

Original report by hahawoo (Bitbucket: hahawoo, GitHub: hahawoo).


I think it would be nice if there was an easier way to draw unwrapped right-aligned or centered text from a specified point.

To draw right-aligned text for example one might currently do something like this:

love.graphics.printf(s, x - 10000, y, 10000, 'right')  -- 10000 selected to be large enough not to wrap text

or this:

local offset = love.graphics.getFont():getWidth(s)
love.graphics.print(s, x - offset, y)
slime73 commented 7 years ago

Original comment by hahawoo (Bitbucket: hahawoo, GitHub: hahawoo).


Some possibilities...

love.graphics.print(text, x, y, align [default is "left"], r, sx, sy, ox, oy, kx, ky)

The align argument has a default value of "left".

love.graphics.print(text, x, y, align [optional], r, sx, sy, ox, oy, kx, ky)

The align argument is optional and uses "left" if left out (like how love.graphics.arc's arc type argument is optional and uses "pie" if left out).

I like that one because it functions the same as love.graphics.print if the align argument is left out.

love.graphics.printf(text, x, y, align, wraplimit, r, sx, sy, ox, oy, kx, ky)

Like normal love.graphics.printf, except if the wrap limit is 0 (or -1 maybe) then the text aligns from the x/y point rather than within the wrap limit.

love.graphics.printf(text, x, y, wraplimit [optional], align, r, sx, sy, ox, oy, kx, ky)

Like normal love.graphics.printf, except the wraplimit and align arguments are swapped and the wrap limit is optional. If the wrap limit is left out then the text aligns from the x/y point rather than within the wrap limit. If 3 arguments are given it functions like love.graphics.print.

slime73 commented 7 years ago

Original comment by Bart van Strien (Bitbucket: bartbes, GitHub: bartbes).


You might disagree, but the longer I look at this, the more I feel like non-wrapping text doesn't make sense at all...

slime73 commented 7 years ago

Original comment by hahawoo (Bitbucket: hahawoo, GitHub: hahawoo).


Interesting! I'd be curious to know your thoughts on that.

I guess I think it's nice to be able to draw text and know it's not going to wrap and overlap something else.

slime73 commented 4 years ago

Original comment by Kyle McLamb (Bitbucket: Alloyed, GitHub: Alloyed).


So here’s my hot take on text wrapping, or, why most text lines really should have a max width defined.

it’s pretty easy to take a bunch of string UI elements, and then line up your print calls to avoid them overlapping with anything else.

What happens when you want to change one of the strings? Well, now you have to go and look at that string ingame to make sure it hasn’t caused some new overlap.

What happens when you want to translate your game into another language? Now you need to go through and replace every string and put it through the same process as if you had just wanted to change the wording on a single string. There are some languages that will take up less space than english on average, and some that will take up more on average so it pays to design with some wiggle room in advance there.

What happens when the text in the string is procedural, or maybe includes text provided by the player? Well, now it’s really tricky to guarantee that nothing will overlap. the best you can do is define an area that you think is big enough, and log out a warning when you go out of that box.

The advantage of specifying width in all these situations is that now you’ve made the problem that used to be implicit explicit. You are saying “i think this element should take up about this amount of space” and it’s easy to check that assumption (in a unit test, even) by just running font:getWidth(). If you’re in a situation where it genuinely doesn’t matter, just provide a really big width number and anybody looking at the code can tell, oh, width doesn’t matter. For your actual game’s actual UI, though, I think you should default to providing a box, instead of not providing one by default and then getting burned by it later.

slime73 commented 4 years ago

Original comment by Gabe Stilez (Bitbucket: z0r8, ).


I don’t exactly get this issue… if your text is shorter than the wrap-limit, then it won’t wrap no matter what alignment you set in printf… is it that you want to be able to specify the coordinates of either the center of the text or the topright side of it instead of the topleft? Because i feel like that’s the gist of the issue… but that’s a technicality, and as you demonstrated yourself, it can be computed by already existing methods; if anything, a small lua-side helper function could take care of this… but that still won’t mean that your text wouldn’t wrap… you could specify a really large number as the wrap-limit in printf, and basically no wraplimit then, but combining that with specifying the “rectangle” around this box with any edge other than topleft will just anchor the text differently, either making it flow out on the left side, or with centered alignment, on both sides…

I’d also argue wrt translation, that it’s the editor’s job to book-keep line lengths and not printf’s; wrap-limit will take care of the horizontal max dimensions; the vertical may overflow, but there’s also a function for returning how many lines a text will take up; if that’s more than what a static textbox could fit, one could write an assertion function that could be fed all lines as a preliminary test that would spit out the lines that don’t fit; basically not in the scope of printf’s functionality. (I think i am agreeing with you though, @{557058:6e1ad8fe-7c8f-491b-82be-b713b756e1ff} )

slime73 commented 4 years ago

Original comment by hahawoo (Bitbucket: hahawoo, GitHub: hahawoo).


@{557058:b42a4858-84d7-49ff-85bf-cbadd6d70b7e} , I think you've got it, it was about wanting to specify the center/topright coordinates of the text instead of the topleft, it was an “ease of use” thing rather than really about wrapping (sorry if it was a bit unclear). This issue was about a shortcut way (as in, an easier way to do something that LOVE can already do) to draw right-aligned or centered text, because the easiest way of drawing left-aligned text is just love.graphics.print(s, x, y) and it felt like a bit of a difficulty jump that the easiest way to draw right-aligned text is love.graphics.printf(s, x - 10000, y, 10000, 'right').

But, as you say, love.graphics.print draws from the top-left, so maybe it's a slippery slope, because if there are easy ways to draw top-right-aligned and top-center-aligned text, why not bottom-right/center/left as well? And if the text drawing functions can do all this, why shouldn’t love.graphics.draw as well? So, I’m not 100% certain that this shortcut is a good idea.

slime73 commented 4 years ago

Original comment by Gabe Stilez (Bitbucket: z0r8, ).


Indeed, although i’m somewhat sure that something like love.graphics.printf(string, left, top, width, 'right') is already “right-aligned” in the sense that the text’s rightmost side will be touching the edge of the left+width pixel column; love.graphics.printf(string, left-width, top, width, 'right') , which was your example above, basically moves the “bounding box” left side a whole width, so that the right side will be where the left would be…

Even if you meant something like love.graphics.printf(s, right, top, -width, 'right') to define the topright with right alignment and the line wrap basically being negative to signal that the “bounding box” goes the other horizontal direction (to the left)… this could be something that would work (and wouldn’t incur adding another parameter to the function) but again, i don’t think it’s that important.

(I might implement something like this in my eventual gui though, but also adding top/bottom linecount limits and writing direction… things i think not the job of the underlying print function /off-topic)

Edit: As slime helpfully mentioned on discord, print[f] also has transform options that include offsets, so that could also be used.

slime73 commented 2 years ago

I think in this case I prefer the text printing APIs be consistent with the other drawing APIs in terms of coordinate conventions. When we add proper RTL text support I'm not sure if that will change things though.