mirror of
https://github.com/neon443/DockPhobia.git
synced 2026-03-11 06:49:12 +00:00
Compare commits
20 Commits
1.0.1
...
58c834f1ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58c834f1ed | ||
|
|
84326f7e15 | ||
|
|
27e410ae76 | ||
|
|
d943415b98 | ||
|
|
83efb14bd5 | ||
|
|
d60d90bc60 | ||
|
|
4e55cc4516 | ||
|
|
1a0d3c2e38 | ||
|
|
e46edecec7 | ||
|
|
22c825dbb2 | ||
|
|
5357ce8443 | ||
|
|
853fc2a2db | ||
|
|
6a68713ad5 | ||
|
|
b15116b292 | ||
|
|
e4c41c4a58 | ||
|
|
c099d4bec3 | ||
|
|
5718e42f41 | ||
|
|
936f18d714 | ||
|
|
3e7a7c2ac2 | ||
|
|
43863d3827 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1 @@
|
|||||||
.DS_Store
|
**.DS_Store
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
VERSION = 1.0.1
|
VERSION = 1.0.1
|
||||||
BUILD_ID = 1
|
BUILD_ID = 2
|
||||||
TEAM_ID = 8JGND254B7
|
TEAM_ID = 8JGND254B7
|
||||||
BUNDLE_ID = com.neon443.DockPhobia
|
BUNDLE_ID = com.neon443.DockPhobia
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 77;
|
objectVersion = 63;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -11,6 +11,10 @@
|
|||||||
A966B4F42DE0842500C721A5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A966B4EE2DE0842400C721A5 /* AppDelegate.swift */; };
|
A966B4F42DE0842500C721A5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A966B4EE2DE0842400C721A5 /* AppDelegate.swift */; };
|
||||||
A966B4F52DE0842500C721A5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A966B4EF2DE0842400C721A5 /* Assets.xcassets */; };
|
A966B4F52DE0842500C721A5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A966B4EF2DE0842400C721A5 /* Assets.xcassets */; };
|
||||||
A966B4F82DE0852900C721A5 /* MouseTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A966B4F72DE0852900C721A5 /* MouseTracker.swift */; };
|
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 */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -21,6 +25,10 @@
|
|||||||
A966B4F02DE0842400C721A5 /* DockPhobia.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DockPhobia.entitlements; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -46,6 +54,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A94BEC0E2DE23E8500D4811D /* MainMenu.xib */,
|
A94BEC0E2DE23E8500D4811D /* MainMenu.xib */,
|
||||||
|
A9C9B0672DE888B20039D7A5 /* Preferences.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -72,6 +81,9 @@
|
|||||||
children = (
|
children = (
|
||||||
A966B4EE2DE0842400C721A5 /* AppDelegate.swift */,
|
A966B4EE2DE0842400C721A5 /* AppDelegate.swift */,
|
||||||
A966B4F72DE0852900C721A5 /* MouseTracker.swift */,
|
A966B4F72DE0852900C721A5 /* MouseTracker.swift */,
|
||||||
|
A9C9AF822DE77CB70039D7A5 /* SkyHigh.swift */,
|
||||||
|
A98C20C52DE614180008D61C /* DPSettings.swift */,
|
||||||
|
A9C9AF802DE777530039D7A5 /* DockSide.swift */,
|
||||||
A94BEC102DE23ECE00D4811D /* Views */,
|
A94BEC102DE23ECE00D4811D /* Views */,
|
||||||
A94BEC0A2DE21F8100D4811D /* Resources */,
|
A94BEC0A2DE21F8100D4811D /* Resources */,
|
||||||
A966B4F02DE0842400C721A5 /* DockPhobia.entitlements */,
|
A966B4F02DE0842400C721A5 /* DockPhobia.entitlements */,
|
||||||
@@ -117,6 +129,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = A966B4D12DE0841000C721A5 /* Build configuration list for PBXProject "DockPhobia" */;
|
buildConfigurationList = A966B4D12DE0841000C721A5 /* Build configuration list for PBXProject "DockPhobia" */;
|
||||||
|
compatibilityVersion = "Xcode 12.0";
|
||||||
developmentRegion = en;
|
developmentRegion = en;
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
knownRegions = (
|
knownRegions = (
|
||||||
@@ -125,7 +138,6 @@
|
|||||||
);
|
);
|
||||||
mainGroup = A966B4CD2DE0841000C721A5;
|
mainGroup = A966B4CD2DE0841000C721A5;
|
||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
preferredProjectObjectVersion = 77;
|
|
||||||
productRefGroup = A966B4D72DE0841000C721A5 /* Products */;
|
productRefGroup = A966B4D72DE0841000C721A5 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@@ -152,8 +164,12 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
A98C20C62DE614180008D61C /* DPSettings.swift in Sources */,
|
||||||
|
A9C9AF832DE77CB70039D7A5 /* SkyHigh.swift in Sources */,
|
||||||
|
A9C9B0682DE888B20039D7A5 /* Preferences.swift in Sources */,
|
||||||
A966B4F82DE0852900C721A5 /* MouseTracker.swift in Sources */,
|
A966B4F82DE0852900C721A5 /* MouseTracker.swift in Sources */,
|
||||||
A966B4F42DE0842500C721A5 /* AppDelegate.swift in Sources */,
|
A966B4F42DE0842500C721A5 /* AppDelegate.swift in Sources */,
|
||||||
|
A9C9AF812DE7776A0039D7A5 /* DockSide.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -294,6 +310,7 @@
|
|||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||||
INFOPLIST_KEY_LSUIElement = YES;
|
INFOPLIST_KEY_LSUIElement = YES;
|
||||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = _;
|
INFOPLIST_KEY_NSAppleEventsUsageDescription = _;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
@@ -328,6 +345,7 @@
|
|||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||||
INFOPLIST_KEY_LSUIElement = YES;
|
INFOPLIST_KEY_LSUIElement = YES;
|
||||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = _;
|
INFOPLIST_KEY_NSAppleEventsUsageDescription = _;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
|||||||
BIN
DockPhobia/.DS_Store
vendored
BIN
DockPhobia/.DS_Store
vendored
Binary file not shown.
@@ -7,30 +7,36 @@
|
|||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
@main
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
public var statusItem: NSStatusItem!
|
public var statusItem: NSStatusItem!
|
||||||
|
|
||||||
var mouseTracker = MouseTracker()
|
var settings = DPSettingsModel()
|
||||||
|
var mouseTracker: MouseTracker
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
self.mouseTracker = MouseTracker(settings: settings)
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||||
if let button = statusItem.button {
|
if let button = statusItem.button {
|
||||||
button.image = NSImage(named: "cursor.slash")
|
button.image = NSImage(named: "cursor.slash")
|
||||||
}
|
}
|
||||||
setupMenus()
|
refreshMenus()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
// Insert code here to tear down your application
|
settings.saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: menu bar stuff
|
||||||
func setupMenus() {
|
func setupMenus() {
|
||||||
let menu = NSMenu()
|
let menu = NSMenu()
|
||||||
let start = NSMenuItem(title: describeStartButton(), action: #selector(didTapStart), keyEquivalent: "")
|
let start = NSMenuItem(title: describeStartButton(), action: #selector(didTapStart), keyEquivalent: "")
|
||||||
@@ -43,37 +49,58 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
)
|
)
|
||||||
menu.addItem(screen)
|
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.separator())
|
||||||
|
|
||||||
menu.addItem(
|
let moveMouseButton = NSMenuItem(
|
||||||
NSMenuItem(
|
title: "Move cursor instead",
|
||||||
title: "Move Dock to left",
|
action: #selector(moveMouseToggle),
|
||||||
action: #selector(moveDockObjcLeft),
|
keyEquivalent: ""
|
||||||
keyEquivalent: ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
menu.addItem(
|
|
||||||
NSMenuItem(
|
|
||||||
title: "Move Dock to bottom",
|
|
||||||
action: #selector(moveDockObjcBottom),
|
|
||||||
keyEquivalent: ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
menu.addItem(
|
|
||||||
NSMenuItem(
|
|
||||||
title: "Move Dock to right",
|
|
||||||
action: #selector(moveDockObjcRight),
|
|
||||||
keyEquivalent: ""
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
moveMouseButton.state = NSControl.StateValue(rawValue: settings.settings.moveMouseInstead ? 1 : 0)
|
||||||
|
menu.addItem(moveMouseButton)
|
||||||
|
|
||||||
menu.addItem(NSMenuItem.separator())
|
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 the bottom",
|
||||||
|
action: #selector(moveDockObjcBottom),
|
||||||
|
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(quit), keyEquivalent: "q")
|
let quit = NSMenuItem(title: "Quit", action: #selector(quit), keyEquivalent: "q")
|
||||||
menu.addItem(quit)
|
menu.addItem(quit)
|
||||||
statusItem.menu = menu
|
statusItem.menu = menu
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeMenuIcon(running: Bool) {
|
func changeMenuIcon(running: Bool) {
|
||||||
guard let button = statusItem.button else { return }
|
guard let button = statusItem.button else { return }
|
||||||
switch running {
|
switch running {
|
||||||
@@ -83,7 +110,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
button.image = NSImage(named: "cursor.slash")
|
button.image = NSImage(named: "cursor.slash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func didTapStart() {
|
@objc func didTapStart() {
|
||||||
if mouseTracker.running {
|
if mouseTracker.running {
|
||||||
mouseTracker.stop()
|
mouseTracker.stop()
|
||||||
@@ -92,17 +118,22 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
mouseTracker.start()
|
mouseTracker.start()
|
||||||
changeMenuIcon(running: true)
|
changeMenuIcon(running: true)
|
||||||
}
|
}
|
||||||
setupMenus()
|
refreshMenus()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func quit() {
|
@objc func quit() {
|
||||||
NSApplication.shared.terminate(self)
|
NSApplication.shared.terminate(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func moveDockObjcLeft() { mouseTracker.moveDock(.left) }
|
@objc func moveDockObjcLeft() { mouseTracker.moveDock(.left) }
|
||||||
@objc func moveDockObjcRight() { mouseTracker.moveDock(.right) }
|
@objc func moveDockObjcRight() { mouseTracker.moveDock(.right) }
|
||||||
@objc func moveDockObjcBottom() { mouseTracker.moveDock(.bottom) }
|
@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 {
|
func describeStartButton() -> String {
|
||||||
if mouseTracker.running {
|
if mouseTracker.running {
|
||||||
return "Stop tracking"
|
return "Stop tracking"
|
||||||
@@ -111,3 +142,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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,128 +7,125 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import AppKit
|
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 {
|
struct Screen {
|
||||||
var width: CGFloat
|
var width: CGFloat
|
||||||
var height: 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 {
|
class MouseTracker {
|
||||||
var screen: Screen
|
var screen: Screen
|
||||||
|
|
||||||
var monitor: Any?
|
var monitor: Any?
|
||||||
|
|
||||||
var running: Bool = false
|
var running: Bool = false
|
||||||
|
|
||||||
var currentDockSide: DockSide
|
var currentDockSide: DockSide
|
||||||
|
|
||||||
var dockHeight: CGFloat = 0
|
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())
|
print(DockSide())
|
||||||
if let screen = NSScreen.main {
|
|
||||||
let rect = screen.frame
|
self.screen = Screen(
|
||||||
self.screen = Screen(
|
width: NSScreen.mainFrameWidth,
|
||||||
width: rect.width,
|
height: NSScreen.mainFrameHeight
|
||||||
height: rect.height
|
)
|
||||||
)
|
print(self.screen)
|
||||||
print(self.screen)
|
|
||||||
} else {
|
self.settings = settings
|
||||||
fatalError("no screen wtf???")
|
self.skyHigh = SkyHigh(settings: settings)
|
||||||
}
|
|
||||||
self.currentDockSide = .left
|
self.currentDockSide = .left
|
||||||
moveDock(.bottom)
|
moveDock(.bottom)
|
||||||
getDockSize()
|
getDockSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkMouse(_ event: NSEvent) {
|
func checkMouse(_ event: NSEvent) {
|
||||||
var location = event.locationInWindow
|
let location = event.mouseLocationCG
|
||||||
location.y = screen.height - location.y
|
#if DEBUG
|
||||||
#if DEBUG
|
var cgpointForSkyHigh = NSEvent.mouseLocation
|
||||||
print(location)
|
cgpointForSkyHigh.x -= 20
|
||||||
#endif
|
cgpointForSkyHigh.y -= 5
|
||||||
switch currentDockSide {
|
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:
|
case .left:
|
||||||
guard location.x < dockHeight else { return }
|
guard location.x < dockHeight else { return }
|
||||||
if location.y < screen.height/2 {
|
if location.y < screen.height/2 {
|
||||||
moveDock(.bottom)
|
moveDockOrMouse(.bottom)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
moveDock(.right)
|
moveDockOrMouse(.right)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case .bottom:
|
case .bottom:
|
||||||
guard location.y > screen.height - dockHeight else { return }
|
guard location.y > screen.height - dockHeight else { return }
|
||||||
if location.x < screen.width/2 {
|
if location.x < screen.width/2 {
|
||||||
moveDock(.right)
|
moveDockOrMouse(.right)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
moveDock(.left)
|
moveDockOrMouse(.left)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case .right:
|
case .right:
|
||||||
guard location.x > screen.width - dockHeight else { return }
|
guard location.x > screen.width - dockHeight else { return }
|
||||||
if location.y < screen.height/2 {
|
if location.y < screen.height/2 {
|
||||||
moveDock(.bottom)
|
moveDockOrMouse(.bottom)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
moveDock(.left)
|
moveDockOrMouse(.left)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func moveDockOrMouse(_ dockTo: DockSide) {
|
||||||
|
if settings.settings.moveMouseInstead {
|
||||||
|
moveMouse()
|
||||||
|
} else {
|
||||||
|
moveDock(dockTo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
self.monitor = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved, handler: checkMouse)
|
self.monitor = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved, handler: checkMouse)
|
||||||
self.running = true
|
self.running = true
|
||||||
@@ -144,6 +141,40 @@ class MouseTracker {
|
|||||||
print("stop tracking")
|
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) {
|
func moveDock(_ toSide: DockSide) {
|
||||||
guard currentDockSide != toSide else { return }
|
guard currentDockSide != toSide else { return }
|
||||||
// let scriptHide = """
|
// let scriptHide = """
|
||||||
@@ -167,10 +198,11 @@ class MouseTracker {
|
|||||||
applescript(scriptMove)
|
applescript(scriptMove)
|
||||||
// applescript(scriptShow)
|
// applescript(scriptShow)
|
||||||
currentDockSide = toSide
|
currentDockSide = toSide
|
||||||
|
settings.settings.dockMoves += 1
|
||||||
|
refreshMenus()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDockSize() {
|
func getDockSize() {
|
||||||
guard let screen = NSScreen.main?.frame else { fatalError() }
|
|
||||||
guard let screenVisible = NSScreen.main?.visibleFrame else { fatalError() }
|
guard let screenVisible = NSScreen.main?.visibleFrame else { fatalError() }
|
||||||
self.dockHeight = screen.height - screenVisible.height
|
self.dockHeight = screen.height - screenVisible.height
|
||||||
}
|
}
|
||||||
@@ -187,3 +219,27 @@ class MouseTracker {
|
|||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
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.
18
README.md
18
README.md
@@ -1,9 +1,14 @@
|
|||||||
# DockPhobia
|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<br/>
|
<br/>
|
||||||
<p>
|
<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="200" />
|
<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>
|
||||||
<p>
|
<p>
|
||||||
make your Dock scared of the mouse
|
make your Dock scared of the mouse
|
||||||
@@ -12,14 +17,11 @@
|
|||||||
made by neon443
|
made by neon443
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
<a href="https://github.com/neon443/DockPhobia/releases">
|
|
||||||
download
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<br/>
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
Have you ever wanted to use your Dock?
|
Have you ever wanted to use your Dock?
|
||||||
well now you cant
|
well now you cant
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user