Skip to content

Instantly share code, notes, and snippets.

@SKaplanOfficial
Created November 15, 2022 15:30
Show Gist options
  • Save SKaplanOfficial/6b800a01f591a369f0c8f1c95fe11c65 to your computer and use it in GitHub Desktop.
Save SKaplanOfficial/6b800a01f591a369f0c8f1c95fe11c65 to your computer and use it in GitHub Desktop.
PyXA script for mimicking Window's window snapping feature on macOS
# Tested with PyXA 0.1.1
import PyXA
from enum import Enum
class LockPosition(Enum):
NONE = 0
LOCK_TOP_LEFT = 1
LOCK_TOP = 2
LOCK_TOP_RIGHT = 3
LOCK_RIGHT = 4
LOCK_BOTTOM_RIGHT = 5
LOCK_BOTTOM = 6
LOCK_BOTTOM_LEFT = 7
LOCK_LEFT = 8
class AnimationState(Enum):
FROZEN = 0
ANIMATING = 2
window_lock = LockPosition.NONE #: The current window position
animation_state = AnimationState.FROZEN #: The current stage of animation
MENUBAR_HEIGHT = 44 #: The pixel height of the menu bar; the minimum valid y value of a window
EASING = 0.6 #: Controls the animation speed
finder = PyXA.Application("Finder")
screen = finder.desktop.window.bounds
window = finder.windows()[0]
(target_x, target_y, target_width, target_height) = (None, None, None, None)
(previous_x, previous_y, previous_width, previous_height) = window.bounds
while True:
(x, y, width, height) = window.bounds
if animation_state == AnimationState.FROZEN:
# Only switch lock states while not animating
if window_lock == LockPosition.NONE:
# Only enter a new lock state once the previous one ends (returns to NONE)
(previous_x, previous_y, previous_width, previous_height) = (x, y, width, height)
if y < 50:
target_y = MENUBAR_HEIGHT
animation_state = AnimationState.ANIMATING
if x < 100:
# Top-left window quarter
target_x = 0
target_width = screen.width / 2
target_height = screen.height / 2
window_lock = LockPosition.LOCK_TOP_LEFT
elif x > screen.width - width - 100:
# Top-right window quarter
target_x = screen.width / 2
target_width = screen.width / 2
target_height = screen.height / 2
window_lock = LockPosition.LOCK_TOP_RIGHT
elif x > 100 and x < screen.width - width - 100:
# Full-height window panel
target_height = screen.height - MENUBAR_HEIGHT
window_lock = LockPosition.LOCK_TOP
elif y > screen.height - MENUBAR_HEIGHT - height - 50:
if x < 100:
# Bottom-left window quart
target_x = 0
target_y = screen.height / 2
target_width = screen.width / 2
target_height = screen.height / 2
animation_state = AnimationState.ANIMATING
window_lock = LockPosition.LOCK_BOTTOM_LEFT
elif x > screen.width - width - 100:
# Bottom-right window quarter
target_x = screen.width / 2
target_y = screen.height / 2
target_width = screen.width / 2
target_height = screen.height / 2
animation_state = AnimationState.ANIMATING
window_lock = LockPosition.LOCK_BOTTOM_RIGHT
elif x < 50:
# Left window half
target_x = 0
target_y = MENUBAR_HEIGHT
target_width = screen.width / 2
target_height = screen.height - MENUBAR_HEIGHT
animation_state = AnimationState.ANIMATING
window_lock = LockPosition.LOCK_LEFT
elif x > screen.width - width - 50:
# Right window half
target_x = screen.width / 2
target_y = MENUBAR_HEIGHT
target_width = screen.width / 2
target_height = screen.height - MENUBAR_HEIGHT
animation_state = AnimationState.ANIMATING
window_lock = LockPosition.LOCK_RIGHT
# Current in some lock position -- check if we should leave it
elif (window_lock == LockPosition.LOCK_TOP and y > 100):
(target_x, target_y, target_width, target_height) = (x, y, width, previous_height)
animation_state = AnimationState.ANIMATING
window_lock = LockPosition.NONE
elif ((window_lock == LockPosition.LOCK_TOP_LEFT and (x > 100 or y > 100))
or (window_lock == LockPosition.LOCK_TOP_RIGHT and (x < screen.width / 2 - 100 or y > 100))
or (window_lock == LockPosition.LOCK_BOTTOM_LEFT and (x > 100 or y < screen.height / 2 - 100))
or (window_lock == LockPosition.LOCK_BOTTOM_RIGHT and (x < screen.width / 2 - 100 or y < screen.height / 2 - 100))
or (window_lock == LockPosition.LOCK_LEFT and x > 100)
or (window_lock == LockPosition.LOCK_RIGHT and x < screen.width / 2 - 100)):
(target_x, target_y, target_width, target_height) = (x, y, previous_width, previous_height)
animation_state = AnimationState.ANIMATING
window_lock = LockPosition.NONE
# Animate until complete, then restore the frozen state
elif animation_state == AnimationState.ANIMATING:
if target_x is None:
target_x = x
if target_y is None:
target_y = y
if target_width is None:
target_width = width
if target_height is None:
target_height = height
bounds = window.bounds
while abs(bounds.x - target_x) > 2 or abs(bounds.y - target_y) > 2 or abs(bounds.width - target_width) > 2 or abs(bounds.height - target_height) > 2:
new_x = bounds.x + (target_x - bounds.x) * EASING
new_y = bounds.y + (target_y - bounds.y) * EASING
new_width = bounds.width + (target_width - bounds.width) * EASING
new_height = bounds.height + (target_height - bounds.height) * EASING
window.bounds = (new_x, new_y, new_width, new_height)
bounds = window.bounds
(target_x, target_y, target_width, target_height) = (None, None, None, None)
animation_state = AnimationState.FROZEN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment