From 4c024e08817fe73198a8503bc4028cf66abcf25d Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Tue, 25 Aug 2026 15:38:30 -0400 Subject: [PATCH] =?UTF-8?q?feat(cli):=20add=20smart=E2=80=91move=20command?= =?UTF-8?q?=20for=20window=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dot_local/bin/executable_omarchy-smart-move | 79 +++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 dot_local/bin/executable_omarchy-smart-move diff --git a/dot_local/bin/executable_omarchy-smart-move b/dot_local/bin/executable_omarchy-smart-move new file mode 100755 index 0000000..54a14cd --- /dev/null +++ b/dot_local/bin/executable_omarchy-smart-move @@ -0,0 +1,79 @@ +#!/bin/bash + +# omarchy:summary=Move the focused window into a neighboring group, or swap with the neighbor +# omarchy:args= + +set -uo pipefail + +DIR="${1:-}" + +case "$DIR" in +l | r | u | d) ;; +*) + echo "Usage: omarchy-smart-move " + exit 1 + ;; +esac + +hypr_dispatch() { + local lua="$1" + shift + + hyprctl dispatch "$lua" >/dev/null 2>&1 || hyprctl dispatch "$@" >/dev/null +} + +ACTIVE=$(hyprctl activewindow -j 2>/dev/null) +[[ -n $ACTIVE && $ACTIVE != "{}" ]] || exit 0 + +ADDRESS=$(jq -r '.address // empty' <<<"$ACTIVE") +WORKSPACE=$(jq -r '.workspace.id // empty' <<<"$ACTIVE") +GROUPED=$(jq -r '(.grouped // []) | length' <<<"$ACTIVE") + +[[ -n $ADDRESS && -n $WORKSPACE ]] || exit 0 + +# Find the nearest window in the given direction. A window counts as a +# neighbor only when it overlaps the focused window on the other axis, so a +# window in a far corner is not picked. +NEIGHBOR_GROUPED=$(hyprctl clients -j | jq -r \ + --arg address "$ADDRESS" \ + --arg dir "$DIR" \ + --argjson workspace "$WORKSPACE" ' + ( [ .[] | select(.address == $address) ] | first ) as $self + | if $self == null then "none" else + ( $self.at[0] + $self.size[0] / 2 ) as $sx + | ( $self.at[1] + $self.size[1] / 2 ) as $sy + | [ .[] + | select(.address != $address) + | select(.workspace.id == $workspace) + | select(.floating == false) + | select(.hidden == false) + | . + { + cx: (.at[0] + .size[0] / 2), + cy: (.at[1] + .size[1] / 2) + } + | select( + if $dir == "l" then .cx < $sx and (.at[1] < ($self.at[1] + $self.size[1])) and (($self.at[1]) < (.at[1] + .size[1])) + elif $dir == "r" then .cx > $sx and (.at[1] < ($self.at[1] + $self.size[1])) and (($self.at[1]) < (.at[1] + .size[1])) + elif $dir == "u" then .cy < $sy and (.at[0] < ($self.at[0] + $self.size[0])) and (($self.at[0]) < (.at[0] + .size[0])) + else .cy > $sy and (.at[0] < ($self.at[0] + $self.size[0])) and (($self.at[0]) < (.at[0] + .size[0])) + end + ) + ] + | sort_by( + if $dir == "l" or $dir == "r" then (.cx - $sx | fabs) else (.cy - $sy | fabs) end + ) + | first + | if . == null then "none" + elif ((.grouped // []) | length) > 0 then "group" + else "single" + end + end') + +# A group on either side means the move is group aware: Hyprland puts the +# window into the neighboring group, or takes it out of its own group at the +# edge. With no group in play, swap places with the neighbor instead. +if [[ $NEIGHBOR_GROUPED == "group" || $GROUPED -gt 1 ]]; then + hypr_dispatch "hl.dsp.window.move({ direction = \"$DIR\", group_aware = true })" movewindoworgroup "$DIR" +else + hypr_dispatch "hl.dsp.window.swap({ direction = \"$DIR\" })" swapwindow "$DIR" +fi