diff --git a/docs/guide.adoc b/docs/guide.adoc index 6284b203..3d9093c1 100644 --- a/docs/guide.adoc +++ b/docs/guide.adoc @@ -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 @@ -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! 🤦) @@ -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' +---- diff --git a/docs/popular-apps-ids.adoc b/docs/popular-apps-ids.adoc index d1c58300..33ad4259 100644 --- a/docs/popular-apps-ids.adoc +++ b/docs/popular-apps-ids.adoc @@ -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` diff --git a/src/tree/MacWindow.swift b/src/tree/MacWindow.swift index 296e6979..6b57ba56 100644 --- a/src/tree/MacWindow.swift +++ b/src/tree/MacWindow.swift @@ -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) @@ -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) @@ -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) } diff --git a/src/util/accessibility.swift b/src/util/accessibility.swift index 4142beed..c9a048c6 100644 --- a/src/util/accessibility.swift +++ b/src/util/accessibility.swift @@ -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( + key: kAXFullScreenButtonAttribute, + getter: { ($0 as! AXUIElement) } + ) } extension AXUIElement {