mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: added Clock module (#929)
This commit is contained in:
@@ -593,3 +593,19 @@ public extension Data {
|
||||
return withUnsafeBytes { $0.load(as: sockaddr_in.self) }
|
||||
}
|
||||
}
|
||||
|
||||
public extension Date {
|
||||
func convertToTimeZone(_ timeZone: TimeZone) -> Date {
|
||||
return addingTimeInterval(TimeInterval(timeZone.secondsFromGMT(for: self) - TimeZone.current.secondsFromGMT(for: self)))
|
||||
}
|
||||
}
|
||||
|
||||
public extension TimeZone {
|
||||
init(fromUTC: String) {
|
||||
if let utc = Int(fromUTC), let tz = TimeZone(secondsFromGMT: utc*3600) {
|
||||
self = tz
|
||||
} else {
|
||||
self = TimeZone.current
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
Modules/Clock/config.plist
Normal file
36
Modules/Clock/config.plist
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>Clock</string>
|
||||
<key>State</key>
|
||||
<false/>
|
||||
<key>Symbol</key>
|
||||
<string>clock.fill</string>
|
||||
<key>Widgets</key>
|
||||
<dict>
|
||||
<key>label</key>
|
||||
<dict>
|
||||
<key>Default</key>
|
||||
<false/>
|
||||
<key>Title</key>
|
||||
<string>CLK</string>
|
||||
<key>Order</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>sensors</key>
|
||||
<dict>
|
||||
<key>Default</key>
|
||||
<true/>
|
||||
<key>Preview</key>
|
||||
<dict>
|
||||
<key>Values</key>
|
||||
<string>23.03.2023 13:45</string>
|
||||
</dict>
|
||||
<key>Order</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
133
Modules/Clock/main.swift
Normal file
133
Modules/Clock/main.swift
Normal file
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// main.swift
|
||||
// Clock
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 23/03/2023
|
||||
// Using Swift 5.0
|
||||
// Running on macOS 13.2
|
||||
//
|
||||
// Copyright © 2023 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Kit
|
||||
|
||||
public struct Clock_t: Codable {
|
||||
public var id: String = UUID().uuidString
|
||||
public var enabled: Bool = true
|
||||
|
||||
public var name: String
|
||||
public var format: String
|
||||
public var tz: String
|
||||
|
||||
public var value: Date? = nil
|
||||
|
||||
public func formatted() -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = self.format
|
||||
formatter.timeZone = TimeZone(fromUTC: self.tz)
|
||||
return formatter.string(from: self.value ?? Date())
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClockReader: Reader<Date> {
|
||||
public override func read() {
|
||||
self.callback(Date())
|
||||
}
|
||||
}
|
||||
|
||||
public class Clock: Module {
|
||||
private let popupView: Popup = Popup()
|
||||
private let settingsView: Settings = Settings()
|
||||
|
||||
private var reader: ClockReader = ClockReader()
|
||||
|
||||
private var list: [Clock_t] {
|
||||
if let objects = Store.shared.data(key: "\(Clock.title)_list") {
|
||||
let decoder = JSONDecoder()
|
||||
if let objectsDecoded = try? decoder.decode(Array.self, from: objects) as [Clock_t] {
|
||||
return objectsDecoded
|
||||
}
|
||||
}
|
||||
return [Clock.local]
|
||||
}
|
||||
|
||||
public init() {
|
||||
super.init(
|
||||
popup: self.popupView,
|
||||
settings: self.settingsView
|
||||
)
|
||||
guard self.available else { return }
|
||||
|
||||
self.reader.callbackHandler = { [unowned self] value in
|
||||
guard let value else { return }
|
||||
self.callback(value)
|
||||
}
|
||||
|
||||
self.addReader(self.reader)
|
||||
self.reader.readyCallback = { [unowned self] in
|
||||
self.readyHandler()
|
||||
}
|
||||
}
|
||||
|
||||
private func callback(_ value: Date) {
|
||||
var clocks: [Clock_t] = self.list.filter({ $0.enabled })
|
||||
var list: [Stack_t] = []
|
||||
|
||||
for (i, c) in clocks.enumerated() {
|
||||
clocks[i].value = value
|
||||
list.append(Stack_t(key: c.name, value: clocks[i].formatted()))
|
||||
}
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.popupView.callback(clocks)
|
||||
})
|
||||
|
||||
self.menuBar.widgets.filter{ $0.isActive }.forEach { (w: Widget) in
|
||||
switch w.item {
|
||||
case let widget as SensorsWidget: widget.setValues(list)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Clock {
|
||||
static let title: String = "Clock"
|
||||
static var local: Clock_t {
|
||||
Clock_t(name: localizedString("Local time"), format: "YYYY-MM-DD HH:mm:ss", tz: "local")
|
||||
}
|
||||
static var zones: [KeyValue_t] {
|
||||
[
|
||||
KeyValue_t(key: "local", value: "Local"),
|
||||
KeyValue_t(key: "separator", value: "separator"),
|
||||
KeyValue_t(key: "-12", value: "UTC-12:00"),
|
||||
KeyValue_t(key: "-11", value: "UTC-11:00"),
|
||||
KeyValue_t(key: "-10", value: "UTC-10:00"),
|
||||
KeyValue_t(key: "-9", value: "UTC-9:00"),
|
||||
KeyValue_t(key: "-8", value: "UTC-8:00"),
|
||||
KeyValue_t(key: "-7", value: "UTC-7:00"),
|
||||
KeyValue_t(key: "-6", value: "UTC-6:00"),
|
||||
KeyValue_t(key: "-5", value: "UTC-5:00"),
|
||||
KeyValue_t(key: "-4", value: "UTC-4:00"),
|
||||
KeyValue_t(key: "-3", value: "UTC-3:00"),
|
||||
KeyValue_t(key: "-2", value: "UTC-2:00"),
|
||||
KeyValue_t(key: "-1", value: "UTC-1:00"),
|
||||
KeyValue_t(key: "0", value: "UTC"),
|
||||
KeyValue_t(key: "1", value: "UTC+1:00"),
|
||||
KeyValue_t(key: "2", value: "UTC+2:00"),
|
||||
KeyValue_t(key: "3", value: "UTC+3:00"),
|
||||
KeyValue_t(key: "4", value: "UTC+4:00"),
|
||||
KeyValue_t(key: "5", value: "UTC+5:00"),
|
||||
KeyValue_t(key: "6", value: "UTC+6:00"),
|
||||
KeyValue_t(key: "7", value: "UTC+7:00"),
|
||||
KeyValue_t(key: "8", value: "UTC+8:00"),
|
||||
KeyValue_t(key: "9", value: "UTC+9:00"),
|
||||
KeyValue_t(key: "10", value: "UTC+10:00"),
|
||||
KeyValue_t(key: "11", value: "UTC+11:00"),
|
||||
KeyValue_t(key: "12", value: "UTC+12:00"),
|
||||
KeyValue_t(key: "13", value: "UTC+13:00"),
|
||||
KeyValue_t(key: "14", value: "UTC+14:00")
|
||||
]
|
||||
}
|
||||
}
|
||||
215
Modules/Clock/popup.swift
Normal file
215
Modules/Clock/popup.swift
Normal file
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// popup.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
|
||||
|
||||
internal class Popup: PopupWrapper {
|
||||
public init() {
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: 0))
|
||||
|
||||
self.orientation = .vertical
|
||||
self.spacing = Constants.Popup.margins
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
internal func callback(_ list: [Clock_t]) {
|
||||
defer {
|
||||
let h = self.arrangedSubviews.map({ $0.bounds.height + self.spacing }).reduce(0, +) - self.spacing
|
||||
if h > 0 && self.frame.size.height != h {
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
self.sizeCallback?(self.frame.size)
|
||||
}
|
||||
}
|
||||
|
||||
var views = self.subviews.filter{ $0 is ClockView }.compactMap{ $0 as? ClockView }
|
||||
if list.count < views.count && !views.isEmpty {
|
||||
views.forEach{ $0.removeFromSuperview() }
|
||||
views = []
|
||||
}
|
||||
|
||||
list.forEach { (c: Clock_t) in
|
||||
if let view = views.first(where: { $0.clock.id == c.id }) {
|
||||
view.update(c)
|
||||
} else {
|
||||
self.addArrangedSubview(ClockView(width: self.frame.width, clock: c))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ClockView: NSStackView {
|
||||
public var clock: Clock_t
|
||||
|
||||
open override var intrinsicContentSize: CGSize {
|
||||
return CGSize(width: self.bounds.width, height: self.bounds.height)
|
||||
}
|
||||
|
||||
private var ready: Bool = false
|
||||
|
||||
private let clockView: ClockChart = ClockChart()
|
||||
private let nameField: NSTextField = TextView()
|
||||
private let timeField: NSTextField = TextView()
|
||||
|
||||
init(width: CGFloat, clock: Clock_t) {
|
||||
self.clock = clock
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: width, height: 44))
|
||||
|
||||
self.orientation = .horizontal
|
||||
self.spacing = 5
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: 5,
|
||||
left: 5,
|
||||
bottom: 5,
|
||||
right: 5
|
||||
)
|
||||
self.wantsLayer = true
|
||||
self.layer?.cornerRadius = 2
|
||||
|
||||
self.clockView.widthAnchor.constraint(equalToConstant: 34).isActive = true
|
||||
|
||||
let container: NSStackView = NSStackView()
|
||||
container.orientation = .vertical
|
||||
container.spacing = 2
|
||||
container.distribution = .fillEqually
|
||||
container.alignment = .left
|
||||
|
||||
self.nameField.font = NSFont.systemFont(ofSize: 11, weight: .light)
|
||||
self.setTZ()
|
||||
self.nameField.cell?.truncatesLastVisibleLine = true
|
||||
|
||||
self.timeField.font = NSFont.systemFont(ofSize: 13, weight: .regular)
|
||||
self.timeField.stringValue = clock.formatted()
|
||||
self.timeField.cell?.truncatesLastVisibleLine = true
|
||||
|
||||
container.addArrangedSubview(self.nameField)
|
||||
container.addArrangedSubview(self.timeField)
|
||||
|
||||
self.addArrangedSubview(self.clockView)
|
||||
self.addArrangedSubview(container)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func updateLayer() {
|
||||
self.layer?.backgroundColor = isDarkMode ? NSColor(hexString: "#111111", alpha: 0.25).cgColor : NSColor(hexString: "#f5f5f5", alpha: 1).cgColor
|
||||
}
|
||||
|
||||
private func setTZ() {
|
||||
self.nameField.stringValue = "\(self.clock.name)"
|
||||
if let tz = Clock.zones.first(where: { $0.key == self.clock.tz }) {
|
||||
self.nameField.stringValue += " (\(tz.value))"
|
||||
}
|
||||
}
|
||||
|
||||
public func update(_ newClock: Clock_t) {
|
||||
if self.clock.tz != newClock.tz {
|
||||
self.clock = newClock
|
||||
self.setTZ()
|
||||
}
|
||||
|
||||
if (self.window?.isVisible ?? false) || !self.ready {
|
||||
self.timeField.stringValue = newClock.formatted()
|
||||
if let value = newClock.value {
|
||||
self.clockView.setValue(value.convertToTimeZone(TimeZone(fromUTC: newClock.tz)))
|
||||
}
|
||||
self.ready = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ClockChart: NSView {
|
||||
private var color: NSColor = Color.systemAccent.additional as! NSColor
|
||||
|
||||
private let calendar = Calendar.current
|
||||
private var hour: Int!
|
||||
private var minute: Int!
|
||||
private var second: Int!
|
||||
|
||||
private let hourLayer = CALayer()
|
||||
private let minuteLayer = CALayer()
|
||||
private let secondsLayer = CALayer()
|
||||
private let pinLayer = CAShapeLayer()
|
||||
|
||||
override init(frame: CGRect = NSRect.zero) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
public override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
|
||||
guard (self.hour != nil), (self.minute != nil), (self.second != nil) else { return }
|
||||
|
||||
let context = NSGraphicsContext.current!.cgContext
|
||||
context.saveGState()
|
||||
context.setFillColor(self.color.cgColor)
|
||||
context.fillEllipse(in: dirtyRect)
|
||||
context.restoreGState()
|
||||
|
||||
let anchor = CGPoint(x: 0.5, y: 0)
|
||||
let center = CGPoint(x: dirtyRect.size.width / 2, y: dirtyRect.size.height / 2)
|
||||
|
||||
let hourAngle: CGFloat = CGFloat(Double(hour) * (360.0 / 12.0)) + CGFloat(Double(minute) * (1.0 / 60.0) * (360.0 / 12.0))
|
||||
let minuteAngle: CGFloat = CGFloat(minute) * CGFloat(360.0 / 60.0)
|
||||
let secondsAngle: CGFloat = CGFloat(self.second) * CGFloat(360.0 / 60.0)
|
||||
|
||||
self.hourLayer.backgroundColor = NSColor.white.cgColor
|
||||
self.hourLayer.anchorPoint = anchor
|
||||
self.hourLayer.position = center
|
||||
self.hourLayer.bounds = CGRect(x: 0, y: 0, width: 3, height: dirtyRect.size.width / 2 - 7)
|
||||
self.hourLayer.transform = CATransform3DMakeRotation(-hourAngle / 180 * CGFloat(Double.pi), 0, 0, 1)
|
||||
self.layer?.addSublayer(self.hourLayer)
|
||||
|
||||
self.minuteLayer.backgroundColor = NSColor.white.cgColor
|
||||
self.minuteLayer.anchorPoint = anchor
|
||||
self.minuteLayer.position = center
|
||||
self.minuteLayer.bounds = CGRect(x: 0, y: 0, width: 2, height: dirtyRect.size.width / 2 - 4)
|
||||
self.minuteLayer.transform = CATransform3DMakeRotation(-minuteAngle / 180 * CGFloat(Double.pi), 0, 0, 1)
|
||||
self.layer?.addSublayer(self.minuteLayer)
|
||||
|
||||
self.secondsLayer.backgroundColor = NSColor.red.cgColor
|
||||
self.secondsLayer.anchorPoint = anchor
|
||||
self.secondsLayer.position = center
|
||||
self.secondsLayer.bounds = CGRect(x: 0, y: 0, width: 1, height: dirtyRect.size.width / 2 - 2)
|
||||
self.secondsLayer.transform = CATransform3DMakeRotation(-secondsAngle / 180 * CGFloat(Double.pi), 0, 0, 1)
|
||||
self.layer?.addSublayer(self.secondsLayer)
|
||||
|
||||
self.pinLayer.fillColor = NSColor.white.cgColor
|
||||
self.pinLayer.anchorPoint = anchor
|
||||
self.pinLayer.path = CGMutablePath(roundedRect: CGRect(
|
||||
x: center.x - 3 / 2,
|
||||
y: center.y - 3 / 2,
|
||||
width: 3,
|
||||
height: 3
|
||||
), cornerWidth: 4, cornerHeight: 4, transform: nil)
|
||||
self.layer?.addSublayer(self.pinLayer)
|
||||
}
|
||||
|
||||
public func setValue(_ value: Date) {
|
||||
self.hour = self.calendar.component(.hour, from: value)
|
||||
self.minute = self.calendar.component(.minute, from: value)
|
||||
self.second = self.calendar.component(.second, from: value)
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.display()
|
||||
})
|
||||
}
|
||||
}
|
||||
267
Modules/Clock/settings.swift
Normal file
267
Modules/Clock/settings.swift
Normal file
@@ -0,0 +1,267 @@
|
||||
//
|
||||
// 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 list: [Clock_t] {
|
||||
get {
|
||||
if let objects = Store.shared.data(key: "\(Clock.title)_list") {
|
||||
let decoder = JSONDecoder()
|
||||
if let objectsDecoded = try? decoder.decode(Array.self, from: objects) as [Clock_t] {
|
||||
return objectsDecoded
|
||||
}
|
||||
}
|
||||
return [Clock.local]
|
||||
}
|
||||
set {
|
||||
if newValue.isEmpty {
|
||||
Store.shared.remove("\(Clock.title)_list")
|
||||
} else {
|
||||
let encoder = JSONEncoder()
|
||||
if let encoded = try? encoder.encode(newValue){
|
||||
Store.shared.set(key: "\(Clock.title)_list", value: encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
super.init(frame: NSRect.zero)
|
||||
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
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 = 27
|
||||
|
||||
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())
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
self.scrollView.heightAnchor.constraint(equalToConstant: 278)
|
||||
])
|
||||
}
|
||||
|
||||
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 = 0
|
||||
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 = .regularSquare
|
||||
btn.translatesAutoresizingMaskIntoConstraints = false
|
||||
if #available(macOS 11.0, *) {
|
||||
btn.image = iconFromSymbol(name: "plus", scale: .large)
|
||||
} else {
|
||||
btn.title = "Add"
|
||||
}
|
||||
btn.isBordered = false
|
||||
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 = .regularSquare
|
||||
btn.translatesAutoresizingMaskIntoConstraints = false
|
||||
if #available(macOS 11.0, *) {
|
||||
btn.image = iconFromSymbol(name: "minus", scale: .large)
|
||||
} else {
|
||||
btn.title = "Add"
|
||||
}
|
||||
btn.isBordered = false
|
||||
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())
|
||||
|
||||
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()
|
||||
cell.addSubview(select)
|
||||
case statusColumnID:
|
||||
let button: NSButton = NSButton(frame: NSRect(x: 0, y: 5, 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?.addArrangedSubview(btn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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: "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()
|
||||
}
|
||||
}
|
||||
12
Modules/Disk/header.h
Normal file
12
Modules/Disk/header.h
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Header.h
|
||||
// Disk
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 25/03/2023
|
||||
// Using Swift 5.0
|
||||
// Running on macOS 13.2
|
||||
//
|
||||
// Copyright © 2023 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
#include <libproc.h>
|
||||
@@ -9,6 +9,14 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
5C0A2A8A292A5B4D009B4C1F /* SMJobBlessUtil.py in Resources */ = {isa = PBXBuildFile; fileRef = 5C0A2A89292A5B4D009B4C1F /* SMJobBlessUtil.py */; };
|
||||
5C21D80B296C7B81005BA16D /* CombinedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C21D80A296C7B81005BA16D /* CombinedView.swift */; };
|
||||
5C2229A329CCB3C400F00E69 /* Clock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22299D29CCB3C400F00E69 /* Clock.framework */; };
|
||||
5C2229A429CCB3C400F00E69 /* Clock.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22299D29CCB3C400F00E69 /* Clock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
5C2229A929CCB41900F00E69 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2229A829CCB41900F00E69 /* main.swift */; };
|
||||
5C2229AB29CCB53E00F00E69 /* config.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5C2229AA29CCB53E00F00E69 /* config.plist */; };
|
||||
5C2229AF29CDC08700F00E69 /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2229AE29CDC08700F00E69 /* settings.swift */; };
|
||||
5C2229B029CDFBF600F00E69 /* Kit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A2846F72666A9CC00EC1F6D /* Kit.framework */; };
|
||||
5C2229B829CE3F3300F00E69 /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2229B729CE3F3300F00E69 /* popup.swift */; };
|
||||
5C2229BD29CF685A00F00E69 /* header.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C2229BB29CF66B100F00E69 /* header.h */; };
|
||||
5C23BC0229A0102500DBA990 /* portal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C23BC0129A0102500DBA990 /* portal.swift */; };
|
||||
5C23BC0429A014AC00DBA990 /* portal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C23BC0329A014AC00DBA990 /* portal.swift */; };
|
||||
5C23BC0829A03D1200DBA990 /* portal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C23BC0729A03D1200DBA990 /* portal.swift */; };
|
||||
@@ -145,6 +153,20 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
5C2229A129CCB3C400F00E69 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 5C22299C29CCB3C400F00E69;
|
||||
remoteInfo = Clock;
|
||||
};
|
||||
5C2229B229CDFBF600F00E69 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 9A2846F62666A9CC00EC1F6D;
|
||||
remoteInfo = Kit;
|
||||
};
|
||||
9A11AAD4266FD77F000C1C05 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
|
||||
@@ -310,6 +332,7 @@
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
9AF9EE0A24648751005D2270 /* Disk.framework in Embed Frameworks */,
|
||||
5C2229A429CCB3C400F00E69 /* Clock.framework in Embed Frameworks */,
|
||||
9A81C75E2449A41400825D92 /* RAM.framework in Embed Frameworks */,
|
||||
9AE29ADD249A50350071B02D /* Sensors.framework in Embed Frameworks */,
|
||||
9A11AAD7266FD77F000C1C05 /* Bluetooth.framework in Embed Frameworks */,
|
||||
@@ -355,6 +378,12 @@
|
||||
4921436D25319699000A1C47 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5C0A2A89292A5B4D009B4C1F /* SMJobBlessUtil.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = SMJobBlessUtil.py; sourceTree = "<group>"; };
|
||||
5C21D80A296C7B81005BA16D /* CombinedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombinedView.swift; sourceTree = "<group>"; };
|
||||
5C22299D29CCB3C400F00E69 /* Clock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Clock.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5C2229A829CCB41900F00E69 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
5C2229AA29CCB53E00F00E69 /* config.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = config.plist; sourceTree = "<group>"; };
|
||||
5C2229AE29CDC08700F00E69 /* settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = "<group>"; };
|
||||
5C2229B729CE3F3300F00E69 /* popup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = popup.swift; sourceTree = "<group>"; };
|
||||
5C2229BB29CF66B100F00E69 /* header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = header.h; sourceTree = "<group>"; };
|
||||
5C23BC0129A0102500DBA990 /* portal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = portal.swift; sourceTree = "<group>"; };
|
||||
5C23BC0329A014AC00DBA990 /* portal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = portal.swift; sourceTree = "<group>"; };
|
||||
5C23BC0729A03D1200DBA990 /* portal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = portal.swift; sourceTree = "<group>"; };
|
||||
@@ -516,6 +545,14 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
5C22299A29CCB3C400F00E69 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C2229B029CDFBF600F00E69 /* Kit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CFE492429264DF1000F2856 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -536,6 +573,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9AF9EE0924648751005D2270 /* Disk.framework in Frameworks */,
|
||||
5C2229A329CCB3C400F00E69 /* Clock.framework in Frameworks */,
|
||||
9AE29ADC249A50350071B02D /* Sensors.framework in Frameworks */,
|
||||
9ABFF8FD248BEBCB00C9041A /* Battery.framework in Frameworks */,
|
||||
9A11AAD6266FD77F000C1C05 /* Bluetooth.framework in Frameworks */,
|
||||
@@ -635,6 +673,17 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
5C22299E29CCB3C400F00E69 /* Clock */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5C2229A829CCB41900F00E69 /* main.swift */,
|
||||
5C2229B729CE3F3300F00E69 /* popup.swift */,
|
||||
5C2229AE29CDC08700F00E69 /* settings.swift */,
|
||||
5C2229AA29CCB53E00F00E69 /* config.plist */,
|
||||
);
|
||||
path = Clock;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5CFE492829264DF1000F2856 /* Helper */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -690,6 +739,7 @@
|
||||
9A11AACF266FD77F000C1C05 /* Bluetooth.framework */,
|
||||
9AAC5E2A280ACC120043D892 /* Tests.xctest */,
|
||||
5CFE492729264DF1000F2856 /* eu.exelban.Stats.SMC.Helper */,
|
||||
5C22299D29CCB3C400F00E69 /* Clock.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -909,6 +959,7 @@
|
||||
9A3E17CD247A94AF00449CD1 /* Net */,
|
||||
9ABFF8F7248BEBCB00C9041A /* Battery */,
|
||||
9A11AAD0266FD77F000C1C05 /* Bluetooth */,
|
||||
5C22299E29CCB3C400F00E69 /* Clock */,
|
||||
);
|
||||
path = Modules;
|
||||
sourceTree = "<group>";
|
||||
@@ -963,6 +1014,7 @@
|
||||
9AB7FD7B246B48DB00387FDA /* settings.swift */,
|
||||
9AF9EE0524648751005D2270 /* Info.plist */,
|
||||
9AF9EE12246492E8005D2270 /* config.plist */,
|
||||
5C2229BB29CF66B100F00E69 /* header.h */,
|
||||
);
|
||||
path = Disk;
|
||||
sourceTree = "<group>";
|
||||
@@ -970,6 +1022,13 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
5C22299829CCB3C400F00E69 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9A11AACA266FD77F000C1C05 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1030,12 +1089,32 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C2229BD29CF685A00F00E69 /* header.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
5C22299C29CCB3C400F00E69 /* Clock */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5C2229A529CCB3C400F00E69 /* Build configuration list for PBXNativeTarget "Clock" */;
|
||||
buildPhases = (
|
||||
5C22299829CCB3C400F00E69 /* Headers */,
|
||||
5C22299929CCB3C400F00E69 /* Sources */,
|
||||
5C22299A29CCB3C400F00E69 /* Frameworks */,
|
||||
5C22299B29CCB3C400F00E69 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
5C2229B329CDFBF600F00E69 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Clock;
|
||||
productName = Clock;
|
||||
productReference = 5C22299D29CCB3C400F00E69 /* Clock.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
5CFE492629264DF1000F2856 /* Helper */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5CFE492D29264DF1000F2856 /* Build configuration list for PBXNativeTarget "Helper" */;
|
||||
@@ -1098,6 +1177,7 @@
|
||||
9A97CED02537331B00742D8F /* PBXTargetDependency */,
|
||||
9A2846FD2666A9CC00EC1F6D /* PBXTargetDependency */,
|
||||
9A11AAD5266FD77F000C1C05 /* PBXTargetDependency */,
|
||||
5C2229A229CCB3C400F00E69 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Stats;
|
||||
packageProductDependencies = (
|
||||
@@ -1325,6 +1405,10 @@
|
||||
LastUpgradeCheck = 1420;
|
||||
ORGANIZATIONNAME = "Serhiy Mytrovtsiy";
|
||||
TargetAttributes = {
|
||||
5C22299C29CCB3C400F00E69 = {
|
||||
CreatedOnToolsVersion = 14.2;
|
||||
LastSwiftMigration = 1420;
|
||||
};
|
||||
5CFE492629264DF1000F2856 = {
|
||||
CreatedOnToolsVersion = 14.1;
|
||||
};
|
||||
@@ -1447,6 +1531,7 @@
|
||||
9ABFF8F5248BEBCB00C9041A /* Battery */,
|
||||
9AE29AD4249A50350071B02D /* Sensors */,
|
||||
9A11AACE266FD77F000C1C05 /* Bluetooth */,
|
||||
5C22299C29CCB3C400F00E69 /* Clock */,
|
||||
9AAC5E29280ACC120043D892 /* Tests */,
|
||||
5CFE492629264DF1000F2856 /* Helper */,
|
||||
);
|
||||
@@ -1454,6 +1539,14 @@
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
5C22299B29CCB3C400F00E69 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C2229AB29CCB53E00F00E69 /* config.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9A11AACD266FD77F000C1C05 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1578,6 +1671,16 @@
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
5C22299929CCB3C400F00E69 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C2229AF29CDC08700F00E69 /* settings.swift in Sources */,
|
||||
5C2229B829CE3F3300F00E69 /* popup.swift in Sources */,
|
||||
5C2229A929CCB41900F00E69 /* main.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CFE492329264DF1000F2856 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1767,6 +1870,16 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
5C2229A229CCB3C400F00E69 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 5C22299C29CCB3C400F00E69 /* Clock */;
|
||||
targetProxy = 5C2229A129CCB3C400F00E69 /* PBXContainerItemProxy */;
|
||||
};
|
||||
5C2229B329CDFBF600F00E69 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 9A2846F62666A9CC00EC1F6D /* Kit */;
|
||||
targetProxy = 5C2229B229CDFBF600F00E69 /* PBXContainerItemProxy */;
|
||||
};
|
||||
9A11AAD5266FD77F000C1C05 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 9A11AACE266FD77F000C1C05 /* Bluetooth */;
|
||||
@@ -1903,6 +2016,81 @@
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
5C2229A629CCB3C400F00E69 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = RP2S87B72W;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Serhiy Mytrovtsiy. All rights reserved.";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats.Clock;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5C2229A729CCB3C400F00E69 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = RP2S87B72W;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Serhiy Mytrovtsiy. All rights reserved.";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats.Clock;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
5CFE492B29264DF1000F2856 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -2908,6 +3096,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = Modules/Disk/header.h;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -2943,6 +3132,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = Modules/Disk/header.h;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -2952,6 +3142,15 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
5C2229A529CCB3C400F00E69 /* Build configuration list for PBXNativeTarget "Clock" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5C2229A629CCB3C400F00E69 /* Debug */,
|
||||
5C2229A729CCB3C400F00E69 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
5CFE492D29264DF1000F2856 /* Build configuration list for PBXNativeTarget "Helper" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
@@ -19,6 +19,7 @@ import Battery
|
||||
import Sensors
|
||||
import GPU
|
||||
import Bluetooth
|
||||
import Clock
|
||||
|
||||
let updater = Updater(github: "exelban/stats", url: "https://api.serhiy.io/v1/stats/release/latest")
|
||||
var modules: [Module] = [
|
||||
@@ -29,7 +30,8 @@ var modules: [Module] = [
|
||||
Sensors(),
|
||||
Network(),
|
||||
Battery(),
|
||||
Bluetooth()
|
||||
Bluetooth(),
|
||||
Clock()
|
||||
]
|
||||
|
||||
@main
|
||||
|
||||
Reference in New Issue
Block a user