Skip to content

Commit

Permalink
Try a new heuristic: float windows without fullscreen button
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitabobko committed Dec 15, 2023
1 parent 44d9ada commit 8442cbb
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 13 deletions.
46 changes: 38 additions & 8 deletions docs/guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -354,14 +354,6 @@ run = 'move-node-to-workspace I' # mnemonics I - IDE
check-further-callbacks = true
run = 'layout floating'
----
* Float '`System Settings`' app
+
[source,toml]
----
[[on-window-detected]]
if.app-id = 'com.apple.systempreferences'
run = 'layout floating'
----
[#multiple-monitors]
== Multiple monitors
Expand Down Expand Up @@ -430,6 +422,8 @@ xref:commands.adoc#move-workspace-to-monitor[move-workspace-to-monitor] command
[#caveats]
== Caveats

=== Unwanted animations

If you use some of the Accessibility features `System Settings -> Accessibility`, you may see weird animations, for no reasons, when AeroSpace moves windows around.
(Shame on you, Apple! 🤦)

Expand All @@ -442,3 +436,39 @@ Known accessibility features that cause the problem:
* Maybe something else...

Please reboot after you disable the accessibility features that cause the problem.

=== Dialog heuristics

* Apple provides accessibility API for apps to let others know which of their windows are dialogs
* A lot of apps don't implement this API or implement it improperly
+
Even some Apple dialogs don't implement the API properly.
(E.g. Finder "Copy" progress window doesn't let others know that it's a dialog)

AeroSpace uses the API to gently ask windows whether they are dialogs, but AeroSpace also applies some heuristics.

For example, windows without a fullscreen button (NB! fullscreen button and maximize button are different buttons) are considered dialogs, excluding terminal apps (WezTerm, Alacritty, iTerm2).

Windows that are recognized as dialogs are floated by default.

It's not possible to disable the heuristics, because the goal is to make the heuristics so good that there won't problems with them.
If you find that some windows are not handled properly please https://github.com/nikitabobko/AeroSpace/issues/new[report an issue]

Anyway, your alternatives are:

. Force tile all the windows (or windows of a particular app)
+
[source,toml]
----
[[on-window-detected]]
check-further-callbacks = true
run = 'layout tiling'
----
. Force float all the windows (or windows of a particular app)
+
[source,toml]
----
[[on-window-detected]]
check-further-callbacks = true
run = 'layout floating'
----
1 change: 1 addition & 0 deletions docs/popular-apps-ids.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ include::header.adoc[]
|VoiceMemos|`com.apple.VoiceMemos`
|VoiceOver Utility|`com.apple.VoiceOverUtility`
|Weather|`com.apple.weather`
|WezTerm|`com.github.wez.wezterm`
|Xcode|`com.apple.dt.Xcode`
|iMovie|`com.apple.iMovieApp`
|iTerm2|`com.googlecode.iterm2`
Expand Down
20 changes: 15 additions & 5 deletions src/tree/MacWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ final class MacWindow: Window, CustomStringConvertible {
} else {
let data = getBindingDataForNewWindow(
axWindow,
startup ? (axWindow.center?.monitorApproximation ?? mainMonitor).activeWorkspace : Workspace.focused
startup ? (axWindow.center?.monitorApproximation ?? mainMonitor).activeWorkspace : Workspace.focused,
app
)
let window = MacWindow(id, app, axWindow, parent: data.parent, adaptiveWeight: data.adaptiveWeight, index: data.index)

Expand Down Expand Up @@ -161,7 +162,7 @@ private func isWindow(_ axWindow: AXUIElement, _ app: MacApp) -> Bool {
app.id == "com.apple.finder" && subrole == "Quick Look" // Finder preview (hit space) is a floating window
}

private func shouldFloat(_ axWindow: AXUIElement) -> Bool { // Note: a lot of windows don't have title on startup
private func shouldFloat(_ axWindow: AXUIElement, _ app: MacApp) -> Bool { // Note: a lot of windows don't have title on startup
// Don't tile:
// - Chrome cmd+f window ("AXUnknown" value)
// - login screen (Yes fuck, it's also a window from Apple's API perspective) ("AXUnknown" value)
Expand All @@ -170,11 +171,20 @@ private func shouldFloat(_ axWindow: AXUIElement) -> Bool { // Note: a lot of wi
// - macOS native file picker ("Open..." menu) (kAXDialogSubrole value)
//
// Minimized windows or windows of a hidden app have subrole "AXDialog"
axWindow.get(Ax.subroleAttr) != kAXStandardWindowSubrole
axWindow.get(Ax.subroleAttr) != kAXStandardWindowSubrole ||
// Heuristic: float windows without maximize button (such windows are not designed to be big)
// - IntelliJ various dialogs (Rebase..., Edit commit message, Settings, Project structure)
// - Finder copy file dialog
// - System Settings
// Exclude terminals from the heuristic (advanced terminals have options to show no decorations)
axWindow.get(Ax.fullscreenButtonAttr) == nil &&
app.id != "org.alacritty" &&
app.id != "com.github.wez.wezterm" &&
app.id != "com.googlecode.iterm2"
}

private func getBindingDataForNewWindow(_ axWindow: AXUIElement, _ workspace: Workspace) -> BindingData {
shouldFloat(axWindow)
private func getBindingDataForNewWindow(_ axWindow: AXUIElement, _ workspace: Workspace, _ app: MacApp) -> BindingData {
shouldFloat(axWindow, app)
? BindingData(parent: workspace as NonLeafTreeNode, adaptiveWeight: WEIGHT_AUTO, index: INDEX_BIND_LAST)
: getBindingDataForNewTilingWindow(workspace)
}
Expand Down
5 changes: 5 additions & 0 deletions src/util/accessibility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ enum Ax {
key: kAXCloseButtonAttribute,
getter: { ($0 as! AXUIElement) }
)
// Note! fullscreen is not the same as "zoom" (green plus)
static let fullscreenButtonAttr = ReadableAttrImpl<AXUIElement>(
key: kAXFullScreenButtonAttribute,
getter: { ($0 as! AXUIElement) }
)
}

extension AXUIElement {
Expand Down

0 comments on commit 8442cbb

Please sign in to comment.