Skip to content
Closed
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ To un-ignore an app that you have selected to ignore, simply bring that app fron

Open the URL `rectangle://execute-action?name=[name]`. Do not activate Rectangle if possible.

Available values for `[name]`: `left-half`, `right-half`, `center-half`, `top-half`, `bottom-half`, `top-left`, `top-right`, `bottom-left`, `bottom-right`, `first-third`, `center-third`, `last-third`, `first-two-thirds`, `last-two-thirds`, `maximize`, `almost-maximize`, `maximize-height`, `smaller`, `larger`, `center`, `center-prominently`, `restore`, `next-display`, `previous-display`, `move-left`, `move-right`, `move-up`, `move-down`, `first-fourth`, `second-fourth`, `third-fourth`, `last-fourth`, `first-three-fourths`, `last-three-fourths`, `top-left-sixth`, `top-center-sixth`, `top-right-sixth`, `bottom-left-sixth`, `bottom-center-sixth`, `bottom-right-sixth`, `specified`, `reverse-all`, `top-left-ninth`, `top-center-ninth`, `top-right-ninth`, `middle-left-ninth`, `middle-center-ninth`, `middle-right-ninth`, `bottom-left-ninth`, `bottom-center-ninth`, `bottom-right-ninth`, `top-left-third`, `top-right-third`, `bottom-left-third`, `bottom-right-third`, `top-left-eighth`, `top-center-left-eighth`, `top-center-right-eighth`, `top-right-eighth`, `bottom-left-eighth`, `bottom-center-left-eighth`, `bottom-center-right-eighth`, `bottom-right-eighth`, `tile-all`, `cascade-all`, `cascade-active-app`
Available values for `[name]`: `left-half`, `right-half`, `center-half`, `top-half`, `bottom-half`, `top-left`, `top-right`, `bottom-left`, `bottom-right`, `first-third`, `center-third`, `last-third`, `first-two-thirds`, `last-two-thirds`, `maximize`, `almost-maximize`, `maximize-height`, `smaller`, `larger`, `center`, `center-prominently`, `restore`, `next-display`, `previous-display`, `move-left`, `move-right`, `move-up`, `move-down`, `first-fourth`, `second-fourth`, `third-fourth`, `last-fourth`, `first-three-fourths`, `last-three-fourths`, `top-left-sixth`, `top-center-sixth`, `top-right-sixth`, `bottom-left-sixth`, `bottom-center-sixth`, `bottom-right-sixth`, `specified`, `reverse-all`, `top-left-ninth`, `top-center-ninth`, `top-right-ninth`, `middle-left-ninth`, `middle-center-ninth`, `middle-right-ninth`, `bottom-left-ninth`, `bottom-center-ninth`, `bottom-right-ninth`, `top-left-third`, `top-right-third`, `bottom-left-third`, `bottom-right-third`, `top-left-eighth`, `top-center-left-eighth`, `top-center-right-eighth`, `top-right-eighth`, `bottom-left-eighth`, `bottom-center-left-eighth`, `bottom-center-right-eighth`, `bottom-right-eighth`, `top-left-twelfth`, `top-center-left-twelfth`, `top-center-right-twelfth`, `top-right-twelfth`, `middle-left-twelfth`, `middle-center-left-twelfth`, `middle-center-right-twelfth`, `middle-right-twelfth`, `bottom-left-twelfth`, `bottom-center-left-twelfth`, `bottom-center-right-twelfth`, `bottom-right-twelfth`, `top-left-sixteenth`, `top-center-left-sixteenth`, `top-center-right-sixteenth`, `top-right-sixteenth`, `upper-middle-left-sixteenth`, `upper-middle-center-left-sixteenth`, `upper-middle-center-right-sixteenth`, `upper-middle-right-sixteenth`, `lower-middle-left-sixteenth`, `lower-middle-center-left-sixteenth`, `lower-middle-center-right-sixteenth`, `lower-middle-right-sixteenth`, `bottom-left-sixteenth`, `bottom-center-left-sixteenth`, `bottom-center-right-sixteenth`, `bottom-right-sixteenth`, `tile-all`, `cascade-all`, `cascade-active-app`

Example, from a shell: `open -g "rectangle://execute-action?name=left-half"`

Expand All @@ -63,6 +63,32 @@ A bundle identifier can also be specified, for example:
rectangle://execute-task?name=ignore-app&app-bundle-id=com.apple.Safari
```

## Dense Grid Layouts (Ninths, Twelfths, Sixteenths)

Rectangle supports dense grid layouts beyond the standard halves, thirds, and quarters — designed for workflows that tile many windows simultaneously.

| Grid | Dimensions | Positions | Use case |
|------------|------------|-----------|-----------------------------------|
| Ninths | 3×3 | 9 | Monitoring dashboards, multi-pane |
| Twelfths | 3×4 | 12 | Terminal-heavy workflows, AI agents |
| Sixteenths | 4×4 | 16 | Maximum density on large displays |

These are especially useful for developers running multiple terminal sessions with tools like [Claude Code](https://claude.ai/code), [Codex](https://openai.com/index/codex/), and similar AI coding agents, where you may have 8–16 sessions open at once and need stable, predictable window placement.

All grid positions support **cycling mode**: repeatedly executing the same position action cycles the window through the grid in reading order (left-to-right, top-to-bottom), just like the existing thirds cycling behavior.

Grid position shortcuts are available in **Settings → Extra Shortcuts** (via the ellipsis button at the bottom of the General tab). Twelfths also have default keyboard shortcuts (Ctrl+Option+1 through Ctrl+Option+0/=/- for the 12 positions).

All positions are scriptable via the URL scheme:

```bash
# Tile a window into the top-left twelfth
open -g "rectangle://execute-action?name=top-left-twelfth"

# Tile into the bottom-right sixteenth
open -g "rectangle://execute-action?name=bottom-right-sixteenth"
```

## Terminal Commands for Hidden Preferences

See [TerminalCommands.md](TerminalCommands.md)
Expand Down
8 changes: 8 additions & 0 deletions Rectangle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@
98C2755E231FF6A9009B9292 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C2755D231FF6A9009B9292 /* EventMonitor.swift */; };
98C27561231FFA5F009B9292 /* SnappingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C27560231FFA5F009B9292 /* SnappingManager.swift */; };
98C275672322E2DA009B9292 /* WindowHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C275662322E2DA009B9292 /* WindowHistory.swift */; };
AA0000000000000000000002 /* WindowFingerprint.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000001 /* WindowFingerprint.swift */; };
AA0000000000000000000004 /* DisplayLayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000003 /* DisplayLayoutManager.swift */; };
98C6DEF023CE191700CC0C1E /* GapCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C6DEEF23CE191700CC0C1E /* GapCalculation.swift */; };
98C97FFD25893B040061F01F /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C97FFC25893B040061F01F /* Config.swift */; };
98D1441324560B1E0090C603 /* AlertUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D1441224560B1E0090C603 /* AlertUtil.swift */; };
Expand Down Expand Up @@ -359,6 +361,8 @@
98C2755D231FF6A9009B9292 /* EventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMonitor.swift; sourceTree = "<group>"; };
98C27560231FFA5F009B9292 /* SnappingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnappingManager.swift; sourceTree = "<group>"; };
98C275662322E2DA009B9292 /* WindowHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowHistory.swift; sourceTree = "<group>"; };
AA0000000000000000000001 /* WindowFingerprint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFingerprint.swift; sourceTree = "<group>"; };
AA0000000000000000000003 /* DisplayLayoutManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayLayoutManager.swift; sourceTree = "<group>"; };
98C6DEEF23CE191700CC0C1E /* GapCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GapCalculation.swift; sourceTree = "<group>"; };
98C97FFC25893B040061F01F /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
98D1441224560B1E0090C603 /* AlertUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertUtil.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -683,6 +687,8 @@
9824703C22B13C7E0037B409 /* AccessibilityElement.swift */,
9824703E22B13FBC0037B409 /* ScreenDetection.swift */,
98C275662322E2DA009B9292 /* WindowHistory.swift */,
AA0000000000000000000001 /* WindowFingerprint.swift */,
AA0000000000000000000003 /* DisplayLayoutManager.swift */,
9824704022B186D00037B409 /* WindowManager.swift */,
AA49DD1029B8C1B100690E13 /* TitleBarManager.swift */,
98987AA62391890C00BE72C4 /* Logging */,
Expand Down Expand Up @@ -1039,6 +1045,8 @@
D04CE31027817ABE00BD47B3 /* BottomRightNinthCalculation.swift in Sources */,
98A009B92512538D00CFBF0C /* TopRightSixthCalculation.swift in Sources */,
98C275672322E2DA009B9292 /* WindowHistory.swift in Sources */,
AA0000000000000000000002 /* WindowFingerprint.swift in Sources */,
AA0000000000000000000004 /* DisplayLayoutManager.swift in Sources */,
9821403722B3D16700ABFB3F /* MaximizeHeightCalculation.swift in Sources */,
9824704122B186D00037B409 /* WindowManager.swift in Sources */,
D0423D8327A8D31D008A4894 /* HorizontalThirdsRepeated.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions Rectangle/AccessibilityElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ class AccessibilityElement {
guard let role = role else { return nil }
return role == .sheet
}

var title: String? {
wrappedElement.getValue(.title) as? String
}

var isToolbar: Bool? {
guard let role = role else { return nil }
Expand Down
1 change: 1 addition & 0 deletions Rectangle/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self.applicationToggle = ApplicationToggle(shortcutManager: shortcutManager)
self.snappingManager = SnappingManager()
self.titleBarManager = TitleBarManager()
DisplayLayoutManager.shared = DisplayLayoutManager()
self.initializeTodo()
checkForProblematicApps()
MacTilingDefaults.checkForBuiltInTiling(skipIfAlreadyNotified: true)
Expand Down
6 changes: 5 additions & 1 deletion Rectangle/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class Defaults {
static let systemWideMouseDownApps = JSONDefault<Set<String>>(key:"systemWideMouseDownApps", defaultValue: Set<String>(["org.languagetool.desktop", "com.microsoft.teams2"]))
static let internalTilingNotified = BoolDefault(key: "internalTilingNotified")
static let screensOrderedByX = OptionalBoolDefault(key: "screensOrderedByX")
static let displayLayouts = JSONDefault<[String: [SavedWindowPosition]]>(key: "displayLayouts")
static let pinnedWindows = JSONDefault<[String: PinnedPosition]>(key: "pinnedWindows")
static var array: [Default] = [
launchOnLogin,
disabledApps,
Expand Down Expand Up @@ -183,7 +185,9 @@ class Defaults {
systemWideMouseDown,
systemWideMouseDownApps,
screensOrderedByX,
showAdditionalSizesInMenu
showAdditionalSizesInMenu,
displayLayouts,
pinnedWindows
]
}

Expand Down
Loading