Howdy folks, I'm sharing this macro to (hopefully) help others who have had issues with the window positioning bug.
See these posts/comments for more information:
- Window resize works inconsistently (dependent on App and Mac) - Questions & Suggestions - Keyboard Maestro Discourse
- Possible Bug: Manipulate a Window Interacts Differently (And Unreliably) With Different Applications - Questions & Suggestions - Keyboard Maestro Discourse
- Getting Started with macOS Shortcuts - #16 by peternlewis
This macro uses several variables passed via the execute a subroutine
action, and then an AppleScript to repeatedly move the target window until it's position matches the coordinates supplied by the variables.
Setup and use is fairly straight-forward and well documented in the macro itself. Special shoutout to @Nige_S for help polishing the AppleScript for version 1.
I used versions 1+ for over a year and a half, and now version 2 for several weeks, and everything has worked quite well. But feel free to supply feedback if for some reason it doesn’t work properly or if you feel it can be improved.
-Chris
Download Macro(s): 16)[MG-SUBRT] Position window (subroutine).kmmacros (21 KB)
Macro-Notes (click to expand/collapse)
- Macros are always disabled when imported into the Keyboard Maestro Editor.
- The user must ensure the macro is enabled.
- The user must also ensure the macro's parent macro-group is enabled.
System Information (click to expand/collapse)
- macOS 13.6
- Keyboard Maestro v11.0.1
AppleScript (click to expand/collapse)
------------------------------------------------------------
# Author : Chris Thomerson (@cdthomer)
#
# Version : 3.0.0 (Now uses bounds to set position/size when possible)
# History : 2.1.0 (Delay is now set from Keyboard Maestro)
# 2.0.1 (Added debugging variables)
# 2.0.0 (Added loop and error handling)
# 1.0.0 (Initial script)
#
# Created : Monday, July 18, 2022
# Modified : Sunday, November 26, 2023
# macOS : 12.6 (Monterey) - macOS 13.4.1 (Ventura)
# Tags : Keyboard Maestro, Window Positioning
#
# PURPOSE
# Reposition a window via a Keyboard Maestro action on
# machines that suffer from the KM repositioning bug
#
# DISCLAIMER
# Permission to use, copy, modify, and/or distribute this
# software for any purpose with or without fee is hereby
# granted.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
# ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
# USE OR PERFORMANCE OF THIS SOFTWARE.
------------------------------------------------------------
# get variables from Keyboard Maestro
set apscDone1 to false
set loopCount1 to 0
repeat until apscDone1 is true or loopCount1 is 15
try
if application "Keyboard Maestro Engine" is not running then
repeat until application "Keyboard Maestro Engine" is running
tell application "Keyboard Maestro Engine" to launch
delay 0.1
end repeat
end if
set kmInst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
set appName to getvariable "instance__App Name" instance kmInst
set windowNames to getvariable "instance__Window Name" instance kmInst
set windowIndex to getvariable "instance__Window Index" instance kmInst
set leftEdge to getvariable "instance__Left Edge" instance kmInst
set topEdge to getvariable "instance__Top Edge" instance kmInst
set width to getvariable "instance__Width" instance kmInst
set height to getvariable "instance__Height" instance kmInst
set commType to getvariable "instance__Command Type" instance kmInst
end tell
set apscDone1 to true
on error
set apscDone1 to false
set loopCount1 to loopCount1 + 1
end try
end repeat
### set variables manually for debugging
set debug to false
if debug is true then
set appName to "OBS"
set leftEdge to "0"
set topEdge to "888"
set width to "1512"
set height to "944"
set windowNames to "Education System"
set windowIndex to "1"
set commType to "Window Index"
end if
set delayInteger to 0.1 as text
set delayTimeout to 10 as integer
set windowFrame to leftEdge & "," & topEdge & "," & (leftEdge + width) & "," & (topEdge + height)
### delimit windowFrame variable
set saveTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {","}
set windowFrame to text items of windowFrame
set AppleScript's text item delimiters to saveTID
### sets window index to integer for use as a variable
set windowIndex to windowIndex as integer
tell application appName
if commType is "Window Name" then # send command to window with specified name
set openWindowNames to name of every window ### get the name of every open window
### extract each window name from the parameter supplied from KM
set saveTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {"|"}
set windowNames to text items of windowNames
set AppleScript's text item delimiters to saveTID
repeat with theWindow in windowNames ### loop through each window in the supplied parameter
try ### try to set bounds using the application's AppleScript
tell (first window whose name contains theWindow) to set its bounds to windowFrame
on error ### if that fails, use System Events to set window position and size
try
tell application "System Events" to tell application process appName to tell (first window whose name contains theWindow)
### set the variables to the proper format
set thePosition to {leftEdge as integer, topEdge as integer} as list
set theSize to {width as integer, height as integer} as list
### position the window
if debug is true then display notification "Setting position"
set loopCount to 0
repeat until its position is thePosition or loopCount is delayTimeout
set its position to thePosition
delay delayInteger
set loopCount to loopCount + 1
end repeat
### size the window
if debug is true then display notification "Setting size"
set loopCount to 0
repeat until its size is theSize or loopCount is delayTimeout
set its size to theSize
delay delayInteger
set loopCount to loopCount + 1
end repeat
end tell
on error
if debug is true then display notification "No windows found matching " & theWindow
end try
end try
end repeat
else if commType is "Window Index" then # send command to window with specified index
try ### try to set bounds using the application's AppleScript
tell window windowIndex to set its bounds to windowFrame
on error ### if that fails, use System Events to set window position and size
if debug is true then display notification "Bounds failed, now trying position and size"
tell application "System Events" to tell application process appName to tell window windowIndex
### set the variables to the proper format
set thePosition to {leftEdge as integer, topEdge as integer} as list
set theSize to {width as integer, height as integer} as list
### position the window
if debug is true then display notification "Setting position"
set loopCount to 0
repeat until its position is thePosition or loopCount is delayTimeout
set its position to thePosition
delay delayInteger
set loopCount to loopCount + 1
end repeat
### size the window
if debug is true then display notification "Setting size"
set loopCount to 0
repeat until its size is theSize or loopCount is delayTimeout
set its size to theSize
delay delayInteger
set loopCount to loopCount + 1
end repeat
end tell
end try
end if
end tell