diff --git a/dots/bin/hypr-snap.sh b/dots/bin/hypr-snap.sh new file mode 100644 index 0000000..6e306f4 --- /dev/null +++ b/dots/bin/hypr-snap.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# hypr-snap.sh +# Move a window in a direction. If already at the edge, snap to fill that edge. +# Works dynamically 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 + +# --- get active window info --- +WIN=$(hyprctl activewindow -j) +WIN_X=$(echo "$WIN" | jq '.at[0]') +WIN_Y=$(echo "$WIN" | jq '.at[1]') +WIN_W=$(echo "$WIN" | jq '.size[0]') +WIN_H=$(echo "$WIN" | jq '.size[1]') +WIN_R=$((WIN_X + WIN_W)) +WIN_B=$((WIN_Y + WIN_H)) + +# --- get the focused monitor info --- +MON=$(hyprctl monitors -j | jq '.[] | select(.focused == true)') +MON_X=$(echo "$MON" | jq '.x') +MON_Y=$(echo "$MON" | jq '.y') +MON_SCALE=$(echo "$MON" | jq '.scale') + +# reserved areas (e.g. status bar): [top, bottom, left, right] +RES_T=$(echo "$MON" | jq '.reserved[0]') +RES_B=$(echo "$MON" | jq '.reserved[1]') +RES_L=$(echo "$MON" | jq '.reserved[2]') +RES_R=$(echo "$MON" | jq '.reserved[3]') + +# usable area on this monitor, accounting for scale and reserved regions +USE_X=$(echo "$MON" | jq "($MON_X + $RES_L) | floor") +USE_Y=$(echo "$MON" | jq "($MON_Y + $RES_T) | floor") +USE_W=$(echo "$MON" | jq "((.width / $MON_SCALE) - $RES_L - $RES_R) | floor") +USE_H=$(echo "$MON" | jq "((.height / $MON_SCALE) - $RES_T - $RES_B) | floor") +USE_R=$((USE_X + USE_W)) +USE_B=$((USE_Y + USE_H)) + +snap() { + local x=$1 y=$2 w=$3 h=$4 + # if window is tiled, make it floating first so we can freely position/resize it + FLOATING=$(echo "$WIN" | jq '.floating') + 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" ]]; then + snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + else + hyprctl dispatch movewindoworgroup l + fi + ;; + r) + if [[ "$WIN_R" -ge "$USE_R" ]]; then + snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + else + hyprctl dispatch movewindoworgroup r + fi + ;; + u) + if [[ "$WIN_Y" -le "$USE_Y" ]]; then + snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + else + hyprctl dispatch movewindoworgroup u + fi + ;; + d) + if [[ "$WIN_B" -ge "$USE_B" ]]; then + snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + else + hyprctl dispatch movewindoworgroup d + fi + ;; + *) + echo "Unknown direction: $DIRECTION. Use l, r, u, or d." + exit 1 + ;; +esac diff --git a/dots/hyprland/hyprland.conf b/dots/hyprland/hyprland.conf index 8e3b2e8..f7f5b7f 100644 --- a/dots/hyprland/hyprland.conf +++ b/dots/hyprland/hyprland.conf @@ -335,10 +335,14 @@ bind = $mainMod SHIFT, 9, movetoworkspace, 9 bind = $mainMod SHIFT, 0, movetoworkspace, 10 # move window -bind = $mainMod SHIFT, left, movewindoworgroup, l -bind = $mainMod SHIFT, right, movewindoworgroup, r -bind = $mainMod SHIFT, up, movewindoworgroup, u -bind = $mainMod SHIFT, down, movewindoworgroup, d +#bind = $mainMod SHIFT, left, movewindoworgroup, l +#bind = $mainMod SHIFT, right, movewindoworgroup, r +#bind = $mainMod SHIFT, up, movewindoworgroup, u +#bind = $mainMod SHIFT, down, movewindoworgroup, d +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 # move floating window binde = $mainMod ALT, left, moveactive, -40 0