Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

how would i split a string that uses bbcode and render it as the right bbcode?

edited November 11 in Questions Posts: 102

i want to properly render strings with bbcode and some animations. i.e. this string should be rendered as a red Hello World! There's no pressing need for this, just wanted to try it this way. if i really cant figure it out I'll go back to the function that takes alternating strings and colors, but imo bbcode would be a lot easier to read and use since im already used to it, plus it would be a lot more flexible than taking and checking the type of multiple function arguments

examplestring=“[color=#ff0032]Hello World![/color]”

Actual code below. Current way to displaying different colors is to set the colors table, similar to setting mesh.colors

-- texttest

-- Use this function to perform your initial setup
function setup()
    test0 = coloredtext(teststring,100,HEIGHT/2,10,10,WIDTH/2)
    test2=coloredtext('a short string', 300, 200, 10,16,200)
    test2.nextshake=0.0 --causes it to bug out
    for i in ipairs(test2.colors) do
        test2.colors[i]=color(10*i,255-10*i,0)
    end
end

coloredtext = class()
function coloredtext:init(s,x,y,w,h,wrapwidth)
    self.colors = {}
    self.positions = {}
    self.strings = {}
    self.timer = 0
    self.nextshake = 1
    self.shakedirection = 1
    self.shakeindex = 1 --only used in coloredtext:update2()
    local i = 1
    local count = 0
    local wrapx = x + wrapwidth
    local newx, newy = x, y
    for c in s:gmatch"." do
        self.strings[i] = c
        self.colors[i] = color(255)
        newx = x + w * count
        newy = newx > wrapx and newy - h or newy
        count = newx > wrapx and 0 or count + 1
        self.positions[i] = vec2(newx, newy)
        i = i+1
    end

end

function coloredtext:draw()
    for i,v in ipairs(self.positions) do
        fill(self.colors[i])
        text(self.strings[i], v.x, v.y)
    end
end

function coloredtext:update()
    self.timer = self.timer + DeltaTime
    if self.timer > self.nextshake then
        local char = self.positions[math.random(#self.positions)]
        local t1 = tween(0.025, char, {char.x-2, char.y+2})
        local t2 = tween(0.025, char, {char.x, char.y})
        tween.sequence(t1, t2)
        self.timer = 0
    end
end

--warning: setting self.nextshake very low results in repeated tweens being applied to the same character
--causing it to not properly reset in time for the next tween
function coloredtext:update2()
    self.timer=self.timer+DeltaTime
    if self.timer > self.nextshake then
        local char = self.positions[self.shakeindex]
        local t1 = tween(0.025, char, {char.x-2, char.y+2})
        local t2 = tween(0.025, char, {char.x, char.y})
        tween.sequence(t1, t2)
        self.timer = 0
        self.shakeindex=self.shakeindex+self.shakedirection
        if self.shakeindex >= #self.positions then 
            self.shakedirection=-1
        elseif self.shakeindex <= 1 then
            self.shakedirection=1
        end
    end
end
-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)


    -- Do your drawing here
    test0:update() --this shakes random characters
    test0:draw()

    test2:update2() --shakes in order by index
    test2:draw()
end


teststring = 
"this is a very long string this is a very long stringthis is a very long string this is a very long stringthis is a very long string this is a very long stringthis is a very long string this is a very long string"

Comments

  • dave1707dave1707 Mod
    Posts: 6,325

    @xThomas Here’s something I wrote long ago that’s kind of similar. It could easily be altered to show text in different sizes and different colors using color values instead of a color table..

    displayMode(FULLSCREEN)
    
    function setup()
        textMode(CORNER)
        str="¥aThis ¥cis ¥ddiff¥aerent ¥ccolored ¥dtext. You ¥bcan setup ¥aas many different ¥dcolors as you want. You can pick ¥athe screen ¥dx,y position and ¥ethe width to print. The colors are ¥ajust a matter ¥dof creating ¥ba ¥dlarger table of ¥ediff¥berent colors ¥dand adding ¥fthe control ¥bcharacters."
        col={a=color(255,0,0),
             b=color(0,255,0),
             c=color(0,0,255),
             d=color(255,255,0),
             e=color(0, 238, 255, 255),
             f=color(255, 0, 223, 255),
            }
    end
    
    function draw()
        background(40, 40, 50)
        showText(100,200,400)
        showText(200,600,150)
        showText(650,650,60)
    end
    
    function showText(x,y,size)
        local count, xoffset, yoffset=0,0,0
        for z=1,str:len() do
            local b1,b2=str:byte(z,z+1)
            if count==0 then
                local ch=str:sub(z,z)
                if b1==194 and b2==165 then
                    fill(col[str:sub(z+2,z+2)])
                    count=2
                else
                    text(ch,x+xoffset,y-yoffset)
                    local w,h=textSize(ch)
                    xoffset = xoffset + w
                    if xoffset>size then
                        xoffset=0
                        yoffset = yoffset + h
                    end
                    count=0
                end
            else
                count=count-1
            end
        end
    end
    
  • dave1707dave1707 Mod
    Posts: 6,325

    @xThomas Here’s an example that handles the [color=xxxxxx] syntax that you show above. This can be modified to do the other [bracket command] parameters that would do text size and font differences on the same text line. I didn’t see any point in adding that code since I would never use it. You can add the updates if you really want them.

    function setup()
        str="[color=#0000ff]Hello World![/color][color=#ff00ff]Goodbye World![/color]  [color=#00ff00]Mary had a little lamb[/color][color=#ff3456]it's fleece was white as snow.[/color][color=#0000ff]And everywhere that Mary went[/color][color=#ff00ff]the lamb was sure to go.[/color][color=#00ff00]He followed her to school one day[/color][color=#ff3456]which was against the rule.[/color]"
    end
    
    function draw()
        background(0)
        parse(WIDTH/2,HEIGHT-100)
    end
    
    function parse(x,y)
        offset=0
        for _,a,_,b,_,_,_ in string.gmatch(str,"(%[)(.-)(%])(.-)(%[/)(.-)(%])") do
            str1=b
            offset=offset+40
            for _,r,g,b in string.gmatch(a,"(color=#)(%w%w)(%w%w)(%w%w)") do
                fill(tonumber("0x"..r),tonumber("0x"..g),tonumber("0x"..b))       
            end
            text(str1,x,y-offset)
        end
    end
    
  • dave1707dave1707 Mod
    Posts: 6,325

    @xThomas I didn’t have anything better to do, so here’s another version that does the following commands, color, size, bold, italic, bold and italic. You can add more if you want.

    function setup()
        str="[color=#0000ff]Blue Regular[/color][size=32][color=#ff0000]Red size 32[/color][/size][color=#00ff00][b][i]Green Bold Italic[/i][/b][/color][color=#00ffff][size=45]Cyan size 45[/size][/color][color=#ffff00][i]Yellow Italic[/i][/color]"
    
        rFont="Georgia" -- regular font
        iFont="Georgia-Italic"
        bFont="Georgia-Bold"
        biFont="Georgia-BoldItalic"
    
        fSize=17    -- font size
        fontSize(fSize)
    end
    
    function draw()
        background(223, 175, 181, 255)
        parse(200,600)
    end
    
    function parse(x,y)
        local offset=0
        local str1,str2="",""
        for z=1,#str do
            if not bold and not italic then
                font(rFont)
            end
            c=string.sub(str,z,z)
            if c=="[" then
                st=true
                fnd=false
                if str1~="" then
                    offset=offset+50
                    text(str1,x,y-offset)
                    str1=""
                end
                str2=str2..c
            elseif c=="]" then
                st=false
                str2=str2..c
                if str2~="" then
                    set(str2)
                    str2=""
                end
                fnd=true
            elseif st then
                str2=str2..c
            elseif fnd then   
                str1=str1..c
            end
        end
    end
    
    function set(st)
        s,e=string.find(st,"%[color=#") -- color
        if s~=nil then
            r=tonumber("0x"..string.sub(st,s+8,s+9))
            g=tonumber("0x"..string.sub(st,s+10,s+11))
            b=tonumber("0x"..string.sub(st,s+12,s+13))
            fill(r,g,b)
        end
    
        s,e=string.find(st,"%[b")   -- start bold
        if s~=nil then
            bold=true
            font(bFont)
        end
        s,e=string.find(st,"%[/b")  -- end bold
        if s~=nil then
            bold=false
        end
    
        s,e=string.find(st,"%[i")   -- start italic
        if s~=nil then
            italic=true
            font(iFont)
        end
        s,e=string.find(st,"%[/i")  -- end italic
        if s~=nil then
            italic=false
        end
    
        if bold and italic then
            font(biFont)  -- bold and italic
        end
    
        s,e=string.find(st,"%[size=")  -- start size
        if s~=nil then
            sz=string.sub(st,s+6,s+7)
            fontSize(sz)
        end
        s,e=string.find(st,"%[/size")  -- end size
        if s~=nil then
            fontSize(fSize)
        end
    end
    
  • Posts: 102

    Thank you. I don't have a good grasp of patterns, your code helps :)

  • dave1707dave1707 Mod
    Posts: 6,325

    @xThomas It turns out that not using string.gmatch was easier to handle imbedded commands. So you don’t really need to worry about patterns.

  • edited November 15 Posts: 102

    Hmm @dave1707

    Well, my internet is out, so typing this from the library (60 minute session!). Anyway. Been trying your example, and making some modifications. Not exactly sure what you mean by that not using patterns is easier, unless you're referring to all the extra captures you were doing (i.e. wwhen you did for ,,a,, because of all the captures with the parentheses, you could just leave out the parantheses and It would be discarded. Ahh, while that works with gmatch, with gsub it is trickier - I still haven't figured out the nuance of it (gsub is so tricky!)

    i.e. i can't figure out how to combine my two uses of gsub in the code below, so that it takes out the color code but leaves the text intact

    please excuse the rushed post, I am almost out of library time :)

    ... but anyway. Some code I wrote just now (don't have my ipad with me). See the difference between how i parsed the colors and you did, im not sure if theres any actual speed difference but its easier to read..

    --bbcode
    
    teststring = "[color=#ffbc60]Hello World![/color]"
    pattern =   {
    
         color ="%[color=#(%x%x)(%x%x)(%x%x)%]"
    
                }
    
    for r,g,b in string.gmatch(teststring, pattern.color) do
        r,g,b = tonumber("0x"..r), tonumber("0x"..g), tonumber("0x"..b)
        print (r,g,b)
    end
    
    local newstring = string.gsub(teststring, "(%[color=#%x*%])", "")
    newstring = string.gsub(newstring, "%[%/color%]", "")
    for char in string.gmatch(newstring, ".") do
        print(char)
    end
    

    The above code first grabs the color from the text, then it parses out the color code leaving you with just a normal string. I did the whole 'char in string.gmatch' thing because I want to still use this with individual animations. I am considering some kind of caching for the color table at least though - when indexes 1 through 100 are all the same color, I don't want to have to call 100 table.lookups when vfor example each character has its own place in the strings table, the positions table, the colors table, the rotations table, etc. I'm not actually sure how to implement

    (i.e. what I have now is more like (to paraphrase my code on the ipad)

    for i,v in ipairs(positions) do
      --fill(colors[i] or fill())
      if colors[i] then fill(colors[i]) end
      text(strings[i], v.x, v.y)
    end
    

    )

    again sorry for the long post, but not much time left :blush:

  • dave1707dave1707 Mod
    Posts: 6,325

    @xThomas When I said not using string.gmatch was easier, I was referring to my last program. In that one I’m just parsing thru the string using string.sub . Using string.sub, I can identify multiple bb commands as I come across them. Using string.gmatch would have been a lot harder trying to set up all the different patterns to seperate multiple bb commands from the text. In your example, you're identifying the color bb command. How would you set up a pattern to handle the string below that has the bb italic command inside the bold command inside the color command. That’s why I gave up using string.gmatch and switched to string.sub .

    teststring = "[color=#ffbc60][b][i]Hello World![/i][/b][/color]"
    
  • Posts: 102

    Ahh, I see what you mean. I'll think about how to solve this one when I go home

Sign In or Register to comment.