feat: added Clock module (#929)

This commit is contained in:
Serhiy Mytrovtsiy
2023-03-31 20:17:36 +02:00
parent 926838c52a
commit 4314de533c
8 changed files with 881 additions and 1 deletions

View File

@@ -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
}
}
}

View 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
View 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
View 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()
})
}
}

View 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
View 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>

View File

@@ -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 = (

View File

@@ -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