mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
286 lines
11 KiB
Swift
286 lines
11 KiB
Swift
//
|
|
// settings.swift
|
|
// Clock
|
|
//
|
|
// Created by Serhiy Mytrovtsiy on 24/03/2023
|
|
// Using Swift 5.0
|
|
// Running on macOS 13.2
|
|
//
|
|
// Copyright © 2023 Serhiy Mytrovtsiy. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
import Kit
|
|
|
|
let nameColumnID = NSUserInterfaceItemIdentifier(rawValue: "name")
|
|
let formatColumnID = NSUserInterfaceItemIdentifier(rawValue: "format")
|
|
let tzColumnID = NSUserInterfaceItemIdentifier(rawValue: "tz")
|
|
let statusColumnID = NSUserInterfaceItemIdentifier(rawValue: "status")
|
|
|
|
internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableViewDataSource, NSTextFieldDelegate {
|
|
public var callback: (() -> Void) = {}
|
|
|
|
private var cachedList: [Clock_t] = []
|
|
private var list: [Clock_t] {
|
|
get {
|
|
return self.cachedList
|
|
}
|
|
set {
|
|
self.cachedList = newValue
|
|
|
|
if newValue.isEmpty {
|
|
Store.shared.remove("\(self.title)_list")
|
|
} else {
|
|
let encoder = JSONEncoder()
|
|
if let encoded = try? encoder.encode(newValue){
|
|
Store.shared.set(key: "\(self.title)_list", value: encoded)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var title: String
|
|
private var selectedRow: Int = -1
|
|
|
|
private let scrollView = NSScrollView()
|
|
private let tableView = NSTableView()
|
|
private var footerView: NSStackView? = nil
|
|
private var deleteButton: NSButton? = nil
|
|
|
|
public init(_ module: ModuleType) {
|
|
self.title = module.stringValue
|
|
|
|
super.init(frame: NSRect.zero)
|
|
|
|
if let objects = Store.shared.data(key: "\(self.title)_list") {
|
|
let decoder = JSONDecoder()
|
|
if let objectsDecoded = try? decoder.decode(Array.self, from: objects) as [Clock_t] {
|
|
self.cachedList = objectsDecoded
|
|
}
|
|
}
|
|
if self.cachedList.isEmpty {
|
|
self.cachedList = [Clock.local]
|
|
}
|
|
|
|
self.orientation = .vertical
|
|
self.distribution = .gravityAreas
|
|
self.spacing = 0
|
|
|
|
self.scrollView.documentView = self.tableView
|
|
self.scrollView.hasHorizontalScroller = false
|
|
self.scrollView.hasVerticalScroller = true
|
|
self.scrollView.autohidesScrollers = true
|
|
self.scrollView.backgroundColor = NSColor.clear
|
|
self.scrollView.drawsBackground = true
|
|
|
|
self.tableView.frame = self.scrollView.bounds
|
|
self.tableView.delegate = self
|
|
self.tableView.dataSource = self
|
|
self.tableView.allowsMultipleSelection = false
|
|
self.tableView.focusRingType = .none
|
|
self.tableView.gridColor = .gridColor
|
|
self.tableView.columnAutoresizingStyle = .firstColumnOnlyAutoresizingStyle
|
|
self.tableView.allowsColumnResizing = false
|
|
self.tableView.gridStyleMask = [.solidVerticalGridLineMask, .solidHorizontalGridLineMask]
|
|
self.tableView.usesAlternatingRowBackgroundColors = true
|
|
if #available(macOS 11.0, *) {
|
|
self.tableView.style = .plain
|
|
}
|
|
self.tableView.rowHeight = 32
|
|
|
|
let nameColumn = NSTableColumn(identifier: nameColumnID)
|
|
nameColumn.headerCell.title = localizedString("Name")
|
|
nameColumn.headerCell.alignment = .center
|
|
let formatColumn = NSTableColumn(identifier: formatColumnID)
|
|
formatColumn.headerCell.title = localizedString("Format")
|
|
formatColumn.headerCell.alignment = .center
|
|
formatColumn.width = 160
|
|
let tzColumn = NSTableColumn(identifier: tzColumnID)
|
|
tzColumn.headerCell.title = localizedString("Time zone")
|
|
tzColumn.headerCell.alignment = .center
|
|
tzColumn.width = 132
|
|
let statusColumn = NSTableColumn(identifier: statusColumnID)
|
|
statusColumn.headerCell.title = ""
|
|
statusColumn.width = 16
|
|
|
|
self.tableView.addTableColumn(nameColumn)
|
|
self.tableView.addTableColumn(formatColumn)
|
|
self.tableView.addTableColumn(tzColumn)
|
|
self.tableView.addTableColumn(statusColumn)
|
|
|
|
let separator = NSBox()
|
|
separator.boxType = .separator
|
|
|
|
self.addArrangedSubview(self.scrollView)
|
|
self.addArrangedSubview(separator)
|
|
self.addArrangedSubview(self.footer())
|
|
|
|
var hight: CGFloat = 296
|
|
if #available(macOS 26.0, *) {
|
|
hight = 290
|
|
}
|
|
|
|
NSLayoutConstraint.activate([
|
|
self.scrollView.heightAnchor.constraint(equalToConstant: hight)
|
|
])
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
private func footer() -> NSView {
|
|
let view = NSStackView()
|
|
view.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
|
view.spacing = 4
|
|
view.orientation = .horizontal
|
|
|
|
var addButton: NSButton {
|
|
let btn = NSButton()
|
|
btn.widthAnchor.constraint(equalToConstant: 27).isActive = true
|
|
btn.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
|
btn.bezelStyle = .rounded
|
|
if #available(macOS 11.0, *) {
|
|
btn.image = iconFromSymbol(name: "plus", scale: .medium)
|
|
} else {
|
|
btn.title = localizedString("Add")
|
|
}
|
|
btn.action = #selector(self.addNewClock)
|
|
btn.target = self
|
|
btn.toolTip = localizedString("Add new clock")
|
|
btn.focusRingType = .none
|
|
return btn
|
|
}
|
|
var deleteButton: NSButton {
|
|
let btn = NSButton()
|
|
btn.widthAnchor.constraint(equalToConstant: 27).isActive = true
|
|
btn.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
|
btn.bezelStyle = .rounded
|
|
if #available(macOS 11.0, *) {
|
|
btn.image = iconFromSymbol(name: "minus", scale: .medium)
|
|
} else {
|
|
btn.title = localizedString("Delete")
|
|
}
|
|
btn.action = #selector(self.deleteClock)
|
|
btn.target = self
|
|
btn.toolTip = localizedString("Delete selected clock")
|
|
btn.focusRingType = .none
|
|
return btn
|
|
}
|
|
self.deleteButton = deleteButton
|
|
|
|
view.addArrangedSubview(addButton)
|
|
view.addArrangedSubview(NSView())
|
|
|
|
let helpBtn = NSButton()
|
|
helpBtn.widthAnchor.constraint(equalToConstant: 27).isActive = true
|
|
helpBtn.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
|
helpBtn.bezelStyle = .helpButton
|
|
helpBtn.title = ""
|
|
helpBtn.action = #selector(self.openFormatHelp)
|
|
helpBtn.target = self
|
|
helpBtn.toolTip = localizedString("Help with datetime format")
|
|
view.addArrangedSubview(helpBtn)
|
|
|
|
self.footerView = view
|
|
return view
|
|
}
|
|
|
|
func load(widgets: [Kit.widget_t]) {
|
|
self.tableView.reloadData()
|
|
}
|
|
|
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
|
return self.list.count
|
|
}
|
|
|
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
|
guard let id = tableColumn?.identifier else { return nil }
|
|
let cell = NSTableCellView()
|
|
let item = self.list[row]
|
|
|
|
switch id {
|
|
case nameColumnID, formatColumnID:
|
|
let text: NSTextField = NSTextField()
|
|
text.identifier = id
|
|
text.drawsBackground = false
|
|
text.isBordered = false
|
|
text.sizeToFit()
|
|
text.delegate = self
|
|
text.stringValue = id == nameColumnID ? item.name : item.format
|
|
text.translatesAutoresizingMaskIntoConstraints = false
|
|
cell.addSubview(text)
|
|
text.widthAnchor.constraint(equalTo: cell.widthAnchor).isActive = true
|
|
text.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
|
|
case tzColumnID:
|
|
let select: NSPopUpButton = selectView(action: #selector(self.toggleTZ), items: Clock.zones, selected: item.tz)
|
|
select.identifier = NSUserInterfaceItemIdentifier("\(row)")
|
|
select.sizeToFit()
|
|
select.preferredEdge = .maxX
|
|
select.translatesAutoresizingMaskIntoConstraints = false
|
|
select.widthAnchor.constraint(lessThanOrEqualToConstant: 132).isActive = true
|
|
cell.addSubview(select)
|
|
case statusColumnID:
|
|
let button: NSButton = NSButton(frame: NSRect(x: 0, y: 8, width: 10, height: 10))
|
|
button.identifier = NSUserInterfaceItemIdentifier("\(row)")
|
|
button.setButtonType(.switch)
|
|
button.state = item.enabled ? .on : .off
|
|
button.action = #selector(self.toggleClock)
|
|
button.title = ""
|
|
button.isBordered = false
|
|
button.isTransparent = false
|
|
button.target = self
|
|
button.sizeToFit()
|
|
cell.addSubview(button)
|
|
default: break
|
|
}
|
|
|
|
return cell
|
|
}
|
|
|
|
func controlTextDidChange(_ notification: Notification) {
|
|
if let textField = notification.object as? NSTextField, let id = textField.identifier {
|
|
let i = self.tableView.selectedRow
|
|
switch id {
|
|
case nameColumnID:
|
|
self.list[i].name = textField.stringValue
|
|
case formatColumnID:
|
|
self.list[i].format = textField.stringValue
|
|
default: return
|
|
}
|
|
}
|
|
}
|
|
|
|
func tableViewSelectionDidChange(_ notification: Notification) {
|
|
if self.tableView.selectedRow == -1 {
|
|
self.deleteButton?.removeFromSuperview()
|
|
} else {
|
|
if let btn = self.deleteButton {
|
|
self.footerView?.insertArrangedSubview(btn, at: 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc private func toggleTZ(_ sender: NSMenuItem) {
|
|
guard let key = sender.representedObject as? String, let id = sender.identifier, let i = Int(id.rawValue) else { return }
|
|
self.list[i].tz = key
|
|
}
|
|
@objc private func toggleClock(_ sender: NSButton) {
|
|
guard let id = sender.identifier, let i = Int(id.rawValue) else { return }
|
|
self.list[i].enabled = sender.state == NSControl.StateValue.on
|
|
}
|
|
@objc private func addNewClock(_ sender: Any) {
|
|
self.list.append(Clock_t(name: "\(localizedString("Clock")) \(self.list.count)", format: Clock.local.format, tz: Clock.local.tz))
|
|
self.tableView.reloadData()
|
|
}
|
|
@objc private func deleteClock(_ sender: Any) {
|
|
guard self.tableView.selectedRow != -1 else { return }
|
|
self.list.remove(at: self.tableView.selectedRow)
|
|
self.tableView.reloadData()
|
|
self.deleteButton?.removeFromSuperview()
|
|
}
|
|
@objc private func openFormatHelp(_ sender: NSButton) {
|
|
NSWorkspace.shared.open(URL(string: "https://www.nsdateformatter.com")!)
|
|
}
|
|
}
|