From c4c4365141ddc3c107b9b23777c25ab817d7c622 Mon Sep 17 00:00:00 2001 From: Myron Koch Date: Fri, 13 Mar 2026 14:32:31 -0400 Subject: [PATCH 01/10] Organize thirds and size actions into menu submenus Move thirds (First Third through Last Two Thirds) and size actions (Almost Maximize, Maximize Height, Larger, Smaller) into their own submenus in the menu bar dropdown, matching the existing pattern used by fourths, sixths, eighths, and move actions. Also removes the conditional hiding of the eighths submenu, making it always visible like all other grid categories. This reduces menu clutter for users with many grid sizes enabled, particularly on large displays and multi-monitor setups. --- Rectangle/AppDelegate.swift | 4 ---- Rectangle/WindowAction.swift | 4 +++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Rectangle/AppDelegate.swift b/Rectangle/AppDelegate.swift index 719f3bea..68f5d310 100644 --- a/Rectangle/AppDelegate.swift +++ b/Rectangle/AppDelegate.swift @@ -411,10 +411,6 @@ extension AppDelegate: NSMenuDelegate { for categoryMenu in categoryMenus { categoryMenu.menu.delegate = self let menuMenuItem = NSMenuItem(title: categoryMenu.category.displayName, action: nil, keyEquivalent: "") - if categoryMenu.category == .eighths { - eighthsMenuItem = menuMenuItem - eighthsMenuItem?.isHidden = !Defaults.showEighthsInMenu.userEnabled - } mainStatusMenu.insertItem(menuMenuItem, at: menuIndex) mainStatusMenu.setSubmenu(categoryMenu.menu, for: menuMenuItem) menuIndex += 1 diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index 4ca3104f..8f750a5c 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -154,7 +154,7 @@ enum WindowAction: Int, Codable { // Determines where separators should be used in the menu var firstInGroup: Bool { switch self { - case .leftHalf, .topLeft, .firstThird, .maximize, .nextDisplay, .moveLeft, .firstFourth, .topLeftSixth, .topLeftEighth: + case .leftHalf, .topLeft, .firstThird, .maximize, .almostMaximize, .nextDisplay, .moveLeft, .firstFourth, .topLeftSixth, .topLeftEighth: return true default: return false @@ -652,10 +652,12 @@ enum WindowAction: Int, Codable { var category: WindowActionCategory? { // used to specify a submenu switch self { + case .firstThird, .centerThird, .lastThird, .firstTwoThirds, .centerTwoThirds, .lastTwoThirds: return .thirds case .firstFourth, .secondFourth, .thirdFourth, .lastFourth, .firstThreeFourths, .centerThreeFourths, .lastThreeFourths: return .fourths case .topLeftSixth, .topCenterSixth, .topRightSixth, .bottomLeftSixth, .bottomCenterSixth, .bottomRightSixth: return .sixths case .topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth, .bottomLeftEighth, .bottomCenterLeftEighth, .bottomCenterRightEighth, .bottomRightEighth: return .eighths case .moveUp, .moveDown, .moveLeft, .moveRight: return .move + case .almostMaximize, .maximizeHeight, .larger, .smaller: return .size default: return nil } } From 3864d2492e9abf9a7daf25927c206a9fa5019fda Mon Sep 17 00:00:00 2001 From: Myron Koch Date: Fri, 13 Mar 2026 14:39:12 -0400 Subject: [PATCH 02/10] Add twelfths (3x4) and sixteenths (4x4) grid window positions Add 28 new window positions for fine-grained window management on large displays. Users with 4K TVs (50"+), ultrawide monitors, or multi-monitor arrays need more granular positioning than halves, thirds, fourths, sixths, and eighths provide. Twelfths divide the screen into a 3-row by 4-column grid (12 positions). Sixteenths divide the screen into a 4-row by 4-column grid (16 positions). Each grid size includes: - Window calculations with orientation-aware layout - Cycling through positions on repeated shortcut presses - Drag-to-snap support - Gap-aware edge positioning - Template images for menu and settings display - Settings panel with shortcut configuration for all positions --- Rectangle.xcodeproj/project.pbxproj | 120 +++++++++ .../Contents.json | 21 ++ .../bottomCenterLeftSixteenthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ .../bottomCenterLeftTwelfthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ .../bottomCenterRightSixteenthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ .../bottomCenterRightTwelfthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ .../bottomLeftSixteenthTemplate.png | Bin 0 -> 101 bytes .../Contents.json | 21 ++ .../bottomLeftTwelfthTemplate.png | Bin 0 -> 101 bytes .../Contents.json | 21 ++ .../bottomRightSixteenthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ .../bottomRightTwelfthTemplate.png | Bin 0 -> 102 bytes .../Contents.json | 21 ++ ...lowerMiddleCenterLeftSixteenthTemplate.png | Bin 0 -> 100 bytes .../Contents.json | 21 ++ ...owerMiddleCenterRightSixteenthTemplate.png | Bin 0 -> 100 bytes .../Contents.json | 21 ++ .../lowerMiddleLeftSixteenthTemplate.png | Bin 0 -> 102 bytes .../Contents.json | 21 ++ .../lowerMiddleRightSixteenthTemplate.png | Bin 0 -> 104 bytes .../Contents.json | 21 ++ .../middleCenterLeftTwelfthTemplate.png | Bin 0 -> 104 bytes .../Contents.json | 21 ++ .../middleCenterRightTwelfthTemplate.png | Bin 0 -> 104 bytes .../Contents.json | 21 ++ .../middleLeftTwelfthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ .../middleRightTwelfthTemplate.png | Bin 0 -> 101 bytes .../Contents.json | 21 ++ .../topCenterLeftSixteenthTemplate.png | Bin 0 -> 106 bytes .../Contents.json | 21 ++ .../topCenterLeftTwelfthTemplate.png | Bin 0 -> 106 bytes .../Contents.json | 21 ++ .../topCenterRightSixteenthTemplate.png | Bin 0 -> 107 bytes .../Contents.json | 21 ++ .../topCenterRightTwelfthTemplate.png | Bin 0 -> 107 bytes .../Contents.json | 21 ++ .../topLeftSixteenthTemplate.png | Bin 0 -> 104 bytes .../Contents.json | 21 ++ .../topLeftTwelfthTemplate.png | Bin 0 -> 104 bytes .../Contents.json | 21 ++ .../topRightSixteenthTemplate.png | Bin 0 -> 102 bytes .../Contents.json | 21 ++ .../topRightTwelfthTemplate.png | Bin 0 -> 102 bytes .../Contents.json | 21 ++ ...upperMiddleCenterLeftSixteenthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ ...pperMiddleCenterRightSixteenthTemplate.png | Bin 0 -> 103 bytes .../Contents.json | 21 ++ .../upperMiddleLeftSixteenthTemplate.png | Bin 0 -> 102 bytes .../Contents.json | 21 ++ .../upperMiddleRightSixteenthTemplate.png | Bin 0 -> 102 bytes .../PrefsWindow/PrefsViewController.swift | 153 ++++++++++- Rectangle/WindowAction.swift | 250 +++++++++++++++++- Rectangle/WindowActionCategory.swift | 8 +- ...BottomCenterLeftSixteenthCalculation.swift | 50 ++++ .../BottomCenterLeftTwelfthCalculation.swift | 50 ++++ ...ottomCenterRightSixteenthCalculation.swift | 50 ++++ .../BottomCenterRightTwelfthCalculation.swift | 50 ++++ .../BottomLeftSixteenthCalculation.swift | 50 ++++ .../BottomLeftTwelfthCalculation.swift | 50 ++++ .../BottomRightSixteenthCalculation.swift | 50 ++++ .../BottomRightTwelfthCalculation.swift | 50 ++++ ...MiddleCenterLeftSixteenthCalculation.swift | 50 ++++ ...iddleCenterRightSixteenthCalculation.swift | 50 ++++ .../LowerMiddleLeftSixteenthCalculation.swift | 50 ++++ ...LowerMiddleRightSixteenthCalculation.swift | 50 ++++ .../MiddleCenterLeftTwelfthCalculation.swift | 50 ++++ .../MiddleCenterRightTwelfthCalculation.swift | 50 ++++ .../MiddleLeftTwelfthCalculation.swift | 50 ++++ .../MiddleRightTwelfthCalculation.swift | 50 ++++ .../SixteenthsRepeated.swift | 95 +++++++ .../TopCenterLeftSixteenthCalculation.swift | 50 ++++ .../TopCenterLeftTwelfthCalculation.swift | 50 ++++ .../TopCenterRightSixteenthCalculation.swift | 50 ++++ .../TopCenterRightTwelfthCalculation.swift | 50 ++++ .../TopLeftSixteenthCalculation.swift | 50 ++++ .../TopLeftTwelfthCalculation.swift | 50 ++++ .../TopRightSixteenthCalculation.swift | 50 ++++ .../TopRightTwelfthCalculation.swift | 50 ++++ .../WindowCalculation/TwelfthsRepeated.swift | 79 ++++++ ...MiddleCenterLeftSixteenthCalculation.swift | 50 ++++ ...iddleCenterRightSixteenthCalculation.swift | 50 ++++ .../UpperMiddleLeftSixteenthCalculation.swift | 50 ++++ ...UpperMiddleRightSixteenthCalculation.swift | 50 ++++ .../WindowCalculation/WindowCalculation.swift | 58 +++- 91 files changed, 2739 insertions(+), 12 deletions(-) create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftSixteenthTemplate.imageset/bottomCenterLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftTwelfthTemplate.imageset/bottomCenterLeftTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightSixteenthTemplate.imageset/bottomCenterRightSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightTwelfthTemplate.imageset/bottomCenterRightTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixteenthTemplate.imageset/bottomLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomLeftTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomLeftTwelfthTemplate.imageset/bottomLeftTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomRightSixteenthTemplate.imageset/bottomRightSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomRightTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomRightTwelfthTemplate.imageset/bottomRightTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterLeftSixteenthTemplate.imageset/lowerMiddleCenterLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterRightSixteenthTemplate.imageset/lowerMiddleCenterRightSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleLeftSixteenthTemplate.imageset/lowerMiddleLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/lowerMiddleRightSixteenthTemplate.imageset/lowerMiddleRightSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleCenterLeftTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleCenterLeftTwelfthTemplate.imageset/middleCenterLeftTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleCenterRightTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleCenterRightTwelfthTemplate.imageset/middleCenterRightTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleLeftTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleLeftTwelfthTemplate.imageset/middleLeftTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleRightTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleRightTwelfthTemplate.imageset/middleRightTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterLeftSixteenthTemplate.imageset/topCenterLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterLeftTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterLeftTwelfthTemplate.imageset/topCenterLeftTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterRightSixteenthTemplate.imageset/topCenterRightSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterRightTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterRightTwelfthTemplate.imageset/topCenterRightTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topLeftSixteenthTemplate.imageset/topLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topLeftTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topLeftTwelfthTemplate.imageset/topLeftTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topRightSixteenthTemplate.imageset/topRightSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topRightTwelfthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topRightTwelfthTemplate.imageset/topRightTwelfthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterLeftSixteenthTemplate.imageset/upperMiddleCenterLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterRightSixteenthTemplate.imageset/upperMiddleCenterRightSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleLeftSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleLeftSixteenthTemplate.imageset/upperMiddleLeftSixteenthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleRightSixteenthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/upperMiddleRightSixteenthTemplate.imageset/upperMiddleRightSixteenthTemplate.png create mode 100644 Rectangle/WindowCalculation/BottomCenterLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/BottomCenterRightSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/BottomLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/BottomRightSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/BottomRightTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/LowerMiddleCenterLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/LowerMiddleCenterRightSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/LowerMiddleLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/LowerMiddleRightSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/SixteenthsRepeated.swift create mode 100644 Rectangle/WindowCalculation/TopCenterLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TopCenterLeftTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TopCenterRightSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TopCenterRightTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TopLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TopLeftTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TopRightSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift create mode 100644 Rectangle/WindowCalculation/TwelfthsRepeated.swift create mode 100644 Rectangle/WindowCalculation/UpperMiddleCenterLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/UpperMiddleCenterRightSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/UpperMiddleLeftSixteenthCalculation.swift create mode 100644 Rectangle/WindowCalculation/UpperMiddleRightSixteenthCalculation.swift diff --git a/Rectangle.xcodeproj/project.pbxproj b/Rectangle.xcodeproj/project.pbxproj index 9d82e53a..e201f98c 100644 --- a/Rectangle.xcodeproj/project.pbxproj +++ b/Rectangle.xcodeproj/project.pbxproj @@ -19,6 +19,36 @@ 6490B39D27BF984D0056C220 /* BottomCenterLeftEighthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6490B39C27BF984D0056C220 /* BottomCenterLeftEighthCalculation.swift */; }; 6490B39F27BF98840056C220 /* BottomCenterRightEighthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6490B39E27BF98840056C220 /* BottomCenterRightEighthCalculation.swift */; }; 6490B3A127BF98C70056C220 /* BottomRightEighthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6490B3A027BF98C70056C220 /* BottomRightEighthCalculation.swift */; }; + 7EC9E3BAD5BC57E49A7BCEE5 /* TwelfthsRepeated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3592F8E85CE82B8FA662A31 /* TwelfthsRepeated.swift */; }; + BAB3F495AD1F3454FAA31AFB /* TopLeftTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9FDCA1333C1BF6930A8D7F3 /* TopLeftTwelfthCalculation.swift */; }; + 924919761FC491952752A147 /* TopCenterLeftTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9698D1D9EE0180D7C4468304 /* TopCenterLeftTwelfthCalculation.swift */; }; + 14DBE979E4453E13A3BF5C17 /* TopCenterRightTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4D034DBD7EFAC4CF5F8D67B /* TopCenterRightTwelfthCalculation.swift */; }; + F4EED30B47FB6C55EA937254 /* TopRightTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8357E928530D10A806F3CD /* TopRightTwelfthCalculation.swift */; }; + 05DBEA44B55E6F8C5FC3D6E0 /* MiddleLeftTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEABF10E66D648DDB37DABBA /* MiddleLeftTwelfthCalculation.swift */; }; + 9FDA3D672AE475B83CA1A34A /* MiddleCenterLeftTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5070DE9100E45D374053F0C2 /* MiddleCenterLeftTwelfthCalculation.swift */; }; + DCD3CF2993E9AFAF30820117 /* MiddleCenterRightTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF2AC59501448BD3BAA16EC /* MiddleCenterRightTwelfthCalculation.swift */; }; + 8B1D14E3A55936EBCCC1E807 /* MiddleRightTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA84AB4DB9E7EEFCE50AA6D8 /* MiddleRightTwelfthCalculation.swift */; }; + BF9871B3BCF0E652444E0C56 /* BottomLeftTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246168B345ECE6C375ACEEB7 /* BottomLeftTwelfthCalculation.swift */; }; + 52953B57D48A4F110779E9BD /* BottomCenterLeftTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FE3EBE50F91F9EE2989279 /* BottomCenterLeftTwelfthCalculation.swift */; }; + 666598377754D51045353CD0 /* BottomCenterRightTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A362206B9F76C269D8F469 /* BottomCenterRightTwelfthCalculation.swift */; }; + 15E8CA7F6AC3EA2C4FB1424C /* BottomRightTwelfthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D038C68560DC11F14496F6 /* BottomRightTwelfthCalculation.swift */; }; + B6366EE24FA928086846969B /* SixteenthsRepeated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16B4044DB4809116039F96EB /* SixteenthsRepeated.swift */; }; + 3FA1D2E6C9B4623227F680CF /* TopLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6294D2CB6E3D8ED982F418AE /* TopLeftSixteenthCalculation.swift */; }; + A0844B53B14365D0278BC124 /* TopCenterLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE9E5E2C8BA2590DD0B6F59 /* TopCenterLeftSixteenthCalculation.swift */; }; + A32D3739AA16C537F6427C4F /* TopCenterRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 918F847FF15D524B6DB2E6DA /* TopCenterRightSixteenthCalculation.swift */; }; + F51A7467767F8EAF97321A00 /* TopRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD222258713B13274FB651B0 /* TopRightSixteenthCalculation.swift */; }; + D8644560FF112B0748CE1975 /* UpperMiddleLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2DBE0733F31BC159BC99C /* UpperMiddleLeftSixteenthCalculation.swift */; }; + E791B00A88D90C24A4C3D2D1 /* UpperMiddleCenterLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B8CA6901E6FE134E930135B /* UpperMiddleCenterLeftSixteenthCalculation.swift */; }; + 895C73559E1ACEFBBE8DC32D /* UpperMiddleCenterRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBF3EA35F0C110A170EAD33 /* UpperMiddleCenterRightSixteenthCalculation.swift */; }; + 0F29956694B5BB31EBDAE445 /* UpperMiddleRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B5B09465C38F8B766B3C3C /* UpperMiddleRightSixteenthCalculation.swift */; }; + 562018A56BC897E769500F1F /* LowerMiddleLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C796BCAA3DA5F85B55A7EE83 /* LowerMiddleLeftSixteenthCalculation.swift */; }; + 88763FEFFA3AECF8502604D6 /* LowerMiddleCenterLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378C96D832D4CC6BCCF9D604 /* LowerMiddleCenterLeftSixteenthCalculation.swift */; }; + A73749B5FB00D2CC2DB08744 /* LowerMiddleCenterRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE08B666CCCD1805CAB08BD /* LowerMiddleCenterRightSixteenthCalculation.swift */; }; + 4266090403BAF4B8A654A28B /* LowerMiddleRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87EC57F37CFD790695177AC8 /* LowerMiddleRightSixteenthCalculation.swift */; }; + F7B6C43BB57F8064F49DDBB6 /* BottomLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE147786B2CD7350FAFB700 /* BottomLeftSixteenthCalculation.swift */; }; + 9988C2A18B6AF03E09204981 /* BottomCenterLeftSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88454ABE5C62BD610AEA3EB5 /* BottomCenterLeftSixteenthCalculation.swift */; }; + 630644571003EB468CB643D8 /* BottomCenterRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D24EBF1F9A6209449A988E15 /* BottomCenterRightSixteenthCalculation.swift */; }; + 2D240EE416CC03AC921E3A4F /* BottomRightSixteenthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 482EA970820F37C6917FCD98 /* BottomRightSixteenthCalculation.swift */; }; 729E0A982AFF76B1006E2F48 /* CenterProminentlyCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 729E0A972AFF76B1006E2F48 /* CenterProminentlyCalculation.swift */; }; 74804F0B2E25521C009F1F7D /* CenterTwoThirdsCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74804F0A2E25521C009F1F7D /* CenterTwoThirdsCalculation.swift */; }; 7BE578EF2C5BF4EE0083DAE3 /* CycleSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE578EE2C5BF4ED0083DAE3 /* CycleSize.swift */; }; @@ -196,6 +226,36 @@ 6490B39C27BF984D0056C220 /* BottomCenterLeftEighthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomCenterLeftEighthCalculation.swift; sourceTree = ""; }; 6490B39E27BF98840056C220 /* BottomCenterRightEighthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomCenterRightEighthCalculation.swift; sourceTree = ""; }; 6490B3A027BF98C70056C220 /* BottomRightEighthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomRightEighthCalculation.swift; sourceTree = ""; }; + F3592F8E85CE82B8FA662A31 /* TwelfthsRepeated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwelfthsRepeated.swift; sourceTree = ""; }; + D9FDCA1333C1BF6930A8D7F3 /* TopLeftTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopLeftTwelfthCalculation.swift; sourceTree = ""; }; + 9698D1D9EE0180D7C4468304 /* TopCenterLeftTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopCenterLeftTwelfthCalculation.swift; sourceTree = ""; }; + A4D034DBD7EFAC4CF5F8D67B /* TopCenterRightTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopCenterRightTwelfthCalculation.swift; sourceTree = ""; }; + 7E8357E928530D10A806F3CD /* TopRightTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopRightTwelfthCalculation.swift; sourceTree = ""; }; + EEABF10E66D648DDB37DABBA /* MiddleLeftTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiddleLeftTwelfthCalculation.swift; sourceTree = ""; }; + 5070DE9100E45D374053F0C2 /* MiddleCenterLeftTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiddleCenterLeftTwelfthCalculation.swift; sourceTree = ""; }; + BCF2AC59501448BD3BAA16EC /* MiddleCenterRightTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiddleCenterRightTwelfthCalculation.swift; sourceTree = ""; }; + AA84AB4DB9E7EEFCE50AA6D8 /* MiddleRightTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiddleRightTwelfthCalculation.swift; sourceTree = ""; }; + 246168B345ECE6C375ACEEB7 /* BottomLeftTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomLeftTwelfthCalculation.swift; sourceTree = ""; }; + 71FE3EBE50F91F9EE2989279 /* BottomCenterLeftTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomCenterLeftTwelfthCalculation.swift; sourceTree = ""; }; + C1A362206B9F76C269D8F469 /* BottomCenterRightTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomCenterRightTwelfthCalculation.swift; sourceTree = ""; }; + D7D038C68560DC11F14496F6 /* BottomRightTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomRightTwelfthCalculation.swift; sourceTree = ""; }; + 16B4044DB4809116039F96EB /* SixteenthsRepeated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SixteenthsRepeated.swift; sourceTree = ""; }; + 6294D2CB6E3D8ED982F418AE /* TopLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopLeftSixteenthCalculation.swift; sourceTree = ""; }; + 5BE9E5E2C8BA2590DD0B6F59 /* TopCenterLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopCenterLeftSixteenthCalculation.swift; sourceTree = ""; }; + 918F847FF15D524B6DB2E6DA /* TopCenterRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopCenterRightSixteenthCalculation.swift; sourceTree = ""; }; + FD222258713B13274FB651B0 /* TopRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopRightSixteenthCalculation.swift; sourceTree = ""; }; + 84E2DBE0733F31BC159BC99C /* UpperMiddleLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpperMiddleLeftSixteenthCalculation.swift; sourceTree = ""; }; + 8B8CA6901E6FE134E930135B /* UpperMiddleCenterLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpperMiddleCenterLeftSixteenthCalculation.swift; sourceTree = ""; }; + BEBF3EA35F0C110A170EAD33 /* UpperMiddleCenterRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpperMiddleCenterRightSixteenthCalculation.swift; sourceTree = ""; }; + A3B5B09465C38F8B766B3C3C /* UpperMiddleRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpperMiddleRightSixteenthCalculation.swift; sourceTree = ""; }; + C796BCAA3DA5F85B55A7EE83 /* LowerMiddleLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LowerMiddleLeftSixteenthCalculation.swift; sourceTree = ""; }; + 378C96D832D4CC6BCCF9D604 /* LowerMiddleCenterLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LowerMiddleCenterLeftSixteenthCalculation.swift; sourceTree = ""; }; + 8AE08B666CCCD1805CAB08BD /* LowerMiddleCenterRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LowerMiddleCenterRightSixteenthCalculation.swift; sourceTree = ""; }; + 87EC57F37CFD790695177AC8 /* LowerMiddleRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LowerMiddleRightSixteenthCalculation.swift; sourceTree = ""; }; + FEE147786B2CD7350FAFB700 /* BottomLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomLeftSixteenthCalculation.swift; sourceTree = ""; }; + 88454ABE5C62BD610AEA3EB5 /* BottomCenterLeftSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomCenterLeftSixteenthCalculation.swift; sourceTree = ""; }; + D24EBF1F9A6209449A988E15 /* BottomCenterRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomCenterRightSixteenthCalculation.swift; sourceTree = ""; }; + 482EA970820F37C6917FCD98 /* BottomRightSixteenthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomRightSixteenthCalculation.swift; sourceTree = ""; }; 729E0A972AFF76B1006E2F48 /* CenterProminentlyCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterProminentlyCalculation.swift; sourceTree = ""; }; 74804F0A2E25521C009F1F7D /* CenterTwoThirdsCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterTwoThirdsCalculation.swift; sourceTree = ""; }; 7BE578EE2C5BF4ED0083DAE3 /* CycleSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CycleSize.swift; sourceTree = ""; }; @@ -492,6 +552,36 @@ 6490B39C27BF984D0056C220 /* BottomCenterLeftEighthCalculation.swift */, 6490B39E27BF98840056C220 /* BottomCenterRightEighthCalculation.swift */, 6490B3A027BF98C70056C220 /* BottomRightEighthCalculation.swift */, + F3592F8E85CE82B8FA662A31 /* TwelfthsRepeated.swift */, + D9FDCA1333C1BF6930A8D7F3 /* TopLeftTwelfthCalculation.swift */, + 9698D1D9EE0180D7C4468304 /* TopCenterLeftTwelfthCalculation.swift */, + A4D034DBD7EFAC4CF5F8D67B /* TopCenterRightTwelfthCalculation.swift */, + 7E8357E928530D10A806F3CD /* TopRightTwelfthCalculation.swift */, + 246168B345ECE6C375ACEEB7 /* BottomLeftTwelfthCalculation.swift */, + 71FE3EBE50F91F9EE2989279 /* BottomCenterLeftTwelfthCalculation.swift */, + C1A362206B9F76C269D8F469 /* BottomCenterRightTwelfthCalculation.swift */, + D7D038C68560DC11F14496F6 /* BottomRightTwelfthCalculation.swift */, + EEABF10E66D648DDB37DABBA /* MiddleLeftTwelfthCalculation.swift */, + 5070DE9100E45D374053F0C2 /* MiddleCenterLeftTwelfthCalculation.swift */, + BCF2AC59501448BD3BAA16EC /* MiddleCenterRightTwelfthCalculation.swift */, + AA84AB4DB9E7EEFCE50AA6D8 /* MiddleRightTwelfthCalculation.swift */, + 16B4044DB4809116039F96EB /* SixteenthsRepeated.swift */, + 6294D2CB6E3D8ED982F418AE /* TopLeftSixteenthCalculation.swift */, + 5BE9E5E2C8BA2590DD0B6F59 /* TopCenterLeftSixteenthCalculation.swift */, + 918F847FF15D524B6DB2E6DA /* TopCenterRightSixteenthCalculation.swift */, + FD222258713B13274FB651B0 /* TopRightSixteenthCalculation.swift */, + 84E2DBE0733F31BC159BC99C /* UpperMiddleLeftSixteenthCalculation.swift */, + 8B8CA6901E6FE134E930135B /* UpperMiddleCenterLeftSixteenthCalculation.swift */, + BEBF3EA35F0C110A170EAD33 /* UpperMiddleCenterRightSixteenthCalculation.swift */, + A3B5B09465C38F8B766B3C3C /* UpperMiddleRightSixteenthCalculation.swift */, + C796BCAA3DA5F85B55A7EE83 /* LowerMiddleLeftSixteenthCalculation.swift */, + 378C96D832D4CC6BCCF9D604 /* LowerMiddleCenterLeftSixteenthCalculation.swift */, + 8AE08B666CCCD1805CAB08BD /* LowerMiddleCenterRightSixteenthCalculation.swift */, + 87EC57F37CFD790695177AC8 /* LowerMiddleRightSixteenthCalculation.swift */, + FEE147786B2CD7350FAFB700 /* BottomLeftSixteenthCalculation.swift */, + 88454ABE5C62BD610AEA3EB5 /* BottomCenterLeftSixteenthCalculation.swift */, + D24EBF1F9A6209449A988E15 /* BottomCenterRightSixteenthCalculation.swift */, + 482EA970820F37C6917FCD98 /* BottomRightSixteenthCalculation.swift */, AA69F83B29909A95001A81AF /* RightTodoCalculation.swift */, AA69F83F2992DCB1001A81AF /* LeftTodoCalculation.swift */, B4521F922BD7CEFB00FD43CC /* ChangeWindowDimensionCalculation.swift */, @@ -1006,6 +1096,36 @@ D04CE30E27817AB500BD47B3 /* BottomCenterNinthCalculation.swift in Sources */, 6490B39727BF96EA0056C220 /* TopCenterLeftEighthCalculation.swift in Sources */, 9818E00D28B59205004AA524 /* CompoundSnapArea.swift in Sources */, + 7EC9E3BAD5BC57E49A7BCEE5 /* TwelfthsRepeated.swift in Sources */, + BAB3F495AD1F3454FAA31AFB /* TopLeftTwelfthCalculation.swift in Sources */, + 924919761FC491952752A147 /* TopCenterLeftTwelfthCalculation.swift in Sources */, + 14DBE979E4453E13A3BF5C17 /* TopCenterRightTwelfthCalculation.swift in Sources */, + F4EED30B47FB6C55EA937254 /* TopRightTwelfthCalculation.swift in Sources */, + BF9871B3BCF0E652444E0C56 /* BottomLeftTwelfthCalculation.swift in Sources */, + 52953B57D48A4F110779E9BD /* BottomCenterLeftTwelfthCalculation.swift in Sources */, + 666598377754D51045353CD0 /* BottomCenterRightTwelfthCalculation.swift in Sources */, + 15E8CA7F6AC3EA2C4FB1424C /* BottomRightTwelfthCalculation.swift in Sources */, + 05DBEA44B55E6F8C5FC3D6E0 /* MiddleLeftTwelfthCalculation.swift in Sources */, + 9FDA3D672AE475B83CA1A34A /* MiddleCenterLeftTwelfthCalculation.swift in Sources */, + DCD3CF2993E9AFAF30820117 /* MiddleCenterRightTwelfthCalculation.swift in Sources */, + 8B1D14E3A55936EBCCC1E807 /* MiddleRightTwelfthCalculation.swift in Sources */, + B6366EE24FA928086846969B /* SixteenthsRepeated.swift in Sources */, + 3FA1D2E6C9B4623227F680CF /* TopLeftSixteenthCalculation.swift in Sources */, + A0844B53B14365D0278BC124 /* TopCenterLeftSixteenthCalculation.swift in Sources */, + A32D3739AA16C537F6427C4F /* TopCenterRightSixteenthCalculation.swift in Sources */, + F51A7467767F8EAF97321A00 /* TopRightSixteenthCalculation.swift in Sources */, + D8644560FF112B0748CE1975 /* UpperMiddleLeftSixteenthCalculation.swift in Sources */, + E791B00A88D90C24A4C3D2D1 /* UpperMiddleCenterLeftSixteenthCalculation.swift in Sources */, + 895C73559E1ACEFBBE8DC32D /* UpperMiddleCenterRightSixteenthCalculation.swift in Sources */, + 0F29956694B5BB31EBDAE445 /* UpperMiddleRightSixteenthCalculation.swift in Sources */, + 562018A56BC897E769500F1F /* LowerMiddleLeftSixteenthCalculation.swift in Sources */, + 88763FEFFA3AECF8502604D6 /* LowerMiddleCenterLeftSixteenthCalculation.swift in Sources */, + A73749B5FB00D2CC2DB08744 /* LowerMiddleCenterRightSixteenthCalculation.swift in Sources */, + 4266090403BAF4B8A654A28B /* LowerMiddleRightSixteenthCalculation.swift in Sources */, + F7B6C43BB57F8064F49DDBB6 /* BottomLeftSixteenthCalculation.swift in Sources */, + 9988C2A18B6AF03E09204981 /* BottomCenterLeftSixteenthCalculation.swift in Sources */, + 630644571003EB468CB643D8 /* BottomCenterRightSixteenthCalculation.swift in Sources */, + 2D240EE416CC03AC921E3A4F /* BottomRightSixteenthCalculation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..24e5c8ab --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomCenterLeftSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftSixteenthTemplate.imageset/bottomCenterLeftSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftSixteenthTemplate.imageset/bottomCenterLeftSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..45be3069abb583712fea526979ad4d1be101c924 GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E zvOmfu>*I=}i^V_;!&I@yt8cmK%2|nBmStw}jkB~lE%;0rsFT6d)z4*}Q$iB}Y91a& literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..c0a66496 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomCenterLeftTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftTwelfthTemplate.imageset/bottomCenterLeftTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterLeftTwelfthTemplate.imageset/bottomCenterLeftTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..a623464406e01a2bdda5bc0854ebf25b01ac820a GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E zvOmfu>*I=}i^ck*d}6ylihcpHY$|k@S+g+|esrAWB5Y|2)XCuK>gTe~DWM4fkE$PC literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..8f77fcd0 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomCenterRightSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightSixteenthTemplate.imageset/bottomCenterRightSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightSixteenthTemplate.imageset/bottomCenterRightSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..326b80ae0ca09a2e4ce3ca908507f2d8999c9338 GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E zvOmfu>*I=}i^V_;!?!xe!r%5r+3e6+X3fS>vBA=&?T$hjP$z?@tDnm{r-UW|oO>WZ literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..fc4cc851 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomCenterRightTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightTwelfthTemplate.imageset/bottomCenterRightTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomCenterRightTwelfthTemplate.imageset/bottomCenterRightTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..9652cbad7c487dab6ceb8e7a341a5d9a8b957383 GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E zvOmfu>*I=}i^ck*eC)eFhBARzHXFn)%Q7=;dgC~&xxY^nsFT6d)z4*}Q$iB}jv62O literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..c49ec0ad --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomLeftSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixteenthTemplate.imageset/bottomLeftSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixteenthTemplate.imageset/bottomLeftSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..b55028b7901dee03a7a4375b0fbac259aa644d48 GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq;x!8978H@B_|{#{IGYB@L6(E xvOmfu>*I=}i^V_;!^G~74+Fc~_a8Mm$e_eyX(Rvk6gyBKgQu&X%Q~loCIDfQAF==d literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomLeftTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomLeftTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..fc4bb7bc --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomLeftTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomLeftTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomLeftTwelfthTemplate.imageset/bottomLeftTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomLeftTwelfthTemplate.imageset/bottomLeftTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..b370ca0b85fbcc8b86a68614294a5779b2714ccd GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq;x!8978H@B_|{#{IGYB@L6(E xvOmfu>*I=}i^ck*e0ClE@d3o-x9`quVl?>XIP1(kbzh)922WQ%mvv4FO#qOcA$|Y= literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomRightSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomRightSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..fb1959ef --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomRightSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomRightSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomRightSixteenthTemplate.imageset/bottomRightSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomRightSixteenthTemplate.imageset/bottomRightSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..5c35a0423c44483688bacad6e36e8bc57006b272 GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E yvOmfu>*I=}i^af<0}(a}S0Al78lk()nvG!zlcmkd_noakoeZ9?elF{r5}E+Iy&+!! literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomRightTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomRightTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..6a38605d --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomRightTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomRightTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomRightTwelfthTemplate.imageset/bottomRightTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomRightTwelfthTemplate.imageset/bottomRightTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..666c66446e35f159b0545aa7bb6c6920590d130f GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq;x%9978H@B_|{#{IGYB@L6(E zvOmfu>*I=}i^YHp3mv%)UP)IT1$MJ9d&$i3`MKk)vR{_(ff^Y+UHx3vIVCg!0Ej~& AQ~&?~ literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterLeftSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterLeftSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..3d502f39 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterLeftSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lowerMiddleCenterLeftSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterLeftSixteenthTemplate.imageset/lowerMiddleCenterLeftSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterLeftSixteenthTemplate.imageset/lowerMiddleCenterLeftSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..9021985c10867c7e6e1827bd275ac6cd9927f5ad GIT binary patch literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq_jO<978H@B_|{#{IGYB@L6(E zvOmfu>*I=}%14rFmY!d6bg@`}+Va5em;H<%g>18n*e5*)YGd$p^>bP0l+XkKY{(%C literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterRightSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterRightSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..a31e1962 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterRightSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lowerMiddleCenterRightSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterRightSixteenthTemplate.imageset/lowerMiddleCenterRightSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleCenterRightSixteenthTemplate.imageset/lowerMiddleCenterRightSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..66347cd023d5508591d8dc4a08f2aaf3373cd9cb GIT binary patch literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq_jO<978H@B_|{#{IGYB@L6(E yvOmfu>*I=}i^U%6&UDk2+gf2_-7C=jvX61OgKc(1X6zlHHU>{uKbLh*2~7Yi5FXY5 literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleLeftSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleLeftSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..d8053b81 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleLeftSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lowerMiddleLeftSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleLeftSixteenthTemplate.imageset/lowerMiddleLeftSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleLeftSixteenthTemplate.imageset/lowerMiddleLeftSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..eb81db8881c7ea7f0b6e5abd1af51461bba75522 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq;x%9978H@B_|{#{IGYB@L6(E zvOmfu>*I=}hfgHcR9tx!*zLag@`|G=>Uy&h7_2_qW;>ic1u})f)78&qol`;+0LYFa AN&o-= literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleRightSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleRightSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..4fdca71a --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleRightSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lowerMiddleRightSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleRightSixteenthTemplate.imageset/lowerMiddleRightSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/lowerMiddleRightSixteenthTemplate.imageset/lowerMiddleRightSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..01c5fa35ac5157c657178e0027dd398fc8364875 GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U*q978H@B_|{#{IGYB@L6(E zvOmfu>*I=}i^YUZY8<@Q1$MXZKf3s5MM0!bIS)f@o^AGrHy_o2S{XcD{an^LB{Ts5 D&7L98 literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleCenterLeftTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/middleCenterLeftTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..37e6e9e5 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/middleCenterLeftTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "middleCenterLeftTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleCenterLeftTwelfthTemplate.imageset/middleCenterLeftTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/middleCenterLeftTwelfthTemplate.imageset/middleCenterLeftTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4d2b2deb3a075837a473ece2dc2aede0065f46 GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U*q978H@B_|{#{IGYB@L6(E zvOmfu>*Il_9)0f~5bMI0iY*M1TPrf6e9CzkmR@t5W$h(h4%Euv>FVdQ&MBb@0JfeW AMgRZ+ literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleCenterRightTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/middleCenterRightTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..98574dce --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/middleCenterRightTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "middleCenterRightTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleCenterRightTwelfthTemplate.imageset/middleCenterRightTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/middleCenterRightTwelfthTemplate.imageset/middleCenterRightTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..0f51d5a2898f6001394108d395cfd61fbfb4cd05 GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U*q978H@B_|{#{IGYB@L6(E zvOmfu>*Is167gk{TPv=%* BBvSwY literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleLeftTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/middleLeftTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..c8422525 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/middleLeftTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "middleLeftTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleLeftTwelfthTemplate.imageset/middleLeftTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/middleLeftTwelfthTemplate.imageset/middleLeftTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..00ab546b635a617b33ebba5ff6a5011f1904353a GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E zvOmfu%kZu4v37S|xvdo$Q8rNxJyABBPG!wXU`V;`IBVDEs5wBL44$rjF6*2UngE5t BAh7@d literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleRightTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/middleRightTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..243f6f2c --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/middleRightTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "middleRightTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleRightTwelfthTemplate.imageset/middleRightTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/middleRightTwelfthTemplate.imageset/middleRightTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..75d059568816697896539f8463476e5aa579e75f GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq;x!8978H@B_|{#{IGYB@L6(E zvOmfu>*Iwj6$dt5UU77>SpT!ff!*yZkCq%~P`~Xst2!cNKTscor>mdKI;Vst0LVfk AKmY&$ literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..1e44ee17 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "topCenterLeftSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftSixteenthTemplate.imageset/topCenterLeftSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftSixteenthTemplate.imageset/topCenterLeftSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..aafcec98061c2697af8caeb8f7e265027f3921c2 GIT binary patch literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnqzpY>978H@B_|{#{IGYB@LBTk z;184K6Fc2?wSVVKzcvX7DfxuuPC%QIo1W(H4JKbLh*2~7YD Cupxo~ literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..d8aed023 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "topCenterLeftTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftTwelfthTemplate.imageset/topCenterLeftTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/topCenterLeftTwelfthTemplate.imageset/topCenterLeftTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..7134c0f2306e9f283582c05c666b886809cd545c GIT binary patch literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnqzpY>978H@B_|{#{IGYB@LBTk z;184K6Fc2?<+fHN=q_UgQdb@Yc7ymcO1fY6G2YzmIO~#znI%v&gQu&X%Q~loCIBxd BBHjQ1 literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/topCenterRightSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/topCenterRightSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..7ca52843 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/topCenterRightSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "topCenterRightSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/topCenterRightSixteenthTemplate.imageset/topCenterRightSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/topCenterRightSixteenthTemplate.imageset/topCenterRightSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..316ff8949f6c51b1c949fe2c19d30b90b644c6d1 GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq>MaW978H@B_|{#{IGYB@L4jk z^P{AX$Nd#Yoew8@$=GClTyb=MaW978H@B_|{#{IGYB@L4jk z^P{AX$Nd#Y7mM-xb!!%W0I~X`Y_dMCIJzX#r<{jD>Zs$ap2v=BfVvqxUHx3vIVCg! E08wcpasU7T literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/topLeftSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/topLeftSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..74a37e04 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/topLeftSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "topLeftSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/topLeftSixteenthTemplate.imageset/topLeftSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/topLeftSixteenthTemplate.imageset/topLeftSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab35143ee1ef320c24bb2eb0957d0906fe54d98 GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U*q978H@B_|{#{IGYJv7pIB z>Oo-l%ltyAxgS>?T`blgWs?PF1c&KfVqs=@(q?HR=JVMXsFlIf)z4*}Q$iB}?7|^* literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/topLeftTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/topLeftTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..2ff2de65 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/topLeftTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "topLeftTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/topLeftTwelfthTemplate.imageset/topLeftTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/topLeftTwelfthTemplate.imageset/topLeftTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..da0b1976dc642d378663b8b84e90d4992ddd2464 GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U*q978H@B_|{#{IGYJv7pIB z>Oo+)yY8~RM>9)~Ef(vKvdQ|m;^<%Uj^!KPHDpNZwzN^s)tCd+$l&Sf=d#Wzp$Pz9ryl75 literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/topRightTwelfthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/topRightTwelfthTemplate.imageset/Contents.json new file mode 100644 index 00000000..df8b07e5 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/topRightTwelfthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "topRightTwelfthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/topRightTwelfthTemplate.imageset/topRightTwelfthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/topRightTwelfthTemplate.imageset/topRightTwelfthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..af0e52e4efe4b5ba601824a29878fa148f890cf1 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq;x%9978H@B_|{#{IGYB@L6(^ zp+Cwd>*I&bGbSu{)0Nv=aph59x4W*|*`!^`4ABQ1XW6dX`WL8?!PC{xWt~$(69A;@ BA<+N; literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterLeftSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterLeftSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..11ae38ed --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterLeftSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "upperMiddleCenterLeftSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterLeftSixteenthTemplate.imageset/upperMiddleCenterLeftSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterLeftSixteenthTemplate.imageset/upperMiddleCenterLeftSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..5f27f2961bd28285b71a2bdcba82780d25a831fe GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E zvOmgZht9F-4sN<~TPsYgd!uZ!KCU>rW%G=85)8@lw%MYhPCP)J44$rjF6*2UngDYq B9@zi@ literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterRightSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterRightSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..f8108342 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterRightSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "upperMiddleCenterRightSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterRightSixteenthTemplate.imageset/upperMiddleCenterRightSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleCenterRightSixteenthTemplate.imageset/upperMiddleCenterRightSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..e8fe68ccfee302c14a181b8ba76ac388631eb128 GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnr1U&p978H@B_|{#{IGYB@L6(E zvOmhEMCaJ_3O8N3traHLy-_w> BAgBNU literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/upperMiddleRightSixteenthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleRightSixteenthTemplate.imageset/Contents.json new file mode 100644 index 00000000..25e1b011 --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleRightSixteenthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "upperMiddleRightSixteenthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/upperMiddleRightSixteenthTemplate.imageset/upperMiddleRightSixteenthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/upperMiddleRightSixteenthTemplate.imageset/upperMiddleRightSixteenthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..950e23c1404bb30f8cd90af06e0abbfaab44de34 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq;x%9978H@B_|{#{IGYB@L6(E zvOmhkMn`VL5!0-XAGTMR$OHLVA2~M8cqhSdevNImh+)B3phgByS3j3^P6 NSView { + let separator = createSectionSeparator() + + let label = NSTextField(labelWithString: title) + label.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize, weight: .semibold) + label.textColor = .secondaryLabelColor + label.alignment = .left + label.translatesAutoresizingMaskIntoConstraints = false + + let container = NSStackView(views: [separator, label]) + container.orientation = .vertical + container.alignment = .leading + container.spacing = 4 + container.translatesAutoresizingMaskIntoConstraints = false + return container + } + + private func createSectionSeparator() -> NSBox { + let box = NSBox() + box.boxType = .separator + box.translatesAutoresizingMaskIntoConstraints = false + return box + } + + private func createShortcutRow(for action: WindowAction) -> NSStackView { + let shortcutView = MASShortcutView() + shortcutView.translatesAutoresizingMaskIntoConstraints = false + shortcutView.widthAnchor.constraint(equalToConstant: 160).isActive = true + shortcutView.heightAnchor.constraint(equalToConstant: 19).isActive = true + + let label = action.displayName ?? action.name + let textField = NSTextField(labelWithString: label) + textField.alignment = .right + textField.lineBreakMode = .byClipping + textField.translatesAutoresizingMaskIntoConstraints = false + textField.setContentHuggingPriority(.init(251), for: .horizontal) + textField.setContentHuggingPriority(.init(750), for: .vertical) + + let imageView = NSImageView() + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.widthAnchor.constraint(equalToConstant: 21).isActive = true + imageView.heightAnchor.constraint(equalToConstant: 14).isActive = true + imageView.image = action.image + imageView.imageScaling = .scaleProportionallyDown + imageView.setContentHuggingPriority(.init(251), for: .horizontal) + imageView.setContentHuggingPriority(.init(251), for: .vertical) + + let labelStack = NSStackView(views: [textField, imageView]) + labelStack.orientation = .horizontal + labelStack.alignment = .centerY + labelStack.distribution = .fill + + let row = NSStackView(views: [labelStack, shortcutView]) + row.orientation = .horizontal + row.alignment = .centerY + row.distribution = .fill + row.spacing = 18 + + actionsToViews[action] = shortcutView + return row } @IBAction func toggleShowMore(_ sender: NSButton) { diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index 4ca3104f..6fc6f816 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -104,7 +104,35 @@ enum WindowAction: Int, Codable { middleVerticalThird = 88, bottomVerticalThird = 89, topVerticalTwoThirds = 90, - bottomVerticalTwoThirds = 91 + bottomVerticalTwoThirds = 91, + topLeftTwelfth = 92, + topCenterLeftTwelfth = 93, + topCenterRightTwelfth = 94, + topRightTwelfth = 95, + middleLeftTwelfth = 96, + middleCenterLeftTwelfth = 97, + middleCenterRightTwelfth = 98, + middleRightTwelfth = 99, + bottomLeftTwelfth = 100, + bottomCenterLeftTwelfth = 101, + bottomCenterRightTwelfth = 102, + bottomRightTwelfth = 103, + topLeftSixteenth = 104, + topCenterLeftSixteenth = 105, + topCenterRightSixteenth = 106, + topRightSixteenth = 107, + upperMiddleLeftSixteenth = 108, + upperMiddleCenterLeftSixteenth = 109, + upperMiddleCenterRightSixteenth = 110, + upperMiddleRightSixteenth = 111, + lowerMiddleLeftSixteenth = 112, + lowerMiddleCenterLeftSixteenth = 113, + lowerMiddleCenterRightSixteenth = 114, + lowerMiddleRightSixteenth = 115, + bottomLeftSixteenth = 116, + bottomCenterLeftSixteenth = 117, + bottomCenterRightSixteenth = 118, + bottomRightSixteenth = 119 // Order matters here - it's used in the menu static let active = [leftHalf, rightHalf, centerHalf, topHalf, bottomHalf, @@ -124,6 +152,13 @@ enum WindowAction: Int, Codable { topLeftThird, topRightThird, bottomLeftThird, bottomRightThird, topLeftEighth, topCenterLeftEighth, topCenterRightEighth, topRightEighth, bottomLeftEighth, bottomCenterLeftEighth, bottomCenterRightEighth, bottomRightEighth, + topLeftTwelfth, topCenterLeftTwelfth, topCenterRightTwelfth, topRightTwelfth, + middleLeftTwelfth, middleCenterLeftTwelfth, middleCenterRightTwelfth, middleRightTwelfth, + bottomLeftTwelfth, bottomCenterLeftTwelfth, bottomCenterRightTwelfth, bottomRightTwelfth, + topLeftSixteenth, topCenterLeftSixteenth, topCenterRightSixteenth, topRightSixteenth, + upperMiddleLeftSixteenth, upperMiddleCenterLeftSixteenth, upperMiddleCenterRightSixteenth, upperMiddleRightSixteenth, + lowerMiddleLeftSixteenth, lowerMiddleCenterLeftSixteenth, lowerMiddleCenterRightSixteenth, lowerMiddleRightSixteenth, + bottomLeftSixteenth, bottomCenterLeftSixteenth, bottomCenterRightSixteenth, bottomRightSixteenth, doubleHeightUp, doubleHeightDown, doubleWidthLeft, doubleWidthRight, halveHeightUp, halveHeightDown, halveWidthLeft, halveWidthRight, tileAll, cascadeAll, @@ -154,7 +189,7 @@ enum WindowAction: Int, Codable { // Determines where separators should be used in the menu var firstInGroup: Bool { switch self { - case .leftHalf, .topLeft, .firstThird, .maximize, .nextDisplay, .moveLeft, .firstFourth, .topLeftSixth, .topLeftEighth: + case .leftHalf, .topLeft, .firstThird, .maximize, .almostMaximize, .nextDisplay, .moveLeft, .firstFourth, .topLeftSixth, .topLeftEighth, .topLeftTwelfth, .topLeftSixteenth: return true default: return false @@ -251,6 +286,34 @@ enum WindowAction: Int, Codable { case .bottomVerticalThird: return "bottomVerticalThird" case .topVerticalTwoThirds: return "topVerticalTwoThirds" case .bottomVerticalTwoThirds: return "bottomVerticalTwoThirds" + case .topLeftTwelfth: return "topLeftTwelfth" + case .topCenterLeftTwelfth: return "topCenterLeftTwelfth" + case .topCenterRightTwelfth: return "topCenterRightTwelfth" + case .topRightTwelfth: return "topRightTwelfth" + case .middleLeftTwelfth: return "middleLeftTwelfth" + case .middleCenterLeftTwelfth: return "middleCenterLeftTwelfth" + case .middleCenterRightTwelfth: return "middleCenterRightTwelfth" + case .middleRightTwelfth: return "middleRightTwelfth" + case .bottomLeftTwelfth: return "bottomLeftTwelfth" + case .bottomCenterLeftTwelfth: return "bottomCenterLeftTwelfth" + case .bottomCenterRightTwelfth: return "bottomCenterRightTwelfth" + case .bottomRightTwelfth: return "bottomRightTwelfth" + case .topLeftSixteenth: return "topLeftSixteenth" + case .topCenterLeftSixteenth: return "topCenterLeftSixteenth" + case .topCenterRightSixteenth: return "topCenterRightSixteenth" + case .topRightSixteenth: return "topRightSixteenth" + case .upperMiddleLeftSixteenth: return "upperMiddleLeftSixteenth" + case .upperMiddleCenterLeftSixteenth: return "upperMiddleCenterLeftSixteenth" + case .upperMiddleCenterRightSixteenth: return "upperMiddleCenterRightSixteenth" + case .upperMiddleRightSixteenth: return "upperMiddleRightSixteenth" + case .lowerMiddleLeftSixteenth: return "lowerMiddleLeftSixteenth" + case .lowerMiddleCenterLeftSixteenth: return "lowerMiddleCenterLeftSixteenth" + case .lowerMiddleCenterRightSixteenth: return "lowerMiddleCenterRightSixteenth" + case .lowerMiddleRightSixteenth: return "lowerMiddleRightSixteenth" + case .bottomLeftSixteenth: return "bottomLeftSixteenth" + case .bottomCenterLeftSixteenth: return "bottomCenterLeftSixteenth" + case .bottomCenterRightSixteenth: return "bottomCenterRightSixteenth" + case .bottomRightSixteenth: return "bottomRightSixteenth" } } @@ -418,6 +481,90 @@ enum WindowAction: Int, Codable { return nil case .topVerticalThird, .middleVerticalThird, .bottomVerticalThird, .topVerticalTwoThirds, .bottomVerticalTwoThirds: return nil + case .topLeftTwelfth: + key = "topLeftTwelfth.title" + value = "Top Left Twelfth" + case .topCenterLeftTwelfth: + key = "topCenterLeftTwelfth.title" + value = "Top Center Left Twelfth" + case .topCenterRightTwelfth: + key = "topCenterRightTwelfth.title" + value = "Top Center Right Twelfth" + case .topRightTwelfth: + key = "topRightTwelfth.title" + value = "Top Right Twelfth" + case .middleLeftTwelfth: + key = "middleLeftTwelfth.title" + value = "Middle Left Twelfth" + case .middleCenterLeftTwelfth: + key = "middleCenterLeftTwelfth.title" + value = "Middle Center Left Twelfth" + case .middleCenterRightTwelfth: + key = "middleCenterRightTwelfth.title" + value = "Middle Center Right Twelfth" + case .middleRightTwelfth: + key = "middleRightTwelfth.title" + value = "Middle Right Twelfth" + case .bottomLeftTwelfth: + key = "bottomLeftTwelfth.title" + value = "Bottom Left Twelfth" + case .bottomCenterLeftTwelfth: + key = "bottomCenterLeftTwelfth.title" + value = "Bottom Center Left Twelfth" + case .bottomCenterRightTwelfth: + key = "bottomCenterRightTwelfth.title" + value = "Bottom Center Right Twelfth" + case .bottomRightTwelfth: + key = "bottomRightTwelfth.title" + value = "Bottom Right Twelfth" + case .topLeftSixteenth: + key = "topLeftSixteenth.title" + value = "Top Left Sixteenth" + case .topCenterLeftSixteenth: + key = "topCenterLeftSixteenth.title" + value = "Top Center Left Sixteenth" + case .topCenterRightSixteenth: + key = "topCenterRightSixteenth.title" + value = "Top Center Right Sixteenth" + case .topRightSixteenth: + key = "topRightSixteenth.title" + value = "Top Right Sixteenth" + case .upperMiddleLeftSixteenth: + key = "upperMiddleLeftSixteenth.title" + value = "Upper Middle Left Sixteenth" + case .upperMiddleCenterLeftSixteenth: + key = "upperMiddleCenterLeftSixteenth.title" + value = "Upper Mid Center Left 16th" + case .upperMiddleCenterRightSixteenth: + key = "upperMiddleCenterRightSixteenth.title" + value = "Upper Mid Center Right 16th" + case .upperMiddleRightSixteenth: + key = "upperMiddleRightSixteenth.title" + value = "Upper Middle Right Sixteenth" + case .lowerMiddleLeftSixteenth: + key = "lowerMiddleLeftSixteenth.title" + value = "Lower Middle Left Sixteenth" + case .lowerMiddleCenterLeftSixteenth: + key = "lowerMiddleCenterLeftSixteenth.title" + value = "Lower Mid Center Left 16th" + case .lowerMiddleCenterRightSixteenth: + key = "lowerMiddleCenterRightSixteenth.title" + value = "Lower Mid Center Right 16th" + case .lowerMiddleRightSixteenth: + key = "lowerMiddleRightSixteenth.title" + value = "Lower Middle Right Sixteenth" + case .bottomLeftSixteenth: + key = "bottomLeftSixteenth.title" + value = "Bottom Left Sixteenth" + case .bottomCenterLeftSixteenth: + key = "bottomCenterLeftSixteenth.title" + value = "Bottom Center Left Sixteenth" + case .bottomCenterRightSixteenth: + key = "bottomCenterRightSixteenth.title" + value = "Bottom Center Right Sixteenth" + case .bottomRightSixteenth: + key = "bottomRightSixteenth.title" + value = "Bottom Right Sixteenth" } return NSLocalizedString(key, tableName: "Main", value: value, comment: "") @@ -602,6 +749,34 @@ enum WindowAction: Int, Codable { case .bottomVerticalThird: return NSImage(imageLiteralResourceName: "bottomThirdTemplate") case .topVerticalTwoThirds: return NSImage(imageLiteralResourceName: "topTwoThirdsTemplate") case .bottomVerticalTwoThirds: return NSImage(imageLiteralResourceName: "bottomTwoThirdsTemplate") + case .topLeftTwelfth: return NSImage(imageLiteralResourceName: "topLeftTwelfthTemplate") + case .topCenterLeftTwelfth: return NSImage(imageLiteralResourceName: "topCenterLeftTwelfthTemplate") + case .topCenterRightTwelfth: return NSImage(imageLiteralResourceName: "topCenterRightTwelfthTemplate") + case .topRightTwelfth: return NSImage(imageLiteralResourceName: "topRightTwelfthTemplate") + case .middleLeftTwelfth: return NSImage(imageLiteralResourceName: "middleLeftTwelfthTemplate") + case .middleCenterLeftTwelfth: return NSImage(imageLiteralResourceName: "middleCenterLeftTwelfthTemplate") + case .middleCenterRightTwelfth: return NSImage(imageLiteralResourceName: "middleCenterRightTwelfthTemplate") + case .middleRightTwelfth: return NSImage(imageLiteralResourceName: "middleRightTwelfthTemplate") + case .bottomLeftTwelfth: return NSImage(imageLiteralResourceName: "bottomLeftTwelfthTemplate") + case .bottomCenterLeftTwelfth: return NSImage(imageLiteralResourceName: "bottomCenterLeftTwelfthTemplate") + case .bottomCenterRightTwelfth: return NSImage(imageLiteralResourceName: "bottomCenterRightTwelfthTemplate") + case .bottomRightTwelfth: return NSImage(imageLiteralResourceName: "bottomRightTwelfthTemplate") + case .topLeftSixteenth: return NSImage(imageLiteralResourceName: "topLeftSixteenthTemplate") + case .topCenterLeftSixteenth: return NSImage(imageLiteralResourceName: "topCenterLeftSixteenthTemplate") + case .topCenterRightSixteenth: return NSImage(imageLiteralResourceName: "topCenterRightSixteenthTemplate") + case .topRightSixteenth: return NSImage(imageLiteralResourceName: "topRightSixteenthTemplate") + case .upperMiddleLeftSixteenth: return NSImage(imageLiteralResourceName: "upperMiddleLeftSixteenthTemplate") + case .upperMiddleCenterLeftSixteenth: return NSImage(imageLiteralResourceName: "upperMiddleCenterLeftSixteenthTemplate") + case .upperMiddleCenterRightSixteenth: return NSImage(imageLiteralResourceName: "upperMiddleCenterRightSixteenthTemplate") + case .upperMiddleRightSixteenth: return NSImage(imageLiteralResourceName: "upperMiddleRightSixteenthTemplate") + case .lowerMiddleLeftSixteenth: return NSImage(imageLiteralResourceName: "lowerMiddleLeftSixteenthTemplate") + case .lowerMiddleCenterLeftSixteenth: return NSImage(imageLiteralResourceName: "lowerMiddleCenterLeftSixteenthTemplate") + case .lowerMiddleCenterRightSixteenth: return NSImage(imageLiteralResourceName: "lowerMiddleCenterRightSixteenthTemplate") + case .lowerMiddleRightSixteenth: return NSImage(imageLiteralResourceName: "lowerMiddleRightSixteenthTemplate") + case .bottomLeftSixteenth: return NSImage(imageLiteralResourceName: "bottomLeftSixteenthTemplate") + case .bottomCenterLeftSixteenth: return NSImage(imageLiteralResourceName: "bottomCenterLeftSixteenthTemplate") + case .bottomCenterRightSixteenth: return NSImage(imageLiteralResourceName: "bottomCenterRightSixteenthTemplate") + case .bottomRightSixteenth: return NSImage(imageLiteralResourceName: "bottomRightSixteenthTemplate") } } @@ -632,6 +807,13 @@ enum WindowAction: Int, Codable { .topLeftThird, .topRightThird, .bottomLeftThird, .bottomRightThird, .topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth, .bottomLeftEighth, .bottomCenterLeftEighth, .bottomCenterRightEighth, .bottomRightEighth, + .topLeftTwelfth, .topCenterLeftTwelfth, .topCenterRightTwelfth, .topRightTwelfth, + .middleLeftTwelfth, .middleCenterLeftTwelfth, .middleCenterRightTwelfth, .middleRightTwelfth, + .bottomLeftTwelfth, .bottomCenterLeftTwelfth, .bottomCenterRightTwelfth, .bottomRightTwelfth, + .topLeftSixteenth, .topCenterLeftSixteenth, .topCenterRightSixteenth, .topRightSixteenth, + .upperMiddleLeftSixteenth, .upperMiddleCenterLeftSixteenth, .upperMiddleCenterRightSixteenth, .upperMiddleRightSixteenth, + .lowerMiddleLeftSixteenth, .lowerMiddleCenterLeftSixteenth, .lowerMiddleCenterRightSixteenth, .lowerMiddleRightSixteenth, + .bottomLeftSixteenth, .bottomCenterLeftSixteenth, .bottomCenterRightSixteenth, .bottomRightSixteenth, .doubleHeightUp, .doubleHeightDown, .doubleWidthLeft, .doubleWidthRight, .halveHeightUp, .halveHeightDown, .halveWidthLeft, .halveWidthRight, .leftTodo, .rightTodo, @@ -655,11 +837,13 @@ enum WindowAction: Int, Codable { case .firstFourth, .secondFourth, .thirdFourth, .lastFourth, .firstThreeFourths, .centerThreeFourths, .lastThreeFourths: return .fourths case .topLeftSixth, .topCenterSixth, .topRightSixth, .bottomLeftSixth, .bottomCenterSixth, .bottomRightSixth: return .sixths case .topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth, .bottomLeftEighth, .bottomCenterLeftEighth, .bottomCenterRightEighth, .bottomRightEighth: return .eighths + case .topLeftTwelfth, .topCenterLeftTwelfth, .topCenterRightTwelfth, .topRightTwelfth, .middleLeftTwelfth, .middleCenterLeftTwelfth, .middleCenterRightTwelfth, .middleRightTwelfth, .bottomLeftTwelfth, .bottomCenterLeftTwelfth, .bottomCenterRightTwelfth, .bottomRightTwelfth: return .twelfths + case .topLeftSixteenth, .topCenterLeftSixteenth, .topCenterRightSixteenth, .topRightSixteenth, .upperMiddleLeftSixteenth, .upperMiddleCenterLeftSixteenth, .upperMiddleCenterRightSixteenth, .upperMiddleRightSixteenth, .lowerMiddleLeftSixteenth, .lowerMiddleCenterLeftSixteenth, .lowerMiddleCenterRightSixteenth, .lowerMiddleRightSixteenth, .bottomLeftSixteenth, .bottomCenterLeftSixteenth, .bottomCenterRightSixteenth, .bottomRightSixteenth: return .sixteenths case .moveUp, .moveDown, .moveLeft, .moveRight: return .move default: return nil } } - + var classification: WindowActionCategory? { switch self { case .firstThird, .firstTwoThirds, .centerThird, .centerTwoThirds, .lastTwoThirds, .lastThird: @@ -753,7 +937,37 @@ enum SubWindowAction { bottomCenterLeftEighth, bottomCenterRightEighth, bottomRightEighth, - + + topLeftTwelfth, + topCenterLeftTwelfth, + topCenterRightTwelfth, + topRightTwelfth, + middleLeftTwelfth, + middleCenterLeftTwelfth, + middleCenterRightTwelfth, + middleRightTwelfth, + bottomLeftTwelfth, + bottomCenterLeftTwelfth, + bottomCenterRightTwelfth, + bottomRightTwelfth, + + topLeftSixteenth, + topCenterLeftSixteenth, + topCenterRightSixteenth, + topRightSixteenth, + upperMiddleLeftSixteenth, + upperMiddleCenterLeftSixteenth, + upperMiddleCenterRightSixteenth, + upperMiddleRightSixteenth, + lowerMiddleLeftSixteenth, + lowerMiddleCenterLeftSixteenth, + lowerMiddleCenterRightSixteenth, + lowerMiddleRightSixteenth, + bottomLeftSixteenth, + bottomCenterLeftSixteenth, + bottomCenterRightSixteenth, + bottomRightSixteenth, + maximize, leftTodo, @@ -828,6 +1042,34 @@ enum SubWindowAction { case .bottomCenterLeftEighth: return [.right, .left, .top] case .bottomCenterRightEighth: return [.right, .left, .top] case .bottomRightEighth: return [.left, .top] + case .topLeftTwelfth: return [.right, .bottom] + case .topCenterLeftTwelfth: return [.right, .left, .bottom] + case .topCenterRightTwelfth: return [.right, .left, .bottom] + case .topRightTwelfth: return [.left, .bottom] + case .middleLeftTwelfth: return [.top, .right, .bottom] + case .middleCenterLeftTwelfth: return [.top, .right, .bottom, .left] + case .middleCenterRightTwelfth: return [.top, .right, .bottom, .left] + case .middleRightTwelfth: return [.left, .top, .bottom] + case .bottomLeftTwelfth: return [.top, .right] + case .bottomCenterLeftTwelfth: return [.left, .top, .right] + case .bottomCenterRightTwelfth: return [.left, .top, .right] + case .bottomRightTwelfth: return [.left, .top] + case .topLeftSixteenth: return [.right, .bottom] + case .topCenterLeftSixteenth: return [.right, .left, .bottom] + case .topCenterRightSixteenth: return [.right, .left, .bottom] + case .topRightSixteenth: return [.left, .bottom] + case .upperMiddleLeftSixteenth: return [.top, .right, .bottom] + case .upperMiddleCenterLeftSixteenth: return [.top, .right, .bottom, .left] + case .upperMiddleCenterRightSixteenth: return [.top, .right, .bottom, .left] + case .upperMiddleRightSixteenth: return [.left, .top, .bottom] + case .lowerMiddleLeftSixteenth: return [.top, .right, .bottom] + case .lowerMiddleCenterLeftSixteenth: return [.top, .right, .bottom, .left] + case .lowerMiddleCenterRightSixteenth: return [.top, .right, .bottom, .left] + case .lowerMiddleRightSixteenth: return [.left, .top, .bottom] + case .bottomLeftSixteenth: return [.top, .right] + case .bottomCenterLeftSixteenth: return [.left, .top, .right] + case .bottomCenterRightSixteenth: return [.left, .top, .right] + case .bottomRightSixteenth: return [.left, .top] case .maximize: return .none case .leftTodo: return .right case .rightTodo: return .left diff --git a/Rectangle/WindowActionCategory.swift b/Rectangle/WindowActionCategory.swift index d24051f2..9104f5f3 100644 --- a/Rectangle/WindowActionCategory.swift +++ b/Rectangle/WindowActionCategory.swift @@ -10,8 +10,8 @@ import Foundation enum WindowActionCategory { - case halves, corners, thirds, max, size, display, move, other, sixths, fourths, eighths - + case halves, corners, thirds, max, size, display, move, other, sixths, fourths, eighths, twelfths, sixteenths + var displayName: String { switch self { case .halves: @@ -36,6 +36,10 @@ enum WindowActionCategory { return NSLocalizedString("Sixths", tableName: "Main", value: "", comment: "") case .eighths: return NSLocalizedString("Eighths", tableName: "Main", value: "", comment: "") + case .twelfths: + return NSLocalizedString("Twelfths", tableName: "Main", value: "Twelfths", comment: "") + case .sixteenths: + return NSLocalizedString("Sixteenths", tableName: "Main", value: "Sixteenths", comment: "") } } } diff --git a/Rectangle/WindowCalculation/BottomCenterLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/BottomCenterLeftSixteenthCalculation.swift new file mode 100644 index 00000000..530eb8e9 --- /dev/null +++ b/Rectangle/WindowCalculation/BottomCenterLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomCenterLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomCenterLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomCenterLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .bottomCenterLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .bottomCenterLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift new file mode 100644 index 00000000..24266bff --- /dev/null +++ b/Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomCenterLeftTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomCenterLeftTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomCenterLeftTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .bottomCenterLeftTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .bottomCenterLeftTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/BottomCenterRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/BottomCenterRightSixteenthCalculation.swift new file mode 100644 index 00000000..63367ad7 --- /dev/null +++ b/Rectangle/WindowCalculation/BottomCenterRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomCenterRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomCenterRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomCenterRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .bottomCenterRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .bottomCenterRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift new file mode 100644 index 00000000..7ab068f5 --- /dev/null +++ b/Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomCenterRightTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomCenterRightTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomCenterRightTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 4.0) * 2.0 + return RectResult(rect, subAction: .bottomCenterRightTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + return RectResult(rect, subAction: .bottomCenterRightTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/BottomLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/BottomLeftSixteenthCalculation.swift new file mode 100644 index 00000000..4cd153ca --- /dev/null +++ b/Rectangle/WindowCalculation/BottomLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .bottomLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .bottomLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift new file mode 100644 index 00000000..5d706865 --- /dev/null +++ b/Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomLeftTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomLeftTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomLeftTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .bottomLeftTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .bottomLeftTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/BottomRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/BottomRightSixteenthCalculation.swift new file mode 100644 index 00000000..26073655 --- /dev/null +++ b/Rectangle/WindowCalculation/BottomRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .bottomRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .bottomRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/BottomRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/BottomRightTwelfthCalculation.swift new file mode 100644 index 00000000..7608bc24 --- /dev/null +++ b/Rectangle/WindowCalculation/BottomRightTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// BottomRightTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class BottomRightTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .bottomRightTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 4.0) * 3.0 + return RectResult(rect, subAction: .bottomRightTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + return RectResult(rect, subAction: .bottomRightTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/LowerMiddleCenterLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/LowerMiddleCenterLeftSixteenthCalculation.swift new file mode 100644 index 00000000..e95ad794 --- /dev/null +++ b/Rectangle/WindowCalculation/LowerMiddleCenterLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// LowerMiddleCenterLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class LowerMiddleCenterLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .lowerMiddleCenterLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .lowerMiddleCenterLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .lowerMiddleCenterLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/LowerMiddleCenterRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/LowerMiddleCenterRightSixteenthCalculation.swift new file mode 100644 index 00000000..60f785b0 --- /dev/null +++ b/Rectangle/WindowCalculation/LowerMiddleCenterRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// LowerMiddleCenterRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class LowerMiddleCenterRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .lowerMiddleCenterRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .lowerMiddleCenterRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .lowerMiddleCenterRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/LowerMiddleLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/LowerMiddleLeftSixteenthCalculation.swift new file mode 100644 index 00000000..2f45dae0 --- /dev/null +++ b/Rectangle/WindowCalculation/LowerMiddleLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// LowerMiddleLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class LowerMiddleLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .lowerMiddleLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .lowerMiddleLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .lowerMiddleLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/LowerMiddleRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/LowerMiddleRightSixteenthCalculation.swift new file mode 100644 index 00000000..097fd1aa --- /dev/null +++ b/Rectangle/WindowCalculation/LowerMiddleRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// LowerMiddleRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class LowerMiddleRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .lowerMiddleRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .lowerMiddleRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .lowerMiddleRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift new file mode 100644 index 00000000..37ceb18f --- /dev/null +++ b/Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// MiddleCenterLeftTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class MiddleCenterLeftTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .middleCenterLeftTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .middleCenterLeftTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .middleCenterLeftTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift new file mode 100644 index 00000000..cd69216a --- /dev/null +++ b/Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// MiddleCenterRightTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class MiddleCenterRightTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .middleCenterRightTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 4.0) * 2.0 + return RectResult(rect, subAction: .middleCenterRightTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + return RectResult(rect, subAction: .middleCenterRightTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift new file mode 100644 index 00000000..e8a897f1 --- /dev/null +++ b/Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// MiddleLeftTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class MiddleLeftTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .middleLeftTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .middleLeftTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .middleLeftTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift new file mode 100644 index 00000000..8f6bb275 --- /dev/null +++ b/Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// MiddleRightTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class MiddleRightTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .middleRightTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.minY + rect.height + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 4.0) * 3.0 + return RectResult(rect, subAction: .middleRightTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + return RectResult(rect, subAction: .middleRightTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/SixteenthsRepeated.swift b/Rectangle/WindowCalculation/SixteenthsRepeated.swift new file mode 100644 index 00000000..feb99e27 --- /dev/null +++ b/Rectangle/WindowCalculation/SixteenthsRepeated.swift @@ -0,0 +1,95 @@ +// +// SixteenthsRepeated.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +protocol SixteenthsRepeated { + func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? +} + +extension SixteenthsRepeated { + func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? { + + if direction == .left { + switch subAction { + case .topLeftSixteenth: + return WindowCalculationFactory.bottomRightSixteenthCalculation.orientationBasedRect + case .topCenterLeftSixteenth: + return WindowCalculationFactory.topLeftSixteenthCalculation.orientationBasedRect + case .topCenterRightSixteenth: + return WindowCalculationFactory.topCenterLeftSixteenthCalculation.orientationBasedRect + case .topRightSixteenth: + return WindowCalculationFactory.topCenterRightSixteenthCalculation.orientationBasedRect + case .upperMiddleLeftSixteenth: + return WindowCalculationFactory.topRightSixteenthCalculation.orientationBasedRect + case .upperMiddleCenterLeftSixteenth: + return WindowCalculationFactory.upperMiddleLeftSixteenthCalculation.orientationBasedRect + case .upperMiddleCenterRightSixteenth: + return WindowCalculationFactory.upperMiddleCenterLeftSixteenthCalculation.orientationBasedRect + case .upperMiddleRightSixteenth: + return WindowCalculationFactory.upperMiddleCenterRightSixteenthCalculation.orientationBasedRect + case .lowerMiddleLeftSixteenth: + return WindowCalculationFactory.upperMiddleRightSixteenthCalculation.orientationBasedRect + case .lowerMiddleCenterLeftSixteenth: + return WindowCalculationFactory.lowerMiddleLeftSixteenthCalculation.orientationBasedRect + case .lowerMiddleCenterRightSixteenth: + return WindowCalculationFactory.lowerMiddleCenterLeftSixteenthCalculation.orientationBasedRect + case .lowerMiddleRightSixteenth: + return WindowCalculationFactory.lowerMiddleCenterRightSixteenthCalculation.orientationBasedRect + case .bottomLeftSixteenth: + return WindowCalculationFactory.lowerMiddleRightSixteenthCalculation.orientationBasedRect + case .bottomCenterLeftSixteenth: + return WindowCalculationFactory.bottomLeftSixteenthCalculation.orientationBasedRect + case .bottomCenterRightSixteenth: + return WindowCalculationFactory.bottomCenterLeftSixteenthCalculation.orientationBasedRect + case .bottomRightSixteenth: + return WindowCalculationFactory.bottomCenterRightSixteenthCalculation.orientationBasedRect + default: break + } + } + + else if direction == .right { + switch subAction { + case .topLeftSixteenth: + return WindowCalculationFactory.topCenterLeftSixteenthCalculation.orientationBasedRect + case .topCenterLeftSixteenth: + return WindowCalculationFactory.topCenterRightSixteenthCalculation.orientationBasedRect + case .topCenterRightSixteenth: + return WindowCalculationFactory.topRightSixteenthCalculation.orientationBasedRect + case .topRightSixteenth: + return WindowCalculationFactory.upperMiddleLeftSixteenthCalculation.orientationBasedRect + case .upperMiddleLeftSixteenth: + return WindowCalculationFactory.upperMiddleCenterLeftSixteenthCalculation.orientationBasedRect + case .upperMiddleCenterLeftSixteenth: + return WindowCalculationFactory.upperMiddleCenterRightSixteenthCalculation.orientationBasedRect + case .upperMiddleCenterRightSixteenth: + return WindowCalculationFactory.upperMiddleRightSixteenthCalculation.orientationBasedRect + case .upperMiddleRightSixteenth: + return WindowCalculationFactory.lowerMiddleLeftSixteenthCalculation.orientationBasedRect + case .lowerMiddleLeftSixteenth: + return WindowCalculationFactory.lowerMiddleCenterLeftSixteenthCalculation.orientationBasedRect + case .lowerMiddleCenterLeftSixteenth: + return WindowCalculationFactory.lowerMiddleCenterRightSixteenthCalculation.orientationBasedRect + case .lowerMiddleCenterRightSixteenth: + return WindowCalculationFactory.lowerMiddleRightSixteenthCalculation.orientationBasedRect + case .lowerMiddleRightSixteenth: + return WindowCalculationFactory.bottomLeftSixteenthCalculation.orientationBasedRect + case .bottomLeftSixteenth: + return WindowCalculationFactory.bottomCenterLeftSixteenthCalculation.orientationBasedRect + case .bottomCenterLeftSixteenth: + return WindowCalculationFactory.bottomCenterRightSixteenthCalculation.orientationBasedRect + case .bottomCenterRightSixteenth: + return WindowCalculationFactory.bottomRightSixteenthCalculation.orientationBasedRect + case .bottomRightSixteenth: + return WindowCalculationFactory.topLeftSixteenthCalculation.orientationBasedRect + default: break + } + } + + return nil + } +} diff --git a/Rectangle/WindowCalculation/TopCenterLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/TopCenterLeftSixteenthCalculation.swift new file mode 100644 index 00000000..bfd13dfc --- /dev/null +++ b/Rectangle/WindowCalculation/TopCenterLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopCenterLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopCenterLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topCenterLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .topCenterLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .topCenterLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/TopCenterLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/TopCenterLeftTwelfthCalculation.swift new file mode 100644 index 00000000..fcfc46ff --- /dev/null +++ b/Rectangle/WindowCalculation/TopCenterLeftTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopCenterLeftTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopCenterLeftTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topCenterLeftTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .topCenterLeftTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .topCenterLeftTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/TopCenterRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/TopCenterRightSixteenthCalculation.swift new file mode 100644 index 00000000..95b0f725 --- /dev/null +++ b/Rectangle/WindowCalculation/TopCenterRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopCenterRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopCenterRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topCenterRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .topCenterRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .topCenterRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/TopCenterRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/TopCenterRightTwelfthCalculation.swift new file mode 100644 index 00000000..4d2c7442 --- /dev/null +++ b/Rectangle/WindowCalculation/TopCenterRightTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopCenterRightTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopCenterRightTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topCenterRightTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 4.0) * 2.0 + return RectResult(rect, subAction: .topCenterRightTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + return RectResult(rect, subAction: .topCenterRightTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/TopLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/TopLeftSixteenthCalculation.swift new file mode 100644 index 00000000..2727465a --- /dev/null +++ b/Rectangle/WindowCalculation/TopLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .topLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .topLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/TopLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/TopLeftTwelfthCalculation.swift new file mode 100644 index 00000000..ed124918 --- /dev/null +++ b/Rectangle/WindowCalculation/TopLeftTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopLeftTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopLeftTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topLeftTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .topLeftTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .topLeftTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/TopRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/TopRightSixteenthCalculation.swift new file mode 100644 index 00000000..0e22357a --- /dev/null +++ b/Rectangle/WindowCalculation/TopRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .topRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .topRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift new file mode 100644 index 00000000..e6792ebf --- /dev/null +++ b/Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift @@ -0,0 +1,50 @@ +// +// TopRightTwelfthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class TopRightTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .topRightTwelfth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 3.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 4.0) * 3.0 + return RectResult(rect, subAction: .topRightTwelfth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 3.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + return RectResult(rect, subAction: .topRightTwelfth) + } +} diff --git a/Rectangle/WindowCalculation/TwelfthsRepeated.swift b/Rectangle/WindowCalculation/TwelfthsRepeated.swift new file mode 100644 index 00000000..abc18ab3 --- /dev/null +++ b/Rectangle/WindowCalculation/TwelfthsRepeated.swift @@ -0,0 +1,79 @@ +// +// TwelfthsRepeated.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +protocol TwelfthsRepeated { + func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? +} + +extension TwelfthsRepeated { + func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? { + + if direction == .left { + switch subAction { + case .topLeftTwelfth: + return WindowCalculationFactory.bottomRightTwelfthCalculation.orientationBasedRect + case .topCenterLeftTwelfth: + return WindowCalculationFactory.topLeftTwelfthCalculation.orientationBasedRect + case .topCenterRightTwelfth: + return WindowCalculationFactory.topCenterLeftTwelfthCalculation.orientationBasedRect + case .topRightTwelfth: + return WindowCalculationFactory.topCenterRightTwelfthCalculation.orientationBasedRect + case .middleLeftTwelfth: + return WindowCalculationFactory.topRightTwelfthCalculation.orientationBasedRect + case .middleCenterLeftTwelfth: + return WindowCalculationFactory.middleLeftTwelfthCalculation.orientationBasedRect + case .middleCenterRightTwelfth: + return WindowCalculationFactory.middleCenterLeftTwelfthCalculation.orientationBasedRect + case .middleRightTwelfth: + return WindowCalculationFactory.middleCenterRightTwelfthCalculation.orientationBasedRect + case .bottomLeftTwelfth: + return WindowCalculationFactory.middleRightTwelfthCalculation.orientationBasedRect + case .bottomCenterLeftTwelfth: + return WindowCalculationFactory.bottomLeftTwelfthCalculation.orientationBasedRect + case .bottomCenterRightTwelfth: + return WindowCalculationFactory.bottomCenterLeftTwelfthCalculation.orientationBasedRect + case .bottomRightTwelfth: + return WindowCalculationFactory.bottomCenterRightTwelfthCalculation.orientationBasedRect + default: break + } + } + + else if direction == .right { + switch subAction { + case .topLeftTwelfth: + return WindowCalculationFactory.topCenterLeftTwelfthCalculation.orientationBasedRect + case .topCenterLeftTwelfth: + return WindowCalculationFactory.topCenterRightTwelfthCalculation.orientationBasedRect + case .topCenterRightTwelfth: + return WindowCalculationFactory.topRightTwelfthCalculation.orientationBasedRect + case .topRightTwelfth: + return WindowCalculationFactory.middleLeftTwelfthCalculation.orientationBasedRect + case .middleLeftTwelfth: + return WindowCalculationFactory.middleCenterLeftTwelfthCalculation.orientationBasedRect + case .middleCenterLeftTwelfth: + return WindowCalculationFactory.middleCenterRightTwelfthCalculation.orientationBasedRect + case .middleCenterRightTwelfth: + return WindowCalculationFactory.middleRightTwelfthCalculation.orientationBasedRect + case .middleRightTwelfth: + return WindowCalculationFactory.bottomLeftTwelfthCalculation.orientationBasedRect + case .bottomLeftTwelfth: + return WindowCalculationFactory.bottomCenterLeftTwelfthCalculation.orientationBasedRect + case .bottomCenterLeftTwelfth: + return WindowCalculationFactory.bottomCenterRightTwelfthCalculation.orientationBasedRect + case .bottomCenterRightTwelfth: + return WindowCalculationFactory.bottomRightTwelfthCalculation.orientationBasedRect + case .bottomRightTwelfth: + return WindowCalculationFactory.topLeftTwelfthCalculation.orientationBasedRect + default: break + } + } + + return nil + } +} diff --git a/Rectangle/WindowCalculation/UpperMiddleCenterLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/UpperMiddleCenterLeftSixteenthCalculation.swift new file mode 100644 index 00000000..546bd9d3 --- /dev/null +++ b/Rectangle/WindowCalculation/UpperMiddleCenterLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// UpperMiddleCenterLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class UpperMiddleCenterLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .upperMiddleCenterLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .upperMiddleCenterLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + rect.width + return RectResult(rect, subAction: .upperMiddleCenterLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/UpperMiddleCenterRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/UpperMiddleCenterRightSixteenthCalculation.swift new file mode 100644 index 00000000..a29495ca --- /dev/null +++ b/Rectangle/WindowCalculation/UpperMiddleCenterRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// UpperMiddleCenterRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class UpperMiddleCenterRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .upperMiddleCenterRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .upperMiddleCenterRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 2 + return RectResult(rect, subAction: .upperMiddleCenterRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/UpperMiddleLeftSixteenthCalculation.swift b/Rectangle/WindowCalculation/UpperMiddleLeftSixteenthCalculation.swift new file mode 100644 index 00000000..eac6b600 --- /dev/null +++ b/Rectangle/WindowCalculation/UpperMiddleLeftSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// UpperMiddleLeftSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class UpperMiddleLeftSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .upperMiddleLeftSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .upperMiddleLeftSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + return RectResult(rect, subAction: .upperMiddleLeftSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/UpperMiddleRightSixteenthCalculation.swift b/Rectangle/WindowCalculation/UpperMiddleRightSixteenthCalculation.swift new file mode 100644 index 00000000..99c447fb --- /dev/null +++ b/Rectangle/WindowCalculation/UpperMiddleRightSixteenthCalculation.swift @@ -0,0 +1,50 @@ +// +// UpperMiddleRightSixteenthCalculation.swift +// Rectangle +// +// Copyright © 2024 Ryan Hanson. All rights reserved. +// + +import Foundation + +class UpperMiddleRightSixteenthCalculation: WindowCalculation, OrientationAware, SixteenthsRepeated { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return orientationBasedRect(visibleFrameOfScreen) + } + + if last.action != .upperMiddleRightSixteenth { + return orientationBasedRect(visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(visibleFrameOfScreen) + } + + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .upperMiddleRightSixteenth) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 4.0) + rect.size.height = floor(visibleFrameOfScreen.height / 4.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height * 2 + rect.origin.x = visibleFrameOfScreen.minX + rect.width * 3 + return RectResult(rect, subAction: .upperMiddleRightSixteenth) + } +} diff --git a/Rectangle/WindowCalculation/WindowCalculation.swift b/Rectangle/WindowCalculation/WindowCalculation.swift index 99d0633a..9de42bd1 100644 --- a/Rectangle/WindowCalculation/WindowCalculation.swift +++ b/Rectangle/WindowCalculation/WindowCalculation.swift @@ -190,6 +190,34 @@ class WindowCalculationFactory { static let bottomVerticalThirdCalculation = BottomVerticalThirdCalculation() static let topVerticalThirdCalculation = TopVerticalThirdCalculation() static let middleVerticalThirdCalculation = MiddleVerticalThirdCalculation() + static let topLeftTwelfthCalculation = TopLeftTwelfthCalculation() + static let topCenterLeftTwelfthCalculation = TopCenterLeftTwelfthCalculation() + static let topCenterRightTwelfthCalculation = TopCenterRightTwelfthCalculation() + static let topRightTwelfthCalculation = TopRightTwelfthCalculation() + static let middleLeftTwelfthCalculation = MiddleLeftTwelfthCalculation() + static let middleCenterLeftTwelfthCalculation = MiddleCenterLeftTwelfthCalculation() + static let middleCenterRightTwelfthCalculation = MiddleCenterRightTwelfthCalculation() + static let middleRightTwelfthCalculation = MiddleRightTwelfthCalculation() + static let bottomLeftTwelfthCalculation = BottomLeftTwelfthCalculation() + static let bottomCenterLeftTwelfthCalculation = BottomCenterLeftTwelfthCalculation() + static let bottomCenterRightTwelfthCalculation = BottomCenterRightTwelfthCalculation() + static let bottomRightTwelfthCalculation = BottomRightTwelfthCalculation() + static let topLeftSixteenthCalculation = TopLeftSixteenthCalculation() + static let topCenterLeftSixteenthCalculation = TopCenterLeftSixteenthCalculation() + static let topCenterRightSixteenthCalculation = TopCenterRightSixteenthCalculation() + static let topRightSixteenthCalculation = TopRightSixteenthCalculation() + static let upperMiddleLeftSixteenthCalculation = UpperMiddleLeftSixteenthCalculation() + static let upperMiddleCenterLeftSixteenthCalculation = UpperMiddleCenterLeftSixteenthCalculation() + static let upperMiddleCenterRightSixteenthCalculation = UpperMiddleCenterRightSixteenthCalculation() + static let upperMiddleRightSixteenthCalculation = UpperMiddleRightSixteenthCalculation() + static let lowerMiddleLeftSixteenthCalculation = LowerMiddleLeftSixteenthCalculation() + static let lowerMiddleCenterLeftSixteenthCalculation = LowerMiddleCenterLeftSixteenthCalculation() + static let lowerMiddleCenterRightSixteenthCalculation = LowerMiddleCenterRightSixteenthCalculation() + static let lowerMiddleRightSixteenthCalculation = LowerMiddleRightSixteenthCalculation() + static let bottomLeftSixteenthCalculation = BottomLeftSixteenthCalculation() + static let bottomCenterLeftSixteenthCalculation = BottomCenterLeftSixteenthCalculation() + static let bottomCenterRightSixteenthCalculation = BottomCenterRightSixteenthCalculation() + static let bottomRightSixteenthCalculation = BottomRightSixteenthCalculation() static let calculationsByAction: [WindowAction: WindowCalculation] = [ .leftHalf: leftHalfCalculation, @@ -273,7 +301,35 @@ class WindowCalculationFactory { .middleVerticalThird: middleVerticalThirdCalculation, .bottomVerticalThird: bottomVerticalThirdCalculation, .topVerticalTwoThirds: topVerticalTwoThirdsCalculation, - .bottomVerticalTwoThirds: bottomVerticalTwoThirdsCalculation + .bottomVerticalTwoThirds: bottomVerticalTwoThirdsCalculation, + .topLeftTwelfth: topLeftTwelfthCalculation, + .topCenterLeftTwelfth: topCenterLeftTwelfthCalculation, + .topCenterRightTwelfth: topCenterRightTwelfthCalculation, + .topRightTwelfth: topRightTwelfthCalculation, + .middleLeftTwelfth: middleLeftTwelfthCalculation, + .middleCenterLeftTwelfth: middleCenterLeftTwelfthCalculation, + .middleCenterRightTwelfth: middleCenterRightTwelfthCalculation, + .middleRightTwelfth: middleRightTwelfthCalculation, + .bottomLeftTwelfth: bottomLeftTwelfthCalculation, + .bottomCenterLeftTwelfth: bottomCenterLeftTwelfthCalculation, + .bottomCenterRightTwelfth: bottomCenterRightTwelfthCalculation, + .bottomRightTwelfth: bottomRightTwelfthCalculation, + .topLeftSixteenth: topLeftSixteenthCalculation, + .topCenterLeftSixteenth: topCenterLeftSixteenthCalculation, + .topCenterRightSixteenth: topCenterRightSixteenthCalculation, + .topRightSixteenth: topRightSixteenthCalculation, + .upperMiddleLeftSixteenth: upperMiddleLeftSixteenthCalculation, + .upperMiddleCenterLeftSixteenth: upperMiddleCenterLeftSixteenthCalculation, + .upperMiddleCenterRightSixteenth: upperMiddleCenterRightSixteenthCalculation, + .upperMiddleRightSixteenth: upperMiddleRightSixteenthCalculation, + .lowerMiddleLeftSixteenth: lowerMiddleLeftSixteenthCalculation, + .lowerMiddleCenterLeftSixteenth: lowerMiddleCenterLeftSixteenthCalculation, + .lowerMiddleCenterRightSixteenth: lowerMiddleCenterRightSixteenthCalculation, + .lowerMiddleRightSixteenth: lowerMiddleRightSixteenthCalculation, + .bottomLeftSixteenth: bottomLeftSixteenthCalculation, + .bottomCenterLeftSixteenth: bottomCenterLeftSixteenthCalculation, + .bottomCenterRightSixteenth: bottomCenterRightSixteenthCalculation, + .bottomRightSixteenth: bottomRightSixteenthCalculation // .restore: nil ] } From 7ed15206c663ec85a7a224f3f2c0a4281bf1ba15 Mon Sep 17 00:00:00 2001 From: Myron Koch Date: Fri, 13 Mar 2026 14:45:57 -0400 Subject: [PATCH 03/10] Include width/height size variants in submenu and clean up eighths code - Add largerWidth, smallerWidth, largerHeight, smallerHeight to the .size category so all size-related actions appear in the Size submenu - Remove dead eighthsMenuItem property from AppDelegate (no longer assigned after removing the conditional hiding logic) - Remove dead reference in SettingsViewController toggle handler --- Rectangle/AppDelegate.swift | 2 -- Rectangle/PrefsWindow/SettingsViewController.swift | 1 - Rectangle/WindowAction.swift | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Rectangle/AppDelegate.swift b/Rectangle/AppDelegate.swift index 68f5d310..9d2101bf 100644 --- a/Rectangle/AppDelegate.swift +++ b/Rectangle/AppDelegate.swift @@ -45,8 +45,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var updatesMenuItem: NSMenuItem! @IBOutlet weak var quitMenuItem: NSMenuItem! - var eighthsMenuItem: NSMenuItem? - static var instance: AppDelegate { NSApp.delegate as! AppDelegate } diff --git a/Rectangle/PrefsWindow/SettingsViewController.swift b/Rectangle/PrefsWindow/SettingsViewController.swift index 57585648..be419e30 100644 --- a/Rectangle/PrefsWindow/SettingsViewController.swift +++ b/Rectangle/PrefsWindow/SettingsViewController.swift @@ -110,7 +110,6 @@ class SettingsViewController: NSViewController { @objc func toggleShowEighthsInMenu(_ sender: NSButton) { let enabled: Bool = sender.state == .on Defaults.showEighthsInMenu.enabled = enabled - AppDelegate.instance.eighthsMenuItem?.isHidden = !enabled } @IBAction func checkForUpdates(_ sender: Any) { diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index 8f750a5c..2b24a1d0 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -657,7 +657,7 @@ enum WindowAction: Int, Codable { case .topLeftSixth, .topCenterSixth, .topRightSixth, .bottomLeftSixth, .bottomCenterSixth, .bottomRightSixth: return .sixths case .topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth, .bottomLeftEighth, .bottomCenterLeftEighth, .bottomCenterRightEighth, .bottomRightEighth: return .eighths case .moveUp, .moveDown, .moveLeft, .moveRight: return .move - case .almostMaximize, .maximizeHeight, .larger, .smaller: return .size + case .almostMaximize, .maximizeHeight, .larger, .smaller, .largerWidth, .smallerWidth, .largerHeight, .smallerHeight: return .size default: return nil } } From 5429dc44c654ec83db107202c24337453a2fa17f Mon Sep 17 00:00:00 2001 From: Myron Koch Date: Fri, 13 Mar 2026 14:48:56 -0400 Subject: [PATCH 04/10] Fix twelfths portrait mode coordinates and standardize display names Portrait mode for twelfths transposes the 4x3 landscape grid to a 3x4 portrait grid using row-major index remapping. The previous code naively mapped landscape column positions to portrait columns, causing 3 pairs of overlapping positions and one empty row. Fixed 8 of 12 portraitRect methods with correct row/column assignments. Also standardize 4 abbreviated sixteenth display names to use the full form consistent with all other positions. --- Rectangle/WindowAction.swift | 8 ++++---- .../BottomCenterLeftTwelfthCalculation.swift | 2 +- .../BottomCenterRightTwelfthCalculation.swift | 2 +- .../WindowCalculation/BottomLeftTwelfthCalculation.swift | 4 ++-- .../MiddleCenterLeftTwelfthCalculation.swift | 4 ++-- .../MiddleCenterRightTwelfthCalculation.swift | 4 ++-- .../WindowCalculation/MiddleLeftTwelfthCalculation.swift | 4 ++-- .../WindowCalculation/MiddleRightTwelfthCalculation.swift | 4 ++-- .../WindowCalculation/TopRightTwelfthCalculation.swift | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index 6fc6f816..6ff8184b 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -534,10 +534,10 @@ enum WindowAction: Int, Codable { value = "Upper Middle Left Sixteenth" case .upperMiddleCenterLeftSixteenth: key = "upperMiddleCenterLeftSixteenth.title" - value = "Upper Mid Center Left 16th" + value = "Upper Middle Center Left Sixteenth" case .upperMiddleCenterRightSixteenth: key = "upperMiddleCenterRightSixteenth.title" - value = "Upper Mid Center Right 16th" + value = "Upper Middle Center Right Sixteenth" case .upperMiddleRightSixteenth: key = "upperMiddleRightSixteenth.title" value = "Upper Middle Right Sixteenth" @@ -546,10 +546,10 @@ enum WindowAction: Int, Codable { value = "Lower Middle Left Sixteenth" case .lowerMiddleCenterLeftSixteenth: key = "lowerMiddleCenterLeftSixteenth.title" - value = "Lower Mid Center Left 16th" + value = "Lower Middle Center Left Sixteenth" case .lowerMiddleCenterRightSixteenth: key = "lowerMiddleCenterRightSixteenth.title" - value = "Lower Mid Center Right 16th" + value = "Lower Middle Center Right Sixteenth" case .lowerMiddleRightSixteenth: key = "lowerMiddleRightSixteenth.title" value = "Lower Middle Right Sixteenth" diff --git a/Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift index 24266bff..1c3b6a6f 100644 --- a/Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/BottomCenterLeftTwelfthCalculation.swift @@ -44,7 +44,7 @@ class BottomCenterLeftTwelfthCalculation: WindowCalculation, OrientationAware, T rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) rect.origin.y = visibleFrameOfScreen.minY - rect.origin.x = visibleFrameOfScreen.minX + rect.width + rect.origin.x = visibleFrameOfScreen.minX return RectResult(rect, subAction: .bottomCenterLeftTwelfth) } } diff --git a/Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift index 7ab068f5..1da0f6f4 100644 --- a/Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/BottomCenterRightTwelfthCalculation.swift @@ -44,7 +44,7 @@ class BottomCenterRightTwelfthCalculation: WindowCalculation, OrientationAware, rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) rect.origin.y = visibleFrameOfScreen.minY - rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + rect.origin.x = visibleFrameOfScreen.minX + rect.width return RectResult(rect, subAction: .bottomCenterRightTwelfth) } } diff --git a/Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift index 5d706865..438a3842 100644 --- a/Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/BottomLeftTwelfthCalculation.swift @@ -43,8 +43,8 @@ class BottomLeftTwelfthCalculation: WindowCalculation, OrientationAware, Twelfth var rect = visibleFrameOfScreen rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) - rect.origin.y = visibleFrameOfScreen.minY - rect.origin.x = visibleFrameOfScreen.minX + rect.origin.y = visibleFrameOfScreen.maxY - (3.0 * rect.height) + rect.origin.x = visibleFrameOfScreen.minX + (2.0 * rect.width) return RectResult(rect, subAction: .bottomLeftTwelfth) } } diff --git a/Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift index 37ceb18f..b4258a66 100644 --- a/Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/MiddleCenterLeftTwelfthCalculation.swift @@ -43,8 +43,8 @@ class MiddleCenterLeftTwelfthCalculation: WindowCalculation, OrientationAware, T var rect = visibleFrameOfScreen rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) - rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) - rect.origin.x = visibleFrameOfScreen.minX + rect.width + rect.origin.y = visibleFrameOfScreen.maxY - (2.0 * rect.height) + rect.origin.x = visibleFrameOfScreen.minX + (2.0 * rect.width) return RectResult(rect, subAction: .middleCenterLeftTwelfth) } } diff --git a/Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift index cd69216a..0dda6bd4 100644 --- a/Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/MiddleCenterRightTwelfthCalculation.swift @@ -43,8 +43,8 @@ class MiddleCenterRightTwelfthCalculation: WindowCalculation, OrientationAware, var rect = visibleFrameOfScreen rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) - rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) - rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + rect.origin.y = visibleFrameOfScreen.maxY - (3.0 * rect.height) + rect.origin.x = visibleFrameOfScreen.minX return RectResult(rect, subAction: .middleCenterRightTwelfth) } } diff --git a/Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift index e8a897f1..2324bdc9 100644 --- a/Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/MiddleLeftTwelfthCalculation.swift @@ -43,8 +43,8 @@ class MiddleLeftTwelfthCalculation: WindowCalculation, OrientationAware, Twelfth var rect = visibleFrameOfScreen rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) - rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) - rect.origin.x = visibleFrameOfScreen.minX + rect.origin.y = visibleFrameOfScreen.maxY - (2.0 * rect.height) + rect.origin.x = visibleFrameOfScreen.minX + rect.width return RectResult(rect, subAction: .middleLeftTwelfth) } } diff --git a/Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift index 8f6bb275..deba74f1 100644 --- a/Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/MiddleRightTwelfthCalculation.swift @@ -43,8 +43,8 @@ class MiddleRightTwelfthCalculation: WindowCalculation, OrientationAware, Twelft var rect = visibleFrameOfScreen rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) - rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) - rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + rect.origin.y = visibleFrameOfScreen.maxY - (3.0 * rect.height) + rect.origin.x = visibleFrameOfScreen.minX + rect.width return RectResult(rect, subAction: .middleRightTwelfth) } } diff --git a/Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift b/Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift index e6792ebf..158be267 100644 --- a/Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift +++ b/Rectangle/WindowCalculation/TopRightTwelfthCalculation.swift @@ -43,8 +43,8 @@ class TopRightTwelfthCalculation: WindowCalculation, OrientationAware, TwelfthsR var rect = visibleFrameOfScreen rect.size.width = floor(visibleFrameOfScreen.width / 3.0) rect.size.height = floor(visibleFrameOfScreen.height / 4.0) - rect.origin.y = visibleFrameOfScreen.maxY - rect.height - rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) * 2.0 + rect.origin.y = visibleFrameOfScreen.maxY - (2.0 * rect.height) + rect.origin.x = visibleFrameOfScreen.minX return RectResult(rect, subAction: .topRightTwelfth) } } From c91161d826678e9e3738ecdacd2372be936a2c01 Mon Sep 17 00:00:00 2001 From: Myron Koch <126993133+MyronKoch@users.noreply.github.com> Date: Sun, 15 Mar 2026 01:17:14 -0400 Subject: [PATCH 05/10] Move grid shortcuts to General tab popover with cycling approach Per maintainer feedback, move twelfths/sixteenths shortcuts from the Shortcuts tab into the ellipsis popover on the General tab. Instead of 28 individual rows, use one cycling shortcut per grid size that cycles through all positions on repeated presses via the existing *Repeated protocols. Restore PrefsViewController to upstream state. --- .../PrefsWindow/PrefsViewController.swift | 153 +----------------- .../PrefsWindow/SettingsViewController.swift | 84 ++++++++++ 2 files changed, 89 insertions(+), 148 deletions(-) diff --git a/Rectangle/PrefsWindow/PrefsViewController.swift b/Rectangle/PrefsWindow/PrefsViewController.swift index 854c417e..04ce8d40 100644 --- a/Rectangle/PrefsWindow/PrefsViewController.swift +++ b/Rectangle/PrefsWindow/PrefsViewController.swift @@ -115,162 +115,19 @@ class PrefsViewController: NSViewController { .bottomCenterSixth: bottomCenterSixthShortcutView, .bottomRightSixth: bottomRightSixthShortcutView ] - - addGridShortcuts() - + for (action, view) in actionsToViews { view.setAssociatedUserDefaultsKey(action.name, withTransformerName: MASDictionaryTransformerName) } - + if Defaults.allowAnyShortcut.enabled { let passThroughValidator = PassthroughShortcutValidator() actionsToViews.values.forEach { $0.shortcutValidator = passThroughValidator } } - + subscribeToAllowAnyShortcutToggle() - - additionalShortcutsStackView.isHidden = false - showMoreButton.title = "▼" - } - - private var gridShortcutsAdded = false - - /// Adds Eighths, Twelfths, and Sixteenths sections to the additional shortcuts stack view. - /// Each position gets its own individual shortcut row. - private func addGridShortcuts() { - guard !gridShortcutsAdded else { return } - gridShortcutsAdded = true - - guard let leftColumn = additionalShortcutsStackView.arrangedSubviews.first as? NSStackView, - let rightColumn = additionalShortcutsStackView.arrangedSubviews.last as? NSStackView else { return } - - // Eighths section header - addSectionSeparator(leftColumn: leftColumn, rightColumn: rightColumn, title: "Eighths") - - // Eighths: 4 left, 4 right - leftColumn.addArrangedSubview(createShortcutRow(for: .topLeftEighth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topCenterLeftEighth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topCenterRightEighth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topRightEighth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomLeftEighth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomCenterLeftEighth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomCenterRightEighth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomRightEighth)) - - // Twelfths section header - addSectionSeparator(leftColumn: leftColumn, rightColumn: rightColumn, title: "Twelfths") - - // Twelfths: 6 left, 6 right - leftColumn.addArrangedSubview(createShortcutRow(for: .topLeftTwelfth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topCenterLeftTwelfth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topCenterRightTwelfth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topRightTwelfth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .middleLeftTwelfth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .middleCenterLeftTwelfth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .middleCenterRightTwelfth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .middleRightTwelfth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomLeftTwelfth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomCenterLeftTwelfth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomCenterRightTwelfth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomRightTwelfth)) - - // Sixteenths section header - addSectionSeparator(leftColumn: leftColumn, rightColumn: rightColumn, title: "Sixteenths") - - // Sixteenths: 8 left, 8 right - leftColumn.addArrangedSubview(createShortcutRow(for: .topLeftSixteenth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topCenterLeftSixteenth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topCenterRightSixteenth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .topRightSixteenth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .upperMiddleLeftSixteenth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .upperMiddleCenterLeftSixteenth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .upperMiddleCenterRightSixteenth)) - leftColumn.addArrangedSubview(createShortcutRow(for: .upperMiddleRightSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .lowerMiddleLeftSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .lowerMiddleCenterLeftSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .lowerMiddleCenterRightSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .lowerMiddleRightSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomLeftSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomCenterLeftSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomCenterRightSixteenth)) - rightColumn.addArrangedSubview(createShortcutRow(for: .bottomRightSixteenth)) - } - - private func addSectionSeparator(leftColumn: NSStackView, rightColumn: NSStackView, title: String) { - let spacerL = NSView() - spacerL.translatesAutoresizingMaskIntoConstraints = false - spacerL.heightAnchor.constraint(equalToConstant: 5).isActive = true - leftColumn.addArrangedSubview(spacerL) - - let spacerR = NSView() - spacerR.translatesAutoresizingMaskIntoConstraints = false - spacerR.heightAnchor.constraint(equalToConstant: 5).isActive = true - rightColumn.addArrangedSubview(spacerR) - - leftColumn.addArrangedSubview(createSectionHeader(title: title)) - rightColumn.addArrangedSubview(createSectionSeparator()) - } - - private func createSectionHeader(title: String) -> NSView { - let separator = createSectionSeparator() - - let label = NSTextField(labelWithString: title) - label.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize, weight: .semibold) - label.textColor = .secondaryLabelColor - label.alignment = .left - label.translatesAutoresizingMaskIntoConstraints = false - - let container = NSStackView(views: [separator, label]) - container.orientation = .vertical - container.alignment = .leading - container.spacing = 4 - container.translatesAutoresizingMaskIntoConstraints = false - return container - } - - private func createSectionSeparator() -> NSBox { - let box = NSBox() - box.boxType = .separator - box.translatesAutoresizingMaskIntoConstraints = false - return box - } - - private func createShortcutRow(for action: WindowAction) -> NSStackView { - let shortcutView = MASShortcutView() - shortcutView.translatesAutoresizingMaskIntoConstraints = false - shortcutView.widthAnchor.constraint(equalToConstant: 160).isActive = true - shortcutView.heightAnchor.constraint(equalToConstant: 19).isActive = true - - let label = action.displayName ?? action.name - let textField = NSTextField(labelWithString: label) - textField.alignment = .right - textField.lineBreakMode = .byClipping - textField.translatesAutoresizingMaskIntoConstraints = false - textField.setContentHuggingPriority(.init(251), for: .horizontal) - textField.setContentHuggingPriority(.init(750), for: .vertical) - - let imageView = NSImageView() - imageView.translatesAutoresizingMaskIntoConstraints = false - imageView.widthAnchor.constraint(equalToConstant: 21).isActive = true - imageView.heightAnchor.constraint(equalToConstant: 14).isActive = true - imageView.image = action.image - imageView.imageScaling = .scaleProportionallyDown - imageView.setContentHuggingPriority(.init(251), for: .horizontal) - imageView.setContentHuggingPriority(.init(251), for: .vertical) - - let labelStack = NSStackView(views: [textField, imageView]) - labelStack.orientation = .horizontal - labelStack.alignment = .centerY - labelStack.distribution = .fill - - let row = NSStackView(views: [labelStack, shortcutView]) - row.orientation = .horizontal - row.alignment = .centerY - row.distribution = .fill - row.spacing = 18 - - actionsToViews[action] = shortcutView - return row + + additionalShortcutsStackView.isHidden = true } @IBAction func toggleShowMore(_ sender: NSButton) { diff --git a/Rectangle/PrefsWindow/SettingsViewController.swift b/Rectangle/PrefsWindow/SettingsViewController.swift index 57585648..0192b968 100644 --- a/Rectangle/PrefsWindow/SettingsViewController.swift +++ b/Rectangle/PrefsWindow/SettingsViewController.swift @@ -748,6 +748,82 @@ class SettingsViewController: NSViewController { showEighthsCheckbox.imageHugsTitle = true mainStackView.addArrangedSubview(showEighthsCheckbox) + + // Grid Positions (cycling shortcuts) + let gridHeaderLabel = NSTextField(labelWithString: NSLocalizedString("Grid Positions", tableName: "Main", value: "", comment: "")) + gridHeaderLabel.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) + gridHeaderLabel.alignment = .center + gridHeaderLabel.translatesAutoresizingMaskIntoConstraints = false + + let cyclingHintLabel = NSTextField(wrappingLabelWithString: NSLocalizedString("Press the shortcut repeatedly to cycle through all positions in the grid.", tableName: "Main", value: "", comment: "")) + cyclingHintLabel.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) + cyclingHintLabel.textColor = .secondaryLabelColor + cyclingHintLabel.alignment = .center + cyclingHintLabel.translatesAutoresizingMaskIntoConstraints = false + + let twelfthsLabel = NSTextField(labelWithString: NSLocalizedString("Twelfths (4\u{00d7}3)", tableName: "Main", value: "", comment: "")) + twelfthsLabel.alignment = .right + twelfthsLabel.translatesAutoresizingMaskIntoConstraints = false + + let sixteenthsLabel = NSTextField(labelWithString: NSLocalizedString("Sixteenths (4\u{00d7}4)", tableName: "Main", value: "", comment: "")) + sixteenthsLabel.alignment = .right + sixteenthsLabel.translatesAutoresizingMaskIntoConstraints = false + + let twelfthsShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let sixteenthsShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + + twelfthsShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + sixteenthsShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + + if Defaults.allowAnyShortcut.enabled { + let passThroughValidator = PassthroughShortcutValidator() + twelfthsShortcutView.shortcutValidator = passThroughValidator + sixteenthsShortcutView.shortcutValidator = passThroughValidator + } + + let twelfthsIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + twelfthsIcon.image = WindowAction.topLeftTwelfth.image + twelfthsIcon.image?.size = NSSize(width: 21, height: 14) + + let sixteenthsIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + sixteenthsIcon.image = WindowAction.topLeftSixteenth.image + sixteenthsIcon.image?.size = NSSize(width: 21, height: 14) + + let twelfthsLabelStack = NSStackView() + twelfthsLabelStack.orientation = .horizontal + twelfthsLabelStack.alignment = .centerY + twelfthsLabelStack.spacing = 8 + twelfthsLabelStack.addArrangedSubview(twelfthsLabel) + twelfthsLabelStack.addArrangedSubview(twelfthsIcon) + + let sixteenthsLabelStack = NSStackView() + sixteenthsLabelStack.orientation = .horizontal + sixteenthsLabelStack.alignment = .centerY + sixteenthsLabelStack.spacing = 8 + sixteenthsLabelStack.addArrangedSubview(sixteenthsLabel) + sixteenthsLabelStack.addArrangedSubview(sixteenthsIcon) + + let twelfthsRow = NSStackView() + twelfthsRow.orientation = .horizontal + twelfthsRow.alignment = .centerY + twelfthsRow.spacing = 18 + twelfthsRow.addArrangedSubview(twelfthsLabelStack) + twelfthsRow.addArrangedSubview(twelfthsShortcutView) + + let sixteenthsRow = NSStackView() + sixteenthsRow.orientation = .horizontal + sixteenthsRow.alignment = .centerY + sixteenthsRow.spacing = 18 + sixteenthsRow.addArrangedSubview(sixteenthsLabelStack) + sixteenthsRow.addArrangedSubview(sixteenthsShortcutView) + + mainStackView.addArrangedSubview(gridHeaderLabel) + mainStackView.setCustomSpacing(4, after: gridHeaderLabel) + mainStackView.addArrangedSubview(cyclingHintLabel) + mainStackView.setCustomSpacing(8, after: cyclingHintLabel) + mainStackView.addArrangedSubview(twelfthsRow) + mainStackView.addArrangedSubview(sixteenthsRow) + mainStackView.addArrangedSubview(splitRatioHeaderLabel) mainStackView.setCustomSpacing(10, after: splitRatioHeaderLabel) mainStackView.addArrangedSubview(hSplitRow) @@ -809,6 +885,14 @@ class SettingsViewController: NSViewController { bottomCenterLeftEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), bottomCenterRightEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), bottomRightEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + gridHeaderLabel.widthAnchor.constraint(equalTo: mainStackView.widthAnchor), + cyclingHintLabel.widthAnchor.constraint(equalTo: mainStackView.widthAnchor, constant: -20), + twelfthsLabel.widthAnchor.constraint(equalTo: bottomRightEighthLabel.widthAnchor), + sixteenthsLabel.widthAnchor.constraint(equalTo: bottomRightEighthLabel.widthAnchor), + twelfthsShortcutView.widthAnchor.constraint(equalToConstant: 160), + sixteenthsShortcutView.widthAnchor.constraint(equalToConstant: 160), + twelfthsShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + sixteenthsShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), hSplitField.trailingAnchor.constraint(equalTo: largerWidthShortcutView.trailingAnchor), vSplitField.trailingAnchor.constraint(equalTo: largerWidthShortcutView.trailingAnchor) ]) From a8ba0293034f85424547a316903fa1af58e77762 Mon Sep 17 00:00:00 2001 From: Myron Koch <126993133+MyronKoch@users.noreply.github.com> Date: Sun, 15 Mar 2026 02:17:26 -0400 Subject: [PATCH 06/10] Add individual twelfths/sixteenths rows and cycling toggle checkbox - Add useCyclingShortcuts BoolDefault (default false) to Defaults.swift - Add 12 individual twelfths rows and 16 individual sixteenths rows to the General tab extra settings popover, following the exact eighths pattern - Add "Use cycling shortcuts" checkbox at top of Grid Positions section - When cycling mode is on, hide individual rows and show one cycling shortcut per category (Eighths, Twelfths, Sixteenths); cycling hint text visible only in cycling mode - When cycling mode is off (default), show all individual position rows and hide cycling rows - Use objc associated objects to wire checkbox to row visibility toggle --- Rectangle/Defaults.swift | 6 +- .../PrefsWindow/SettingsViewController.swift | 592 ++++++++++++++++-- 2 files changed, 534 insertions(+), 64 deletions(-) diff --git a/Rectangle/Defaults.swift b/Rectangle/Defaults.swift index a6fa1ca9..4fec2a92 100644 --- a/Rectangle/Defaults.swift +++ b/Rectangle/Defaults.swift @@ -99,7 +99,8 @@ class Defaults { static let internalTilingNotified = BoolDefault(key: "internalTilingNotified") static let screensOrderedByX = OptionalBoolDefault(key: "screensOrderedByX") static let showEighthsInMenu = OptionalBoolDefault(key: "showEighthsInMenu") - + static let useCyclingShortcuts = BoolDefault(key: "useCyclingShortcuts") + static var array: [Default] = [ launchOnLogin, disabledApps, @@ -183,7 +184,8 @@ class Defaults { systemWideMouseDown, systemWideMouseDownApps, screensOrderedByX, - showEighthsInMenu + showEighthsInMenu, + useCyclingShortcuts ] } diff --git a/Rectangle/PrefsWindow/SettingsViewController.swift b/Rectangle/PrefsWindow/SettingsViewController.swift index 0192b968..780b6ed1 100644 --- a/Rectangle/PrefsWindow/SettingsViewController.swift +++ b/Rectangle/PrefsWindow/SettingsViewController.swift @@ -112,6 +112,24 @@ class SettingsViewController: NSViewController { Defaults.showEighthsInMenu.enabled = enabled AppDelegate.instance.eighthsMenuItem?.isHidden = !enabled } + + private static var individualRowsKey = "individualRowsKey" + private static var cyclingRowsKey = "cyclingRowsKey" + private static var cyclingHintKey = "cyclingHintKey" + + @objc func toggleCyclingShortcuts(_ sender: NSButton) { + let enabled = sender.state == .on + Defaults.useCyclingShortcuts.enabled = enabled + if let individualRows = objc_getAssociatedObject(sender, &SettingsViewController.individualRowsKey) as? [NSView] { + individualRows.forEach { $0.isHidden = enabled } + } + if let cyclingRows = objc_getAssociatedObject(sender, &SettingsViewController.cyclingRowsKey) as? [NSView] { + cyclingRows.forEach { $0.isHidden = !enabled } + } + if let hintLabel = objc_getAssociatedObject(sender, &SettingsViewController.cyclingHintKey) as? NSView { + hintLabel.isHidden = !enabled + } + } @IBAction func checkForUpdates(_ sender: Any) { AppDelegate.instance.updaterController?.checkForUpdates(sender) @@ -746,83 +764,446 @@ class SettingsViewController: NSViewController { showEighthsCheckbox.translatesAutoresizingMaskIntoConstraints = false showEighthsCheckbox.alignment = .right showEighthsCheckbox.imageHugsTitle = true - + mainStackView.addArrangedSubview(showEighthsCheckbox) - // Grid Positions (cycling shortcuts) + // Grid Positions section let gridHeaderLabel = NSTextField(labelWithString: NSLocalizedString("Grid Positions", tableName: "Main", value: "", comment: "")) gridHeaderLabel.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) gridHeaderLabel.alignment = .center gridHeaderLabel.translatesAutoresizingMaskIntoConstraints = false + let cyclingCheckbox = NSButton(checkboxWithTitle: NSLocalizedString("Use cycling shortcuts", tableName: "Main", value: "", comment: ""), target: self, action: #selector(toggleCyclingShortcuts(_:))) + cyclingCheckbox.state = Defaults.useCyclingShortcuts.enabled ? .on : .off + cyclingCheckbox.translatesAutoresizingMaskIntoConstraints = false + cyclingCheckbox.imageHugsTitle = true + let cyclingHintLabel = NSTextField(wrappingLabelWithString: NSLocalizedString("Press the shortcut repeatedly to cycle through all positions in the grid.", tableName: "Main", value: "", comment: "")) cyclingHintLabel.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) cyclingHintLabel.textColor = .secondaryLabelColor cyclingHintLabel.alignment = .center cyclingHintLabel.translatesAutoresizingMaskIntoConstraints = false + cyclingHintLabel.isHidden = !Defaults.useCyclingShortcuts.enabled + + // Individual twelfths rows + let topLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Left Twelfth", tableName: "Main", value: "", comment: "")) + topLeftTwelfthLabel.alignment = .right + topLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let topCenterLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Left Twelfth", tableName: "Main", value: "", comment: "")) + topCenterLeftTwelfthLabel.alignment = .right + topCenterLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let topCenterRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Right Twelfth", tableName: "Main", value: "", comment: "")) + topCenterRightTwelfthLabel.alignment = .right + topCenterRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let topRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Right Twelfth", tableName: "Main", value: "", comment: "")) + topRightTwelfthLabel.alignment = .right + topRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let middleLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Left Twelfth", tableName: "Main", value: "", comment: "")) + middleLeftTwelfthLabel.alignment = .right + middleLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let middleCenterLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Center Left Twelfth", tableName: "Main", value: "", comment: "")) + middleCenterLeftTwelfthLabel.alignment = .right + middleCenterLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let middleCenterRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Center Right Twelfth", tableName: "Main", value: "", comment: "")) + middleCenterRightTwelfthLabel.alignment = .right + middleCenterRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let middleRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Right Twelfth", tableName: "Main", value: "", comment: "")) + middleRightTwelfthLabel.alignment = .right + middleRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Left Twelfth", tableName: "Main", value: "", comment: "")) + bottomLeftTwelfthLabel.alignment = .right + bottomLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomCenterLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Left Twelfth", tableName: "Main", value: "", comment: "")) + bottomCenterLeftTwelfthLabel.alignment = .right + bottomCenterLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomCenterRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Right Twelfth", tableName: "Main", value: "", comment: "")) + bottomCenterRightTwelfthLabel.alignment = .right + bottomCenterRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Right Twelfth", tableName: "Main", value: "", comment: "")) + bottomRightTwelfthLabel.alignment = .right + bottomRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false + + let topLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let topCenterLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let topCenterRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let topRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let middleLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let middleCenterLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let middleCenterRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let middleRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomCenterLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomCenterRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + + topLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + topCenterLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + topCenterRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) + topRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) + middleLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + middleCenterLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleCenterLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + middleCenterRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleCenterRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) + middleRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) + bottomLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + bottomCenterLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + bottomCenterRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) + bottomRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) + + let topLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topLeftTwelfthIcon.image = WindowAction.topLeftTwelfth.image + topLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let topCenterLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topCenterLeftTwelfthIcon.image = WindowAction.topCenterLeftTwelfth.image + topCenterLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let topCenterRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topCenterRightTwelfthIcon.image = WindowAction.topCenterRightTwelfth.image + topCenterRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let topRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topRightTwelfthIcon.image = WindowAction.topRightTwelfth.image + topRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let middleLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + middleLeftTwelfthIcon.image = WindowAction.middleLeftTwelfth.image + middleLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let middleCenterLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + middleCenterLeftTwelfthIcon.image = WindowAction.middleCenterLeftTwelfth.image + middleCenterLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let middleCenterRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + middleCenterRightTwelfthIcon.image = WindowAction.middleCenterRightTwelfth.image + middleCenterRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let middleRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + middleRightTwelfthIcon.image = WindowAction.middleRightTwelfth.image + middleRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomLeftTwelfthIcon.image = WindowAction.bottomLeftTwelfth.image + bottomLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomCenterLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomCenterLeftTwelfthIcon.image = WindowAction.bottomCenterLeftTwelfth.image + bottomCenterLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomCenterRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomCenterRightTwelfthIcon.image = WindowAction.bottomCenterRightTwelfth.image + bottomCenterRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomRightTwelfthIcon.image = WindowAction.bottomRightTwelfth.image + bottomRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) + + func makeLabelStack(_ label: NSTextField, _ icon: NSImageView) -> NSStackView { + let stack = NSStackView() + stack.orientation = .horizontal + stack.alignment = .centerY + stack.spacing = 8 + stack.addArrangedSubview(label) + stack.addArrangedSubview(icon) + return stack + } - let twelfthsLabel = NSTextField(labelWithString: NSLocalizedString("Twelfths (4\u{00d7}3)", tableName: "Main", value: "", comment: "")) - twelfthsLabel.alignment = .right - twelfthsLabel.translatesAutoresizingMaskIntoConstraints = false - - let sixteenthsLabel = NSTextField(labelWithString: NSLocalizedString("Sixteenths (4\u{00d7}4)", tableName: "Main", value: "", comment: "")) - sixteenthsLabel.alignment = .right - sixteenthsLabel.translatesAutoresizingMaskIntoConstraints = false - - let twelfthsShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let sixteenthsShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + func makeRow(_ labelStack: NSStackView, _ shortcutView: MASShortcutView) -> NSStackView { + let row = NSStackView() + row.orientation = .horizontal + row.alignment = .centerY + row.spacing = 18 + row.addArrangedSubview(labelStack) + row.addArrangedSubview(shortcutView) + return row + } - twelfthsShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) - sixteenthsShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + let topLeftTwelfthRow = makeRow(makeLabelStack(topLeftTwelfthLabel, topLeftTwelfthIcon), topLeftTwelfthShortcutView) + let topCenterLeftTwelfthRow = makeRow(makeLabelStack(topCenterLeftTwelfthLabel, topCenterLeftTwelfthIcon), topCenterLeftTwelfthShortcutView) + let topCenterRightTwelfthRow = makeRow(makeLabelStack(topCenterRightTwelfthLabel, topCenterRightTwelfthIcon), topCenterRightTwelfthShortcutView) + let topRightTwelfthRow = makeRow(makeLabelStack(topRightTwelfthLabel, topRightTwelfthIcon), topRightTwelfthShortcutView) + let middleLeftTwelfthRow = makeRow(makeLabelStack(middleLeftTwelfthLabel, middleLeftTwelfthIcon), middleLeftTwelfthShortcutView) + let middleCenterLeftTwelfthRow = makeRow(makeLabelStack(middleCenterLeftTwelfthLabel, middleCenterLeftTwelfthIcon), middleCenterLeftTwelfthShortcutView) + let middleCenterRightTwelfthRow = makeRow(makeLabelStack(middleCenterRightTwelfthLabel, middleCenterRightTwelfthIcon), middleCenterRightTwelfthShortcutView) + let middleRightTwelfthRow = makeRow(makeLabelStack(middleRightTwelfthLabel, middleRightTwelfthIcon), middleRightTwelfthShortcutView) + let bottomLeftTwelfthRow = makeRow(makeLabelStack(bottomLeftTwelfthLabel, bottomLeftTwelfthIcon), bottomLeftTwelfthShortcutView) + let bottomCenterLeftTwelfthRow = makeRow(makeLabelStack(bottomCenterLeftTwelfthLabel, bottomCenterLeftTwelfthIcon), bottomCenterLeftTwelfthShortcutView) + let bottomCenterRightTwelfthRow = makeRow(makeLabelStack(bottomCenterRightTwelfthLabel, bottomCenterRightTwelfthIcon), bottomCenterRightTwelfthShortcutView) + let bottomRightTwelfthRow = makeRow(makeLabelStack(bottomRightTwelfthLabel, bottomRightTwelfthIcon), bottomRightTwelfthShortcutView) + + // Individual sixteenths rows + let topLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Left Sixteenth", tableName: "Main", value: "", comment: "")) + topLeftSixteenthLabel.alignment = .right + topLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let topCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Left Sixteenth", tableName: "Main", value: "", comment: "")) + topCenterLeftSixteenthLabel.alignment = .right + topCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let topCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Right Sixteenth", tableName: "Main", value: "", comment: "")) + topCenterRightSixteenthLabel.alignment = .right + topCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let topRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Right Sixteenth", tableName: "Main", value: "", comment: "")) + topRightSixteenthLabel.alignment = .right + topRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let upperMiddleLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Left Sixteenth", tableName: "Main", value: "", comment: "")) + upperMiddleLeftSixteenthLabel.alignment = .right + upperMiddleLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let upperMiddleCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Center Left Sixteenth", tableName: "Main", value: "", comment: "")) + upperMiddleCenterLeftSixteenthLabel.alignment = .right + upperMiddleCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let upperMiddleCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Center Right Sixteenth", tableName: "Main", value: "", comment: "")) + upperMiddleCenterRightSixteenthLabel.alignment = .right + upperMiddleCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let upperMiddleRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Right Sixteenth", tableName: "Main", value: "", comment: "")) + upperMiddleRightSixteenthLabel.alignment = .right + upperMiddleRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let lowerMiddleLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Left Sixteenth", tableName: "Main", value: "", comment: "")) + lowerMiddleLeftSixteenthLabel.alignment = .right + lowerMiddleLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let lowerMiddleCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Center Left Sixteenth", tableName: "Main", value: "", comment: "")) + lowerMiddleCenterLeftSixteenthLabel.alignment = .right + lowerMiddleCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let lowerMiddleCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Center Right Sixteenth", tableName: "Main", value: "", comment: "")) + lowerMiddleCenterRightSixteenthLabel.alignment = .right + lowerMiddleCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let lowerMiddleRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Right Sixteenth", tableName: "Main", value: "", comment: "")) + lowerMiddleRightSixteenthLabel.alignment = .right + lowerMiddleRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Left Sixteenth", tableName: "Main", value: "", comment: "")) + bottomLeftSixteenthLabel.alignment = .right + bottomLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Left Sixteenth", tableName: "Main", value: "", comment: "")) + bottomCenterLeftSixteenthLabel.alignment = .right + bottomCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Right Sixteenth", tableName: "Main", value: "", comment: "")) + bottomCenterRightSixteenthLabel.alignment = .right + bottomCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + let bottomRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Right Sixteenth", tableName: "Main", value: "", comment: "")) + bottomRightSixteenthLabel.alignment = .right + bottomRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false + + let topLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let topCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let topCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let topRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let upperMiddleLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let upperMiddleCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let upperMiddleCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let upperMiddleRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let lowerMiddleLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let lowerMiddleCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let lowerMiddleCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let lowerMiddleRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let bottomRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + + topLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + topCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + topCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + topRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + upperMiddleLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + upperMiddleCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + upperMiddleCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + upperMiddleRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + lowerMiddleLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + lowerMiddleCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + lowerMiddleCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + lowerMiddleRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + bottomLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + bottomCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + bottomCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + bottomRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) + + let topLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topLeftSixteenthIcon.image = WindowAction.topLeftSixteenth.image + topLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let topCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topCenterLeftSixteenthIcon.image = WindowAction.topCenterLeftSixteenth.image + topCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let topCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topCenterRightSixteenthIcon.image = WindowAction.topCenterRightSixteenth.image + topCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let topRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + topRightSixteenthIcon.image = WindowAction.topRightSixteenth.image + topRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let upperMiddleLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + upperMiddleLeftSixteenthIcon.image = WindowAction.upperMiddleLeftSixteenth.image + upperMiddleLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let upperMiddleCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + upperMiddleCenterLeftSixteenthIcon.image = WindowAction.upperMiddleCenterLeftSixteenth.image + upperMiddleCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let upperMiddleCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + upperMiddleCenterRightSixteenthIcon.image = WindowAction.upperMiddleCenterRightSixteenth.image + upperMiddleCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let upperMiddleRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + upperMiddleRightSixteenthIcon.image = WindowAction.upperMiddleRightSixteenth.image + upperMiddleRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let lowerMiddleLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + lowerMiddleLeftSixteenthIcon.image = WindowAction.lowerMiddleLeftSixteenth.image + lowerMiddleLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let lowerMiddleCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + lowerMiddleCenterLeftSixteenthIcon.image = WindowAction.lowerMiddleCenterLeftSixteenth.image + lowerMiddleCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let lowerMiddleCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + lowerMiddleCenterRightSixteenthIcon.image = WindowAction.lowerMiddleCenterRightSixteenth.image + lowerMiddleCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let lowerMiddleRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + lowerMiddleRightSixteenthIcon.image = WindowAction.lowerMiddleRightSixteenth.image + lowerMiddleRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomLeftSixteenthIcon.image = WindowAction.bottomLeftSixteenth.image + bottomLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomCenterLeftSixteenthIcon.image = WindowAction.bottomCenterLeftSixteenth.image + bottomCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomCenterRightSixteenthIcon.image = WindowAction.bottomCenterRightSixteenth.image + bottomCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + let bottomRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + bottomRightSixteenthIcon.image = WindowAction.bottomRightSixteenth.image + bottomRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) + + let topLeftSixteenthRow = makeRow(makeLabelStack(topLeftSixteenthLabel, topLeftSixteenthIcon), topLeftSixteenthShortcutView) + let topCenterLeftSixteenthRow = makeRow(makeLabelStack(topCenterLeftSixteenthLabel, topCenterLeftSixteenthIcon), topCenterLeftSixteenthShortcutView) + let topCenterRightSixteenthRow = makeRow(makeLabelStack(topCenterRightSixteenthLabel, topCenterRightSixteenthIcon), topCenterRightSixteenthShortcutView) + let topRightSixteenthRow = makeRow(makeLabelStack(topRightSixteenthLabel, topRightSixteenthIcon), topRightSixteenthShortcutView) + let upperMiddleLeftSixteenthRow = makeRow(makeLabelStack(upperMiddleLeftSixteenthLabel, upperMiddleLeftSixteenthIcon), upperMiddleLeftSixteenthShortcutView) + let upperMiddleCenterLeftSixteenthRow = makeRow(makeLabelStack(upperMiddleCenterLeftSixteenthLabel, upperMiddleCenterLeftSixteenthIcon), upperMiddleCenterLeftSixteenthShortcutView) + let upperMiddleCenterRightSixteenthRow = makeRow(makeLabelStack(upperMiddleCenterRightSixteenthLabel, upperMiddleCenterRightSixteenthIcon), upperMiddleCenterRightSixteenthShortcutView) + let upperMiddleRightSixteenthRow = makeRow(makeLabelStack(upperMiddleRightSixteenthLabel, upperMiddleRightSixteenthIcon), upperMiddleRightSixteenthShortcutView) + let lowerMiddleLeftSixteenthRow = makeRow(makeLabelStack(lowerMiddleLeftSixteenthLabel, lowerMiddleLeftSixteenthIcon), lowerMiddleLeftSixteenthShortcutView) + let lowerMiddleCenterLeftSixteenthRow = makeRow(makeLabelStack(lowerMiddleCenterLeftSixteenthLabel, lowerMiddleCenterLeftSixteenthIcon), lowerMiddleCenterLeftSixteenthShortcutView) + let lowerMiddleCenterRightSixteenthRow = makeRow(makeLabelStack(lowerMiddleCenterRightSixteenthLabel, lowerMiddleCenterRightSixteenthIcon), lowerMiddleCenterRightSixteenthShortcutView) + let lowerMiddleRightSixteenthRow = makeRow(makeLabelStack(lowerMiddleRightSixteenthLabel, lowerMiddleRightSixteenthIcon), lowerMiddleRightSixteenthShortcutView) + let bottomLeftSixteenthRow = makeRow(makeLabelStack(bottomLeftSixteenthLabel, bottomLeftSixteenthIcon), bottomLeftSixteenthShortcutView) + let bottomCenterLeftSixteenthRow = makeRow(makeLabelStack(bottomCenterLeftSixteenthLabel, bottomCenterLeftSixteenthIcon), bottomCenterLeftSixteenthShortcutView) + let bottomCenterRightSixteenthRow = makeRow(makeLabelStack(bottomCenterRightSixteenthLabel, bottomCenterRightSixteenthIcon), bottomCenterRightSixteenthShortcutView) + let bottomRightSixteenthRow = makeRow(makeLabelStack(bottomRightSixteenthLabel, bottomRightSixteenthIcon), bottomRightSixteenthShortcutView) + + // Cycling rows - one per category (hidden by default, shown when cycling mode is on) + let eighthsCyclingLabel = NSTextField(labelWithString: NSLocalizedString("Eighths (4\u{00d7}2)", tableName: "Main", value: "", comment: "")) + eighthsCyclingLabel.alignment = .right + eighthsCyclingLabel.translatesAutoresizingMaskIntoConstraints = false + let twelfthsCyclingLabel = NSTextField(labelWithString: NSLocalizedString("Twelfths (4\u{00d7}3)", tableName: "Main", value: "", comment: "")) + twelfthsCyclingLabel.alignment = .right + twelfthsCyclingLabel.translatesAutoresizingMaskIntoConstraints = false + let sixteenthsCyclingLabel = NSTextField(labelWithString: NSLocalizedString("Sixteenths (4\u{00d7}4)", tableName: "Main", value: "", comment: "")) + sixteenthsCyclingLabel.alignment = .right + sixteenthsCyclingLabel.translatesAutoresizingMaskIntoConstraints = false + + let eighthsCyclingShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let twelfthsCyclingShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let sixteenthsCyclingShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + + eighthsCyclingShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftEighth.name, withTransformerName: MASDictionaryTransformerName) + twelfthsCyclingShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) + sixteenthsCyclingShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) + + let eighthsCyclingIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + eighthsCyclingIcon.image = WindowAction.topLeftEighth.image + eighthsCyclingIcon.image?.size = NSSize(width: 21, height: 14) + let twelfthsCyclingIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + twelfthsCyclingIcon.image = WindowAction.topLeftTwelfth.image + twelfthsCyclingIcon.image?.size = NSSize(width: 21, height: 14) + let sixteenthsCyclingIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + sixteenthsCyclingIcon.image = WindowAction.topLeftSixteenth.image + sixteenthsCyclingIcon.image?.size = NSSize(width: 21, height: 14) + + let eighthsCyclingRow = makeRow(makeLabelStack(eighthsCyclingLabel, eighthsCyclingIcon), eighthsCyclingShortcutView) + let twelfthsCyclingRow = makeRow(makeLabelStack(twelfthsCyclingLabel, twelfthsCyclingIcon), twelfthsCyclingShortcutView) + let sixteenthsCyclingRow = makeRow(makeLabelStack(sixteenthsCyclingLabel, sixteenthsCyclingIcon), sixteenthsCyclingShortcutView) if Defaults.allowAnyShortcut.enabled { let passThroughValidator = PassthroughShortcutValidator() - twelfthsShortcutView.shortcutValidator = passThroughValidator - sixteenthsShortcutView.shortcutValidator = passThroughValidator + topLeftTwelfthShortcutView.shortcutValidator = passThroughValidator + topCenterLeftTwelfthShortcutView.shortcutValidator = passThroughValidator + topCenterRightTwelfthShortcutView.shortcutValidator = passThroughValidator + topRightTwelfthShortcutView.shortcutValidator = passThroughValidator + middleLeftTwelfthShortcutView.shortcutValidator = passThroughValidator + middleCenterLeftTwelfthShortcutView.shortcutValidator = passThroughValidator + middleCenterRightTwelfthShortcutView.shortcutValidator = passThroughValidator + middleRightTwelfthShortcutView.shortcutValidator = passThroughValidator + bottomLeftTwelfthShortcutView.shortcutValidator = passThroughValidator + bottomCenterLeftTwelfthShortcutView.shortcutValidator = passThroughValidator + bottomCenterRightTwelfthShortcutView.shortcutValidator = passThroughValidator + bottomRightTwelfthShortcutView.shortcutValidator = passThroughValidator + topLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + topCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + topCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator + topRightSixteenthShortcutView.shortcutValidator = passThroughValidator + upperMiddleLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + upperMiddleCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + upperMiddleCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator + upperMiddleRightSixteenthShortcutView.shortcutValidator = passThroughValidator + lowerMiddleLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + lowerMiddleCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + lowerMiddleCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator + lowerMiddleRightSixteenthShortcutView.shortcutValidator = passThroughValidator + bottomLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + bottomCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator + bottomCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator + bottomRightSixteenthShortcutView.shortcutValidator = passThroughValidator + eighthsCyclingShortcutView.shortcutValidator = passThroughValidator + twelfthsCyclingShortcutView.shortcutValidator = passThroughValidator + sixteenthsCyclingShortcutView.shortcutValidator = passThroughValidator } - let twelfthsIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - twelfthsIcon.image = WindowAction.topLeftTwelfth.image - twelfthsIcon.image?.size = NSSize(width: 21, height: 14) - - let sixteenthsIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - sixteenthsIcon.image = WindowAction.topLeftSixteenth.image - sixteenthsIcon.image?.size = NSSize(width: 21, height: 14) - - let twelfthsLabelStack = NSStackView() - twelfthsLabelStack.orientation = .horizontal - twelfthsLabelStack.alignment = .centerY - twelfthsLabelStack.spacing = 8 - twelfthsLabelStack.addArrangedSubview(twelfthsLabel) - twelfthsLabelStack.addArrangedSubview(twelfthsIcon) - - let sixteenthsLabelStack = NSStackView() - sixteenthsLabelStack.orientation = .horizontal - sixteenthsLabelStack.alignment = .centerY - sixteenthsLabelStack.spacing = 8 - sixteenthsLabelStack.addArrangedSubview(sixteenthsLabel) - sixteenthsLabelStack.addArrangedSubview(sixteenthsIcon) - - let twelfthsRow = NSStackView() - twelfthsRow.orientation = .horizontal - twelfthsRow.alignment = .centerY - twelfthsRow.spacing = 18 - twelfthsRow.addArrangedSubview(twelfthsLabelStack) - twelfthsRow.addArrangedSubview(twelfthsShortcutView) - - let sixteenthsRow = NSStackView() - sixteenthsRow.orientation = .horizontal - sixteenthsRow.alignment = .centerY - sixteenthsRow.spacing = 18 - sixteenthsRow.addArrangedSubview(sixteenthsLabelStack) - sixteenthsRow.addArrangedSubview(sixteenthsShortcutView) + // Collect rows for toggling + let individualGridRows: [NSView] = [ + topLeftTwelfthRow, topCenterLeftTwelfthRow, topCenterRightTwelfthRow, topRightTwelfthRow, + middleLeftTwelfthRow, middleCenterLeftTwelfthRow, middleCenterRightTwelfthRow, middleRightTwelfthRow, + bottomLeftTwelfthRow, bottomCenterLeftTwelfthRow, bottomCenterRightTwelfthRow, bottomRightTwelfthRow, + topLeftSixteenthRow, topCenterLeftSixteenthRow, topCenterRightSixteenthRow, topRightSixteenthRow, + upperMiddleLeftSixteenthRow, upperMiddleCenterLeftSixteenthRow, upperMiddleCenterRightSixteenthRow, upperMiddleRightSixteenthRow, + lowerMiddleLeftSixteenthRow, lowerMiddleCenterLeftSixteenthRow, lowerMiddleCenterRightSixteenthRow, lowerMiddleRightSixteenthRow, + bottomLeftSixteenthRow, bottomCenterLeftSixteenthRow, bottomCenterRightSixteenthRow, bottomRightSixteenthRow + ] + let cyclingGridRows: [NSView] = [eighthsCyclingRow, twelfthsCyclingRow, sixteenthsCyclingRow] + + let isCycling = Defaults.useCyclingShortcuts.enabled + individualGridRows.forEach { $0.isHidden = isCycling } + cyclingGridRows.forEach { $0.isHidden = !isCycling } + + // Assemble the grid section in main stack mainStackView.addArrangedSubview(gridHeaderLabel) - mainStackView.setCustomSpacing(4, after: gridHeaderLabel) + mainStackView.setCustomSpacing(6, after: gridHeaderLabel) + mainStackView.addArrangedSubview(cyclingCheckbox) + mainStackView.setCustomSpacing(4, after: cyclingCheckbox) mainStackView.addArrangedSubview(cyclingHintLabel) mainStackView.setCustomSpacing(8, after: cyclingHintLabel) - mainStackView.addArrangedSubview(twelfthsRow) - mainStackView.addArrangedSubview(sixteenthsRow) + + // Cycling rows + mainStackView.addArrangedSubview(eighthsCyclingRow) + mainStackView.addArrangedSubview(twelfthsCyclingRow) + mainStackView.addArrangedSubview(sixteenthsCyclingRow) + + // Individual twelfths rows + mainStackView.addArrangedSubview(topLeftTwelfthRow) + mainStackView.addArrangedSubview(topCenterLeftTwelfthRow) + mainStackView.addArrangedSubview(topCenterRightTwelfthRow) + mainStackView.addArrangedSubview(topRightTwelfthRow) + mainStackView.addArrangedSubview(middleLeftTwelfthRow) + mainStackView.addArrangedSubview(middleCenterLeftTwelfthRow) + mainStackView.addArrangedSubview(middleCenterRightTwelfthRow) + mainStackView.addArrangedSubview(middleRightTwelfthRow) + mainStackView.addArrangedSubview(bottomLeftTwelfthRow) + mainStackView.addArrangedSubview(bottomCenterLeftTwelfthRow) + mainStackView.addArrangedSubview(bottomCenterRightTwelfthRow) + mainStackView.addArrangedSubview(bottomRightTwelfthRow) + + // Individual sixteenths rows + mainStackView.addArrangedSubview(topLeftSixteenthRow) + mainStackView.addArrangedSubview(topCenterLeftSixteenthRow) + mainStackView.addArrangedSubview(topCenterRightSixteenthRow) + mainStackView.addArrangedSubview(topRightSixteenthRow) + mainStackView.addArrangedSubview(upperMiddleLeftSixteenthRow) + mainStackView.addArrangedSubview(upperMiddleCenterLeftSixteenthRow) + mainStackView.addArrangedSubview(upperMiddleCenterRightSixteenthRow) + mainStackView.addArrangedSubview(upperMiddleRightSixteenthRow) + mainStackView.addArrangedSubview(lowerMiddleLeftSixteenthRow) + mainStackView.addArrangedSubview(lowerMiddleCenterLeftSixteenthRow) + mainStackView.addArrangedSubview(lowerMiddleCenterRightSixteenthRow) + mainStackView.addArrangedSubview(lowerMiddleRightSixteenthRow) + mainStackView.addArrangedSubview(bottomLeftSixteenthRow) + mainStackView.addArrangedSubview(bottomCenterLeftSixteenthRow) + mainStackView.addArrangedSubview(bottomCenterRightSixteenthRow) + mainStackView.addArrangedSubview(bottomRightSixteenthRow) + + // Store row references for toggling via tag on cyclingCheckbox + cyclingCheckbox.tag = 0 + objc_setAssociatedObject(cyclingCheckbox, &SettingsViewController.individualRowsKey, individualGridRows, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(cyclingCheckbox, &SettingsViewController.cyclingRowsKey, cyclingGridRows, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(cyclingCheckbox, &SettingsViewController.cyclingHintKey, cyclingHintLabel, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) mainStackView.addArrangedSubview(splitRatioHeaderLabel) mainStackView.setCustomSpacing(10, after: splitRatioHeaderLabel) @@ -847,7 +1228,38 @@ class SettingsViewController: NSViewController { bottomLeftEighthLabel.widthAnchor.constraint(equalTo: bottomCenterLeftEighthLabel.widthAnchor), bottomCenterLeftEighthLabel.widthAnchor.constraint(equalTo: bottomCenterRightEighthLabel.widthAnchor), bottomCenterRightEighthLabel.widthAnchor.constraint(equalTo: bottomRightEighthLabel.widthAnchor), - bottomVerticalTwoThirdsLabel.widthAnchor.constraint(equalTo: hSplitLabel.widthAnchor), + bottomRightEighthLabel.widthAnchor.constraint(equalTo: topLeftTwelfthLabel.widthAnchor), + topLeftTwelfthLabel.widthAnchor.constraint(equalTo: topCenterLeftTwelfthLabel.widthAnchor), + topCenterLeftTwelfthLabel.widthAnchor.constraint(equalTo: topCenterRightTwelfthLabel.widthAnchor), + topCenterRightTwelfthLabel.widthAnchor.constraint(equalTo: topRightTwelfthLabel.widthAnchor), + topRightTwelfthLabel.widthAnchor.constraint(equalTo: middleLeftTwelfthLabel.widthAnchor), + middleLeftTwelfthLabel.widthAnchor.constraint(equalTo: middleCenterLeftTwelfthLabel.widthAnchor), + middleCenterLeftTwelfthLabel.widthAnchor.constraint(equalTo: middleCenterRightTwelfthLabel.widthAnchor), + middleCenterRightTwelfthLabel.widthAnchor.constraint(equalTo: middleRightTwelfthLabel.widthAnchor), + middleRightTwelfthLabel.widthAnchor.constraint(equalTo: bottomLeftTwelfthLabel.widthAnchor), + bottomLeftTwelfthLabel.widthAnchor.constraint(equalTo: bottomCenterLeftTwelfthLabel.widthAnchor), + bottomCenterLeftTwelfthLabel.widthAnchor.constraint(equalTo: bottomCenterRightTwelfthLabel.widthAnchor), + bottomCenterRightTwelfthLabel.widthAnchor.constraint(equalTo: bottomRightTwelfthLabel.widthAnchor), + bottomRightTwelfthLabel.widthAnchor.constraint(equalTo: topLeftSixteenthLabel.widthAnchor), + topLeftSixteenthLabel.widthAnchor.constraint(equalTo: topCenterLeftSixteenthLabel.widthAnchor), + topCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: topCenterRightSixteenthLabel.widthAnchor), + topCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: topRightSixteenthLabel.widthAnchor), + topRightSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleLeftSixteenthLabel.widthAnchor), + upperMiddleLeftSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleCenterLeftSixteenthLabel.widthAnchor), + upperMiddleCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleCenterRightSixteenthLabel.widthAnchor), + upperMiddleCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleRightSixteenthLabel.widthAnchor), + upperMiddleRightSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleLeftSixteenthLabel.widthAnchor), + lowerMiddleLeftSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleCenterLeftSixteenthLabel.widthAnchor), + lowerMiddleCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleCenterRightSixteenthLabel.widthAnchor), + lowerMiddleCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleRightSixteenthLabel.widthAnchor), + lowerMiddleRightSixteenthLabel.widthAnchor.constraint(equalTo: bottomLeftSixteenthLabel.widthAnchor), + bottomLeftSixteenthLabel.widthAnchor.constraint(equalTo: bottomCenterLeftSixteenthLabel.widthAnchor), + bottomCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: bottomCenterRightSixteenthLabel.widthAnchor), + bottomCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: bottomRightSixteenthLabel.widthAnchor), + bottomRightSixteenthLabel.widthAnchor.constraint(equalTo: eighthsCyclingLabel.widthAnchor), + eighthsCyclingLabel.widthAnchor.constraint(equalTo: twelfthsCyclingLabel.widthAnchor), + twelfthsCyclingLabel.widthAnchor.constraint(equalTo: sixteenthsCyclingLabel.widthAnchor), + sixteenthsCyclingLabel.widthAnchor.constraint(equalTo: hSplitLabel.widthAnchor), hSplitLabel.widthAnchor.constraint(equalTo: vSplitLabel.widthAnchor), largerWidthLabelStack.widthAnchor.constraint(equalTo: smallerWidthLabelStack.widthAnchor), largerWidthShortcutView.widthAnchor.constraint(equalToConstant: 160), @@ -866,11 +1278,42 @@ class SettingsViewController: NSViewController { bottomCenterLeftEighthShortcutView.widthAnchor.constraint(equalToConstant: 160), bottomCenterRightEighthShortcutView.widthAnchor.constraint(equalToConstant: 160), bottomRightEighthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topCenterLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topCenterRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + middleLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + middleCenterLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + middleCenterRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + middleRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomCenterLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomCenterRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + topRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + upperMiddleLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + upperMiddleCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + upperMiddleCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + upperMiddleRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + lowerMiddleLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + lowerMiddleCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + lowerMiddleCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + lowerMiddleRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + bottomRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), + eighthsCyclingShortcutView.widthAnchor.constraint(equalToConstant: 160), + twelfthsCyclingShortcutView.widthAnchor.constraint(equalToConstant: 160), + sixteenthsCyclingShortcutView.widthAnchor.constraint(equalToConstant: 160), widthStepField.trailingAnchor.constraint(equalTo: largerWidthShortcutView.trailingAnchor), hSplitField.widthAnchor.constraint(equalToConstant: 160), vSplitField.widthAnchor.constraint(equalToConstant: 160), - widthStepField.trailingAnchor.constraint(equalTo: largerWidthShortcutView.trailingAnchor), showEighthsCheckbox.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + cyclingCheckbox.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), smallerWidthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), topVerticalThirdShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), middleVerticalThirdShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), @@ -885,14 +1328,39 @@ class SettingsViewController: NSViewController { bottomCenterLeftEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), bottomCenterRightEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), bottomRightEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topCenterLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topCenterRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + middleLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + middleCenterLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + middleCenterRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + middleRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomCenterLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomCenterRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + topRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + upperMiddleLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + upperMiddleCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + upperMiddleCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + upperMiddleRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + lowerMiddleLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + lowerMiddleCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + lowerMiddleCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + lowerMiddleRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + bottomRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + eighthsCyclingShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + twelfthsCyclingShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + sixteenthsCyclingShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), gridHeaderLabel.widthAnchor.constraint(equalTo: mainStackView.widthAnchor), cyclingHintLabel.widthAnchor.constraint(equalTo: mainStackView.widthAnchor, constant: -20), - twelfthsLabel.widthAnchor.constraint(equalTo: bottomRightEighthLabel.widthAnchor), - sixteenthsLabel.widthAnchor.constraint(equalTo: bottomRightEighthLabel.widthAnchor), - twelfthsShortcutView.widthAnchor.constraint(equalToConstant: 160), - sixteenthsShortcutView.widthAnchor.constraint(equalToConstant: 160), - twelfthsShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - sixteenthsShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), hSplitField.trailingAnchor.constraint(equalTo: largerWidthShortcutView.trailingAnchor), vSplitField.trailingAnchor.constraint(equalTo: largerWidthShortcutView.trailingAnchor) ]) From 10bd300fdb316933b5e1cb2ba7d8fabaeaace0f2 Mon Sep 17 00:00:00 2001 From: Myron Koch <126993133+MyronKoch@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:55:16 -0400 Subject: [PATCH 07/10] Simplify grid shortcuts UI and unify menu checkbox per maintainer feedback - Replace "Show Eighths in Menu" with "Show additional sizes in menu" checkbox that shows 8ths, 9ths, 12ths, 16ths submenus and converts Thirds & Size into submenus when enabled - Remove "Use cycling shortcuts" checkbox and individual twelfths/ sixteenths rows (-454 lines); keep only cycling shortcut recorders for Ninths (3x3), Twelfths (4x3), Sixteenths (4x4) - Add ninths category to WindowActionCategory for menu grouping - Remove useCyclingShortcuts preference (no longer needed) --- Rectangle/AppDelegate.swift | 10 +- Rectangle/Defaults.swift | 8 +- .../PrefsWindow/SettingsViewController.swift | 555 ++---------------- Rectangle/WindowAction.swift | 3 +- Rectangle/WindowActionCategory.swift | 4 +- 5 files changed, 63 insertions(+), 517 deletions(-) diff --git a/Rectangle/AppDelegate.swift b/Rectangle/AppDelegate.swift index 9d2101bf..f8bc4b0e 100644 --- a/Rectangle/AppDelegate.swift +++ b/Rectangle/AppDelegate.swift @@ -377,6 +377,7 @@ extension AppDelegate: NSMenuDelegate { } func addWindowActionMenuItems() { + let additionalSizeCategories: Set = [.eighths, .ninths, .twelfths, .sixteenths] var menuIndex = 0 var categoryMenus: [CategoryMenu] = [] for action in WindowAction.active { @@ -393,7 +394,7 @@ extension AppDelegate: NSMenuDelegate { categoryMenus.last?.menu.addItem(newMenuItem) continue } - + if menuIndex != 0 && action.firstInGroup { mainStatusMenu.insertItem(NSMenuItem.separator(), at: menuIndex) menuIndex += 1 @@ -405,16 +406,19 @@ extension AppDelegate: NSMenuDelegate { if !categoryMenus.isEmpty { mainStatusMenu.insertItem(NSMenuItem.separator(), at: menuIndex) menuIndex += 1 - + for categoryMenu in categoryMenus { categoryMenu.menu.delegate = self let menuMenuItem = NSMenuItem(title: categoryMenu.category.displayName, action: nil, keyEquivalent: "") + if additionalSizeCategories.contains(categoryMenu.category) { + menuMenuItem.isHidden = !Defaults.showAdditionalSizesInMenu.userEnabled + } mainStatusMenu.insertItem(menuMenuItem, at: menuIndex) mainStatusMenu.setSubmenu(categoryMenu.menu, for: menuMenuItem) menuIndex += 1 } } - + mainStatusMenu.insertItem(NSMenuItem.separator(), at: menuIndex) menuIndex += 1 diff --git a/Rectangle/Defaults.swift b/Rectangle/Defaults.swift index 4fec2a92..c523c690 100644 --- a/Rectangle/Defaults.swift +++ b/Rectangle/Defaults.swift @@ -47,6 +47,7 @@ class Defaults { static let lastVersion = StringDefault(key: "lastVersion") static let installVersion = StringDefault(key: "installVersion") static let showAllActionsInMenu = OptionalBoolDefault(key: "showAllActionsInMenu") + static let showAdditionalSizesInMenu = OptionalBoolDefault(key: "showAdditionalSizesInMenu") static var SUHasLaunchedBefore: Bool { UserDefaults.standard.bool(forKey: "SUHasLaunchedBefore") } static let footprintAlpha = FloatDefault(key: "footprintAlpha", defaultValue: 0.3) static let footprintBorderWidth = FloatDefault(key: "footprintBorderWidth", defaultValue: 2) @@ -98,9 +99,6 @@ class Defaults { static let systemWideMouseDownApps = JSONDefault>(key:"systemWideMouseDownApps", defaultValue: Set(["org.languagetool.desktop", "com.microsoft.teams2"])) static let internalTilingNotified = BoolDefault(key: "internalTilingNotified") static let screensOrderedByX = OptionalBoolDefault(key: "screensOrderedByX") - static let showEighthsInMenu = OptionalBoolDefault(key: "showEighthsInMenu") - static let useCyclingShortcuts = BoolDefault(key: "useCyclingShortcuts") - static var array: [Default] = [ launchOnLogin, disabledApps, @@ -137,6 +135,7 @@ class Defaults { screenEdgeGapsOnMainScreenOnly, screenEdgeGapTopNotch, showAllActionsInMenu, + showAdditionalSizesInMenu, footprintAlpha, footprintBorderWidth, footprintFade, @@ -184,8 +183,7 @@ class Defaults { systemWideMouseDown, systemWideMouseDownApps, screensOrderedByX, - showEighthsInMenu, - useCyclingShortcuts + showAdditionalSizesInMenu ] } diff --git a/Rectangle/PrefsWindow/SettingsViewController.swift b/Rectangle/PrefsWindow/SettingsViewController.swift index c6c6fafb..cb53bfa5 100644 --- a/Rectangle/PrefsWindow/SettingsViewController.swift +++ b/Rectangle/PrefsWindow/SettingsViewController.swift @@ -107,27 +107,9 @@ class SettingsViewController: NSViewController { Notification.Name.allowAnyShortcut.post(object: newSetting) } - @objc func toggleShowEighthsInMenu(_ sender: NSButton) { + @objc func toggleShowAdditionalSizesInMenu(_ sender: NSButton) { let enabled: Bool = sender.state == .on - Defaults.showEighthsInMenu.enabled = enabled - } - - private static var individualRowsKey = "individualRowsKey" - private static var cyclingRowsKey = "cyclingRowsKey" - private static var cyclingHintKey = "cyclingHintKey" - - @objc func toggleCyclingShortcuts(_ sender: NSButton) { - let enabled = sender.state == .on - Defaults.useCyclingShortcuts.enabled = enabled - if let individualRows = objc_getAssociatedObject(sender, &SettingsViewController.individualRowsKey) as? [NSView] { - individualRows.forEach { $0.isHidden = enabled } - } - if let cyclingRows = objc_getAssociatedObject(sender, &SettingsViewController.cyclingRowsKey) as? [NSView] { - cyclingRows.forEach { $0.isHidden = !enabled } - } - if let hintLabel = objc_getAssociatedObject(sender, &SettingsViewController.cyclingHintKey) as? NSView { - hintLabel.isHidden = !enabled - } + Defaults.showAdditionalSizesInMenu.enabled = enabled } @IBAction func checkForUpdates(_ sender: Any) { @@ -758,338 +740,49 @@ class SettingsViewController: NSViewController { mainStackView.addArrangedSubview(bottomCenterRightEighthRow) mainStackView.addArrangedSubview(bottomRightEighthRow) - let showEighthsCheckbox = NSButton(checkboxWithTitle: NSLocalizedString("Show Eighths in menu", tableName: "Main", value: "", comment: ""), target: self, action: #selector(toggleShowEighthsInMenu(_:))) - showEighthsCheckbox.state = Defaults.showEighthsInMenu.userEnabled ? .on : .off - showEighthsCheckbox.translatesAutoresizingMaskIntoConstraints = false - showEighthsCheckbox.alignment = .right - showEighthsCheckbox.imageHugsTitle = true + let showAdditionalSizesCheckbox = NSButton(checkboxWithTitle: NSLocalizedString("Show additional sizes in menu", tableName: "Main", value: "", comment: ""), target: self, action: #selector(toggleShowAdditionalSizesInMenu(_:))) + showAdditionalSizesCheckbox.state = Defaults.showAdditionalSizesInMenu.userEnabled ? .on : .off + showAdditionalSizesCheckbox.translatesAutoresizingMaskIntoConstraints = false + showAdditionalSizesCheckbox.alignment = .right + showAdditionalSizesCheckbox.imageHugsTitle = true - mainStackView.addArrangedSubview(showEighthsCheckbox) + mainStackView.addArrangedSubview(showAdditionalSizesCheckbox) - // Grid Positions section + // Grid Positions - cycling shortcuts for larger grids let gridHeaderLabel = NSTextField(labelWithString: NSLocalizedString("Grid Positions", tableName: "Main", value: "", comment: "")) gridHeaderLabel.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) gridHeaderLabel.alignment = .center gridHeaderLabel.translatesAutoresizingMaskIntoConstraints = false - let cyclingCheckbox = NSButton(checkboxWithTitle: NSLocalizedString("Use cycling shortcuts", tableName: "Main", value: "", comment: ""), target: self, action: #selector(toggleCyclingShortcuts(_:))) - cyclingCheckbox.state = Defaults.useCyclingShortcuts.enabled ? .on : .off - cyclingCheckbox.translatesAutoresizingMaskIntoConstraints = false - cyclingCheckbox.imageHugsTitle = true - let cyclingHintLabel = NSTextField(wrappingLabelWithString: NSLocalizedString("Press the shortcut repeatedly to cycle through all positions in the grid.", tableName: "Main", value: "", comment: "")) cyclingHintLabel.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) cyclingHintLabel.textColor = .secondaryLabelColor cyclingHintLabel.alignment = .center cyclingHintLabel.translatesAutoresizingMaskIntoConstraints = false - cyclingHintLabel.isHidden = !Defaults.useCyclingShortcuts.enabled - - // Individual twelfths rows - let topLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Left Twelfth", tableName: "Main", value: "", comment: "")) - topLeftTwelfthLabel.alignment = .right - topLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let topCenterLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Left Twelfth", tableName: "Main", value: "", comment: "")) - topCenterLeftTwelfthLabel.alignment = .right - topCenterLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let topCenterRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Right Twelfth", tableName: "Main", value: "", comment: "")) - topCenterRightTwelfthLabel.alignment = .right - topCenterRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let topRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Top Right Twelfth", tableName: "Main", value: "", comment: "")) - topRightTwelfthLabel.alignment = .right - topRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let middleLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Left Twelfth", tableName: "Main", value: "", comment: "")) - middleLeftTwelfthLabel.alignment = .right - middleLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let middleCenterLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Center Left Twelfth", tableName: "Main", value: "", comment: "")) - middleCenterLeftTwelfthLabel.alignment = .right - middleCenterLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let middleCenterRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Center Right Twelfth", tableName: "Main", value: "", comment: "")) - middleCenterRightTwelfthLabel.alignment = .right - middleCenterRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let middleRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Middle Right Twelfth", tableName: "Main", value: "", comment: "")) - middleRightTwelfthLabel.alignment = .right - middleRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Left Twelfth", tableName: "Main", value: "", comment: "")) - bottomLeftTwelfthLabel.alignment = .right - bottomLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomCenterLeftTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Left Twelfth", tableName: "Main", value: "", comment: "")) - bottomCenterLeftTwelfthLabel.alignment = .right - bottomCenterLeftTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomCenterRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Right Twelfth", tableName: "Main", value: "", comment: "")) - bottomCenterRightTwelfthLabel.alignment = .right - bottomCenterRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomRightTwelfthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Right Twelfth", tableName: "Main", value: "", comment: "")) - bottomRightTwelfthLabel.alignment = .right - bottomRightTwelfthLabel.translatesAutoresizingMaskIntoConstraints = false - - let topLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let topCenterLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let topCenterRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let topRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let middleLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let middleCenterLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let middleCenterRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let middleRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomCenterLeftTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomCenterRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomRightTwelfthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - - topLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) - topCenterLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) - topCenterRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) - topRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) - middleLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) - middleCenterLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleCenterLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) - middleCenterRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleCenterRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) - middleRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.middleRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) - bottomLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) - bottomCenterLeftTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) - bottomCenterRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) - bottomRightTwelfthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomRightTwelfth.name, withTransformerName: MASDictionaryTransformerName) - - let topLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topLeftTwelfthIcon.image = WindowAction.topLeftTwelfth.image - topLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let topCenterLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topCenterLeftTwelfthIcon.image = WindowAction.topCenterLeftTwelfth.image - topCenterLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let topCenterRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topCenterRightTwelfthIcon.image = WindowAction.topCenterRightTwelfth.image - topCenterRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let topRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topRightTwelfthIcon.image = WindowAction.topRightTwelfth.image - topRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let middleLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - middleLeftTwelfthIcon.image = WindowAction.middleLeftTwelfth.image - middleLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let middleCenterLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - middleCenterLeftTwelfthIcon.image = WindowAction.middleCenterLeftTwelfth.image - middleCenterLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let middleCenterRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - middleCenterRightTwelfthIcon.image = WindowAction.middleCenterRightTwelfth.image - middleCenterRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let middleRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - middleRightTwelfthIcon.image = WindowAction.middleRightTwelfth.image - middleRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomLeftTwelfthIcon.image = WindowAction.bottomLeftTwelfth.image - bottomLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomCenterLeftTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomCenterLeftTwelfthIcon.image = WindowAction.bottomCenterLeftTwelfth.image - bottomCenterLeftTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomCenterRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomCenterRightTwelfthIcon.image = WindowAction.bottomCenterRightTwelfth.image - bottomCenterRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomRightTwelfthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomRightTwelfthIcon.image = WindowAction.bottomRightTwelfth.image - bottomRightTwelfthIcon.image?.size = NSSize(width: 21, height: 14) - - func makeLabelStack(_ label: NSTextField, _ icon: NSImageView) -> NSStackView { - let stack = NSStackView() - stack.orientation = .horizontal - stack.alignment = .centerY - stack.spacing = 8 - stack.addArrangedSubview(label) - stack.addArrangedSubview(icon) - return stack - } - - func makeRow(_ labelStack: NSStackView, _ shortcutView: MASShortcutView) -> NSStackView { - let row = NSStackView() - row.orientation = .horizontal - row.alignment = .centerY - row.spacing = 18 - row.addArrangedSubview(labelStack) - row.addArrangedSubview(shortcutView) - return row - } - let topLeftTwelfthRow = makeRow(makeLabelStack(topLeftTwelfthLabel, topLeftTwelfthIcon), topLeftTwelfthShortcutView) - let topCenterLeftTwelfthRow = makeRow(makeLabelStack(topCenterLeftTwelfthLabel, topCenterLeftTwelfthIcon), topCenterLeftTwelfthShortcutView) - let topCenterRightTwelfthRow = makeRow(makeLabelStack(topCenterRightTwelfthLabel, topCenterRightTwelfthIcon), topCenterRightTwelfthShortcutView) - let topRightTwelfthRow = makeRow(makeLabelStack(topRightTwelfthLabel, topRightTwelfthIcon), topRightTwelfthShortcutView) - let middleLeftTwelfthRow = makeRow(makeLabelStack(middleLeftTwelfthLabel, middleLeftTwelfthIcon), middleLeftTwelfthShortcutView) - let middleCenterLeftTwelfthRow = makeRow(makeLabelStack(middleCenterLeftTwelfthLabel, middleCenterLeftTwelfthIcon), middleCenterLeftTwelfthShortcutView) - let middleCenterRightTwelfthRow = makeRow(makeLabelStack(middleCenterRightTwelfthLabel, middleCenterRightTwelfthIcon), middleCenterRightTwelfthShortcutView) - let middleRightTwelfthRow = makeRow(makeLabelStack(middleRightTwelfthLabel, middleRightTwelfthIcon), middleRightTwelfthShortcutView) - let bottomLeftTwelfthRow = makeRow(makeLabelStack(bottomLeftTwelfthLabel, bottomLeftTwelfthIcon), bottomLeftTwelfthShortcutView) - let bottomCenterLeftTwelfthRow = makeRow(makeLabelStack(bottomCenterLeftTwelfthLabel, bottomCenterLeftTwelfthIcon), bottomCenterLeftTwelfthShortcutView) - let bottomCenterRightTwelfthRow = makeRow(makeLabelStack(bottomCenterRightTwelfthLabel, bottomCenterRightTwelfthIcon), bottomCenterRightTwelfthShortcutView) - let bottomRightTwelfthRow = makeRow(makeLabelStack(bottomRightTwelfthLabel, bottomRightTwelfthIcon), bottomRightTwelfthShortcutView) - - // Individual sixteenths rows - let topLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Left Sixteenth", tableName: "Main", value: "", comment: "")) - topLeftSixteenthLabel.alignment = .right - topLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let topCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Left Sixteenth", tableName: "Main", value: "", comment: "")) - topCenterLeftSixteenthLabel.alignment = .right - topCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let topCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Center Right Sixteenth", tableName: "Main", value: "", comment: "")) - topCenterRightSixteenthLabel.alignment = .right - topCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let topRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Top Right Sixteenth", tableName: "Main", value: "", comment: "")) - topRightSixteenthLabel.alignment = .right - topRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let upperMiddleLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Left Sixteenth", tableName: "Main", value: "", comment: "")) - upperMiddleLeftSixteenthLabel.alignment = .right - upperMiddleLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let upperMiddleCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Center Left Sixteenth", tableName: "Main", value: "", comment: "")) - upperMiddleCenterLeftSixteenthLabel.alignment = .right - upperMiddleCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let upperMiddleCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Center Right Sixteenth", tableName: "Main", value: "", comment: "")) - upperMiddleCenterRightSixteenthLabel.alignment = .right - upperMiddleCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let upperMiddleRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Upper Middle Right Sixteenth", tableName: "Main", value: "", comment: "")) - upperMiddleRightSixteenthLabel.alignment = .right - upperMiddleRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let lowerMiddleLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Left Sixteenth", tableName: "Main", value: "", comment: "")) - lowerMiddleLeftSixteenthLabel.alignment = .right - lowerMiddleLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let lowerMiddleCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Center Left Sixteenth", tableName: "Main", value: "", comment: "")) - lowerMiddleCenterLeftSixteenthLabel.alignment = .right - lowerMiddleCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let lowerMiddleCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Center Right Sixteenth", tableName: "Main", value: "", comment: "")) - lowerMiddleCenterRightSixteenthLabel.alignment = .right - lowerMiddleCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let lowerMiddleRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Lower Middle Right Sixteenth", tableName: "Main", value: "", comment: "")) - lowerMiddleRightSixteenthLabel.alignment = .right - lowerMiddleRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Left Sixteenth", tableName: "Main", value: "", comment: "")) - bottomLeftSixteenthLabel.alignment = .right - bottomLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomCenterLeftSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Left Sixteenth", tableName: "Main", value: "", comment: "")) - bottomCenterLeftSixteenthLabel.alignment = .right - bottomCenterLeftSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomCenterRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Center Right Sixteenth", tableName: "Main", value: "", comment: "")) - bottomCenterRightSixteenthLabel.alignment = .right - bottomCenterRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - let bottomRightSixteenthLabel = NSTextField(labelWithString: NSLocalizedString("Bottom Right Sixteenth", tableName: "Main", value: "", comment: "")) - bottomRightSixteenthLabel.alignment = .right - bottomRightSixteenthLabel.translatesAutoresizingMaskIntoConstraints = false - - let topLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let topCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let topCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let topRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let upperMiddleLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let upperMiddleCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let upperMiddleCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let upperMiddleRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let lowerMiddleLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let lowerMiddleCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let lowerMiddleCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let lowerMiddleRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomCenterLeftSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomCenterRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - let bottomRightSixteenthShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - - topLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - topCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - topCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - topRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.topRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - upperMiddleLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - upperMiddleCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - upperMiddleCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - upperMiddleRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.upperMiddleRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - lowerMiddleLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - lowerMiddleCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - lowerMiddleCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - lowerMiddleRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.lowerMiddleRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - bottomLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - bottomCenterLeftSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - bottomCenterRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomCenterRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - bottomRightSixteenthShortcutView.setAssociatedUserDefaultsKey(WindowAction.bottomRightSixteenth.name, withTransformerName: MASDictionaryTransformerName) - - let topLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topLeftSixteenthIcon.image = WindowAction.topLeftSixteenth.image - topLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let topCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topCenterLeftSixteenthIcon.image = WindowAction.topCenterLeftSixteenth.image - topCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let topCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topCenterRightSixteenthIcon.image = WindowAction.topCenterRightSixteenth.image - topCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let topRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - topRightSixteenthIcon.image = WindowAction.topRightSixteenth.image - topRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let upperMiddleLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - upperMiddleLeftSixteenthIcon.image = WindowAction.upperMiddleLeftSixteenth.image - upperMiddleLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let upperMiddleCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - upperMiddleCenterLeftSixteenthIcon.image = WindowAction.upperMiddleCenterLeftSixteenth.image - upperMiddleCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let upperMiddleCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - upperMiddleCenterRightSixteenthIcon.image = WindowAction.upperMiddleCenterRightSixteenth.image - upperMiddleCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let upperMiddleRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - upperMiddleRightSixteenthIcon.image = WindowAction.upperMiddleRightSixteenth.image - upperMiddleRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let lowerMiddleLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - lowerMiddleLeftSixteenthIcon.image = WindowAction.lowerMiddleLeftSixteenth.image - lowerMiddleLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let lowerMiddleCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - lowerMiddleCenterLeftSixteenthIcon.image = WindowAction.lowerMiddleCenterLeftSixteenth.image - lowerMiddleCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let lowerMiddleCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - lowerMiddleCenterRightSixteenthIcon.image = WindowAction.lowerMiddleCenterRightSixteenth.image - lowerMiddleCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let lowerMiddleRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - lowerMiddleRightSixteenthIcon.image = WindowAction.lowerMiddleRightSixteenth.image - lowerMiddleRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomLeftSixteenthIcon.image = WindowAction.bottomLeftSixteenth.image - bottomLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomCenterLeftSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomCenterLeftSixteenthIcon.image = WindowAction.bottomCenterLeftSixteenth.image - bottomCenterLeftSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomCenterRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomCenterRightSixteenthIcon.image = WindowAction.bottomCenterRightSixteenth.image - bottomCenterRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - let bottomRightSixteenthIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - bottomRightSixteenthIcon.image = WindowAction.bottomRightSixteenth.image - bottomRightSixteenthIcon.image?.size = NSSize(width: 21, height: 14) - - let topLeftSixteenthRow = makeRow(makeLabelStack(topLeftSixteenthLabel, topLeftSixteenthIcon), topLeftSixteenthShortcutView) - let topCenterLeftSixteenthRow = makeRow(makeLabelStack(topCenterLeftSixteenthLabel, topCenterLeftSixteenthIcon), topCenterLeftSixteenthShortcutView) - let topCenterRightSixteenthRow = makeRow(makeLabelStack(topCenterRightSixteenthLabel, topCenterRightSixteenthIcon), topCenterRightSixteenthShortcutView) - let topRightSixteenthRow = makeRow(makeLabelStack(topRightSixteenthLabel, topRightSixteenthIcon), topRightSixteenthShortcutView) - let upperMiddleLeftSixteenthRow = makeRow(makeLabelStack(upperMiddleLeftSixteenthLabel, upperMiddleLeftSixteenthIcon), upperMiddleLeftSixteenthShortcutView) - let upperMiddleCenterLeftSixteenthRow = makeRow(makeLabelStack(upperMiddleCenterLeftSixteenthLabel, upperMiddleCenterLeftSixteenthIcon), upperMiddleCenterLeftSixteenthShortcutView) - let upperMiddleCenterRightSixteenthRow = makeRow(makeLabelStack(upperMiddleCenterRightSixteenthLabel, upperMiddleCenterRightSixteenthIcon), upperMiddleCenterRightSixteenthShortcutView) - let upperMiddleRightSixteenthRow = makeRow(makeLabelStack(upperMiddleRightSixteenthLabel, upperMiddleRightSixteenthIcon), upperMiddleRightSixteenthShortcutView) - let lowerMiddleLeftSixteenthRow = makeRow(makeLabelStack(lowerMiddleLeftSixteenthLabel, lowerMiddleLeftSixteenthIcon), lowerMiddleLeftSixteenthShortcutView) - let lowerMiddleCenterLeftSixteenthRow = makeRow(makeLabelStack(lowerMiddleCenterLeftSixteenthLabel, lowerMiddleCenterLeftSixteenthIcon), lowerMiddleCenterLeftSixteenthShortcutView) - let lowerMiddleCenterRightSixteenthRow = makeRow(makeLabelStack(lowerMiddleCenterRightSixteenthLabel, lowerMiddleCenterRightSixteenthIcon), lowerMiddleCenterRightSixteenthShortcutView) - let lowerMiddleRightSixteenthRow = makeRow(makeLabelStack(lowerMiddleRightSixteenthLabel, lowerMiddleRightSixteenthIcon), lowerMiddleRightSixteenthShortcutView) - let bottomLeftSixteenthRow = makeRow(makeLabelStack(bottomLeftSixteenthLabel, bottomLeftSixteenthIcon), bottomLeftSixteenthShortcutView) - let bottomCenterLeftSixteenthRow = makeRow(makeLabelStack(bottomCenterLeftSixteenthLabel, bottomCenterLeftSixteenthIcon), bottomCenterLeftSixteenthShortcutView) - let bottomCenterRightSixteenthRow = makeRow(makeLabelStack(bottomCenterRightSixteenthLabel, bottomCenterRightSixteenthIcon), bottomCenterRightSixteenthShortcutView) - let bottomRightSixteenthRow = makeRow(makeLabelStack(bottomRightSixteenthLabel, bottomRightSixteenthIcon), bottomRightSixteenthShortcutView) - - // Cycling rows - one per category (hidden by default, shown when cycling mode is on) - let eighthsCyclingLabel = NSTextField(labelWithString: NSLocalizedString("Eighths (4\u{00d7}2)", tableName: "Main", value: "", comment: "")) - eighthsCyclingLabel.alignment = .right - eighthsCyclingLabel.translatesAutoresizingMaskIntoConstraints = false + // Cycling shortcut rows - Ninths, Twelfths, Sixteenths + let ninthsCyclingLabel = NSTextField(labelWithString: NSLocalizedString("Ninths (3\u{00d7}3)", tableName: "Main", value: "", comment: "")) + ninthsCyclingLabel.alignment = .right + ninthsCyclingLabel.translatesAutoresizingMaskIntoConstraints = false let twelfthsCyclingLabel = NSTextField(labelWithString: NSLocalizedString("Twelfths (4\u{00d7}3)", tableName: "Main", value: "", comment: "")) twelfthsCyclingLabel.alignment = .right twelfthsCyclingLabel.translatesAutoresizingMaskIntoConstraints = false + let sixteenthsCyclingLabel = NSTextField(labelWithString: NSLocalizedString("Sixteenths (4\u{00d7}4)", tableName: "Main", value: "", comment: "")) sixteenthsCyclingLabel.alignment = .right sixteenthsCyclingLabel.translatesAutoresizingMaskIntoConstraints = false - let eighthsCyclingShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) + let ninthsCyclingShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) let twelfthsCyclingShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) let sixteenthsCyclingShortcutView = MASShortcutView(frame: NSRect(x: 0, y: 0, width: 160, height: 19)) - eighthsCyclingShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftEighth.name, withTransformerName: MASDictionaryTransformerName) + ninthsCyclingShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftNinth.name, withTransformerName: MASDictionaryTransformerName) twelfthsCyclingShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftTwelfth.name, withTransformerName: MASDictionaryTransformerName) sixteenthsCyclingShortcutView.setAssociatedUserDefaultsKey(WindowAction.topLeftSixteenth.name, withTransformerName: MASDictionaryTransformerName) - let eighthsCyclingIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) - eighthsCyclingIcon.image = WindowAction.topLeftEighth.image - eighthsCyclingIcon.image?.size = NSSize(width: 21, height: 14) + let ninthsCyclingIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) + ninthsCyclingIcon.image = WindowAction.topLeftNinth.image + ninthsCyclingIcon.image?.size = NSSize(width: 21, height: 14) let twelfthsCyclingIcon = NSImageView(frame: NSRect(x: 0, y: 0, width: 21, height: 14)) twelfthsCyclingIcon.image = WindowAction.topLeftTwelfth.image twelfthsCyclingIcon.image?.size = NSSize(width: 21, height: 14) @@ -1097,112 +790,45 @@ class SettingsViewController: NSViewController { sixteenthsCyclingIcon.image = WindowAction.topLeftSixteenth.image sixteenthsCyclingIcon.image?.size = NSSize(width: 21, height: 14) - let eighthsCyclingRow = makeRow(makeLabelStack(eighthsCyclingLabel, eighthsCyclingIcon), eighthsCyclingShortcutView) + func makeLabelStack(_ label: NSTextField, _ icon: NSImageView) -> NSStackView { + let stack = NSStackView() + stack.orientation = .horizontal + stack.alignment = .centerY + stack.spacing = 8 + stack.addArrangedSubview(label) + stack.addArrangedSubview(icon) + return stack + } + + func makeRow(_ labelStack: NSStackView, _ shortcutView: MASShortcutView) -> NSStackView { + let row = NSStackView() + row.orientation = .horizontal + row.alignment = .centerY + row.spacing = 18 + row.addArrangedSubview(labelStack) + row.addArrangedSubview(shortcutView) + return row + } + + let ninthsCyclingRow = makeRow(makeLabelStack(ninthsCyclingLabel, ninthsCyclingIcon), ninthsCyclingShortcutView) let twelfthsCyclingRow = makeRow(makeLabelStack(twelfthsCyclingLabel, twelfthsCyclingIcon), twelfthsCyclingShortcutView) let sixteenthsCyclingRow = makeRow(makeLabelStack(sixteenthsCyclingLabel, sixteenthsCyclingIcon), sixteenthsCyclingShortcutView) if Defaults.allowAnyShortcut.enabled { let passThroughValidator = PassthroughShortcutValidator() - topLeftTwelfthShortcutView.shortcutValidator = passThroughValidator - topCenterLeftTwelfthShortcutView.shortcutValidator = passThroughValidator - topCenterRightTwelfthShortcutView.shortcutValidator = passThroughValidator - topRightTwelfthShortcutView.shortcutValidator = passThroughValidator - middleLeftTwelfthShortcutView.shortcutValidator = passThroughValidator - middleCenterLeftTwelfthShortcutView.shortcutValidator = passThroughValidator - middleCenterRightTwelfthShortcutView.shortcutValidator = passThroughValidator - middleRightTwelfthShortcutView.shortcutValidator = passThroughValidator - bottomLeftTwelfthShortcutView.shortcutValidator = passThroughValidator - bottomCenterLeftTwelfthShortcutView.shortcutValidator = passThroughValidator - bottomCenterRightTwelfthShortcutView.shortcutValidator = passThroughValidator - bottomRightTwelfthShortcutView.shortcutValidator = passThroughValidator - topLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - topCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - topCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator - topRightSixteenthShortcutView.shortcutValidator = passThroughValidator - upperMiddleLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - upperMiddleCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - upperMiddleCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator - upperMiddleRightSixteenthShortcutView.shortcutValidator = passThroughValidator - lowerMiddleLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - lowerMiddleCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - lowerMiddleCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator - lowerMiddleRightSixteenthShortcutView.shortcutValidator = passThroughValidator - bottomLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - bottomCenterLeftSixteenthShortcutView.shortcutValidator = passThroughValidator - bottomCenterRightSixteenthShortcutView.shortcutValidator = passThroughValidator - bottomRightSixteenthShortcutView.shortcutValidator = passThroughValidator - eighthsCyclingShortcutView.shortcutValidator = passThroughValidator + ninthsCyclingShortcutView.shortcutValidator = passThroughValidator twelfthsCyclingShortcutView.shortcutValidator = passThroughValidator sixteenthsCyclingShortcutView.shortcutValidator = passThroughValidator } - // Collect rows for toggling - let individualGridRows: [NSView] = [ - topLeftTwelfthRow, topCenterLeftTwelfthRow, topCenterRightTwelfthRow, topRightTwelfthRow, - middleLeftTwelfthRow, middleCenterLeftTwelfthRow, middleCenterRightTwelfthRow, middleRightTwelfthRow, - bottomLeftTwelfthRow, bottomCenterLeftTwelfthRow, bottomCenterRightTwelfthRow, bottomRightTwelfthRow, - topLeftSixteenthRow, topCenterLeftSixteenthRow, topCenterRightSixteenthRow, topRightSixteenthRow, - upperMiddleLeftSixteenthRow, upperMiddleCenterLeftSixteenthRow, upperMiddleCenterRightSixteenthRow, upperMiddleRightSixteenthRow, - lowerMiddleLeftSixteenthRow, lowerMiddleCenterLeftSixteenthRow, lowerMiddleCenterRightSixteenthRow, lowerMiddleRightSixteenthRow, - bottomLeftSixteenthRow, bottomCenterLeftSixteenthRow, bottomCenterRightSixteenthRow, bottomRightSixteenthRow - ] - - let cyclingGridRows: [NSView] = [eighthsCyclingRow, twelfthsCyclingRow, sixteenthsCyclingRow] - - let isCycling = Defaults.useCyclingShortcuts.enabled - individualGridRows.forEach { $0.isHidden = isCycling } - cyclingGridRows.forEach { $0.isHidden = !isCycling } - - // Assemble the grid section in main stack mainStackView.addArrangedSubview(gridHeaderLabel) - mainStackView.setCustomSpacing(6, after: gridHeaderLabel) - mainStackView.addArrangedSubview(cyclingCheckbox) - mainStackView.setCustomSpacing(4, after: cyclingCheckbox) + mainStackView.setCustomSpacing(4, after: gridHeaderLabel) mainStackView.addArrangedSubview(cyclingHintLabel) mainStackView.setCustomSpacing(8, after: cyclingHintLabel) - - // Cycling rows - mainStackView.addArrangedSubview(eighthsCyclingRow) + mainStackView.addArrangedSubview(ninthsCyclingRow) mainStackView.addArrangedSubview(twelfthsCyclingRow) mainStackView.addArrangedSubview(sixteenthsCyclingRow) - // Individual twelfths rows - mainStackView.addArrangedSubview(topLeftTwelfthRow) - mainStackView.addArrangedSubview(topCenterLeftTwelfthRow) - mainStackView.addArrangedSubview(topCenterRightTwelfthRow) - mainStackView.addArrangedSubview(topRightTwelfthRow) - mainStackView.addArrangedSubview(middleLeftTwelfthRow) - mainStackView.addArrangedSubview(middleCenterLeftTwelfthRow) - mainStackView.addArrangedSubview(middleCenterRightTwelfthRow) - mainStackView.addArrangedSubview(middleRightTwelfthRow) - mainStackView.addArrangedSubview(bottomLeftTwelfthRow) - mainStackView.addArrangedSubview(bottomCenterLeftTwelfthRow) - mainStackView.addArrangedSubview(bottomCenterRightTwelfthRow) - mainStackView.addArrangedSubview(bottomRightTwelfthRow) - - // Individual sixteenths rows - mainStackView.addArrangedSubview(topLeftSixteenthRow) - mainStackView.addArrangedSubview(topCenterLeftSixteenthRow) - mainStackView.addArrangedSubview(topCenterRightSixteenthRow) - mainStackView.addArrangedSubview(topRightSixteenthRow) - mainStackView.addArrangedSubview(upperMiddleLeftSixteenthRow) - mainStackView.addArrangedSubview(upperMiddleCenterLeftSixteenthRow) - mainStackView.addArrangedSubview(upperMiddleCenterRightSixteenthRow) - mainStackView.addArrangedSubview(upperMiddleRightSixteenthRow) - mainStackView.addArrangedSubview(lowerMiddleLeftSixteenthRow) - mainStackView.addArrangedSubview(lowerMiddleCenterLeftSixteenthRow) - mainStackView.addArrangedSubview(lowerMiddleCenterRightSixteenthRow) - mainStackView.addArrangedSubview(lowerMiddleRightSixteenthRow) - mainStackView.addArrangedSubview(bottomLeftSixteenthRow) - mainStackView.addArrangedSubview(bottomCenterLeftSixteenthRow) - mainStackView.addArrangedSubview(bottomCenterRightSixteenthRow) - mainStackView.addArrangedSubview(bottomRightSixteenthRow) - - // Store row references for toggling via tag on cyclingCheckbox - cyclingCheckbox.tag = 0 - objc_setAssociatedObject(cyclingCheckbox, &SettingsViewController.individualRowsKey, individualGridRows, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(cyclingCheckbox, &SettingsViewController.cyclingRowsKey, cyclingGridRows, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(cyclingCheckbox, &SettingsViewController.cyclingHintKey, cyclingHintLabel, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) mainStackView.addArrangedSubview(splitRatioHeaderLabel) mainStackView.setCustomSpacing(10, after: splitRatioHeaderLabel) @@ -1227,36 +853,8 @@ class SettingsViewController: NSViewController { bottomLeftEighthLabel.widthAnchor.constraint(equalTo: bottomCenterLeftEighthLabel.widthAnchor), bottomCenterLeftEighthLabel.widthAnchor.constraint(equalTo: bottomCenterRightEighthLabel.widthAnchor), bottomCenterRightEighthLabel.widthAnchor.constraint(equalTo: bottomRightEighthLabel.widthAnchor), - bottomRightEighthLabel.widthAnchor.constraint(equalTo: topLeftTwelfthLabel.widthAnchor), - topLeftTwelfthLabel.widthAnchor.constraint(equalTo: topCenterLeftTwelfthLabel.widthAnchor), - topCenterLeftTwelfthLabel.widthAnchor.constraint(equalTo: topCenterRightTwelfthLabel.widthAnchor), - topCenterRightTwelfthLabel.widthAnchor.constraint(equalTo: topRightTwelfthLabel.widthAnchor), - topRightTwelfthLabel.widthAnchor.constraint(equalTo: middleLeftTwelfthLabel.widthAnchor), - middleLeftTwelfthLabel.widthAnchor.constraint(equalTo: middleCenterLeftTwelfthLabel.widthAnchor), - middleCenterLeftTwelfthLabel.widthAnchor.constraint(equalTo: middleCenterRightTwelfthLabel.widthAnchor), - middleCenterRightTwelfthLabel.widthAnchor.constraint(equalTo: middleRightTwelfthLabel.widthAnchor), - middleRightTwelfthLabel.widthAnchor.constraint(equalTo: bottomLeftTwelfthLabel.widthAnchor), - bottomLeftTwelfthLabel.widthAnchor.constraint(equalTo: bottomCenterLeftTwelfthLabel.widthAnchor), - bottomCenterLeftTwelfthLabel.widthAnchor.constraint(equalTo: bottomCenterRightTwelfthLabel.widthAnchor), - bottomCenterRightTwelfthLabel.widthAnchor.constraint(equalTo: bottomRightTwelfthLabel.widthAnchor), - bottomRightTwelfthLabel.widthAnchor.constraint(equalTo: topLeftSixteenthLabel.widthAnchor), - topLeftSixteenthLabel.widthAnchor.constraint(equalTo: topCenterLeftSixteenthLabel.widthAnchor), - topCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: topCenterRightSixteenthLabel.widthAnchor), - topCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: topRightSixteenthLabel.widthAnchor), - topRightSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleLeftSixteenthLabel.widthAnchor), - upperMiddleLeftSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleCenterLeftSixteenthLabel.widthAnchor), - upperMiddleCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleCenterRightSixteenthLabel.widthAnchor), - upperMiddleCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: upperMiddleRightSixteenthLabel.widthAnchor), - upperMiddleRightSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleLeftSixteenthLabel.widthAnchor), - lowerMiddleLeftSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleCenterLeftSixteenthLabel.widthAnchor), - lowerMiddleCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleCenterRightSixteenthLabel.widthAnchor), - lowerMiddleCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: lowerMiddleRightSixteenthLabel.widthAnchor), - lowerMiddleRightSixteenthLabel.widthAnchor.constraint(equalTo: bottomLeftSixteenthLabel.widthAnchor), - bottomLeftSixteenthLabel.widthAnchor.constraint(equalTo: bottomCenterLeftSixteenthLabel.widthAnchor), - bottomCenterLeftSixteenthLabel.widthAnchor.constraint(equalTo: bottomCenterRightSixteenthLabel.widthAnchor), - bottomCenterRightSixteenthLabel.widthAnchor.constraint(equalTo: bottomRightSixteenthLabel.widthAnchor), - bottomRightSixteenthLabel.widthAnchor.constraint(equalTo: eighthsCyclingLabel.widthAnchor), - eighthsCyclingLabel.widthAnchor.constraint(equalTo: twelfthsCyclingLabel.widthAnchor), + bottomRightEighthLabel.widthAnchor.constraint(equalTo: ninthsCyclingLabel.widthAnchor), + ninthsCyclingLabel.widthAnchor.constraint(equalTo: twelfthsCyclingLabel.widthAnchor), twelfthsCyclingLabel.widthAnchor.constraint(equalTo: sixteenthsCyclingLabel.widthAnchor), sixteenthsCyclingLabel.widthAnchor.constraint(equalTo: hSplitLabel.widthAnchor), hSplitLabel.widthAnchor.constraint(equalTo: vSplitLabel.widthAnchor), @@ -1277,42 +875,13 @@ class SettingsViewController: NSViewController { bottomCenterLeftEighthShortcutView.widthAnchor.constraint(equalToConstant: 160), bottomCenterRightEighthShortcutView.widthAnchor.constraint(equalToConstant: 160), bottomRightEighthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topCenterLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topCenterRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - middleLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - middleCenterLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - middleCenterRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - middleRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomCenterLeftTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomCenterRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomRightTwelfthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - topRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - upperMiddleLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - upperMiddleCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - upperMiddleCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - upperMiddleRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - lowerMiddleLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - lowerMiddleCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - lowerMiddleCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - lowerMiddleRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomCenterLeftSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomCenterRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - bottomRightSixteenthShortcutView.widthAnchor.constraint(equalToConstant: 160), - eighthsCyclingShortcutView.widthAnchor.constraint(equalToConstant: 160), + ninthsCyclingShortcutView.widthAnchor.constraint(equalToConstant: 160), twelfthsCyclingShortcutView.widthAnchor.constraint(equalToConstant: 160), sixteenthsCyclingShortcutView.widthAnchor.constraint(equalToConstant: 160), widthStepField.trailingAnchor.constraint(equalTo: largerWidthShortcutView.trailingAnchor), hSplitField.widthAnchor.constraint(equalToConstant: 160), vSplitField.widthAnchor.constraint(equalToConstant: 160), - showEighthsCheckbox.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - cyclingCheckbox.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + showAdditionalSizesCheckbox.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), smallerWidthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), topVerticalThirdShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), middleVerticalThirdShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), @@ -1327,35 +896,7 @@ class SettingsViewController: NSViewController { bottomCenterLeftEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), bottomCenterRightEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), bottomRightEighthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topCenterLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topCenterRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - middleLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - middleCenterLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - middleCenterRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - middleRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomCenterLeftTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomCenterRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomRightTwelfthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - topRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - upperMiddleLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - upperMiddleCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - upperMiddleCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - upperMiddleRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - lowerMiddleLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - lowerMiddleCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - lowerMiddleCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - lowerMiddleRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomCenterLeftSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomCenterRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - bottomRightSixteenthShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), - eighthsCyclingShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), + ninthsCyclingShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), twelfthsCyclingShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), sixteenthsCyclingShortcutView.leadingAnchor.constraint(equalTo: largerWidthShortcutView.leadingAnchor), gridHeaderLabel.widthAnchor.constraint(equalTo: mainStackView.widthAnchor), diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index ef1e863a..c5d97726 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -189,7 +189,7 @@ enum WindowAction: Int, Codable { // Determines where separators should be used in the menu var firstInGroup: Bool { switch self { - case .leftHalf, .topLeft, .firstThird, .maximize, .almostMaximize, .nextDisplay, .moveLeft, .firstFourth, .topLeftSixth, .topLeftEighth, .topLeftTwelfth, .topLeftSixteenth: + case .leftHalf, .topLeft, .firstThird, .maximize, .almostMaximize, .nextDisplay, .moveLeft, .firstFourth, .topLeftSixth, .topLeftEighth, .topLeftNinth, .topLeftTwelfth, .topLeftSixteenth: return true default: return false @@ -838,6 +838,7 @@ enum WindowAction: Int, Codable { case .firstFourth, .secondFourth, .thirdFourth, .lastFourth, .firstThreeFourths, .centerThreeFourths, .lastThreeFourths: return .fourths case .topLeftSixth, .topCenterSixth, .topRightSixth, .bottomLeftSixth, .bottomCenterSixth, .bottomRightSixth: return .sixths case .topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth, .bottomLeftEighth, .bottomCenterLeftEighth, .bottomCenterRightEighth, .bottomRightEighth: return .eighths + case .topLeftNinth, .topCenterNinth, .topRightNinth, .middleLeftNinth, .middleCenterNinth, .middleRightNinth, .bottomLeftNinth, .bottomCenterNinth, .bottomRightNinth: return .ninths case .topLeftTwelfth, .topCenterLeftTwelfth, .topCenterRightTwelfth, .topRightTwelfth, .middleLeftTwelfth, .middleCenterLeftTwelfth, .middleCenterRightTwelfth, .middleRightTwelfth, .bottomLeftTwelfth, .bottomCenterLeftTwelfth, .bottomCenterRightTwelfth, .bottomRightTwelfth: return .twelfths case .topLeftSixteenth, .topCenterLeftSixteenth, .topCenterRightSixteenth, .topRightSixteenth, .upperMiddleLeftSixteenth, .upperMiddleCenterLeftSixteenth, .upperMiddleCenterRightSixteenth, .upperMiddleRightSixteenth, .lowerMiddleLeftSixteenth, .lowerMiddleCenterLeftSixteenth, .lowerMiddleCenterRightSixteenth, .lowerMiddleRightSixteenth, .bottomLeftSixteenth, .bottomCenterLeftSixteenth, .bottomCenterRightSixteenth, .bottomRightSixteenth: return .sixteenths case .moveUp, .moveDown, .moveLeft, .moveRight: return .move diff --git a/Rectangle/WindowActionCategory.swift b/Rectangle/WindowActionCategory.swift index 9104f5f3..c9a58903 100644 --- a/Rectangle/WindowActionCategory.swift +++ b/Rectangle/WindowActionCategory.swift @@ -10,7 +10,7 @@ import Foundation enum WindowActionCategory { - case halves, corners, thirds, max, size, display, move, other, sixths, fourths, eighths, twelfths, sixteenths + case halves, corners, thirds, max, size, display, move, other, sixths, fourths, eighths, ninths, twelfths, sixteenths var displayName: String { switch self { @@ -36,6 +36,8 @@ enum WindowActionCategory { return NSLocalizedString("Sixths", tableName: "Main", value: "", comment: "") case .eighths: return NSLocalizedString("Eighths", tableName: "Main", value: "", comment: "") + case .ninths: + return NSLocalizedString("Ninths", tableName: "Main", value: "Ninths", comment: "") case .twelfths: return NSLocalizedString("Twelfths", tableName: "Main", value: "Twelfths", comment: "") case .sixteenths: From 1ba6f5d75277a89c5d585d6aa1db8af789c74734 Mon Sep 17 00:00:00 2001 From: Myron Koch <126993133+MyronKoch@users.noreply.github.com> Date: Tue, 17 Mar 2026 01:28:46 -0400 Subject: [PATCH 08/10] Add position cycling for quarter-screen actions Quarter actions (Top Left, Top Right, Bottom Left, Bottom Right) now cycle through all four corner positions on repeated presses, matching the pattern used by sixths, eighths, ninths, twelfths, and sixteenths. Adds QuartersRepeated protocol following the same design as SixthsRepeated and other *Repeated protocols. --- Rectangle.xcodeproj/project.pbxproj | 4 ++ Rectangle/WindowAction.swift | 11 ++++- .../LowerLeftCalculation.swift | 33 +++++++++---- .../LowerRightCalculation.swift | 33 +++++++++---- .../WindowCalculation/QuartersRepeated.swift | 47 +++++++++++++++++++ .../UpperLeftCalculation.swift | 33 +++++++++---- .../UpperRightCalculation.swift | 33 ++++++++++--- 7 files changed, 161 insertions(+), 33 deletions(-) create mode 100644 Rectangle/WindowCalculation/QuartersRepeated.swift diff --git a/Rectangle.xcodeproj/project.pbxproj b/Rectangle.xcodeproj/project.pbxproj index e201f98c..d29a7ce9 100644 --- a/Rectangle.xcodeproj/project.pbxproj +++ b/Rectangle.xcodeproj/project.pbxproj @@ -134,6 +134,7 @@ 98A009BD251253A000CFBF0C /* BottomCenterSixthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98A009BC251253A000CFBF0C /* BottomCenterSixthCalculation.swift */; }; 98A009BF251253AB00CFBF0C /* BottomRightSixthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98A009BE251253AB00CFBF0C /* BottomRightSixthCalculation.swift */; }; 98A6EDDD251F3F4A00F74B10 /* SixthsRepeated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98A6EDDC251F3F4A00F74B10 /* SixthsRepeated.swift */; }; + BB0000000000000000000001 /* QuartersRepeated.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB0000000000000000000002 /* QuartersRepeated.swift */; }; 98A6EDEC2528FFC100F74B10 /* WindowActionCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98A6EDEB2528FFC100F74B10 /* WindowActionCategory.swift */; }; 98B3559823CE025700E410E0 /* CenteringFixedSizedWindowMover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98B3559723CE025700E410E0 /* CenteringFixedSizedWindowMover.swift */; }; 98BEFA482620DEDD00D9D54F /* NSImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98BEFA472620DEDC00D9D54F /* NSImageExtension.swift */; }; @@ -349,6 +350,7 @@ 98A009BC251253A000CFBF0C /* BottomCenterSixthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomCenterSixthCalculation.swift; sourceTree = ""; }; 98A009BE251253AB00CFBF0C /* BottomRightSixthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomRightSixthCalculation.swift; sourceTree = ""; }; 98A6EDDC251F3F4A00F74B10 /* SixthsRepeated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SixthsRepeated.swift; sourceTree = ""; }; + BB0000000000000000000002 /* QuartersRepeated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuartersRepeated.swift; sourceTree = ""; }; 98A6EDEB2528FFC100F74B10 /* WindowActionCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowActionCategory.swift; sourceTree = ""; }; 98B3559723CE025700E410E0 /* CenteringFixedSizedWindowMover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenteringFixedSizedWindowMover.swift; sourceTree = ""; }; 98BEFA472620DEDC00D9D54F /* NSImageExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImageExtension.swift; sourceTree = ""; }; @@ -526,6 +528,7 @@ 98C6DEEF23CE191700CC0C1E /* GapCalculation.swift */, 9851A5C2251BEBA300ECF78C /* OrientationAware.swift */, 98A6EDDC251F3F4A00F74B10 /* SixthsRepeated.swift */, + BB0000000000000000000002 /* QuartersRepeated.swift */, 866661F1257D248A00A9CD2D /* RepeatedExecutionsInThirdsCalculation.swift */, 30166BCF24F27D6A00A38608 /* SpecifiedCalculation.swift */, D04CE31127817C9B00BD47B3 /* NinthsRepeated.swift */, @@ -1081,6 +1084,7 @@ BB0B804D2EFB0AF900A9B165 /* TopVerticalThirdCalculation.swift in Sources */, 6490B39D27BF984D0056C220 /* BottomCenterLeftEighthCalculation.swift in Sources */, 98A6EDDD251F3F4A00F74B10 /* SixthsRepeated.swift in Sources */, + BB0000000000000000000001 /* QuartersRepeated.swift in Sources */, AAADE1AF28CBAB0000036331 /* WindowUtil.swift in Sources */, 98A009B72512538200CFBF0C /* TopCenterSixthCalculation.swift in Sources */, AA69F8402992DCB1001A81AF /* LeftTodoCalculation.swift in Sources */, diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index c5d97726..3728fd2f 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -931,7 +931,12 @@ enum SubWindowAction { topRightThird, bottomLeftThird, bottomRightThird, - + + topLeftQuarter, + topRightQuarter, + bottomLeftQuarter, + bottomRightQuarter, + topLeftEighth, topCenterLeftEighth, topCenterRightEighth, @@ -1037,6 +1042,10 @@ enum SubWindowAction { case .topRightThird: return [.left, .bottom] case .bottomLeftThird: return [.right, .top] case .bottomRightThird: return [.left, .top] + case .topLeftQuarter: return [.right, .bottom] + case .topRightQuarter: return [.left, .bottom] + case .bottomLeftQuarter: return [.right, .top] + case .bottomRightQuarter: return [.left, .top] case .topLeftEighth: return [.right, .bottom] case .topCenterLeftEighth: return [.right, .left, .bottom] case .topCenterRightEighth: return [.right, .left, .bottom] diff --git a/Rectangle/WindowCalculation/LowerLeftCalculation.swift b/Rectangle/WindowCalculation/LowerLeftCalculation.swift index 323593ac..7a7d6ab4 100644 --- a/Rectangle/WindowCalculation/LowerLeftCalculation.swift +++ b/Rectangle/WindowCalculation/LowerLeftCalculation.swift @@ -8,26 +8,41 @@ import Foundation -class LowerLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { +class LowerLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation, QuartersRepeated { override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { - return calculateFirstRect(params) + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return quarterRect(params.visibleFrameOfScreen) } - - return calculateRepeatedRect(params) + + if last.action != .bottomLeft && lastSubAction != .bottomLeftQuarter { + return quarterRect(params.visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + + return quarterRect(params.visibleFrameOfScreen) } - + + func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 2.0) + rect.size.height = floor(visibleFrameOfScreen.height / 2.0) + return RectResult(rect, subAction: .bottomLeftQuarter) + } + func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { let visibleFrameOfScreen = params.visibleFrameOfScreen var rect = visibleFrameOfScreen - rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) - rect.size.height = floor(visibleFrameOfScreen.height / 2.0) - return RectResult(rect) } } diff --git a/Rectangle/WindowCalculation/LowerRightCalculation.swift b/Rectangle/WindowCalculation/LowerRightCalculation.swift index 30f64f11..6b10bc9f 100644 --- a/Rectangle/WindowCalculation/LowerRightCalculation.swift +++ b/Rectangle/WindowCalculation/LowerRightCalculation.swift @@ -8,26 +8,43 @@ import Foundation -class LowerRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { +class LowerRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation, QuartersRepeated { override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { - return calculateFirstRect(params) + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return quarterRect(params.visibleFrameOfScreen) } - - return calculateRepeatedRect(params) + + if last.action != .bottomRight && lastSubAction != .bottomRightQuarter { + return quarterRect(params.visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + + return quarterRect(params.visibleFrameOfScreen) } - + + func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 2.0) + rect.origin.x = visibleFrameOfScreen.maxX - rect.width + rect.size.height = floor(visibleFrameOfScreen.height / 2.0) + return RectResult(rect, subAction: .bottomRightQuarter) + } + func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { let visibleFrameOfScreen = params.visibleFrameOfScreen var rect = visibleFrameOfScreen - rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) rect.origin.x = visibleFrameOfScreen.maxX - rect.width rect.size.height = floor(visibleFrameOfScreen.height / 2.0) - return RectResult(rect) } } diff --git a/Rectangle/WindowCalculation/QuartersRepeated.swift b/Rectangle/WindowCalculation/QuartersRepeated.swift new file mode 100644 index 00000000..2a971ecd --- /dev/null +++ b/Rectangle/WindowCalculation/QuartersRepeated.swift @@ -0,0 +1,47 @@ +// +// QuartersRepeated.swift +// Rectangle +// +// Copyright © 2026 Ryan Hanson. All rights reserved. +// + +import Foundation + +protocol QuartersRepeated { + func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? +} + +extension QuartersRepeated { + func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? { + + if direction == .left { + switch subAction { + case .topLeftQuarter: + return WindowCalculationFactory.lowerRightCalculation.quarterRect + case .topRightQuarter: + return WindowCalculationFactory.upperLeftCalculation.quarterRect + case .bottomLeftQuarter: + return WindowCalculationFactory.upperRightCalculation.quarterRect + case .bottomRightQuarter: + return WindowCalculationFactory.lowerLeftCalculation.quarterRect + default: break + } + } + + else if direction == .right { + switch subAction { + case .topLeftQuarter: + return WindowCalculationFactory.upperRightCalculation.quarterRect + case .topRightQuarter: + return WindowCalculationFactory.lowerLeftCalculation.quarterRect + case .bottomLeftQuarter: + return WindowCalculationFactory.lowerRightCalculation.quarterRect + case .bottomRightQuarter: + return WindowCalculationFactory.upperLeftCalculation.quarterRect + default: break + } + } + + return nil + } +} diff --git a/Rectangle/WindowCalculation/UpperLeftCalculation.swift b/Rectangle/WindowCalculation/UpperLeftCalculation.swift index a3a5d2df..95b50022 100644 --- a/Rectangle/WindowCalculation/UpperLeftCalculation.swift +++ b/Rectangle/WindowCalculation/UpperLeftCalculation.swift @@ -8,24 +8,41 @@ import Foundation -class UpperLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { +class UpperLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation, QuartersRepeated { override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { - return calculateFirstRect(params) + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return quarterRect(params.visibleFrameOfScreen) } - - return calculateRepeatedRect(params) + + if last.action != .topLeft && lastSubAction != .topLeftQuarter { + return quarterRect(params.visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + + return quarterRect(params.visibleFrameOfScreen) } - + + func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 2.0) + rect.size.height = floor(visibleFrameOfScreen.height / 2.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + return RectResult(rect, subAction: .topLeftQuarter) + } + func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { let visibleFrameOfScreen = params.visibleFrameOfScreen var rect = visibleFrameOfScreen - rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) - rect.size.height = floor(visibleFrameOfScreen.height / 2.0) rect.origin.y = visibleFrameOfScreen.maxY - rect.height return RectResult(rect) diff --git a/Rectangle/WindowCalculation/UpperRightCalculation.swift b/Rectangle/WindowCalculation/UpperRightCalculation.swift index 6c14f2d2..d1fb8f21 100644 --- a/Rectangle/WindowCalculation/UpperRightCalculation.swift +++ b/Rectangle/WindowCalculation/UpperRightCalculation.swift @@ -8,22 +8,41 @@ import Foundation -class UpperRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { +class UpperRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation, QuartersRepeated { override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { - return calculateFirstRect(params) + guard Defaults.subsequentExecutionMode.value != .none, + let last = params.lastAction, + let lastSubAction = last.subAction + else { + return quarterRect(params.visibleFrameOfScreen) } - - return calculateRepeatedRect(params) + + if last.action != .topRight && lastSubAction != .topRightQuarter { + return quarterRect(params.visibleFrameOfScreen) + } + + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + + return quarterRect(params.visibleFrameOfScreen) + } + + func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.size.width = floor(visibleFrameOfScreen.width / 2.0) + rect.origin.x = visibleFrameOfScreen.maxX - rect.width + rect.size.height = floor(visibleFrameOfScreen.height / 2.0) + rect.origin.y = visibleFrameOfScreen.maxY - rect.height + return RectResult(rect, subAction: .topRightQuarter) } - + func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { let visibleFrameOfScreen = params.visibleFrameOfScreen var rect = visibleFrameOfScreen - rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) rect.origin.x = visibleFrameOfScreen.maxX - rect.width rect.size.height = floor(visibleFrameOfScreen.height / 2.0) From 43a538185a3e2bf958a4882ca550e59038b9212d Mon Sep 17 00:00:00 2001 From: Myron Koch <126993133+MyronKoch@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:16:49 -0400 Subject: [PATCH 09/10] Address maintainer feedback: preserve original menu, add ninths, new cycling mode - Keep original menu when "Show additional sizes" is off (Thirds/Size as flat items) - Live menu rebuild on checkbox toggle (no restart needed) - Migrate old "showEighthsInMenu" default to new setting - New subsequentExecutionMode (resizeAndCycleQuadrants) for quarter position cycling so existing modes are unchanged for long-time users - Add ninth (3x3) grid display names, image assets, and icons in popover - Move eighths into Grid Positions section, checkbox at top - Order menu submenus numerically: Sixths, Eighths, Ninths, Twelfths, Sixteenths --- Rectangle/AppDelegate.swift | 57 ++++++++++++++---- .../Contents.json | 21 +++++++ .../bottomCenterNinthTemplate.png | Bin 0 -> 130 bytes .../Contents.json | 21 +++++++ .../bottomLeftNinthTemplate.png | Bin 0 -> 132 bytes .../Contents.json | 21 +++++++ .../bottomRightNinthTemplate.png | Bin 0 -> 128 bytes .../Contents.json | 21 +++++++ .../middleCenterNinthTemplate.png | Bin 0 -> 135 bytes .../Contents.json | 21 +++++++ .../middleLeftNinthTemplate.png | Bin 0 -> 131 bytes .../Contents.json | 21 +++++++ .../middleRightNinthTemplate.png | Bin 0 -> 130 bytes .../Contents.json | 21 +++++++ .../topCenterNinthTemplate.png | Bin 0 -> 135 bytes .../Contents.json | 21 +++++++ .../topLeftNinthTemplate.png | Bin 0 -> 127 bytes .../Contents.json | 21 +++++++ .../topRightNinthTemplate.png | Bin 0 -> 129 bytes Rectangle/Base.lproj/Main.storyboard | 1 + .../PrefsWindow/SettingsViewController.swift | 27 +++++---- Rectangle/SubsequentExecutionMode.swift | 10 ++- .../Utilities/NotificationExtension.swift | 1 + Rectangle/WindowAction.swift | 53 +++++++++++----- .../LeftRightHalfCalculation.swift | 2 +- .../LowerLeftCalculation.swift | 22 +++---- .../LowerRightCalculation.swift | 22 +++---- .../UpperLeftCalculation.swift | 22 +++---- .../UpperRightCalculation.swift | 22 +++---- 29 files changed, 344 insertions(+), 84 deletions(-) create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomCenterNinthTemplate.imageset/bottomCenterNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomLeftNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomLeftNinthTemplate.imageset/bottomLeftNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomRightNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/bottomRightNinthTemplate.imageset/bottomRightNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleCenterNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleCenterNinthTemplate.imageset/middleCenterNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleLeftNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleLeftNinthTemplate.imageset/middleLeftNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleRightNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/middleRightNinthTemplate.imageset/middleRightNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topCenterNinthTemplate.imageset/topCenterNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topLeftNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topLeftNinthTemplate.imageset/topLeftNinthTemplate.png create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topRightNinthTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/topRightNinthTemplate.imageset/topRightNinthTemplate.png diff --git a/Rectangle/AppDelegate.swift b/Rectangle/AppDelegate.swift index f8bc4b0e..82e67063 100644 --- a/Rectangle/AppDelegate.swift +++ b/Rectangle/AppDelegate.swift @@ -37,7 +37,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var prevActiveAppObservation: NSKeyValueObservation? private var prevActiveApp: NSRunningApplication? - + private var additionalSizeMenuItems: [NSMenuItem] = [] + private var dynamicMenuItemCount: Int = 0 + @IBOutlet weak var mainStatusMenu: NSMenu! @IBOutlet weak var unauthorizedMenu: NSMenu! @IBOutlet weak var ignoreMenuItem: NSMenuItem! @@ -51,6 +53,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { Defaults.loadFromSupportDir() + migrateShowEighthsInMenu() checkVersion() mainStatusMenu.delegate = self @@ -75,7 +78,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { mainStatusMenu.autoenablesItems = false addWindowActionMenuItems() - + + NotificationCenter.default.addObserver(self, selector: #selector(rebuildMenu), name: .showAdditionalSizesInMenuChanged, object: nil) + updaterController = SPUStandardUpdaterController(updaterDelegate: nil, userDriverDelegate: self) checkAutoCheckForUpdates() @@ -378,6 +383,8 @@ extension AppDelegate: NSMenuDelegate { func addWindowActionMenuItems() { let additionalSizeCategories: Set = [.eighths, .ninths, .twelfths, .sixteenths] + let submenuOnlyWhenAdditional: Set = [.thirds, .size] + let showAdditional = Defaults.showAdditionalSizesInMenu.userEnabled var menuIndex = 0 var categoryMenus: [CategoryMenu] = [] for action in WindowAction.active { @@ -386,16 +393,23 @@ extension AppDelegate: NSMenuDelegate { newMenuItem.representedObject = action if !Defaults.showAllActionsInMenu.userEnabled, let category = action.category { - if menuIndex != 0 && action.firstInGroup { - let menu = NSMenu(title: category.displayName) - menu.autoenablesItems = false - categoryMenus.append(CategoryMenu(menu: menu, category: category)) + // When additional sizes are off, keep Thirds and Size as flat items + if submenuOnlyWhenAdditional.contains(category) && !showAdditional { + // Fall through to flat item handling below + } else { + if menuIndex != 0 && action.firstInGroup { + let menu = NSMenu(title: category.displayName) + menu.autoenablesItems = false + categoryMenus.append(CategoryMenu(menu: menu, category: category)) + } + categoryMenus.last?.menu.addItem(newMenuItem) + continue } - categoryMenus.last?.menu.addItem(newMenuItem) - continue } - if menuIndex != 0 && action.firstInGroup { + // Flat item - suppress extra separator for almostMaximize when Size is not a submenu + let showSeparator = action.firstInGroup && !(action == .almostMaximize && !showAdditional) + if menuIndex != 0 && showSeparator { mainStatusMenu.insertItem(NSMenuItem.separator(), at: menuIndex) menuIndex += 1 } @@ -412,6 +426,7 @@ extension AppDelegate: NSMenuDelegate { let menuMenuItem = NSMenuItem(title: categoryMenu.category.displayName, action: nil, keyEquivalent: "") if additionalSizeCategories.contains(categoryMenu.category) { menuMenuItem.isHidden = !Defaults.showAdditionalSizesInMenu.userEnabled + additionalSizeMenuItems.append(menuMenuItem) } mainStatusMenu.insertItem(menuMenuItem, at: menuIndex) mainStatusMenu.setSubmenu(categoryMenu.menu, for: menuMenuItem) @@ -423,13 +438,33 @@ extension AppDelegate: NSMenuDelegate { menuIndex += 1 addTodoModeMenuItems(startingIndex: menuIndex) + // Track total dynamic items: window actions + separators + todo items (4 items + 1 separator) + dynamicMenuItemCount = menuIndex + 5 } - + + @objc func rebuildMenu() { + // Remove all dynamically added items + for _ in 0..0<}eT87#`ESkz`#nXX?GI+ZBxvXFVdQ&MBb@03q8eu>b%7 literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomRightNinthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/bottomRightNinthTemplate.imageset/Contents.json new file mode 100644 index 00000000..bec668ce --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/bottomRightNinthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bottomRightNinthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/bottomRightNinthTemplate.imageset/bottomRightNinthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/bottomRightNinthTemplate.imageset/bottomRightNinthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..0ce6c354b502a429866c222b382d7915e0a3fe9c GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq})AS978JRB&Q@K{5WsG(7@`o z{PN9vGktiNS!THDE)!2!G}|{L;Y!jjMzvp&rk8Zn7BQT8fd^<0l8)w;SXyC!^1&?`}N5z_gn*}=W-R$151 k&5@ny0{c5vUw?hMcsk3Z$|=`kfhIF}y85}Sb4q9e0IQoXfB*mh literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleLeftNinthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/middleLeftNinthTemplate.imageset/Contents.json new file mode 100644 index 00000000..9c27a94b --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/middleLeftNinthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "middleLeftNinthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleLeftNinthTemplate.imageset/middleLeftNinthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/middleLeftNinthTemplate.imageset/middleLeftNinthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..1ea61721c85032d1dc707720fb906764b067b740 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq`W*`978JRB&Q@K{5WsG(7@`o z{PN9vGktiNS!THD%6)S@%d|9IXyLR~n)5qnwe?2z@EaKRTnU-$m)||}MXn-GPn6A{ f=2?XcX4x@ZU(Yhh(Qy4%ps5U=u6{1-oD!M<2y-mx literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleRightNinthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/middleRightNinthTemplate.imageset/Contents.json new file mode 100644 index 00000000..beb8fe0d --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/middleRightNinthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "middleRightNinthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/middleRightNinthTemplate.imageset/middleRightNinthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/middleRightNinthTemplate.imageset/middleRightNinthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..9bb5ae4a1dd1cf5242ac749738ef0b06f23daa8e GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq&z)c978JRB&Q@K{5WsG(7@`o z{PN9vGktiNS!THD%6)S@>%?rFF0{C#GqHi;@`|Hhn0$B+YlJQrUYPbQW|`rpyG%S`(QMz0 jgeytA7>_k({bFF4?$0tI?^#X(&}0TrS3j3^P6x>^cjl) bUwMY>511!J@0uA8G?Bs6)z4*}Q$iB}Nc<}C literal 0 HcmV?d00001 diff --git a/Rectangle/Assets.xcassets/WindowPositions/topRightNinthTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/topRightNinthTemplate.imageset/Contents.json new file mode 100644 index 00000000..1947335c --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/topRightNinthTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "topRightNinthTemplate.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/topRightNinthTemplate.imageset/topRightNinthTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/topRightNinthTemplate.imageset/topRightNinthTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..04cc3ac35f3169192d71110120fd62b8fa3cada9 GIT binary patch literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^azHG?!3HE}t?oSnq&z%b978JRB&Q@K{5WsG(7@`o zJow_7nMX8|k|Y!km}Gt2z@H^1HN(?~Z>Hs}9WU5syhs<-lGr*Uuc5OpY++Mi_e(Yj d_Ej&L864lTXnypZdm3magQu&X%Q~loCIE<|D)ayV literal 0 HcmV?d00001 diff --git a/Rectangle/Base.lproj/Main.storyboard b/Rectangle/Base.lproj/Main.storyboard index 64b1510b..eb104c79 100644 --- a/Rectangle/Base.lproj/Main.storyboard +++ b/Rectangle/Base.lproj/Main.storyboard @@ -2814,6 +2814,7 @@ + diff --git a/Rectangle/PrefsWindow/SettingsViewController.swift b/Rectangle/PrefsWindow/SettingsViewController.swift index cb53bfa5..f89ff7aa 100644 --- a/Rectangle/PrefsWindow/SettingsViewController.swift +++ b/Rectangle/PrefsWindow/SettingsViewController.swift @@ -110,6 +110,7 @@ class SettingsViewController: NSViewController { @objc func toggleShowAdditionalSizesInMenu(_ sender: NSButton) { let enabled: Bool = sender.state == .on Defaults.showAdditionalSizesInMenu.enabled = enabled + Notification.Name.showAdditionalSizesInMenuChanged.post() } @IBAction func checkForUpdates(_ sender: Any) { @@ -731,24 +732,14 @@ class SettingsViewController: NSViewController { mainStackView.addArrangedSubview(bottomVerticalThirdRow) mainStackView.addArrangedSubview(topVerticalTwoThirdsRow) mainStackView.addArrangedSubview(bottomVerticalTwoThirdsRow) - mainStackView.addArrangedSubview(topLeftEighthRow) - mainStackView.addArrangedSubview(topCenterLeftEighthRow) - mainStackView.addArrangedSubview(topCenterRightEighthRow) - mainStackView.addArrangedSubview(topRightEighthRow) - mainStackView.addArrangedSubview(bottomLeftEighthRow) - mainStackView.addArrangedSubview(bottomCenterLeftEighthRow) - mainStackView.addArrangedSubview(bottomCenterRightEighthRow) - mainStackView.addArrangedSubview(bottomRightEighthRow) - + // Grid Positions - cycling shortcuts for larger grids let showAdditionalSizesCheckbox = NSButton(checkboxWithTitle: NSLocalizedString("Show additional sizes in menu", tableName: "Main", value: "", comment: ""), target: self, action: #selector(toggleShowAdditionalSizesInMenu(_:))) showAdditionalSizesCheckbox.state = Defaults.showAdditionalSizesInMenu.userEnabled ? .on : .off showAdditionalSizesCheckbox.translatesAutoresizingMaskIntoConstraints = false - showAdditionalSizesCheckbox.alignment = .right + showAdditionalSizesCheckbox.alignment = .left showAdditionalSizesCheckbox.imageHugsTitle = true - mainStackView.addArrangedSubview(showAdditionalSizesCheckbox) - - // Grid Positions - cycling shortcuts for larger grids + // let gridHeaderLabel = NSTextField(labelWithString: NSLocalizedString("Grid Positions", tableName: "Main", value: "", comment: "")) gridHeaderLabel.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) gridHeaderLabel.alignment = .center @@ -823,8 +814,18 @@ class SettingsViewController: NSViewController { mainStackView.addArrangedSubview(gridHeaderLabel) mainStackView.setCustomSpacing(4, after: gridHeaderLabel) + mainStackView.addArrangedSubview(showAdditionalSizesCheckbox) + mainStackView.setCustomSpacing(8, after: showAdditionalSizesCheckbox) mainStackView.addArrangedSubview(cyclingHintLabel) mainStackView.setCustomSpacing(8, after: cyclingHintLabel) + mainStackView.addArrangedSubview(topLeftEighthRow) + mainStackView.addArrangedSubview(topCenterLeftEighthRow) + mainStackView.addArrangedSubview(topCenterRightEighthRow) + mainStackView.addArrangedSubview(topRightEighthRow) + mainStackView.addArrangedSubview(bottomLeftEighthRow) + mainStackView.addArrangedSubview(bottomCenterLeftEighthRow) + mainStackView.addArrangedSubview(bottomCenterRightEighthRow) + mainStackView.addArrangedSubview(bottomRightEighthRow) mainStackView.addArrangedSubview(ninthsCyclingRow) mainStackView.addArrangedSubview(twelfthsCyclingRow) mainStackView.addArrangedSubview(sixteenthsCyclingRow) diff --git a/Rectangle/SubsequentExecutionMode.swift b/Rectangle/SubsequentExecutionMode.swift index 7070c95c..1a96ddb1 100644 --- a/Rectangle/SubsequentExecutionMode.swift +++ b/Rectangle/SubsequentExecutionMode.swift @@ -14,6 +14,7 @@ enum SubsequentExecutionMode: Int { case none = 2 case acrossAndResize = 3 // across monitor for right/left, spectacle resize for all else case cycleMonitor = 4 + case resizeAndCycleQuadrants = 5 } class SubsequentExecutionDefault: Default { @@ -36,7 +37,14 @@ class SubsequentExecutionDefault: Default { var resizes: Bool { switch value { - case .resize, .acrossAndResize: return true + case .resize, .acrossAndResize, .resizeAndCycleQuadrants: return true + default: return false + } + } + + var cyclesQuadrantPositions: Bool { + switch value { + case .resizeAndCycleQuadrants: return true default: return false } } diff --git a/Rectangle/Utilities/NotificationExtension.swift b/Rectangle/Utilities/NotificationExtension.swift index 79319c57..3b15a77c 100644 --- a/Rectangle/Utilities/NotificationExtension.swift +++ b/Rectangle/Utilities/NotificationExtension.swift @@ -22,6 +22,7 @@ extension Notification.Name { static let windowTitleBar = Notification.Name("windowTitleBar") static let defaultSnapAreas = Notification.Name("defaultSnapAreas") static let updateAvailability = Notification.Name("updateAvailability") + static let showAdditionalSizesInMenuChanged = Notification.Name("showAdditionalSizesInMenuChanged") func post( center: NotificationCenter = NotificationCenter.default, diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index 3728fd2f..d2a5d62b 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -146,12 +146,12 @@ enum WindowAction: Int, Codable { firstFourth, secondFourth, thirdFourth, lastFourth, firstThreeFourths, centerThreeFourths, lastThreeFourths, topLeftSixth, topCenterSixth, topRightSixth, bottomLeftSixth, bottomCenterSixth, bottomRightSixth, specified, reverseAll, - topLeftNinth, topCenterNinth, topRightNinth, - middleLeftNinth, middleCenterNinth, middleRightNinth, - bottomLeftNinth, bottomCenterNinth, bottomRightNinth, topLeftThird, topRightThird, bottomLeftThird, bottomRightThird, topLeftEighth, topCenterLeftEighth, topCenterRightEighth, topRightEighth, bottomLeftEighth, bottomCenterLeftEighth, bottomCenterRightEighth, bottomRightEighth, + topLeftNinth, topCenterNinth, topRightNinth, + middleLeftNinth, middleCenterNinth, middleRightNinth, + bottomLeftNinth, bottomCenterNinth, bottomRightNinth, topLeftTwelfth, topCenterLeftTwelfth, topCenterRightTwelfth, topRightTwelfth, middleLeftTwelfth, middleCenterLeftTwelfth, middleCenterRightTwelfth, middleRightTwelfth, bottomLeftTwelfth, bottomCenterLeftTwelfth, bottomCenterRightTwelfth, bottomRightTwelfth, @@ -445,8 +445,33 @@ enum WindowAction: Int, Codable { case .bottomRightSixth: key = "m2F-eA-g7w.title" value = "Bottom Right Sixth" - case .topLeftNinth, .topCenterNinth, .topRightNinth, .middleLeftNinth, .middleCenterNinth, .middleRightNinth, .bottomLeftNinth, .bottomCenterNinth, .bottomRightNinth: - return nil + case .topLeftNinth: + key = "topLeftNinth.title" + value = "Top Left Ninth" + case .topCenterNinth: + key = "topCenterNinth.title" + value = "Top Center Ninth" + case .topRightNinth: + key = "topRightNinth.title" + value = "Top Right Ninth" + case .middleLeftNinth: + key = "middleLeftNinth.title" + value = "Middle Left Ninth" + case .middleCenterNinth: + key = "middleCenterNinth.title" + value = "Middle Center Ninth" + case .middleRightNinth: + key = "middleRightNinth.title" + value = "Middle Right Ninth" + case .bottomLeftNinth: + key = "bottomLeftNinth.title" + value = "Bottom Left Ninth" + case .bottomCenterNinth: + key = "bottomCenterNinth.title" + value = "Bottom Center Ninth" + case .bottomRightNinth: + key = "bottomRightNinth.title" + value = "Bottom Right Ninth" case .topLeftThird, .topRightThird, .bottomLeftThird, .bottomRightThird: return nil case .topLeftEighth: @@ -703,15 +728,15 @@ enum WindowAction: Int, Codable { case .bottomLeftSixth: return NSImage(imageLiteralResourceName: "bottomLeftSixthTemplate") case .bottomCenterSixth: return NSImage(imageLiteralResourceName: "bottomCenterSixthTemplate") case .bottomRightSixth: return NSImage(imageLiteralResourceName: "bottomRightSixthTemplate") - case .topLeftNinth: return NSImage() - case .topCenterNinth: return NSImage() - case .topRightNinth: return NSImage() - case .middleLeftNinth: return NSImage() - case .middleCenterNinth: return NSImage() - case .middleRightNinth: return NSImage() - case .bottomLeftNinth: return NSImage() - case .bottomCenterNinth: return NSImage() - case .bottomRightNinth: return NSImage() + case .topLeftNinth: return NSImage(imageLiteralResourceName: "topLeftNinthTemplate") + case .topCenterNinth: return NSImage(imageLiteralResourceName: "topCenterNinthTemplate") + case .topRightNinth: return NSImage(imageLiteralResourceName: "topRightNinthTemplate") + case .middleLeftNinth: return NSImage(imageLiteralResourceName: "middleLeftNinthTemplate") + case .middleCenterNinth: return NSImage(imageLiteralResourceName: "middleCenterNinthTemplate") + case .middleRightNinth: return NSImage(imageLiteralResourceName: "middleRightNinthTemplate") + case .bottomLeftNinth: return NSImage(imageLiteralResourceName: "bottomLeftNinthTemplate") + case .bottomCenterNinth: return NSImage(imageLiteralResourceName: "bottomCenterNinthTemplate") + case .bottomRightNinth: return NSImage(imageLiteralResourceName: "bottomRightNinthTemplate") case .topLeftThird: return NSImage() case .topRightThird: return NSImage() case .bottomLeftThird: return NSImage() diff --git a/Rectangle/WindowCalculation/LeftRightHalfCalculation.swift b/Rectangle/WindowCalculation/LeftRightHalfCalculation.swift index b75f3eff..049d2926 100644 --- a/Rectangle/WindowCalculation/LeftRightHalfCalculation.swift +++ b/Rectangle/WindowCalculation/LeftRightHalfCalculation.swift @@ -23,7 +23,7 @@ class LeftRightHalfCalculation: WindowCalculation, RepeatedExecutionsInThirdsCal return calculateResize(params) } return calculateAcrossDisplays(params) - case .resize: + case .resize, .resizeAndCycleQuadrants: return calculateResize(params) case .none, .cycleMonitor: let screen = usableScreens.currentScreen diff --git a/Rectangle/WindowCalculation/LowerLeftCalculation.swift b/Rectangle/WindowCalculation/LowerLeftCalculation.swift index 7a7d6ab4..4b52c573 100644 --- a/Rectangle/WindowCalculation/LowerLeftCalculation.swift +++ b/Rectangle/WindowCalculation/LowerLeftCalculation.swift @@ -12,22 +12,22 @@ class LowerLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalcula override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - guard Defaults.subsequentExecutionMode.value != .none, - let last = params.lastAction, - let lastSubAction = last.subAction - else { + if Defaults.subsequentExecutionMode.cyclesQuadrantPositions { + if let last = params.lastAction, + let lastSubAction = last.subAction, + last.action == .bottomLeft || lastSubAction == .bottomLeftQuarter { + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + } return quarterRect(params.visibleFrameOfScreen) } - if last.action != .bottomLeft && lastSubAction != .bottomLeftQuarter { - return quarterRect(params.visibleFrameOfScreen) - } - - if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { - return calculation(params.visibleFrameOfScreen) + if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { + return calculateFirstRect(params) } - return quarterRect(params.visibleFrameOfScreen) + return calculateRepeatedRect(params) } func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { diff --git a/Rectangle/WindowCalculation/LowerRightCalculation.swift b/Rectangle/WindowCalculation/LowerRightCalculation.swift index 6b10bc9f..257cb1fe 100644 --- a/Rectangle/WindowCalculation/LowerRightCalculation.swift +++ b/Rectangle/WindowCalculation/LowerRightCalculation.swift @@ -12,22 +12,22 @@ class LowerRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalcul override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - guard Defaults.subsequentExecutionMode.value != .none, - let last = params.lastAction, - let lastSubAction = last.subAction - else { + if Defaults.subsequentExecutionMode.cyclesQuadrantPositions { + if let last = params.lastAction, + let lastSubAction = last.subAction, + last.action == .bottomRight || lastSubAction == .bottomRightQuarter { + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + } return quarterRect(params.visibleFrameOfScreen) } - if last.action != .bottomRight && lastSubAction != .bottomRightQuarter { - return quarterRect(params.visibleFrameOfScreen) - } - - if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { - return calculation(params.visibleFrameOfScreen) + if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { + return calculateFirstRect(params) } - return quarterRect(params.visibleFrameOfScreen) + return calculateRepeatedRect(params) } func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { diff --git a/Rectangle/WindowCalculation/UpperLeftCalculation.swift b/Rectangle/WindowCalculation/UpperLeftCalculation.swift index 95b50022..ffd2da62 100644 --- a/Rectangle/WindowCalculation/UpperLeftCalculation.swift +++ b/Rectangle/WindowCalculation/UpperLeftCalculation.swift @@ -12,22 +12,22 @@ class UpperLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalcula override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - guard Defaults.subsequentExecutionMode.value != .none, - let last = params.lastAction, - let lastSubAction = last.subAction - else { + if Defaults.subsequentExecutionMode.cyclesQuadrantPositions { + if let last = params.lastAction, + let lastSubAction = last.subAction, + last.action == .topLeft || lastSubAction == .topLeftQuarter { + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + } return quarterRect(params.visibleFrameOfScreen) } - if last.action != .topLeft && lastSubAction != .topLeftQuarter { - return quarterRect(params.visibleFrameOfScreen) - } - - if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { - return calculation(params.visibleFrameOfScreen) + if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { + return calculateFirstRect(params) } - return quarterRect(params.visibleFrameOfScreen) + return calculateRepeatedRect(params) } func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { diff --git a/Rectangle/WindowCalculation/UpperRightCalculation.swift b/Rectangle/WindowCalculation/UpperRightCalculation.swift index d1fb8f21..e3e2da68 100644 --- a/Rectangle/WindowCalculation/UpperRightCalculation.swift +++ b/Rectangle/WindowCalculation/UpperRightCalculation.swift @@ -12,22 +12,22 @@ class UpperRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalcul override func calculateRect(_ params: RectCalculationParameters) -> RectResult { - guard Defaults.subsequentExecutionMode.value != .none, - let last = params.lastAction, - let lastSubAction = last.subAction - else { + if Defaults.subsequentExecutionMode.cyclesQuadrantPositions { + if let last = params.lastAction, + let lastSubAction = last.subAction, + last.action == .topRight || lastSubAction == .topRightQuarter { + if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { + return calculation(params.visibleFrameOfScreen) + } + } return quarterRect(params.visibleFrameOfScreen) } - if last.action != .topRight && lastSubAction != .topRightQuarter { - return quarterRect(params.visibleFrameOfScreen) - } - - if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { - return calculation(params.visibleFrameOfScreen) + if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { + return calculateFirstRect(params) } - return quarterRect(params.visibleFrameOfScreen) + return calculateRepeatedRect(params) } func quarterRect(_ visibleFrameOfScreen: CGRect) -> RectResult { From 046bfc5750a4cafdb37fc051615466542e9249c8 Mon Sep 17 00:00:00 2001 From: Myron Koch <126993133+MyronKoch@users.noreply.github.com> Date: Thu, 19 Mar 2026 01:07:56 -0400 Subject: [PATCH 10/10] Reorder Thirds submenu after Move to Edge, rename quadrant cycling mode - Sort category submenus by explicit menuOrder so Thirds appears after Move to Edge and before Fourths when additional sizes are enabled - Rename subsequentExecutionMode label from "resize and cycle quadrant positions" to "cycle positions on quadrants, cycle sizes on half" --- Rectangle/AppDelegate.swift | 3 ++- Rectangle/Base.lproj/Main.storyboard | 2 +- Rectangle/WindowActionCategory.swift | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Rectangle/AppDelegate.swift b/Rectangle/AppDelegate.swift index 82e67063..67bd1d70 100644 --- a/Rectangle/AppDelegate.swift +++ b/Rectangle/AppDelegate.swift @@ -421,7 +421,8 @@ extension AppDelegate: NSMenuDelegate { mainStatusMenu.insertItem(NSMenuItem.separator(), at: menuIndex) menuIndex += 1 - for categoryMenu in categoryMenus { + let sortedCategoryMenus = categoryMenus.sorted { $0.category.menuOrder < $1.category.menuOrder } + for categoryMenu in sortedCategoryMenus { categoryMenu.menu.delegate = self let menuMenuItem = NSMenuItem(title: categoryMenu.category.displayName, action: nil, keyEquivalent: "") if additionalSizeCategories.contains(categoryMenu.category) { diff --git a/Rectangle/Base.lproj/Main.storyboard b/Rectangle/Base.lproj/Main.storyboard index eb104c79..1c76ba5c 100644 --- a/Rectangle/Base.lproj/Main.storyboard +++ b/Rectangle/Base.lproj/Main.storyboard @@ -2814,7 +2814,7 @@ - + diff --git a/Rectangle/WindowActionCategory.swift b/Rectangle/WindowActionCategory.swift index c9a58903..6a61073d 100644 --- a/Rectangle/WindowActionCategory.swift +++ b/Rectangle/WindowActionCategory.swift @@ -12,6 +12,21 @@ enum WindowActionCategory { case halves, corners, thirds, max, size, display, move, other, sixths, fourths, eighths, ninths, twelfths, sixteenths + var menuOrder: Int { + switch self { + case .size: return 0 + case .move: return 1 + case .thirds: return 2 + case .fourths: return 3 + case .sixths: return 4 + case .eighths: return 5 + case .ninths: return 6 + case .twelfths: return 7 + case .sixteenths: return 8 + default: return 99 + } + } + var displayName: String { switch self { case .halves: