Yabai is certainly making everyday workflow enjoyable. Thank you!
BTW, I always have the feeling that, to achieve close to 100% keyboard oriented operation, I should be able to focus between floating and tiled windows OR cycle through all floating windows (assume a few windows are floating and the rest are tiled) using some keyboard shortcuts.
Related issue
Would love to see this feature in Yabai.
Let me know if this feature already exists and that I am missing some thing.
You will be able to do this if #112 gets added, so you can focus a window by its id.
You can test it by replacing the focus command with an echo or something like that.
# 1. Focus window with id
# 2. Query all windows on active space
# 3. Get the ids of all floating windows, sorted by id and set $x to the element
# after the element with the id of the currently focused window, using the
# first id in the list as a fallback
yabai -m window --focus "$( \
yabai -m query --windows --space \
| jq -re "[sort_by(.id) | .[] | select(.floating == 1) | .id] | nth(1 + index($( \
yabai -m query --windows --window | jq '.id'))) // first")"
If you want to cycle backwards, you can either replace the nth(1+...) with nth(...-1) and the // first with // last, or reverse the sort order by using sort_by(.id) | reverse instead of sort_by(.id).
You could similarly also cycle through all windows on a space sorted by their coordinates:
yabai -m window --focus "$( \
yabai -m query --windows --space \
| jq -re "sort_by(.frame) | map(.id) | nth(1 + index($( \
yabai -m query --windows --window | jq '.id'))) // first")"
(Edited for optimised version)
@jack836 on current master you can use this beautifully long oneliner to cycle through all windows on a space, sorted by their x coordinate first and then by their y coordinate.
# cycle all windows on space backwards by coordinate
alt - tab : yabai -m window --focus "$(yabai -m query --windows --space | jq -re "[sort_by(.id, .frame) | reverse | .[] | select(.role == \"AXWindow\" and .subrole == \"AXStandardWindow\") | .id] | nth(index($(yabai -m query --windows --window | jq -re ".id")) - 1)")"
# cycle all windows on space backwards by coordinate
shift + alt - tab : yabai -m window --focus "$(yabai -m query --windows --space | jq -re "[sort_by(.id, .frame) | .[] | select(.role == \"AXWindow\" and .subrole == \"AXStandardWindow\") | .id] | nth(index($(yabai -m query --windows --window | jq -re ".id")) - 1)")"
The scripts in my above post still had some very slight issues. For example chrome spawns some unfocusable windows that have a subrole of AXUnknown and these needed to be filtered out.
Now that #112 has landed on master you can do some pretty fancy stuff:
# cycle through all windows sorted by: coordinates -> space index -> display index
yabai -m window --focus "$(yabai -m query --windows | jq -re "sort_by(.display, .space, .frame.x, .frame.y, .id) | map(select(.subrole != \"AXUnknown\")) | reverse | nth(index(map(select(.focused == 1))) - 1).id")"
Or just on the visible spaces...
# cycle through all visible windows sorted by: coordinates -> display index
yabai -m window --focus "$(yabai -m query --windows | jq -re "sort_by(.display, .frame.x, .frame.y, .id) | map(select(.visible == 1 and .subrole != \"AXUnknown\")) | reverse | nth(index(map(select(.focused == 1))) - 1).id")"
To reverse the order, just take out the | reverse.
Pretty awesome. Thank you!!
I am not able to test it immediately (vacations and other stuff). I will report back soon.....
Sorry for the delay.... Tested it today.
@dominiklohmann Thank you for putting up a readymade recipe. It worked out of the box on high Sierra. Anyway, it delivers what I wanted.
With my level of knowledge I wouldn't have come up with such a nice solution (I still don't understand what the command does exactly).
So far, I was restricting myself from making windows float (even though the need arises), and I don't have to do that any more. Thank you for the freedom.
I still don't understand what the command does exactly
I'll try to explain the last one
# the full command
yabai -m window --focus "$(yabai -m query --windows | jq -re "sort_by(.display, .frame.x, .frame.y, .id) | map(select(.visible == 1 and .subrole != \"AXUnknown\")) | reverse | nth(index(map(select(.focused == 1))) - 1).id")"
# in parts, explained:
# focus window with selector (window id in this case)
yabai -m window --focus "$(...)"
# to get the selector, we first get information about all windows
yabai -m query --windows
# and run that information through jq, which is a tool for operating on JSON data
| jq -re "..."
# we first sort our available windows by their display and by their coordinates
# and also sort by id in case two windows have the same origin
sort_by(.display, .frame.x, .frame.y, .id)
# next we select only windows that are visible and whose subrole is not AXUnknown
# (on current master the check for subrole != AXUnknown is no longer necessary)
| map(select(.visible == 1 and .subrole != \"AXUnknown\"))
# we then reverse the list
| reverse
# and grab the element with the index that's one less than
| nth(... - 1)
# the index of the currently focused window
index(map(select(.focused == 1)))
# then we return only its id so we can focus the window
.id
Now you may wonder why we reverse the list and reduce the index by 1 instead of just raising the index by 1. That is because with jq, the array index [-1] indicates the last element of the array so we get wraparound for free.
@dominiklohmann I truly appreciate your enthusiasm in explaining things. Its nicely explained that it helps as a good starting point and motivates to explore further. I believe it will be the same for many other people too.
Thank you for the nice work!
Most helpful comment
I'll try to explain the last one
Now you may wonder why we reverse the list and reduce the index by 1 instead of just raising the index by 1. That is because with
jq, the array index [-1] indicates the last element of the array so we get wraparound for free.