diff --git a/dots/bin/hypr-snap-1.sh b/dots/bin/hypr-snap-1.sh new file mode 100644 index 0000000..69ee76b --- /dev/null +++ b/dots/bin/hypr-snap-1.sh @@ -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 +# +# 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 " + 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 diff --git a/dots/bin/hypr-snap.sh b/dots/bin/hypr-snap.sh index 69ee76b..2c7ea59 100644 --- a/dots/bin/hypr-snap.sh +++ b/dots/bin/hypr-snap.sh @@ -1,8 +1,9 @@ #!/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. +# Move a window in a direction. If already touching that edge, snap: +# - top/bottom edge: fill full width, keep height +# - left/right edge: fill full height, keep width +# Window becomes floating on snap (required for free resize/move in Hyprland). # # Usage: hypr-snap.sh # @@ -32,56 +33,78 @@ DATA=$(jq -n \ ($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]), + win_x: $win.at[0], + win_y: $win.at[1], + win_w: $win.size[0], + win_h: $win.size[1], + win_r: ($win.at[0] + $win.size[0]), + win_b: ($win.at[1] + $win.size[1]), + floating: $win.floating, - 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) + use_x: ($mon.x + $res_l), + 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_b: ($mon.y + $mh - $res_b) } ') WIN_X=$(echo "$DATA" | jq '.win_x') 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_B=$(echo "$DATA" | jq '.win_b') +FLOATING=$(echo "$DATA" | jq '.floating') USE_X=$(echo "$DATA" | jq '.use_x') 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_B=$(echo "$DATA" | jq '.use_b') -# How close (in pixels) the window edge must be to the monitor edge to trigger snap. 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 l) 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 hyprctl dispatch movewindoworgroup l fi ;; r) 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 hyprctl dispatch movewindoworgroup r fi ;; u) 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 hyprctl dispatch movewindoworgroup u fi ;; d) 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 hyprctl dispatch movewindoworgroup d fi