diff --git a/SpacesRenamer/AppDelegate.swift b/SpacesRenamer/AppDelegate.swift index 8d77773..ef8ce0c 100644 --- a/SpacesRenamer/AppDelegate.swift +++ b/SpacesRenamer/AppDelegate.swift @@ -7,13 +7,14 @@ // import Cocoa +import Foundation @NSApplicationMain @objc class AppDelegate: NSObject, NSApplicationDelegate { - let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) - let popover = NSPopover() + var nameChangeWindow: NameChangeWindow = NameChangeWindow() + let hiddenPopover = NSPopover() var eventMonitor: EventMonitor? var workspace: NSWorkspace? @@ -37,6 +38,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { ) } + // Watches the file to determine if the spaces update (new one added or deleted) fileprivate func configureSpaceMonitor() { let fullPath = (Utils.spacesPath as NSString).expandingTildeInPath let queue = DispatchQueue.global(qos: .default) @@ -64,23 +66,42 @@ class AppDelegate: NSObject, NSApplicationDelegate { source.resume() } + // Runs when a space is moved or switched, which confirms that the current list is in the right order @objc func updateActiveSpaces() { let info = CGSCopyManagedDisplaySpaces(conn) as! [NSDictionary] let spacesDict = NSMutableDictionary() spacesDict.setValue(info, forKey: "Monitors") spacesDict.write(toFile: Utils.listOfSpacesPlist, atomically: true) + + if (nameChangeWindow.isVisible) { + nameChangeWindow.refresh() + } } func applicationDidFinishLaunching(_ aNotification: Notification) { if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarIcon")) - button.action = #selector(togglePopover(_:)) } - popover.contentViewController = ViewController.freshController() + + // Listen for left click + NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { [weak self] event in + if event.window == self?.statusItem.button?.window { + self?.togglePopover(self?.statusItem.button) + return nil + } + + return event + } + + nameChangeWindow.contentViewController = ViewController.freshController() + hiddenPopover.contentViewController = ViewController.freshController() + hiddenPopover.animates = false eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in - if let strongSelf = self, strongSelf.popover.isShown { - strongSelf.closePopover(sender: event) + if let strongSelf = self { + if (strongSelf.nameChangeWindow.isVisible) { + strongSelf.closeNameChangeWindow(sender: event) + } } } @@ -100,24 +121,35 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc func togglePopover(_ sender: Any?) { - if popover.isShown { - closePopover(sender: sender) + if nameChangeWindow.isVisible { + closeNameChangeWindow(sender: sender) } else { - showPopover(sender: sender) + showNameChangeWindow(sender: sender) } } - func showPopover(sender: Any?) { + func showNameChangeWindow(sender: Any?) { NSApplication.shared.activate(ignoringOtherApps: true) eventMonitor?.start() + self.statusItem.button?.isHighlighted = true if let button = statusItem.button { - popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) + // Use the hidden popover to get the dimensions, and then immediately hide it + hiddenPopover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) + if let frame = hiddenPopover.contentViewController?.view.window?.frame { + nameChangeWindow.setFrame(frame, display: true) + } + hiddenPopover.close() + + nameChangeWindow.makeKeyAndOrderFront(nil) + NSApp.activate(ignoringOtherApps: true) } } - func closePopover(sender: Any?) { - popover.performClose(sender) + @objc func closeNameChangeWindow(sender: Any?) { + nameChangeWindow.setIsVisible(false) + DispatchQueue.main.async { + self.statusItem.button?.isHighlighted = false + } eventMonitor?.stop() } } - diff --git a/SpacesRenamer/Info.plist b/SpacesRenamer/Info.plist index c3bfe6c..5440f28 100644 --- a/SpacesRenamer/Info.plist +++ b/SpacesRenamer/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.0 + 1.3.1 CFBundleVersion 1 LSMinimumSystemVersion diff --git a/SpacesRenamer/NameChangeWindow.swift b/SpacesRenamer/NameChangeWindow.swift new file mode 100644 index 0000000..4ac2e66 --- /dev/null +++ b/SpacesRenamer/NameChangeWindow.swift @@ -0,0 +1,52 @@ +// +// NameChangeWindow.swift +// SpacesRenamer +// +// Created by Alex Beals on 4/1/18. +// Copyright © 2018 Alex Beals. All rights reserved. +// + +import Cocoa + +class NameChangeWindow: NSWindow { + override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) { + super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag) + self.title = "Spaces Renamer" + self.isOpaque = false + self.isMovable = false + self.backgroundColor = NSColor(calibratedHue: 0, saturation: 0.0, brightness: 100, alpha: 0.95) + // To make it auto-hide on F3 + self.collectionBehavior = [.transient, .ignoresCycle, .canJoinAllSpaces] + self.level = .modalPanel + + // Adapted from https://stackoverflow.com/a/27613308/3951475 for rounded corners + self.styleMask = [.resizable, .titled, .fullSizeContentView] + self.titlebarAppearsTransparent = true + self.titleVisibility = .hidden + self.showsToolbarButton = false + + self.standardWindowButton(.miniaturizeButton)?.isHidden = true + self.standardWindowButton(.closeButton)?.isHidden = true + self.standardWindowButton(.zoomButton)?.isHidden = true + } + + func refresh() { + DispatchQueue.main.async { + if let appDelegate = NSApplication.shared.delegate as? AppDelegate, let button = appDelegate.statusItem.button { + // Use the hidden popover to get the dimensions, and then immediately hide it + appDelegate.hiddenPopover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) + if let frame = appDelegate.hiddenPopover.contentViewController?.view.window?.frame { + appDelegate.nameChangeWindow.setFrame(frame, display: true) + } + appDelegate.hiddenPopover.close() + + if let viewController = appDelegate.nameChangeWindow.contentViewController as? ViewController { + viewController.refreshViews() + } + + appDelegate.nameChangeWindow.makeKeyAndOrderFront(nil) + NSApp.activate(ignoringOtherApps: true) + } + } + } +} diff --git a/SpacesRenamer/ViewController.swift b/SpacesRenamer/ViewController.swift index 0604bb3..b8cb74a 100644 --- a/SpacesRenamer/ViewController.swift +++ b/SpacesRenamer/ViewController.swift @@ -9,7 +9,6 @@ import Cocoa class ViewController: NSViewController { - @IBOutlet var updateButton: NSButton! var desktops: [String: NSTextField] = [String: NSTextField]() @@ -43,7 +42,7 @@ class ViewController: NSViewController { var prev: DesktopSnippet? var above: NSView? - print(spacesDict) + let maxSpacesPerMonitor = allMonitors.reduce(Int.min, { max($0, (($1 as? NSDictionary)?.value(forKey: "Spaces") as! NSArray).count) }) for j in 1...allMonitors.count { let allSpaces = (allMonitors[j-1] as? NSDictionary)?.value(forKey: "Spaces") as! NSArray @@ -82,7 +81,6 @@ class ViewController: NSViewController { let snippet = DesktopSnippet.instanceFromNib() if (uuid == currentSpace) { snippet.monitorImage.image = NSImage(named: NSImage.Name("MonitorSelected") ) - // snippet.starImage.isHidden = false } snippet.label.stringValue = "\(i)" @@ -113,9 +111,11 @@ class ViewController: NSViewController { } above = prev - let horizontalLayout = NSLayoutConstraint(item: self.view, attribute: .trailing, relatedBy: .greaterThanOrEqual, toItem: prev!, attribute: .trailing, multiplier: 1.0, constant: 10) - constraints.append(horizontalLayout) - self.view.addConstraints([horizontalLayout]) + if (allSpaces.count == maxSpacesPerMonitor) { + let horizontalLayout = NSLayoutConstraint(item: self.view, attribute: .trailing, relatedBy: .equal, toItem: prev!, attribute: .trailing, multiplier: 1.0, constant: 10) + constraints.append(horizontalLayout) + self.view.addConstraints([horizontalLayout]) + } } let verticalConstraint = NSLayoutConstraint(item: updateButton, attribute: .top, relatedBy: .equal, toItem: prev!, attribute: .bottom, multiplier: 1.0, constant: 10) @@ -124,9 +124,7 @@ class ViewController: NSViewController { self.view.addConstraints([verticalConstraint]) } - override func viewWillAppear() { - super.viewWillAppear() - + func refreshViews() { teardownViews() setupViews() @@ -135,7 +133,7 @@ class ViewController: NSViewController { let spacesRemaining = preferencesDict.value(forKey: "spaces_renaming") as? NSMutableDictionary { currentMapping = spacesRemaining } - + // Update with the current names for (uuid, textField) in desktops { if let newName = currentMapping.value(forKey: uuid) { @@ -144,6 +142,12 @@ class ViewController: NSViewController { } } + override func viewWillAppear() { + super.viewWillAppear() + + refreshViews() + } + @IBAction func quitMenuApp(_ sender: Any) { NSApp.terminate(nil) } @@ -165,7 +169,7 @@ class ViewController: NSViewController { // Close the popup let delegate = NSApplication.shared.delegate as! AppDelegate - delegate.closePopover(sender: delegate) + delegate.closeNameChangeWindow(sender: delegate) } } diff --git a/build/spaces-renamer.zip b/build/spaces-renamer.zip index 084b821..8b8169a 100644 Binary files a/build/spaces-renamer.zip and b/build/spaces-renamer.zip differ diff --git a/spaces-renamer.xcodeproj/project.pbxproj b/spaces-renamer.xcodeproj/project.pbxproj index bbbe14a..d8c4eba 100755 --- a/spaces-renamer.xcodeproj/project.pbxproj +++ b/spaces-renamer.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ A80DDE3C1FBCEBDE00794F8F /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80DDE3B1FBCEBDE00794F8F /* Utils.swift */; }; A80DDE421FBE2D9C00794F8F /* DesktopSnippet.xib in Resources */ = {isa = PBXBuildFile; fileRef = A80DDE411FBE2D9C00794F8F /* DesktopSnippet.xib */; }; A80DDE441FBE2ECA00794F8F /* DesktopSnippet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80DDE431FBE2ECA00794F8F /* DesktopSnippet.swift */; }; + A81F4EFC2070AEED00660338 /* NameChangeWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81F4EFB2070AEED00660338 /* NameChangeWindow.swift */; }; C398D84715BD8B0700543187 /* spacesRenamer.m in Sources */ = {isa = PBXBuildFile; fileRef = C398D84615BD8B0700543187 /* spacesRenamer.m */; }; FBAE21EE1CFCCA4F007E5A8E /* ZKSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = FBAE21ED1CFCCA4F007E5A8E /* ZKSwizzle.m */; }; /* End PBXBuildFile section */ @@ -34,6 +35,7 @@ A80DDE411FBE2D9C00794F8F /* DesktopSnippet.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DesktopSnippet.xib; sourceTree = ""; }; A80DDE431FBE2ECA00794F8F /* DesktopSnippet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesktopSnippet.swift; sourceTree = ""; }; A81F4EFA206C012500660338 /* SpacesRenamerBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpacesRenamerBridge.h; sourceTree = ""; }; + A81F4EFB2070AEED00660338 /* NameChangeWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NameChangeWindow.swift; sourceTree = ""; }; C398D84615BD8B0700543187 /* spacesRenamer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = spacesRenamer.m; path = "spaces-renamer/spacesRenamer.m"; sourceTree = ""; }; FBAE21EC1CFCCA4F007E5A8E /* ZKSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZKSwizzle.h; path = "spaces-renamer/ZKSwizzle.h"; sourceTree = ""; }; FBAE21ED1CFCCA4F007E5A8E /* ZKSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ZKSwizzle.m; path = "spaces-renamer/ZKSwizzle.m"; sourceTree = ""; }; @@ -99,6 +101,7 @@ isa = PBXGroup; children = ( A80DDE2B1FBCE4D600794F8F /* AppDelegate.swift */, + A81F4EFB2070AEED00660338 /* NameChangeWindow.swift */, A81F4EFA206C012500660338 /* SpacesRenamerBridge.h */, A80DDE391FBCE89B00794F8F /* EventMonitor.swift */, A80DDE2D1FBCE4D600794F8F /* ViewController.swift */, @@ -221,6 +224,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A81F4EFC2070AEED00660338 /* NameChangeWindow.swift in Sources */, A80DDE3A1FBCE89B00794F8F /* EventMonitor.swift in Sources */, A80DDE441FBE2ECA00794F8F /* DesktopSnippet.swift in Sources */, A80DDE2E1FBCE4D600794F8F /* ViewController.swift in Sources */, diff --git a/spaces-renamer.xcodeproj/project.xcworkspace/xcuserdata/alexbeals.xcuserdatad/UserInterfaceState.xcuserstate b/spaces-renamer.xcodeproj/project.xcworkspace/xcuserdata/alexbeals.xcuserdatad/UserInterfaceState.xcuserstate index 0b94671..2507816 100644 Binary files a/spaces-renamer.xcodeproj/project.xcworkspace/xcuserdata/alexbeals.xcuserdatad/UserInterfaceState.xcuserstate and b/spaces-renamer.xcodeproj/project.xcworkspace/xcuserdata/alexbeals.xcuserdatad/UserInterfaceState.xcuserstate differ