forked from I2P_Developers/i2p.i2p
267 lines
8.1 KiB
Swift
267 lines
8.1 KiB
Swift
![]() |
//
|
||
|
// ServiceTableViewController.swift
|
||
|
// I2PLauncher
|
||
|
//
|
||
|
// Created by Mikal Villa on 20/04/2019.
|
||
|
// Copyright © 2019 The I2P Project. All rights reserved.
|
||
|
//
|
||
|
|
||
|
import Cocoa
|
||
|
import SnapKit
|
||
|
import MBPopup
|
||
|
|
||
|
class ServiceTableViewController: NSObject, SwitchableTableViewController {
|
||
|
let contentView = NSStackView(frame: CGRect(x: 0, y: 0, width: 220, height: 400))
|
||
|
let scrollView = CustomScrollView()
|
||
|
let tableView = NSTableView()
|
||
|
let bottomBar = BottomBar()
|
||
|
let addServicesNoticeField = NSTextField()
|
||
|
|
||
|
var editorTableViewController: EditorTableViewController
|
||
|
|
||
|
var services: [BaseService] = [I2PRouterService(),HttpTunnelService(),IrcTunnelService()] {
|
||
|
didSet {
|
||
|
addServicesNoticeField.isHidden = services.count > 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var servicesBeingUpdated = [BaseService]()
|
||
|
var generalStatus: ServiceStatus {
|
||
|
let hasBadServices = services.first { $0.status > .stopped } != nil
|
||
|
|
||
|
return hasBadServices ? .killed : .started
|
||
|
}
|
||
|
|
||
|
var hidden: Bool = false
|
||
|
|
||
|
var updateCallback: (() -> Void)?
|
||
|
|
||
|
override init() {
|
||
|
self.editorTableViewController = EditorTableViewController(contentView: contentView, scrollView: scrollView)
|
||
|
super.init()
|
||
|
}
|
||
|
|
||
|
func setup() {
|
||
|
//bottomBar.reloadServicesCallback = (NSApp.delegate as? SwiftApplicationDelegate)!.updateServices
|
||
|
|
||
|
bottomBar.openSettingsCallback = { [weak self] in
|
||
|
self?.hide()
|
||
|
self?.editorTableViewController.show()
|
||
|
}
|
||
|
|
||
|
bottomBar.closeSettingsCallback = { [weak self] in
|
||
|
self?.editorTableViewController.hide()
|
||
|
self?.show()
|
||
|
}
|
||
|
|
||
|
contentView.snp.makeConstraints { make in
|
||
|
make.left.right.bottom.equalTo(0)
|
||
|
make.width.greaterThanOrEqualTo(220)
|
||
|
make.height.greaterThanOrEqualTo(40 + 30 + 2) // tableView.rowHeight + bottomBar.frame.size.height + 2
|
||
|
}
|
||
|
|
||
|
contentView.addSubview(scrollView)
|
||
|
contentView.addSubview(bottomBar)
|
||
|
|
||
|
scrollView.snp.makeConstraints { make in
|
||
|
scrollView.topConstraint = make.top.equalToSuperview().constraint
|
||
|
make.left.right.equalTo(0)
|
||
|
}
|
||
|
|
||
|
bottomBar.snp.makeConstraints { make in
|
||
|
make.width.equalToSuperview()
|
||
|
make.top.equalTo(scrollView.snp.bottom)
|
||
|
make.height.equalTo(30)
|
||
|
make.left.right.equalTo(0)
|
||
|
make.bottom.equalTo(0)
|
||
|
}
|
||
|
|
||
|
contentView.addSubview(addServicesNoticeField)
|
||
|
addServicesNoticeField.snp.makeConstraints { make in
|
||
|
make.height.equalTo(22)
|
||
|
make.left.right.equalTo(0)
|
||
|
make.centerY.equalToSuperview().offset(-14)
|
||
|
}
|
||
|
|
||
|
scrollView.borderType = .noBorder
|
||
|
scrollView.hasVerticalScroller = true
|
||
|
scrollView.hasHorizontalScroller = false
|
||
|
scrollView.autoresizesSubviews = true
|
||
|
scrollView.documentView = tableView
|
||
|
scrollView.drawsBackground = false
|
||
|
scrollView.wantsLayer = true
|
||
|
scrollView.layer?.cornerRadius = 6
|
||
|
|
||
|
tableView.frame = scrollView.bounds
|
||
|
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "serviceColumnIdentifier"))
|
||
|
column.width = tableView.frame.size.width
|
||
|
tableView.addTableColumn(column)
|
||
|
tableView.autoresizesSubviews = true
|
||
|
tableView.wantsLayer = true
|
||
|
tableView.layer?.cornerRadius = 6
|
||
|
tableView.headerView = nil
|
||
|
tableView.rowHeight = 40
|
||
|
tableView.gridStyleMask = NSTableView.GridLineStyle.init(rawValue: 0)
|
||
|
tableView.dataSource = self
|
||
|
tableView.delegate = self
|
||
|
tableView.selectionHighlightStyle = .none
|
||
|
tableView.backgroundColor = NSColor.clear
|
||
|
|
||
|
addServicesNoticeField.isEditable = false
|
||
|
addServicesNoticeField.isBordered = false
|
||
|
addServicesNoticeField.isSelectable = false
|
||
|
|
||
|
let italicFont = NSFontManager.shared.font(
|
||
|
withFamily: NSFont.systemFont(ofSize: 13).fontName,
|
||
|
traits: NSFontTraitMask.italicFontMask,
|
||
|
weight: 5,
|
||
|
size: 13
|
||
|
)
|
||
|
|
||
|
addServicesNoticeField.font = italicFont
|
||
|
addServicesNoticeField.textColor = NSColor.textColor
|
||
|
addServicesNoticeField.maximumNumberOfLines = 1
|
||
|
addServicesNoticeField.cell!.truncatesLastVisibleLine = true
|
||
|
addServicesNoticeField.alignment = .center
|
||
|
addServicesNoticeField.stringValue = "Oh, maybe too empty? :)"
|
||
|
addServicesNoticeField.backgroundColor = .clear
|
||
|
}
|
||
|
|
||
|
func willOpenPopup() {
|
||
|
resizeViews()
|
||
|
reloadData()
|
||
|
|
||
|
if case let .updated(date) = bottomBar.status {
|
||
|
if Date().timeIntervalSince1970 - date.timeIntervalSince1970 > 60 {
|
||
|
//(NSApp.delegate as? SwiftApplicationDelegate)?.updateServices()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func willShow() {
|
||
|
scrollView.topConstraint?.update(offset: 0)
|
||
|
scrollView.documentView = tableView
|
||
|
|
||
|
if editorTableViewController.selectionChanged {
|
||
|
//self.services = ["I2PRouter","HttpTunnel"]//Preferences.shared.selectedServices
|
||
|
reloadData()
|
||
|
|
||
|
//(NSApp.delegate as? SwiftApplicationDelegate)?.updateServices()
|
||
|
} else {
|
||
|
addServicesNoticeField.isHidden = services.count > 0
|
||
|
}
|
||
|
|
||
|
resizeViews()
|
||
|
}
|
||
|
|
||
|
func willHide() {
|
||
|
addServicesNoticeField.isHidden = true
|
||
|
}
|
||
|
|
||
|
func resizeViews() {
|
||
|
var frame = scrollView.frame
|
||
|
frame.size.height = min(tableView.intrinsicContentSize.height, 490)
|
||
|
scrollView.frame = frame
|
||
|
|
||
|
//(NSApp.delegate as? SwiftApplicationDelegate)?.popupController.resizePopup(height: scrollView.frame.size.height + bottomBar.frame.size.height)
|
||
|
}
|
||
|
|
||
|
func reloadData(at index: Int? = nil) {
|
||
|
services.sort()
|
||
|
|
||
|
bottomBar.updateStatusText()
|
||
|
|
||
|
guard index != nil else {
|
||
|
tableView.reloadData()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tableView.reloadData(forRowIndexes: IndexSet(integer: index!), columnIndexes: IndexSet(integer: 0))
|
||
|
}
|
||
|
|
||
|
func updateServices(updateCallback: @escaping () -> Void) {
|
||
|
self.servicesBeingUpdated = [Service]()
|
||
|
|
||
|
guard services.count > 0 else {
|
||
|
reloadData()
|
||
|
bottomBar.status = .updated(Date())
|
||
|
|
||
|
self.updateCallback?()
|
||
|
self.updateCallback = nil
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
self.updateCallback = updateCallback
|
||
|
let serviceCallback: ((BaseService) -> Void) = { [weak self] service in self?.updatedStatus(for: service) }
|
||
|
|
||
|
bottomBar.status = .updating
|
||
|
|
||
|
services.forEach {
|
||
|
servicesBeingUpdated.append($0)
|
||
|
$0.updateStatus(callback: serviceCallback)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func updatedStatus(for service: BaseService) {
|
||
|
if let index = servicesBeingUpdated.index(of: service) {
|
||
|
servicesBeingUpdated.remove(at: index)
|
||
|
}
|
||
|
|
||
|
DispatchQueue.main.async { [weak self] in
|
||
|
self?.reloadData()
|
||
|
|
||
|
if self?.servicesBeingUpdated.count == 0 {
|
||
|
self?.bottomBar.status = .updated(Date())
|
||
|
|
||
|
self?.updateCallback?()
|
||
|
self?.updateCallback = nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension ServiceTableViewController: NSTableViewDataSource {
|
||
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||
|
return services.count
|
||
|
}
|
||
|
|
||
|
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension ServiceTableViewController: NSTableViewDelegate {
|
||
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||
|
let identifier = tableColumn?.identifier ?? NSUserInterfaceItemIdentifier(rawValue: "identifier")
|
||
|
let cell = tableView.makeView(withIdentifier: identifier, owner: self) ?? StatusTableCell()
|
||
|
|
||
|
guard let view = cell as? StatusTableCell else { return nil }
|
||
|
guard let service = services[row] as? Service else { return nil }
|
||
|
|
||
|
view.textField?.stringValue = service.name
|
||
|
view.statusField.stringValue = service.message
|
||
|
view.statusIndicator.status = service.status
|
||
|
|
||
|
return view
|
||
|
}
|
||
|
|
||
|
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
||
|
let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "rowView")
|
||
|
let cell = tableView.makeView(withIdentifier: cellIdentifier, owner: self) ?? ServiceTableRowView()
|
||
|
|
||
|
guard let view = cell as? ServiceTableRowView else { return nil }
|
||
|
|
||
|
view.showSeparator = row + 1 < services.count
|
||
|
|
||
|
return view
|
||
|
}
|
||
|
|
||
|
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||
|
guard let _ = services[row] as? Service else { return false }
|
||
|
|
||
|
//NSWorkspace.shared.open(service.url)
|
||
|
return false
|
||
|
}
|
||
|
}
|