Drawing boxes!

This commit is contained in:
Ryan Hanson 2019-09-05 16:06:58 +02:00
parent 7f62992211
commit d13b9b34b9
8 changed files with 245 additions and 23 deletions

View file

@ -60,6 +60,8 @@
98910B42231476B30066EC23 /* PrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98910B41231476B30066EC23 /* PrefsViewController.swift */; };
98C1008C2305F1FA006E5344 /* SubsequentExecutionMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C1008B2305F1FA006E5344 /* SubsequentExecutionMode.swift */; };
98C1008E230B9EF6006E5344 /* NextPrevDisplayCalculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C1008D230B9EF6006E5344 /* NextPrevDisplayCalculation.swift */; };
98C2755E231FF6A9009B9292 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C2755D231FF6A9009B9292 /* EventMonitor.swift */; };
98C27561231FFA5F009B9292 /* SnappingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C27560231FFA5F009B9292 /* SnappingManager.swift */; };
CC0B7937429AC28C21ABF5B4 /* Pods_RectangleLauncher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F2D8480CC730811C953FC1B6 /* Pods_RectangleLauncher.framework */; };
F0A0DFB36FCC3FCE6E184500 /* Pods_Rectangle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20A533B9F2D3215AC7B85D1F /* Pods_Rectangle.framework */; };
/* End PBXBuildFile section */
@ -173,6 +175,8 @@
98910B6E231D16B00066EC23 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Main.strings"; sourceTree = "<group>"; };
98C1008B2305F1FA006E5344 /* SubsequentExecutionMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubsequentExecutionMode.swift; sourceTree = "<group>"; };
98C1008D230B9EF6006E5344 /* NextPrevDisplayCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextPrevDisplayCalculation.swift; sourceTree = "<group>"; };
98C2755D231FF6A9009B9292 /* EventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMonitor.swift; sourceTree = "<group>"; };
98C27560231FFA5F009B9292 /* SnappingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnappingManager.swift; sourceTree = "<group>"; };
BFFF93EBEA6E2FF7245A7CF5 /* Pods-Rectangle.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Rectangle.release.xcconfig"; path = "Target Support Files/Pods-Rectangle/Pods-Rectangle.release.xcconfig"; sourceTree = "<group>"; };
DE4AE568B8BDB98BF602D588 /* Pods-RectangleMAS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RectangleMAS.release.xcconfig"; path = "Target Support Files/Pods-RectangleMAS/Pods-RectangleMAS.release.xcconfig"; sourceTree = "<group>"; };
E2625F45B180F733E8508494 /* Pods-RectangleMAS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RectangleMAS.debug.xcconfig"; path = "Target Support Files/Pods-RectangleMAS/Pods-RectangleMAS.debug.xcconfig"; sourceTree = "<group>"; };
@ -346,6 +350,7 @@
9824703E22B13FBC0037B409 /* ScreenDetection.swift */,
9824704022B186D00037B409 /* WindowManager.swift */,
98910B3C2311304D0066EC23 /* PrefsWindow */,
98C2755F231FF6AE009B9292 /* Snapping */,
985B9BFA22BE218C00A2E8F0 /* Popover */,
982140EA22B7DB0400ABFB3F /* WindowMover */,
982140E922B7DA3100ABFB3F /* WindowCalculation */,
@ -382,6 +387,15 @@
path = PrefsWindow;
sourceTree = "<group>";
};
98C2755F231FF6AE009B9292 /* Snapping */ = {
isa = PBXGroup;
children = (
98C2755D231FF6A9009B9292 /* EventMonitor.swift */,
98C27560231FFA5F009B9292 /* SnappingManager.swift */,
);
path = Snapping;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -457,7 +471,7 @@
CreatedOnToolsVersion = 10.2.1;
SystemCapabilities = {
com.apple.HardenedRuntime = {
enabled = 0;
enabled = 1;
};
com.apple.Sandbox = {
enabled = 1;
@ -468,7 +482,7 @@
CreatedOnToolsVersion = 10.2.1;
SystemCapabilities = {
com.apple.HardenedRuntime = {
enabled = 0;
enabled = 1;
};
com.apple.Sandbox = {
enabled = 0;
@ -664,6 +678,7 @@
9824703D22B13C7E0037B409 /* AccessibilityElement.swift in Sources */,
985B9BF522B93EEC00A2E8F0 /* ApplicationToggle.swift in Sources */,
988D066522EB4CB6004EABD7 /* CenterThirdCalculation.swift in Sources */,
98C2755E231FF6A9009B9292 /* EventMonitor.swift in Sources */,
9821406022B3EFB200ABFB3F /* Defaults.swift in Sources */,
9821403122B38A0500ABFB3F /* TopHalfCalculation.swift in Sources */,
9824704B22B189250037B409 /* StandardWindowMover.swift in Sources */,
@ -682,6 +697,7 @@
9824703122AFA8470037B409 /* RectangleStatusItem.swift in Sources */,
9821403722B3D16700ABFB3F /* MaximizeHeightCalculation.swift in Sources */,
9824704122B186D00037B409 /* WindowManager.swift in Sources */,
98C27561231FFA5F009B9292 /* SnappingManager.swift in Sources */,
9824703722B0F3200037B409 /* WindowAction.swift in Sources */,
9821402922B3889100ABFB3F /* LowerLeftCalculation.swift in Sources */,
9821402122B3884600ABFB3F /* BottomHalfCalculation.swift in Sources */,
@ -765,10 +781,11 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = RectangleLauncher/RectangleLauncher.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XSYZ3E4B7D;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = RectangleLauncher/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -789,10 +806,11 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = RectangleLauncher/RectangleLauncher.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XSYZ3E4B7D;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = RectangleLauncher/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -929,10 +947,11 @@
baseConfigurationReference = 53A6B6ACF05CD88EB34E0D00 /* Pods-Rectangle.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XSYZ3E4B7D;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Rectangle/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -951,10 +970,11 @@
baseConfigurationReference = BFFF93EBEA6E2FF7245A7CF5 /* Pods-Rectangle.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XSYZ3E4B7D;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Rectangle/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -974,10 +994,10 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XSYZ3E4B7D;
INFOPLIST_FILE = RectangleTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -998,10 +1018,10 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XSYZ3E4B7D;
INFOPLIST_FILE = RectangleTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

View file

@ -17,7 +17,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private let accessibilityAuthorization = AccessibilityAuthorization()
private let statusItem = RectangleStatusItem.instance
private let applicationToggle = ApplicationToggle()
private let shortcutManager: ShortcutManager
private let windowManager: WindowManager
private let windowCalculationFactory: WindowCalculationFactory
private let snappingManager: SnappingManager
private let sparkleUpdater = SUUpdater()
@ -28,7 +32,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var ignoreMenuItem: NSMenuItem!
override init() {
self.shortcutManager = ShortcutManager(applicationToggle: applicationToggle)
self.windowCalculationFactory = WindowCalculationFactory()
self.windowManager = WindowManager(windowCalculationFactory: windowCalculationFactory)
self.shortcutManager = ShortcutManager(applicationToggle: applicationToggle, windowManager: windowManager)
self.snappingManager = SnappingManager(windowCalculationFactory: windowCalculationFactory)
super.init()
}

View file

@ -10,7 +10,7 @@ import Cocoa
class ScreenDetection {
func detectScreens(for action: WindowAction, using frontmostWindowElement: AccessibilityElement?) -> UsableScreens? {
func detectScreens(using frontmostWindowElement: AccessibilityElement?) -> UsableScreens? {
let screens = NSScreen.screens
guard let firstScreen = screens.first else { return nil }

View file

@ -11,11 +11,12 @@ import MASShortcut
class ShortcutManager {
let windowManager = WindowManager()
let windowManager: WindowManager
let applicationToggle: ApplicationToggle
init(applicationToggle: ApplicationToggle) {
init(applicationToggle: ApplicationToggle, windowManager: WindowManager) {
self.applicationToggle = applicationToggle
self.windowManager = windowManager
registerDefaults()

View file

@ -0,0 +1,35 @@
//
// EventMonitor.swift
// Rectangle
//
// Created by Ryan Hanson on 9/4/19.
// Copyright © 2019 Ryan Hanson. All rights reserved.
//
import Cocoa
public class EventMonitor {
private var monitor: Any?
private let mask: NSEvent.EventTypeMask
private let handler: (NSEvent?) -> Void
public init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) {
self.mask = mask
self.handler = handler
}
deinit {
stop()
}
public func start() {
monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler)
}
public func stop() {
if monitor != nil {
NSEvent.removeMonitor(monitor!)
monitor = nil
}
}
}

View file

@ -0,0 +1,146 @@
//
// SnappingManager.swift
// Rectangle
//
// Created by Ryan Hanson on 9/4/19.
// Copyright © 2019 Ryan Hanson. All rights reserved.
//
import Cocoa
class SnappingManager {
let windowCalculationFactory: WindowCalculationFactory
var eventMonitor: EventMonitor?
var frontmostWindow: AccessibilityElement?
var windowMoving: Bool = false
var previousWindowRect: CGRect?
var currentHotSpot: HotSpot?
let box: NSWindow
let screenDetection = ScreenDetection()
init(windowCalculationFactory: WindowCalculationFactory) {
self.windowCalculationFactory = windowCalculationFactory
let initialRect = NSRect(x: 0, y: 0, width: 10, height: 10)
box = NSWindow(contentRect: initialRect, styleMask: .borderless, backing: .buffered, defer: false)
box.backgroundColor = .blue
box.isReleasedWhenClosed = false
eventMonitor = EventMonitor(mask: [.leftMouseUp, .leftMouseDragged]) { event in
guard let event = event else { return }
switch event.type {
case .leftMouseUp:
self.frontmostWindow = nil
self.windowMoving = false
self.previousWindowRect = nil
// print(event.locationInWindow)
case .leftMouseDragged:
if self.frontmostWindow == nil {
self.frontmostWindow = AccessibilityElement.frontmostWindow()
}
guard let currentRect = self.frontmostWindow?.rectOfElement() else {
return
}
if !self.windowMoving {
if currentRect.size == self.previousWindowRect?.size && currentRect.origin != self.previousWindowRect?.origin {
self.windowMoving = true
}
}
if self.windowMoving {
if let newHotSpot = self.getMouseHotSpot() {
if newHotSpot == self.currentHotSpot {
return
}
if let newBoxRect = self.getBoxRect(hotSpot: newHotSpot, currentWindowRect: currentRect) {
self.box.setFrame(newBoxRect, display: true)
self.box.makeKeyAndOrderFront(nil)
print(newBoxRect)
}
self.currentHotSpot = newHotSpot
} else {
if self.currentHotSpot != nil {
self.box.close()
self.currentHotSpot = nil
}
}
}
self.previousWindowRect = currentRect
default:
return
}
}
eventMonitor?.start()
}
func getBoxRect(hotSpot: HotSpot, currentWindowRect: CGRect) -> CGRect? {
if let calculation = windowCalculationFactory.calculation(for: hotSpot.type.action) {
return calculation.calculateRect(currentWindowRect, visibleFrameOfScreen: hotSpot.screen.visibleFrame, action: hotSpot.type.action)
}
return nil
}
func getMouseHotSpot() -> HotSpot? {
for screen in NSScreen.screens {
let frame = screen.frame
let loc = NSEvent.mouseLocation
if loc.x >= frame.minX && loc.x < frame.minX + 5 {
if loc.y >= frame.minY && loc.y <= frame.maxY {
return HotSpot(screen: screen, type: .left)
}
}
if loc.x <= frame.maxX && loc.x > frame.maxX - 5 {
if loc.y >= frame.minY && loc.y <= frame.maxY {
return HotSpot(screen: screen, type: .right)
}
}
if loc.y >= frame.minY && loc.y < frame.minY + 5 {
if loc.x >= frame.minX && loc.x <= frame.maxX {
return HotSpot(screen: screen, type: .bottom)
}
}
if loc.y <= frame.maxY && loc.y > frame.maxY - 5 {
if loc.x >= frame.minX && loc.x <= frame.maxX {
return HotSpot(screen: screen, type: .top)
}
}
}
return nil
}
}
struct HotSpot: Equatable {
let screen: NSScreen
let type: HotSpotType
}
enum HotSpotType {
case top
case bottom
case left
case right
var action: WindowAction {
switch self {
case .top: return .maximize
case .bottom: return .maximize
case .left: return .leftHalf
case .right: return .rightHalf
}
}
}

View file

@ -105,8 +105,20 @@ class LeftRightHalfCalculation: WindowCalculation {
return WindowCalculationResult(rect: oneHalfRect, screen: screen, resultingAction: .rightHalf)
}
// unused
// Used to draw box for snapping
func calculateRect(_ windowRect: CGRect, visibleFrameOfScreen: CGRect, action: WindowAction) -> CGRect? {
return nil
switch action {
case .leftHalf:
var oneHalfRect = visibleFrameOfScreen
oneHalfRect.size.width = floor(oneHalfRect.width / 2.0)
return oneHalfRect
case .rightHalf:
var oneHalfRect = visibleFrameOfScreen
oneHalfRect.size.width = floor(oneHalfRect.width / 2.0)
oneHalfRect.origin.x += oneHalfRect.size.width
return oneHalfRect
default:
return nil
}
}
}

View file

@ -13,12 +13,13 @@ class WindowManager {
private let screenDetection = ScreenDetection()
private let windowMoverChain: [WindowMover]
private let windowCalculationFactory = WindowCalculationFactory()
private let windowCalculationFactory: WindowCalculationFactory
private var restoreRects = [appBundleId: CGRect]() // the last window frame that the user positioned
private var lastRectangleActions = [appBundleId: RectangleAction]() // the last window frame that this app positioned
init() {
init(windowCalculationFactory: WindowCalculationFactory) {
self.windowCalculationFactory = windowCalculationFactory
windowMoverChain = [
StandardWindowMover(),
// QuantizedWindowMover(), // This was used in Spectacle, but doesn't seem to help on any windows I've tried. It just makes some actions feel more jenky
@ -42,7 +43,7 @@ class WindowManager {
return
}
guard let usableScreens = screenDetection.detectScreens(for: action, using: frontmostWindowElement) else {
guard let usableScreens = screenDetection.detectScreens(using: frontmostWindowElement) else {
NSSound.beep()
return
}