feat: added an option to pause/resume the application (#834)

This commit is contained in:
Serhiy Mytrovtsiy
2022-09-10 12:08:46 +02:00
parent 347b606972
commit 4056b61616
8 changed files with 213 additions and 7 deletions

View File

@@ -1132,3 +1132,104 @@ public func restoreNSStatusItemPosition(id: String) {
Store.shared.remove("NSStatusItem Restore Position \(id)")
}
}
public class AppIcon: NSView {
public static let size: CGSize = CGSize(width: 16, height: 16)
public init() {
super.init(frame: NSRect(x: 0, y: 3, width: AppIcon.size.width, height: AppIcon.size.height))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
guard let ctx = NSGraphicsContext.current?.cgContext else { return }
ctx.setShouldAntialias(true)
NSColor.textColor.set()
NSBezierPath(roundedRect: NSRect(
x: 0,
y: 0,
width: AppIcon.size.width,
height: AppIcon.size.height
), xRadius: 4, yRadius: 4).fill()
NSColor.controlTextColor.set()
NSBezierPath(roundedRect: NSRect(
x: 1.5,
y: 1.5,
width: AppIcon.size.width - 3,
height: AppIcon.size.height - 3
), xRadius: 3, yRadius: 3).fill()
let lineWidth = 1 / (NSScreen.main?.backingScaleFactor ?? 1) / 2
let offset = lineWidth/2
let zero = (AppIcon.size.height - 3 + 1.5)/2 + lineWidth
let x = 1.5
let downloadLine = drawLine(points: [
(x+0, zero-offset),
(x+1, zero-offset),
(x+2, zero-offset-2.5),
(x+3, zero-offset-4),
(x+4, zero-offset),
(x+5, zero-offset-2),
(x+6, zero-offset),
(x+7, zero-offset),
(x+8, zero-offset-2),
(x+9, zero-offset),
(x+10, zero-offset-4),
(x+11, zero-offset-0.5),
(x+12, zero-offset)
], color: NSColor.systemBlue, lineWidth: lineWidth)
let uploadLine = drawLine(points: [
(x+0, zero+offset),
(x+1, zero+offset),
(x+2, zero+offset+2),
(x+3, zero+offset),
(x+4, zero+offset),
(x+5, zero+offset),
(x+6, zero+offset+3),
(x+7, zero+offset+3),
(x+8, zero+offset),
(x+9, zero+offset+1),
(x+10, zero+offset+5),
(x+11, zero+offset),
(x+12, zero+offset)
], color: NSColor.systemRed, lineWidth: lineWidth)
ctx.saveGState()
drawUnderLine(dirtyRect, path: downloadLine, color: NSColor.systemBlue, x: x, y: zero-offset)
ctx.restoreGState()
ctx.saveGState()
drawUnderLine(dirtyRect, path: uploadLine, color: NSColor.systemRed, x: x, y: zero+offset)
ctx.restoreGState()
}
private func drawLine(points: [(CGFloat, CGFloat)], color: NSColor, lineWidth: CGFloat) -> NSBezierPath {
let linePath = NSBezierPath()
linePath.move(to: CGPoint(x: points[0].0, y: points[0].1))
for i in 1..<points.count {
linePath.line(to: CGPoint(x: points[i].0, y: points[i].1))
}
color.setStroke()
linePath.lineWidth = lineWidth
linePath.stroke()
return linePath
}
private func drawUnderLine(_ rect: NSRect, path: NSBezierPath, color: NSColor, x: CGFloat, y: CGFloat) {
let underLinePath = path.copy() as! NSBezierPath
underLinePath.line(to: CGPoint(x: x, y: y))
underLinePath.line(to: CGPoint(x: x, y: y))
underLinePath.close()
underLinePath.addClip()
color.withAlphaComponent(0.5).setFill()
NSBezierPath(rect: rect).fill()
}
}

View File

@@ -24,11 +24,11 @@ public struct module_c {
public var name: String = ""
public var icon: NSImage?
var defaultState: Bool = false
var defaultWidget: widget_t = .unknown
var availableWidgets: [widget_t] = []
public var defaultState: Bool = false
internal var defaultWidget: widget_t = .unknown
internal var availableWidgets: [widget_t] = []
var widgetsConfig: NSDictionary = NSDictionary()
internal var widgetsConfig: NSDictionary = NSDictionary()
init(in path: String) {
let dict: NSDictionary = NSDictionary(contentsOfFile: path)!
@@ -80,6 +80,15 @@ open class Module: Module_p {
private let log: NextLog
private var readers: [Reader_p] = []
private var pauseState: Bool {
get {
return Store.shared.bool(key: "pause", defaultValue: false)
}
set {
Store.shared.set(key: "pause", value: newValue)
}
}
public init(popup: Popup_p? = nil, settings: Settings_v? = nil) {
self.config = module_c(in: Bundle(for: type(of: self)).path(forResource: "config", ofType: "plist")!)
@@ -99,6 +108,8 @@ open class Module: Module_p {
}
return
} else if self.pauseState {
self.disable()
}
NotificationCenter.default.addObserver(self, selector: #selector(listenForMouseDownInSettings), name: .clickInSettings, object: nil)
@@ -116,6 +127,10 @@ open class Module: Module_p {
self.settings = Settings(config: &self.config, widgets: &self.menuBar.widgets, enabled: self.enabled, moduleSettings: self.settingsView)
self.settings?.toggleCallback = { [weak self] in
self?.toggleEnabled()
if self?.pauseState == true {
self?.pauseState = false
NotificationCenter.default.post(name: .pause, object: nil, userInfo: ["state": false])
}
}
self.popup = PopupWindow(title: self.config.name, view: self.popupView, visibilityCallback: self.visibilityCallback)
@@ -168,6 +183,7 @@ open class Module: Module_p {
reader.start()
}
self.menuBar.enable()
self.settings?.setState(self.enabled)
debug("Module enabled", log: self.log)
}
@@ -176,9 +192,12 @@ open class Module: Module_p {
guard self.available else { return }
self.enabled = false
Store.shared.set(key: "\(self.config.name)_state", value: false)
if !self.pauseState { // omit saving the disable state when toggle by pause, need for resume state restoration
Store.shared.set(key: "\(self.config.name)_state", value: false)
}
self.readers.forEach{ $0.stop() }
self.menuBar.disable()
self.settings?.setState(self.enabled)
self.popup?.setIsVisible(false)
debug("Module disabled", log: self.log)
}

View File

@@ -13,6 +13,7 @@ import Cocoa
public protocol Settings_p: NSView {
var toggleCallback: () -> Void { get set }
func setState(_ newState: Bool)
}
public protocol Settings_v: NSView {
@@ -214,6 +215,10 @@ open class Settings: NSStackView, Settings_p {
}
}
public func setState(_ newState: Bool) {
toggleNSControlState(self.enableControl, state: newState ? .on : .off)
}
private func loadWidget() {
self.loadModuleSettings()
self.loadWidgetSettings()

View File

@@ -207,6 +207,7 @@ public extension Notification.Name {
static let syncFansControl = Notification.Name("syncFansControl")
static let toggleOneView = Notification.Name("toggleOneView")
static let widgetRearrange = Notification.Name("widgetRearrange")
static let pause = Notification.Name("pause")
}
public var isARM: Bool {

View File

@@ -39,6 +39,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
internal let setupWindow: SetupWindow = SetupWindow()
internal let updateActivity = NSBackgroundActivityScheduler(identifier: "eu.exelban.Stats.updateCheck")
internal var clickInNotification: Bool = false
internal var menuBarItem: NSStatusItem? = nil
internal var pauseState: Bool {
Store.shared.bool(key: "pause", defaultValue: false)
}
static func main() {
let app = NSApplication.shared
@@ -56,8 +61,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
modules.forEach{ $0.mount() }
self.settingsWindow.setModules()
}
self.defaultValues()
self.icon()
NotificationCenter.default.addObserver(self, selector: #selector(listenForAppPause), name: .pause, object: nil)
info("Stats started in \((startingPoint.timeIntervalSinceNow * -1).rounded(toPlaces: 4)) seconds")
}

View File

@@ -28,9 +28,19 @@ class ApplicationSettings: NSStackView {
}
}
private var pauseState: Bool {
get {
return Store.shared.bool(key: "pause", defaultValue: false)
}
set {
Store.shared.set(key: "pause", value: newValue)
}
}
private let updateWindow: UpdateWindow = UpdateWindow()
private var updateSelector: NSPopUpButton?
private var startAtLoginBtn: NSButton?
private var pauseButton: NSButton?
init() {
super.init(frame: NSRect(
@@ -49,12 +59,18 @@ class ApplicationSettings: NSStackView {
self.addArrangedSubview(self.settingsView())
self.addArrangedSubview(self.separatorView())
self.addArrangedSubview(self.buttonsView())
NotificationCenter.default.addObserver(self, selector: #selector(listenForPause), name: .pause, object: nil)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
public func viewWillAppear() {
self.startAtLoginBtn?.state = LaunchAtLogin.isEnabled ? .on : .off
@@ -183,7 +199,15 @@ class ApplicationSettings: NSStackView {
reset.target = self
reset.action = #selector(self.resetSettings)
let pause: NSButton = NSButton()
pause.title = localizedString(self.pauseState ? "Resume the Stats" : "Pause the Stats")
pause.bezelStyle = .rounded
pause.target = self
pause.action = #selector(self.togglePause)
self.pauseButton = pause
view.addArrangedSubview(reset)
view.addArrangedSubview(pause)
return view
}
@@ -288,4 +312,14 @@ class ApplicationSettings: NSStackView {
}
}
}
@objc func togglePause(_ sender: NSButton) {
self.pauseState = !self.pauseState
self.pauseButton?.title = localizedString(self.pauseState ? "Resume the Stats" : "Pause the Stats")
NotificationCenter.default.post(name: .pause, object: nil, userInfo: ["state": self.pauseState])
}
@objc func listenForPause() {
self.pauseButton?.title = localizedString(self.pauseState ? "Resume the Stats" : "Pause the Stats")
}
}

View File

@@ -15,6 +15,10 @@ import Kit
class SettingsWindow: NSWindow, NSWindowDelegate {
private let viewController: SettingsViewController = SettingsViewController()
private var pauseState: Bool {
Store.shared.bool(key: "pause", defaultValue: false)
}
init() {
super.init(
contentRect: NSRect(
@@ -76,6 +80,9 @@ class SettingsWindow: NSWindow, NSWindowDelegate {
self.setIsVisible(true)
self.makeKeyAndOrderFront(nil)
}
if !self.isKeyWindow {
self.orderFrontRegardless()
}
if let name = notification.userInfo?["module"] as? String {
self.viewController.openMenu(name)
@@ -84,7 +91,7 @@ class SettingsWindow: NSWindow, NSWindowDelegate {
public func setModules() {
self.viewController.setModules(modules)
if modules.filter({ $0.enabled != false && $0.available != false && !$0.menuBar.widgets.filter({ $0.isActive }).isEmpty }).isEmpty {
if !self.pauseState && modules.filter({ $0.enabled != false && $0.available != false && !$0.menuBar.widgets.filter({ $0.isActive }).isEmpty }).isEmpty {
self.setIsVisible(true)
}
}

View File

@@ -237,4 +237,36 @@ extension AppDelegate {
self.updateWindow.open(version)
})
}
@objc internal func listenForAppPause() {
for m in modules {
if self.pauseState && m.enabled {
m.disable()
} else if !self.pauseState && !m.enabled && Store.shared.bool(key: "\(m.config.name)_state", defaultValue: m.config.defaultState) {
m.enable()
}
}
self.icon()
}
internal func icon() {
if self.pauseState {
self.menuBarItem = NSStatusBar.system.statusItem(withLength: AppIcon.size.width)
self.menuBarItem?.autosaveName = "Stats"
self.menuBarItem?.button?.addSubview(AppIcon())
self.menuBarItem?.button?.target = self
self.menuBarItem?.button?.action = #selector(self.openSettings)
self.menuBarItem?.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
} else {
if let item = self.menuBarItem {
NSStatusBar.system.removeStatusItem(item)
}
self.menuBarItem = nil
}
}
@objc internal func openSettings() {
NotificationCenter.default.post(name: .toggleSettings, object: nil, userInfo: ["module": "Dashboard"])
}
}