Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
layoutUtils (library for text layout)
#6
Hi,

New version with colorization; I let you discover
There might be a few bugs, please let me know if you find any! 
The next step is to add verification code to raise errors if the layout declaration contains mistakes.

layoutUtils.bi
Code: (Select All)
$INCLUDEONCE

type layoutUtils_layoutType
    id as integer
    layoutName as string
    fullpath as string
    content as string
    resolved as string
    prepared as integer
end type

type layoutUtils_elementType
    layoutIndex as integer
    position as integer
    length as integer
    reference as string
end type

type layoutUtils_colorType
    layoutIndex as integer
    value as long
    activatorChar as string * 1
end type

type layoutUtils_activatorType
    layoutIndex as integer
    position as integer
    activatorChar as string * 1
    colorValue as long
    row as integer
    column as integer
end type

redim shared layoutUtils_layoutList(0) as layoutUtils_layoutType
redim shared layoutUtils_elementList(0) as layoutUtils_elementType
redim shared layoutUtils_colorList(-1) as layoutUtils_colorType
redim shared layoutUtils_activatorList(-1) as layoutUtils_activatorType

layoutUtils.bm
Code: (Select All)
$INCLUDEONCE

'------------------------------------------------------------------------------
' layoutUtils Library
'
' Author : Herve Heritier
' Date  : July 18 2025
' Version: 0.0.2
'
' Overview:
'  The layoutUtils library provides a simple framework for creating, loading,
'  parsing, and manipulating layout data structures represented as strings.
'  Layouts can be built directly from in-memory strings, loaded from external
'  files, or defined in DATA statements. The library supports placeholder
'  references within layouts that can be dynamically resolved or replaced, and
'  allows retrieval of the final resolved content.
'
' Key Features:
'  1. Layout Creation & Loading
'      - layoutUtils_build%: Create a new layout directly from a string, assign
'        it a name, and prepare it for reference resolution.
'      - layoutUtils_load%: Load layout content from an external file.
'      - layoutUtils_loadData%: Load layout content from embedded DATA statements.
'      - Internally manages all layouts in layoutUtils_layoutList.
'
'  2. Reference Extraction & Tracking
'      - Scan layout content for placeholder patterns "<&reference>".
'      - Record each placeholder’s layout index, position, length, and name in
'        layoutUtils_elementList for subsequent processing.
'
'  3. Reference Resolution & Replacement
'      - layoutUtils_populateIndex: Replace a named reference in a layout by index.
'      - layoutUtils_populate: Replace a named reference in a layout by name.
'      - Automatically preserves field width by padding or truncating the replacement.
'
'  4. Layout Retrieval
'      - layoutUtils_getPopulatedIndex$: Return the fully resolved content
'        of a layout by its numeric index.
'      - layoutUtils_getPopulated$: Return the fully resolved content of a
'        layout by its name.
'
'  5. Utility Functions
'      - layoutUtils_find: Locate a layout by name and return its index.
'      - layoutUtils_reset / layoutUtils_resetIndex: Restore a layout’s
'        resolved content back to its original raw content.
'      - layoutUtils_getNext$: Extract the next placeholder substring from layout
'        content between the markers "<&" and ">".
'
' Usage Workflow:
'  1. Create or load a layout:
'        idx1 = layoutUtils_build("intro", rawStringContent, "")
'        idx2 = layoutUtils_load("level1", "C:\layouts\level1.layout")
'        idx3 = layoutUtils_loadData("level2", 5)
'
'  2. Layout preparation (automatic during build/load):
'        success = layoutUtils_prepare(idx1)
'
'  3. Populate references:
'        layoutUtils_populate("level1", "playerStart", "X:10,Y:20")
'
'  4. Retrieve the final resolved layout content:
'        final$. = layoutUtils_getPopulated("level1")
'        final$. = layoutUtils_getPopulatedIndex(idx1)
'
'  5. Reset to original content (if needed):
'        layoutUtils_reset("level1")
'
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
' Function: layoutUtils_build%
' Purpose : Adds a new layout to the layoutUtils_layoutList array and prepares it.
'
' Parameters:
'  layoutName  - (string) The name of the layout to be added.
'  content  - (string) The content/data of the layout.
'  fullpath - (string) The full file path associated with the layout.
'
' Returns:
'  (integer) The index of the newly added layout in the layoutUtils_layoutList array.
'
' Description:
'  This function creates a new layoutUtils_layoutType object, assigns it an ID,
'  sets its properties (layoutName, fullpath, content), and appends it to the
'  layoutUtils_layoutList array. It then prepares the layout using layoutUtils_prepare
'  and stores the result in the 'prepared' field. The function returns the index
'  of the newly added layout.
'------------------------------------------------------------------------------
function layoutUtils_build% (layoutName as string, content as string, fullpath as string)
    dim res as layoutUtils_layoutType
    dim last%
    last% = ubound(layoutUtils_layoutList) + 1
    redim _preserve layoutUtils_layoutList(last%) as layoutUtils_layoutType
    dim layout as layoutUtils_layoutType
    layout.id = last%
    layout.layoutName = layoutName
    layout.fullpath = fullpath
    dim colors(0) as layoutUtils_colorType
    dim activators(0)  as layoutUtils_activatorType
    layoutUtils_extractColors last%, content
    layout.content = content
    layoutUtils_layoutList(last%) = layout
    layoutUtils_layoutList(last%).prepared = layoutUtils_prepare(last%)
    layoutUtils_build = last%
end function

'------------------------------------------------------------------------------
' Function: layoutUtils_load%
' Purpose : Loads a layout from a file and adds it to the layoutUtils_layoutList array.
'
' Parameters:
'  layoutName  - (string) The name to assign to the loaded layout.
'  fullpath - (string) The full file path to load the layout content from.
'
' Returns:
'  (integer) The index of the newly added layout in the layoutUtils_layoutList array.
'
' Description:
'  This function opens the specified file in binary mode, reads its content,
'  and creates a new layout entry using layoutUtils_build. The layout is then
'  prepared and stored in the layoutUtils_layoutList array.
'------------------------------------------------------------------------------
function layoutUtils_load% (layoutName as string, fullpath as string)
    open fullpath for binary as #1
    dim content$, c as _unsigned _byte
    do until eof(1)
        get #1,,c
        content$ = content$ + chr$(c)
    loop
    close #1
    layoutUtils_load = layoutUtils_build (layoutName, content$, fullpath)
end function

'------------------------------------------------------------------------------
' Function: layoutUtils_loadData%
' Purpose : Loads a layout from DATA statements and adds it to the layoutUtils_layoutList array.
'
' Parameters:
'  layoutName - (string) The name to assign to the loaded layout.
'  length  - (integer) The number of DATA lines to read for the layout content.
'
' Returns:
'  (integer) The index of the newly added layout in the layoutUtils_layoutList array.
'
' Description:
'  This function reads the specified number of lines from DATA statements,
'  concatenates them with line breaks, and creates a new layout entry using
'  layoutUtils_build. The layout is then prepared and stored in the
'  layoutUtils_layoutList array.
'------------------------------------------------------------------------------
function layoutUtils_loadData% (layoutName as string, length as integer)
    dim content$, i%, c$
    for i% = 1 to length
        read c$
        content$ = content$ + c$ + _STR_NAT_EOL
    next i%
    layoutUtils_loadData = layoutUtils_build (layoutName, content$, "")
end function

'------------------------------------------------------------------------------
' Function: layoutUtils_prepare%
' Purpose : Scans the layout content for references and populates the layoutUtils_elementList array.
'
' Parameters:
'  index - (integer) The index of the layout in layoutUtils_layoutList to prepare.
'
' Returns:
'  (integer) 1 if preparation is successful.
'
' Description:
'  This function searches the layout content for reference patterns (e.g., <&reference>)
'  and records their positions, lengths, and reference names in the layoutUtils_elementList array.
'  Each found reference is stored as a layoutUtils_elementType object.
'------------------------------------------------------------------------------
function layoutUtils_prepare% (layoutIndex as integer)
    layoutUtils_prepare = 0
    dim position%
    position% = 1
    dim ref as string
    do
        ref = layoutUtils_getNext (layoutIndex, position%)
        if ref = "" then exit do
        dim length%
        length% = len(ref) + 3
        dim n%
        n% = instr(,ref,".")
        dim reference$
        if n% > 0 then
            reference$ = left$(ref,n%-1)
        else
            reference$ = ref
        end if
        dim res as layoutUtils_elementType
        res.layoutIndex = layoutIndex
        res.position = position% - 2
        res.length = length%
        res.reference = reference$
        dim last%
        last% = ubound(layoutUtils_elementList) + 1
        redim _preserve layoutUtils_elementList(last%) as layoutUtils_elementType
        layoutUtils_elementList(last%).layoutIndex = res.layoutIndex
        layoutUtils_elementList(last%).position = res.position
        layoutUtils_elementList(last%).length = res.length
        layoutUtils_elementList(last%).reference = res.reference
    loop
    layoutUtils_prepare = 1
end function

'------------------------------------------------------------------------------
' Function: layoutUtils_find
' Purpose : Finds the index of a layout in the layoutUtils_layoutList array by its name.
'
' Parameters:
'  layoutName - (string) The name of the layout to search for.
'
' Returns:
'  (integer) The index of the layout if found, otherwise 0.
'
' Description:
'  This function iterates through the layoutUtils_layoutList array and compares
'  each layout's name to the provided name. If a match is found, it returns
'  the index of the layout. If no match is found, it returns 0.
'------------------------------------------------------------------------------
function layoutUtils_find (layoutName as string)
    layoutUtils_find = 0
    dim i%, maxi%
    maxi% = ubound(layoutUtils_layoutList)
    for i% = 1 to maxi% + 1
        if i% > maxi% then exit sub
        if layoutUtils_layoutList(i%).layoutName = layoutName then exit for
    next i%
    layoutUtils_find = i%
end function

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_resetIndex
' Purpose  : Resets the resolved content of a layout to its original content.
'
' Parameters:
'  index - (integer) The index of the layout in layoutUtils_layoutList to reset.
'
' Description:
'  This subroutine sets the 'resolved' field of the specified layout to its
'  original 'content', effectively undoing any replacements or modifications.
'------------------------------------------------------------------------------
sub layoutUtils_resetIndex (index as integer)
    layoutUtils_layoutList(index).resolved = layoutUtils_layoutList(index).content
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_reset
' Purpose  : Resets the resolved content of a layout (by name) to its original content.
'
' Parameters:
'  layoutName - (string) The name of the layout to reset.
'
' Description:
'  This subroutine finds the layout index by name and calls layoutUtils_resetIndex
'  to restore the 'resolved' field to its original 'content'.
'------------------------------------------------------------------------------
sub layoutUtils_reset (layoutName as string)
    dim i%
    i% = layoutUtils_find(layoutName)
    if i% > 0 then
        layoutUtils_resetIndex(i%)
    end if
end sub

'------------------------------------------------------------------------------
' Function: layoutUtils_findReference
' Purpose : Finds the index of a reference in the layoutUtils_elementList array for a given layout.
'
' Parameters:
'  index  - (integer) The index of the layout in layoutUtils_layoutList.
'  reference - (string) The reference name to search for.
'
' Returns:
'  (integer) The index of the reference in layoutUtils_elementList if found, otherwise 0.
'
' Description:
'  This function iterates through the layoutUtils_elementList array and compares
'  each element's index and reference name to the provided values. If a match
'  is found, it returns the index of the reference. If no match is found, it returns 0.
'------------------------------------------------------------------------------
function layoutUtils_findReference (layoutIndex as integer, reference as string)
    layoutUtils_findReference = 0
    dim i%, maxi%
    maxi% = ubound(layoutUtils_elementList)
    for i% = 1 to maxi% + 1
        if i% > maxi% then exit sub
        if layoutUtils_elementList(i%).layoutIndex = layoutIndex and layoutUtils_elementList(i%).reference = reference then exit for
    next i%
    layoutUtils_findReference = i%
end function

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_populateIndex
' Purpose  : Replaces a reference in the resolved layout content with a replacement string.
'
' Parameters:
'  index        - (integer) The index of the layout in layoutUtils_layoutList.
'  reference        - (string) The reference name to replace.
'  replacementString- (string) The string to insert in place of the reference.
'
' Description:
'  This subroutine finds the reference in the layoutUtils_elementList for the given layout.
'  It then replaces the corresponding substring in the 'resolved' field of the layout with
'  the replacement string, padded or truncated to match the reference length.
'------------------------------------------------------------------------------
sub layoutUtils_populateIndex (layoutIndex as integer, reference as string, replacementString as string)
    dim refIndex%
    refIndex% = layoutUtils_findReference(layoutIndex, reference)
    mid$( _
            layoutUtils_layoutList(layoutIndex).resolved, _
            layoutUtils_elementList(refIndex%).position _
        ) = _
    left$( _
            replacementString$ + space$( layoutUtils_elementList(refIndex%).length ), _
            layoutUtils_elementList(refIndex%).length _
        )
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_populate
' Purpose  : Replaces a reference in the resolved layout content (by layout name) with a replacement string.
'
' Parameters:
'  layoutName      - (string) The name of the layout in layoutUtils_layoutList.
'  reference        - (string) The reference name to replace.
'  replacementString- (string) The string to insert in place of the reference.
'
' Description:
'  This subroutine finds the layout index by name and calls layoutUtils_populateIndex
'  to replace the specified reference in the resolved content with the replacement string.
'------------------------------------------------------------------------------
sub layoutUtils_populate (layoutName as string, reference as string, replacementString as string)
    dim index%
    index% = layoutUtils_find(layoutName)
    layoutUtils_populateIndex index%, reference, replacementString
end sub

'------------------------------------------------------------------------------
' Function: layoutUtils_getPopulatedIndex$
' Purpose : Returns the resolved (populated) content of a layout by its index.
'
' Parameters:
'  index - (integer) The index of the layout in layoutUtils_layoutList.
'
' Returns:
'  (string) The resolved content of the layout if the index is valid, otherwise an empty string.
'
' Description:
'  This function checks if the provided index is within the valid range of layoutUtils_layoutList.
'  If valid, it returns the 'resolved' field of the layout, which contains the content with all
'  references replaced. If the index is invalid, it returns an empty string.
'------------------------------------------------------------------------------
function layoutUtils_getPopulatedIndex$ (index as integer)
    if index > 0 and index <= ubound(layoutUtils_layoutList) then
        layoutUtils_getPopulatedIndex = layoutUtils_layoutList(index).resolved
    else
        layoutUtils_getPopulatedIndex = ""
    end if
end function

'------------------------------------------------------------------------------
' Function: layoutUtils_getPopulated$
' Purpose : Returns the resolved (populated) content of a layout by its name.
'
' Parameters:
'  layoutName - (string) The name of the layout in layoutUtils_layoutList.
'
' Returns:
'  (string) The resolved content of the layout if found, otherwise an empty string.
'
' Description:
'  This function finds the layout index by name and returns the 'resolved' field,
'  which contains the content with all references replaced. If the layout is not
'  found, it returns an empty string.
'------------------------------------------------------------------------------
function layoutUtils_getPopulated$ (layoutName as string)
    dim index%
    index% = layoutUtils_find(layoutName)
    layoutUtils_getPopulated = layoutUtils_getPopulatedIndex(index%)
end function

'------------------------------------------------------------------------------
' Function: layoutUtils_getNext$ (layoutIndex, position%)
'
' Description:
'  Extracts the next substring found between the markers "<&" and ">" from the
'  content of a layout identified by layoutIndex, starting the search at position%.
'
' Parameters:
'  layoutIndex  - The index of the layout in layoutUtils_layoutList to search within.
'  position%  - The position in the content string to start searching from.
'
' Returns:
'  A string containing the substring found between "<&" and ">" markers.
'  Returns an empty string if no such substring is found.
'
' Example Usage:
'  nextValue$ = layoutUtils_getNext$(1, 10)
'
' Notes:
'  - The function updates position% to the start of the found substring.
'  - If no marker is found, the function returns an empty string.
'------------------------------------------------------------------------------
function layoutUtils_getNext$ (layoutIndex as integer, position%)
    dim beginning$, middle$, ending$
    dim l%, b%, e%, m%
    dim content$
    content$ = layoutUtils_layoutList(layoutIndex).content
    beginning$ = "<&"
    middle$ = ""
    ending$ = ">"
    l% = len(beginning$)
    b% = instr(position%,content$,beginning$)
    if b% = 0 then
        layoutUtils_getNext = ""
        exit sub
    end if
    e% = instr(b%+l%,content$,ending$)
    position% = b%+l%
    middle$ = mid$(content$,position%,e%-b%-l%)
    layoutUtils_getNext = middle$
end function

'------------------------------------------------------------------------------
' Subroutine : layoutUtils_extractColors
''
' Purpose : Parses a layout’s resolved content for embedded color directives
'  and populates two global lists—one for color definitions (layoutUtils_colorList)
'  and one for per‐character activators (layoutUtils_activatorList).
'
' Parameters :
'  layoutIndex - (Integer) Index of the layout whose content is being processed.
'  content    - (String) The full, resolved text of the layout. May contain color
'                        directives prefixed by “$”.
'------------------------------------------------------------------------------
' Subroutine: layoutUtils_extractColors
'
' Purpose : Extracts colorization information and activator positions from a specially
'  formatted layout string. The function parses the content string, identifies
'  colorization characters, and populates the global color and activator lists
'  for the given layout index.
'
' Parameters:
'  layoutIndex - (integer) The index of the layout to associate extracted colors
'                and activators with.
'  content    - (string) The input string containing layout and colorization data.

'                There are 2 colorization modes; the first mode, which we will call the pointed_mode,
'                involves following a text line with a colorization line when necessary to indicate
'                the positions of color changes; the second mode, which we will call the integrated_mode,
'                uses activator characters directly within the text lines.

'                The content must start with a '$' character and follow a specific format.
''
'
'                $<newline>                                         
'                <colorizationCharacter><newline>                   
'                <colorValue1><newline>                             
'                <activatorChar1><newline>
'                <colorValue2><newline>
'                <activatorChar2><newline>
'                ...
'                $<newline>
'                <text line><newline>                                    x 1,1    \
'                                                                                    ) x 1,n
'                <colorizationCharacter><colorization line><newline>      x 0,1    /
'       
'                If content starts with a $ character, then everything up to the next $
'                is used to manage colorization.
'
'                If the colorizationCharacter is blank, then we are in integrated_mode;
'                otherwise, we are in pointed_mode and the colorizationCharacter will be
'                used to designate the colorization lines.
'
'                After the colorizationCharacter there is a repetition of <colorValue>
'                (in long hexadecimal format) followed by a color <activatorChar>.
'
'                After this declarative part, which ends with $, you will find text lines
'                and colorization lines.
'
'                Integrated_mode
'                  In this mode, we have only text lines, and each activatorCharacter mark
'                  the beginning of the corresponding colorization. The activationCharacters
'                  are replaced by a space during display and causes a color change for the rest
'                  of the text until the next activatorCharacter.

'                Pointed_mode :
'                  A colorization line starts with the colorizationCharacter.
'                  The line contains some activatorChars that are positioned relative
'                  to the start of the colorization in the preceding text line;
'                  the activated color remains until the end of the layout or
'                  until the next activator. Other lines are text..
'
' notes:
'  This subroutine assumes that the global arrays are already initialized.
'  It uses dynamic array resizing to append new color and activator entries.
'------------------------------------------------------------------------------
sub layoutUtils_extractColors ( layoutIndex as integer, content as string )
    dim colorizationCharacter as string * 1

    ' color extraction is only performed if the layout content begins with $ or %

    if left$(content,1) <> "$" then exit sub
    dim result$(0)
    layoutUtils_split content, _STR_LF, result$()
    layoutUtils_clean result$()

    ' first line contains only $ or %

    if result$(0) <> "$" then exit sub
    colorizationCharacter = result$(1)
    dim i%
    i% = 2

    ' extract colors

    do while result$(i%) <> "$"
        ReDim _Preserve layoutUtils_colorList(ubound(layoutUtils_colorList) + 1) as layoutUtils_colorType
        layoutUtils_colorList(ubound(layoutUtils_colorList)).layoutIndex = layoutIndex
        layoutUtils_colorList(ubound(layoutUtils_colorList)).value = val(result$(i%))
        i% = i% + 1
        layoutUtils_colorList(ubound(layoutUtils_colorList)).activatorChar = result$(i%)
        i% = i% + 1
    loop
    i% = i% + 1

    redim textArray$(-1)
    dim activatorArray$(-1)
    dim lastTextLength as integer
    lastTextLength = 0

    ' Retrieve the colorization lines
    ' and match a colorization line to each text line, even if there is no colorization.
    ' Note: There is no colorization line for the integrated_mode.

    do while i% <= ubound(result$)
        if _trim$(colorizationCharacter) <> "" and left$(result$(i%),1) = colorizationCharacter then
            redim _preserve activatorArray$(ubound(activatorArray$) + 1)
            dim k%
            k% = ubound(activatorArray$)
            activatorArray$(k%) = left$(mid$(result$(i%)+space$(lastTextLength),2),lastTextLength)
            lastTextLength = 0
        else
            if lastTextLength > 0 then
                redim _preserve activatorArray$(ubound(activatorArray$) + 1)
                k% = ubound(activatorArray$)
                activatorArray$(k%) = space$(lastTextLength)
                lastTextLength = 0
            end if
            redim _preserve textArray$(ubound(textArray$) + 1)
            k% = ubound(textArray$)
            textArray$(k%) = result$(i%)
            lastTextLength = len(textArray$(k%))
        end if
        i% = i% + 1
    loop

    if ubound(activatorArray$)<ubound(textArray$) then
        redim _preserve activatorArray$(ubound(activatorArray$) + 1)
        activatorArray$(ubound(activatorArray$)) = ""
    end if

    ' Generate colorization lines for the integrated_mode

    if _trim$(colorizationCharacter) = "" then
        dim j%, p%
        redim activatorArray$(-1)
        for i% = lbound(textArray$) to ubound(textArray$)
            redim _preserve activatorArray$(ubound(activatorArray$) + 1)
            k% = ubound(activatorArray$)
            activatorArray$(k%) = space$(len(textArray$(i%)))
            for j% = lbound(layoutUtils_colorList) to ubound(layoutUtils_colorList)
                if layoutUtils_colorList(j%).layoutIndex <> layoutIndex then _continue
                do   
                    p% = instr(1, textArray$(i%), layoutUtils_colorList(j%).activatorChar)
                    if p% = 0 then exit do
                    mid$(textArray$(i%),p%,1) = " "
                    mid$(activatorArray$(k%),p%,1) = layoutUtils_colorList(j%).activatorChar
                loop
            next j%
        next i%
    end if

    ' Update the list of colorization activators (determine the line
    ' and column of each activator as well as the color it activates).

    dim position%, s$
    position% = 0
    for i%=lbound(activatorArray$) to ubound(activatorArray$)
        for p% = 1 to len(activatorArray$(i%))
            s$ = mid$(activatorArray$(i%),p%,1)
            if s$ <> " " then
                for j% = lbound(layoutUtils_colorList) to ubound(layoutUtils_colorList)
                    if layoutUtils_colorList(j%).layoutIndex = layoutIndex and _
                        layoutUtils_colorList(j%).activatorChar = s$ then
                        redim _preserve layoutUtils_activatorList(ubound(layoutUtils_activatorList) + 1) as layoutUtils_activatorType
                        layoutUtils_activatorList(ubound(layoutUtils_activatorList)).layoutIndex = layoutUtils_colorList(j%).layoutIndex
                        layoutUtils_activatorList(ubound(layoutUtils_activatorList)).position = position% + p% '+ linePosition%
                        layoutUtils_activatorList(ubound(layoutUtils_activatorList)).colorValue = layoutUtils_colorList(j%).value
                        layoutUtils_activatorList(ubound(layoutUtils_activatorList)).activatorChar = layoutUtils_colorList(j%).activatorChar
                        layoutUtils_activatorList(ubound(layoutUtils_activatorList)).row = i% + 1
                        layoutUtils_activatorList(ubound(layoutUtils_activatorList)).column = p%
                    end if
                next j%
            end if
        next p%
        position% = position% + len(activatorArray$(i%)) + 1
    next i%

    ' Aggregate the extracted text lines into a single string with line breaks.

    layoutUtils_join textArray$(), lbound(textArray$), ubound(textArray$), _STR_NAT_EOL, content

end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_printWithoutColor
' Purpose  : Prints the resolved layout content without any colorization.
'
' Parameters:
'  layoutName - (string) The name of the layout to print.
'
' Description:
'  This subroutine retrieves the populated (resolved) content of the specified
'  layout and prints it directly to the screen, without applying any color formatting.
'------------------------------------------------------------------------------
sub layoutUtils_printWithoutColor (layoutName as string)
    print layoutUtils_getPopulated(layoutName)
end sub

'------------------------------------------------------------------------------
' Function: layoutUtils_isColorized%
' Purpose : Determines if a layout has colorization information.
'
' Parameters:
'  layoutIndex - (integer) The index of the layout in layoutUtils_layoutList.
'
' Returns:
'  (integer) 1 if the layout has colorization data, 0 otherwise.
'
' Description:
'  This function checks the layoutUtils_colorList array for any entries
'  associated with the specified layout index. If at least one color entry
'  is found, it returns 1; otherwise, it returns 0.
'------------------------------------------------------------------------------
function layoutUtils_isColorized%(layoutIndex)
    layoutUtils_isColorized = 0
    dim i%
    for i% = lbound(layoutUtils_colorList) to ubound(layoutUtils_colorList)
        if layoutUtils_colorList(i%).layoutIndex = layoutIndex then
            layoutUtils_isColorized = 1
            exit function
        end if
    next i%
end function

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_printWithColors
' Purpose  : Prints the resolved layout content with colorization applied.
'
' Parameters:
'  layoutName - (string) The name of the layout to print.
'
' Description:
'  This subroutine retrieves the populated (resolved) content of the specified
'  layout and prints it to the screen, applying color formatting based on the
'  colorization and activator information extracted from the layout. If the
'  layout is not colorized, it falls back to printing without color.
'------------------------------------------------------------------------------
sub layoutUtils_printWithColors (layoutName as string)
    dim layoutIndex
    layoutIndex = layoutUtils_find(layoutName)
    if layoutIndex = 0 then exit sub
    if layoutUtils_isColorized(layoutIndex) = 0 then
        layoutUtils_printWithoutColor layoutName
        exit sub
    end if
    dim text as string
    text = layoutUtils_getPopulated(layoutName)
    dim position%
    position% = 1
    dim i%, s$, j%, t$
    dim currentRow%, currentColumn%, currentColor&
    currentRow% = 1
    currentColumn% = 1
    currentColor& = &HFFFFFFFF
    for i% = lbound(layoutUtils_activatorList) to ubound(layoutUtils_activatorList)
        if layoutUtils_activatorList(i%).layoutIndex = layoutIndex then
            s$ = mid$(text,position%,layoutUtils_activatorList(i%).position-position%) 
            layoutUtils_cutAndPrintWithColors s$, currentColor&, currentRow%, currentColumn%
            position% = layoutUtils_activatorList(i%).position 
            currentColor& = layoutUtils_activatorList(i%).colorValue
            currentRow% = layoutUtils_activatorList(i%).row
            currentColumn% = layoutUtils_activatorList(i%).column
        end if
    next i%
    s$ = mid$(text,position%)
    layoutUtils_cutAndPrintWithColors s$, currentColor&, currentRow%, currentColumn%
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_cutAndPrintWithColors
' Purpose  : Prints a string segment with colorization, handling line breaks and cursor position.
'
' Parameters:
'  s$          - (string) The string segment to print (may contain line breaks).
'  currentColor&- (long)  The color attribute to use for printing.
'  currentRow%  - (integer) The starting row position for printing.
'  currentColumn%- (integer) The starting column position for printing.
'
' Description:
'  This subroutine prints the provided string segment s$ to the screen, applying
'  the specified color and starting at the given row and column. It handles
'  line breaks (_STR_NAT_EOL) by moving the cursor to the next line and resetting
'  the column. Each line is printed with the current color.
'------------------------------------------------------------------------------
sub layoutUtils_cutAndPrintWithColors (s$, currentColor&, currentRow%, currentColumn%)
    dim j%, t$
    do   
        j% = instr(1,s$,_STR_NAT_EOL)
        if j% = 0 then
            color currentColor&
            locate currentRow%, currentColumn%
            print s$
            exit do
        else
            color currentColor&
            locate currentRow%, currentColumn%
            t$ = left$(s$,j%-1)
            print t$
            currentRow% = currentRow% + 1
            currentColumn% = 1
            s$ = right$(s$,len(s$)-j%)
        end if
    loop
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_join
'
' Concatenates elements from a string array (source$) into a single string
' (destination$), joining elements from index beginning% to ending% (inclusive)
' with a specified delimiter$ between each element.
'
' Parameters:
'  source$()    - The array of strings to join.
'  beginning%    - The starting index in the array (inclusive).
'  ending%      - The ending index in the array (inclusive).
'  delimiter$    - The string to insert between each joined element.
'  destination$  - The resulting concatenated string (output parameter).
'
' Example:
'  dim arr$(1 to 3)
'  arr$(1) = "apple": arr$(2) = "banana": arr$(3) = "cherry"
'  call layoutUtils_join(arr$(), 1, 3, ", ", result$)
'  ' result$ will be "apple, banana, cherry, "
'------------------------------------------------------------------------------
sub layoutUtils_join (source$(), beginning%, ending%, delimiter$,destination$)
    destination$ = ""
    dim i%
    for i% = beginning% to ending%
        destination$ = destination$ + source$(i%) + delimiter$
    next i%
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_split
' Purpose  : Splits a string into an array of substrings using a specified delimiter.
'
' Parameters:
'  in$        - (string) The input string to split.
'  delimiter$ - (string) The delimiter to split the string on.
'  result$()  - (string array, output) The resulting array of substrings.
'
' Description:
'  This subroutine splits the input string in$ into substrings wherever the delimiter$
'  occurs, and stores the substrings in result$(). Leading delimiters are skipped.
'  The result$ array is dynamically resized to fit the number of substrings found.
'------------------------------------------------------------------------------
Sub layoutUtils_split (in$, delimiter$, result$())
    ReDim result$(-1)
    dim start, finish
    start = 1
    Do
        While Mid$(in$, start, 1) = delimiter$
            start = start + 1
            If start > Len(in$) Then Exit Sub
        Wend
        finish = InStr(start, in$, delimiter$)
        If finish = 0 Then finish = Len(in$) + 1
        ReDim _Preserve result$(0 To UBound(result$) + 1)
        result$(UBound(result$)) = Mid$(in$, start, finish - start)
        start = finish + 1
    Loop While start <= Len(in$)
End Sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_clean
' Purpose  : Removes trailing carriage return characters (CHR$(13)) from each string in the array.
'
' Parameters:
'  result$() - (string array) The array of strings to clean in-place.
'
' Description:
'  This subroutine iterates through the result$() array and, for each element,
'  checks if the last character is a carriage return (CHR$(13)). If so, it removes
'  the trailing character. This is useful for cleaning up lines read from files or DATA
'  statements that may end with CRLF line endings.
'------------------------------------------------------------------------------
sub layoutUtils_clean (result$())
    dim i%
    for i% = lbound(result$) to ubound(result$)
        if right$(result$(i%),1) = chr$(13) then
            result$(i%) = left$(result$(i%),len(result$(i%))-1)
        end if 
    next i%
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_dumpLayoutList
' Purpose  : Logs the contents of the layoutUtils_layoutList array for debugging.
'
' Description:
'  This subroutine iterates through the layoutUtils_layoutList array and logs
'  each layout's index, id, name, full path, content, resolved content, and
'  preparation status. Useful for inspecting the state of all loaded layouts.
'------------------------------------------------------------------------------
sub layoutUtils_dumpLayoutList ()
    dim currentDest&
    currentDest& = _dest
    $CONSOLE
    _dest _console
    print "==========================="
    print "dump layoutUtils_layoutList"
    print "==========================="
    dim i%
    for i% = lbound(layoutUtils_layoutList) to ubound(layoutUtils_layoutList)
        print _
            str$(i%) + ":" + _
            str$(layoutUtils_layoutList(i%).id) + ":" + _
            layoutUtils_layoutList(i%).layoutName + ":" + _
            layoutUtils_layoutList(i%).fullpath + ":" + _
            layoutUtils_layoutList(i%).content + ":" + _
            layoutUtils_layoutList(i%).resolved + ":" + _
            str$(layoutUtils_layoutList(i%).prepared)
    next i%
    _dest currentDest&
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_dumpElementList
' Purpose  : Logs the contents of the layoutUtils_elementList array for debugging.
'
' Description:
'  This subroutine iterates through the layoutUtils_elementList array and logs
'  each element's index, layout index, position, length, and reference name.
'  Useful for inspecting the state of all extracted references.
'------------------------------------------------------------------------------
sub layoutUtils_dumpElementList ()
    dim currentDest&
    currentDest& = _dest
    $CONSOLE
    _dest _console
    print "============================"
    print "dump layoutUtils_elementList"
    print "============================"
    print "n: layoutIndex: position: length: reference"
    dim i%
    for i% = lbound(layoutUtils_elementList) to ubound(layoutUtils_elementList)
        print _
            str$(i%) + ":" + _
            str$(layoutUtils_elementList(i%).layoutIndex) + ":" +  _
            str$(layoutUtils_elementList(i%).position) + ":" + _
            str$(layoutUtils_elementList(i%).length) + ":" + _
            layoutUtils_elementList(i%).reference
    next i%
    _dest currentDest&
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_dumpActivatorList
' Purpose  : Logs the contents of the layoutUtils_activatorList array for debugging.
'
' Description:
'  This subroutine iterates through the layoutUtils_activatorList array and logs
'  each activator's index, layout index, position, activator character, color value,
'  row, and column. Useful for inspecting the state of all color activators extracted
'  from layouts.
'------------------------------------------------------------------------------
sub layoutUtils_dumpActivatorList ()
    dim currentDest&
    currentDest& = _dest
    $CONSOLE
    _dest _console
    print "=============================="
    print "dump layoutUtils_activatorList"
    print "=============================="
    print "n: layoutIndex: position: activatorChar: colorValue: row: column"
    dim i%
    for i% = lbound(layoutUtils_activatorList) to ubound(layoutUtils_activatorList)
        print _
            str$(i%) + ":" + _
            str$(layoutUtils_activatorList(i%).layoutIndex) + ":" +  _
            str$(layoutUtils_activatorList(i%).position) + ":" + _
            layoutUtils_activatorList(i%).activatorChar + ":" + _
            str$(layoutUtils_activatorList(i%).colorValue) + ":" + _
            str$(layoutUtils_activatorList(i%).row) + ":" + _
            str$(layoutUtils_activatorList(i%).column)
    next i%
    _dest currentDest&
end sub

'------------------------------------------------------------------------------
' Subroutine: layoutUtils_dumpColorList
' Purpose  : Logs the contents of the layoutUtils_colorList array for debugging.
'
' Description:
'  This subroutine iterates through the layoutUtils_colorList array and logs
'  each color's index, layout index, color value, and activator character.
'  Useful for inspecting the state of all color definitions extracted from layouts.
'------------------------------------------------------------------------------
sub layoutUtils_dumpColorList ()
    dim currentDest&
    currentDest& = _dest
    $CONSOLE
    _dest _console
    print "=============================="
    print "dump layoutUtils_colorList"
    print "=============================="
    print "n: layoutIndex: value: activatorChar"
    dim i%
    for i% = lbound(layoutUtils_colorList) to ubound(layoutUtils_colorList)
        print _
            str$(i%) + ":" + _
            str$(layoutUtils_colorList(i%).layoutIndex) + ":" +  _
            str$(layoutUtils_colorList(i%).value) + ":" + _
            layoutUtils_colorList(i%).activatorChar
    next i%
    _dest currentDest&
end sub

layoutUtilsTesterFunctional.bas
Code: (Select All)
Option _Explicit

' Include layout utility declarations
'$include: '../library/layoutUtils.bi'

' Create two off‐screen buffers (800×600, 32‐bit) for text and graphics
screen _NewImage(800, 600, 32)
dim as single textDest, grafDest
textDest = _NewImage(800, 600, 32)
grafDest = _NewImage(800, 600, 32)

'=====================================================================
' Define first screen layout (User Information) using DATA statements
'=====================================================================
' use the pointed mode (the colorization activators are on dedicated lines).
LAYOUT1:
    DATA "$","&",&HFF007FFF,"1",&HE0FFFFFF,"2",&HFF00E000,"3",&HFFB0B000,"4","$"
    DATA  "                                                                            "
    DATA  "  +-----------------------------------------------------------------------+"
    DATA "&  2"
    DATA  "  |                          User Information                      <&pg> |"
    DATA "&                              1                                      3    2"
    DATA  "  +-----------------------------------------------------------------------+"
    DATA  "  |                                                                      |"
    DATA  "  |    Last Name          <&lastName.................................>    |"
    DATA "&        1                  4                                              2"
    DATA  "  |    First Name        <&firstName................................>    |"
    DATA "&        1                  4                                              2"
    DATA  "  |                                                                      |"
    DATA  "  |  Address:                                                            |"
    DATA "&      1      2"
    DATA  "  |                                                                      |"
    DATA  "  |    Street Address    <&streetAddress......................>          |"
    DATA "&        1                  4                                              2"
    DATA  "  |    Apartment / Suite  <&apartmentSuite...................>            |"
    DATA "&        1                  4                                              2"
    DATA  "  |    City              <&city...............................>          |"
    DATA "&        1                  4                                              2"
    DATA  "  |    State / Province  <&stateProvince.....................>          |"
    DATA "&        1                  4                                              2"
    DATA  "  |    ZIP / Postal      <&zipPostal.........................>          |"
    DATA "&        1                  4                                              2"
    DATA  "  |    Country            <&country...........................>          |"
    DATA "&        1                  4                                              2"
    DATA  "  |                                                                      |"
    DATA  "  +-----------------------------------------------------------------------+"
    DATA  "                                        [L:list] [N:next] [P:prev] [Q:quit] "

'==================================================================
' Define second screen layout (User List) using DATA statements
'==================================================================
' use the integrated mode (the colorization activators are directly in the text).'
LAYOUT2:
    DATA "$"," ",&HFF007FFF,"#",&HE0A0FFFF,"@",&HFF30E400,"*",&HFFB0B020,"?","$"
    DATA  "                                                                                "
    DATA  "  #+----------------------------------------------------------------------------+"
    DATA  "  |                                @User List                                #|"
    DATA  "  +------------------+------------------+--------------------+-----------------+"
    DATA  "  |@Last Name      #|@First Name      #|@City              #|@State          #|"
    DATA  "  +------------------+------------------+--------------------+-----------------+"
    DATA  "  |?<&last1........>#|?<&first1.......>#|?<&city1..........>#|?<&state1......>#|"
    DATA  "  |?<&last2........>#|?<&first2.......>#|?<&city2..........>#|?<&state2......>#|"
    DATA  "  |?<&last3........>#|?<&first3.......>#|?<&city3..........>#|?<&state3......>#|"
    DATA  "  +------------------+------------------+--------------------+-----------------+"
    DATA  "                                                          [R:return] [Q:quit] "

'==================================================================
' Load screen layouts into memory using layoutUtils library
'==================================================================
dim m1%, m2%
restore layout1
m1% = layoutUtils_loadData ("user", 41) 
restore layout2
m2% = layoutUtils_loadData ("list", 22)

'==================================================================
' Dump shared data to the console
'==================================================================
layoutUtils_dumpLayoutList
layoutUtils_dumpElementList
layoutUtils_dumpActivatorList
layoutUtils_dumpColorList

'==================================================================
' Define sample database entries (3 users, each with 9 fields)
'==================================================================
TEST1:
    DATA "1/3","Smith","John","123 Maple Street","Apt. 4B","Springfield","Illinois","62704","United States"
    DATA "2/3","Doe","Jane","456 Oak Avenue","Suite 12","Madison","Wisconsin","53703","United States"
    DATA "3/3","Brown","Charlie","789 Pine Road","Unit 5","Portland","Oregon","97205","United States"

' Initialize the in‐memory database array and fill it from TEST1
dim db$(1 To 3, 0 To 8)
restore TEST1
dim i%, j%
for i% = 1 to 3
    for j% = 0 to 8
        read db$(i%, j%)
    next j%
next i%

'==================================================================
' Main loop showing the User Information screen
'==================================================================
dim k$, user%
user% = 1  ' Start with the first user

SCREEN1:
    ' Populate the layout placeholders for the current user
    populateLayout1 m1%, db$(), user%
    ' Render text layer into off‐screen buffer
    refreshDestWithLayout "user", textDest

    do
        _limit 60          ' Cap frame rate to ~60 FPS
        _dest grafDest      ' Draw graphics into grafDest
        cls                ' Clear graphics buffer
        drawCircle          ' Draw animated circle behind text
        _putimage , textDest, grafDest  ' Blit text buffer over graphics
        _dest 0            ' Set drawing back to actual screen
        _putimage , grafDest, 0          ' Present final composed image

        select case _keyhit  ' Handle key input
            case 81 or 113  ' Q or q to quit
                system
            case 78 or 110  ' N or n to go to next user
                if user% < 3 then
                    user% = user% + 1
                    exit do
                end if
            case 80 or 112  ' P or p to go to previous user
                if user% > 1 then
                    user% = user% - 1
                    exit do
                end if
            case 76 or 108  ' L or l to show user list
                gosub screen2
                exit do
        end select
    loop

    goto SCREEN1

'==================================================================
' Subroutine to display the User List screen
'==================================================================
SCREEN2:
    ' Fill in the list layout with all database entries
    populateLayout2 "list", db$()
    refreshDestWithLayout "list", textDest

    do
        _limit 60
        _dest grafDest
        cls
        drawTriangle    ' Draw rotating triangle graphic
        _putimage , textDest, grafDest
        _dest 0
        _putimage , grafDest, 0

        select case _keyhit
            case 81 or 113  ' Q or q to quit
                system
            case 82 or 114  ' R or r to return to info screen
                exit do
        end select
    loop

    return  ' Return back to SCREEN1 loop

system  ' Exit the program

'==================================================================
' Subroutine: populateLayout1
' Fills placeholders in the User Information layout
'==================================================================
sub populateLayout1 (layoutId as integer, database() as string, user as integer)
    layoutUtils_resetIndex layoutId
    layoutUtils_populateIndex layoutId, "pg", database(user, 0)
    layoutUtils_populateIndex layoutId, "lastName", database(user, 1)
    layoutUtils_populateIndex layoutId, "firstName", database(user, 2)
    layoutUtils_populateIndex layoutId, "streetAddress", database(user, 3)
    layoutUtils_populateIndex layoutId, "apartmentSuite", database(user, 4)
    layoutUtils_populateIndex layoutId, "city", database(user, 5)
    layoutUtils_populateIndex layoutId, "stateProvince", database(user, 6)
    layoutUtils_populateIndex layoutId, "zipPostal", database(user, 7)
    layoutUtils_populateIndex layoutId, "country", database(user, 8)
end sub

'==================================================================
' Subroutine: populateLayout2
' Fills placeholders in the User List layout
'==================================================================
sub populateLayout2 (layoutName as string, database() as string)
    layoutUtils_reset layoutName
    layoutUtils_populate layoutName, "last1", database(1, 1)
    layoutUtils_populate layoutName, "first1", database(1, 2)
    layoutUtils_populate layoutName, "city1", database(1, 5)
    layoutUtils_populate layoutName, "state1", database(1, 6)
    layoutUtils_populate layoutName, "last2", database(2, 1)
    layoutUtils_populate layoutName, "first2", database(2, 2)
    layoutUtils_populate layoutName, "city2", database(2, 5)
    layoutUtils_populate layoutName, "state2", database(2, 6)
    layoutUtils_populate layoutName, "last3", database(3, 1)
    layoutUtils_populate layoutName, "first3", database(3, 2)
    layoutUtils_populate layoutName, "city3", database(3, 5)
    layoutUtils_populate layoutName, "state3", database(3, 6)
end sub

'==================================================================
' Subroutine: refreshDestWithLayout
' Renders a populated layout into the specified destination buffer
'==================================================================
sub refreshDestWithLayout (layoutName as string, destination as double)
    dim prevDestination
    prevDestination = _dest        ' Remember previous drawing target
    _dest destination              ' Switch to the off‐screen buffer
    cls , _rgba(0,0,0,0)            ' Clear with fully transparent background
    _printmode _KEEPBACKGROUND      ' Preserve any background pixels
    layoutUtils_printWithColors(layoutName)  ' Print layout text
    _dest prevDestination          ' Restore the original drawing target
end sub

'==================================================================
' Subroutine: drawCircle
' Draws an animated pulsing circle at the center of the screen
'==================================================================
sub drawCircle ()
    static growing as integer
    static rFactor as single
    dim cx%, cy%, r%

    cx% = _width / 2
    cy% = _height / 2

    ' Initialize animation parameters on first run
    if rFactor = 0 then
        rFactor = 0.10
        growing = 1
    end if

    ' Pulse the radius factor up and down
    if growing then
        rFactor = rFactor + 0.001
        if rFactor >= 0.48 then growing = 0
    else
        rFactor = rFactor - 0.001
        if rFactor <= 0.10 then growing = 1
    end if

    r% = rFactor * _min(_width, _height)
    circle (cx%, cy%), r%, _rgba(255,255,0,100)
end sub

'==================================================================
' Subroutine: drawTriangle
' Draws a continuously rotating semi‐transparent triangle
'==================================================================
sub drawTriangle ()
    static angleOffset#
    angleOffset# = angleOffset# - _pi / 180  ' Decrease rotation by 1° each frame

    dim cx%, cy%, r%
    cx% = _width / 2
    cy% = _height / 2
    r% = 0.48 * _min(_width, _height)

    ' Calculate the three vertex angles
    dim angle1#, angle2#, angle3#
    angle1# = angleOffset#
    angle2# = angleOffset# + 2 * _pi / 3
    angle3# = angleOffset# + 4 * _pi / 3

    ' Compute vertex coordinates
    dim x1%, y1%, x2%, y2%, x3%, y3%
    x1% = cx% + r% * cos(angle1#)
    y1% = cy% - r% * sin(angle1#)
    x2% = cx% + r% * cos(angle2#)
    y2% = cy% - r% * sin(angle2#)
    x3% = cx% + r% * cos(angle3#)
    y3% = cy% - r% * sin(angle3#)

    ' Draw triangle edges
    line (x1%, y1%)-(x2%, y2%), _rgba(0,255,0,100)
    line (x2%, y2%)-(x3%, y3%), _rgba(0,255,0,100)
    line (x3%, y3%)-(x1%, y1%), _rgba(0,255,0,100)
end sub

' Include the binary module for layoutUtils implementation
'$include: '../library/layoutUtils.bm'
Reply


Messages In This Thread
layoutUtils (library for text layout) - by Herve - 07-14-2025, 09:10 PM
RE: layoutUtils (library for text layout) - by Herve - 07-19-2025, 12:37 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  Format Library eoredson 8 498 02-28-2026, 03:31 AM
Last Post: eoredson
  SPV Video library. ahenry3068 10 674 01-21-2026, 06:46 PM
Last Post: ahenry3068
  Large 2D Graphics Library TarotRedhand 6 2,649 12-05-2023, 05:17 AM
Last Post: grymmjack
  Huge Matrices Library [Updated] TarotRedhand 8 2,684 05-17-2022, 11:42 AM
Last Post: TarotRedhand

Forum Jump:


Users browsing this thread: 1 Guest(s)