Wrap String

Simple question, unfortunately complicated answer it seems.

All I’m trying to do is wrap a string to a given width by inserting new lines. I’ve never done this before, but the general idea is:
[ul][li]Break the string up into lines by [/li]
[li]In each line, go through and check the width, and then break it up into more [/li]
[li]Rinse and repeat[/li][/ul]
Here’s what I have so far:

[lua]function PANEL:WrapString( str )
local width = self:GetWide() - self.ModPadding * 6 - self.TextX;
local lines = string.Explode("
",str);
surface.SetFont(self.Font);
for k,line in pairs(lines) do
local line_w,line_h = surface.GetTextSize(line);
if line_w > width then
for i = 1,#line do
local str = string.sub(line,1,i);
local w,h = surface.GetTextSize(str);
if w > width then
lines[k] = string.sub(line,1,i - 1) … "
" … string.sub(line,i,#line);
break;
end
end
end
end
return table.concat(lines, “”);
end[/lua]

However, this only wraps the string once [in the sense that it works for one line but then doesn’t wrap any others]. Is there a better solution to this?

-snip- wrong again

I am kind of drunk right now so i cann only guess if this the thing you’re refering to; I used this code in a chatbox a friend made: (bear in mind that I wrote this 2 months ago, the beginning of my “lua career” so it is kind of messy)

[lua]
– line breaking
function getLines(titlestuff, ptext, isConsole)

    -- titlestuff example:     [Admin] nonSENSE
    -- ptext example:        "this is a message"
    -- if isConsole is true, titleStuff is ""
    
    -- the width of the title and name stuff
    if isConsole then
        titleWidth = 0
    else
        titleWidth = surface.GetTextSize(titlestuff .. ": ")
    end
    
    -- save this in a second var, we will need this later
    x = titleWidth 
            
    -- adding the text to the username and title stuff
    wholemsg = titlestuff .. ": " .. ptext
        
    -- save the total message width
    msgWidth = surface.GetTextSize(wholemsg)
        
    -- if the msgWidth is higher than 400, consider a line break    
    if msgWidth > 400 then
    
        -- msg is the text the user entered
        local msg = ptext
        
        -- we split this into words now (delimiter ' ')
        local msgParts = string.Explode(" ", msg)
        
        --PrintTable(msgParts)
        
        -- nrLines is the number of lines we will display
        local nrLines = 1
        
        -- total number of words
        local j = 1
        
        -- wpl is the number of words that will be displayed in this line
        local wpl = 1            
        
        -- i is a string of words
        local i = ""
        
        -- lengthI is the length of the string of words (at start equal to length of i)
        -- for line 1, this shouldnt be longer than 410-x, for other lines not longer than 410
        local lengthI = surface.GetTextSize(i)
        
        -- table that contains words for each line
        lineWords = {}
        lineWords[1] = { }
        
        
        -- go through all words; couldve used a 'for loop' too, but we this works just as well
        while (j <= #msgParts) do
            
            -- if the word fits into the line, we put it into the table for that line
            lineWords[nrLines][wpl] = msgParts[j]
            
            -- we increase the temp string so we can calculate a new width
            i = i .. msgParts[j] .. " "
            
            -- the number of words per line increases too
            wpl = wpl + 1
            
            -- and ofc the total number of words so we can continue
            j = j + 1
            
            -- lets calc the length with the next word attached
            if j <= #msgParts then
                lengthI = surface.GetTextSize(i .. msgParts[j] )
            else
                lengthI = surface.GetTextSize(i)
            end
            
            -- lets decide if the last added word exceeds our capacity
            if (lengthI > (410-x)) then
            
                -- if so, increase number of lines, and reset the temp string
                nrLines = nrLines + 1
                i = msgParts[j]
                
                -- we need to initialize the new line table
                lineWords[nrLines] = {}
                
                -- we reset x to 0 (which equaled to the length of the name and titles),
                -- as we start a new line and it wont contain the name
                x = 0
                
                -- words per line also reset to 1 
                wpl = 1
            end
        end
        return lineWords    
    
    end

end

[/lua]

This returns a table with the following structure:

[1] (line nr 1) => {‘This’, ‘is’, ‘a’, ‘test’, ‘okay’}
[2] => {‘so’, ‘lets’, ‘see’, ‘if’, ‘this’, ‘works’}

To display, just loop through the words in each table index and add spaces between elements.

[lua]function wrap(str, limit, indent, indent1)
indent = indent or “”
indent1 = indent1 or indent
limit = limit or 72
local here = 1-#indent1
return indent1…str:gsub("(%s+)()(%S+)()",
function(sp, st, word, fi)
if fi-here > limit then
here = st - #indent
return "
"…indent…word
end
end)
end[/lua]

I use this one myself, it’s from lua-users.org

Maybe not exactly what you want, but it works for the chatbox we use.