// // TrayIconController.swift // PrivadoVPN // // Created by Juraldinio on 12/7/20. // Copyright © 2020 Privado LLC. All rights reserved. // import Foundation import AppKit protocol TrayIconMenuItem { var title: String { get } var enabled: Bool { get } var keyEquivalent: String? { get } } protocol TrayIconControllerInput: AnyObject { func icon(named: String) func icon(by url: URL) func refresh() func menu(enabled: Bool) } protocol TrayIconControllerOutput: AnyObject { func showRequired() func hideRequired() func actionRequired() // func menuItemsCount() -> Int func menuItem(at index: Int) -> TrayIconMenuItem? func menuItem(selected row: Int) } final class TrayIconController: NSObject, TrayIconControllerModule, TrayIconControllerInput { private let output: TrayIconControllerOutput private let statusItem: NSStatusItem private let menu = NSMenu() init(output: TrayIconControllerOutput) { self.output = output self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) super.init() self.menu.delegate = self } // MARK: - Private @objc private func menuItemHandler(_ sender: Any?) { guard let menuItem = sender as? NSMenuItem else { return } self.output.menuItem(selected: menuItem.tag) } @objc private func onAction() { self.output.actionRequired() } // MARK: - TrayIconControllerModule var button: NSButton? { self.statusItem.button } func show() { self.output.showRequired() } func hide() { self.output.hideRequired() } // MARK: - TrayIconControllerInput func icon(named: String) { let image = NSImage(named: NSImage.Name(named)) guard let button = self.statusItem.button else { return } button.image = image } func icon(by url: URL) { let image = NSImage(byReferencing: url) guard let button = self.statusItem.button else { return } button.image = image } func refresh() { self.statusItem.menu?.update() } func menu(enabled: Bool) { if enabled { self.statusItem.menu = self.menu if let button = self.statusItem.button { button.target = nil button.action = nil } } else { self.statusItem.menu = nil if let button = self.statusItem.button { button.target = self button.action = #selector(self.onAction) } } } } extension TrayIconController: NSMenuDelegate { func numberOfItems(in menu: NSMenu) -> Int { return self.output.menuItemsCount() } func menu(_ menu: NSMenu, update item: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool { guard let menuItem = self.output.menuItem(at: index) else { return false } guard menuItem.title != "-" else { menu.removeItem(item) menu.insertItem(NSMenuItem.separator(), at: index) return true } if let key = menuItem.keyEquivalent { item.keyEquivalent = key } item.title = menuItem.title item.tag = index if menuItem.enabled { item.action = #selector(self.menuItemHandler) item.target = self } else { item.action = nil item.target = nil } return true } }