This commit is contained in:
committer@tuxwarrior
2026-05-25 20:31:36 -05:00
parent 1e16dc4e66
commit 08ad9838e5
2 changed files with 132 additions and 16 deletions
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# hypr-snap.sh
# Move a window in a direction. If already touching that edge, snap to fill
# the full usable area of the monitor (via fullscreen). Stays tiled.
# Works across any monitor configuration.
#
# Usage: hypr-snap.sh <l|r|u|d>
#
# Suggested binds in hyprland.conf:
# bind = $mainMod SHIFT, left, exec, hypr-snap.sh l
# bind = $mainMod SHIFT, right, exec, hypr-snap.sh r
# bind = $mainMod SHIFT, up, exec, hypr-snap.sh u
# bind = $mainMod SHIFT, down, exec, hypr-snap.sh d
DIRECTION=$1
if [[ -z "$DIRECTION" ]]; then
echo "Usage: hypr-snap.sh <l|r|u|d>"
exit 1
fi
WIN=$(hyprctl activewindow -j)
MON=$(hyprctl monitors -j | jq '.[] | select(.focused == true)')
DATA=$(jq -n \
--argjson win "$WIN" \
--argjson mon "$MON" '
($mon.width / $mon.scale | floor) as $mw |
($mon.height / $mon.scale | floor) as $mh |
($mon.reserved[2]) as $res_l |
($mon.reserved[3]) as $res_r |
($mon.reserved[0]) as $res_t |
($mon.reserved[1]) as $res_b |
{
win_x: $win.at[0],
win_y: $win.at[1],
win_r: ($win.at[0] + $win.size[0]),
win_b: ($win.at[1] + $win.size[1]),
use_x: ($mon.x + $res_l),
use_y: ($mon.y + $res_t),
use_r: ($mon.x + $mw - $res_r),
use_b: ($mon.y + $mh - $res_b)
}
')
WIN_X=$(echo "$DATA" | jq '.win_x')
WIN_Y=$(echo "$DATA" | jq '.win_y')
WIN_R=$(echo "$DATA" | jq '.win_r')
WIN_B=$(echo "$DATA" | jq '.win_b')
USE_X=$(echo "$DATA" | jq '.use_x')
USE_Y=$(echo "$DATA" | jq '.use_y')
USE_R=$(echo "$DATA" | jq '.use_r')
USE_B=$(echo "$DATA" | jq '.use_b')
# How close (in pixels) the window edge must be to the monitor edge to trigger snap.
TOLERANCE=50
case $DIRECTION in
l)
if [[ "$WIN_X" -le $((USE_X + TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1
else
hyprctl dispatch movewindoworgroup l
fi
;;
r)
if [[ "$WIN_R" -ge $((USE_R - TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1
else
hyprctl dispatch movewindoworgroup r
fi
;;
u)
if [[ "$WIN_Y" -le $((USE_Y + TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1
else
hyprctl dispatch movewindoworgroup u
fi
;;
d)
if [[ "$WIN_B" -ge $((USE_B - TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1
else
hyprctl dispatch movewindoworgroup d
fi
;;
*)
echo "Unknown direction: $DIRECTION. Use l, r, u, or d."
exit 1
;;
esac
+31 -8
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# hypr-snap.sh # hypr-snap.sh
# Move a window in a direction. If already touching that edge, snap to fill # Move a window in a direction. If already touching that edge, snap:
# the full usable area of the monitor (via fullscreen). Stays tiled. # - top/bottom edge: fill full width, keep height
# Works across any monitor configuration. # - left/right edge: fill full height, keep width
# Window becomes floating on snap (required for free resize/move in Hyprland).
# #
# Usage: hypr-snap.sh <l|r|u|d> # Usage: hypr-snap.sh <l|r|u|d>
# #
@@ -34,11 +35,16 @@ DATA=$(jq -n \
{ {
win_x: $win.at[0], win_x: $win.at[0],
win_y: $win.at[1], win_y: $win.at[1],
win_w: $win.size[0],
win_h: $win.size[1],
win_r: ($win.at[0] + $win.size[0]), win_r: ($win.at[0] + $win.size[0]),
win_b: ($win.at[1] + $win.size[1]), win_b: ($win.at[1] + $win.size[1]),
floating: $win.floating,
use_x: ($mon.x + $res_l), use_x: ($mon.x + $res_l),
use_y: ($mon.y + $res_t), use_y: ($mon.y + $res_t),
use_w: ($mw - $res_l - $res_r),
use_h: ($mh - $res_t - $res_b),
use_r: ($mon.x + $mw - $res_r), use_r: ($mon.x + $mw - $res_r),
use_b: ($mon.y + $mh - $res_b) use_b: ($mon.y + $mh - $res_b)
} }
@@ -46,42 +52,59 @@ DATA=$(jq -n \
WIN_X=$(echo "$DATA" | jq '.win_x') WIN_X=$(echo "$DATA" | jq '.win_x')
WIN_Y=$(echo "$DATA" | jq '.win_y') WIN_Y=$(echo "$DATA" | jq '.win_y')
WIN_W=$(echo "$DATA" | jq '.win_w')
WIN_H=$(echo "$DATA" | jq '.win_h')
WIN_R=$(echo "$DATA" | jq '.win_r') WIN_R=$(echo "$DATA" | jq '.win_r')
WIN_B=$(echo "$DATA" | jq '.win_b') WIN_B=$(echo "$DATA" | jq '.win_b')
FLOATING=$(echo "$DATA" | jq '.floating')
USE_X=$(echo "$DATA" | jq '.use_x') USE_X=$(echo "$DATA" | jq '.use_x')
USE_Y=$(echo "$DATA" | jq '.use_y') USE_Y=$(echo "$DATA" | jq '.use_y')
USE_W=$(echo "$DATA" | jq '.use_w')
USE_H=$(echo "$DATA" | jq '.use_h')
USE_R=$(echo "$DATA" | jq '.use_r') USE_R=$(echo "$DATA" | jq '.use_r')
USE_B=$(echo "$DATA" | jq '.use_b') USE_B=$(echo "$DATA" | jq '.use_b')
# How close (in pixels) the window edge must be to the monitor edge to trigger snap.
TOLERANCE=50 TOLERANCE=50
snap() {
local x=$1 y=$2 w=$3 h=$4
if [[ "$FLOATING" != "true" ]]; then
hyprctl dispatch togglefloating
fi
hyprctl dispatch moveactive exact "$x" "$y"
hyprctl dispatch resizeactive exact "$w" "$h"
}
case $DIRECTION in case $DIRECTION in
l) l)
if [[ "$WIN_X" -le $((USE_X + TOLERANCE)) ]]; then if [[ "$WIN_X" -le $((USE_X + TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1 # fill full height, keep width, pin to left edge
snap "$USE_X" "$USE_Y" "$WIN_W" "$USE_H"
else else
hyprctl dispatch movewindoworgroup l hyprctl dispatch movewindoworgroup l
fi fi
;; ;;
r) r)
if [[ "$WIN_R" -ge $((USE_R - TOLERANCE)) ]]; then if [[ "$WIN_R" -ge $((USE_R - TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1 # fill full height, keep width, pin to right edge
snap "$((USE_R - WIN_W))" "$USE_Y" "$WIN_W" "$USE_H"
else else
hyprctl dispatch movewindoworgroup r hyprctl dispatch movewindoworgroup r
fi fi
;; ;;
u) u)
if [[ "$WIN_Y" -le $((USE_Y + TOLERANCE)) ]]; then if [[ "$WIN_Y" -le $((USE_Y + TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1 # fill full width, keep height, pin to top edge
snap "$USE_X" "$USE_Y" "$USE_W" "$WIN_H"
else else
hyprctl dispatch movewindoworgroup u hyprctl dispatch movewindoworgroup u
fi fi
;; ;;
d) d)
if [[ "$WIN_B" -ge $((USE_B - TOLERANCE)) ]]; then if [[ "$WIN_B" -ge $((USE_B - TOLERANCE)) ]]; then
hyprctl dispatch fullscreen 1 # fill full width, keep height, pin to bottom edge
snap "$USE_X" "$((USE_B - WIN_H))" "$USE_W" "$WIN_H"
else else
hyprctl dispatch movewindoworgroup d hyprctl dispatch movewindoworgroup d
fi fi