• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] [Snippet] InfoBox - Multiboard wrapper

Level 23
Joined
Apr 16, 2012
Messages
4,041
Author's notes: I know the API is vast, but you dont need to care for most of it, the only really integeresting API is InfoBoxCell, InfoBoxRow and InfoBox itself. The other structs are helper for syntax such as row[0].color.red = 0xFF. Listing of full capabilities can be found inside function FullListing inside Examples.

Code:
JASS:
library InfoBox requires Table
/*
    By: edo494
    Version: 2.1
    Requires:
        Table - [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
    
    This resource is a wrapper around multiboard with convinient Object oriented API
    that automates a lot of work for you.
    
    This system allows you to work with individual rows as well as cells
    of the multiboard in object-orientated way via rich API.
    You can create colors, styles(whether to show or hide icon or text),
    create indvidual cells with properties(color, style, text, icon)
    as well as rows, and even configure the multiboard itself.
    
    API:
        See below example
    
    Limitations: - If you change width after adding first row,
                   bad stuff will happen This is direct limitation
                   of multiboard, this system could handle it by destroying
                   the multiboard and creating new one but it doesnt currently.
                 - You can not access or create columns(I attempted to make this,
                   but the code got hilariously complicated and big)
    
    Expected use case:
        Single player maps, like mine.
        (This system could be updated probably without change to support 1 InfoBox
         per player. If this is desired, I can update it later)
                 
*/
    struct InfoBoxColor
        private integer redPriv
        private integer greenPriv
        private integer bluePriv
        private integer alphaPriv
        
        readonly boolean redSet
        readonly boolean greenSet
        readonly boolean blueSet
        readonly boolean alphaSet
        
        static method create takes nothing returns thistype
            local thistype this = allocate()
            set redPriv = 0
            set greenPriv = 0
            set bluePriv = 0
            set alphaPriv = 0
            
            set redSet = false
            set greenSet = false
            set blueSet = false
            set alphaSet = false
            
            return this
        endmethod
        
        static method createEx takes integer r, integer g, integer b, integer a returns thistype
            local thistype this = create()
            set redPriv = r
            set greenPriv = g
            set bluePriv = b
            set alphaPriv = a
            
            set redSet = true
            set greenSet = true
            set blueSet = true
            set alphaSet = true
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            call deallocate()
        endmethod
        
        method setColors takes integer Red, integer Green, integer Blue, integer Alpha returns nothing
            set redPriv = Red
            set greenPriv = Green
            set bluePriv = Blue
            set alphaPriv = Alpha
            
            set redSet = true
            set greenSet = true
            set blueSet = true
            set alphaSet = true
        endmethod
        
        method operator red takes nothing returns integer
            return redPriv
        endmethod
        
        method operator red= takes integer r returns nothing
            set redSet = true
            set redPriv = r
        endmethod
        
        method operator green takes nothing returns integer
            return greenPriv
        endmethod
        
        method operator green= takes integer g returns nothing
            set greenSet = true
            set greenPriv = g
        endmethod
        
        method operator blue takes nothing returns integer
            return bluePriv
        endmethod
        
        method operator blue= takes integer b returns nothing
            set blueSet = true
            set blue = b
        endmethod
        
        method operator alpha takes nothing returns integer
            return alphaPriv
        endmethod
        
        method operator alpha= takes integer a returns nothing
            set alphaSet = true
            set alphaPriv = a
        endmethod
    endstruct

    struct InfoBoxStyle
        readonly boolean iconSet
        readonly boolean textSet
        
        private boolean iconPriv
        private boolean textPriv
        
        static method create takes nothing returns thistype
            local thistype this = allocate()
            set iconPriv = false
            set textPriv = false
            
            set iconSet = false
            set textSet = false
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            call deallocate()
        endmethod
        
        method show takes boolean text, boolean icon returns nothing
            set iconPriv = icon
            set textPriv = text
            
            set iconSet = true
            set textSet = true
        endmethod
        
        method operator icon takes nothing returns boolean
            return iconPriv
        endmethod
        
        method operator icon= takes boolean b returns nothing
            set iconSet = true
            set iconPriv = b
        endmethod
        
        method operator text takes nothing returns boolean
            return textPriv
        endmethod
        
        method operator text= takes boolean b returns nothing
            set textSet = true
            set textPriv = b
        endmethod
    endstruct

    struct InfoBoxCell
        InfoBoxStyle style
        InfoBoxColor color
        
        string text
        string icon
        
        static method create takes nothing returns thistype
            local thistype this = allocate()
            set style = InfoBoxStyle.create()
            set color = InfoBoxColor.create()
			set text = ""
			set icon = ""
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            call deallocate()
            call style.destroy()
            call color.destroy()
        endmethod
        
        method colors takes integer red, integer green, integer blue, integer alpha returns nothing
            call color.setColors(red, green, blue, alpha)
        endmethod
        
        method styles takes boolean text, boolean icon, integer red,/*
                            */integer blue, integer green, integer alpha returns nothing
            call style.show(icon, text)
            call colors(red, blue, green, alpha)
        endmethod
        
        method stylesEx takes InfoBoxStyle style, InfoBoxColor color returns nothing
            call style.show(style.text, style.icon)
            call color.setColors(color.red, color.green, color.blue, color.alpha)
        endmethod
    endstruct
	
    struct InfoBoxRow
        private Table cellList
		
		InfoBoxColor color
        InfoBoxStyle style
        
        string text
        string icon
        
        private method removeCells takes nothing returns nothing
            local integer size = cellList[-1]
            local integer looper = 0
            loop
                exitwhen looper >= size
                call InfoBoxCell(cellList[looper]).destroy()
                set looper = looper + 1
            endloop
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = allocate()
            set cellList = Table.create()
            set style = InfoBoxStyle.create()
            set color = InfoBoxColor.create()
            set text = ""
            set icon = ""
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            call removeCells()
            call deallocate()
            call cellList.destroy()
			call style.destroy()
            call color.destroy()
        endmethod
        
        method colors takes integer red, integer green, integer blue, integer alpha returns nothing
            call color.setColors(red, green, blue, alpha)
        endmethod
        
        method styles takes boolean text, boolean icon, integer red,/*
                            */integer blue, integer green, integer alpha returns nothing
            call style.show(text, icon)
            call colors(red, blue, green, alpha)
        endmethod
        
        method stylesEx takes InfoBoxStyle style, InfoBoxColor color returns nothing
            call style.show(style.text, style.icon)
            call color.setColors(color.red, color.green, color.blue, color.alpha)
        endmethod
		
		method addCell takes InfoBoxCell cell returns thistype
			local integer newSize = cellList[-1]
			set cellList[newSize] = cell
			set cellList[-1] = newSize + 1
			return this
		endmethod
        
        method addCells takes integer howMany returns thistype
            local integer i = 1
            loop
                exitwhen i > howMany
                call addCell(InfoBoxCell.create())
                set i = i + 1
            endloop
            
            return this
        endmethod
		
		method size takes nothing returns integer
			return cellList[-1]
		endmethod
		
        static method createEx takes integer emptyCells returns thistype
            local thistype this = create()
            if emptyCells < 0 then
                return this
            endif
            call addCells(emptyCells)
            
            return this
        endmethod
        
        static method createExStyled takes integer howMany, InfoBoxStyle toApply,/*
                                            */InfoBoxColor toApplyC returns thistype
            local thistype this = create()
            local InfoBoxCell cell = 0
            if howMany < 0 then
                return this
            endif
            
            call addCells(howMany)
            call styles(toApply.text, toApply.icon, toApplyC.red,/*
                            */toApplyC.green, toApplyC.blue, toApplyC.alpha)
            
            return this
        endmethod
        
        method operator [] takes integer offset returns InfoBoxCell
            if offset < 0 or offset > (cellList[-1] - 1) then
                static if DEBUG_MODE then
                    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, /*
                            */"call to InfoBoxRow.operator[" + I2S(offset) + "]: invalid offset")
                endif
                return 0
            endif
            return cellList[offset]
        endmethod
    endstruct
    
    private module widthm
        private static boolean init = false
        private static method onInit takes nothing returns nothing
            if init then
                return
            endif
            set init = true
            set data = Table.create()
        endmethod
    endmodule
    
    struct InfoBoxWidth extends array
        private static Table data
        implement widthm
        
        static method size takes nothing returns integer
            return data[-1]
        endmethod
        
        method operator [] takes integer colId returns real
            if colId < 0 or colId >= size() then
                static if DEBUG_MODE then
                    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10,/*
                                                    */"InfoBoxWidth.operator[" + I2S(colId)/*
                                                    */+ "]: index out of bounds")
                endif
                return 0.
            endif
            return data.real[colId]
        endmethod
        
        method operator []= takes integer colId, real width returns nothing
            local integer size = data[-1]
            
            if colId < 0 then
                static if DEBUG_MODE then
                    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10,/*
                                                    */"InfoBoxWidth.operator[" + I2S(colId)/*
                                                    */+ "] = " + R2S(width) + ": index out of bounds")
                endif
                return
            endif
            
            set data.real[colId] = width
            if colId >= size then
                set data[-1] = colId + 1
            endif
        endmethod
        
        static method operator [] takes integer colId returns real
            return thistype(1)[colId]
        endmethod
        
        static method operator []= takes integer colId, real width returns nothing
            set thistype(1)[colId] = width
        endmethod
    endstruct

    private module m
        static boolean init = false
        private static method creation takes nothing returns nothing
            call DestroyTimer(GetExpiredTimer())
            set board = CreateMultiboard()
            call silentCreate()
        endmethod
        
        private static method onInit takes nothing returns nothing
            if init then
                return
            endif
            
            call TimerStart(CreateTimer(), 0., false, function thistype.creation)
            set rows = Table.create()
            set tColor = InfoBoxColor.createEx(0xFF, 0xFF, 0xFF, 0xFF)
            
            //magic, but InfoBoxWidth is not instancable anyways
            set width = 1
        endmethod
    endmodule
    
    struct InfoBox extends array
        private static multiboard board = null
        private static Table rows
        
        private static string titleText = ""
        private static InfoBoxColor tColor
        
        private static boolean doMinimize = true
        private static boolean doDisplay = true
        
        static InfoBoxWidth width
        
        static method operator minimize= takes boolean do returns nothing
            set doMinimize = do
            
            if board != null then
                call MultiboardMinimize(board, true)
                call MultiboardMinimize(board, doMinimize)
            endif
        endmethod
        
        static method operator minimize takes nothing returns boolean
            return doMinimize
        endmethod
        
        private static method getBooleanIf takes boolean dIf, boolean a, boolean b returns boolean
            if dIf then
                return a
            endif
            return b
        endmethod
        
        private static method getStringIf takes boolean dIf, string a, string b returns string
            if dIf then
                return a
            endif
            return b
        endmethod
        
        private static method getIntegerIf takes boolean dIf, integer a, integer b returns integer
            if dIf then
                return a
            endif
            return b
        endmethod
        
        private static method checkStr takes string s returns boolean
            return s != "" and s != null
        endmethod
        
		private static method applyCell takes multiboarditem mItem, InfoBoxCell cell,/*
                                                */integer colId, InfoBoxRow fromRow returns nothing
            local boolean showText = getBooleanIf(cell.style.textSet,/*
                                                */cell.style.text, fromRow.style.text)
            local boolean showIcon = getBooleanIf(cell.style.iconSet,/*
                                                */cell.style.icon, fromRow.style.icon)
            local string text = getStringIf(checkStr(cell.text), cell.text, fromRow.text)
            local string icon = getStringIf(checkStr(cell.icon), cell.icon, fromRow.icon)
            local integer red = getIntegerIf(cell.color.redSet,/*
                                            */cell.color.red, fromRow.color.red)
            local integer green = getIntegerIf(cell.color.greenSet,/*
                                            */cell.color.green, fromRow.color.green)
            local integer blue = getIntegerIf(cell.color.blueSet,/*
                                            */cell.color.blue, fromRow.color.blue)
            local integer alpha = getIntegerIf(cell.color.alphaSet,/*
                                            */cell.color.alpha, fromRow.color.alpha)
            
			call MultiboardSetItemStyle(mItem, showText, showIcon)
			call MultiboardSetItemWidth(mItem, width[colId])
            call MultiboardSetItemValue(mItem, text)
			call MultiboardSetItemValueColor(mItem, red, green, blue, alpha)
            call MultiboardSetItemIcon(mItem, icon)
		endmethod
		
		private static method physAddRow takes InfoBoxRow row, integer atRowIndex returns nothing
			local integer cSize = row.size()
			local integer looper = 0
			local multiboarditem mItem = null
			local InfoBoxCell cell = 0
			
			loop
				exitwhen looper >= cSize
				set cell = row[looper]
				set mItem = MultiboardGetItem(board, atRowIndex, looper)
				call applyCell(mItem, cell, looper, row)
				call MultiboardReleaseItem(mItem)
				set looper = looper + 1
			endloop
			
			set mItem = null
		endmethod
        
		private static method initColumns takes integer maxColumnIndex returns nothing
			local InfoBoxRow row = 0
			local integer rowTotal = rows[-1]
			local integer looper = 0
			local multiboarditem mItem = null
            local integer rowSize = 0
			
			loop
				exitwhen looper >= rowTotal
				set row = rows[looper]
				loop
                    set rowSize = row.size()
					exitwhen rowSize == maxColumnIndex
					call row.addCell(InfoBoxCell.create())
					set mItem = MultiboardGetItem(board, looper, rowSize-1)
					call applyCell(mItem, row[rowSize-1], rowSize-1, row)
				endloop
				set looper = looper + 1
			endloop
			
			set mItem = null
		endmethod
		
        static method appendRow takes InfoBoxRow row returns nothing
			local integer newSize = rows[-1]
			local integer columnCount = MultiboardGetColumnCount(board)
			set rows[newSize] = row
			set rows[-1] = newSize + 1
			call MultiboardSetRowCount(board, newSize + 1)
            
            if (row.size() > columnCount) then
                call MultiboardSetColumnCount(board, row.size())
				call initColumns(row.size())
			endif
            
			call physAddRow(row, newSize)
        endmethod
        
        static method operator title takes nothing returns string
            return titleText
        endmethod
        
        static method operator title= takes string s returns nothing
            set titleText = s
            
            if board != null then
                call MultiboardSetTitleText(board, titleText)
            endif
        endmethod
        
        static method operator titleColor takes nothing returns InfoBoxColor
            return tColor
        endmethod
        
        static method operator titleColor= takes InfoBoxColor color returns nothing
			if color != tColor then
				call tColor.destroy()
				set tColor = color
			endif
            
            if board != null then
                call MultiboardSetTitleTextColor(board, tColor.red, tColor.green,/*
                                                */tColor.blue, tColor.alpha)
            endif
        endmethod
        
        static method setTitle takes string s, InfoBoxColor color returns nothing
            set thistype.titleColor = color
            set thistype.title = s
        endmethod
        
        static method operator display= takes boolean do returns nothing
            set doDisplay = do
            
            if board != null then
                call MultiboardDisplay(board, doDisplay)
            endif
        endmethod
        
        static method operator display takes nothing returns boolean
            return doDisplay
        endmethod
        
        private static method reduceSize takes nothing returns nothing
            local integer looper = MultiboardGetColumnCount(board)
            loop
                exitwhen looper == 0
                call MultiboardSetColumnCount(board, looper - 1)
                set looper = looper - 1
            endloop
            
            set looper = MultiboardGetRowCount(board)
            loop
                exitwhen looper == 0
                call MultiboardSetRowCount(board, looper - 1)
                set looper = looper - 1
            endloop
        endmethod
        
        static method clear takes nothing returns nothing
            call reduceSize()
			call rows.flush()
        endmethod
		
		private static method silentClear takes nothing returns nothing
			call reduceSize()
		endmethod
        
        private static method silentCreate takes nothing returns nothing
            local integer size = rows[-1] - 1
			local integer looper = 0
			
			set display = (doDisplay)
			set minimize = (doMinimize)
			call setTitle(titleText, tColor)
			
			loop
				exitwhen looper >= size
				call appendRow(rows[looper])
				set looper = looper + 1
			endloop
		endmethod
		
		static method updateRowAtPos takes integer which returns nothing
			local integer looper = 0
			local integer max = MultiboardGetColumnCount(board) - 1
			local multiboarditem mItem = null
            local InfoBoxRow row = 0
			
			loop
				set mItem = MultiboardGetItem(board, which, looper)
                set row = rows[which]
				call applyCell(mItem, row[looper], looper, row)
				call MultiboardReleaseItem(mItem)
				exitwhen max == looper
				set looper = looper + 1
			endloop
			set mItem = null
		endmethod
		
		static method updateRow takes InfoBoxRow row returns nothing
			local integer looper = 0
			local integer max = rows[-1] - 1
			
			loop
				if (row) == (rows[looper]) then
					call updateRowAtPos(looper)
					exitwhen true
				endif
				exitwhen looper == max
				set looper = looper + 1
			endloop
		endmethod
		
		static method refresh takes nothing returns nothing
			local integer looper = 0
			local integer max = rows[-1]
			
			call silentClear()
			call MultiboardSetColumnCount(board, InfoBoxRow(rows[0]).size())
			call MultiboardSetRowCount(board, rows[-1])
			set looper = 0
			set max = rows[-1]
			
			loop
				exitwhen max <= looper
				call updateRowAtPos(looper)
				set looper = looper + 1
			endloop
		endmethod
		
		static method operator [] takes integer offset returns InfoBoxRow
			if offset < 0 or offset > (rows[-1] - 1) then
				static if DEBUG_MODE then
                    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, /*
                                */"call to InfoBox.operator[" + I2S(offset) + "]: invalid offset")
				endif
                return 0
			endif
			return rows[offset]
		endmethod
		
		static method columnCount takes nothing returns integer
			return InfoBoxRow(rows[0]).size()
		endmethod
		
		static method rowCount takes nothing returns integer
			return rows[-1]
		endmethod
        
        static method createRow takes nothing returns InfoBoxRow
            return InfoBoxRow.createEx(columnCount())
        endmethod
		
        implement m
    endstruct
endlibrary

Example:
JASS:
scope sw initializer i
    private function w takes nothing returns nothing
        //locals
        local InfoBoxRow row = 0
        //create new cell
        local InfoBoxCell cell = InfoBoxCell.create()
        //instance for style
        local InfoBoxStyle style = InfoBoxStyle.create()
        //instance for color
        local InfoBoxColor color = InfoBoxColor.create()
        
        //set text of the cell
        set cell.text = "Aw"
        //set the cell to show text and to not show icon
        set cell.style.icon = false
        set cell.style.text = true
        
        //configure the InfoBox itself:
        //set title
        set InfoBox.title = "Title extreme edition"
        //set title's color
        set InfoBox.titleColor = InfoBoxColor.createEx(0xFF, 0xFA, 0xBB, 0xFF)
        
        //configuring width of columns:
        //supports any number of columns, even ones that do not exist yet
        //first column will have width 0.1(10%), second 15% and third 12%.
        set InfoBox.width[0] = 0.1
        set InfoBox.width[1] = 0.15
        set InfoBox.width[2] = 0.12
        
        //create new instance of row.
        set row = InfoBoxRow.create()
        //add the cell we configured to it
        call row.addCell(cell)
        //add 2 empty cells to it
        //can chain call them
        call row.addCells(2)
        
        //alernativelly:
        //set row = InfoBoxRow.createEx(3)
        //row will be initialized with 3 empty cells
        
        //say to second cell(cell in first row and second column)
        //to show both text and icon.
        call row[1].style.show(true, true)
        
        //set colors for text
        call row[0].colors(0xFF, 0x00, 0xFF, 0xFF)
        call row[1].colors(0xFF, 0xFF, 0x00, 0xFF)
        call row[2].colors(0x00, 0xFF, 0xFF, 0xFF)
        
        //set the icon
        set row[1].icon = "UI\\Feedback\\Resources\\ResourceGold.blp"
        
        //set the text
        set row[1].text = "ww"
        
        //set the style combined, to show text and to not show icon
        //for third cell in the row.
        call row[2].style.show(true, false)
        //set the text.
        set row[2].text = "text"
        
        //doing row[3] would result in InfoBoxCell(0) to be returned, and if in debug mode,
        //error message to be printed
        
        //Add the row to the InfoBox
        call InfoBox.appendRow(row)
        
        //create row from the InfoBox, which has
        //as many cells as the InfoBox has columns
        //Since the row we just added had 3 cells, and before that
        //the InfoBox had 0 columns, now it has 3
        //so the returned instance also has 3 cells.
        set row = InfoBox.createRow()
        
        //same as calling
        //set row = InfoBoxRow.createEx(InfoBox.columnCount())
        
        //extra, styled
        call style.show(true, false)
        call color.setColors(0xFF, 0xFA, 0xCC, 0xFF)
        set row = InfoBoxRow.createExStyled(InfoBox.columnCount(), style, color)
        
        //set text of all the cells inside the row to 'text'
        set row.text = "Row"
        //set color of the text for all cells in the row
        call row.colors(0xAA, 0xAF, 0xFF, 0xFF)
        
        //give all the cells the same style(show text, dont shot icon)
        call row.style.show(true, false)
        
        //change text of the first cell in row specifically.
        set row[0].text = "Row superior"
        
        //add this row to the InfoBox
        call InfoBox.appendRow(row)
        
        //unminimize and display the InfoBox.
        set InfoBox.minimize = false
        set InfoBox.display = true
    endfunction
    
    function BoolDummy takes boolean a returns nothing
    endfunction
    
    function FullListing takes nothing returns nothing
        local InfoBoxRow row = InfoBoxRow.create()
        local InfoBoxCell cell = InfoBoxCell.create()
        local InfoBoxStyle style = InfoBoxStyle.create()
        local InfoBoxColor color = InfoBoxColor.create()
        
        //Color posibility:
        set color = InfoBoxColor.createEx(0xFF, 0xCC, 0xAA, 0xFF)
        call color.setColors(0xFF, 0xAA, 0xCC, 0xFF)
        set color.red = 0xAA
        set color.green = 0xFF
        set color.blue = 0xAC
        set color.alpha = 0xFF
        call BoolDummy(color.redSet)
        call BoolDummy(color.greenSet)
        call BoolDummy(color.blueSet)
        call BoolDummy(color.alphaSet)
        
        //Style posibility:
        call style.show(true, false)
        set style.icon = false
        set style.text = true
        call BoolDummy(style.iconSet)
        call BoolDummy(style.textSet)
        
        //Cell possibility:
        set cell.style.text = true
        set cell.style.icon = false
        call cell.style.show(true, false)
        set cell.color.red = 0
        set cell.color.green = 0
        set cell.color.blue = 0
        set cell.color.alpha = 0
        call cell.colors(0, 0, 0, 0)
        set cell.color = InfoBoxColor.createEx(0, 0, 0, 0)
        set cell.text = "some text"
        set cell.icon = "some icon"
        //yea, I call it with its own values
        //so it is no-op, but to showcase the arguments
        call cell.styles(cell.style.text, cell.style.icon, cell.color.red,/*
                        */cell.color.green, cell.color.blue, cell.color.alpha)
        call cell.stylesEx(style, color)
        
        //Row possibility:
        set row = InfoBoxRow.createEx(4)
        set row = InfoBoxRow.createExStyled(4, style, color)
        call row.addCell(cell)
        call row.addCells(5)
        set cell = row[1]
        set row.style.icon = false
        set row.style.text = true
        call row.style.show(true, false)
        set row.color.red = 5
        set row.color.green = 5
        set row.color.blue = 5
        set row.color.alpha = 5
        call row.colors(5, 5, 5, 5)
        set row.text = "some text"
        set row.icon = "some icon"
        call row.styles(row.style.text, row.style.icon, row.color.red,/*
                        */row.color.blue, row.color.green, row.color.alpha)
        call row.stylesEx(style, color)
        
        //InfoBox
        call InfoBox.appendRow(row)
        call InfoBox.clear()
        call InfoBox.refresh()
        set row = InfoBox.createRow()
        set row = InfoBox[3]
        set InfoBox.title = "title supa mida"
        set InfoBox.titleColor = InfoBoxColor.createEx(0, 0, 5, 0)
        call InfoBox.setTitle("title", InfoBoxColor.createEx(5, 5, 4, 0))
        set InfoBox.minimize = false
        set InfoBox.display = false
        call InfoBox.updateRowAtPos(0)
        call InfoBox.updateRow(row)
        call BJDebugMsg("The InfoBox has " + I2S(InfoBox.rowCount()) +/*
                        */" rows and " + I2S(InfoBox.columnCount()) + " columns")
    endfunction
    
    private function i takes nothing returns nothing
        call TimerStart(CreateTimer(), 1.0, false, function w)
    endfunction
endscope

API:
JASS:
struct InfoBoxColor
	/* Stores colors for text of cells as well as title for the multiboard. */
	
	integer red
	integer green
	integer blue
	integer alpha
		/* - Individual color channels.
		   - If empty instance was created and user writes to
			 any of these, the respective Set boolean flag will
			 bet set to true. */ 
	
	readonly boolean redSet
	readonly boolean greenSet
	readonly boolean blueSet
	readonly boolean alphaSet
		/* - Whether X is set or not(mainly for the applying of cells into InfoBox,
		  so we can prioritize cells over rows). */

	static method create takes nothing returns thistype
		/* - Creates new empty instance(all colors are set to 0, all Sets to false) and returns it. */
	
	static method createEx takes integer r, integer g, integer b, integer a returns thistype
		/* - Creates new instance, sets all colors to respective arguments, sets all Sets to true
		  (colors are set) and returns the instance. */
	
	method destroy takes nothing returns nothing
		/* - Deallocates calling instance. */
	
	method setColors takes integer Red, integer Green, integer Blue, integer Alpha returns nothing
		/* - Sets all colors to respective arguments of the method, sets all Set flags to true. */

endstruct

struct InfoBoxStyle
	/* Stores information about style options for individual cells(or rows) on multiboard. */
	
	boolean icon
	boolean text
		/* - Represent whether to show text and/or icon on
			 individual cells(elements of multiboard) or not.
		   - Whenever write to either of these happens,
			 respective Set flag is also set to true. */
	
	readonly boolean iconSet
	readonly boolean textSet
		/* - Represent whether icon/text was set or not */

	static method create takes nothing returns thistype
		/* - Creates new empty instance, sets icon and text to false,
			 sets iconSet and textSet to false, and return the instance. */
		
	static method show takes boolean icon, boolean text returns nothing
		/* - Sets the structs members to respective arguments.
		   - Sets Set flags to true. */
	
	method destroy takes nothing returns nothing
		/* - Destroys calling instance. */
		
endstruct

struct InfoBoxCell
	/* Stores data for individual cells of multiboard. */
	
	InfoBoxStyle style
	InfoBoxColor color
		/* - Individual variables, for convenient syntax
			 (for instance set cell.color.red = 45). */
	
	string text
	string icon
		/* - Strings representing the text and/or icon for given cell */
		/* - Unlike in Style/Color, these do not have special
			 Set flags, but can be checked against "" or null for Set. */
	
	static method create takes nothing returns thistype
		/* - Creates new empty instance of cell,
			 which constructs empty style and color and returns it. */
	
	method destroy takes nothing returns nothing
		/* - Destroys calling instance as well as its members. */
	
	method colors takes integer red, integer green, integer blue, integer alpha returns nothing
		/* - Sets colors for color to their respective arguments. */
	
	method styles takes boolean text, boolean icon, integer red,/*
						*/integer blue, integer green, integer alpha returns nothing
		/* - Sets the styling and color on cell to respective arguments. */
	
	method stylesEx takes InfoBoxStyle style, InfoBoxColor color returns nothing
		/* - Sets values for style and color to their respective arguments.
		   - This method does deep copy of style and color, so these instances
			 can be destroyed after calling this method. */
endstruct

struct InfoBoxRow
	/* Represents a single Row filled with cells of multiboard. */
	
	InfoBoxColor color
	InfoBoxStyle style
		/* - Variables, like for cell, but for row-wide changes. */
	
	string text
	string icon
		/* - Same as color, style. */
	
	static method create takes nothing returns thistype
		/* - Creates empty instance, creates empty color and style
			 and returns the created row instance. */
	
	method destroy takes nothing returns nothing
		/* - Destroys calling instance as well as color and style. */
	
	method colors takes integer red, integer green, integer blue, integer alpha returns nothing
		/* - Sets the color's RBGA values to respective method arguments. */
	
	method styles takes boolean text, boolean icon, integer red,/*
						*/integer blue, integer green, integer alpha returns nothing
		/* - Sets the color's and style's values to respective arguments. */
	
	method stylesEx takes InfoBoxStyle style, InfoBoxColor color returns nothing
		/* - Sets fields of stored color and style to the ones from argument.
		   - This method does deep copy of the fields, so you can destroy
			 the instances you call this method with afterwards. */
	
	method addCell takes InfoBoxCell cell returns thistype
		/* - Adds cell into the row.
		   - Returns this for chaining. */
	
	method addCells takes integer howMany returns thistype
		/* - Adds 'howMany' empty cells into the row.
		   - Returns this for chaining. */
	
	method size takes nothing returns integer
		/* - Returns number of cells stored inside the row. */
	
	static method createEx takes integer emptyCells returns thistype
		/* - Creates empty row and adds 'emptyCells' empty cells to it.
		   - Equivalent to:
				return create().addCells(emptyCells)
		*/
	
	static method createExStyled takes integer howMany, InfoBoxStyle toApply,/*
										*/InfoBoxColor toApplyC returns thistype
		/* - Same as createEx, but additionally sets style and color for
			 the added cells to the arguments.
		   - Does a deep copy of fields, so you can destroy the instances
			 for color and style passed into this method afterwards. */
	
	method operator [] takes integer offset returns InfoBoxCell
		/* - Returns cell at given 'column' position.
		   - If offset < 0 or offset >= size(), then returns 0
			 and if in debug mode, prints out of bounds message. */
endstruct

struct InfoBoxWidth extends array
	static method size takes nothing returns integer
		/* - Returns number of columns that have had their width specified */
	
	method operator [] takes integer colId returns real
		/* - Returns the width for given column(indexing starts at 0).
		   - If colId < 0 or colId >= size(), returns 0. and if in debug mode,
			 prints out of bounds message. */
	
	method operator []= takes integer colId, real width returns nothing
		/* - Sets the width for given column(indexing starts at 0).
		   - if colId < 0, does nothing, and if in debug mode,
			 prints out of bounds message.
		   - All columns with id < colId have their width automatically
			 set to 0. */
	
	static method operator [] takes integer colId returns real
		/* - returns thistype(1)[colId] */
	
	static method operator []= takes integer colId, real width returns nothing
		/* - equivalent to: set thistype(1)[colId] = width */

endstruct

struct InfoBox extends array
	/* Represents single multiboard. */
	
	static InfoBoxWidth width
		/* - Instance of width for convenient syntax, such as InfoBox.width[5] = 0.25 */
	
	static boolean minimize
		/* - Represents whether to minimize the multiboard or not.
		   - Change in value has immediate effect. */
	
	static boolean display
		/* - Represents whether to display the multiboard or not.
		   - Change in value has immediate effect. */
	
	static string title
		/* - Represents the title of the multiboard.
		   - Change in value has immediate effect. */
	
	static InfoBoxColor titleColor
		/* - Represents the color for title.
		   - Change in value has immediate effect. */
	
	static method appendRow takes InfoBoxRow row returns nothing
		/* - Adds row into InfoBox.
		   - All values are set in following manner:
				- If cell has set the value for given field, use cell's value.
				- Otherwise use row's value, whether set or not. */
	
	static method setTitle takes string s, InfoBoxColor color returns nothing
		/* - Sets both title and titleColor. */
	
	static method clear takes nothing returns nothing
		/* - Clears the multiboard, making it effectivelly 0x0. */
	
	static method updateRowAtPos takes integer which returns nothing
		/* - Indexing from 0.
		   - Redraws given row, as if it was newly added, but at its position. */
	
	static method updateRow takes InfoBoxRow row returns nothing
		/* - If row is not stored inside InfoBox, nothing happens.
		   - Loops through stored rows, and then calls updateRowAtPos
			 with its index */
	
	static method refresh takes nothing returns nothing
		/* - Resizes the multiboard down to 0, stretches it back
			 to original size again and calls updateRow for every row in the InfoBox. */
	
	static method operator [] takes integer offset returns InfoBoxRow
		/* - Indexing from 0.
		   - Returns row for InfoBox at given position.
		   - if offset < 0 or offset >= size(), returns 0 and if in debug mode,
			 also prints out of bounds message. */
	
	static method columnCount takes nothing returns integer
		/* - Returns number of columns of InfoBox. */
	
	static method rowCount takes nothing returns integer
		/* - Returns number of rows of InfoBox. */
	
	static method createRow takes nothing returns InfoBoxRow
		/* - Creates new instance of InfoBoxRow with
			 exactly as many empty cells, as there are columns in InfoBox
			 and returns it. */
endstruct


Version 2.1:
  • Made initialization private
  • Marked the conditional error display inside InfoBoxWidth with static if, instead of normal

Version 2.0:
  • Completely redid the API
  • Removed InfoBoxGroupX
  • Added mechanism that now uses cell data when applying cells, if they were set and uses the row data otherwise(regardless if they were set or not)
  • create methods now always create empty instances, that have all fields marked as not set
  • set cell.style.show.icon/text -> set cell.style.icon/text
  • set cell.style.color.X -> set cell.color.X
  • Removed struct InfoBoxShow, redundant, as its guts have effectivelly became InfoBoxStyle.
  • Removed methods:
    • InfoBoxColor operator X and operator X=
    • InfoBoxStyle.colors InfoBoxStyle.shows
    • InfoBox.show and InfoBox.show=
    • InfoBox.nativeHandler
  • changed methods:
    • InfoBoxColor.create: Took 4 arguments, now takes 0 and creates empty color instance.
  • Added new Methods:
    • InfoBoxColor.createEx(integer r, integer g, integer b, integer a): Works as InfoBoxColor.create in 1.1
    • InfoBoxStyle.show(boolean text, boolean icon): Works as InfoBoxStyle.shows in 1.1
    • InfoBoxCell.colors(integer r, integer h, integer b, integer a)
    • InfoBoxCell.stylesEx(InfoBoxStyle style, InfoBoxColor color)
    • InfoBoxRow.colors(integer r, integer g, integer b, integer a)
    • InfoBoxRow.stylesEx(InfoBoxStyle style, InfoBoxColor color)
    • InfoBoxRow.addCells(integer howMany)
    • InfoBox.minimize, InfoBox.minimize=

Version 1.1:
  • Added InfoBoxRow.createEx(integer) and InfoBoxCell.createExStyled(integer, InfoBoxStyle)
  • InfoBox.createRow() now uses InfoBoxRow.createEx(columnCount())

Version 1.0:
  • Initial release
  • Brain explosion
 
Last edited:
Cool design.

I can't give an extended review, but I'll give my initial impression/notes:
  • Your color assignment is all over the place. Stick to one name: choose to use 'r' or 'red, but not both. Having two names refer to the same object is confusing. And you don't need to use operators if you support both access and assignment, e.g. here is a potential simplification of one of the structs:
    JASS:
        struct InfoBoxColor
            integer red
            integer green
            integer blue
            integer alpha
    
            method setColors takes integer Red, integer Green, integer Blue, integer Alpha returns nothing
                set red = Red
                set green = Green
                set blue = Blue
                set alpha = Alpha
            endmethod
            
            static method create takes integer r, integer g, integer b, integer a returns thistype
                local thistype this = allocate()
                call this.setColors(r, g, b, a)
                return this
            endmethod
            
            method destroy takes nothing returns nothing
                call deallocate()
            endmethod
        endstruct
  • Some of your color methods manually assign red/green/blue/alpha. Use 'setColors' when possible.
  • Try to avoid magic numbers here:
    JASS:
    private static Table rows = 546872
    private static InfoBoxColor tColor = 7555
    If you need a random number, you should use the 'key' feature:
    http://www.wc3c.net/vexorian/jasshelpermanual.html#keykeykey
    Otherwise, those things should be constants.
  • There should be more convenience methods. Using .addCell.addCell.addCell() is redundant. You should allow the user to set the number of cells in a row (and have them be made automatically), and directly set the number of rows/columns.

Good work so far. You have a good design--it just needs to be better on the user's side. :)
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I kinda agree on the color, I wanted to be fancy :D

The tables have redundant values, I will remove these

I added in version 1.1 Row.createEx(N), which creates N empty cells, or createExStyled(N, style) which creates N cells with style set to the input(copied)
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
Update to version 2.0

Why 2.0 and not 1.2? Every single struct underwent "surgery", completly changing them.

New policy has been added: If cell has certain field set, it will be used, otherwise the row-wide(the one that the cell belongs to) will be used, regardless if its set or not.

There are a very few API breakings, but I doubt this was used anyhow, so Im fine with that.

The API listing is also a lot lot better now.

One thing to note is that the API lists things like InfoBoxColor.red as variable, when in fact they are operators, but they act as variables(both red and red= are overloaded), so I think user doesnt need to necessarily care about it being operator.

They are operators, because the design requires it. I need to know whether the cell had their values changed, so they have to be method calls, and appropriate readonly variables are set when the values are set, so that when I "apply" the cells to the multiboard, I can correctly distinguish whether to use the cell's settings, or use row's settings. This is trivial for strings(hence text and icon strings in InfoBoxCell is not operator), but for boolean it is impossible to tell without additional variable. Thats also a reason why all the constructors are making empty instances(nothing is "set", all is set to default values, so color is (0, 0, 0, 0), style is (false, false) etc)
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
this is design decision. Without the operators, the cell fields over row fields is not implemetable, because you do not know whether something changed, without calling function, but I much prefer writing set a.a = true than call a.setA(true).
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
the user in fact doesnt need, because every single access to the multiboard is done in a way that it writes into a variable, and then it checks if the multiboard exists, and if it indeed does, it also performs the change in that. Finally, when the multiboard is created, all the already done changes(in other system's initializators) are commited into the InfoBox itself.

The module m is private, the function is purposelly not private, for the InitInfoBox function(otherwise it couldn't call it).

This function exists and is meant to be called if you really really need the multiboard to exist before this initialization has ran.

No that is not true, JassHelper gives no fucks when it comes to order. The only guaranteed order is module > struct > library > scope > InitTrig. There are no guarantees that if I have library A and library B requires A, that module inside library A will be initialized before module inside library B(this is reasonable behaviour tho, because the module can be in library A, and be implemented in struct inside library B)
 
Nestharus wrote a very nice library for multiboards, I also used it happily myself recently. System - Multiboard | The Helper
Your approach has also good aspects, like for example "addCells" but as whole product it seems a bit less straight forward, especially with the styles/color I'm not sure if the extra hustle is useful.
Also this aspect seems like a big limitation for public usage in comparison to Nestharus's:
JASS:
/*    Expected use case:
        Single player maps, like mine.
        (This system could be updated probably without change to support 1 InfoBox
         per player. If this is desired, I can update it later)
*/

Edit: Gy for now, you can PM me always when it should be changed.
 
Last edited:
Top