mirror of
https://github.com/neon443/DockPhobia.git
synced 2026-03-11 06:49:12 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0b8d2f3ed | ||
|
|
a6f06c4e12 | ||
|
|
9ff4f67e33 | ||
|
|
5bfeef66ab | ||
|
|
58c834f1ed | ||
|
|
84326f7e15 | ||
|
|
27e410ae76 | ||
|
|
d943415b98 | ||
|
|
83efb14bd5 | ||
|
|
d60d90bc60 | ||
|
|
4e55cc4516 | ||
|
|
1a0d3c2e38 | ||
|
|
e46edecec7 | ||
|
|
22c825dbb2 | ||
|
|
5357ce8443 | ||
|
|
853fc2a2db | ||
|
|
6a68713ad5 | ||
|
|
b15116b292 | ||
|
|
e4c41c4a58 | ||
|
|
c099d4bec3 | ||
|
|
5718e42f41 | ||
|
|
936f18d714 | ||
|
|
3e7a7c2ac2 | ||
|
|
43863d3827 | ||
|
|
f18874deb3 | ||
|
|
7ba2e84cb1 | ||
|
|
bc2bc9bb6d | ||
|
|
d954730117 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.DS_Store
|
||||
**.DS_Store
|
||||
**DockPhobia.dmg
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
// Created by neon443 on 26/05/2025.
|
||||
//
|
||||
|
||||
VERSION = 1.0
|
||||
BUILD_ID = 4
|
||||
VERSION = 1.2.1
|
||||
BUILD_ID = 3
|
||||
TEAM_ID = 8JGND254B7
|
||||
BUNDLE_ID = com.neon443.DockPhobia
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objectVersion = 63;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -11,6 +11,11 @@
|
||||
A966B4F42DE0842500C721A5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A966B4EE2DE0842400C721A5 /* AppDelegate.swift */; };
|
||||
A966B4F52DE0842500C721A5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A966B4EF2DE0842400C721A5 /* Assets.xcassets */; };
|
||||
A966B4F82DE0852900C721A5 /* MouseTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A966B4F72DE0852900C721A5 /* MouseTracker.swift */; };
|
||||
A98C20C62DE614180008D61C /* DPSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20C52DE614180008D61C /* DPSettings.swift */; };
|
||||
A9C9AF812DE7776A0039D7A5 /* DockSide.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C9AF802DE777530039D7A5 /* DockSide.swift */; };
|
||||
A9C9AF832DE77CB70039D7A5 /* SkyHigh.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C9AF822DE77CB70039D7A5 /* SkyHigh.swift */; };
|
||||
A9C9B0682DE888B20039D7A5 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C9B0672DE888B20039D7A5 /* Preferences.swift */; };
|
||||
A9C9B06D2DE99E5F0039D7A5 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = A9C9B06C2DE99E5F0039D7A5 /* Sparkle */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -21,6 +26,11 @@
|
||||
A966B4F02DE0842400C721A5 /* DockPhobia.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DockPhobia.entitlements; sourceTree = "<group>"; };
|
||||
A966B4F72DE0852900C721A5 /* MouseTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MouseTracker.swift; sourceTree = "<group>"; };
|
||||
A97798072DE485F200B6CB13 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
A98C20C52DE614180008D61C /* DPSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DPSettings.swift; sourceTree = "<group>"; };
|
||||
A9C9AF802DE777530039D7A5 /* DockSide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DockSide.swift; sourceTree = "<group>"; };
|
||||
A9C9AF822DE77CB70039D7A5 /* SkyHigh.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkyHigh.swift; sourceTree = "<group>"; };
|
||||
A9C9B0672DE888B20039D7A5 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||
A9C9B06E2DE9B55F0039D7A5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -28,6 +38,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A9C9B06D2DE99E5F0039D7A5 /* Sparkle in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -46,6 +57,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A94BEC0E2DE23E8500D4811D /* MainMenu.xib */,
|
||||
A9C9B0672DE888B20039D7A5 /* Preferences.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -70,8 +82,12 @@
|
||||
A966B4F32DE0842400C721A5 /* DockPhobia */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A9C9B06E2DE9B55F0039D7A5 /* Info.plist */,
|
||||
A966B4EE2DE0842400C721A5 /* AppDelegate.swift */,
|
||||
A966B4F72DE0852900C721A5 /* MouseTracker.swift */,
|
||||
A9C9AF822DE77CB70039D7A5 /* SkyHigh.swift */,
|
||||
A98C20C52DE614180008D61C /* DPSettings.swift */,
|
||||
A9C9AF802DE777530039D7A5 /* DockSide.swift */,
|
||||
A94BEC102DE23ECE00D4811D /* Views */,
|
||||
A94BEC0A2DE21F8100D4811D /* Resources */,
|
||||
A966B4F02DE0842400C721A5 /* DockPhobia.entitlements */,
|
||||
@@ -96,6 +112,7 @@
|
||||
);
|
||||
name = DockPhobia;
|
||||
packageProductDependencies = (
|
||||
A9C9B06C2DE99E5F0039D7A5 /* Sparkle */,
|
||||
);
|
||||
productName = DockPhobia;
|
||||
productReference = A966B4D62DE0841000C721A5 /* DockPhobia.app */;
|
||||
@@ -117,6 +134,7 @@
|
||||
};
|
||||
};
|
||||
buildConfigurationList = A966B4D12DE0841000C721A5 /* Build configuration list for PBXProject "DockPhobia" */;
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@@ -125,7 +143,9 @@
|
||||
);
|
||||
mainGroup = A966B4CD2DE0841000C721A5;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
preferredProjectObjectVersion = 77;
|
||||
packageReferences = (
|
||||
A9C9B06B2DE99E5F0039D7A5 /* XCRemoteSwiftPackageReference "Sparkle" */,
|
||||
);
|
||||
productRefGroup = A966B4D72DE0841000C721A5 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
@@ -152,8 +172,12 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A98C20C62DE614180008D61C /* DPSettings.swift in Sources */,
|
||||
A9C9AF832DE77CB70039D7A5 /* SkyHigh.swift in Sources */,
|
||||
A9C9B0682DE888B20039D7A5 /* Preferences.swift in Sources */,
|
||||
A966B4F82DE0852900C721A5 /* MouseTracker.swift in Sources */,
|
||||
A966B4F42DE0842500C721A5 /* AppDelegate.swift in Sources */,
|
||||
A9C9AF812DE7776A0039D7A5 /* DockSide.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -294,6 +318,8 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = DockPhobia/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||
INFOPLIST_KEY_LSUIElement = YES;
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = _;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
@@ -328,6 +354,8 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = DockPhobia/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||
INFOPLIST_KEY_LSUIElement = YES;
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = _;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
@@ -370,6 +398,25 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
A9C9B06B2DE99E5F0039D7A5 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/sparkle-project/Sparkle";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.7.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
A9C9B06C2DE99E5F0039D7A5 /* Sparkle */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = A9C9B06B2DE99E5F0039D7A5 /* XCRemoteSwiftPackageReference "Sparkle" */;
|
||||
productName = Sparkle;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = A966B4CE2DE0841000C721A5 /* Project object */;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"originHash" : "e721da7f9826abdffcb6185e886155efa2514bd6234475f1afa893e29eb258d6",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "sparkle",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/sparkle-project/Sparkle",
|
||||
"state" : {
|
||||
"revision" : "0ca3004e98712ea2b39dd881d28448630cce1c99",
|
||||
"version" : "2.7.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
BIN
DockPhobia/.DS_Store
vendored
BIN
DockPhobia/.DS_Store
vendored
Binary file not shown.
@@ -6,36 +6,58 @@
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Sparkle
|
||||
|
||||
@main
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
public var statusItem: NSStatusItem!
|
||||
|
||||
var mouseTracker = MouseTracker()
|
||||
var settings = DPSettingsModel()
|
||||
var mouseTracker: MouseTracker
|
||||
let updateController: SPUStandardUpdaterController
|
||||
|
||||
override init() {
|
||||
self.mouseTracker = MouseTracker(settings: settings)
|
||||
//call .startUpdater() later
|
||||
updateController = SPUStandardUpdaterController(
|
||||
startingUpdater: true,
|
||||
updaterDelegate: nil,
|
||||
userDriverDelegate: nil
|
||||
)
|
||||
super.init()
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
if let button = statusItem.button {
|
||||
button.image = NSImage(named: "cursor.slash")
|
||||
}
|
||||
setupMenus()
|
||||
refreshMenus()
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
// Insert code here to tear down your application
|
||||
settings.saveSettings()
|
||||
}
|
||||
|
||||
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
//MARK: menu bar stuff
|
||||
func setupMenus() {
|
||||
let menu = NSMenu()
|
||||
let start = NSMenuItem(title: describeStartButton(), action: #selector(didTapStart), keyEquivalent: "")
|
||||
menu.addItem(start)
|
||||
|
||||
let checkforUpdatesMenuItem = NSMenuItem(
|
||||
title: "Check for Updates...",
|
||||
action: #selector(SPUStandardUpdaterController.checkForUpdates(_:)),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
checkforUpdatesMenuItem.target = updateController
|
||||
menu.addItem(checkforUpdatesMenuItem)
|
||||
|
||||
let screen = NSMenuItem(
|
||||
title: "\(mouseTracker.screen.width)x\(mouseTracker.screen.height)",
|
||||
action: nil,
|
||||
@@ -43,37 +65,58 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
)
|
||||
menu.addItem(screen)
|
||||
|
||||
let dockMoves = NSMenuItem(
|
||||
title: "Moved the Dock \(settings.settings.dockMoves) time\(settings.settings.dockMoves.plural)",
|
||||
action: nil,
|
||||
keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(dockMoves)
|
||||
|
||||
let mouseMoves = NSMenuItem(
|
||||
title: "Moved the cursor \(settings.settings.mouseMoves) time\(settings.settings.mouseMoves.plural)",
|
||||
action: nil,
|
||||
keyEquivalent: ""
|
||||
)
|
||||
menu.addItem(mouseMoves)
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
menu.addItem(
|
||||
NSMenuItem(
|
||||
title: "Move Dock to left",
|
||||
let moveMouseButton = NSMenuItem(
|
||||
title: "Move cursor instead",
|
||||
action: #selector(moveMouseToggle),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
moveMouseButton.state = NSControl.StateValue(rawValue: settings.settings.moveMouseInstead ? 1 : 0)
|
||||
menu.addItem(moveMouseButton)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let checkfullscreenButton = NSMenuItem(
|
||||
title: "Smaller deathzone in fullscreen",
|
||||
action: #selector(checkFullscreenToggle),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
checkfullscreenButton.state = NSControl.StateValue(rawValue: settings.settings.checkFullscreen ? 1 : 0)
|
||||
menu.addItem(checkfullscreenButton)
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
menu.addItem(NSMenuItem(
|
||||
title: "Move Dock to the left",
|
||||
action: #selector(moveDockObjcLeft),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
)
|
||||
menu.addItem(
|
||||
NSMenuItem(
|
||||
title: "Move Dock to bottom",
|
||||
keyEquivalent: ""))
|
||||
menu.addItem(NSMenuItem(
|
||||
title: "Move Dock to the bottom",
|
||||
action: #selector(moveDockObjcBottom),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
)
|
||||
menu.addItem(
|
||||
NSMenuItem(
|
||||
title: "Move Dock to right",
|
||||
action: #selector(moveDockObjcRight),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
)
|
||||
|
||||
keyEquivalent: ""))
|
||||
menu.addItem(NSMenuItem(
|
||||
title: "Move Dock to the right",
|
||||
action: #selector(moveDockObjcRight),
|
||||
keyEquivalent: ""))
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let quit = NSMenuItem(title: "Quit", action: #selector(didTapStart), keyEquivalent: "q")
|
||||
let quit = NSMenuItem(title: "Quit", action: #selector(quit), keyEquivalent: "q")
|
||||
menu.addItem(quit)
|
||||
statusItem.menu = menu
|
||||
}
|
||||
|
||||
func changeMenuIcon(running: Bool) {
|
||||
guard let button = statusItem.button else { return }
|
||||
switch running {
|
||||
@@ -83,7 +126,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
button.image = NSImage(named: "cursor.slash")
|
||||
}
|
||||
}
|
||||
|
||||
@objc func didTapStart() {
|
||||
if mouseTracker.running {
|
||||
mouseTracker.stop()
|
||||
@@ -92,13 +134,22 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
mouseTracker.start()
|
||||
changeMenuIcon(running: true)
|
||||
}
|
||||
setupMenus()
|
||||
refreshMenus()
|
||||
}
|
||||
@objc func quit() {
|
||||
NSApplication.shared.terminate(self)
|
||||
}
|
||||
|
||||
@objc func moveDockObjcLeft() { mouseTracker.moveDock(.left) }
|
||||
@objc func moveDockObjcRight() { mouseTracker.moveDock(.right) }
|
||||
@objc func moveDockObjcBottom() { mouseTracker.moveDock(.bottom) }
|
||||
|
||||
@objc func checkFullscreenToggle() {
|
||||
settings.settings.checkFullscreen.toggle()
|
||||
refreshMenus()
|
||||
}
|
||||
@objc func moveMouseToggle() {
|
||||
settings.settings.moveMouseInstead.toggle()
|
||||
refreshMenus()
|
||||
}
|
||||
func describeStartButton() -> String {
|
||||
if mouseTracker.running {
|
||||
return "Stop tracking"
|
||||
@@ -107,3 +158,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func refreshMenus() {
|
||||
guard let delegate = NSApp.delegate as? AppDelegate else { return }
|
||||
delegate.setupMenus()
|
||||
}
|
||||
|
||||
extension Numeric {
|
||||
var plural: String {
|
||||
return self == 1 ? "" : "s"
|
||||
}
|
||||
}
|
||||
|
||||
114
DockPhobia/DPSettings.swift
Normal file
114
DockPhobia/DPSettings.swift
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// Persistence.swift
|
||||
// DockPhobia
|
||||
//
|
||||
// Created by neon443 on 27/05/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
extension NSScreen {
|
||||
static var mainFrame: CGRect {
|
||||
main?.frame ?? CGRect(x: 0, y: 0, width: 1920, height: 1080)
|
||||
}
|
||||
static var mainFrameWidth: CGFloat {
|
||||
main?.frame.width ?? 1920
|
||||
}
|
||||
static var mainFrameHeight: CGFloat {
|
||||
main?.frame.height ?? 1080
|
||||
}
|
||||
}
|
||||
|
||||
struct DPSettings: Codable {
|
||||
var dockMoves: Int
|
||||
var mouseMoves: Int
|
||||
var checkFullscreen: Bool
|
||||
var moveMouseInstead: Bool
|
||||
|
||||
var mouseInsetLeading: CGFloat {
|
||||
NSScreen.mainFrameWidth*insetHorizontal
|
||||
}
|
||||
var mouseInsetBottom: CGFloat {
|
||||
NSScreen.mainFrameHeight*insetVertical
|
||||
}
|
||||
var mouseInsetTop: CGFloat {
|
||||
NSScreen.mainFrameHeight*(1-(insetVertical))
|
||||
}
|
||||
var mouseInsetTrailing: CGFloat {
|
||||
NSScreen.mainFrameWidth*(1-(insetHorizontal))
|
||||
}
|
||||
|
||||
var insetHorizontal: CGFloat
|
||||
var insetVertical: CGFloat
|
||||
|
||||
var mouseMoveRect: CGRect {
|
||||
return CGRect(
|
||||
x: mouseInsetLeading,
|
||||
y: mouseInsetBottom,
|
||||
width: mouseInsetTrailing - mouseInsetLeading,
|
||||
height: mouseInsetTop - mouseInsetBottom
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
dockMoves: Int = 0,
|
||||
mouseMoves: Int = 0,
|
||||
checkFullscreen: Bool = false,
|
||||
moveMouseInstead: Bool = false,
|
||||
insetHorizontal: CGFloat = 0.05,
|
||||
insetVertical: CGFloat = 0.1
|
||||
) {
|
||||
self.dockMoves = dockMoves
|
||||
self.mouseMoves = mouseMoves
|
||||
self.checkFullscreen = checkFullscreen
|
||||
self.moveMouseInstead = moveMouseInstead
|
||||
self.insetHorizontal = insetHorizontal
|
||||
self.insetVertical = insetVertical
|
||||
}
|
||||
|
||||
init(from decoder: any Decoder) throws {
|
||||
let defaults = DPSettings()
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
dockMoves = try container.decodeIfPresent(Int.self, forKey: .dockMoves)
|
||||
?? defaults.dockMoves
|
||||
mouseMoves = try container.decodeIfPresent(Int.self, forKey: .mouseMoves)
|
||||
?? defaults.mouseMoves
|
||||
|
||||
checkFullscreen = try container.decodeIfPresent(Bool.self, forKey: .checkFullscreen)
|
||||
?? defaults.checkFullscreen
|
||||
moveMouseInstead = try container.decodeIfPresent(Bool.self, forKey: .moveMouseInstead)
|
||||
?? defaults.moveMouseInstead
|
||||
|
||||
insetHorizontal = try container.decodeIfPresent(CGFloat.self, forKey: .insetHorizontal)
|
||||
?? defaults.insetHorizontal
|
||||
insetVertical = try container.decodeIfPresent(CGFloat.self, forKey: .insetVertical)
|
||||
?? defaults.insetVertical
|
||||
}
|
||||
}
|
||||
|
||||
class DPSettingsModel {
|
||||
var settings = DPSettings()
|
||||
|
||||
let userDefaults = UserDefaults.standard
|
||||
|
||||
init() {
|
||||
guard let data = userDefaults.data(forKey: "settings") else { return }
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
guard let decoded = try? decoder.decode(DPSettings.self, from: data) else {
|
||||
return
|
||||
}
|
||||
self.settings = decoded
|
||||
refreshMenus()
|
||||
}
|
||||
|
||||
func saveSettings() {
|
||||
let encoder = JSONEncoder()
|
||||
guard let encoded = try? encoder.encode(settings) else {
|
||||
return
|
||||
}
|
||||
userDefaults.set(encoded, forKey: "settings")
|
||||
}
|
||||
}
|
||||
59
DockPhobia/DockSide.swift
Normal file
59
DockPhobia/DockSide.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// DockSide.swift
|
||||
// DockPhobia
|
||||
//
|
||||
// Created by neon443 on 28/05/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum DockSide: Int, RawRepresentable {
|
||||
case left
|
||||
case bottom
|
||||
case right
|
||||
|
||||
public typealias RawValue = String
|
||||
|
||||
public var rawValue: RawValue {
|
||||
switch self {
|
||||
case .left:
|
||||
return "left"
|
||||
case .right:
|
||||
return "right"
|
||||
case .bottom:
|
||||
return "bottom"
|
||||
}
|
||||
}
|
||||
|
||||
/// Random Dock Side
|
||||
/// - will return a random Dock Side when calling DockSide()
|
||||
public init() {
|
||||
self = DockSide(rawValue: Int.random(in: 1...3))!
|
||||
}
|
||||
|
||||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case "left":
|
||||
self = .left
|
||||
case "right":
|
||||
self = .right
|
||||
case "bottom":
|
||||
self = .bottom
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 1:
|
||||
self = .left
|
||||
case 2:
|
||||
self = .bottom
|
||||
case 3:
|
||||
self = .right
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
12
DockPhobia/Info.plist
Normal file
12
DockPhobia/Info.plist
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://neon443.github.io/appcasts/DockPhobia/appcast.xml</string>
|
||||
<key>SUpublicEDKey</key>
|
||||
<string>ahgIyJGs4MCQ6UXwQ11aknk3a6GwOodHKSmVv4bw6KY=</string>
|
||||
<key>SUEnableAutomaticChecks</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -7,128 +7,125 @@
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
import Cocoa
|
||||
import ApplicationServices
|
||||
|
||||
extension NSEvent {
|
||||
var mouseLocationCG: CGPoint {
|
||||
var loc = NSEvent.mouseLocation
|
||||
loc.y = NSScreen.mainFrameHeight - loc.y
|
||||
return loc
|
||||
}
|
||||
}
|
||||
|
||||
extension CGPoint {
|
||||
var invertedForScreen: CGPoint {
|
||||
return CGPoint(
|
||||
x: self.x,
|
||||
y: NSScreen.mainFrameHeight - self.y
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct Screen {
|
||||
var width: CGFloat
|
||||
var height: CGFloat
|
||||
}
|
||||
|
||||
enum DockSide: Int, RawRepresentable {
|
||||
case left
|
||||
case bottom
|
||||
case right
|
||||
|
||||
public typealias RawValue = String
|
||||
|
||||
public var rawValue: RawValue {
|
||||
switch self {
|
||||
case .left:
|
||||
return "left"
|
||||
case .right:
|
||||
return "right"
|
||||
case .bottom:
|
||||
return "bottom"
|
||||
}
|
||||
}
|
||||
|
||||
/// Random Dock Side
|
||||
/// - will return a random Dock Side when calling DockSide()
|
||||
public init() {
|
||||
self = DockSide(rawValue: Int.random(in: 1...3))!
|
||||
}
|
||||
|
||||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case "left":
|
||||
self = .left
|
||||
case "right":
|
||||
self = .right
|
||||
case "bottom":
|
||||
self = .bottom
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 1:
|
||||
self = .left
|
||||
case 2:
|
||||
self = .bottom
|
||||
case 3:
|
||||
self = .right
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MouseTracker {
|
||||
var screen: Screen
|
||||
|
||||
var monitor: Any?
|
||||
|
||||
var running: Bool = false
|
||||
|
||||
var currentDockSide: DockSide
|
||||
|
||||
var dockHeight: CGFloat = 0
|
||||
|
||||
init() {
|
||||
var settings: DPSettingsModel
|
||||
var skyHigh: SkyHigh
|
||||
|
||||
private var timer: Timer?
|
||||
private var loopIteration: Double = 0
|
||||
|
||||
init(settings: DPSettingsModel) {
|
||||
print(DockSide())
|
||||
if let screen = NSScreen.main {
|
||||
let rect = screen.frame
|
||||
self.screen = Screen(
|
||||
width: rect.width,
|
||||
height: rect.height
|
||||
)
|
||||
print(self.screen)
|
||||
} else {
|
||||
fatalError("no screen wtf???")
|
||||
}
|
||||
|
||||
self.screen = Screen(
|
||||
width: NSScreen.mainFrameWidth,
|
||||
height: NSScreen.mainFrameHeight
|
||||
)
|
||||
print(self.screen)
|
||||
|
||||
self.settings = settings
|
||||
self.skyHigh = SkyHigh(settings: settings)
|
||||
|
||||
self.currentDockSide = .left
|
||||
moveDock(.bottom)
|
||||
getDockSize()
|
||||
}
|
||||
|
||||
func checkMouse(_ event: NSEvent) {
|
||||
var location = event.locationInWindow
|
||||
location.y = screen.height - location.y
|
||||
#if DEBUG
|
||||
print(location)
|
||||
#endif
|
||||
switch currentDockSide {
|
||||
let location = event.mouseLocationCG
|
||||
#if DEBUG
|
||||
var cgpointForSkyHigh = NSEvent.mouseLocation
|
||||
cgpointForSkyHigh.x -= 20
|
||||
cgpointForSkyHigh.y -= 5
|
||||
skyHigh.move(to: cgpointForSkyHigh)
|
||||
#endif
|
||||
|
||||
guard settings.settings.checkFullscreen else {
|
||||
handleDockValue(dockIsAt: currentDockSide, location: location)
|
||||
return
|
||||
}
|
||||
|
||||
guard isFrontmostFullscreen() else {
|
||||
if location.x < 1 ||
|
||||
location.x < screen.width-1 ||
|
||||
location.y > screen.height-1 {
|
||||
handleDockValue(dockIsAt: currentDockSide, location: location)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func handleDockValue(dockIsAt: DockSide, location: NSPoint) {
|
||||
switch dockIsAt {
|
||||
case .left:
|
||||
guard location.x < dockHeight else { return }
|
||||
if location.y < screen.height/2 {
|
||||
moveDock(.bottom)
|
||||
moveDockOrMouse(.bottom)
|
||||
return
|
||||
} else {
|
||||
moveDock(.right)
|
||||
moveDockOrMouse(.right)
|
||||
return
|
||||
}
|
||||
case .bottom:
|
||||
guard location.y > screen.height - dockHeight else { return }
|
||||
if location.x < screen.width/2 {
|
||||
moveDock(.right)
|
||||
moveDockOrMouse(.right)
|
||||
return
|
||||
} else {
|
||||
moveDock(.left)
|
||||
moveDockOrMouse(.left)
|
||||
return
|
||||
}
|
||||
case .right:
|
||||
guard location.x > screen.width - dockHeight else { return }
|
||||
if location.y < screen.height/2 {
|
||||
moveDock(.bottom)
|
||||
moveDockOrMouse(.bottom)
|
||||
return
|
||||
} else {
|
||||
moveDock(.left)
|
||||
moveDockOrMouse(.left)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func moveDockOrMouse(_ dockTo: DockSide) {
|
||||
if settings.settings.moveMouseInstead {
|
||||
moveMouse()
|
||||
} else {
|
||||
moveDock(dockTo)
|
||||
}
|
||||
}
|
||||
|
||||
func start() {
|
||||
self.monitor = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved, handler: checkMouse)
|
||||
self.running = true
|
||||
@@ -144,6 +141,40 @@ class MouseTracker {
|
||||
print("stop tracking")
|
||||
}
|
||||
|
||||
func moveMouse() {
|
||||
let prevPoint = NSEvent().mouseLocationCG
|
||||
let rangeW = settings.settings.mouseInsetLeading...settings.settings.mouseInsetTrailing
|
||||
let posX = CGFloat.random(in: rangeW)
|
||||
let rangeH = settings.settings.mouseInsetBottom...settings.settings.mouseInsetTop
|
||||
let posY = CGFloat.random(in: rangeH)
|
||||
|
||||
timer?.invalidate()
|
||||
loopIteration = 0
|
||||
skyHigh.move(to: prevPoint.invertedForScreen)
|
||||
skyHigh.show()
|
||||
timer = Timer(timeInterval: 0.005, repeats: true) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
guard loopIteration < 500 else {
|
||||
skyHigh.hide()
|
||||
settings.settings.mouseMoves += 1
|
||||
refreshMenus()
|
||||
timer?.invalidate()
|
||||
return
|
||||
}
|
||||
let newPosX = (prevPoint.x > posX ? prevPoint.x-loopIteration : prevPoint.x+loopIteration)
|
||||
let newPosY = (prevPoint.y > posY ? prevPoint.y-loopIteration : prevPoint.y+loopIteration)
|
||||
let cgpoint = CGPoint(x: newPosX, y: newPosY)
|
||||
CGWarpMouseCursorPosition(cgpoint)
|
||||
var cgpointForSkyHigh = cgpoint
|
||||
cgpointForSkyHigh.x -= 20
|
||||
cgpointForSkyHigh.y += 5
|
||||
skyHigh.move(to: cgpointForSkyHigh.invertedForScreen)
|
||||
|
||||
self.loopIteration += 1
|
||||
}
|
||||
RunLoop.main.add(timer!, forMode: .common)
|
||||
}
|
||||
|
||||
func moveDock(_ toSide: DockSide) {
|
||||
guard currentDockSide != toSide else { return }
|
||||
// let scriptHide = """
|
||||
@@ -167,10 +198,11 @@ class MouseTracker {
|
||||
applescript(scriptMove)
|
||||
// applescript(scriptShow)
|
||||
currentDockSide = toSide
|
||||
settings.settings.dockMoves += 1
|
||||
refreshMenus()
|
||||
}
|
||||
|
||||
func getDockSize() {
|
||||
guard let screen = NSScreen.main?.frame else { fatalError() }
|
||||
guard let screenVisible = NSScreen.main?.visibleFrame else { fatalError() }
|
||||
self.dockHeight = screen.height - screenVisible.height
|
||||
}
|
||||
@@ -187,3 +219,27 @@ class MouseTracker {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func isFrontmostFullscreen() -> Bool {
|
||||
guard let frontmostApp = NSWorkspace.shared.frontmostApplication,
|
||||
let appPID = frontmostApp.processIdentifier as pid_t? else {
|
||||
return false
|
||||
}
|
||||
|
||||
let appElement = AXUIElementCreateApplication(appPID)
|
||||
var frontWindo: AnyObject?
|
||||
let result = AXUIElementCopyAttributeValue(appElement, kAXFocusedWindowAttribute as CFString, &frontWindo)
|
||||
|
||||
guard result == .success, let windowElement = frontWindo else {
|
||||
return false
|
||||
}
|
||||
|
||||
var fullscreenValue: AnyObject?
|
||||
let fullscreenAttr = AXUIElementCopyAttributeValue(windowElement as! AXUIElement, "AXFullScreen" as CFString, &fullscreenValue)
|
||||
|
||||
if fullscreenAttr == .success,
|
||||
let isFullscreen = fullscreenValue as? Bool {
|
||||
return isFullscreen
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "DockPhobiaAppIcon1024.png",
|
||||
"filename" : "DockPhobiaAppIcon.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
|
||||
|
Before Width: | Height: | Size: 560 KiB After Width: | Height: | Size: 560 KiB |
23
DockPhobia/Resources/Assets.xcassets/pinch.imageset/Contents.json
vendored
Normal file
23
DockPhobia/Resources/Assets.xcassets/pinch.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "pinch.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pinch.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pinch.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
DockPhobia/Resources/Assets.xcassets/pinch.imageset/pinch.png
vendored
Normal file
BIN
DockPhobia/Resources/Assets.xcassets/pinch.imageset/pinch.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
54
DockPhobia/SkyHigh.swift
Normal file
54
DockPhobia/SkyHigh.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// SkyHigh.swift
|
||||
// DockPhobia
|
||||
//
|
||||
// Created by neon443 on 28/05/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class SkyHigh {
|
||||
private var window: NSWindow
|
||||
private var x = 1
|
||||
private var timer: Timer?
|
||||
var settings: DPSettingsModel
|
||||
|
||||
init(settings: DPSettingsModel) {
|
||||
self.settings = settings
|
||||
|
||||
self.window = NSWindow(
|
||||
contentRect: CGRect(
|
||||
x: NSScreen.mainFrameWidth/2,
|
||||
y: NSScreen.mainFrameHeight/2,
|
||||
width: 100,
|
||||
height: 100
|
||||
),
|
||||
styleMask: .borderless,
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
window.backgroundColor = .init(srgbRed: 1, green: 1, blue: 1, alpha: 0)
|
||||
window.contentView = NSImageView(image: NSImage(named: "pinch")!)
|
||||
window.isOpaque = false
|
||||
window.level = NSWindow.Level.statusBar + 1
|
||||
window.ignoresMouseEvents = true
|
||||
window.hasShadow = true
|
||||
window.collectionBehavior = NSWindow.CollectionBehavior.canJoinAllSpaces.union(.stationary)
|
||||
#if DEBUG
|
||||
show()
|
||||
#endif
|
||||
}
|
||||
|
||||
func move(to: CGPoint) {
|
||||
self.window.setFrameOrigin(to)
|
||||
}
|
||||
|
||||
func show() {
|
||||
window.orderFront(nil)
|
||||
}
|
||||
|
||||
func hide() {
|
||||
window.orderOut(nil)
|
||||
}
|
||||
}
|
||||
@@ -414,19 +414,6 @@
|
||||
</items>
|
||||
<point key="canvasLocation" x="200" y="121"/>
|
||||
</menu>
|
||||
<menu id="ONX-cK-IuT">
|
||||
<items>
|
||||
<menuItem title="Item 1" id="6WY-F1-Y6A">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Item 2" id="Vew-Im-EWB">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Item 3" id="dVu-3p-SKK">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="-160" y="321"/>
|
||||
</menu>
|
||||
<userDefaultsController representsSharedInstance="YES" id="KLo-tU-Av3"/>
|
||||
</objects>
|
||||
</document>
|
||||
|
||||
8
DockPhobia/Views/Preferences.swift
Normal file
8
DockPhobia/Views/Preferences.swift
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Preferences.swift
|
||||
// DockPhobia
|
||||
//
|
||||
// Created by neon443 on 29/05/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
BIN
DockPhobiaPy/.DS_Store
vendored
BIN
DockPhobiaPy/.DS_Store
vendored
Binary file not shown.
40
README.md
40
README.md
@@ -1,4 +1,26 @@
|
||||
# DockPhobia
|
||||
<div align="center">
|
||||
<br/>
|
||||
<p>
|
||||
<img src="https://github.com/neon443/DockPhobia/blob/main/DockPhobia/Resources/Assets.xcassets/AppIcon.appiconset/DockPhobiaAppIcon.png?raw=true" title="dockphobia" alt="dockphobia icon" width="100" />
|
||||
</p>
|
||||
<h3>DockPhobia</h3>
|
||||
<p>
|
||||
<a href="https://github.com/neon443/DockPhobia/releases/latest/download/DockPhobia.dmg">
|
||||
download
|
||||
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/neon443/DockPhobia">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
make your Dock scared of the mouse
|
||||
<br/>
|
||||
<a href="https://neon443.github.io">
|
||||
made by neon443
|
||||
</a>
|
||||
</p>
|
||||
<br/>
|
||||
</div>
|
||||
|
||||

|
||||
|
||||
Have you ever wanted to use your Dock?
|
||||
well now you cant
|
||||
@@ -8,17 +30,17 @@ well now you cant
|
||||
- calculates the size of your screen & Dock
|
||||
- when your cursor is on the Dock (+ extra 5% of screen), the Dock moves to a different side of the screen
|
||||
- it also figures out the furthest it can be, for example if your cursor is at the top left quarter of the screen and near the Dock on the left, the dock moves to the bottom as that is the furthest, etc
|
||||
- -
|
||||
|
||||
## Quick start Guide (Swift Mac App)
|
||||
(Currently not functional)
|
||||
```
|
||||
git clone https://github.com/neon443/DockPhobia
|
||||
cd DockPhobia
|
||||
open DockPhobia.xcodeproj
|
||||
```
|
||||
## Quick start Guide (Mac App)
|
||||
- Go to [releases](https://github.com/neon443/DockPhobia/releases)
|
||||
- Download the latest version
|
||||
- That's it!
|
||||
|
||||
## Contributing
|
||||
PRs welcome, as long as you use tabs instead of spaces lol
|
||||
|
||||
## Quick start Guide (Python version)
|
||||
Dont use this, still here purely for archival purposes, use the native Mac app instead.
|
||||
```
|
||||
xcode-select --install
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
|
||||
Reference in New Issue
Block a user