[System] WordWrap

Scripts

13 years
edited 8 years
http://en.wikipedia.org/wiki/Word_wrapping

This is not as featured/precise as text editor word wrapping, but it does the job pretty well.

The description is in the documentation.

Code (jass) Select
library WordWrap /* v2.1.0.0
*********************************************************************
*
*  Simulates word wrapping for multiboards and texttags.
*  Word wrapping is where, if a text is too long, it will
*  cut to the next line rather than cutting off the text.
*
*  How it works:
*
*      - Specify a source string and a size margin. The margin
*        determines what the size of the line can be before it
*        splits the string into fragments. The value is more or
*        less arbritrary, so just experiment with values.
*      - Explodes the string by spaces into words.
*      - Checks the size of each word.
*      - Once the size margin is reached, it will cut the string.
*      - The first line is stored in the array under the index 0.
*      - Rinse and repeat until it fragments the entire string.
*      - You can retrieve the count to know the max index reached.
*
*********************************************************************
*
*   */uses/*
*   
*       */ StringSize /*  https://wc3modding.info/5014/snippet-stringsize/
*
*    This library does not require Ascii, but StringSize requires
*    the Ascii library:
*
*
*********************************************************************
*
*  function WordWrapString takes string source, real sizeMargin, boolean preserveColors returns nothing
*
*      - Breaks the string into several fragments.
*      - Size margin determines the size the string can reach
*        before it fragments the string and breaks to the next
*        line.
*      - Preserve colors is a boolean to check if a color
*        code is being broken during the word wrap. If you
*        are not using color codes (ex: |cffffcc00text|r)
*        then you should set this to false to save performance.
*
*  function GetWrappedStringFragment takes integer index returns string
*
*      - Retrieves the fragmented strings. The indices start at 0
*        and will go up to the "count" index - 1. (see below) The last
*        line will be:
*
*          GetWrappedStringFragment(GetWrappedStringCount() - 1)
*
*  function GetWrappedStringCount takes nothing returns integer
*
*      - Returns the total number of fragments. Indices start at 0,
*        and the final line will be this number minus 1.
*
*********************************************************************
*
*  struct WordWrap
*
*      static method getLine takes integer index returns string
*
*      static method getCount takes nothing returns integer
*
*      static method create takes string source, real sizeMargin, boolean preserveColors returns thistype
*
*********************************************************************
*
*  Credits
*
*      - cleeezzz & tooltiperror for the idea.
*
*  Bugs
*
*      - If there is a single word that is longer than the
*        "sizeMargin" input, then this system will fail.
*        It requires extra checks. I may update this later
*        to fix that problem, but for now this should be fine.
*        PM or message me if you have any problems.
*
*********************************************************************/

    struct WordWrap extends array
        private static string array line
        private static integer count = 0

        static method getLine takes integer index returns string
            return line[index]
        endmethod
       
        static method getCount takes nothing returns integer
            return count
        endmethod
       
        private static method preserveColor takes nothing returns nothing
            local integer i = 0
            local integer k = 0
            local integer l = 0
            local string temp = ""
            local string hex = ""
            local boolean carryOn = false
            loop
                exitwhen i == count
                set carryOn = false
                set hex = ""
                set k = 0
                set l = StringLength(line[i])
                loop
                    exitwhen k == l
                    set temp = SubString(line[i], k, k+1)
                    if temp == "|" then
                        set temp = SubString(line[i], k+1, k+2)
                        if temp == "c" then
                            set hex = SubString(line[i], k, k+10)
                            set carryOn = true
                        elseif temp == "r" then
                            set carryOn = false
                        endif
                    endif
                    set k = k + 1
                endloop
                set i = i + 1
                if carryOn then
                    set line[i] = hex + line[i]
                endif
            endloop
        endmethod
       
        static method create takes string source, real size, boolean preserveColors returns thistype
            local integer l = StringLength(source)
            local integer i = 0
            local integer k = 0
            local string s
           
            local string word
            local real result = 0
            local real ssize = 0
           
            loop
                exitwhen count == 0
                set count = count - 1
                set line[count] = ""
            endloop
            loop
                exitwhen i == l
                set s = SubString(source, i, i+1)
                if s == " " or i == (l-1) then
                    set word = SubString(source, k, i) + s
                    set ssize = MeasureString(word)
                    set result = result + ssize
                    if result <= size then
                        set line[count] = line[count] + word
                    else
                        set count = count + 1
                        set line[count] = word
                        set result = ssize
                    endif
                    set k = i + 1
                endif
                set i = i + 1
            endloop
            set count = count + 1
            if preserveColors then
                call ForForce(bj_FORCE_PLAYER[0], function thistype.preserveColor)
            endif
            return 0
        endmethod
    endstruct

    function WordWrapString takes string source, real sizeMargin, boolean preserveColors returns nothing
        call WordWrap.create(source, sizeMargin, preserveColors)
    endfunction

    function GetWrappedStringFragment takes integer i returns string
        return WordWrap.getLine(i)
    endfunction

    function GetWrappedStringCount takes nothing returns integer
        return WordWrap.getCount()
    endfunction

endlibrary


Here is more information and pictures. First, take a look at this sample code:
Code (jass) Select
scope Testing initializer Init
   
    private function esc takes nothing returns nothing
        local string text = "Hello, my name is Joe. I work in a button factory. One day, my boss said to me, \"Hiya Joe! Are you busy?\" "+/*
        */" I said, \"No\"."    //our original text
        local integer i = 0
        call WordWrapString(text, 450, true) //We will break it into lines, 450 maximum pixels per line
        loop
            exitwhen i == GetWrappedStringCount()    //loop through each line up to the max line count.
            call BJDebugMsg(GetWrappedStringFragment(i))  //This is how you read a line
            set i = i + 1
        endloop
    endfunction
   
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(t, function esc)
    endfunction
endscope


This is that sample above, with a width of 150:

This is that sample above, but with a width of 450:


For color preservation, it is basically an option to preserve color codes when skipping to the next line. If it is set to "false", then it will not preserve the colors, boosting performance, but ruining how it looks. If you have something like this:
|cff00FF00Hello World! It is a
beautiful day!|r

It should color the entire text. However, without color preservation, it will only color the top line. It should be fine to disable this if you are only color coding individual words, or if you aren't using color codes, but otherwise it should be enabled.

Here is an example with color preservation:


Without it, it would look like this:


Enjoy, all feedback is welcome.
13 years
This is great! Really need a good system for making text on my SRPG, thanks for this one purge! Gonna try it when I got home.
13 years
Thanks.

I might end up making a multiboard/text tag system for this. It seems a bit much to have a system for everything, but it is really frustrating to deal with. If I made something to auto-extend multiboards or w/e or to create new text tags for new lines, then that thing would no longer be a problem. :)

If only there was a way to get resolution. I'm still looking to see if there is any hope of retrieving that.
13 years
I think the best way is to ask the user to say which resolution he is using through a dialog box or whatever else.
And because we don't always know which one we are using you could chek for each resolution what is the lowest texttag size available, and then display all these sizes with the text equal to the resolution, then the user just have to choice the biggest one he can see, but meh maybe it's already too much for the average battle.net player :p
13 years
Updated with optimizations and fixed a bug that came with my 2.0 update.
13 years
Definitely a very nice piece of code, and it does the job pretty well. Approved of course!!