macOS: Different status bar icon looks for different states
- Looks dimmed when no tunnel is active - Looks normal when a tunnel is active - Animates when a tunnel is activating
|
@ -54,6 +54,7 @@
|
||||||
6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774EE21722D97006A79B3 /* TunnelsManager.swift */; };
|
6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774EE21722D97006A79B3 /* TunnelsManager.swift */; };
|
||||||
6F7774F321774263006A79B3 /* TunnelEditTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774F221774263006A79B3 /* TunnelEditTableViewController.swift */; };
|
6F7774F321774263006A79B3 /* TunnelEditTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774F221774263006A79B3 /* TunnelEditTableViewController.swift */; };
|
||||||
6F7F7E5F21C7D74B00527607 /* TunnelErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7F7E5E21C7D74B00527607 /* TunnelErrors.swift */; };
|
6F7F7E5F21C7D74B00527607 /* TunnelErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7F7E5E21C7D74B00527607 /* TunnelErrors.swift */; };
|
||||||
|
6F89E17A21EDEB0E00C97BB9 /* StatusItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F89E17921EDEB0E00C97BB9 /* StatusItemController.swift */; };
|
||||||
6F919EC3218A2AE90023B400 /* ErrorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */; };
|
6F919EC3218A2AE90023B400 /* ErrorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */; };
|
||||||
6F919ED9218C65C50023B400 /* wireguard_doc_logo_22x29.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED5218C65C50023B400 /* wireguard_doc_logo_22x29.png */; };
|
6F919ED9218C65C50023B400 /* wireguard_doc_logo_22x29.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED5218C65C50023B400 /* wireguard_doc_logo_22x29.png */; };
|
||||||
6F919EDA218C65C50023B400 /* wireguard_doc_logo_44x58.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */; };
|
6F919EDA218C65C50023B400 /* wireguard_doc_logo_44x58.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */; };
|
||||||
|
@ -263,6 +264,7 @@
|
||||||
6F7774EE21722D97006A79B3 /* TunnelsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsManager.swift; sourceTree = "<group>"; };
|
6F7774EE21722D97006A79B3 /* TunnelsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsManager.swift; sourceTree = "<group>"; };
|
||||||
6F7774F221774263006A79B3 /* TunnelEditTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelEditTableViewController.swift; sourceTree = "<group>"; };
|
6F7774F221774263006A79B3 /* TunnelEditTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelEditTableViewController.swift; sourceTree = "<group>"; };
|
||||||
6F7F7E5E21C7D74B00527607 /* TunnelErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelErrors.swift; sourceTree = "<group>"; };
|
6F7F7E5E21C7D74B00527607 /* TunnelErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelErrors.swift; sourceTree = "<group>"; };
|
||||||
|
6F89E17921EDEB0E00C97BB9 /* StatusItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemController.swift; sourceTree = "<group>"; };
|
||||||
6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorPresenter.swift; sourceTree = "<group>"; };
|
6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorPresenter.swift; sourceTree = "<group>"; };
|
||||||
6F919ED5218C65C50023B400 /* wireguard_doc_logo_22x29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_22x29.png; sourceTree = "<group>"; };
|
6F919ED5218C65C50023B400 /* wireguard_doc_logo_22x29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_22x29.png; sourceTree = "<group>"; };
|
||||||
6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_44x58.png; sourceTree = "<group>"; };
|
6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_44x58.png; sourceTree = "<group>"; };
|
||||||
|
@ -512,6 +514,7 @@
|
||||||
6FBA104421D7EA750051C35F /* ViewController */,
|
6FBA104421D7EA750051C35F /* ViewController */,
|
||||||
6FBA101321D613F30051C35F /* Application.swift */,
|
6FBA101321D613F30051C35F /* Application.swift */,
|
||||||
6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */,
|
6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */,
|
||||||
|
6F89E17921EDEB0E00C97BB9 /* StatusItemController.swift */,
|
||||||
6FBA101621D655340051C35F /* StatusMenu.swift */,
|
6FBA101621D655340051C35F /* StatusMenu.swift */,
|
||||||
6FBA104121D6BC210051C35F /* ErrorPresenter.swift */,
|
6FBA104121D6BC210051C35F /* ErrorPresenter.swift */,
|
||||||
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */,
|
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */,
|
||||||
|
@ -1111,6 +1114,7 @@
|
||||||
6FB1BDBC21D50F0200A991BF /* ringlogger.c in Sources */,
|
6FB1BDBC21D50F0200A991BF /* ringlogger.c in Sources */,
|
||||||
6FB1BDBD21D50F0200A991BF /* ringlogger.h in Sources */,
|
6FB1BDBD21D50F0200A991BF /* ringlogger.h in Sources */,
|
||||||
6FBA103F21D6B6FF0051C35F /* TunnelImporter.swift in Sources */,
|
6FBA103F21D6B6FF0051C35F /* TunnelImporter.swift in Sources */,
|
||||||
|
6F89E17A21EDEB0E00C97BB9 /* StatusItemController.swift in Sources */,
|
||||||
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */,
|
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */,
|
||||||
5F52D0BF21E3788900283CEA /* NSColor+Hex.swift in Sources */,
|
5F52D0BF21E3788900283CEA /* NSColor+Hex.swift in Sources */,
|
||||||
6FB1BDBE21D50F0200A991BF /* Logger.swift in Sources */,
|
6FB1BDBE21D50F0200A991BF /* Logger.swift in Sources */,
|
||||||
|
|
|
@ -222,6 +222,10 @@ class TunnelsManager {
|
||||||
return tunnels.first { $0.name == tunnelName }
|
return tunnels.first { $0.name == tunnelName }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitingTunnel() -> TunnelContainer? {
|
||||||
|
return tunnels.first { $0.status == .waiting }
|
||||||
|
}
|
||||||
|
|
||||||
func startActivation(of tunnel: TunnelContainer) {
|
func startActivation(of tunnel: TunnelContainer) {
|
||||||
guard tunnels.contains(tunnel) else { return } // Ensure it's not deleted
|
guard tunnels.contains(tunnel) else { return } // Ensure it's not deleted
|
||||||
guard tunnel.status == .inactive else {
|
guard tunnel.status == .inactive else {
|
||||||
|
|
|
@ -6,7 +6,8 @@ import Cocoa
|
||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
var statusItem: NSStatusItem?
|
var statusItemController: StatusItemController?
|
||||||
|
var currentTunnelStatusObserver: AnyObject?
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
Logger.configureGlobal(withFilePath: FileManager.appLogFileURL?.path)
|
Logger.configureGlobal(withFilePath: FileManager.appLogFileURL?.path)
|
||||||
|
@ -19,21 +20,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tunnelsManager: TunnelsManager = result.value!
|
let tunnelsManager: TunnelsManager = result.value!
|
||||||
|
let statusItemController = StatusItemController()
|
||||||
|
|
||||||
let statusMenu = StatusMenu(tunnelsManager: tunnelsManager)
|
let statusMenu = StatusMenu(tunnelsManager: tunnelsManager)
|
||||||
self.statusItem = createStatusBarItem(with: statusMenu)
|
|
||||||
|
statusItemController.statusItem.menu = statusMenu
|
||||||
|
statusItemController.currentTunnel = statusMenu.currentTunnel
|
||||||
|
self.currentTunnelStatusObserver = statusMenu.observe(\.currentTunnel) { statusMenu, _ in
|
||||||
|
statusItemController.currentTunnel = statusMenu.currentTunnel
|
||||||
|
}
|
||||||
|
self.statusItemController = statusItemController
|
||||||
|
|
||||||
tunnelsManager.tunnelsListDelegate = statusMenu
|
tunnelsManager.tunnelsListDelegate = statusMenu
|
||||||
tunnelsManager.activationDelegate = statusMenu
|
tunnelsManager.activationDelegate = statusMenu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStatusBarItem(with statusMenu: StatusMenu) -> NSStatusItem {
|
|
||||||
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
|
||||||
if let statusBarImage = NSImage(named: "WireGuardMacStatusBarIcon") {
|
|
||||||
statusBarImage.isTemplate = true
|
|
||||||
statusItem.button?.image = statusBarImage
|
|
||||||
}
|
|
||||||
statusItem.menu = statusMenu
|
|
||||||
return statusItem
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,22 +2,25 @@
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "WireGuardMacStatusBarIcon@1x.png",
|
"filename" : "StatusBarIcon@1x.png",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "WireGuardMacStatusBarIcon@2x.png",
|
"filename" : "StatusBarIcon@2x.png",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "WireGuardMacStatusBarIcon@3x.png",
|
"filename" : "StatusBarIcon@3x.png",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"version" : 1,
|
||||||
"author" : "xcode"
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
}
|
}
|
||||||
}
|
}
|
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIcon.imageset/StatusBarIcon@1x.png
vendored
Normal file
After Width: | Height: | Size: 978 B |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIcon.imageset/StatusBarIcon@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIcon.imageset/StatusBarIcon@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.9 KiB |
26
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDimmed.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDimmed@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDimmed@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDimmed@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDimmed.imageset/StatusBarIconDimmed@1x.png
vendored
Normal file
After Width: | Height: | Size: 881 B |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDimmed.imageset/StatusBarIconDimmed@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDimmed.imageset/StatusBarIconDimmed@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
26
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot1.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot1@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot1@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot1@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot1.imageset/StatusBarIconDot1@1x.png
vendored
Normal file
After Width: | Height: | Size: 953 B |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot1.imageset/StatusBarIconDot1@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot1.imageset/StatusBarIconDot1@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.7 KiB |
26
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot2.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot2@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot2@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot2@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot2.imageset/StatusBarIconDot2@1x.png
vendored
Normal file
After Width: | Height: | Size: 942 B |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot2.imageset/StatusBarIconDot2@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot2.imageset/StatusBarIconDot2@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
26
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot3.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot3@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot3@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "StatusBarIconDot3@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot3.imageset/StatusBarIconDot3@1x.png
vendored
Normal file
After Width: | Height: | Size: 958 B |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot3.imageset/StatusBarIconDot3@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
WireGuard/WireGuard/UI/macOS/Assets.xcassets/StatusBarIconDot3.imageset/StatusBarIconDot3@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 990 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,66 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class StatusItemController {
|
||||||
|
var currentTunnel: TunnelContainer? {
|
||||||
|
didSet {
|
||||||
|
updateStatusItemImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
||||||
|
private let statusBarImageWhenActive = NSImage(named: "StatusBarIcon")!
|
||||||
|
private let statusBarImageWhenInactive = NSImage(named: "StatusBarIconDimmed")!
|
||||||
|
|
||||||
|
private let animationImages = [
|
||||||
|
NSImage(named: "StatusBarIconDot1")!,
|
||||||
|
NSImage(named: "StatusBarIconDot2")!,
|
||||||
|
NSImage(named: "StatusBarIconDot3")!
|
||||||
|
]
|
||||||
|
private var animationImageIndex: Int = 0
|
||||||
|
private var animationTimer: Timer?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
updateStatusItemImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateStatusItemImage() {
|
||||||
|
guard let currentTunnel = currentTunnel else {
|
||||||
|
stopActivatingAnimation()
|
||||||
|
statusItem.button?.image = statusBarImageWhenInactive
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch currentTunnel.status {
|
||||||
|
case .inactive:
|
||||||
|
stopActivatingAnimation()
|
||||||
|
statusItem.button?.image = statusBarImageWhenInactive
|
||||||
|
case .active:
|
||||||
|
stopActivatingAnimation()
|
||||||
|
statusItem.button?.image = statusBarImageWhenActive
|
||||||
|
case .activating, .waiting, .reasserting, .restarting:
|
||||||
|
startActivatingAnimation()
|
||||||
|
case .deactivating:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startActivatingAnimation() {
|
||||||
|
guard animationTimer == nil else { return }
|
||||||
|
let timer = Timer(timeInterval: 0.3, repeats: true) { [weak self] _ in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.statusItem.button?.image = self.animationImages[self.animationImageIndex]
|
||||||
|
self.animationImageIndex = (self.animationImageIndex + 1) % self.animationImages.count
|
||||||
|
}
|
||||||
|
RunLoop.main.add(timer, forMode: .default)
|
||||||
|
animationTimer = timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopActivatingAnimation() {
|
||||||
|
guard let timer = self.animationTimer else { return }
|
||||||
|
timer.invalidate()
|
||||||
|
animationTimer = nil
|
||||||
|
animationImageIndex = 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,8 @@ class StatusMenu: NSMenu {
|
||||||
var firstTunnelMenuItemIndex = 0
|
var firstTunnelMenuItemIndex = 0
|
||||||
var numberOfTunnelMenuItems = 0
|
var numberOfTunnelMenuItems = 0
|
||||||
|
|
||||||
|
@objc dynamic var currentTunnel: TunnelContainer?
|
||||||
|
|
||||||
var manageTunnelsRootVC: ManageTunnelsRootViewController?
|
var manageTunnelsRootVC: ManageTunnelsRootViewController?
|
||||||
lazy var manageTunnelsWindow: NSWindow = {
|
lazy var manageTunnelsWindow: NSWindow = {
|
||||||
manageTunnelsRootVC = ManageTunnelsRootViewController(tunnelsManager: tunnelsManager)
|
manageTunnelsRootVC = ManageTunnelsRootViewController(tunnelsManager: tunnelsManager)
|
||||||
|
@ -30,7 +32,11 @@ class StatusMenu: NSMenu {
|
||||||
addStatusMenuItems()
|
addStatusMenuItems()
|
||||||
addItem(NSMenuItem.separator())
|
addItem(NSMenuItem.separator())
|
||||||
for index in 0 ..< tunnelsManager.numberOfTunnels() {
|
for index in 0 ..< tunnelsManager.numberOfTunnels() {
|
||||||
let isUpdated = updateStatusMenuItems(with: tunnelsManager.tunnel(at: index), ignoreInactive: true)
|
let tunnel = tunnelsManager.tunnel(at: index)
|
||||||
|
if tunnel.status != .inactive {
|
||||||
|
currentTunnel = tunnel
|
||||||
|
}
|
||||||
|
let isUpdated = updateStatusMenuItems(with: tunnel, ignoreInactive: true)
|
||||||
if isUpdated {
|
if isUpdated {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -176,6 +182,13 @@ extension StatusMenu {
|
||||||
updateTunnelMenuItem(menuItem)
|
updateTunnelMenuItem(menuItem)
|
||||||
let statusObservationToken = tunnel.observe(\.status) { [weak self] tunnel, _ in
|
let statusObservationToken = tunnel.observe(\.status) { [weak self] tunnel, _ in
|
||||||
updateTunnelMenuItem(menuItem)
|
updateTunnelMenuItem(menuItem)
|
||||||
|
if tunnel.status == .deactivating || tunnel.status == .inactive {
|
||||||
|
if self?.currentTunnel == tunnel {
|
||||||
|
self?.currentTunnel = self?.tunnelsManager.waitingTunnel()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self?.currentTunnel = tunnel
|
||||||
|
}
|
||||||
self?.updateStatusMenuItems(with: tunnel, ignoreInactive: false)
|
self?.updateStatusMenuItems(with: tunnel, ignoreInactive: false)
|
||||||
}
|
}
|
||||||
tunnelStatusObservers.insert(statusObservationToken, at: tunnelIndex)
|
tunnelStatusObservers.insert(statusObservationToken, at: tunnelIndex)
|
||||||
|
|