WARNING: EXPERIMENTAL MODULE. DO NOT USE IN PRODUCTION. This module is for testing purposes only. It can undergo breaking API changes or go away entirely at any point and without notice. (Should you encounter any issues, please feel free to report them on https://github.com/Hammerspoon/hammerspoon/issues or #hammerspoon on irc.libera.chat)
Window management
Windowlayouts work by selecting certain windows via windowfilters and arranging them onscreen according to specific rules.
A layout is composed of a list of rules and, optionally, a screen arrangement definition. Rules within a layout are evaluated in order; once a window is acted upon by a rule, subsequent rules will not affect it further. A rule needs a windowfilter, producing a dynamic list of windows (the "window pool") to which the rule is applied, and a list of commands, evaluated in order. A command acts on one or more of the windows, and is composed of:
move
: moves the window(s) to a specified onscreen rect (if the action is omitted, move
is assumed)minimize
, maximize
, fullscreen
tile
, fit
: tiles the windows onto a specified rect, using hs.window.tiling.tileWindows()
; for fit
, the
preserveRelativeArea
parameter will be set to truehide
, unhide
: hides or unhides the window's application (like when using cmd-h)noaction
: skip action on the window(s)all
) all the remaining windows will be processed by this command; processed
windows are "consumed" and are excluded from the window pool for subsequent commands in this rule, and from subsequent rulesfocused
(pick maxn most recently focused windows), frontmost
(pick the recent focused window if its
application is frontmost application, otherwise the command will be skipped), newest
(most recently created), oldest
(least recently created), or closest
(pick the maxn windows that are closest to the destination rect); if omitted,
defaults to closest
for move, tile and fit, and newest
for everything elsehs.geometry
size (only valid for tile and fit) indicating the desired optimal aspect ratio for the tiled windows;
if omitted, defaults to 1x1 (i.e. square windows)hs.geometry
rect, or a unit rect plus a screen hint (for hs.screen.find()
),
indicating the destination rect for the commandYou should place higher-priority rules (with highly specialized windowfilters) first, and "fallback" rules
(with more generic windowfilters) last; similarly, within a rule, you should have commands for the more "important"
(i.e. relevant to your current workflow) windows first (move, maximize...) and after that deal with less prominent
windows, if any remain, e.g. by placing them out of the way (minimize).
unhide
and hide
, if used, should usually go into their own rules (with a windowfilter that allows invisible windows
for unhide
) that come before other rules that deal with actual window placement - unlike the other actions,
they don't "consume" windows making them unavailable for subsequent rules, as they act on applications.
In order to avoid dealing with deeply nested maps, you can define a layout in your scripts via a list, where each element (or row) denotes a rule; in turn every rule can be a simplified list of two elements:
hs.window.filter.new()
and hs.window.filter:setFilters()
)hs.geometry
constructor strings);
for greater clarity you can separate commands with |
(pipe character)Some command string examples:
"move 1 [0,0,50,50] -1,0"
moves the closest window to the topleft quadrant of the left screen"max 0,0"
maximizes all the windows onto the primary screen, one on top of another"move 1 foc [0,0,30,100] 0,0 | tile all foc [30,0,100,100] 0,0"
moves the most recently focused window to the left third,and tiles the remaining windows onto the right side, keeping the most recently focused on top and to the left
"1 new [0,0,50,100] 0,0 | 1 new [50,0,100,100] 0,0 | min"
divides the primary screen between the two newest windowsand minimizes any other windows
Each layout can work in "passive" or "active" modes; passive layouts must be triggered manually (via hs.hotkey.bind()
,
hs.menubar
, etc.) while active layouts continuously keep their rules enforced (see hs.window.layout:start()
for more information); in general you should avoid having multiple active layouts targeting the same windows, as the
results will be unpredictable (if such a situation is detected, you'll see an error in the Hammerspoon console); you
can have multiple active layouts, but be careful to maintain a clear "separation of concerns" between their respective windowfilters.
Each layout can have an associated screen configuration; if so, the layout will only be valid while the current screen
arrangement satisfies it; see hs.window.layout:setScreenConfiguration()
for more information.
Signature | hs.window.layout.applyDelay |
---|---|
Type | Variable |
Description | When "active mode" windowlayouts apply a rule, they will pause briefly for this amount of time in seconds, to allow windows |
Source | extensions/window/window_layout.lua line 598 |
Signature | hs.window.layout.screensChangedDelay |
---|---|
Type | Variable |
Description | The number of seconds to wait, after a screen configuration change has been detected, before |
Source | extensions/window/window_layout.lua line 736 |
Signature | hs.window.layout.applyLayout(rules) |
---|---|
Type | Function |
Description | Applies a layout |
Parameters |
|
Returns |
|
Notes |
|
Source | extensions/window/window_layout.lua line 871 |
Signature | hs.window.layout.pauseAllInstances() |
---|---|
Type | Function |
Description | Pauses all active windowlayout instances |
Parameters |
|
Returns |
|
Source | extensions/window/window_layout.lua line 849 |
Signature | hs.window.layout.resumeAllInstances() |
---|---|
Type | Function |
Description | Resumes all active windowlayout instances |
Parameters |
|
Returns |
|
Source | extensions/window/window_layout.lua line 860 |
Signature | hs.window.layout.new(rules[,logname[,loglevel]]) -> hs.window.layout object |
---|---|
Type | Constructor |
Description | Creates a new hs.window.layout instance |
Parameters |
|
Returns |
|
Source | extensions/window/window_layout.lua line 311 |
Signature | hs.window.layout:apply() |
---|---|
Type | Method |
Description | Applies the layout |
Parameters |
|
Returns |
|
Notes |
|
Source | extensions/window/window_layout.lua line 576 |
Signature | hs.window.layout:getRules() -> table |
---|---|
Type | Method |
Description | Return a table with all the rules (and the screen configuration, if present) defined for this windowlayout |
Parameters |
|
Returns |
|
Source | extensions/window/window_layout.lua line 342 |
Signature | hs.window.layout:pause() -> hs.window.layout object |
---|---|
Type | Method |
Description | Pauses an active windowlayout instance; while paused no automatic window management will occur |
Parameters |
|
Returns |
|
Source | extensions/window/window_layout.lua line 683 |
Signature | hs.window.layout:resume() -> hs.window.layout object |
---|---|
Type | Method |
Description | Resumes an active windowlayout instance after it was paused |
Parameters |
|
Returns |
|
Notes |
|
Source | extensions/window/window_layout.lua line 641 |
Signature | hs.window.layout:setScreenConfiguration(screens) -> hs.window.layout object |
---|---|
Type | Method |
Description | Determines the screen configuration that permits applying this windowlayout |
Parameters |
|
Returns |
|
Notes |
|
Examples | |
Source | extensions/window/window_layout.lua line 792 |
Signature | hs.window.layout:start() -> hs.window.layout object |
---|---|
Type | Method |
Description | Puts a windowlayout instance in "active mode" |
Parameters |
|
Returns |
|
Notes |
|
Source | extensions/window/window_layout.lua line 620 |
Signature | hs.window.layout:stop() -> hs.window.layout object |
---|---|
Type | Method |
Description | Stops a windowlayout instance (i.e. not in "active mode" anymore) |
Parameters |
|
Returns |
|
Source | extensions/window/window_layout.lua line 705 |