From c41e12676cc0b2ab901351523f189699e6b6de12 Mon Sep 17 00:00:00 2001 From: Tom Grimwood-Taylor Date: Sat, 26 Jul 2025 12:09:54 +0100 Subject: [PATCH] =?UTF-8?q?Add=20=E2=80=9CCenter=20Three=20Fourths?= =?UTF-8?q?=E2=80=9D=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Rectangle.xcodeproj/project.pbxproj | 4 + .../Contents.json | 21 ++++ .../centerThreeFourthsTemplate.png | Bin 0 -> 1894 bytes .../centerTwoThirdsTemplate.png | Bin 233 -> 1749 bytes Rectangle/Base.lproj/Main.storyboard | 95 ++++++++++++++---- .../PrefsWindow/PrefsViewController.swift | 2 + Rectangle/WindowAction.swift | 18 +++- .../CenterThreeFourthsCalculation.swift | 36 +++++++ .../WindowCalculation/WindowCalculation.swift | 2 + Rectangle/mul.lproj/Main.xcstrings | 20 +++- 10 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 Rectangle/Assets.xcassets/WindowPositions/centerThreeFourthsTemplate.imageset/Contents.json create mode 100644 Rectangle/Assets.xcassets/WindowPositions/centerThreeFourthsTemplate.imageset/centerThreeFourthsTemplate.png create mode 100644 Rectangle/WindowCalculation/CenterThreeFourthsCalculation.swift diff --git a/Rectangle.xcodeproj/project.pbxproj b/Rectangle.xcodeproj/project.pbxproj index 7f0c0e60..bd87e200 100644 --- a/Rectangle.xcodeproj/project.pbxproj +++ b/Rectangle.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ 98FA9497235A2D7600F95C4F /* RepeatedExecutionsCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98FA9496235A2D7600F95C4F /* RepeatedExecutionsCalculation.swift */; }; 98FD7C5F2687BC14009E9DAF /* FirstThreeFourthsCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98FD7C5E2687BC14009E9DAF /* FirstThreeFourthsCalculation.swift */; }; 98FD7C612687BCB6009E9DAF /* LastThreeFourthsCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98FD7C602687BCB6009E9DAF /* LastThreeFourthsCalculation.swift */; }; + 9FF740232E34DE9000D22955 /* CenterThreeFourthsCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF740222E34DE9000D22955 /* CenterThreeFourthsCalculation.swift */; }; AA040C67290B2640003181D5 /* RunLoopThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA040C66290B2640003181D5 /* RunLoopThread.swift */; }; AA0AC000291C1B5E00D125D2 /* CGExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0ABFFF291C1B5E00D125D2 /* CGExtension.swift */; }; AA0AC002291C1B9100D125D2 /* AXExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0AC001291C1B9100D125D2 /* AXExtension.swift */; }; @@ -296,6 +297,7 @@ 98FA9496235A2D7600F95C4F /* RepeatedExecutionsCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatedExecutionsCalculation.swift; sourceTree = ""; }; 98FD7C5E2687BC14009E9DAF /* FirstThreeFourthsCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstThreeFourthsCalculation.swift; sourceTree = ""; }; 98FD7C602687BCB6009E9DAF /* LastThreeFourthsCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastThreeFourthsCalculation.swift; sourceTree = ""; }; + 9FF740222E34DE9000D22955 /* CenterThreeFourthsCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterThreeFourthsCalculation.swift; sourceTree = ""; }; AA040C66290B2640003181D5 /* RunLoopThread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunLoopThread.swift; sourceTree = ""; }; AA0ABFFF291C1B5E00D125D2 /* CGExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGExtension.swift; sourceTree = ""; }; AA0AC001291C1B9100D125D2 /* AXExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AXExtension.swift; sourceTree = ""; }; @@ -402,6 +404,7 @@ 982140E922B7DA3100ABFB3F /* WindowCalculation */ = { isa = PBXGroup; children = ( + 9FF740222E34DE9000D22955 /* CenterThreeFourthsCalculation.swift */, 74804F0A2E25521C009F1F7D /* CenterTwoThirdsCalculation.swift */, 98A009AA2512491300CFBF0C /* CenterHalfCalculation.swift */, 98A009AC2512498000CFBF0C /* FirstFourthCalculation.swift */, @@ -890,6 +893,7 @@ 9821402722B3888100ABFB3F /* ChangeSizeCalculation.swift in Sources */, 6490B39927BF97BB0056C220 /* TopCenterRightEighthCalculation.swift in Sources */, 98B3559823CE025700E410E0 /* CenteringFixedSizedWindowMover.swift in Sources */, + 9FF740232E34DE9000D22955 /* CenterThreeFourthsCalculation.swift in Sources */, 729E0A982AFF76B1006E2F48 /* CenterProminentlyCalculation.swift in Sources */, 9824704E22B189250037B409 /* QuantizedWindowMover.swift in Sources */, AA536C2729005DD000579AC6 /* TimeoutCache.swift in Sources */, diff --git a/Rectangle/Assets.xcassets/WindowPositions/centerThreeFourthsTemplate.imageset/Contents.json b/Rectangle/Assets.xcassets/WindowPositions/centerThreeFourthsTemplate.imageset/Contents.json new file mode 100644 index 00000000..d705533e --- /dev/null +++ b/Rectangle/Assets.xcassets/WindowPositions/centerThreeFourthsTemplate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "centerThreeFourthsTemplate.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Rectangle/Assets.xcassets/WindowPositions/centerThreeFourthsTemplate.imageset/centerThreeFourthsTemplate.png b/Rectangle/Assets.xcassets/WindowPositions/centerThreeFourthsTemplate.imageset/centerThreeFourthsTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..92db77a42d5994eae09765f94b749996b5748946 GIT binary patch literal 1894 zcmb7FeM}o=7{5761~~WS+?a-3u1s*yUO8x4Z#@=hkpe40V6JSD^=RL=C+%IiyOsl; zF6cxUpv*)QqGmG8bUK(}(LWXt$NYXA->bUla@Po$s{`Ac)4sR5(SaV-xA-f_mD+ zRRg^g3_%=0EE}Z|?XCeLS`B;*OJO(qk7Fp~p|CARhrtoD0zYGK;6ZsqNrk(i#%=as z8*}0<5(xtYfk>lLFu)3=L}5x?628l3977d~SVLh>M=5IMcz_!9M!f;ciAOEG$4i#k z9*sJ~6@~dlF+}2cU0t2NE>q9(K0L#0Hsb~YCkP#6=!AM!q$M3IXyp)U3>y&KJQETb zjz#5|bTtqr&`X&=}GSfA*puxR`lU<>1s?a`zZyIf!( zEO*FemVuHG%DxKZoWUq8o4{vnQ#mgL4~=xd?)K0kZKFk)DPbUtIzzUOFjZt2$SgCN znVn|HAPs8a3ZL^Z-unMy%N>ncAm8C2?TjFDe7#~)+q@jqsw;IFV3hurJ6z_|Zn-)N z3-O#M>;@jCK&Ag>RN%Z~9nFJWA3R_R%k_F0s6#ypKOM%$0v;t$LT}Qm$1&|y=Laz2 z9o3o0tf}hodGk@a?ymW$=>N?}^_n-IS!ze~vEcF)n3Bta&kCp&SI1hO^_>u#ui3qabIR-ZCoF+w()Q)h+$Hms%rA^0^i!50D))$>I&DB?z zX=8#hD{PvMrCP~2l-cz1$XHcl%(l->d(m&Cm4B}Cr#TZEyB1X+JlNHCT6lBW@`1C& zhAWd1-|!onn$&l@$Jb?dU#irt9~Hhhhp+$T{4lfc)n`6A+0xS`Hll|KoiJhPIv16=6rypT@r3H>BCcZKBGqr@S(fKbcjT zptP<-+WEWq$i}`UR<3JRQD@DPHD6yH@9NI~@M>V;)*F`( zJuul3xb~Iv-m3Pu=%mixz3d0WeOGq0kD`Yk-X4zI(0^g=j}N|fY(Zz0?^agJiBI7_ zEG}fP6c8j~g?ueQI$vK6jpjmIZiT(s?Qywoo;M&nHAVZi&F8v1o=A-tE}mN6==$>7 pW4jA+tIX_n~F(p4K zRj(qq04UGEU}IlVkeHmETB4AYnx2_wtMq>NekFy>6kDZmQ(pt$0_W6>OpmIf)Zi+= zkmRcDWXlvKdpj8?tx|+Ru%5+Il5l!Br*G%pkA7bApO5ePY3C37QvBx@qEO2Om73hX-H{1l+$ zjKKyXq&%IS1B&v~GIM}(#X;^)3ZAacW`>q#X1Ydt5F3DzW(CxnnO5nNSOQdKWME{f zYhbQxWENs*U}a)vWn!*vU}$9k2@SAnU|vW`EJ-A!9+aXLY=Am^eXTq)i%as0D#1dK zWFDMa2;o7vIho+}t)KvoWvj$wP$1bV6%^&ClqRR9fV~AY4kBBepH@`dCgS#=KGb#kpj-!Yo{c_+2W-F! zkg_RIA4Ck8{ek6(KD;0Sa^OW#Y96rsDFPNWSLQhUWnf@z$^=FYMwBoxXiO}fcwXBj zP~^D1amF8s-Xz6HpF>W@Qj3qcNph-Y_88qbWAoYhtn~WtX_rz~=ZLDB)#?7=zM+-9 zv}4Men?lmdp6`(~oqT5J+-ke;)$c9uR_onx)^s&pRl8uBaaDoMgL(Y=J7m{eWGwEJ zd!^j9u=mUvpTLWD?>9z%yq0}+Rn5Ej?1(-|t{Yjc?V`TtIs7^N*QT0um$yVuNlA(P z;d5j0Y1Y>FI<`?y#Jh!E|80xlXLUb#Pvwo@l)^1NQy(BvLwu7f=aXdl{F## zo;}@ilP4;rfBl~y-&?VNf5ctMhFR&CYu+;*H+!<(=kx0mCZCT7d}W#bGU(qcMV`8j zeW91E_z!Gld>*cHIOx6dip{mbCHF#Q(O(Yjl}Duc7YBeIx*fm;}a85w5HkpK!>db&7a{$1;%rY<>kYHcx~LxZWLqrOA7zvEam7(6<3RM869x{x@(gM>SkwibibO%R Nk*BMl%Q~loCIG8JTE74Q delta 206 zcmcc0`;u{jayhvY?&J)W+9F6*2UngAZ6Ix7GG diff --git a/Rectangle/Base.lproj/Main.storyboard b/Rectangle/Base.lproj/Main.storyboard index 02964244..4c95ce0e 100644 --- a/Rectangle/Base.lproj/Main.storyboard +++ b/Rectangle/Base.lproj/Main.storyboard @@ -319,14 +319,14 @@ - + - + - + @@ -1311,10 +1311,10 @@ - + - + @@ -1340,13 +1340,13 @@ - + - + - + @@ -1395,7 +1395,7 @@ - + @@ -1444,7 +1444,7 @@ - + @@ -1493,7 +1493,7 @@ - + @@ -1542,7 +1542,7 @@ - + @@ -1591,7 +1591,7 @@ - + @@ -1640,14 +1640,14 @@ - + - + @@ -1696,7 +1696,7 @@ - + @@ -1745,7 +1745,7 @@ - + @@ -1794,7 +1794,7 @@ - + @@ -1843,7 +1843,7 @@ - + @@ -1891,6 +1891,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1955,6 +2004,7 @@ + @@ -1970,10 +2020,11 @@ + - + @@ -2550,6 +2601,7 @@ + @@ -4348,6 +4400,7 @@ DQ + diff --git a/Rectangle/PrefsWindow/PrefsViewController.swift b/Rectangle/PrefsWindow/PrefsViewController.swift index bdf92001..04ce8d40 100644 --- a/Rectangle/PrefsWindow/PrefsViewController.swift +++ b/Rectangle/PrefsWindow/PrefsViewController.swift @@ -55,6 +55,7 @@ class PrefsViewController: NSViewController { @IBOutlet weak var thirdFourthShortcutView: MASShortcutView! @IBOutlet weak var lastFourthShortcutView: MASShortcutView! @IBOutlet weak var firstThreeFourthsShortcutView: MASShortcutView! + @IBOutlet weak var centerThreeFourthsShortcutView: MASShortcutView! @IBOutlet weak var lastThreeFourthsShortcutView: MASShortcutView! @IBOutlet weak var topLeftSixthShortcutView: MASShortcutView! @@ -105,6 +106,7 @@ class PrefsViewController: NSViewController { .thirdFourth: thirdFourthShortcutView, .lastFourth: lastFourthShortcutView, .firstThreeFourths: firstThreeFourthsShortcutView, + .centerThreeFourths: centerThreeFourthsShortcutView, .lastThreeFourths: lastThreeFourthsShortcutView, .topLeftSixth: topLeftSixthShortcutView, .topCenterSixth: topCenterSixthShortcutView, diff --git a/Rectangle/WindowAction.swift b/Rectangle/WindowAction.swift index 7e70c64d..e0ab7397 100644 --- a/Rectangle/WindowAction.swift +++ b/Rectangle/WindowAction.swift @@ -97,7 +97,8 @@ enum WindowAction: Int, Codable { smallerWidth = 81, largerHeight = 82, smallerHeight = 83, - centerTwoThirds = 84 + centerTwoThirds = 84, + centerThreeFourths = 85 // Order matters here - it's used in the menu static let active = [leftHalf, rightHalf, centerHalf, topHalf, bottomHalf, @@ -107,7 +108,7 @@ enum WindowAction: Int, Codable { center, centerProminently, restore, nextDisplay, previousDisplay, moveLeft, moveRight, moveUp, moveDown, - firstFourth, secondFourth, thirdFourth, lastFourth, firstThreeFourths, lastThreeFourths, + firstFourth, secondFourth, thirdFourth, lastFourth, firstThreeFourths, centerThreeFourths, lastThreeFourths, topLeftSixth, topCenterSixth, topRightSixth, bottomLeftSixth, bottomCenterSixth, bottomRightSixth, specified, reverseAll, topLeftNinth, topCenterNinth, topRightNinth, @@ -188,6 +189,7 @@ enum WindowAction: Int, Codable { case .thirdFourth: return "thirdFourth" case .lastFourth: return "lastFourth" case .firstThreeFourths: return "firstThreeFourths" + case .centerThreeFourths: return "centerThreeFourths" case .lastThreeFourths: return "lastThreeFourths" case .topLeftSixth: return "topLeftSixth" case .topCenterSixth: return "topCenterSixth" @@ -343,6 +345,9 @@ enum WindowAction: Int, Codable { case .firstThreeFourths: key = "T9Z-QF-gwc.title" value = "First Three Fourths" + case .centerThreeFourths: + key = "Vph-Z0-euH.title" + value = "Center Three Fourths" case .lastThreeFourths: key = "nwX-h6-fwm.title" value = "Last Three Fourths" @@ -509,6 +514,7 @@ enum WindowAction: Int, Codable { case .thirdFourth: return NSImage(imageLiteralResourceName: "centerRightFourthTemplate") case .lastFourth: return NSImage(imageLiteralResourceName: "rightFourthTemplate") case .firstThreeFourths: return NSImage(imageLiteralResourceName: "firstThreeFourthsTemplate") + case .centerThreeFourths: return NSImage(imageLiteralResourceName: "centerThreeFourthsTemplate") case .lastThreeFourths: return NSImage(imageLiteralResourceName: "lastThreeFourthsTemplate") case .topLeftSixth: return NSImage(imageLiteralResourceName: "topLeftSixthTemplate") case .topCenterSixth: return NSImage(imageLiteralResourceName: "topCenterSixthTemplate") @@ -581,7 +587,7 @@ enum WindowAction: Int, Codable { var gapsApplicable: Dimension { switch self { case .leftHalf, .rightHalf, .bottomHalf, .topHalf, .centerHalf, .bottomLeft, .bottomRight, .topLeft, .topRight, .firstThird, .firstTwoThirds, .centerThird, .centerTwoThirds, .lastTwoThirds, .lastThird, - .firstFourth, .secondFourth, .thirdFourth, .lastFourth, .firstThreeFourths, .lastThreeFourths, .topLeftSixth, .topCenterSixth, .topRightSixth, .bottomLeftSixth, .bottomCenterSixth, .bottomRightSixth, + .firstFourth, .secondFourth, .thirdFourth, .lastFourth, .firstThreeFourths, .centerThreeFourths, .lastThreeFourths, .topLeftSixth, .topCenterSixth, .topRightSixth, .bottomLeftSixth, .bottomCenterSixth, .bottomRightSixth, .topLeftNinth, .topCenterNinth, .topRightNinth, .middleLeftNinth, .middleCenterNinth, .middleRightNinth, .bottomLeftNinth, .bottomCenterNinth, .bottomRightNinth, .topLeftThird, .topRightThird, .bottomLeftThird, .bottomRightThird, .topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth, @@ -605,7 +611,7 @@ enum WindowAction: Int, Codable { var category: WindowActionCategory? { // used to specify a submenu switch self { - case .firstFourth, .secondFourth, .thirdFourth, .lastFourth, .firstThreeFourths, .lastThreeFourths: return .fourths + case .firstFourth, .secondFourth, .thirdFourth, .lastFourth, .firstThreeFourths, .centerThreeFourths, .lastThreeFourths: return .fourths case .topLeftSixth, .topCenterSixth, .topRightSixth, .bottomLeftSixth, .bottomCenterSixth, .bottomRightSixth: return .sixths case .moveUp, .moveDown, .moveLeft, .moveRight: return .move default: return nil @@ -652,6 +658,8 @@ enum SubWindowAction { bottomThreeFourths, leftThreeFourths, topThreeFourths, + centerVerticalThreeFourths, + centerHorizontalThreeFourths, centerVerticalHalf, centerHorizontalHalf, @@ -733,6 +741,8 @@ enum SubWindowAction { case .bottomThreeFourths: return .top case .leftThreeFourths: return .right case .topThreeFourths: return .bottom + case .centerVerticalThreeFourths: return [.right, .left] + case .centerHorizontalThreeFourths: return [.top, .bottom] case .centerVerticalHalf: return [.right, .left] case .centerHorizontalHalf: return [.top, .bottom] case .topLeftSixthLandscape: return [.right, .bottom] diff --git a/Rectangle/WindowCalculation/CenterThreeFourthsCalculation.swift b/Rectangle/WindowCalculation/CenterThreeFourthsCalculation.swift new file mode 100644 index 00000000..4820a341 --- /dev/null +++ b/Rectangle/WindowCalculation/CenterThreeFourthsCalculation.swift @@ -0,0 +1,36 @@ +// +// CenterThreeFourthsCalculation.swift +// Rectangle +// +// Created by Tom Grimwood-Taylor on 26/07/2025. +// Copyright © 2025 Ryan Hanson. All rights reserved. +// + +import Foundation + +class CenterThreeFourthsCalculation: WindowCalculation, OrientationAware { + + override func calculateRect(_ params: RectCalculationParameters) -> RectResult { + let visibleFrameOfScreen = params.visibleFrameOfScreen + return orientationBasedRect(visibleFrameOfScreen) + } + + func landscapeRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.origin.x = visibleFrameOfScreen.minX + floor(visibleFrameOfScreen.width / 4.0) / 2 + rect.origin.y = visibleFrameOfScreen.minY + rect.size.width = visibleFrameOfScreen.width / 4.0 * 3 + rect.size.height = visibleFrameOfScreen.height + return RectResult(rect, subAction: .centerVerticalThreeFourths) + } + + func portraitRect(_ visibleFrameOfScreen: CGRect) -> RectResult { + var rect = visibleFrameOfScreen + rect.origin.x = visibleFrameOfScreen.minX + rect.origin.y = visibleFrameOfScreen.minY + floor(visibleFrameOfScreen.height / 4.0) / 2 + rect.size.width = visibleFrameOfScreen.width + rect.size.height = visibleFrameOfScreen.height / 4.0 * 3 + return RectResult(rect, subAction: .centerHorizontalThreeFourths) + } +} + diff --git a/Rectangle/WindowCalculation/WindowCalculation.swift b/Rectangle/WindowCalculation/WindowCalculation.swift index d739c2c2..e410e266 100644 --- a/Rectangle/WindowCalculation/WindowCalculation.swift +++ b/Rectangle/WindowCalculation/WindowCalculation.swift @@ -153,6 +153,7 @@ class WindowCalculationFactory { static let thirdFourthCalculation = ThirdFourthCalculation() static let lastFourthCalculation = LastFourthCalculation() static let firstThreeFourthsCalculation = FirstThreeFourthsCalculation() + static let centerThreeFourthsCalculation = CenterThreeFourthsCalculation() static let lastThreeFourthsCalculation = LastThreeFourthsCalculation() static let topLeftSixthCalculation = TopLeftSixthCalculation() static let topCenterSixthCalculation = TopCenterSixthCalculation() @@ -223,6 +224,7 @@ class WindowCalculationFactory { .thirdFourth: thirdFourthCalculation, .lastFourth: lastFourthCalculation, .firstThreeFourths: firstThreeFourthsCalculation, + .centerThreeFourths: centerThreeFourthsCalculation, .lastThreeFourths: lastThreeFourthsCalculation, .topLeftSixth: topLeftSixthCalculation, .topCenterSixth: topCenterSixthCalculation, diff --git a/Rectangle/mul.lproj/Main.xcstrings b/Rectangle/mul.lproj/Main.xcstrings index 1419a68e..a2a95cde 100644 --- a/Rectangle/mul.lproj/Main.xcstrings +++ b/Rectangle/mul.lproj/Main.xcstrings @@ -45004,6 +45004,24 @@ } } }, + "Vph-Z0-euH.title" : { + "comment" : "Class = \"NSTextFieldCell\"; title = \"Center Three Fourths\"; ObjectID = \"Vph-Z0-euH\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Center Three Fourths" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Centre Three Quarters" + } + } + } + }, "w0m-vy-SC9.title" : { "comment" : "Class = \"NSMenu\"; title = \"Ligatures\"; ObjectID = \"w0m-vy-SC9\";", "extractionState" : "manual", @@ -49908,4 +49926,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file