shadow2020
shadow2020

Reputation: 1351

Powershell MultiColored text on single line AND Centered

I have a powershell function that I need to be able to change. The function centers text in the terminal however, I need to be able to output multiple colors for text on a single line. If I do -NoNewLine and do more Write-host to change the color... then it still calculates the width of the terminal and still adds as much padding as it would without me adding -NoNewLine. Essentially I want my text centered and I want to be able to use multiple colors. With what I have I can only do 1 color per line.

function WriteCentered([String] $text, $color = $null)
{
    $width = [int](Get-Host).UI.RawUI.BufferSize.Width
    $twidth = [int]$text.Length
    $offset = ($width / 2) - ($twidth / 2)
    $newText = $text.PadLeft($offset + $twidth)

    if($color)
    {
        Write-Host $newText -ForegroundColor $color
    }        
    else
    {
        Write-Host $newText 
    }


}   

I have added more IF conditions, I have changed my padding calculations, I am having trouble with getting it just right.

Upvotes: 1

Views: 2158

Answers (1)

boris
boris

Reputation: 467

The PowerShell-Module PSWriteColor already does a good job in outputting multiple colors on a single line. Either you download it from GitHub directly and import it with Import-Module <PATH-TO>\PSWriteColor.psd1 or you install it from the PowerShell Gallery directly with Install-Module -Name PSWriteColor.

The syntax in short is Write-Color -Text "GreenText","RedText","BlueText" -Color Green,Red,Blue. So we need to prepend the [String[]]$Text argument with a string containing the necessary whitespace in order to center the message on the screen and prepend a color to the [ConsoleColor[]]$Color argument accordingly.

Here's a little helper function for centering.

#Requires -Modules @{ ModuleName="PSWriteColor"; ModuleVersion="0.8.5" }
function WriteColor-Centered {
param(
    [Parameter(Mandatory=$true)][string[]]$Text,
    [Parameter(Mandatory=$true)][ConsoleColor[]]$Color
)
    $messageLength = 0
    $Text | ForEach-Object { $messageLength += $_.Length }

    [String[]] $centeredText = "{0}" -f (' ' * (([Math]::Max(0, $Host.UI.RawUI.BufferSize.Width / 2) - [Math]::Floor($messageLength / 2))))
    $centeredText += $Text

    [ConsoleColor[]]$OutColor = @([ConsoleColor]::White)
    $OutColor += $Color

    Write-Color -Text $centeredText -Color $OutColor
    # Alt.: use WriteColor-Core, see below
    # WriteColor-Core -Text $centeredText -Color $OutColor
}

I copied the whitespace calculation from this stackoverflow answer.

EDIT: I was being asked if it's possible to make this work without importing the module. To be honest I feel a little dirty now because I went into source code of a well-written module stripped all the functionality and error handling from it and pasted it here.

Well anyways - if you replace the invocation of Write-Color in the wrapper function above and invoke the following WriteColor-Core instead you can dispense with loading the PSWriteColor module.

function WriteColor-Core {
param(
    [Parameter(Mandatory=$true)][string[]]$Text,
    [Parameter(Mandatory=$true)][ConsoleColor[]]$Color
)
    # Fallback defaults if one of the values isn't set
    $LastForegroundColor = [console]::ForegroundColor
    # The real deal coloring
    for ($i = 0; $i -lt $Text.Count; $i++) {
        $CurrentFGColor = if ($Color[$i]) { $Color[$i] } else { $LastForegroundColor }
        $WriteParams = @{
            NoNewLine       = $true
            ForegroundColor = $CurrentFGColor
        }
        Write-Host $Text[$i] @WriteParams
        # Store last color set, in case next iteration doesn't have a set color
        $LastForegroundColor = $CurrentFGColor
    }

    Write-Host
}

Upvotes: 4

Related Questions