Skip to content

Commit

Permalink
Merge pull request #1370 from isaacy2012/feat/#1369-double-or-half-wi…
Browse files Browse the repository at this point in the history
…dth-or-height-of-window

Double/Halve the Width/Height of a window
  • Loading branch information
rxhanson committed Apr 26, 2024
2 parents 67ae3d5 + 26117a1 commit d326390
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 20 deletions.
8 changes: 8 additions & 0 deletions Rectangle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@
AA69F83C29909A95001A81AF /* RightTodoCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69F83B29909A95001A81AF /* RightTodoCalculation.swift */; };
AA69F8402992DCB1001A81AF /* LeftTodoCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69F83F2992DCB1001A81AF /* LeftTodoCalculation.swift */; };
AAADE1AF28CBAB0000036331 /* WindowUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAADE1AE28CBAB0000036331 /* WindowUtil.swift */; };
B4521F932BD7CEFB00FD43CC /* ChangeWindowDimensionCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4521F922BD7CEFB00FD43CC /* ChangeWindowDimensionCalculation.swift */; };
B4780A322BD4C75900732B9E /* HalfOrDoubleDimensionCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4780A312BD4C75900732B9E /* HalfOrDoubleDimensionCalculation.swift */; };
D0423D8327A8D31D008A4894 /* HorizontalThirdsRepeated.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0423D8227A8D31D008A4894 /* HorizontalThirdsRepeated.swift */; };
D04CE3002781794E00BD47B3 /* TopLeftNinthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CE2FF2781794E00BD47B3 /* TopLeftNinthCalculation.swift */; };
D04CE30227817A6100BD47B3 /* TopCenterNinthCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CE30127817A6100BD47B3 /* TopCenterNinthCalculation.swift */; };
Expand Down Expand Up @@ -298,6 +300,8 @@
AA69F83B29909A95001A81AF /* RightTodoCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RightTodoCalculation.swift; sourceTree = "<group>"; };
AA69F83F2992DCB1001A81AF /* LeftTodoCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftTodoCalculation.swift; sourceTree = "<group>"; };
AAADE1AE28CBAB0000036331 /* WindowUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowUtil.swift; sourceTree = "<group>"; };
B4521F922BD7CEFB00FD43CC /* ChangeWindowDimensionCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeWindowDimensionCalculation.swift; sourceTree = "<group>"; };
B4780A312BD4C75900732B9E /* HalfOrDoubleDimensionCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HalfOrDoubleDimensionCalculation.swift; sourceTree = "<group>"; };
D0423D8227A8D31D008A4894 /* HorizontalThirdsRepeated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalThirdsRepeated.swift; sourceTree = "<group>"; };
D04CE2FF2781794E00BD47B3 /* TopLeftNinthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopLeftNinthCalculation.swift; sourceTree = "<group>"; };
D04CE30127817A6100BD47B3 /* TopCenterNinthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopCenterNinthCalculation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -411,6 +415,7 @@
729E0A972AFF76B1006E2F48 /* CenterProminentlyCalculation.swift */,
9821402422B3887200ABFB3F /* MaximizeCalculation.swift */,
9821402622B3888100ABFB3F /* ChangeSizeCalculation.swift */,
B4780A312BD4C75900732B9E /* HalfOrDoubleDimensionCalculation.swift */,
9821402822B3889100ABFB3F /* LowerLeftCalculation.swift */,
9821402A22B388A000ABFB3F /* LowerRightCalculation.swift */,
9821403222B38A1B00ABFB3F /* UpperLeftCalculation.swift */,
Expand Down Expand Up @@ -457,6 +462,7 @@
6490B3A027BF98C70056C220 /* BottomRightEighthCalculation.swift */,
AA69F83B29909A95001A81AF /* RightTodoCalculation.swift */,
AA69F83F2992DCB1001A81AF /* LeftTodoCalculation.swift */,
B4521F922BD7CEFB00FD43CC /* ChangeWindowDimensionCalculation.swift */,
);
path = WindowCalculation;
sourceTree = "<group>";
Expand Down Expand Up @@ -905,6 +911,7 @@
9818E01228B59B64004AA524 /* ThirdsCompoundCalculation.swift in Sources */,
98C27561231FFA5F009B9292 /* SnappingManager.swift in Sources */,
9824703722B0F3200037B409 /* WindowAction.swift in Sources */,
B4521F932BD7CEFB00FD43CC /* ChangeWindowDimensionCalculation.swift in Sources */,
9821402922B3889100ABFB3F /* LowerLeftCalculation.swift in Sources */,
9821402122B3884600ABFB3F /* BottomHalfCalculation.swift in Sources */,
98910B42231476B30066EC23 /* PrefsViewController.swift in Sources */,
Expand All @@ -921,6 +928,7 @@
98A009AD2512498000CFBF0C /* FirstFourthCalculation.swift in Sources */,
AA4DA2FB28FDC94A00355CEB /* DispatchTimeExtension.swift in Sources */,
98D4B6C525B6256C009C7BF6 /* TodoManager.swift in Sources */,
B4780A322BD4C75900732B9E /* HalfOrDoubleDimensionCalculation.swift in Sources */,
AA69F83C29909A95001A81AF /* RightTodoCalculation.swift in Sources */,
AA49DD1129B8C1B100690E13 /* TitleBarManager.swift in Sources */,
9824700D22AF9B7D0037B409 /* AppDelegate.swift in Sources */,
Expand Down
41 changes: 40 additions & 1 deletion Rectangle/WindowAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@ enum WindowAction: Int, Codable {
leftTodo = 68,
rightTodo = 69,
cascadeActiveApp = 70,
centerProminently = 71
centerProminently = 71,
doubleHeightUp = 72,
doubleHeightDown = 73,
doubleWidthLeft = 74,
doubleWidthRight = 75,
halveHeightUp = 76,
halveHeightDown = 77,
halveWidthLeft = 78,
halveWidthRight = 79

// Order matters here - it's used in the menu
static let active = [leftHalf, rightHalf, centerHalf, topHalf, bottomHalf,
Expand All @@ -102,6 +110,8 @@ enum WindowAction: Int, Codable {
topLeftThird, topRightThird, bottomLeftThird, bottomRightThird,
topLeftEighth, topCenterLeftEighth, topCenterRightEighth, topRightEighth,
bottomLeftEighth, bottomCenterLeftEighth, bottomCenterRightEighth, bottomRightEighth,
doubleHeightUp, doubleHeightDown, doubleWidthLeft, doubleWidthRight,
halveHeightUp, halveHeightDown, halveWidthLeft, halveWidthRight,
tileAll, cascadeAll,
leftTodo, rightTodo,
cascadeActiveApp
Expand Down Expand Up @@ -201,6 +211,14 @@ enum WindowAction: Int, Codable {
case .bottomCenterLeftEighth: return "bottomCenterLeftEighth"
case .bottomCenterRightEighth: return "bottomCenterRightEighth"
case .bottomRightEighth: return "bottomRightEighth"
case .doubleHeightUp: return "doubleHeightUp"
case .doubleHeightDown: return "doubleHeightDown"
case .doubleWidthLeft: return "doubleWidthLeft"
case .doubleWidthRight: return "doubleWidthRight"
case .halveHeightUp: return "halveHeightUp"
case .halveHeightDown: return "halveHeightDown"
case .halveWidthLeft: return "halveWidthLeft"
case .halveWidthRight: return "halveWidthRight"
case .tileAll: return "tileAll"
case .cascadeAll: return "cascadeAll"
case .leftTodo: return "leftTodo"
Expand Down Expand Up @@ -339,6 +357,8 @@ enum WindowAction: Int, Codable {
case .topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth,
.bottomLeftEighth, .bottomCenterLeftEighth, .bottomCenterRightEighth, .bottomRightEighth:
return nil
case .doubleHeightUp, .doubleHeightDown, .doubleWidthLeft, .doubleWidthRight, .halveHeightUp, .halveHeightDown, .halveWidthLeft, .halveWidthRight:
return nil
case .specified, .reverseAll, .tileAll, .cascadeAll, .leftTodo, .rightTodo, .cascadeActiveApp:
return nil
case .centerProminently:
Expand Down Expand Up @@ -367,6 +387,15 @@ enum WindowAction: Int, Codable {
}
}

var allowedToExtendOutsideCurrentScreenArea: Bool {
switch self {
case .doubleHeightUp, .doubleHeightDown, .doubleWidthLeft, .doubleWidthRight:
return true
default:
return false
}
}

var isDragSnappable: Bool {
switch self {
case .restore, .previousDisplay, .nextDisplay, .moveUp, .moveDown, .moveLeft, .moveRight, .specified, .reverseAll, .tileAll, .cascadeAll, .smaller, .larger, .cascadeActiveApp,
Expand Down Expand Up @@ -493,6 +522,14 @@ enum WindowAction: Int, Codable {
case .bottomCenterLeftEighth: return NSImage()
case .bottomCenterRightEighth: return NSImage()
case .bottomRightEighth: return NSImage()
case .doubleHeightUp: return NSImage()
case .doubleHeightDown: return NSImage()
case .doubleWidthLeft: return NSImage()
case .doubleWidthRight: return NSImage()
case .halveHeightUp: return NSImage()
case .halveHeightDown: return NSImage()
case .halveWidthLeft: return NSImage()
case .halveWidthRight: return NSImage()
case .specified, .reverseAll: return NSImage()
case .tileAll: return NSImage()
case .cascadeAll: return NSImage()
Expand Down Expand Up @@ -530,6 +567,8 @@ enum WindowAction: Int, Codable {
.topLeftThird, .topRightThird, .bottomLeftThird, .bottomRightThird,
.topLeftEighth, .topCenterLeftEighth, .topCenterRightEighth, .topRightEighth,
.bottomLeftEighth, .bottomCenterLeftEighth, .bottomCenterRightEighth, .bottomRightEighth,
.doubleHeightUp, .doubleHeightDown, .doubleWidthLeft, .doubleWidthRight,
.halveHeightUp, .halveHeightDown, .halveWidthLeft, .halveWidthRight,
.leftTodo, .rightTodo:
return .both
case .moveUp, .moveDown:
Expand Down
20 changes: 1 addition & 19 deletions Rectangle/WindowCalculation/ChangeSizeCalculation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,13 @@

import Foundation

class ChangeSizeCalculation: WindowCalculation {
class ChangeSizeCalculation: WindowCalculation, ChangeWindowDimensionCalculation {

let minimumWindowWidth: CGFloat
let minimumWindowHeight: CGFloat
let screenEdgeGapSize: CGFloat
let sizeOffsetAbs: CGFloat
let curtainChangeSize = Defaults.curtainChangeSize.enabled != false

override init() {
let defaultHeight = Defaults.minimumWindowHeight.value
minimumWindowHeight = (defaultHeight <= 0 || defaultHeight > 1)
? 0.25
: CGFloat(defaultHeight)

let defaultWidth = Defaults.minimumWindowWidth.value
minimumWindowWidth = (defaultWidth <= 0 || defaultWidth > 1)
? 0.25
: CGFloat(defaultWidth)

let windowGapSize = Defaults.gapSize.value
screenEdgeGapSize = (windowGapSize <= 0) ? 5.0 : CGFloat(windowGapSize)

Expand Down Expand Up @@ -132,10 +120,4 @@ class ChangeSizeCalculation: WindowCalculation {
return adjustedWindowRect
}

private func resizedWindowRectIsTooSmall(windowRect: CGRect, visibleFrameOfScreen: CGRect) -> Bool {
let minimumWindowRectWidth = floor(visibleFrameOfScreen.width * minimumWindowWidth)
let minimumWindowRectHeight = floor(visibleFrameOfScreen.height * minimumWindowHeight)
return (windowRect.width <= minimumWindowRectWidth) || (windowRect.height <= minimumWindowRectHeight)
}

}
35 changes: 35 additions & 0 deletions Rectangle/WindowCalculation/ChangeWindowDimensionCalculation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// MinimumWindowDimensionAware.swift
// Rectangle
//
// Created by Isaac Young on 23/04/24.
// Copyright © 2024 Ryan Hanson. All rights reserved.
//

import Foundation

protocol ChangeWindowDimensionCalculation {
func resizedWindowRectIsTooSmall(windowRect: CGRect, visibleFrameOfScreen: CGRect) -> Bool;
}

extension ChangeWindowDimensionCalculation {
private func minimumWindowWidth() -> CGFloat {
let defaultWidth = Defaults.minimumWindowWidth.value
return (defaultWidth <= 0 || defaultWidth > 1)
? 0.25
: CGFloat(defaultWidth)
}

private func minimumWindowHeight() -> CGFloat {
let defaultHeight = Defaults.minimumWindowHeight.value
return (defaultHeight <= 0 || defaultHeight > 1)
? 0.25
: CGFloat(defaultHeight)
}

func resizedWindowRectIsTooSmall(windowRect: CGRect, visibleFrameOfScreen: CGRect) -> Bool {
let minimumWindowRectWidth = floor(visibleFrameOfScreen.width * minimumWindowWidth())
let minimumWindowRectHeight = floor(visibleFrameOfScreen.height * minimumWindowHeight())
return (windowRect.width <= minimumWindowRectWidth) || (windowRect.height <= minimumWindowRectHeight)
}
}
67 changes: 67 additions & 0 deletions Rectangle/WindowCalculation/HalfOrDoubleDimensionCalculation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// HalfOrDoubleDimensionCalculation.swift
// Rectangle
//
// Created by Isaac Young on 23/04/24.
// Copyright © 2024 Ryan Hanson. All rights reserved.
//

import Foundation

class HalfOrDoubleDimensionCalculation: WindowCalculation, ChangeWindowDimensionCalculation {

override func calculateRect(_ params: RectCalculationParameters) -> RectResult {
let window = params.window

let resizedWindowRect = resized(window.rect, with: params.action)

var resizedAndMovedWindowRect = repositionedIfRequired(original: window.rect, resized: resizedWindowRect, after: params.action)

let visibleFrameOfScreen = params.visibleFrameOfScreen
if isSizeReducing(params.action), resizedWindowRectIsTooSmall(windowRect: resizedAndMovedWindowRect, visibleFrameOfScreen: visibleFrameOfScreen) {
resizedAndMovedWindowRect = window.rect
}
return RectResult(resizedAndMovedWindowRect)
}

private func isSizeReducing(_ action: WindowAction) -> Bool {
switch (action) {
case .halveHeightUp, .halveHeightDown, .halveWidthLeft, .halveWidthRight:
return true
default:
return false
}
}

private func resized(_ windowRect: CGRect, with action: WindowAction) -> CGRect {
var resized = windowRect
switch (action) {
case .halveHeightUp, .halveHeightDown:
resized.size.height = resized.height * 0.5
case .halveWidthLeft, .halveWidthRight:
resized.size.width = resized.width * 0.5
case .doubleHeightUp, .doubleHeightDown:
resized.size.height = resized.height * 2.0
case .doubleWidthLeft, .doubleWidthRight:
resized.size.width = resized.width * 2.0
default:
break
}
return resized
}

private func repositionedIfRequired(original originalWindowRect: CGRect, resized resizedWindowRect: CGRect, after action: WindowAction) -> CGRect {
switch (action) {
case .halveHeightUp:
return resizedWindowRect.offsetBy(dx: 0, dy: resizedWindowRect.height)
case .halveWidthRight:
return resizedWindowRect.offsetBy(dx: resizedWindowRect.width, dy: 0)
case .doubleHeightDown:
return resizedWindowRect.offsetBy(dx: 0, dy: -originalWindowRect.height)
case .doubleWidthLeft:
return resizedWindowRect.offsetBy(dx: -originalWindowRect.width, dy: 0)
default:
return resizedWindowRect
}
}
}
9 changes: 9 additions & 0 deletions Rectangle/WindowCalculation/WindowCalculation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class WindowCalculationFactory {
static let nextPrevDisplayCalculation = NextPrevDisplayCalculation()
static let maximizeCalculation = MaximizeCalculation()
static let changeSizeCalculation = ChangeSizeCalculation()
static let halfOrDoubleDimensionCalculation = HalfOrDoubleDimensionCalculation()
static let lowerLeftCalculation = LowerLeftCalculation()
static let lowerRightCalculation = LowerRightCalculation()
static let upperLeftCalculation = UpperLeftCalculation()
Expand Down Expand Up @@ -244,6 +245,14 @@ class WindowCalculationFactory {
.bottomCenterLeftEighth: bottomCenterLeftEighthCalculation,
.bottomCenterRightEighth: bottomCenterRightEighthCalculation,
.bottomRightEighth: bottomRightEighthCalculation,
.halveHeightUp: halfOrDoubleDimensionCalculation,
.halveHeightDown: halfOrDoubleDimensionCalculation,
.halveWidthLeft: halfOrDoubleDimensionCalculation,
.halveWidthRight: halfOrDoubleDimensionCalculation,
.doubleHeightUp: halfOrDoubleDimensionCalculation,
.doubleHeightDown: halfOrDoubleDimensionCalculation,
.doubleWidthLeft: halfOrDoubleDimensionCalculation,
.doubleWidthRight: halfOrDoubleDimensionCalculation,
.specified: specifiedCalculation,
.leftTodo: leftTodoCalculation,
.rightTodo: rightTodoCalculation
Expand Down
1 change: 1 addition & 0 deletions Rectangle/WindowMover/BestEffortWindowMover.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Foundation
class BestEffortWindowMover: WindowMover {
func moveWindowRect(_ windowRect: CGRect, frameOfScreen: CGRect, visibleFrameOfScreen: CGRect, frontmostWindowElement: AccessibilityElement?, action: WindowAction?) {
guard let currentWindowRect: CGRect = frontmostWindowElement?.frame else { return }
if action?.allowedToExtendOutsideCurrentScreenArea == true && !NSScreen.screensHaveSeparateSpaces { return }

var adjustedWindowRect: CGRect = currentWindowRect

Expand Down
24 changes: 24 additions & 0 deletions TerminalCommands.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The preferences window is purposefully slim, but there's a lot that can be modif
- [Add extra "ninths" sizing commands](#add-extra-ninths-sizing-commands)
- [Add extra "eighths" sizing commands](#add-extra-eighths-sizing-commands)
- [Add additional "thirds" sizing commands](#add-additional-thirds-sizing-commands)
- [Add doubling/halving window sizing commands](#add-doublinghalving-window-sizing-commands)
- [Add additional tiling and cascading commands](#add-additional-tiling-and-cascading-commands)
- [Modify the "footprint" displayed for drag to snap area](#modify-the-footprint-displayed-for-drag-to-snap-area)
- [Move Up/Down/Left/Right: Don't center on edge](#move-updownleftright-dont-center-on-edge)
Expand Down Expand Up @@ -221,6 +222,29 @@ For example, the command for setting the top left two-thirds shortcut to `ctrl o
defaults write com.knollsoft.Rectangle topLeftThird -dict-add keyCode -float 18 modifierFlags -float 917504
```

## Add doubling/halving window sizing commands

These commands for doubling/halving the window width/height are not available in the UI but can be configured via CLI.

The key codes are:

* doubleHeightUp
* doubleHeightDown
* doubleWidthLeft
* doubleWidthRight
* halveHeightUp
* halveHeightDown
* halveWidthLeft
* halveWidthRight

The action direction (e.g., "Right") is the direction that the center of the window will move towards as a result of resizing.

For example, the command for setting the doubleWidthRight shortcut to `ctrl option shift right` would be:

```bash
defaults write com.knollsoft.Rectangle doubleWidthRight -dict-add keyCode -float 124 modifierFlags -float 11403555
```

## Add additional tiling and cascading commands

Commands for tiling and cascading the visible windows are not available in the UI but can be configured via CLI.
Expand Down

0 comments on commit d326390

Please sign in to comment.