Created
November 15, 2022 15:30
-
-
Save SKaplanOfficial/6b800a01f591a369f0c8f1c95fe11c65 to your computer and use it in GitHub Desktop.
PyXA script for mimicking Window's window snapping feature on macOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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