├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── funding.yml └── workflows │ └── build.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Rectangle.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ └── Rectangle.xcscheme ├── Rectangle ├── AccessibilityAuthorization │ ├── AccessibilityAuthorization.swift │ └── AccessibilityWindowController.swift ├── AccessibilityElement.swift ├── AppDelegate.swift ├── ApplicationToggle.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── mac016pts1x.png │ │ ├── mac016pts2x.png │ │ ├── mac032pts1x.png │ │ ├── mac032pts2x.png │ │ ├── mac128pts1x.png │ │ ├── mac128pts2x.png │ │ ├── mac256pts1x.png │ │ ├── mac256pts2x.png │ │ ├── mac512pts1x.png │ │ └── mac512pts2x.png │ ├── Contents.json │ ├── StatusTemplate.imageset │ │ ├── Contents.json │ │ ├── RectangleStatusTemplate.png │ │ ├── RectangleStatusTemplate22.png │ │ └── RectangleStatusTemplate44.png │ ├── Untilted.imageset │ │ ├── Contents.json │ │ └── mac256pts1x.png │ ├── WindowPositions │ │ ├── Contents.json │ │ ├── almostMaximizeTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── almostMaximizeTemplate.png │ │ ├── bottomCenterSixthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── bottomCenterSixthTemplate.png │ │ ├── bottomHalfTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── bottomHalfTemplate.png │ │ ├── bottomLeftSixthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── bottomLeftSixthTemplate.png │ │ ├── bottomLeftTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── bottomLeftTemplate.png │ │ ├── bottomRightSixthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── bottomRightSixthTemplate.png │ │ ├── bottomRightTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── bottomRightTemplate.png │ │ ├── centerLeftFourthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── centerLeftFourthTemplate.png │ │ ├── centerRightFourthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── centerRightFourthTemplate.png │ │ ├── centerTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── centerTemplate.png │ │ ├── centerThirdTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── centerThirdTemplate.png │ │ ├── firstThirdTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── firstThirdTemplate.png │ │ ├── firstThreeFourthsTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── firstThreeFourthsTemplate.png │ │ ├── firstTwoThirdsTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── firstTwoThirdsTemplate.png │ │ ├── halfWidthCenterTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── halfWidthCenterTemplate.png │ │ ├── lastThirdTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── lastThirdTemplate.png │ │ ├── lastThreeFourthsTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── lastThreeFourthsTemplate.png │ │ ├── lastTwoThirdsTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── lastTwoThirdsTemplate.png │ │ ├── leftFourthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── leftFourthTemplate.png │ │ ├── leftHalfTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── leftHalfTemplate.png │ │ ├── makeLargerTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── makeLargerTemplate.png │ │ ├── makeSmallerTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── makeSmallerTemplate.png │ │ ├── maximizeHeightTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── maximizeHeightTemplate.png │ │ ├── maximizeTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── maximizeTemplate.png │ │ ├── moveDownTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── moveDownTemplate.png │ │ ├── moveLeftTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── moveLeftTemplate.png │ │ ├── moveRightTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── moveRightTemplate.png │ │ ├── moveUpTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── moveUpTemplate.png │ │ ├── nextDisplayTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── nextDisplayTemplate.png │ │ ├── prevDisplayTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── prevDisplayTemplate.png │ │ ├── restoreTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── restoreTemplate.png │ │ ├── rightFourthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── rightFourthTemplate.png │ │ ├── rightHalfTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── rightHalfTemplate.png │ │ ├── topCenterSixthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── topCenterSixthTemplate.png │ │ ├── topHalfTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── topHalfTemplate.png │ │ ├── topLeftSixthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── topLeftSixthTemplate.png │ │ ├── topLeftTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── topLeftTemplate.png │ │ ├── topRightSixthTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── topRightSixthTemplate.png │ │ └── topRightTemplate.imageset │ │ │ ├── Contents.json │ │ │ └── topRightTemplate.png │ ├── keyboardToolbarTemplate.imageset │ │ ├── Contents.json │ │ ├── keyboardToolbarTemplate.png │ │ └── keyboardToolbarTemplateSmall.png │ ├── snapAreaTemplate.imageset │ │ ├── Contents.json │ │ ├── snapAreaTemplate.png │ │ └── snapAreaTemplateSmall.png │ ├── square.and.arrow.down.imageset │ │ ├── Contents.json │ │ └── square.and.arrow.down.png │ ├── square.and.arrow.up.imageset │ │ ├── Contents.json │ │ └── square.and.arrow.up.png │ ├── toolbarSettingsTemplate.imageset │ │ ├── Contents.json │ │ ├── toolbarSettingsTemplate copy.png │ │ └── toolbarSettingsTemplate.png │ ├── wallpaperTiger.imageset │ │ ├── Contents.json │ │ └── wallpaperTiger.png │ └── wallpaperTigerVertical.imageset │ │ ├── Contents.json │ │ └── wallpaperTigerVertical.png ├── Base.lproj │ └── Main.storyboard ├── CycleSize.swift ├── Defaults.swift ├── Info.plist ├── InternetAccessPolicy.plist ├── LaunchOnLogin.swift ├── Logging │ ├── LogViewer.storyboard │ └── LogViewer.swift ├── MultiWindow │ ├── MultiWindowManager.swift │ └── ReverseAllManager.swift ├── Popover │ ├── MessageView.swift │ └── MessageView.xib ├── PrefsWindow │ ├── Config.swift │ ├── PrefsViewController.swift │ ├── SettingsViewController.swift │ └── SnapAreaViewController.swift ├── Rectangle-Bridging-Header.h ├── Rectangle.entitlements ├── RectangleRelease.entitlements ├── RectangleStatusItem.swift ├── ScreenDetection.swift ├── ShortcutManager.swift ├── Snapping │ ├── CompoundSnapArea │ │ ├── CompoundSnapArea.swift │ │ ├── FourthsCompoundCalculation.swift │ │ ├── HalvesCompoundCalculation.swift │ │ ├── SixthsCompoundCalculation.swift │ │ └── ThirdsCompoundCalculation.swift │ ├── FootprintWindow.swift │ ├── SnapAreaModel.swift │ └── SnappingManager.swift ├── SubsequentExecutionMode.swift ├── TitleBarManager.swift ├── TodoMode │ └── TodoManager.swift ├── Utilities │ ├── AXExtension.swift │ ├── AlertUtil.swift │ ├── CFExtension.swift │ ├── CGExtension.swift │ ├── CUtil.swift │ ├── Debounce.swift │ ├── DispatchTimeExtension.swift │ ├── EventMonitor.swift │ ├── MASShortcutMigration.swift │ ├── MacTilingDefaults.swift │ ├── NSImageExtension.swift │ ├── NotificationExtension.swift │ ├── RunLoopThread.swift │ ├── SequenceExtension.swift │ ├── StageUtil.swift │ ├── StringExtension.swift │ ├── TimeoutCache.swift │ └── WindowUtil.swift ├── WelcomeWindow │ └── WelcomeViewController.swift ├── WindowAction.swift ├── WindowActionCategory.swift ├── WindowCalculation │ ├── AlmostMaximizeCalculation.swift │ ├── BottomCenterLeftEighthCalculation.swift │ ├── BottomCenterNinthCalculation.swift │ ├── BottomCenterRightEighthCalculation.swift │ ├── BottomCenterSixthCalculation.swift │ ├── BottomHalfCalculation.swift │ ├── BottomLeftEighthCalculation.swift │ ├── BottomLeftNinthCalculation.swift │ ├── BottomLeftSixthCalculation.swift │ ├── BottomLeftThirdCalculation.swift │ ├── BottomRightEighthCalculation.swift │ ├── BottomRightNinthCalculation.swift │ ├── BottomRightSixthCalculation.swift │ ├── BottomRightThirdCalculation.swift │ ├── CenterCalculation.swift │ ├── CenterHalfCalculation.swift │ ├── CenterProminentlyCalculation.swift │ ├── CenterThirdCalculation.swift │ ├── ChangeSizeCalculation.swift │ ├── ChangeWindowDimensionCalculation.swift │ ├── EighthsRepeated.swift │ ├── FirstFourthCalculation.swift │ ├── FirstThirdCalculation.swift │ ├── FirstThreeFourthsCalculation.swift │ ├── FirstTwoThirdsCalculation.swift │ ├── GapCalculation.swift │ ├── HalfOrDoubleDimensionCalculation.swift │ ├── HorizontalThirdsRepeated.swift │ ├── LastFourthCalculation.swift │ ├── LastThirdCalculation.swift │ ├── LastThreeFourthsCalculation.swift │ ├── LastTwoThirdsCalculation.swift │ ├── LeftRightHalfCalculation.swift │ ├── LeftTodoCalculation.swift │ ├── LowerLeftCalculation.swift │ ├── LowerRightCalculation.swift │ ├── MaximizeCalculation.swift │ ├── MaximizeHeightCalculation.swift │ ├── MiddleCenterNinthCalculation.swift │ ├── MiddleLeftNinthCalculation.swift │ ├── MiddleRightNinthCalculation.swift │ ├── MoveLeftRightCalculation.swift │ ├── MoveUpDownCalculation.swift │ ├── NextPrevDisplayCalculation.swift │ ├── NinthsRepeated.swift │ ├── OrientationAware.swift │ ├── RepeatedExecutionsCalculation.swift │ ├── RepeatedExecutionsInThirdsCalculation.swift │ ├── RightTodoCalculation.swift │ ├── SecondFourthCalculation.swift │ ├── SixthsRepeated.swift │ ├── SpecifiedCalculation.swift │ ├── ThirdFourthCalculation.swift │ ├── TopCenterLeftEighthCalculation.swift │ ├── TopCenterNinthCalculation.swift │ ├── TopCenterRightEighthCalculation.swift │ ├── TopCenterSixthCalculation.swift │ ├── TopHalfCalculation.swift │ ├── TopLeftEighthCalculation.swift │ ├── TopLeftNinthCalculation.swift │ ├── TopLeftSixthCalculation.swift │ ├── TopLeftThirdCalculation.swift │ ├── TopRightEighthCalculation.swift │ ├── TopRightNinthCalculation.swift │ ├── TopRightSixthCalculation.swift │ ├── TopRightThirdCalculation.swift │ ├── UpperLeftCalculation.swift │ ├── UpperRightCalculation.swift │ └── WindowCalculation.swift ├── WindowHistory.swift ├── WindowManager.swift ├── WindowMover │ ├── BestEffortWindowMover.swift │ ├── CenteringFixedSizedWindowMover.swift │ ├── QuantizedWindowMover.swift │ ├── StandardWindowMover.swift │ └── WindowMover.swift └── mul.lproj │ └── Main.xcstrings ├── RectangleLauncher ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist ├── RectangleLauncher.entitlements ├── RectangleLauncherRelease.entitlements └── mul.lproj │ └── Main.xcstrings ├── RectangleTests ├── Info.plist └── RectangleTests.swift └── TerminalCommands.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | - [ ] This is a new bug that hasn't been documented in a closed issue or in the Readme. 11 | 12 | macOS version: 13 | Rectangle version: 14 | Logs if applicable (In Rectangle menu, hold option, "View Logging..."): 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature idea 3 | about: Please use the Discussions tab to share a feature idea ⬆️ 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | contact_links: 8 | - name: Rectangle Discussions 9 | url: https://github.com/rxhanson/Rectangle/discussions 10 | about: Please discuss feature ideas here. 11 | --- 12 | 13 | Feature ideas are now kept in the [discussions tab](https://github.com/rxhanson/Rectangle/discussions). Please create an issue there, not here. 14 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: rxhanson 2 | custom: https://rectangleapp.com 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | env: 8 | XCODE_PROJECT: "Rectangle.xcodeproj" 9 | XCODE_SCHEME: "Rectangle" 10 | CODE_SIGN_IDENTITY: "-" 11 | BUILD_DIR: "build" 12 | XCODE_ARCHIVE: "Rectangle.xcarchive" 13 | APP_NAME: "Rectangle.app" 14 | EXPORT_OPTIONS_PLIST: "exportOptions.plist" 15 | DMG_NAME: "Rectangle" 16 | DMG_FILE_NAME: "Rectangle.dmg" 17 | 18 | jobs: 19 | build: 20 | runs-on: macos-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | - name: Build 25 | run: xcodebuild -project "$XCODE_PROJECT" -scheme "$XCODE_SCHEME" archive CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" -archivePath "$BUILD_DIR/$XCODE_ARCHIVE" 26 | - name: Export 27 | run: | 28 | plutil -convert xml1 - -o "$EXPORT_OPTIONS_PLIST" << EOF 29 | { 30 | "destination": "export", 31 | "method": "mac-application" 32 | } 33 | EOF 34 | 35 | xcodebuild -exportArchive -archivePath "$BUILD_DIR/$XCODE_ARCHIVE" -exportPath "$BUILD_DIR" -exportOptionsPlist "$EXPORT_OPTIONS_PLIST" 36 | - name: Resign App 37 | run: codesign --force --deep -s "$CODE_SIGN_IDENTITY" "$BUILD_DIR/$APP_NAME" 38 | - name: Make DMG 39 | run: hdiutil create -srcdir "$BUILD_DIR" -volname "$DMG_NAME" "$DMG_FILE_NAME" 40 | - name: Upload 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: Build 44 | path: ${{ env.DMG_FILE_NAME }} 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | **/.DS_Store 3 | */xcuserdata/ 4 | xcuserdata/ 5 | **.xcuserstate 6 | *.xcworkspace 7 | Pods 8 | 9 | ## Xcode per-user config 10 | *.mode1 11 | *.mode1v3 12 | *.mode2v3 13 | *.perspective 14 | *.perspectivev3 15 | *.pbxuser 16 | 17 | ## Xcode Patch 18 | *.xcodeproj/* 19 | !*.xcodeproj/project.pbxproj 20 | !*.xcodeproj/xcshareddata/ 21 | !*.xcworkspace/contents.xcworkspacedata 22 | /*.gcno 23 | 24 | ## Build products 25 | build/ 26 | *.o 27 | *.LinkFileList 28 | *.hmap 29 | 30 | ## Automatic backup files 31 | *~.nib/ 32 | *.swp 33 | *~ 34 | *.dat 35 | *.dep 36 | 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Feature Requests 4 | 5 | Rectangle is not accepting any new feature requests at this time, sorry. You can file a feature request for a feature that you plan to implement and submit a pull request for, so that the feature can be reviewed and you will know ahead of time if the feature will be rejected. 6 | 7 | ## Bugs 8 | 9 | Please search through the existing issues, open and closed, before filing a new bug. 10 | Add the version of Rectangle, the version of the OS, and screenshots or videos as necessary. 11 | 12 | ## Coding Style 13 | 14 | Please match the existing coding style as much as possible. 15 | 16 | ## License 17 | 18 | By contributing to Rectangle you agree that your contributions will be licensed under its MIT license. 19 | 20 | ## Incentives 21 | 22 | Logic from Rectangle is used in the [Multitouch](https://multitouch.app) app. The [Rectangle Pro](https://rectangleapp.com/pro) app is entirely built on top of Rectangle. If you contribute significant code or localizations that get merged into Rectangle, you get a free license of Multitouch or Rectangle Pro. Contributors to Sparkle, MASShortcut, or Spectacle can also receive free Multitouch or Rectangle Pro licenses (just send me a direct message on [Gitter](https://gitter.im)). 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2025 Ryan Hanson 4 | Based on the Spectacle app, Copyright (c) 2017 Eric Czarny eczarny@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Rectangle/AccessibilityAuthorization/AccessibilityAuthorization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccessibilityAuthorization.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 6/11/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | 12 | class AccessibilityAuthorization { 13 | 14 | private var accessibilityWindowController: NSWindowController? 15 | 16 | public func checkAccessibility(completion: @escaping () -> Void) -> Bool { 17 | if !AXIsProcessTrusted() { 18 | 19 | accessibilityWindowController = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "AccessibilityWindowController") as? NSWindowController 20 | 21 | NSApp.activate(ignoringOtherApps: true) 22 | accessibilityWindowController?.showWindow(self) 23 | pollAccessibility(completion: completion) 24 | return false 25 | } else { 26 | return true 27 | } 28 | } 29 | 30 | private func pollAccessibility(completion: @escaping () -> Void) { 31 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { 32 | if AXIsProcessTrusted() { 33 | self.accessibilityWindowController?.close() 34 | self.accessibilityWindowController = nil 35 | completion() 36 | } else { 37 | self.pollAccessibility(completion: completion) 38 | } 39 | } 40 | } 41 | 42 | func showAuthorizationWindow() { 43 | if accessibilityWindowController?.window?.isMiniaturized == true { 44 | accessibilityWindowController?.window?.deminiaturize(self) 45 | } 46 | NSApp.activate(ignoringOtherApps: true) 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Rectangle/AccessibilityAuthorization/AccessibilityWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccessibilityWindow.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 6/13/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AccessibilityWindowController: NSWindowController { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | let closeButton = self.window?.standardWindowButton(.closeButton) 16 | closeButton?.target = self 17 | closeButton?.action = #selector(quit) 18 | } 19 | 20 | @objc func quit() { 21 | exit(1) 22 | } 23 | 24 | } 25 | 26 | class AccessibilityViewController: NSViewController { 27 | 28 | @IBOutlet weak var sysPrefsPathField: NSTextField! 29 | @IBOutlet weak var openSysPrefsButton: NSButton! 30 | @IBOutlet weak var padlockField: NSTextField! 31 | 32 | override func viewDidLoad() { 33 | if #available(OSX 13, *) { 34 | sysPrefsPathField.stringValue = NSLocalizedString( 35 | "Go to System Settings → Privacy & Security → Accessibility", tableName: "Main", value: "", comment: "") 36 | openSysPrefsButton.title = NSLocalizedString( 37 | "Open System Settings", tableName: "Main", value: "", comment: "") 38 | padlockField.isHidden = true 39 | } 40 | } 41 | 42 | @IBAction func openSystemPrefs(_ sender: Any) { 43 | NSWorkspace.shared.open(URL(string:"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "mac016pts1x.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "mac016pts2x.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "mac032pts1x.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "mac032pts2x.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "mac128pts1x.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "mac128pts2x.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "mac256pts1x.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "mac256pts2x.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "mac512pts1x.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "mac512pts2x.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac016pts1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac016pts1x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac016pts2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac016pts2x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac032pts1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac032pts1x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac032pts2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac032pts2x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac128pts1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac128pts1x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac128pts2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac128pts2x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac256pts1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac256pts1x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac256pts2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac256pts2x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac512pts1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac512pts1x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/AppIcon.appiconset/mac512pts2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/AppIcon.appiconset/mac512pts2x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/StatusTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "RectangleStatusTemplate22.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "RectangleStatusTemplate44.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "RectangleStatusTemplate.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/StatusTemplate.imageset/RectangleStatusTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/StatusTemplate.imageset/RectangleStatusTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/StatusTemplate.imageset/RectangleStatusTemplate22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/StatusTemplate.imageset/RectangleStatusTemplate22.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/StatusTemplate.imageset/RectangleStatusTemplate44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/StatusTemplate.imageset/RectangleStatusTemplate44.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/Untilted.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "mac256pts1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/Untilted.imageset/mac256pts1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/Untilted.imageset/mac256pts1x.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/almostMaximizeTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "almostMaximizeTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/almostMaximizeTemplate.imageset/almostMaximizeTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/almostMaximizeTemplate.imageset/almostMaximizeTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomCenterSixthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "bottomCenterSixthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomCenterSixthTemplate.imageset/bottomCenterSixthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/bottomCenterSixthTemplate.imageset/bottomCenterSixthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomHalfTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bottomHalfTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomHalfTemplate.imageset/bottomHalfTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/bottomHalfTemplate.imageset/bottomHalfTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "bottomLeftSixthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixthTemplate.imageset/bottomLeftSixthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/bottomLeftSixthTemplate.imageset/bottomLeftSixthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomLeftTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bottomLeftTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomLeftTemplate.imageset/bottomLeftTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/bottomLeftTemplate.imageset/bottomLeftTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomRightSixthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "bottomRightSixthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomRightSixthTemplate.imageset/bottomRightSixthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/bottomRightSixthTemplate.imageset/bottomRightSixthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomRightTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bottomRightTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/bottomRightTemplate.imageset/bottomRightTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/bottomRightTemplate.imageset/bottomRightTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerLeftFourthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "centerLeftFourthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerLeftFourthTemplate.imageset/centerLeftFourthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/centerLeftFourthTemplate.imageset/centerLeftFourthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerRightFourthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "centerRightFourthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerRightFourthTemplate.imageset/centerRightFourthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/centerRightFourthTemplate.imageset/centerRightFourthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "centerTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerTemplate.imageset/centerTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/centerTemplate.imageset/centerTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerThirdTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "centerThirdTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/centerThirdTemplate.imageset/centerThirdTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/centerThirdTemplate.imageset/centerThirdTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/firstThirdTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "firstThirdTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/firstThirdTemplate.imageset/firstThirdTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/firstThirdTemplate.imageset/firstThirdTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/firstThreeFourthsTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "firstThreeFourthsTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/firstThreeFourthsTemplate.imageset/firstThreeFourthsTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/firstThreeFourthsTemplate.imageset/firstThreeFourthsTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/firstTwoThirdsTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "firstTwoThirdsTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/firstTwoThirdsTemplate.imageset/firstTwoThirdsTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/firstTwoThirdsTemplate.imageset/firstTwoThirdsTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/halfWidthCenterTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "halfWidthCenterTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/halfWidthCenterTemplate.imageset/halfWidthCenterTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/halfWidthCenterTemplate.imageset/halfWidthCenterTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/lastThirdTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "lastThirdTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/lastThirdTemplate.imageset/lastThirdTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/lastThirdTemplate.imageset/lastThirdTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/lastThreeFourthsTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "lastThreeFourthsTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/lastThreeFourthsTemplate.imageset/lastThreeFourthsTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/lastThreeFourthsTemplate.imageset/lastThreeFourthsTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/lastTwoThirdsTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "lastTwoThirdsTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/lastTwoThirdsTemplate.imageset/lastTwoThirdsTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/lastTwoThirdsTemplate.imageset/lastTwoThirdsTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/leftFourthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "leftFourthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/leftFourthTemplate.imageset/leftFourthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/leftFourthTemplate.imageset/leftFourthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/leftHalfTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "leftHalfTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/leftHalfTemplate.imageset/leftHalfTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/leftHalfTemplate.imageset/leftHalfTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/makeLargerTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "makeLargerTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/makeLargerTemplate.imageset/makeLargerTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/makeLargerTemplate.imageset/makeLargerTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/makeSmallerTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "makeSmallerTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/makeSmallerTemplate.imageset/makeSmallerTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/makeSmallerTemplate.imageset/makeSmallerTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/maximizeHeightTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "maximizeHeightTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/maximizeHeightTemplate.imageset/maximizeHeightTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/maximizeHeightTemplate.imageset/maximizeHeightTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/maximizeTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "maximizeTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/maximizeTemplate.imageset/maximizeTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/maximizeTemplate.imageset/maximizeTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveDownTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "moveDownTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveDownTemplate.imageset/moveDownTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/moveDownTemplate.imageset/moveDownTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveLeftTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "moveLeftTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveLeftTemplate.imageset/moveLeftTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/moveLeftTemplate.imageset/moveLeftTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveRightTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "moveRightTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveRightTemplate.imageset/moveRightTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/moveRightTemplate.imageset/moveRightTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveUpTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "moveUpTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/moveUpTemplate.imageset/moveUpTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/moveUpTemplate.imageset/moveUpTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/nextDisplayTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "nextDisplayTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/nextDisplayTemplate.imageset/nextDisplayTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/nextDisplayTemplate.imageset/nextDisplayTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/prevDisplayTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "prevDisplayTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/prevDisplayTemplate.imageset/prevDisplayTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/prevDisplayTemplate.imageset/prevDisplayTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/restoreTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "restoreTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/restoreTemplate.imageset/restoreTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/restoreTemplate.imageset/restoreTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/rightFourthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rightFourthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/rightFourthTemplate.imageset/rightFourthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/rightFourthTemplate.imageset/rightFourthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/rightHalfTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "rightHalfTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/rightHalfTemplate.imageset/rightHalfTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/rightHalfTemplate.imageset/rightHalfTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topCenterSixthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "topCenterSixthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topCenterSixthTemplate.imageset/topCenterSixthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/topCenterSixthTemplate.imageset/topCenterSixthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topHalfTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "topHalfTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topHalfTemplate.imageset/topHalfTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/topHalfTemplate.imageset/topHalfTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topLeftSixthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "topLeftSixthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topLeftSixthTemplate.imageset/topLeftSixthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/topLeftSixthTemplate.imageset/topLeftSixthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topLeftTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "topLeftTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topLeftTemplate.imageset/topLeftTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/topLeftTemplate.imageset/topLeftTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topRightSixthTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "topRightSixthTemplate.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topRightSixthTemplate.imageset/topRightSixthTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/topRightSixthTemplate.imageset/topRightSixthTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topRightTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "topRightTemplate.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/WindowPositions/topRightTemplate.imageset/topRightTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/WindowPositions/topRightTemplate.imageset/topRightTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/keyboardToolbarTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "keyboardToolbarTemplateSmall.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "keyboardToolbarTemplate.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/keyboardToolbarTemplate.imageset/keyboardToolbarTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/keyboardToolbarTemplate.imageset/keyboardToolbarTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/keyboardToolbarTemplate.imageset/keyboardToolbarTemplateSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/keyboardToolbarTemplate.imageset/keyboardToolbarTemplateSmall.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/snapAreaTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "snapAreaTemplateSmall.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "snapAreaTemplate.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/snapAreaTemplate.imageset/snapAreaTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/snapAreaTemplate.imageset/snapAreaTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/snapAreaTemplate.imageset/snapAreaTemplateSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/snapAreaTemplate.imageset/snapAreaTemplateSmall.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/square.and.arrow.down.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "square.and.arrow.down.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/square.and.arrow.down.imageset/square.and.arrow.down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/square.and.arrow.down.imageset/square.and.arrow.down.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/square.and.arrow.up.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "square.and.arrow.up.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/square.and.arrow.up.imageset/square.and.arrow.up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/square.and.arrow.up.imageset/square.and.arrow.up.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/toolbarSettingsTemplate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "toolbarSettingsTemplate copy.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "toolbarSettingsTemplate.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/toolbarSettingsTemplate.imageset/toolbarSettingsTemplate copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/toolbarSettingsTemplate.imageset/toolbarSettingsTemplate copy.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/toolbarSettingsTemplate.imageset/toolbarSettingsTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/toolbarSettingsTemplate.imageset/toolbarSettingsTemplate.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/wallpaperTiger.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "wallpaperTiger.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/wallpaperTiger.imageset/wallpaperTiger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/wallpaperTiger.imageset/wallpaperTiger.png -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/wallpaperTigerVertical.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "wallpaperTigerVertical.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rectangle/Assets.xcassets/wallpaperTigerVertical.imageset/wallpaperTigerVertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxhanson/Rectangle/3f65edde130b13dfa267493ff2e9e9d873e8f9b5/Rectangle/Assets.xcassets/wallpaperTigerVertical.imageset/wallpaperTigerVertical.png -------------------------------------------------------------------------------- /Rectangle/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleURLSchemes 25 | 26 | rectangle 27 | 28 | 29 | 30 | CFBundleVersion 31 | $(CURRENT_PROJECT_VERSION) 32 | LSApplicationCategoryType 33 | public.app-category.productivity 34 | LSMinimumSystemVersion 35 | $(MACOSX_DEPLOYMENT_TARGET) 36 | LSUIElement 37 | 38 | NSHumanReadableCopyright 39 | Copyright © 2019-2025 Ryan Hanson. All rights reserved. 40 | NSMainStoryboardFile 41 | Main 42 | NSPrincipalClass 43 | NSApplication 44 | SUFeedURL 45 | https://rectangleapp.com/downloads/updates.xml 46 | SUPublicEDKey 47 | lpt9M3PhocbZ3MZiLH+crEqRfU11kfoNzGxSqiEIdvM= 48 | SUScheduledCheckInterval 49 | 172800 50 | 51 | 52 | -------------------------------------------------------------------------------- /Rectangle/InternetAccessPolicy.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ApplicationDescription 6 | Rectangle is the gold standard for window management on macOS. Free and open source. 7 | Connections 8 | 9 | 10 | IsIncoming 11 | 12 | Host 13 | rectangleapp.com 14 | NetworkProtocol 15 | TCP 16 | Port 17 | 443 18 | Purpose 19 | Rectangle checks for new versions 20 | DenyConsequences 21 | If you deny this connection, you will not be notified about new versions. 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Rectangle/LaunchOnLogin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchOnLogin.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 2/20/23. 6 | // Copyright © 2023 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ServiceManagement 11 | import os.log 12 | 13 | @available(macOS 13.0, *) 14 | public enum LaunchOnLogin { 15 | public static var isEnabled: Bool { 16 | get { SMAppService.mainApp.status == .enabled } 17 | set { 18 | do { 19 | if newValue { 20 | if SMAppService.mainApp.status == .enabled { 21 | try? SMAppService.mainApp.unregister() 22 | } 23 | 24 | try SMAppService.mainApp.register() 25 | } else { 26 | try SMAppService.mainApp.unregister() 27 | } 28 | } catch { 29 | os_log("Failed to \(newValue ? "enable" : "disable") launch at login: \(error.localizedDescription)") 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Rectangle/MultiWindow/ReverseAllManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TodoManager.swift 3 | // Rectangle 4 | // 5 | // Created by Charlie Harding on 7/25/21. 6 | // Copyright © 2021 Ryan Hanson. All rights reserved. 7 | // 8 | import Cocoa 9 | import MASShortcut 10 | 11 | class ReverseAllManager { 12 | 13 | static func reverseAll(windowElement: AccessibilityElement? = nil) { 14 | let sd = ScreenDetection() 15 | 16 | let currentWindow = windowElement ?? AccessibilityElement.getFrontWindowElement() 17 | guard let currentScreen = sd.detectScreens(using: currentWindow)?.currentScreen else { return } 18 | 19 | let windows = AccessibilityElement.getAllWindowElements() 20 | 21 | let screenFrame = currentScreen.adjustedVisibleFrame() 22 | 23 | for w in windows { 24 | let wScreen = sd.detectScreens(using: w)?.currentScreen 25 | if Defaults.todo.userEnabled && TodoManager.isTodoWindow(w) { continue } 26 | if wScreen == currentScreen { 27 | reverseWindowPosition(w, screenFrame: screenFrame) 28 | } 29 | } 30 | } 31 | 32 | private static func reverseWindowPosition(_ w: AccessibilityElement, screenFrame: CGRect) { 33 | var rect = w.frame 34 | 35 | let offsetFromLeft = rect.minX - screenFrame.minX 36 | 37 | rect.origin.x = screenFrame.maxX - offsetFromLeft - rect.width 38 | 39 | w.setFrame(rect) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Rectangle/Popover/MessageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageView.swift 3 | // Multitouch 4 | // 5 | // Created by Ryan Hanson on 9/10/17. 6 | // Copyright © 2017-2018 BrassMonkery, LLC. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MessagePopover { 12 | 13 | var messageView: MessageView 14 | var popover: NSPopover 15 | 16 | init() { 17 | popover = NSPopover() 18 | messageView = MessageView() 19 | popover.behavior = .transient 20 | popover.contentViewController = messageView 21 | } 22 | 23 | public func show(message: String, sender: NSView) { 24 | let positioningView = sender 25 | let positioningRect = NSZeroRect 26 | let preferredEdge: NSRectEdge = .maxX 27 | messageView.message = message 28 | 29 | popover.show(relativeTo: positioningRect, of: positioningView, preferredEdge: preferredEdge) 30 | } 31 | } 32 | 33 | class MessageView: NSViewController { 34 | 35 | @IBOutlet weak var messageField: NSTextField! 36 | 37 | var message: String? 38 | 39 | override func viewWillAppear() { 40 | super.viewWillAppear() 41 | if let message = message { 42 | self.messageField?.stringValue = message 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Rectangle/Rectangle-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | 7 | AXError _AXUIElementGetWindow(AXUIElementRef element, uint32_t *identifier); 8 | -------------------------------------------------------------------------------- /Rectangle/Rectangle.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Rectangle/RectangleRelease.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Rectangle/RectangleStatusItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RectangleStatusItem.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 6/11/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class RectangleStatusItem { 12 | static let instance = RectangleStatusItem() 13 | 14 | private var nsStatusItem: NSStatusItem? 15 | private var added: Bool = false 16 | public var statusMenu: NSMenu? { 17 | didSet { 18 | nsStatusItem?.menu = statusMenu 19 | } 20 | } 21 | private var isVisibleObserver: NSKeyValueObservation? 22 | 23 | private init() {} 24 | 25 | public func refreshVisibility() { 26 | if Defaults.hideMenuBarIcon.enabled { 27 | remove() 28 | } else { 29 | add() 30 | } 31 | } 32 | 33 | public func openMenu() { 34 | if !added { 35 | add() 36 | } 37 | nsStatusItem?.button?.performClick(self) 38 | refreshVisibility() 39 | } 40 | 41 | private func add() { 42 | added = true 43 | nsStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) 44 | nsStatusItem?.menu = self.statusMenu 45 | nsStatusItem?.button?.image = NSImage(named: "StatusTemplate") 46 | nsStatusItem?.behavior = .removalAllowed 47 | isVisibleObserver = nsStatusItem?.observe(\.isVisible, options: [.old, .new]) { nsStatusItem, change in 48 | if change.oldValue == true && change.newValue == false { 49 | Notification.Name.menuBarIconHidden.post() 50 | Defaults.hideMenuBarIcon.enabled = true 51 | } 52 | } 53 | nsStatusItem?.isVisible = true 54 | } 55 | 56 | private func remove() { 57 | added = false 58 | guard let nsStatusItem = nsStatusItem else { return } 59 | NSStatusBar.system.removeStatusItem(nsStatusItem) 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Rectangle/Snapping/CompoundSnapArea/FourthsCompoundCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FourthsCompoundCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 8/24/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct FourthsColumnCompoundCalculation: CompoundSnapAreaCalculation { 12 | 13 | func snapArea(cursorLocation loc: NSPoint, screen: NSScreen, directional: Directional, priorSnapArea: SnapArea?) -> SnapArea? { 14 | let frame = screen.frame 15 | let quarterWidth = floor(frame.width / 4) 16 | 17 | // check first quarter column 18 | if loc.x <= frame.minX + quarterWidth { 19 | return SnapArea(screen: screen, directional: directional, action: .firstFourth) 20 | } 21 | 22 | // check second quarter column 23 | if loc.x >= frame.minX + quarterWidth && loc.x <= frame.maxX - quarterWidth*2 { 24 | if let priorAction = priorSnapArea?.action { 25 | if priorAction == .firstFourth || priorAction == .firstThreeFourths { 26 | return SnapArea(screen: screen, directional: directional, action: .firstThreeFourths) 27 | } 28 | if priorAction == .thirdFourth || priorAction == .lastThreeFourths || priorAction == .centerHalf { 29 | return SnapArea(screen: screen, directional: directional, action: .centerHalf) 30 | } 31 | } 32 | return SnapArea(screen: screen, directional: directional, action: .secondFourth) 33 | } 34 | 35 | // check third quarter column 36 | if loc.x >= frame.minX + quarterWidth * 2 && loc.x <= frame.maxX - quarterWidth { 37 | if let priorAction = priorSnapArea?.action { 38 | if priorAction == .lastFourth || priorAction == .lastThreeFourths { 39 | return SnapArea(screen: screen, directional: directional, action: .lastThreeFourths) 40 | } 41 | if priorAction == .secondFourth || priorAction == .firstThreeFourths || priorAction == .centerHalf { 42 | return SnapArea(screen: screen, directional: directional, action: .centerHalf) 43 | } 44 | } 45 | return SnapArea(screen: screen, directional: directional, action: .thirdFourth) 46 | } 47 | 48 | // check fourth quarter column 49 | if loc.x >= frame.minX + quarterWidth * 2 { 50 | return SnapArea(screen: screen, directional: directional, action: .lastFourth) 51 | } 52 | 53 | return nil 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Rectangle/SubsequentExecutionMode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubsequentExecutionMode.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 8/15/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SubsequentExecutionMode: Int { 12 | case resize = 0 // based on Spectacle 13 | case acrossMonitor = 1 14 | case none = 2 15 | case acrossAndResize = 3 // across monitor for right/left, spectacle resize for all else 16 | case cycleMonitor = 4 17 | } 18 | 19 | class SubsequentExecutionDefault: Default { 20 | public private(set) var key: String = "subsequentExecutionMode" 21 | private var initialized = false 22 | 23 | var value: SubsequentExecutionMode { 24 | didSet { 25 | if initialized { 26 | UserDefaults.standard.set(value.rawValue, forKey: key) 27 | } 28 | } 29 | } 30 | 31 | init() { 32 | let intValue = UserDefaults.standard.integer(forKey: key) 33 | value = SubsequentExecutionMode(rawValue: intValue) ?? .resize 34 | initialized = true 35 | } 36 | 37 | var resizes: Bool { 38 | switch value { 39 | case .resize, .acrossAndResize: return true 40 | default: return false 41 | } 42 | } 43 | 44 | var traversesDisplays: Bool { 45 | switch value { 46 | case .acrossMonitor, .acrossAndResize: return true 47 | default: return false 48 | } 49 | } 50 | 51 | func load(from codable: CodableDefault) { 52 | if let int = codable.int, 53 | let mode = SubsequentExecutionMode(rawValue: int) { 54 | value = mode 55 | } 56 | } 57 | 58 | func toCodable() -> CodableDefault { 59 | return CodableDefault(int: value.rawValue) 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Rectangle/Utilities/AlertUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertUtil.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 4/26/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AlertUtil { 12 | 13 | static func oneButtonAlert(question: String, text: String, confirmText: String = "OK") { 14 | let alert = NSAlert() 15 | alert.messageText = question 16 | alert.informativeText = text 17 | alert.alertStyle = .warning 18 | alert.addButton(withTitle: confirmText) 19 | alert.runModal() 20 | } 21 | 22 | static func twoButtonAlert(question: String, text: String, confirmText: String = "OK", cancelText: String = "Cancel") -> NSApplication.ModalResponse { 23 | let alert = NSAlert() 24 | alert.messageText = question 25 | alert.informativeText = text 26 | alert.alertStyle = .warning 27 | alert.addButton(withTitle: confirmText) 28 | alert.addButton(withTitle: cancelText) 29 | return alert.runModal() 30 | } 31 | 32 | static func threeButtonAlert(question: String, text: String, buttonOneText: String, buttonTwoText: String, buttonThreeText: String) -> NSApplication.ModalResponse { 33 | let alert = NSAlert() 34 | alert.messageText = question 35 | alert.informativeText = text 36 | alert.alertStyle = .warning 37 | alert.addButton(withTitle: buttonOneText) 38 | alert.addButton(withTitle: buttonTwoText) 39 | alert.addButton(withTitle: buttonThreeText) 40 | return alert.runModal() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Rectangle/Utilities/CFExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CFExtension.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2022 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension CFArray { 11 | func getValue(_ index: CFIndex) -> T { 12 | return unsafeBitCast(CFArrayGetValueAtIndex(self, index), to: T.self) 13 | } 14 | 15 | func getCount() -> CFIndex { 16 | return CFArrayGetCount(self) 17 | } 18 | } 19 | 20 | extension CFDictionary { 21 | func getValue(_ key: CFString) -> T { 22 | return unsafeBitCast(CFDictionaryGetValue(self, unsafeBitCast(key, to: UnsafeRawPointer.self)), to: T.self) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Rectangle/Utilities/CGExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGExtension.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2022 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension CGPoint { 11 | var screenFlipped: CGPoint { 12 | .init(x: x, y: NSScreen.screens[0].frame.maxY - y) 13 | } 14 | } 15 | 16 | extension CGRect { 17 | var screenFlipped: CGRect { 18 | guard !isNull else { 19 | return self 20 | } 21 | return .init(origin: .init(x: origin.x, y: NSScreen.screens[0].frame.maxY - maxY), size: size) 22 | } 23 | 24 | var isLandscape: Bool { width > height } 25 | 26 | var centerPoint: CGPoint { 27 | NSMakePoint(NSMidX(self), NSMidY(self)) 28 | } 29 | 30 | func numSharedEdges(withRect rect: CGRect) -> Int { 31 | var sharedEdgeCount = 0 32 | if minX == rect.minX { sharedEdgeCount += 1 } 33 | if maxX == rect.maxX { sharedEdgeCount += 1 } 34 | if minY == rect.minY { sharedEdgeCount += 1 } 35 | if maxY == rect.maxY { sharedEdgeCount += 1 } 36 | return sharedEdgeCount 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Rectangle/Utilities/CUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CUtil.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 6/12/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class CUtil { 12 | 13 | // bridge object into a pointer to pass into C function 14 | static func bridge(obj : T) -> UnsafeMutableRawPointer { 15 | return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque()) 16 | } 17 | 18 | // bridge pointer back into an object within C function 19 | static func bridge(ptr : UnsafeMutableRawPointer) -> T { 20 | return Unmanaged.fromOpaque(ptr).takeUnretainedValue() 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Rectangle/Utilities/Debounce.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Debounce.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 10/7/21. 6 | // Copyright © 2021 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Dispatch 11 | 12 | class Debounce { 13 | 14 | private init() {} 15 | 16 | static func input(_ input: T, 17 | comparedAgainst current: @escaping @autoclosure () -> (T), 18 | perform: @escaping (T) -> ()) { 19 | 20 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 21 | if input == current() { perform(input) } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Rectangle/Utilities/DispatchTimeExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchTimeExtension.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2022 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension DispatchTime { 11 | var uptimeMilliseconds: UInt64 { uptimeNanoseconds / 1_000_000 } 12 | } 13 | -------------------------------------------------------------------------------- /Rectangle/Utilities/MASShortcutMigration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MASShortcutMigration.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 12/22/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MASShortcut 11 | 12 | class MASShortcutMigration { 13 | 14 | static func migrate() { 15 | 16 | guard let dataTransformer = ValueTransformer(forName: .secureUnarchiveFromDataTransformerName) else { return } 17 | 18 | guard let dictTransformer = ValueTransformer(forName: NSValueTransformerName(rawValue: MASDictionaryTransformerName)) else { return } 19 | 20 | for action in WindowAction.active { 21 | 22 | if let dataValue = UserDefaults.standard.data(forKey: action.name) { 23 | if let shortcut = dataTransformer.transformedValue(dataValue) { 24 | 25 | let dictValue = dictTransformer.reverseTransformedValue(shortcut) 26 | UserDefaults.standard.setValue(dictValue, forKey: action.name) 27 | } 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Rectangle/Utilities/NSImageExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSImageExtension.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 4/9/21. 6 | // Copyright © 2021 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSImage { 12 | func rotated(by degrees: CGFloat) -> NSImage { 13 | let sinDegrees = abs(sin(degrees * CGFloat.pi / 180.0)) 14 | let cosDegrees = abs(cos(degrees * CGFloat.pi / 180.0)) 15 | let newSize = CGSize(width: size.height * sinDegrees + size.width * cosDegrees, 16 | height: size.width * sinDegrees + size.height * cosDegrees) 17 | 18 | let imageBounds = NSRect(x: (newSize.width - size.width) / 2, 19 | y: (newSize.height - size.height) / 2, 20 | width: size.width, height: size.height) 21 | 22 | let otherTransform = NSAffineTransform() 23 | otherTransform.translateX(by: newSize.width / 2, yBy: newSize.height / 2) 24 | otherTransform.rotate(byDegrees: degrees) 25 | otherTransform.translateX(by: -newSize.width / 2, yBy: -newSize.height / 2) 26 | 27 | let rotatedImage = NSImage(size: newSize) 28 | rotatedImage.lockFocus() 29 | otherTransform.concat() 30 | draw(in: imageBounds, from: CGRect.zero, operation: NSCompositingOperation.copy, fraction: 1.0) 31 | rotatedImage.unlockFocus() 32 | 33 | return rotatedImage 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Rectangle/Utilities/NotificationExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationExtension.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 12/23/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension Notification.Name { 12 | 13 | static let configImported = Notification.Name("configImported") 14 | static let windowSnapping = Notification.Name("windowSnapping") 15 | static let frontAppChanged = Notification.Name("frontAppChanged") 16 | static let allowAnyShortcut = Notification.Name("allowAnyShortcutToggle") 17 | static let changeDefaults = Notification.Name("changeDefaults") 18 | static let todoMenuToggled = Notification.Name("todoMenuToggled") 19 | static let appWillBecomeActive = Notification.Name("appWillBecomeActive") 20 | static let missionControlDragging = Notification.Name("missionControlDragging") 21 | static let menuBarIconHidden = Notification.Name("menuBarIconHidden") 22 | static let windowTitleBar = Notification.Name("windowTitleBar") 23 | static let defaultSnapAreas = Notification.Name("defaultSnapAreas") 24 | 25 | func post( 26 | center: NotificationCenter = NotificationCenter.default, 27 | object: Any? = nil, 28 | userInfo: [AnyHashable : Any]? = nil) { 29 | 30 | center.post(name: self, object: object, userInfo: userInfo) 31 | } 32 | 33 | @discardableResult 34 | func onPost( 35 | center: NotificationCenter = NotificationCenter.default, 36 | object: Any? = nil, 37 | queue: OperationQueue? = nil, 38 | using: @escaping (Notification) -> Void) 39 | -> NSObjectProtocol { 40 | 41 | return center.addObserver( 42 | forName: self, 43 | object: object, 44 | queue: queue, 45 | using: using) 46 | } 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Rectangle/Utilities/RunLoopThread.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RunLoopThread.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2022 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | class RunLoopThread: Thread { 11 | private let startSemaphore = DispatchSemaphore(value: 0) 12 | private let mode: RunLoop.Mode 13 | private(set) var runLoop: RunLoop? 14 | 15 | init(mode: RunLoop.Mode, qualityOfService: QualityOfService? = nil, start: Bool = false) { 16 | self.mode = mode 17 | super.init() 18 | if let qualityOfService = qualityOfService { self.qualityOfService = qualityOfService } 19 | if start { self.start() } 20 | } 21 | 22 | override func start() { 23 | super.start() 24 | startSemaphore.wait() 25 | } 26 | 27 | override func main() { 28 | runLoop = RunLoop.current 29 | startSemaphore.signal() 30 | while !isCancelled { 31 | if !runLoop!.run(mode: mode, before: .distantFuture) { 32 | Thread.sleep(forTimeInterval: 0.1) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Rectangle/Utilities/SequenceExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceExtension.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2022 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Sequence { 11 | func uniqueMap(_ transform: (Element) -> T) -> [T] where T: Hashable { 12 | var set = Set() 13 | var array = Array() 14 | for element in self { 15 | let element = transform(element) 16 | if set.insert(element).inserted { 17 | array.append(element) 18 | } 19 | } 20 | return array 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Rectangle/Utilities/StringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringExtension.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2024 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension String { 11 | 12 | var localized: String { 13 | NSLocalizedString(self, tableName: "Main", comment: "") 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Rectangle/Utilities/TimeoutCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeoutCache.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2022 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | class TimeoutCache { 11 | private let timeout: UInt64 12 | private var head: Entry? 13 | private var tail: Entry? 14 | private var cache = [Key: Entry]() 15 | private var purgeRestrictionExpirationTimestamp: UInt64 = 0 16 | private var purgeRestrictionExpired: Bool { DispatchTime.now().uptimeMilliseconds > purgeRestrictionExpirationTimestamp } 17 | 18 | init(timeout: UInt64) { 19 | self.timeout = timeout 20 | } 21 | 22 | subscript(key: Key) -> Value? { 23 | get { 24 | guard let entry = cache[key], !entry.expired else { 25 | remove(key) 26 | return nil 27 | } 28 | return entry.value 29 | } 30 | set { 31 | remove(key) 32 | purge() 33 | guard let value = newValue else { return } 34 | let expirationTimestamp = DispatchTime.now().uptimeMilliseconds + timeout 35 | let entry = Entry(key: key, value: value, expirationTimestamp: expirationTimestamp, previous: tail) 36 | entry.previous?.next = entry 37 | if head == nil { head = entry } 38 | tail = entry 39 | cache[key] = entry 40 | } 41 | } 42 | 43 | private func remove(_ key: Key) { 44 | guard let entry = cache[key] else { return } 45 | cache[key] = nil 46 | if entry === tail { tail = entry.previous } 47 | if entry === head { head = entry.next } 48 | entry.previous?.next = entry.next 49 | entry.next?.previous = entry.previous 50 | } 51 | 52 | private func purge() { 53 | guard purgeRestrictionExpired else { return } 54 | var entry = head 55 | while entry != nil && entry!.expired { 56 | remove(entry!.key) 57 | entry = entry!.next 58 | } 59 | purgeRestrictionExpirationTimestamp = DispatchTime.now().uptimeMilliseconds + (100 * timeout) 60 | } 61 | 62 | private class Entry { 63 | let key: Key 64 | let value: Value 65 | let expirationTimestamp: UInt64 66 | var previous: Entry? 67 | var next: Entry? 68 | var expired: Bool { DispatchTime.now().uptimeMilliseconds > expirationTimestamp } 69 | 70 | init(key: Key, value: Value, expirationTimestamp: UInt64, previous: Entry?) { 71 | self.key = key 72 | self.value = value 73 | self.expirationTimestamp = expirationTimestamp 74 | self.previous = previous 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Rectangle/Utilities/WindowUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WindowUtil.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2022 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | class WindowUtil { 11 | private static var windowListCache = TimeoutCache<[CGWindowID]?, [WindowInfo]>(timeout: 100) 12 | 13 | static func getWindowList(ids: [CGWindowID]? = nil, all: Bool = false) -> [WindowInfo] { 14 | if let infos = windowListCache[ids] { 15 | return infos 16 | } 17 | var infos = [WindowInfo]() 18 | var rawInfos: CFArray? 19 | if let ids { 20 | let values = UnsafeMutablePointer.allocate(capacity: ids.count) 21 | for (i, id) in ids.enumerated() { 22 | values[i] = UnsafeRawPointer(bitPattern: UInt(id)) 23 | } 24 | let rawIds = CFArrayCreate(kCFAllocatorDefault, values, ids.count, nil) 25 | rawInfos = CGWindowListCreateDescriptionFromArray(rawIds) 26 | } else { 27 | rawInfos = CGWindowListCopyWindowInfo([all ? .optionAll : .optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID) 28 | } 29 | if let rawInfos { 30 | let count = rawInfos.getCount() 31 | for i in 0.. 1) 19 | ? 0.9 20 | : CGFloat(defaultHeight) 21 | 22 | let defaultWidth = Defaults.almostMaximizeWidth.value 23 | almostMaximizeWidth = (defaultWidth <= 0 || defaultWidth > 1) 24 | ? 0.9 25 | : CGFloat(defaultWidth) 26 | } 27 | 28 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 29 | 30 | let visibleFrameOfScreen = params.visibleFrameOfScreen 31 | var calculatedWindowRect = visibleFrameOfScreen 32 | 33 | // Resize 34 | calculatedWindowRect.size.height = round(visibleFrameOfScreen.height * almostMaximizeHeight) 35 | calculatedWindowRect.size.width = round(visibleFrameOfScreen.width * almostMaximizeWidth) 36 | 37 | // Center 38 | calculatedWindowRect.origin.x = round((visibleFrameOfScreen.width - calculatedWindowRect.width) / 2.0) + visibleFrameOfScreen.minX 39 | calculatedWindowRect.origin.y = round((visibleFrameOfScreen.height - calculatedWindowRect.height) / 2.0) + visibleFrameOfScreen.minY 40 | 41 | return RectResult(calculatedWindowRect) 42 | } 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomCenterLeftEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomCenterLeftEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomCenterLeftEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomCenterLeftEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 40 | return RectResult(rect, subAction: .bottomCenterLeftEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = floor(visibleFrameOfScreen.maxY - (visibleFrameOfScreen.height * 0.75)) 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | return RectResult(rect, subAction: .bottomCenterLeftEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomCenterNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomCenterNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomCenterNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomCenterNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 40 | return RectResult(rect, subAction: .bottomCenterNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | return RectResult(rect, subAction: .bottomCenterNinth) 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomCenterRightEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomCenterRightEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomCenterRightEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomCenterRightEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX + (2.0 * rect.width) 40 | return RectResult(rect, subAction: .bottomCenterRightEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = visibleFrameOfScreen.minY 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .bottomCenterRightEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomHalfCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomHalfCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomHalfCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { 16 | return calculateFirstRect(params) 17 | } 18 | 19 | return calculateRepeatedRect(params) 20 | } 21 | 22 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { 23 | let visibleFrameOfScreen = params.visibleFrameOfScreen 24 | 25 | var rect = visibleFrameOfScreen 26 | rect.size.height = floor(visibleFrameOfScreen.height * CGFloat(fraction)) 27 | return RectResult(rect) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomLeftEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomLeftEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomLeftEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomLeftEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX 40 | return RectResult(rect, subAction: .bottomLeftEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = floor(visibleFrameOfScreen.maxY - (visibleFrameOfScreen.height * 0.75)) 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .bottomLeftEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomLeftNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomLeftNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomLeftNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomLeftNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX 40 | return RectResult(rect, subAction: .bottomLeftNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .bottomLeftNinth) 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomLeftSixthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomLeftSixthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/16/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomLeftSixthCalculation: WindowCalculation, OrientationAware, SixthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomLeftSixth 24 | && lastSubAction != .bottomLeftSixthLandscape 25 | && lastSubAction != .bottomLeftSixthPortrait { 26 | return orientationBasedRect(visibleFrameOfScreen) 27 | } 28 | 29 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 30 | return calculation(visibleFrameOfScreen) 31 | } 32 | 33 | return orientationBasedRect(visibleFrameOfScreen) 34 | } 35 | 36 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 37 | var rect = visibleFrameOfScreen 38 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 39 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 40 | return RectResult(rect, subAction: .bottomLeftSixthLandscape) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | return RectResult(rect, subAction: .bottomLeftSixthPortrait) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomLeftThirdCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomLeftThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomLeftThirdCalculation: WindowCalculation, OrientationAware, HorizontalThirdsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomLeftThird { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(2.0 * visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX 40 | return RectResult(rect, subAction: .bottomLeftThird) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(2.0 * visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .bottomLeftThird) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomRightEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomRightEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomRightEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomRightEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX + (3.0 * rect.width) 40 | return RectResult(rect, subAction: .bottomRightEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = visibleFrameOfScreen.minY 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | return RectResult(rect, subAction: .bottomRightEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomRightNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomRightNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomRightNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomRightNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 40 | return RectResult(rect, subAction: .bottomRightNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY 48 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 49 | return RectResult(rect, subAction: .bottomRightNinth) 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomRightSixthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomRightSixthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/16/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomRightSixthCalculation: WindowCalculation, OrientationAware, SixthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | let visibleFrameOfScreen = params.visibleFrameOfScreen 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomRightSixth 24 | && lastSubAction != .bottomRightSixthLandscape 25 | && lastSubAction != .bottomRightSixthPortrait { 26 | return orientationBasedRect(visibleFrameOfScreen) 27 | } 28 | 29 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .left) { 30 | return calculation(visibleFrameOfScreen) 31 | } 32 | 33 | return orientationBasedRect(visibleFrameOfScreen) 34 | } 35 | 36 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 37 | var rect = visibleFrameOfScreen 38 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 39 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 40 | rect.origin.x = visibleFrameOfScreen.origin.x + visibleFrameOfScreen.width - rect.width 41 | return RectResult(rect, subAction: .bottomRightSixthLandscape) 42 | } 43 | 44 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 45 | var rect = visibleFrameOfScreen 46 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 47 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 48 | rect.origin.x = visibleFrameOfScreen.origin.x + visibleFrameOfScreen.width - rect.width 49 | return RectResult(rect, subAction: .bottomRightSixthPortrait) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/BottomRightThirdCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BottomRightThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BottomRightThirdCalculation: WindowCalculation, OrientationAware, HorizontalThirdsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .bottomRightThird { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(2.0 * visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.minY 39 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width / 3.0 40 | return RectResult(rect, subAction: .bottomRightThird) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(2.0 * visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY 48 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width / 2.0 49 | return RectResult(rect, subAction: .bottomRightThird) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/CenterCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CenterCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class CenterCalculation: WindowCalculation { 12 | 13 | override func calculate(_ params: WindowCalculationParameters) -> WindowCalculationResult? { 14 | 15 | var screenFrame: CGRect? 16 | if !Defaults.alwaysAccountForStage.userEnabled { 17 | screenFrame = params.usableScreens.currentScreen.adjustedVisibleFrame(params.ignoreTodo, true) 18 | } 19 | 20 | let rectResult = calculateRect(params.asRectParams(visibleFrame: screenFrame)) 21 | 22 | let resultingAction: WindowAction = rectResult.resultingAction ?? params.action 23 | 24 | return WindowCalculationResult(rect: rectResult.rect, 25 | screen: params.usableScreens.currentScreen, 26 | resultingAction: resultingAction, 27 | resultingScreenFrame: screenFrame) 28 | } 29 | 30 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 31 | 32 | let visibleFrameOfScreen = params.visibleFrameOfScreen 33 | var calculatedWindowRect = params.window.rect 34 | 35 | let heightExceeded = params.window.rect.height > visibleFrameOfScreen.height 36 | let widthExceeded = params.window.rect.width > visibleFrameOfScreen.width 37 | 38 | if heightExceeded && widthExceeded { 39 | return RectResult(visibleFrameOfScreen, resultingAction: .maximize) 40 | } 41 | 42 | if heightExceeded { 43 | calculatedWindowRect.size.height = visibleFrameOfScreen.height 44 | calculatedWindowRect.origin.y = visibleFrameOfScreen.minY 45 | } else { 46 | calculatedWindowRect.origin.y = round((visibleFrameOfScreen.height - params.window.rect.height) / 2.0) + visibleFrameOfScreen.minY 47 | } 48 | 49 | if widthExceeded { 50 | calculatedWindowRect.size.width = visibleFrameOfScreen.width 51 | calculatedWindowRect.origin.x = visibleFrameOfScreen.minX 52 | } else { 53 | calculatedWindowRect.origin.x = round((visibleFrameOfScreen.width - params.window.rect.width) / 2.0) + visibleFrameOfScreen.minX 54 | } 55 | 56 | return RectResult(calculatedWindowRect) 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/CenterProminentlyCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CenterProminentlyCalculation.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2019 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | class CenterProminentlyCalculation: WindowCalculation { 11 | 12 | override func calculate(_ params: WindowCalculationParameters) -> WindowCalculationResult? { 13 | 14 | var screenFrame: CGRect? 15 | if !Defaults.alwaysAccountForStage.userEnabled { 16 | screenFrame = params.usableScreens.currentScreen.adjustedVisibleFrame(params.ignoreTodo, true) 17 | } 18 | 19 | let rectResult = calculateRect(params.asRectParams(visibleFrame: screenFrame)) 20 | 21 | let resultingAction: WindowAction = rectResult.resultingAction ?? params.action 22 | 23 | return WindowCalculationResult(rect: rectResult.rect, 24 | screen: params.usableScreens.currentScreen, 25 | resultingAction: resultingAction, 26 | resultingScreenFrame: screenFrame) 27 | } 28 | 29 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 30 | 31 | let rectResult = WindowCalculationFactory.centerCalculation.calculateRect(params) 32 | var rect = rectResult.rect 33 | rect.origin.y += -0.25 * rect.height + 0.25 * params.visibleFrameOfScreen.height 34 | return RectResult(rect, resultingAction: rectResult.resultingAction) 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/CenterThirdCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HorizCenterThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 7/26/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class CenterThirdCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | return orientationBasedRect(visibleFrameOfScreen) 16 | } 17 | 18 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 19 | var rect = visibleFrameOfScreen 20 | rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 3.0) 21 | rect.origin.y = visibleFrameOfScreen.minY 22 | rect.size.width = visibleFrameOfScreen.width / 3.0 23 | rect.size.height = visibleFrameOfScreen.height 24 | return RectResult(rect, subAction: .centerVerticalThird) 25 | } 26 | 27 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 28 | var rect = visibleFrameOfScreen 29 | rect.origin.x = visibleFrameOfScreen.minX 30 | rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 3.0) 31 | rect.size.width = visibleFrameOfScreen.width 32 | rect.size.height = visibleFrameOfScreen.height / 3.0 33 | return RectResult(rect, subAction: .centerHorizontalThird) 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/ChangeWindowDimensionCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MinimumWindowDimensionAware.swift 3 | // Rectangle 4 | // 5 | // Created by Isaac Young on 23/04/24. 6 | // Copyright © 2024 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ChangeWindowDimensionCalculation { 12 | func resizedWindowRectIsTooSmall(windowRect: CGRect, visibleFrameOfScreen: CGRect) -> Bool; 13 | } 14 | 15 | extension ChangeWindowDimensionCalculation { 16 | private func minimumWindowWidth() -> CGFloat { 17 | let defaultWidth = Defaults.minimumWindowWidth.value 18 | return (defaultWidth <= 0 || defaultWidth > 1) 19 | ? 0.25 20 | : CGFloat(defaultWidth) 21 | } 22 | 23 | private func minimumWindowHeight() -> CGFloat { 24 | let defaultHeight = Defaults.minimumWindowHeight.value 25 | return (defaultHeight <= 0 || defaultHeight > 1) 26 | ? 0.25 27 | : CGFloat(defaultHeight) 28 | } 29 | 30 | func resizedWindowRectIsTooSmall(windowRect: CGRect, visibleFrameOfScreen: CGRect) -> Bool { 31 | let minimumWindowRectWidth = floor(visibleFrameOfScreen.width * minimumWindowWidth()) 32 | let minimumWindowRectHeight = floor(visibleFrameOfScreen.height * minimumWindowHeight()) 33 | return (windowRect.width <= minimumWindowRectWidth) || (windowRect.height <= minimumWindowRectHeight) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/FirstFourthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeftThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 7/26/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class FirstFourthCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | params.action == .firstFourth, 18 | let last = params.lastAction, 19 | let lastSubAction = last.subAction 20 | else { 21 | return orientationBasedRect(visibleFrameOfScreen) 22 | } 23 | 24 | var calculation: WindowCalculation? 25 | if last.action == .firstFourth { 26 | switch lastSubAction { 27 | case .topFourth, .leftFourth: 28 | calculation = WindowCalculationFactory.secondFourthCalculation 29 | case .centerTopFourth, .centerLeftFourth: 30 | calculation = WindowCalculationFactory.thirdFourthCalculation 31 | case .centerBottomFourth, .centerRightFourth: 32 | calculation = WindowCalculationFactory.lastFourthCalculation 33 | default: 34 | break 35 | } 36 | } else if last.action == .lastFourth { 37 | switch lastSubAction { 38 | case .leftFourth, .topFourth: 39 | calculation = WindowCalculationFactory.secondFourthCalculation 40 | default: 41 | break 42 | } 43 | } 44 | 45 | if let calculation = calculation { 46 | return calculation.calculateRect(params) 47 | } 48 | 49 | return orientationBasedRect(visibleFrameOfScreen) 50 | } 51 | 52 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 53 | var rect = visibleFrameOfScreen 54 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 55 | return RectResult(rect, subAction: .leftFourth) 56 | } 57 | 58 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 59 | var rect = visibleFrameOfScreen 60 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 61 | rect.origin.y = visibleFrameOfScreen.origin.y + visibleFrameOfScreen.height - rect.height 62 | return RectResult(rect, subAction: .topFourth) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/FirstThirdCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeftThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 7/26/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class FirstThirdCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | guard Defaults.subsequentExecutionMode.value != .none, 16 | let last = params.lastAction, let lastSubAction = last.subAction else { 17 | return orientationBasedRect(visibleFrameOfScreen) 18 | } 19 | 20 | var calculation: WindowCalculation? 21 | 22 | if last.action == .firstThird { 23 | switch lastSubAction { 24 | case .topThird, .leftThird: 25 | calculation = WindowCalculationFactory.centerThirdCalculation 26 | case .centerHorizontalThird, .centerVerticalThird: 27 | calculation = WindowCalculationFactory.lastThirdCalculation 28 | default: 29 | break 30 | } 31 | } else if last.action == .lastThird { 32 | switch lastSubAction { 33 | case .topThird, .leftThird: 34 | calculation = WindowCalculationFactory.centerThirdCalculation 35 | default: 36 | break 37 | } 38 | } 39 | 40 | if let calculation = calculation { 41 | return calculation.calculateRect(params) 42 | } 43 | 44 | return orientationBasedRect(visibleFrameOfScreen) 45 | } 46 | 47 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 48 | var rect = visibleFrameOfScreen 49 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 50 | return RectResult(rect, subAction: .leftThird) 51 | } 52 | 53 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 54 | var rect = visibleFrameOfScreen 55 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 56 | rect.origin.y = visibleFrameOfScreen.minY + visibleFrameOfScreen.height - rect.height 57 | return RectResult(rect, subAction: .topThird) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/FirstThreeFourthsCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstThreeFourthsCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Björn Orri Sæmundsson on 26.06.21. 6 | // Copyright © 2021 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class FirstThreeFourthsCalculation: WindowCalculation, OrientationAware { 12 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 13 | let visibleFrameOfScreen = params.visibleFrameOfScreen 14 | 15 | guard Defaults.subsequentExecutionMode.value != .none, 16 | let last = params.lastAction, let lastSubAction = last.subAction else { 17 | return orientationBasedRect(visibleFrameOfScreen) 18 | } 19 | 20 | if lastSubAction == .leftThreeFourths || lastSubAction == .topThreeFourths { 21 | return WindowCalculationFactory.lastThreeFourthsCalculation.orientationBasedRect(visibleFrameOfScreen) 22 | } 23 | 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 28 | var rect = visibleFrameOfScreen 29 | rect.size.width = floor(visibleFrameOfScreen.width * 3 / 4.0) 30 | return RectResult(rect, subAction: .leftThreeFourths) 31 | } 32 | 33 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 34 | var rect = visibleFrameOfScreen 35 | rect.size.height = floor(visibleFrameOfScreen.height * 3 / 4.0) 36 | rect.origin.y = visibleFrameOfScreen.origin.y + visibleFrameOfScreen.height - rect.height 37 | return RectResult(rect, subAction: .topThreeFourths) 38 | } 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/FirstTwoThirdsCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeftTwoThirdsCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 7/26/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class FirstTwoThirdsCalculation: WindowCalculation, OrientationAware { 12 | 13 | 14 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 15 | let visibleFrameOfScreen = params.visibleFrameOfScreen 16 | 17 | guard Defaults.subsequentExecutionMode.value != .none, 18 | let last = params.lastAction, let lastSubAction = last.subAction else { 19 | return orientationBasedRect(visibleFrameOfScreen) 20 | } 21 | 22 | if lastSubAction == .leftTwoThirds || lastSubAction == .topTwoThirds { 23 | return WindowCalculationFactory.lastTwoThirdsCalculation.orientationBasedRect(visibleFrameOfScreen) 24 | } 25 | 26 | return orientationBasedRect(visibleFrameOfScreen) 27 | } 28 | 29 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 30 | var rect = visibleFrameOfScreen 31 | rect.size.width = floor(visibleFrameOfScreen.width * 2 / 3.0) 32 | return RectResult(rect, subAction: .leftTwoThirds) 33 | } 34 | 35 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 36 | var rect = visibleFrameOfScreen 37 | rect.size.height = floor(visibleFrameOfScreen.height * 2 / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.origin.y + visibleFrameOfScreen.height - rect.height 39 | return RectResult(rect, subAction: .topTwoThirds) 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/GapCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GapCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 1/14/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class GapCalculation { 12 | 13 | static func applyGaps(_ rect: CGRect, dimension: Dimension = .both, sharedEdges: Edge = .none, gapSize: Float) -> CGRect { 14 | 15 | let cgGapSize = CGFloat(gapSize) 16 | let halfGapSize = cgGapSize / 2 17 | 18 | var withGaps = rect.insetBy( 19 | dx: dimension.contains(.horizontal) ? cgGapSize : 0, 20 | dy: dimension.contains(.vertical) ? cgGapSize : 0 21 | ) 22 | 23 | if dimension.contains(.horizontal) { 24 | if sharedEdges.contains(.left) { 25 | withGaps.origin.x -= halfGapSize 26 | withGaps.size.width += halfGapSize 27 | } 28 | 29 | if sharedEdges.contains(.right) { 30 | withGaps.size.width += halfGapSize 31 | } 32 | } 33 | 34 | 35 | if dimension.contains(.vertical) { 36 | if sharedEdges.contains(.bottom) { 37 | withGaps.origin.y -= halfGapSize 38 | withGaps.size.height += halfGapSize 39 | } 40 | 41 | if sharedEdges.contains(.top) { 42 | withGaps.size.height += halfGapSize 43 | } 44 | } 45 | 46 | return withGaps 47 | } 48 | } 49 | 50 | struct Dimension: OptionSet { 51 | let rawValue: Int 52 | 53 | static let horizontal = Dimension(rawValue: 1 << 0) 54 | static let vertical = Dimension(rawValue: 1 << 1) 55 | 56 | static let both: Dimension = [.horizontal, .vertical] 57 | static let none: Dimension = [] 58 | } 59 | 60 | struct Edge: OptionSet { 61 | let rawValue: Int 62 | 63 | static let left = Edge(rawValue: 1 << 0) 64 | static let right = Edge(rawValue: 1 << 1) 65 | static let top = Edge(rawValue: 1 << 2) 66 | static let bottom = Edge(rawValue: 1 << 3) 67 | 68 | static let all: Edge = [.left, .right, .top, .bottom] 69 | static let none: Edge = [] 70 | } 71 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/HorizontalThirdsRepeated.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HorizontalThirdsRepeated.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol HorizontalThirdsRepeated { 12 | func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? 13 | } 14 | 15 | extension HorizontalThirdsRepeated { 16 | func nextCalculation(subAction: SubWindowAction, direction: Direction) -> SimpleCalc? { 17 | 18 | if direction == .left { 19 | switch subAction { 20 | case .topLeftThird: 21 | return WindowCalculationFactory.bottomRightThirdCalculation.orientationBasedRect 22 | case .topRightThird: 23 | return WindowCalculationFactory.topLeftThirdCalculation.orientationBasedRect 24 | case .bottomLeftThird: 25 | return WindowCalculationFactory.topRightThirdCalculation.orientationBasedRect 26 | case .bottomRightThird: 27 | return WindowCalculationFactory.bottomLeftThirdCalculation.orientationBasedRect 28 | default: break 29 | } 30 | } 31 | 32 | else if direction == .right { 33 | switch subAction { 34 | case .topLeftThird: 35 | return WindowCalculationFactory.topRightThirdCalculation.orientationBasedRect 36 | case .topRightThird: 37 | return WindowCalculationFactory.bottomLeftThirdCalculation.orientationBasedRect 38 | case .bottomLeftThird: 39 | return WindowCalculationFactory.bottomRightThirdCalculation.orientationBasedRect 40 | case .bottomRightThird: 41 | return WindowCalculationFactory.topLeftThirdCalculation.orientationBasedRect 42 | default: break 43 | } 44 | } 45 | 46 | return nil 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/LastFourthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LastFourthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/16/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class LastFourthCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | guard Defaults.subsequentExecutionMode.value != .none, 16 | let last = params.lastAction, let lastSubAction = last.subAction else { 17 | return orientationBasedRect(visibleFrameOfScreen) 18 | } 19 | 20 | var calculation: WindowCalculation? 21 | if last.action == .lastFourth { 22 | switch lastSubAction { 23 | case .bottomFourth, .rightFourth: 24 | calculation = WindowCalculationFactory.thirdFourthCalculation 25 | case .centerBottomFourth, .centerRightFourth: 26 | calculation = WindowCalculationFactory.secondFourthCalculation 27 | case .centerTopFourth, .centerLeftFourth: 28 | calculation = WindowCalculationFactory.firstFourthCalculation 29 | default: 30 | break 31 | } 32 | } else if last.action == .firstFourth { 33 | switch lastSubAction { 34 | case .bottomFourth, .rightFourth: 35 | calculation = WindowCalculationFactory.thirdFourthCalculation 36 | default: 37 | break 38 | } 39 | } 40 | 41 | if let calculation = calculation { 42 | return calculation.calculateRect(params) 43 | } 44 | 45 | return orientationBasedRect(visibleFrameOfScreen) 46 | } 47 | 48 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 49 | var rect = visibleFrameOfScreen 50 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 51 | rect.origin.x = visibleFrameOfScreen.origin.x + visibleFrameOfScreen.width - rect.width 52 | return RectResult(rect, subAction: .rightFourth) 53 | } 54 | 55 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 56 | var rect = visibleFrameOfScreen 57 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 58 | return RectResult(rect, subAction: .bottomFourth) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/LastThirdCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RightThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 7/26/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class LastThirdCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, let lastSubAction = last.subAction else { 18 | return orientationBasedRect(visibleFrameOfScreen) 19 | } 20 | 21 | var calculation: WindowCalculation? 22 | 23 | if last.action == .lastThird { 24 | switch lastSubAction { 25 | case .bottomThird, .rightThird: 26 | calculation = WindowCalculationFactory.centerThirdCalculation 27 | case .centerHorizontalThird, .centerVerticalThird: 28 | calculation = WindowCalculationFactory.firstThirdCalculation 29 | default: 30 | break 31 | } 32 | } else if last.action == .firstThird { 33 | switch lastSubAction { 34 | case .bottomThird, .rightThird: 35 | calculation = WindowCalculationFactory.centerThirdCalculation 36 | default: 37 | break 38 | } 39 | } 40 | 41 | if let calculation = calculation { 42 | return calculation.calculateRect(params) 43 | } 44 | 45 | return orientationBasedRect(visibleFrameOfScreen) 46 | } 47 | 48 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 49 | var rect = visibleFrameOfScreen 50 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 51 | rect.origin.x = visibleFrameOfScreen.origin.x + visibleFrameOfScreen.width - rect.width 52 | return RectResult(rect, subAction: .rightThird) 53 | } 54 | 55 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 56 | var rect = visibleFrameOfScreen 57 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 58 | return RectResult(rect, subAction: .bottomThird) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/LastThreeFourthsCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LastThreeFourthsCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Björn Orri Sæmundsson on 26.06.21. 6 | // Copyright © 2021 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class LastThreeFourthsCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, let lastSubAction = last.subAction else { 18 | return orientationBasedRect(visibleFrameOfScreen) 19 | } 20 | 21 | if lastSubAction == .rightThreeFourths || lastSubAction == .bottomThreeFourths { 22 | return WindowCalculationFactory.firstThreeFourthsCalculation.orientationBasedRect(visibleFrameOfScreen) 23 | } 24 | 25 | return orientationBasedRect(visibleFrameOfScreen) 26 | } 27 | 28 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 29 | var rect = visibleFrameOfScreen 30 | rect.size.width = floor(visibleFrameOfScreen.width * 3 / 4.0) 31 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 32 | return RectResult(rect, subAction: .rightThreeFourths) 33 | } 34 | 35 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 36 | var rect = visibleFrameOfScreen 37 | rect.size.height = floor(visibleFrameOfScreen.height * 3 / 4.0) 38 | return RectResult(rect, subAction: .bottomThreeFourths) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/LastTwoThirdsCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RightTwoThirdsCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 7/26/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class LastTwoThirdsCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, let lastSubAction = last.subAction else { 18 | return orientationBasedRect(visibleFrameOfScreen) 19 | } 20 | 21 | if lastSubAction == .rightTwoThirds || lastSubAction == .bottomTwoThirds { 22 | return WindowCalculationFactory.firstTwoThirdsCalculation.orientationBasedRect(visibleFrameOfScreen) 23 | } 24 | 25 | return orientationBasedRect(visibleFrameOfScreen) 26 | } 27 | 28 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 29 | var rect = visibleFrameOfScreen 30 | rect.size.width = floor(visibleFrameOfScreen.width * 2 / 3.0) 31 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 32 | return RectResult(rect, subAction: .rightTwoThirds) 33 | } 34 | 35 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 36 | var rect = visibleFrameOfScreen 37 | rect.size.height = floor(visibleFrameOfScreen.height * 2 / 3.0) 38 | return RectResult(rect, subAction: .bottomTwoThirds) 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/LeftTodoCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeftTodoCalculation.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2023 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | final class LeftTodoCalculation: WindowCalculation { 11 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 12 | let visibleFrameOfScreen = params.visibleFrameOfScreen 13 | var calculatedWindowRect = visibleFrameOfScreen 14 | 15 | calculatedWindowRect.size.width = Defaults.todoSidebarWidth.cgFloat 16 | 17 | return RectResult(calculatedWindowRect, subAction: .leftTodo) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/LowerLeftCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LowerLeftCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class LowerLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { 16 | return calculateFirstRect(params) 17 | } 18 | 19 | return calculateRepeatedRect(params) 20 | } 21 | 22 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { 23 | let visibleFrameOfScreen = params.visibleFrameOfScreen 24 | 25 | var rect = visibleFrameOfScreen 26 | 27 | rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) 28 | 29 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 30 | 31 | return RectResult(rect) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/LowerRightCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LowerRightCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class LowerRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { 16 | return calculateFirstRect(params) 17 | } 18 | 19 | return calculateRepeatedRect(params) 20 | } 21 | 22 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { 23 | let visibleFrameOfScreen = params.visibleFrameOfScreen 24 | 25 | var rect = visibleFrameOfScreen 26 | 27 | rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) 28 | rect.origin.x = visibleFrameOfScreen.maxX - rect.width 29 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 30 | 31 | return RectResult(rect) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/MaximizeCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MaximizeCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class MaximizeCalculation: WindowCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | return RectResult(visibleFrameOfScreen) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/MaximizeHeightCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MaximizeHeightCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class MaximizeHeightCalculation: WindowCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | let visibleFrameOfScreen = params.visibleFrameOfScreen 16 | var maxHeightRect = params.window.rect 17 | maxHeightRect.origin.y = visibleFrameOfScreen.minY 18 | maxHeightRect.size.height = visibleFrameOfScreen.height 19 | return RectResult(maxHeightRect) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/MiddleCenterNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiddleCenterNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class MiddleCenterNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .middleCenterNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.minY + rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 40 | return RectResult(rect, subAction: .middleCenterNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY + rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | return RectResult(rect, subAction: .middleCenterNinth) 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/MiddleLeftNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiddleLeftNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class MiddleLeftNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .middleLeftNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.minY + rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX 40 | return RectResult(rect, subAction: .middleLeftNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY + rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .middleLeftNinth) 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/MiddleRightNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiddleRightNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class MiddleRightNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .middleRightNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.minY + rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 40 | return RectResult(rect, subAction: .middleRightNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.minY + rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 49 | return RectResult(rect, subAction: .middleRightNinth) 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/MoveUpDownCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoveUpDownCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 7/26/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class MoveUpDownCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | let visibleFrameOfScreen = params.visibleFrameOfScreen 16 | 17 | var calculatedWindowRect: CGRect 18 | 19 | if Defaults.resizeOnDirectionalMove.enabled { 20 | calculatedWindowRect = calculateRepeatedRect(params).rect 21 | } else { 22 | calculatedWindowRect = calculateGenericRect(params).rect 23 | } 24 | 25 | if Defaults.centeredDirectionalMove.enabled != false { 26 | calculatedWindowRect.origin.x = round((visibleFrameOfScreen.width - calculatedWindowRect.width) / 2.0) + visibleFrameOfScreen.minX 27 | } 28 | 29 | if params.window.rect.width >= visibleFrameOfScreen.width { 30 | calculatedWindowRect.size.width = visibleFrameOfScreen.width 31 | calculatedWindowRect.origin.x = visibleFrameOfScreen.minX 32 | } 33 | 34 | return RectResult(calculatedWindowRect) 35 | 36 | } 37 | 38 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { 39 | return calculateGenericRect(params, fraction: fraction) 40 | } 41 | 42 | func calculateGenericRect(_ params: RectCalculationParameters, fraction: Float? = nil) -> RectResult { 43 | let visibleFrameOfScreen = params.visibleFrameOfScreen 44 | 45 | var rect = params.window.rect 46 | if let requestedFraction = fraction { 47 | rect.size.height = floor(visibleFrameOfScreen.height * CGFloat(requestedFraction)) 48 | } 49 | 50 | if params.action == .moveUp { 51 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 52 | } else { 53 | rect.origin.y = visibleFrameOfScreen.minY 54 | } 55 | 56 | return RectResult(rect) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/OrientationAware.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrientationAware.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/23/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | typealias SimpleCalc = (_ visibleFrameOfScreen: CGRect) -> RectResult 12 | 13 | protocol OrientationAware { 14 | 15 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult 16 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult 17 | func orientationBasedRect(_ visibleFrameOfScreen: CGRect) -> RectResult 18 | 19 | } 20 | 21 | extension OrientationAware { 22 | func orientationBasedRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 23 | return visibleFrameOfScreen.isLandscape 24 | ? landscapeRect(visibleFrameOfScreen) 25 | : portraitRect(visibleFrameOfScreen) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/RepeatedExecutionsCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepeatedExecutionsCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 10/18/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol RepeatedExecutionsCalculation { 12 | 13 | func calculateFirstRect(_ params: RectCalculationParameters) -> RectResult 14 | 15 | func calculateRect(for cycleDivision: CycleSize, params: RectCalculationParameters) -> RectResult 16 | 17 | } 18 | 19 | extension RepeatedExecutionsCalculation { 20 | 21 | func calculateRepeatedRect(_ params: RectCalculationParameters) -> RectResult { 22 | 23 | guard let count = params.lastAction?.count, 24 | params.lastAction?.action == params.action 25 | else { 26 | return calculateFirstRect(params) 27 | } 28 | 29 | let useDefaultPositions = !Defaults.cycleSizesIsChanged.enabled 30 | let positions = useDefaultPositions ? CycleSize.defaultSizes : Defaults.selectedCycleSizes.value 31 | 32 | let sortedPositions = CycleSize.sortedSizes 33 | .filter { positions.contains($0) } 34 | 35 | let position = count % sortedPositions.count 36 | 37 | return calculateRect(for: sortedPositions[position], params: params) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/RepeatedExecutionsInThirdsCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepeatedExecutionsInThirdsCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Charlie Harding on 12/06/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol RepeatedExecutionsInThirdsCalculation: RepeatedExecutionsCalculation { 12 | 13 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult 14 | 15 | } 16 | 17 | extension RepeatedExecutionsInThirdsCalculation { 18 | 19 | func calculateFirstRect(_ params: RectCalculationParameters) -> RectResult { 20 | return calculateFractionalRect(params, fraction: 1 / 2.0) 21 | } 22 | 23 | func calculateRect(for cycleDivision: CycleSize, params: RectCalculationParameters) -> RectResult { 24 | let fraction = cycleDivision.fraction 25 | return calculateFractionalRect(params, fraction: fraction) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/RightTodoCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RightTodoCalculation.swift 3 | // Rectangle 4 | // 5 | // Copyright © 2023 Ryan Hanson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | final class RightTodoCalculation: WindowCalculation { 11 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 12 | let visibleFrameOfScreen = params.visibleFrameOfScreen 13 | var calculatedWindowRect = visibleFrameOfScreen 14 | 15 | calculatedWindowRect.origin.x = visibleFrameOfScreen.maxX - Defaults.todoSidebarWidth.cgFloat 16 | calculatedWindowRect.size.width = Defaults.todoSidebarWidth.cgFloat 17 | 18 | return RectResult(calculatedWindowRect, subAction: .rightTodo) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/SecondFourthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondFourthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/16/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class SecondFourthCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | let visibleFrameOfScreen = params.visibleFrameOfScreen 16 | 17 | guard Defaults.subsequentExecutionMode.value != .none, 18 | let last = params.lastAction, 19 | let lastSubAction = last.subAction, 20 | last.action == .secondFourth 21 | else { 22 | return orientationBasedRect(visibleFrameOfScreen) 23 | } 24 | 25 | let calc: SimpleCalc 26 | switch lastSubAction { 27 | case .centerLeftFourth: 28 | calc = WindowCalculationFactory.lastThreeFourthsCalculation.landscapeRect 29 | case .centerTopFourth: 30 | calc = WindowCalculationFactory.lastThreeFourthsCalculation.portraitRect 31 | case .rightThreeFourths: 32 | calc = WindowCalculationFactory.centerHalfCalculation.landscapeRect 33 | case .bottomThreeFourths: 34 | calc = WindowCalculationFactory.centerHalfCalculation.portraitRect 35 | default: 36 | calc = orientationBasedRect 37 | } 38 | 39 | return calc(visibleFrameOfScreen) 40 | } 41 | 42 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 43 | var rect = visibleFrameOfScreen 44 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 45 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 46 | return RectResult(rect, subAction: .centerLeftFourth) 47 | } 48 | 49 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 50 | var rect = visibleFrameOfScreen 51 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 52 | rect.origin.y = visibleFrameOfScreen.minY + visibleFrameOfScreen.height - (rect.height * 2.0) 53 | return RectResult(rect, subAction: .centerTopFourth) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/SpecifiedCalculation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class SpecifiedCalculation: WindowCalculation { 4 | 5 | private let specifiedHeight: CGFloat 6 | private let specifiedWidth: CGFloat 7 | 8 | override init() { 9 | specifiedHeight = CGFloat(Defaults.specifiedHeight.value) 10 | specifiedWidth = CGFloat(Defaults.specifiedWidth.value) 11 | } 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | let visibleFrameOfScreen = params.visibleFrameOfScreen 16 | var calculatedWindowRect = visibleFrameOfScreen 17 | 18 | // Resize 19 | calculatedWindowRect.size.height = specifiedHeight <= 1 20 | ? visibleFrameOfScreen.height * specifiedHeight 21 | : round(specifiedHeight) 22 | calculatedWindowRect.size.width = specifiedWidth <= 1 23 | ? visibleFrameOfScreen.width * specifiedWidth 24 | : min(visibleFrameOfScreen.width, round(specifiedWidth)) 25 | 26 | // Center 27 | calculatedWindowRect.origin.x = round((visibleFrameOfScreen.width - calculatedWindowRect.width) / 2.0) + visibleFrameOfScreen.minX 28 | calculatedWindowRect.origin.y = round((visibleFrameOfScreen.height - calculatedWindowRect.height) / 2.0) + visibleFrameOfScreen.minY 29 | 30 | return RectResult(calculatedWindowRect) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/ThirdFourthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThirdFourthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/16/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ThirdFourthCalculation: WindowCalculation, OrientationAware { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | let visibleFrameOfScreen = params.visibleFrameOfScreen 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction, 19 | last.action == .thirdFourth 20 | else { 21 | return orientationBasedRect(visibleFrameOfScreen) 22 | } 23 | 24 | let calc: SimpleCalc 25 | switch lastSubAction { 26 | case .centerRightFourth: 27 | calc = WindowCalculationFactory.firstThreeFourthsCalculation.landscapeRect 28 | case .centerBottomFourth: 29 | calc = WindowCalculationFactory.firstThreeFourthsCalculation.portraitRect 30 | case .leftThreeFourths: 31 | calc = WindowCalculationFactory.centerHalfCalculation.landscapeRect 32 | case .topThreeFourths: 33 | calc = WindowCalculationFactory.centerHalfCalculation.portraitRect 34 | default: 35 | calc = orientationBasedRect 36 | } 37 | 38 | return calc(visibleFrameOfScreen) 39 | } 40 | 41 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 42 | var rect = visibleFrameOfScreen 43 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 44 | rect.origin.x = visibleFrameOfScreen.minX + (rect.width * 2) 45 | return RectResult(rect, subAction: .centerRightFourth) 46 | } 47 | 48 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 49 | var rect = visibleFrameOfScreen 50 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 51 | rect.origin.y = visibleFrameOfScreen.minY + visibleFrameOfScreen.height - (rect.height * 3.0) 52 | return RectResult(rect, subAction: .centerBottomFourth) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopCenterLeftEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopCenterLeftEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopCenterLeftEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topCenterLeftEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 40 | return RectResult(rect, subAction: .topCenterLeftEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | return RectResult(rect, subAction: .topCenterLeftEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopCenterNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopCenterNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopCenterNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topCenterNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 40 | return RectResult(rect, subAction: .topCenterNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | return RectResult(rect, subAction: .topCenterNinth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopCenterRightEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopCenterRightEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopCenterRightEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topCenterRightEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + (2.0 * rect.width) 40 | return RectResult(rect, subAction: .topCenterRightEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - (visibleFrameOfScreen.height / 2.0) 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .topCenterRightEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopHalfCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopHalfCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopHalfCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { 16 | return calculateFirstRect(params) 17 | } 18 | 19 | return calculateRepeatedRect(params) 20 | } 21 | 22 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { 23 | let visibleFrameOfScreen = params.visibleFrameOfScreen 24 | 25 | var rect = visibleFrameOfScreen 26 | rect.size.height = floor(visibleFrameOfScreen.height * CGFloat(fraction)) 27 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 28 | return RectResult(rect) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopLeftEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopLeftEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopLeftEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topLeftEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX 40 | return RectResult(rect, subAction: .topLeftEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .topLeftEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopLeftNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopLeftNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopLeftNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topLeftNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX 40 | return RectResult(rect, subAction: .topLeftNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .topLeftNinth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopLeftSixthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopLeftSixthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/16/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopLeftSixthCalculation: WindowCalculation, OrientationAware, SixthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topLeftSixth 24 | && lastSubAction != .topLeftSixthLandscape 25 | && lastSubAction != .topLeftSixthPortrait { 26 | return orientationBasedRect(visibleFrameOfScreen) 27 | } 28 | 29 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 30 | return calculation(visibleFrameOfScreen) 31 | } 32 | 33 | return orientationBasedRect(visibleFrameOfScreen) 34 | } 35 | 36 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 37 | var rect = visibleFrameOfScreen 38 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 39 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 40 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 41 | return RectResult(rect, subAction: .topLeftSixthLandscape) 42 | } 43 | 44 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 45 | var rect = visibleFrameOfScreen 46 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 47 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 48 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 49 | return RectResult(rect, subAction: .topLeftSixthPortrait) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopLeftThirdCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopLeftThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopLeftThirdCalculation: WindowCalculation, OrientationAware, HorizontalThirdsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topLeftThird { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(2.0 * visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - visibleFrameOfScreen.height / 2.0 39 | rect.origin.x = visibleFrameOfScreen.minX 40 | return RectResult(rect, subAction: .topLeftThird) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(2.0 * visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - visibleFrameOfScreen.height / 3.0 48 | rect.origin.x = visibleFrameOfScreen.minX 49 | return RectResult(rect, subAction: .topLeftThird) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopRightEighthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopRightEighthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Johannes Trussell Rasch on 2022-02-18. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopRightEighthCalculation: WindowCalculation, OrientationAware, EighthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topRightEighth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 4.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + (3.0 * rect.width) 40 | return RectResult(rect, subAction: .topRightEighth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 4.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - (visibleFrameOfScreen.height / 2.0) 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | return RectResult(rect, subAction: .topRightEighth) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopRightNinthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopRightNinthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopRightNinthCalculation: WindowCalculation, OrientationAware, NinthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topRightNinth { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 40 | return RectResult(rect, subAction: .topRightNinth) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 46 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 48 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 49 | return RectResult(rect, subAction: .topRightNinth) 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopRightSixthCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopRightSixthCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/16/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopRightSixthCalculation: WindowCalculation, OrientationAware, SixthsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | guard Defaults.subsequentExecutionMode.value != .none, 16 | let last = params.lastAction, 17 | let lastSubAction = last.subAction 18 | else { 19 | return orientationBasedRect(visibleFrameOfScreen) 20 | } 21 | 22 | if last.action != .topRightSixth 23 | && lastSubAction != .topRightSixthLandscape 24 | && lastSubAction != .topRightSixthPortrait { 25 | return orientationBasedRect(visibleFrameOfScreen) 26 | } 27 | 28 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .left) { 29 | return calculation(visibleFrameOfScreen) 30 | } 31 | 32 | return orientationBasedRect(visibleFrameOfScreen) 33 | } 34 | 35 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 36 | var rect = visibleFrameOfScreen 37 | rect.size.width = floor(visibleFrameOfScreen.width / 3.0) 38 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 39 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - rect.width 40 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 41 | return RectResult(rect, subAction: .topRightSixthLandscape) 42 | } 43 | 44 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 45 | var rect = visibleFrameOfScreen 46 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 47 | rect.size.height = floor(visibleFrameOfScreen.height / 3.0) 48 | rect.origin.x = visibleFrameOfScreen.minX + rect.width 49 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 50 | return RectResult(rect, subAction: .topRightSixthPortrait) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/TopRightThirdCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopRightThirdCalculation.swift 3 | // Rectangle 4 | // 5 | // Created by Daniel Schultz on 1/2/22. 6 | // Copyright © 2022 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TopRightThirdCalculation: WindowCalculation, OrientationAware, HorizontalThirdsRepeated { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | let visibleFrameOfScreen = params.visibleFrameOfScreen 15 | 16 | guard Defaults.subsequentExecutionMode.value != .none, 17 | let last = params.lastAction, 18 | let lastSubAction = last.subAction 19 | else { 20 | return orientationBasedRect(visibleFrameOfScreen) 21 | } 22 | 23 | if last.action != .topRightThird { 24 | return orientationBasedRect(visibleFrameOfScreen) 25 | } 26 | 27 | if let calculation = self.nextCalculation(subAction: lastSubAction, direction: .right) { 28 | return calculation(visibleFrameOfScreen) 29 | } 30 | 31 | return orientationBasedRect(visibleFrameOfScreen) 32 | } 33 | 34 | func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 35 | var rect = visibleFrameOfScreen 36 | rect.size.width = floor(2.0 * visibleFrameOfScreen.width / 3.0) 37 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 38 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 39 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width / 3.0 40 | return RectResult(rect, subAction: .topRightThird) 41 | } 42 | 43 | func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { 44 | var rect = visibleFrameOfScreen 45 | rect.size.width = floor(visibleFrameOfScreen.width / 2.0) 46 | rect.size.height = floor(2.0 * visibleFrameOfScreen.height / 3.0) 47 | rect.origin.y = visibleFrameOfScreen.maxY - visibleFrameOfScreen.height / 3.0 48 | rect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width / 2.0 49 | return RectResult(rect, subAction: .topRightThird) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/UpperLeftCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpperLeftCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UpperLeftCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { 16 | return calculateFirstRect(params) 17 | } 18 | 19 | return calculateRepeatedRect(params) 20 | } 21 | 22 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { 23 | let visibleFrameOfScreen = params.visibleFrameOfScreen 24 | 25 | var rect = visibleFrameOfScreen 26 | 27 | rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) 28 | 29 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 30 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 31 | return RectResult(rect) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Rectangle/WindowCalculation/UpperRightCalculation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpperRightCalculation.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UpperRightCalculation: WindowCalculation, RepeatedExecutionsInThirdsCalculation { 12 | 13 | override func calculateRect(_ params: RectCalculationParameters) -> RectResult { 14 | 15 | if params.lastAction == nil || !Defaults.subsequentExecutionMode.resizes { 16 | return calculateFirstRect(params) 17 | } 18 | 19 | return calculateRepeatedRect(params) 20 | } 21 | 22 | func calculateFractionalRect(_ params: RectCalculationParameters, fraction: Float) -> RectResult { 23 | let visibleFrameOfScreen = params.visibleFrameOfScreen 24 | 25 | var rect = visibleFrameOfScreen 26 | 27 | rect.size.width = floor(visibleFrameOfScreen.width * CGFloat(fraction)) 28 | rect.origin.x = visibleFrameOfScreen.maxX - rect.width 29 | rect.size.height = floor(visibleFrameOfScreen.height / 2.0) 30 | rect.origin.y = visibleFrameOfScreen.maxY - rect.height 31 | return RectResult(rect) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Rectangle/WindowHistory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WindowHistory.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 9/6/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class WindowHistory { 12 | 13 | var restoreRects = [CGWindowID: CGRect]() // the last window frame that the user positioned 14 | 15 | var lastRectangleActions = [CGWindowID: RectangleAction]() // the last window frame that this app positioned 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Rectangle/WindowMover/BestEffortWindowMover.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BestEffortWindowMover.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/13/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | * After a window has been moved and resized, if the window could not be resized small enough to fit the intended size, then some of the window may appear off the screen. The BestEffortWindowMover will move the window so that it fits entirely on the screen. 13 | */ 14 | 15 | class BestEffortWindowMover: WindowMover { 16 | func moveWindowRect(_ windowRect: CGRect, frameOfScreen: CGRect, visibleFrameOfScreen: CGRect, frontmostWindowElement: AccessibilityElement?, action: WindowAction?) { 17 | guard let currentWindowRect: CGRect = frontmostWindowElement?.frame else { return } 18 | if action?.allowedToExtendOutsideCurrentScreenArea == true && !NSScreen.screensHaveSeparateSpaces { return } 19 | 20 | var adjustedWindowRect: CGRect = currentWindowRect 21 | 22 | if adjustedWindowRect.minX < visibleFrameOfScreen.minX { 23 | 24 | adjustedWindowRect.origin.x = visibleFrameOfScreen.minX 25 | 26 | } else if adjustedWindowRect.minX + adjustedWindowRect.width > visibleFrameOfScreen.minX + visibleFrameOfScreen.width { 27 | 28 | adjustedWindowRect.origin.x = visibleFrameOfScreen.minX + visibleFrameOfScreen.width - (adjustedWindowRect.width) - CGFloat(Defaults.gapSize.value) 29 | } 30 | 31 | adjustedWindowRect = adjustedWindowRect.screenFlipped 32 | if adjustedWindowRect.minY < visibleFrameOfScreen.minY { 33 | 34 | adjustedWindowRect.origin.y = visibleFrameOfScreen.minY 35 | 36 | } else if adjustedWindowRect.minY + adjustedWindowRect.height > visibleFrameOfScreen.minY + visibleFrameOfScreen.height { 37 | 38 | adjustedWindowRect.origin.y = visibleFrameOfScreen.minY + visibleFrameOfScreen.height - (adjustedWindowRect.height) - CGFloat(Defaults.gapSize.value) 39 | } 40 | 41 | adjustedWindowRect = adjustedWindowRect.screenFlipped 42 | if !currentWindowRect.equalTo(adjustedWindowRect) { 43 | frontmostWindowElement?.setFrame(adjustedWindowRect) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Rectangle/WindowMover/CenteringFixedSizedWindowMover.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CenteringFixedSizedWindowMover.swift 3 | // Rectangle 4 | // 5 | // Created by Ryan Hanson on 1/14/20. 6 | // Copyright © 2020 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | If the window is fixed size, center it in the proposed window area 13 | */ 14 | 15 | class CenteringFixedSizedWindowMover: WindowMover { 16 | 17 | func moveWindowRect(_ windowRect: CGRect, frameOfScreen: CGRect, visibleFrameOfScreen: CGRect, frontmostWindowElement: AccessibilityElement?, action: WindowAction?) { 18 | guard let currentWindowRect: CGRect = frontmostWindowElement?.frame else { return } 19 | 20 | var adjustedWindowRect: CGRect = currentWindowRect 21 | 22 | if currentWindowRect.size.width != windowRect.width { 23 | adjustedWindowRect.origin.x = round((windowRect.width - currentWindowRect.width) / 2.0) + windowRect.minX 24 | } 25 | 26 | if currentWindowRect.size.height != windowRect.height { 27 | adjustedWindowRect.origin.y = round((windowRect.height - currentWindowRect.height) / 2.0) + windowRect.minY 28 | } 29 | 30 | if !adjustedWindowRect.equalTo(currentWindowRect) { 31 | frontmostWindowElement?.setFrame(adjustedWindowRect) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Rectangle/WindowMover/QuantizedWindowMover.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuantizedWindowMover.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/13/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class QuantizedWindowMover: WindowMover { 12 | func moveWindowRect(_ windowRect: CGRect, frameOfScreen: CGRect, visibleFrameOfScreen: CGRect, frontmostWindowElement: AccessibilityElement?, action: WindowAction?) { 13 | guard var movedWindowRect: CGRect = frontmostWindowElement?.frame else { return } 14 | if !movedWindowRect.equalTo(windowRect) { 15 | var adjustedWindowRect: CGRect = windowRect 16 | while movedWindowRect.width > windowRect.width || movedWindowRect.height > windowRect.height { 17 | 18 | if movedWindowRect.width > windowRect.width { 19 | adjustedWindowRect.size.width -= 2 20 | } 21 | if movedWindowRect.height > windowRect.height { 22 | adjustedWindowRect.size.height -= 2 23 | } 24 | if adjustedWindowRect.width < windowRect.width * 0.85 || adjustedWindowRect.height < windowRect.height * 0.85 { 25 | break 26 | } 27 | frontmostWindowElement?.setFrame(adjustedWindowRect) 28 | if let frontMostRect = frontmostWindowElement?.frame { 29 | movedWindowRect = frontMostRect 30 | } 31 | } 32 | adjustedWindowRect.origin.x += floor((windowRect.size.width - (movedWindowRect.size.width)) / 2.0) 33 | adjustedWindowRect.origin.y += floor((windowRect.size.height - (movedWindowRect.size.height)) / 2.0) 34 | frontmostWindowElement?.setFrame(adjustedWindowRect) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Rectangle/WindowMover/StandardWindowMover.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StandardWindowMover.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/13/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class StandardWindowMover: WindowMover { 12 | func moveWindowRect(_ windowRect: CGRect, frameOfScreen: CGRect, visibleFrameOfScreen: CGRect, frontmostWindowElement: AccessibilityElement?, action: WindowAction?) { 13 | let previousWindowRect: CGRect? = frontmostWindowElement?.frame 14 | if previousWindowRect?.isNull == true { 15 | return 16 | } 17 | frontmostWindowElement?.setFrame(windowRect) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Rectangle/WindowMover/WindowMover.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WindowMover.swift 3 | // Rectangle, Ported from Spectacle 4 | // 5 | // Created by Ryan Hanson on 6/13/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol WindowMover { 12 | func moveWindowRect(_ windowRect: CGRect, frameOfScreen: CGRect, visibleFrameOfScreen: CGRect, frontmostWindowElement: AccessibilityElement?, action: WindowAction?) 13 | } 14 | -------------------------------------------------------------------------------- /RectangleLauncher/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RectangleLauncher 4 | // 5 | // Created by Ryan Hanson on 6/14/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | func applicationDidFinishLaunching(_ aNotification: Notification) { 15 | if #available(macOS 13, *) { 16 | terminate() 17 | return 18 | } 19 | let mainAppIdentifier = "com.knollsoft.Rectangle" 20 | let running = NSWorkspace.shared.runningApplications 21 | let isRunning = !running.filter({$0.bundleIdentifier == mainAppIdentifier}).isEmpty 22 | 23 | if isRunning { 24 | self.terminate() 25 | } else { 26 | let killNotification = Notification.Name("killLauncher") 27 | DistributedNotificationCenter.default().addObserver(self, 28 | selector: #selector(self.terminate), 29 | name: killNotification, 30 | object: mainAppIdentifier) 31 | let path = Bundle.main.bundlePath as NSString 32 | var components = path.pathComponents 33 | components.removeLast() 34 | components.removeLast() 35 | components.removeLast() 36 | components.append("MacOS") 37 | components.append("Rectangle") 38 | let newPath = NSString.path(withComponents: components) 39 | NSWorkspace.shared.launchApplication(newPath) 40 | } 41 | } 42 | 43 | @objc func terminate() { 44 | NSApp.terminate(nil) 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /RectangleLauncher/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /RectangleLauncher/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /RectangleLauncher/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSBackgroundOnly 26 | 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | NSHumanReadableCopyright 30 | Copyright © 2019 Ryan Hanson. All rights reserved. 31 | NSMainStoryboardFile 32 | Main 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /RectangleLauncher/RectangleLauncher.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /RectangleLauncher/RectangleLauncherRelease.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RectangleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RectangleTests/RectangleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RectangleTests.swift 3 | // RectangleTests 4 | // 5 | // Created by Ryan Hanson on 6/11/19. 6 | // Copyright © 2019 Ryan Hanson. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Rectangle 11 | 12 | class RectangleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | --------------------------------------------------------------------------------