Source/Set-GridLayout.ps1

<#
.SYNOPSIS
Sets applications automatically in a grid layout on the screen
 
.DESCRIPTION
 
Sets applications in an automatic grid layout with predefined formats ('Vertical','Horizontal','Mosaic') using the Process object of the target applications
passed as a parameter(-Process) value and also brings the window(s) of the target application(s) to the foreground
 
A custom format can also be used to set the applications in a grid-layout on the display, by passing a custom string as a value to the parameter(-Custom).
 
Run below commands to see some examples
 
    Get-Help Set-GridLayout -Examples
 
.PARAMETER Process
Process(s) objects of the application(s) you want to set in a grid-layout
 
.PARAMETER Vertical
Switch to align application(s) in 'Vertical' grid-layout
 
.PARAMETER Horizontal
Switch to align application(s) in 'Horizontal' grid-layout
 
.PARAMETER Mosaic
Switch to align application(s) in 'Mosaic' grid-layout, that can best fit 'n' number of application(s) in rows
 
.PARAMETER Custom
Accepts a Customizable grid-layout format like '***,**,*,****'
Where '*' represent an application and ',' separates a row in the grid-layout"
So, with custom format = '***,**,*,****' in the grid layout
    Row1 has 3 applications
    Row2 has 2 applications
    Row3 has 1 applications
    Row4 has 4 applications
.PARAMETER IncludeSource
Switch parameter that includes the source process that executes the cmdlet in the th Grid-Layout.
.EXAMPLE
Get-Process notepad | Set-GridLayout
 
Capture all running notepad process objects and set them in 'Mosaic' (Default) grid-layout.
 
.EXAMPLE
$Process = @()
$Process = 1..3 | Foreach {(Start-Process Powershell.exe -WorkingDirectory c:\ -PassThru)}
$Process += 1 | Foreach {(Start-Process notepad.exe -PassThru)}
$Process += 1..5 | Foreach {(Start-Process cmd.exe -WorkingDirectory c:\ -PassThru)}
 
Get the Process objects of target applications,
 
Set-GridLayout -Process $Process
 
then run the below cmdlet to set them in 'Mosaic' (Default) grid-layout.
 
 
.EXAMPLE
Set-GridLayout $Process -layout Vertical
 
Use the 'Layout' parameter set the applications in a 'Vetrical' grid-layout
 
 
.EXAMPLE
Set-GridLayout -Process $Process -Layout Horizontal
 
Use the 'Layout' parameter set the applications in a 'Horizontal' grid-layout
 
 
.EXAMPLE
Set-GridLayout -Process $Process -Custom '**,**,*,****'
 
To set applications is custom grid-layout utilize the 'Custom' parameter and pass the custom layout as comma-separated string of '*' (Astrix)
 
Where '*' represent an application and ',' separates a row in the grid-layout"
So, with custom format = '***,**,*,****' in the grid layout
    Row1 has 3 applications
    Row2 has 2 applications
    Row3 has 1 applications
    Row4 has 4 applications
 
.EXAMPLE
Grid $Process
 
'grid' is an alias of cmdlet Set-GridLayout
 
.EXAMPLE
sgl $Process -Layout Vertical
 
'sgl' is an alias of cmdlet Set-GridLayout
 
.INPUTS
System.Diagnostics.Process
 
.OUTPUTS
None
 
.NOTES
Author : Prateek Singh
TechnologyBlog : http://Geekeefy.wordpress.com
Twitter : http://twitter.com/SinghPrateik
Github : http://github.com/PrateekKumarSingh
LinkedIn : http://in.linkedIn.com/in/PrateekSingh1590
Reddit : http://Reddit.com/user/PrateekSingh1590
Medium : http://Medium.com/@SinghPrateik
 
#>

Function Set-GridLayout {
    [CmdletBinding(
        DefaultParameterSetName = 'default'
    )]
    [Alias("sgl", "grid")]
    Param(
        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Custom',
            ValueFromPipeline=$true,
            Position=0,
            HelpMessage = "Provide a System.Diagnostics.Process[] object"
            )]

        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'default',
            ValueFromPipeline=$true,
            Position=0,
            HelpMessage = "Provide a System.Diagnostics.Process[] object"
        )] [System.Diagnostics.Process[]] $Process,
        [Parameter(ParameterSetName = 'default')] [ValidateSet('Vertical', 'Horizontal', 'Cascade', 'Mosaic')] [string] $Layout='Mosaic',
        [Parameter(ParameterSetName = 'Custom')]
        [ValidateScript({
                            If ($_ -match '^[-\w\s\*]+(?:,[-\w\s\*]+)*$'){$True}
                            Else {
                                Throw "`nAccepted custom grid format is something like -custom '***,**,*,****' `nhere '*' represent an application and ',' separates a row in the grid-layout"
                            }
        })] [String] $Custom,
        [Switch] $IncludeSource
    )

    Add-Type -AssemblyName System.Windows.Forms
    $Monitor = [System.Windows.Forms.Screen]::AllScreens.Where( {$_.Primary -eq $true }).Bounds
    $vRes = $monitor.Height -40  # -40 to make the edges appear above the task bar
    $hRes = $monitor.Width

    # Handling the Layout verbosity
    if($Custom){
        Write-Verbose "Setting Processes in Custom layout"
    }else{
        Write-Verbose "Setting Processes in $Layout layout"
    }

    # Handling the pipeline input with -IncludeSource switch
    if($IncludeSource -and $Input){ # when PipelineInput = $true AND $IncludeSource = $true
        $Process = $Input + $(Get-ParentProcess)
    }
    elseif($IncludeSource -and $Process){ # when PipelineInput = $false AND $IncludeSource = $true
        $Process = $Process + $(Get-ParentProcess)
    }
    elseif($Input -and -not $IncludeSource){ # when PipelineInput = $true AND $IncludeSource = false
        $Process = $Input
    }

    # when PipelineInput = $false AND $IncludeSource = false
    $Count = $Process.Count

    if($Layout -eq 'Vertical'){
        $Height = $vRes
        $Width = $hRes/$Count
        $Position = 0

        $Process | ForEach-Object {
            MoveApplication -Process $_ -X $Position -Y 0 -Height $Height -Width $Width
            $Position += $Width
        }
    }
    elseif($Layout -eq 'Horizontal') {
        $Height = $vRes/$Count
        $Width = $hRes
        $Position = 0

        $Process | ForEach-Object {
            MoveApplication -Process $_ -X 0 -Y $Position -Height $Height -Width $Width
            $Position += $Height
        }
    }
    elseif($Layout -eq 'Cascade') {
        $Height = $vRes/1.5
        $Width = $hRes/1.2
        $X = 40
        $y = 40

        $Process | ForEach-Object {
            MoveApplication -Process $_ -X $X -Y $Y -Height $Height -Width $Width
            $X += 25 ;  $Y += 25
        }
    }
    elseif ($Layout -eq 'Mosaic'){
        $PositionX = 0
        $PositionY = 0
        $Rows = 2 #Row more than 2 won't be able to display data properly, hence made it static
        $even = $Count%2 -eq 0

        If($even){
            $Col = 0
            $Columns = $Count/$Rows
            $Height = $vRes/$Rows
            $Width = $hRes/$Columns

            For($i =0 ;$i -lt $Count;$i++)
            {
                $Col++
                If($col -gt $Columns){
                    $PositionX = 0
                    $Col = 0
                    $PositionY = $Height
                }

                MoveApplication -Process $Process[$i] -X $PositionX -Y $PositionY -Height $Height -Width $Width
                $PositionX += $Width
            }
        }
        else{
            $Col = 0
            $Columns = [math]::Floor($Count/$Rows)
            $Height = $vRes/$Rows
            $Width = $hRes/$Columns

            For($i =0 ;$i -lt $Count;$i++)
            {
                $Col++
                If($col -gt $Columns){
                    ++$Columns
                    $Width = $hRes/$Columns
                    $PositionX = 0
                    $Col = 0
                    $PositionY = $Height
                    MoveApplication -Process $Process[$i] -X $PositionX -Y $PositionY -Height $Height -Width $Width
                }
                else {
                    MoveApplication -Process $Process[$i] -X $PositionX -Y $PositionY -Height $Height -Width $Width
                }
                $PositionX += $Width
            }
        }
    }

    if ($Custom) {
        $AppsPlacedInGrid = 0
        $XCoordinate = 0 ; $YCoordinate = 0
        $RowArray = ($Custom -split ",").foreach({$_.ToCharArray().Where({$_ -eq '*'}) -join ''})
        $Ratio = Get-ApplicationRatio -String $Custom

        $NumberOfRows = $RowArray.count
        $NumberOfApps = ( ($RowArray -join '') -replace ",","").length
        $height = $vRes/$NumberOfRows

        if($NumberOfApps -ne $Process.Count){
            throw "Number of apps = $NumberOfApps in custom grid layout is not equal to the Process Id's passed = $($Process.Count)"
        }
        else{
            # Iterate through each row
            For($i=0;$i -lt $NumberOfRows;$i++){
                $NumberOfAppsInCurrentRow = $RowArray[$i].Length
                $RowApplicationRatioArray =  $Ratio[$i] -split ':'
                #$N = ($RowApplicationRatio -split ':' | Measure-Object -Sum).Sum
                $width = $hRes / $(($RowApplicationRatioArray | Measure-Object -Sum).Sum)
                #$width = $hRes / $NumberOfAppsInCurrentRow

                $j = 0 # counter to access application ratio per row

                # Iterate through ProcessID's
                $Process[$AppsPlacedInGrid..$($AppsPlacedInGrid+$NumberOfAppsInCurrentRow-1)] | ForEach-Object {
                    # Set application in a grid with coordinates, height and width
                    $CurrentAppplicationWidth = $Width * $RowApplicationRatioArray[$j] # base width multiplied by the ratio
                    MoveApplication -Process $_ -X $XCoordinate -Y $YCoordinate -Height $Height -Width $CurrentAppplicationWidth
                    $XCoordinate += $CurrentAppplicationWidth # move the XCoordinate of next app to the width of previous app
                    $AppsPlacedInGrid++
                    $j++ # Next application ratio in the Row
                }
                $YCoordinate += $height # Change YCoordinates for every row
                $XCoordinate = 0 # XCoordinate resets after every row
            }
        }
    }
}