Functions/GenXdev.Windows/Set-WindowPosition.ps1
################################################################################ <# .SYNOPSIS Positions and resizes windows when explicit positioning parameters are provided. .DESCRIPTION Provides precise control over window positioning and sizing when positioning parameters are specified. Supports multiple monitors, border removal, and various preset positions like left/right split, top/bottom split, and center placement. Windows can be positioned by coordinates or using predefined layouts. Without positioning parameters, the function performs no action on the window. .PARAMETER ProcessName The process name of the window to position .PARAMETER Process Process or processes whose windows need positioning .PARAMETER WindowHelper Window helper object for direct window manipulation .PARAMETER Monitor Monitor selection: 0=primary, 1+=specific monitor, -1=current, -2=secondary .PARAMETER NoBorders Removes window borders and title bar for a cleaner appearance .PARAMETER Width Window width in pixels .PARAMETER Height Window height in pixels .PARAMETER X Window horizontal position in pixels .PARAMETER Y Window vertical position in pixels .PARAMETER Left Places window on left half of screen .PARAMETER Right Places window on right half of screen .PARAMETER Top Places window on top half of screen .PARAMETER Bottom Places window on bottom half of screen .PARAMETER Centered Centers window on screen .PARAMETER Fullscreen Maximizes window to fill entire screen .PARAMETER RestoreFocus Returns focus to PowerShell window after positioning .PARAMETER PassThru Returns window helper object for further manipulation .PARAMETER SideBySide Will either set the window fullscreen on a different monitor than Powershell, or side by side with Powershell on the same monitor .PARAMETER FocusWindow Focus the window after positioning .PARAMETER SetForeground Set the window to foreground after positioning .PARAMETER Maximize Maximize the window after positioning .PARAMETER KeysToSend Keystrokes to send to the window after positioning .PARAMETER SendKeyEscape Escape control characters and modifiers when sending keys .PARAMETER SendKeyHoldKeyboardFocus Hold keyboard focus on target window when sending keys .PARAMETER SendKeyUseShiftEnter Use Shift+Enter instead of Enter when sending keys .PARAMETER SendKeyDelayMilliSeconds Delay between different input strings in milliseconds when sending keys .PARAMETER SessionOnly Switch to use alternative settings stored in session for AI preferences .PARAMETER ClearSession Switch to clear alternative settings stored in session for AI preferences .PARAMETER SkipSession Switch to store settings only in persistent preferences without affecting session .EXAMPLE Set-WindowPosition -Centered -Monitor 0 -NoBorders Position PowerShell window centered on primary monitor with no borders .EXAMPLE Get-Process notepad,calc | wp -m 1 -l,-r Split notepad and calc side by side on second monitor using aliases .EXAMPLE Set-WindowPosition -ProcessName notepad Does nothing - no positioning parameters specified .EXAMPLE Set-WindowPosition -ProcessName notepad -KeysToSend "Hello World" Sends keystrokes to notepad window without repositioning it #> ################################################################################ function Set-WindowPosition { [CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true)] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [Alias('wp')] param( ######################################################################## [parameter( ParameterSetName = 'ProcessName', Mandatory = $false, Position = 0, HelpMessage = 'The process name of the window to position', ValueFromPipeline, ValueFromPipelineByPropertyName, ValueFromRemainingArguments = $false )] [SupportsWildcards()] [Alias('Name')] [string] $ProcessName, ######################################################################## [parameter( ParameterSetName = 'Process', Mandatory = $false, HelpMessage = 'The process of the window to position', ValueFromPipeline, ValueFromPipelineByPropertyName, ValueFromRemainingArguments = $false )] [ValidateNotNull()] [System.Diagnostics.Process] $Process, ######################################################################## [parameter( ParameterSetName = 'WindowHelper', Mandatory = $false, HelpMessage = 'Get-Window helper object for direct window manipulation', ValueFromPipeline, ValueFromPipelineByPropertyName, ValueFromRemainingArguments = $false )] [ValidateNotNull()] [GenXdev.Helpers.WindowObj[]] $WindowHelper, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Monitor selection: 0=primary, 1+=specific monitor, -1=current, -2=secondary' )] [Alias('m', 'mon')] [int] $Monitor = -1, ######################################################################## [Alias('nb')] [parameter( Mandatory = $false, HelpMessage = 'Removes the borders of the window' )] [switch] $NoBorders, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Window width in pixels' )] [int] $Width = -999999, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Window height in pixels' )] [int] $Height = -999999, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Window horizontal position in pixels' )] [int] $X = -999999, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Window vertical position in pixels' )] [int] $Y = -999999, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the left side of the screen' )] [switch] $Left, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the right side of the screen' )] [switch] $Right, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the top side of the screen' )] [switch] $Top, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window on the bottom side of the screen' )] [switch] $Bottom, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Place window in the center of the screen' )] [switch] $Centered, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Maximize the window' )] [Alias('fs')] [switch] $Fullscreen, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Restore PowerShell window focus' )] [Alias('rf', 'bg')] [switch]$RestoreFocus, ######################################################################## [parameter( Mandatory = $false, HelpMessage = 'Returns the window helper for each process' )] [Alias('pt')] [switch]$PassThru, ######################################################################## [parameter( Mandatory = $false, HelpMessage = ('Will either set the window fullscreen on a different ' + 'monitor than Powershell, or side by side with Powershell on the ' + 'same monitor') )] [Alias('sbs')] [switch]$SideBySide, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Focus the window after opening' )] [Alias('fw','focus')] [switch] $FocusWindow, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Set the window to foreground after opening' )] [Alias('fg')] [switch] $SetForeground, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Maximize the window after positioning' )] [switch] $Maximize, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Keystrokes to send to the Window, ' + 'see documentation for cmdlet GenXdev.Windows\Send-Key') )] [string[]] $KeysToSend, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Escape control characters and modifiers when sending keys' )] [Alias('Escape')] [switch] $SendKeyEscape, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Hold keyboard focus on target window when sending keys' )] [Alias('HoldKeyboardFocus')] [switch] $SendKeyHoldKeyboardFocus, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = 'Use Shift+Enter instead of Enter when sending keys' )] [Alias('UseShiftEnter')] [switch] $SendKeyUseShiftEnter, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Delay between different input strings in ' + 'milliseconds when sending keys') )] [Alias('DelayMilliSeconds')] [int] $SendKeyDelayMilliSeconds, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Use alternative settings stored in session for AI ' + 'preferences') )] [switch] $SessionOnly, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Clear alternative settings stored in session for AI ' + 'preferences') )] [switch] $ClearSession, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = ('Store settings only in persistent preferences without ' + 'affecting session') )] [Alias('FromPreferences')] [switch] $SkipSession ######################################################################## ) begin { # store reference to bound parameters for later use $myPSBoundParameters = $PSBoundParameters # store reference to powershell window for focus restoration $powerShellWindow = GenXdev.Windows\Get-PowershellMainWindow # Warn if not running as admin/backup operator $hasElevated = $false try { $hasElevated = GenXdev.Windows\CurrentUserHasElevatedRights } catch { $hasElevated = $false } if ($hasElevated -ne $true) { Microsoft.PowerShell.Utility\Write-Warning "WARNING: Due to missing administrator rights, only windows created by the current PowerShell process can be positioned." } # determine which process to work with based on parameter set switch ($PSCmdlet.ParameterSetName) { 'ProcessName' { Microsoft.PowerShell.Utility\Write-Verbose 'ParameterSetName: ProcessName' # get processes by name $foundProcess = Microsoft.PowerShell.Management\Get-Process ` -Name $ProcessName ` -ErrorAction SilentlyContinue | Microsoft.PowerShell.Core\Where-Object ` -Property 'MainWindowHandle' ` -NE 0 | Microsoft.PowerShell.Utility\Sort-Object ` -Property 'StartTime' ` -Descending | Microsoft.PowerShell.Utility\Select-Object ` -First 1 if ($null -eq $foundProcess) { Microsoft.PowerShell.Utility\Write-Verbose "No process found with name '$ProcessName' (ProcessName set)" Microsoft.PowerShell.Utility\Write-Error ('No process found with ' + "name '$ProcessName'") } else { Microsoft.PowerShell.Utility\Write-Verbose "Process found: $($foundProcess.ProcessName) with ID $($foundProcess.Id) (ProcessName set)" $Process = $foundProcess Microsoft.PowerShell.Utility\Write-Verbose ('Found process: ' + "$($Process.ProcessName) with ID $($Process.Id)") } break; } 'Process' { Microsoft.PowerShell.Utility\Write-Verbose 'ParameterSetName: Process' # get process by id first $foundProcess = Microsoft.PowerShell.Management\Get-Process ` -Id ($Process.Id) ` -ErrorAction SilentlyContinue | Microsoft.PowerShell.Core\Where-Object ` -Property 'MainWindowHandle' ` -NE 0 if ($null -eq $foundProcess) { Microsoft.PowerShell.Utility\Write-Verbose "No process found by Id $($Process.Id), trying by Name $($Process.Name) (Process set)" # fallback to process by name $foundProcess = Microsoft.PowerShell.Management\Get-Process ` -Name ($Process.Name) ` -ErrorAction SilentlyContinue | Microsoft.PowerShell.Core\Where-Object ` -Property 'MainWindowHandle' ` -NE 0 if ($null -eq $foundProcess) { Microsoft.PowerShell.Utility\Write-Verbose "No process found with name '$($Process.Name)' (Process set)" Microsoft.PowerShell.Utility\Write-Error ('No process found with ' + "name '$($Process.Name)'") $Process = $null } else { Microsoft.PowerShell.Utility\Write-Verbose "Process found by Name: $($foundProcess.ProcessName) with ID $($foundProcess.Id) (Process set)" } } else { Microsoft.PowerShell.Utility\Write-Verbose "Process found by Id: $($foundProcess.ProcessName) with ID $($foundProcess.Id) (Process set)" $Process = $foundProcess Microsoft.PowerShell.Utility\Write-Verbose ('Found process: ' + "$($Process.ProcessName) with ID $($Process.Id)") } break; } 'WindowHelper' { Microsoft.PowerShell.Utility\Write-Verbose 'ParameterSetName: WindowHelper' # get processes from window helper handles $foundProcess = Microsoft.PowerShell.Management\Get-Process ` -ErrorAction SilentlyContinue | Microsoft.PowerShell.Core\Where-Object ` -Property MainWindowHandle ` -EQ $WindowHelper.Handle if ($null -eq $foundProcess) { Microsoft.PowerShell.Utility\Write-Verbose "No process found with window handle '$($WindowHelper.Handle)' (WindowHelper set)" Microsoft.PowerShell.Utility\Write-Error ('No process found with ' + "window handle '$($WindowHelper.Handle)'") } else { Microsoft.PowerShell.Utility\Write-Verbose "Process found by WindowHelper: $($foundProcess.ProcessName) with ID $($foundProcess.Id) (WindowHelper set)" $Process = $foundProcess Microsoft.PowerShell.Utility\Write-Verbose ('Found process: ' + "$($Process.ProcessName) with ID $($Process.Id)") } break; } default { Microsoft.PowerShell.Utility\Write-Verbose 'ParameterSetName: default (using PowerShell main window process)' # default to powershell main window process $Process = (GenXdev.Windows\Get-PowershellMainWindowProcess) break; } } } process { # get primary screen and all available screens $screen = $null $allScreens = @([WpfScreenHelper.Screen]::AllScreens | Microsoft.PowerShell.Core\ForEach-Object { $PSItem }) # helper function to restore powershell window focus if requested function refocusTab() { # only proceed if restore focus was requested if ($RestoreFocus -eq $true) { # get current foreground window $currentActiveWindow = [GenXdev.Helpers.WindowObj]::GetFocusedWindow() # check if focus needs to be restored if (($null -ne $powerShellWindow) -and ($powerShellWindow.Handle -ne $currentActiveWindow.Handle)) { # attempt to restore focus $null = $powerShellWindow.SetForeground() # wait for window manager [System.Threading.Thread]::Sleep(250) # verify focus restoration $currentActiveWindow = [GenXdev.Helpers.WindowObj]::GetFocusedWindow() if ($powerShellWindow.Handle -ne $currentActiveWindow.Handle) { try { # fallback to alt-tab if direct focus failed $helper = Microsoft.PowerShell.Utility\New-Object ` -ComObject WScript.Shell $null = $helper.sendKeys('%{TAB}') Microsoft.PowerShell.Utility\Write-Verbose ('Used Alt-Tab to restore ' + 'focus') [System.Threading.Thread]::Sleep(500) } catch { # silently continue if focus restoration fails } } } } } ######################################################################## # process each window that needs positioning if ($null -ne $Process) { Microsoft.PowerShell.Utility\Write-Verbose "Processing window for process: $($Process.ProcessName) (Id: $($Process.Id))" # determine if any positioning parameters are provided $havePositioningParams = ($X -gt -999999) -or ($Y -gt -999999) ` -or ($Width -gt 0) -or ($Height -gt 0) ` -or $Left -or $Right -or $Top -or $Bottom ` -or $Centered -or $Fullscreen -or $SideBySide ` -or $Maximize $havePositioning = $havePositioningParams # get window handle - use powershell window or process main window $window = $WindowHelper ? $WindowHelper : (GenXdev.Windows\Get-Window -ProcessId ($Process.Id)) # detect window's current monitor for comparison $currentWindowScreen = $null if ($null -ne $window) { $currentWindowScreen = [WpfScreenHelper.Screen]::FromPoint(@{X = $window.Position().X; Y = $window.Position().Y }) } if ($Monitor -eq 0) { Microsoft.PowerShell.Utility\Write-Verbose ('Choosing primary monitor, because default monitor requested using -Monitor 0') $Screen = [WpfScreenHelper.Screen]::PrimaryScreen; } elseif (((GenXdev.Windows\Get-MonitorCount) -gt 1) -and $Monitor -eq -2 -and $Global:DefaultSecondaryMonitor -is [int] -and $Global:DefaultSecondaryMonitor -ge 0) { $userMonitorNum = $Global:DefaultSecondaryMonitor $screenIndex = ($Global:DefaultSecondaryMonitor) % $AllScreens.Length Microsoft.PowerShell.Utility\Write-Verbose ('Picking monitor ' + "#$userMonitorNum as secondary (requested with -monitor -2) set by `$Global:DefaultSecondaryMonitor") $Screen = $AllScreens[$screenIndex]; $Monitor = $Global:DefaultSecondaryMonitor; } elseif ($Monitor -eq -2 -and ((GenXdev.Windows\Get-MonitorCount) -gt 1)) { Microsoft.PowerShell.Utility\Write-Verbose ('Picking monitor #1 as default secondary (requested with -monitor -2), because `$Global:DefaultSecondaryMonitor not set') $Screen = $AllScreens[1 % $AllScreens.Length]; } elseif ($Monitor -eq -2) { Microsoft.PowerShell.Utility\Write-Verbose 'Monitor -2 requested but no secondary monitor found, defaulting to primary.' $Monitor = 0 $Screen = [WpfScreenHelper.Screen]::PrimaryScreen; } elseif ($Monitor -ge 1) { Microsoft.PowerShell.Utility\Write-Verbose ('Picking monitor ' + "#$((($Monitor - 1) % $AllScreens.Length)) as requested by the -Monitor parameter") $Screen = $AllScreens[($Monitor - 1) % $AllScreens.Length] } else { Microsoft.PowerShell.Utility\Write-Verbose 'Picking monitor #1 (FromPoint)' $Screen = $currentWindowScreen } # handle side-by-side positioning if ($SideBySide -and ($powerShellWindow.Handle -ne $window.Handle)) { Microsoft.PowerShell.Utility\Write-Verbose 'SideBySide requested and window is not PowerShell main window.' $powershellMonitorIndex = $AllScreens.IndexOf($PowershellScreen)+1 Microsoft.PowerShell.Utility\Write-Verbose 'Window and PowerShell are on the same screen.' $left = $false; $top = $false; $right = $false; $FullScreen = $false; $Centered = $false # split horizontally or vertically based on screen orientation if ($PowershellScreen.WorkingArea.Width -gt $PowershellScreen.WorkingArea.Height) { Microsoft.PowerShell.Utility\Write-Verbose 'Screen is taller than wide, splitting vertically (Bottom).' GenXdev.Windows\Set-WindowPosition -Bottom -Monitor $powershellMonitorIndex $FullScreen = $false $Top = $true $havePositioning = $true; } else { Microsoft.PowerShell.Utility\Write-Verbose 'Screen is wider than tall, splitting horizontally (Left).' GenXdev.Windows\Set-WindowPosition -Left -Monitor $powershellMonitorIndex $FullScreen = $false $right = $true; $havePositioning = $true; } } if ($null -eq $screen) { Microsoft.PowerShell.Utility\Write-Verbose "No screen object set, using window's current monitor as fallback." $screen = $currentWindowScreen ? $currentWindowScreen : $allScreens[0] } # detect if monitor change is being requested $isMonitorChangeRequest = $false if (($Monitor -ge 0) -and ($null -ne $currentWindowScreen) -and ($null -ne $Screen)) { $isMonitorChangeRequest = ($currentWindowScreen.DeviceName -ne $Screen.DeviceName) if ($isMonitorChangeRequest) { Microsoft.PowerShell.Utility\Write-Verbose ("Monitor change detected: Moving window from '$($currentWindowScreen.DeviceName)' to '$($Screen.DeviceName)'") # Monitor change implies positioning is needed if (-not $havePositioningParams) { Microsoft.PowerShell.Utility\Write-Verbose 'No explicit positioning parameters, but monitor change requested - enabling positioning' $havePositioning = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detecting current window position to preserve when moving to new monitor' # Get current window position and size $currentPos = $window.Position() $currentSize = $window.Size() $currentWorkArea = $currentWindowScreen.WorkingArea # Calculate relative position within current monitor's work area $relativeX = ($currentPos.X - $currentWorkArea.X) / $currentWorkArea.Width $relativeY = ($currentPos.Y - $currentWorkArea.Y) / $currentWorkArea.Height $relativeWidth = $currentSize.Width / $currentWorkArea.Width $relativeHeight = $currentSize.Height / $currentWorkArea.Height Microsoft.PowerShell.Utility\Write-Verbose ("Current relative position: X=$([Math]::Round($relativeX, 2)), Y=$([Math]::Round($relativeY, 2)), W=$([Math]::Round($relativeWidth, 2)), H=$([Math]::Round($relativeHeight, 2))") # Detect positioning style based on relative position and size $tolerance = 0.1 # 10% tolerance for position detection $sizeTolerance = 0.4 # 40% minimum size to consider positioned (allows for half-screen at 50%) # Determine primary positioning based on which dimension is more constrained # If width is more constrained (< 90% of screen), check left/right first # If height is more constrained (< 90% of screen), check top/bottom first $widthConstrained = $relativeWidth -lt 0.9 $heightConstrained = $relativeHeight -lt 0.9 # Priority 1: Check the more constrained dimension first if ($widthConstrained -and (-not $heightConstrained)) { # Width is constrained, height spans most/all screen - check left/right if ($relativeWidth -ge $sizeTolerance) { if ($relativeX -le $tolerance) { $Left = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected LEFT positioning (full height) - preserving on new monitor' } elseif (($relativeX + $relativeWidth) -ge (1.0 - $tolerance)) { $Right = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected RIGHT positioning (full height) - preserving on new monitor' } } } elseif ($heightConstrained -and (-not $widthConstrained)) { # Height is constrained, width spans most/all screen - check top/bottom if ($relativeHeight -ge $sizeTolerance) { if ($relativeY -le $tolerance) { $Top = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected TOP positioning (full width) - preserving on new monitor' } elseif (($relativeY + $relativeHeight) -ge (1.0 - $tolerance)) { $Bottom = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected BOTTOM positioning (full width) - preserving on new monitor' } } } elseif ($widthConstrained -and $heightConstrained) { # Both dimensions constrained - check for corner positioning or centered $centerX = $relativeX + ($relativeWidth / 2) $centerY = $relativeY + ($relativeHeight / 2) # Check if window is within 5% of screen boundaries (very relaxed centered detection) $nearLeftEdge = $relativeX -le 0.05 # Left margin <= 5% $nearRightEdge = ($relativeX + $relativeWidth) -ge 0.95 # Right margin <= 5% $nearTopEdge = $relativeY -le 0.05 # Top margin <= 5% $nearBottomEdge = ($relativeY + $relativeHeight) -ge 0.95 # Bottom margin <= 5% # BUT actually, if user wants "5% off boundaries", let's be more generous # Window with 10% margins (like X=0.1, W=0.8) should be considered centered $leftMargin = $relativeX $rightMargin = 1.0 - ($relativeX + $relativeWidth) $topMargin = $relativeY $bottomMargin = 1.0 - ($relativeY + $relativeHeight) # Consider centered if ALL margins are small (≤ 10% for more reasonable detection) $hasSmallMargins = ($leftMargin -le 0.1) -and ($rightMargin -le 0.1) -and ($topMargin -le 0.1) -and ($bottomMargin -le 0.1) Microsoft.PowerShell.Utility\Write-Verbose "Positioning check: centerX=$([Math]::Round($centerX, 2)), centerY=$([Math]::Round($centerY, 2))" Microsoft.PowerShell.Utility\Write-Verbose "Window bounds: X=$([Math]::Round($relativeX, 2)), Y=$([Math]::Round($relativeY, 2)), W=$([Math]::Round($relativeWidth, 2)), H=$([Math]::Round($relativeHeight, 2))" Microsoft.PowerShell.Utility\Write-Verbose "Actual margins: Left=$([Math]::Round($leftMargin, 3)), Right=$([Math]::Round($rightMargin, 3)), Top=$([Math]::Round($topMargin, 3)), Bottom=$([Math]::Round($bottomMargin, 3))" Microsoft.PowerShell.Utility\Write-Verbose "Has small margins (all ≤ 10%): $hasSmallMargins" # If window has small margins on all sides, consider it centered if ($hasSmallMargins) { $Centered = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected CENTERED positioning (small margins on all sides) - preserving on new monitor' } else { # Check for specific edge positioning # Check left/right first if ($relativeWidth -ge $sizeTolerance) { if ($relativeX -le $tolerance) { $Left = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected LEFT positioning - preserving on new monitor' } elseif (($relativeX + $relativeWidth) -ge (1.0 - $tolerance)) { $Right = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected RIGHT positioning - preserving on new monitor' } } # Then check top/bottom if ($relativeHeight -ge $sizeTolerance) { if ($relativeY -le $tolerance) { $Top = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected TOP positioning - preserving on new monitor' } elseif (($relativeY + $relativeHeight) -ge (1.0 - $tolerance)) { $Bottom = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected BOTTOM positioning - preserving on new monitor' } } # If no specific positioning detected, just enable positioning for auto-sizing if ((-not $Left) -and (-not $Right) -and (-not $Top) -and (-not $Bottom)) { Microsoft.PowerShell.Utility\Write-Verbose 'No specific positioning detected - enabling auto-sizing to maximum' } } } else { # Neither dimension particularly constrained - likely fullscreen or very large window # Check margins for large window centered detection $leftMargin = $relativeX $rightMargin = 1.0 - ($relativeX + $relativeWidth) $topMargin = $relativeY $bottomMargin = 1.0 - ($relativeY + $relativeHeight) # For large windows, be even more generous (≤ 15% margins) $hasSmallMargins = ($leftMargin -le 0.15) -and ($rightMargin -le 0.15) -and ($topMargin -le 0.15) -and ($bottomMargin -le 0.15) if (-not $hasSmallMargins) { $Centered = $true Microsoft.PowerShell.Utility\Write-Verbose 'Detected CENTERED positioning (large window with small margins) - preserving on new monitor' } else { Microsoft.PowerShell.Utility\Write-Verbose 'Large window not centered - enabling auto-sizing to maximum' } } } } } # calculate final window coordinates and dimensions if (($X -le -999999) -or ($X -isnot [int])) { Microsoft.PowerShell.Utility\Write-Verbose 'X not provided or invalid, using screen.WorkingArea.X' $X = $Screen.WorkingArea.X; } else { # adjust x position for monitor offset if ($Monitor -ge 0) { Microsoft.PowerShell.Utility\Write-Verbose 'Adjusting X for monitor offset.' $X = $Screen.WorkingArea.X + $X; } } Microsoft.PowerShell.Utility\Write-Verbose "X determined to be $X" # calculate y position if (($Y -le -999999) -or ($Y -isnot [int])) { Microsoft.PowerShell.Utility\Write-Verbose 'Y not provided or invalid, using screen.WorkingArea.Y' $Y = $Screen.WorkingArea.Y; } else { # adjust y position for monitor offset if ($Monitor -ge 0) { Microsoft.PowerShell.Utility\Write-Verbose 'Adjusting Y for monitor offset.' $Y = $Screen.WorkingArea.Y + $Y; } } Microsoft.PowerShell.Utility\Write-Verbose "Y determined to be $Y" # set fullscreen dimensions if requested if ($Fullscreen -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Fullscreen requested, setting Width/Height to screen.WorkingArea.' $Width = $Screen.WorkingArea.Width; $Height = $Screen.WorkingArea.Height; } Microsoft.PowerShell.Utility\Write-Verbose 'Have positioning parameters set' # Reset width/height for smart positioning if detected during monitor change if ($isMonitorChangeRequest -and ($Left -or $Right -or $Top -or $Bottom -or $Centered)) { $Width = -999999 $Height = -999999 Microsoft.PowerShell.Utility\Write-Verbose 'Reset Width/Height to allow smart positioning to control sizing' } # check if width and height were explicitly provided $widthProvided = ($Width -gt 0) -and ($Width -is [int]); $heightProvided = ($Height -gt 0) -and ($Height -is [int]); # use screen width if width not provided if ($widthProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Width not provided, using screen.WorkingArea.Width' $Width = $Screen.WorkingArea.Width; Microsoft.PowerShell.Utility\Write-Verbose ('Width not provided ' + "resetted to $Width") } # use screen height if height not provided if ($heightProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Height not provided, using screen.WorkingArea.Height' $Height = $Screen.WorkingArea.Height; Microsoft.PowerShell.Utility\Write-Verbose ('Height not provided ' + "resetted to $Height") } # apply layout positioning (left/right/top/bottom/centered) if ($Left -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Left positioning requested.' $X = $Screen.WorkingArea.X; # use half width if not explicitly provided if ($widthProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Width not provided for Left, using half screen width.' $Width = [Math]::Min($Screen.WorkingArea.Width / 2, $Width); } Microsoft.PowerShell.Utility\Write-Verbose ("Left chosen, X = $X, Width = $Width") } else { # position on right side if ($Right -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Right positioning requested.' # use half width if not explicitly provided if ($widthProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Width not provided for Right, using half screen width.' $Width = [Math]::Min($Screen.WorkingArea.Width / 2, $Width); } $X = $Screen.WorkingArea.X + $Screen.WorkingArea.Width - $Width; Microsoft.PowerShell.Utility\Write-Verbose ("Right chosen, X = $X, Width = $Width") } } # position on top if ($Top -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Top positioning requested.' $Y = $Screen.WorkingArea.Y; # use half height if not explicitly provided if ($heightProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Height not provided for Top, using half screen height.' $Height = [Math]::Min($Screen.WorkingArea.Height / 2, $Height); } Microsoft.PowerShell.Utility\Write-Verbose ("Top chosen, Y = $Y, Height = $Height") } else { # position on bottom if ($Bottom -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Bottom positioning requested.' # use half height if not explicitly provided if ($heightProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Height not provided for Bottom, using half screen height.' $Height = [Math]::Min($Screen.WorkingArea.Height / 2, $Height); } $Y = $Screen.WorkingArea.Y + $Screen.WorkingArea.Height - $Height; Microsoft.PowerShell.Utility\Write-Verbose ("Bottom chosen, Y = $Y, Height = $Height") } } # center window on screen if ($Centered -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Centered positioning requested.' # use 80% of screen dimensions if not explicitly provided if ($heightProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Height not provided for Centered, using 80% of screen height.' $Height = [Math]::Round([Math]::Min($Screen.WorkingArea.Height * 0.8, $Height), 0); } if ($widthProvided -eq $false) { Microsoft.PowerShell.Utility\Write-Verbose 'Width not provided for Centered, using 80% of screen width.' $Width = [Math]::Round([Math]::Min($Screen.WorkingArea.Width * 0.8, $Width), 0); } # calculate center position $X = $Screen.WorkingArea.X + [Math]::Round(($screen.WorkingArea.Width - $Width) / 2, 0); $Y = $Screen.WorkingArea.Y + [Math]::Round(($screen.WorkingArea.Height - $Height) / 2, 0); Microsoft.PowerShell.Utility\Write-Verbose ("Centered chosen, X = $X, Width = $Width, Y = $Y, Height = $Height") } if ((-not $havePositioning) -and ($KeysToSend -isnot [System.Collections.IEnumerable] -or ($KeysToSend.Count -eq 0))) { return; } # confirm with user if whatif support is enabled if ($PSCmdlet.ShouldProcess( "Window of process '$($Process.ProcessName)'", "Set position/size to: X=$X Y=$Y W=$Width H=$Height")) { Microsoft.PowerShell.Utility\Write-Verbose 'ShouldProcess returned true, proceeding to set window position/size.' # have a handle to the mainwindow of the browser? if ($null -ne $window) { Microsoft.PowerShell.Utility\Write-Verbose ('Restoring and positioning window') # restore window and position it $null = $window.Show() $null = $window.Restore() $null = $window.Show() if ($havePositioning) { Microsoft.PowerShell.Utility\Write-Verbose ("[Set-WindowPosition] About to move window. Handle: $($window.Handle) Target: X=$X, Y=$Y, Width=$Width, Height=$Height") $null = $window.Move($X, $Y, $Width, $Height) $null = $window.Move($X, $Y, $Width, $Height) } # maximize window if fullscreen requested if ($Fullscreen) { Microsoft.PowerShell.Utility\Write-Verbose 'Fullscreen requested, sending F11 keystroke.' $KeysToSend = ($KeysToSend ? $KeysToSend : @()) + @('{F11}') } # needs to be set noborders manually? if ($NoBorders -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Setting NoBorders' $null = $window.RemoveBorder(); } if ($Maximize) { Microsoft.PowerShell.Utility\Write-Verbose 'Maximize requested or no positioning, maximizing window.' $null = $window.Maximize() } # handle focus and foreground at the end before maximize if ($FocusWindow -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Focusing window' $null = $window.Focus() } # set window to foreground if requested if ($SetForeground -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose ('Setting window to foreground') $null = $window.SetForeground() } # send keys if specified, after a delay to ensure window is ready if ($null -ne $KeysToSend -and ($KeysToSend.Count -gt 0)) { Microsoft.PowerShell.Utility\Write-Verbose ('Sending keystrokes to window after 500ms delay') Microsoft.PowerShell.Utility\Start-Sleep -Milliseconds 500 # copy identical parameters between functions $params = GenXdev.Helpers\Copy-IdenticalParamValues ` -FunctionName 'GenXdev.Windows\Send-Key' ` -BoundParameters $myPSBoundParameters ` -DefaultValues (Microsoft.PowerShell.Utility\Get-Variable ` -Scope Local -ErrorAction SilentlyContinue) # set the window handle for the send-key function $params.WindowHandle = $window.Handle $null = $params.Remove('Process') $null = $params.Remove('ProcessName') # send keys to the window Microsoft.PowerShell.Utility\Write-Verbose "Calling Send-Key with parameters: $($params | Microsoft.PowerShell.Utility\Out-String)" $null = GenXdev.Windows\Send-Key @params } } else { Microsoft.PowerShell.Utility\Write-Verbose 'No window object available to position.' } # return window helper if passthru specified if ($PassThru -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'PassThru specified, returning window object.' $window } } else { Microsoft.PowerShell.Utility\Write-Verbose 'ShouldProcess returned false, skipping window positioning.' } } else { Microsoft.PowerShell.Utility\Write-Verbose 'No process object available, skipping window positioning.' } } end { } } ################################################################################ |