From 68ad4eec14e88b0a96f9b0af98c1b96df4e2eb97 Mon Sep 17 00:00:00 2001 From: meeh <meeh@mail.i2p> Date: Sat, 8 Dec 2018 09:05:55 +0000 Subject: [PATCH] Mac OS X Launcher: Adding user interface classes for the different Preferences views so far. --- .../preferences/AdvancedTableView.swift | 17 ++ .../PreferencesViewController+TableView.swift | 94 +++++++ .../PreferencesViewController.swift | 257 ++++++++++++++++++ .../PreferencesWindowController.swift | 24 ++ 4 files changed, 392 insertions(+) create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift new file mode 100644 index 0000000000..76a4bb3d17 --- /dev/null +++ b/launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift @@ -0,0 +1,17 @@ +// +// AdvancedTableView.swift +// I2PLauncher +// +// Created by Mikal Villa on 08/12/2018. +// Copyright © 2018 The I2P Project. All rights reserved. +// + +import Cocoa + + +class AdvancedTableView: NSTableView { + override func viewWillDraw() { + super.viewWillDraw() + } +} + diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift new file mode 100644 index 0000000000..1b009ddd63 --- /dev/null +++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift @@ -0,0 +1,94 @@ +// +// PreferencesViewController+TableView.swift +// I2PLauncher +// +// Created by Mikal Villa on 08/12/2018. +// Copyright © 2018 The I2P Project. All rights reserved. +// + +import Cocoa + +extension PreferencesViewController: NSTableViewDataSource { + + func numberOfRows(in tableView: NSTableView) -> Int { + return Preferences.shared().count + } + +} + +extension PreferencesViewController: NSTableViewDelegate { + + fileprivate enum CellIdentifiers { + static let NameCell = "KeyColumnID" + static let DefaultCell = "DefaultColumnID" + static let ValueCell = "ValueColumnID" + } + + func tableViewDoubleClick(_ sender:AnyObject) { + + // 1 + /*guard tableView.selectedRow >= 0, + let item = Preferences.shared()[tableView.selectedRow] else { + return + } + + if item.isFolder { + // 2 + self.representedObject = item.url as Any + } + else { + // 3 + NSWorkspace.shared().open(item.url as URL) + } + */ + } + + func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) { + // 1 + guard let sortDescriptor = tableView.sortDescriptors.first else { + return + } + /*if let order = Directory.FileOrder(rawValue: sortDescriptor.key!) { + // 2 + sortOrder = order + sortAscending = sortDescriptor.ascending + reloadFileList() + }*/ + } + + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + + //var image: NSImage? + var text: String = "" + var cellIdentifier: String = "" + + + // 1 + guard let item = Preferences.shared()[row] else { + return nil + } + + // 2 + if tableColumn == tableView.tableColumns[0] { + text = item.name! + cellIdentifier = CellIdentifiers.NameCell + } else if tableColumn == tableView.tableColumns[1] { + text = "\(item.defaultValue!)" + cellIdentifier = CellIdentifiers.DefaultCell + } else if tableColumn == tableView.tableColumns[2] { + let thing = (item.selectedValue ?? "none") + text = "\(thing)" + cellIdentifier = CellIdentifiers.ValueCell + } + + // 3 + if let cell = tableView.make(withIdentifier: cellIdentifier, owner: nil) as? NSTableCellView { + cell.textField?.stringValue = text + //cell.imageView?.image = image ?? nil + return cell + } + return nil + } + +} diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift new file mode 100644 index 0000000000..823a1d70ba --- /dev/null +++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift @@ -0,0 +1,257 @@ +// +// PreferencesViewController.swift +// I2PLauncher +// +// Created by Mikal Villa on 07/11/2018. +// Copyright © 2018 The I2P Project. All rights reserved. +// +// Table view programming guide from Apple: +// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TableView/Introduction/Introduction.html +// + +import Cocoa + + +class PreferencesViewController: NSViewController { + + enum ShowAsMode { + case bothIcon + case menubarIcon + case dockIcon + } + + var changeDockMenubarIconTimer: Timer? + + // MARK: - Advanced settings objects + @IBOutlet weak var advPrefTableView: NSTableView! + + // MARK: - Launcher settings objects + @IBOutlet var radioDockIcon: NSButton? + @IBOutlet var radioMenubarIcon: NSButton? + @IBOutlet var radioBothIcon: NSButton? + @IBOutlet var checkboxStartWithOSX: NSButton? + @IBOutlet var checkboxStartFirefoxAlso: NSButton? + + // MARK: - Router objects + @IBOutlet var checkboxStartWithLauncher: NSButton? + @IBOutlet var checkboxStopWithLauncher: NSButton? + @IBOutlet var buttonResetRouterConfig: NSButton? + + + override func viewDidLoad() { + super.viewDidLoad() + + self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height) + + if (advPrefTableView != nil) { + // For data feeding and view + advPrefTableView.delegate = self + advPrefTableView.dataSource = self + + // Responding to Double-Click + advPrefTableView.target = self + advPrefTableView.doubleAction = #selector(tableViewDoubleClick(_:)) + + // Always redraw preference items which might have changed state since last draw. + Preferences.shared().redrawPrefTableItems() + + // For sorting + advPrefTableView.tableColumns[0].sortDescriptorPrototype = NSSortDescriptor(key: "name", ascending: true) + advPrefTableView.tableColumns[1].sortDescriptorPrototype = NSSortDescriptor(key: "defaultValue", ascending: true) + advPrefTableView.tableColumns[2].sortDescriptorPrototype = NSSortDescriptor(key: "selectedValue", ascending: true) + } + + // Update radio buttons to reflect runtime/stored preferences + self.updateRadioButtonEffect(mode: Preferences.shared().showAsIconMode, withSideEffect: false) + + } + + override func viewDidAppear() { + super.viewDidAppear() + + // Update window title + self.parent?.view.window?.title = self.title! + } + + // MARK: - Router settings functions + + @IBAction func checkboxStartRouterWithLauncherClicked(_ sender: NSButton) { + switch sender.state { + case NSOnState: + print("on") + Preferences.shared().startRouterOnLauncherStart = true + case NSOffState: + print("off") + Preferences.shared().startRouterOnLauncherStart = false + case NSMixedState: + print("mixed") + default: break + } + } + + @IBAction func checkboxStopRouterWithLauncherClicked(_ sender: NSButton) { + switch sender.state { + case NSOnState: + print("on") + Preferences.shared().stopRouterOnLauncherShutdown = true + case NSOffState: + print("off") + Preferences.shared().stopRouterOnLauncherShutdown = false + case NSMixedState: + print("mixed") + default: break + } + } + + @IBAction func buttonResetRouterConfigClicked(_ sender: Any) { + // TODO: Add a modal dialog asking user if they are **really** sure + } + + // MARK: - Launcher settings functions + + @IBAction func checkboxStartLauncherOnOSXStartupClicked(_ sender: NSButton) { + switch sender.state { + case NSOnState: + print("on") + case NSOffState: + print("off") + case NSMixedState: + print("mixed") + default: break + } + } + @IBAction func checkboxStartFirefoxAlsoAtLaunchClicked(_ sender: NSButton) { + switch sender.state { + case NSOnState: + print("on") + case NSOffState: + print("off") + case NSMixedState: + print("mixed") + default: break + } + } + + // MARK: - Radio buttons functions + + func updateDockMenubarIcons(_ mode: ShowAsMode) -> Bool { + // Update preferences with latest choise + Preferences.shared().showAsIconMode = mode + // Update runtime + switch mode { + case .bothIcon, .dockIcon: + // Show dock icon + print("Preferences: Update Dock Icon -> Show") + if (!getDockIconStateIsShowing()) { + return triggerDockIconShowHide(showIcon: true) + } + case .menubarIcon: + // Hide dock icon + print("Preferences: Update Dock Icon -> Hide") + if (getDockIconStateIsShowing()) { + return triggerDockIconShowHide(showIcon: false) + } + } + // Note: In reality, this won't ever happen. + // The switch statement above would return before this. + return false + } + + func updateRadioButtonEffect(mode: ShowAsMode, withSideEffect: Bool = true) { + changeDockMenubarIconTimer?.invalidate() + + radioDockIcon?.state = NSOffState + radioMenubarIcon?.state = NSOffState + radioBothIcon?.state = NSOffState + + switch mode { + case .bothIcon: + radioBothIcon?.state = NSOnState + case .dockIcon: + radioDockIcon?.state = NSOnState + case .menubarIcon: + radioMenubarIcon?.state = NSOnState + } + + if (withSideEffect) { + if #available(OSX 10.12, *) { + changeDockMenubarIconTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { _ in + // If we're on 10.12 or later + self.updateDockMenubarIcons(mode) + }) + } else { + // Fallback on earlier versions + self.updateDockMenubarIcons(mode) + } + } + } + + @IBAction func radioBothIconSelected(_ sender: Any) { + updateRadioButtonEffect(mode: ShowAsMode.bothIcon) + } + + @IBAction func radioDockIconOnlySelected(_ sender: Any) { + updateRadioButtonEffect(mode: ShowAsMode.dockIcon) + } + + @IBAction func radioMenubarOnlySelected(_ sender: Any) { + updateRadioButtonEffect(mode: ShowAsMode.menubarIcon) + } + + // MARK: - Triggers + + func triggerDockIconHideShow(showIcon state: Bool) -> Bool { + // Get transform state. + var transformState: ProcessApplicationTransformState + if state { + transformState = ProcessApplicationTransformState(kProcessTransformToForegroundApplication) + } else { + transformState = ProcessApplicationTransformState(kProcessTransformToUIElementApplication) + } + + // Show / hide dock icon. + var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess)) + let transformStatus: OSStatus = TransformProcessType(&psn, transformState) + return transformStatus == 0 + } + + func triggerDockIconShowHide(showIcon state: Bool) -> Bool { + var result: Bool + if state { + result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular) + } else { + result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.accessory) + } + return result + } + + func getDockIconStateIsShowing() -> Bool { + if NSApp.activationPolicy() == NSApplicationActivationPolicy.regular { + return true + } else { + return false + } + } + + + // MARK: - Advanced + + @IBAction func checkboxEnableAdvancedPreferencesClicked(_ sender: NSButton) { + switch sender.state { + case NSOnState: + print("on") + Preferences.shared().allowAdvancedPreferenceEdit = true + case NSOffState: + print("off") + Preferences.shared().allowAdvancedPreferenceEdit = false + case NSMixedState: + print("mixed") + default: break + } + } + + + // End of Class +} + + diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift new file mode 100644 index 0000000000..845a1f7596 --- /dev/null +++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift @@ -0,0 +1,24 @@ +// +// PreferencesWindowController.swift +// I2PLauncher +// +// Created by Mikal Villa on 07/11/2018. +// Copyright © 2018 The I2P Project. All rights reserved. +// + +import Cocoa + +class PreferencesWindowController: NSWindowController, NSWindowDelegate { + + override func windowDidLoad() { + super.windowDidLoad() + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. + } + + func windowShouldClose(_ sender: NSWindow) -> Bool { + self.window?.orderOut(sender) + return false + } + +} -- GitLab