From 58dcab754f054165cf362a5e7ea6c37365d73b11 Mon Sep 17 00:00:00 2001 From: Dan Barber Date: Fri, 8 Feb 2019 12:02:10 -0500 Subject: [PATCH] Bunch of changes based on PR feedback --- Persephone.xcodeproj/project.pbxproj | 16 ++++++ .../Controllers/NotificationsController.swift | 16 +++--- .../Controllers/QueueViewController.swift | 54 ++++++++++--------- Persephone/Controllers/WindowController.swift | 24 +++++---- .../NSUserInterfaceItemIdentifier.swift | 18 +++++++ Persephone/Extensions/Notification.swift | 19 +++++++ Persephone/MPDClient/MPDClient.swift | 42 ++++++--------- Persephone/MPDClient/Models/Status.swift | 26 +++++++-- Persephone/MPDClient/Protocols/Delegate.swift | 4 +- 9 files changed, 141 insertions(+), 78 deletions(-) create mode 100644 Persephone/Extensions/NSUserInterfaceItemIdentifier.swift create mode 100644 Persephone/Extensions/Notification.swift diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 1d5a54c..982ec1a 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ E40786232110CE70006887B1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E40786212110CE70006887B1 /* Main.storyboard */; }; E407862F2110CE70006887B1 /* PersephoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E407862E2110CE70006887B1 /* PersephoneTests.swift */; }; E407863A2110CE70006887B1 /* PersephoneUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40786392110CE70006887B1 /* PersephoneUITests.swift */; }; + E408D3B6220DD8970006D9BE /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3B5220DD8970006D9BE /* Notification.swift */; }; + E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */; }; E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (Required, ); }; }; E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; }; @@ -67,6 +69,8 @@ E40786352110CE70006887B1 /* PersephoneUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PersephoneUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E40786392110CE70006887B1 /* PersephoneUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersephoneUITests.swift; sourceTree = ""; }; E407863B2110CE70006887B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E408D3B5220DD8970006D9BE /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; + E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSUserInterfaceItemIdentifier.swift; sourceTree = ""; }; E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpdclient.2.dylib; path = libmpdclient/output/libmpdclient.2.dylib; sourceTree = ""; }; E41B22C421FB715A00D544F6 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; E41B22C521FB932700D544F6 /* MPDClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDClient.swift; sourceTree = ""; }; @@ -163,6 +167,7 @@ E407861A2110CE6E006887B1 /* Persephone */ = { isa = PBXGroup; children = ( + E408D3B7220DE8CC0006D9BE /* Extensions */, E4D1B598220BA3C90026F233 /* Resources */, E4D1B597220BA3A20026F233 /* Controllers */, E41B22C721FB966C00D544F6 /* include */, @@ -192,6 +197,15 @@ path = PersephoneUITests; sourceTree = ""; }; + E408D3B7220DE8CC0006D9BE /* Extensions */ = { + isa = PBXGroup; + children = ( + E408D3B5220DD8970006D9BE /* Notification.swift */, + E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */, + ); + path = Extensions; + sourceTree = ""; + }; E41B22BE21FB6B3300D544F6 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -435,6 +449,8 @@ files = ( E4A642DA22090CBE00067D21 /* Status.swift in Sources */, E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */, + E408D3B6220DD8970006D9BE /* Notification.swift in Sources */, + E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */, E465049A21E94DF500A70F4C /* WindowController.swift in Sources */, E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */, E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */, diff --git a/Persephone/Controllers/NotificationsController.swift b/Persephone/Controllers/NotificationsController.swift index edda291..ffa9c1d 100644 --- a/Persephone/Controllers/NotificationsController.swift +++ b/Persephone/Controllers/NotificationsController.swift @@ -12,24 +12,24 @@ import mpdclient class NotificationsController: MPDClientDelegate { let notificationQueue = DispatchQueue.main - func didUpdateState(mpdClient: MPDClient, state: mpd_state) { + func didUpdateState(mpdClient: MPDClient, state: MPDClient.Status.State) { sendNotification( - name: MPDClient.stateChanged, - userInfo: [MPDClient.stateKey: state] + name: Notification.stateChanged, + userInfo: [Notification.stateKey: state] ) } func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song]) { sendNotification( - name: MPDClient.queueChanged, - userInfo: [MPDClient.queueKey: queue] + name: Notification.queueChanged, + userInfo: [Notification.queueKey: queue] ) } - func didUpdateQueuePos(mpdClient: MPDClient, song: Int32) { + func didUpdateQueuePos(mpdClient: MPDClient, song: Int) { sendNotification( - name: MPDClient.queuePosChanged, - userInfo: [MPDClient.queuePosKey: song] + name: Notification.queuePosChanged, + userInfo: [Notification.queuePosKey: song] ) } diff --git a/Persephone/Controllers/QueueViewController.swift b/Persephone/Controllers/QueueViewController.swift index 2d09bb6..f48ec83 100644 --- a/Persephone/Controllers/QueueViewController.swift +++ b/Persephone/Controllers/QueueViewController.swift @@ -11,13 +11,16 @@ import mpdclient class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate { var queue: [MPDClient.Song] = [] - var queuePos: Int32 = -1 + var queuePos: Int = -1 var queueIcon: NSImage? = nil let systemFontRegular = NSFont.systemFont(ofSize: 13, weight: .regular) let systemFontBold = NSFont.systemFont(ofSize: 13, weight: .bold) + let playIcon = NSImage(named: NSImage.Name("playButton")) + let pauseIcon = NSImage(named: NSImage.Name("pauseButton")) + struct SongItem { var song: MPDClient.Song var queuePos: Int @@ -31,34 +34,34 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV NotificationCenter.default.addObserver( self, selector: #selector(stateChanged(_:)), - name: MPDClient.stateChanged, + name: Notification.stateChanged, object: AppDelegate.mpdClient ) NotificationCenter.default.addObserver( self, selector: #selector(queueChanged(_:)), - name: MPDClient.queueChanged, + name: Notification.queueChanged, object: AppDelegate.mpdClient ) NotificationCenter.default.addObserver( self, selector: #selector(queuePosChanged(_:)), - name: MPDClient.queuePosChanged, + name: Notification.queuePosChanged, object: AppDelegate.mpdClient ) } @objc func stateChanged(_ notification: Notification) { - guard let state = notification.userInfo?[MPDClient.stateKey] as? mpd_state + guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.Status.State else { return } setQueueIcon(state) } @objc func queueChanged(_ notification: Notification) { - guard let queue = notification.userInfo?[MPDClient.queueKey] as? [MPDClient.Song] + guard let queue = notification.userInfo?[Notification.queueKey] as? [MPDClient.Song] else { return } self.queue = queue @@ -67,11 +70,13 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV } @objc func queuePosChanged(_ notification: Notification) { - guard let queuePos = notification.userInfo?[MPDClient.queuePosKey] as? Int32 + guard let queuePos = notification.userInfo?[Notification.queuePosKey] as? Int else { return } - let oldSongRowPos = Int(self.queuePos + 1) - let newSongRowPos = Int(queuePos + 1) + print(queuePos) + + let oldSongRowPos = self.queuePos + 1 + let newSongRowPos = queuePos + 1 self.queuePos = queuePos setQueuePos(oldSongRowPos: oldSongRowPos, newSongRowPos: newSongRowPos) @@ -82,12 +87,12 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV ) } - func setQueueIcon(_ state: mpd_state) { + func setQueueIcon(_ state: MPDClient.Status.State) { switch state { - case MPD_STATE_PLAY: - self.queueIcon = NSImage(named: NSImage.Name("playButton")) - case MPD_STATE_PAUSE: - self.queueIcon = NSImage(named: NSImage.Name("pauseButton")) + case .playing: + self.queueIcon = playIcon + case .paused: + self.queueIcon = pauseIcon default: self.queueIcon = nil } @@ -95,18 +100,16 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV func setQueuePos(oldSongRowPos: Int, newSongRowPos: Int) { if oldSongRowPos > 0 { - guard let oldSongRow = queueView.rowView(atRow: oldSongRowPos, makeIfNecessary: true) - else { return } - guard let oldSongTitleCell = oldSongRow.view(atColumn: 0) as? NSTableCellView + guard let oldSongRow = queueView.rowView(atRow: oldSongRowPos, makeIfNecessary: true), + let oldSongTitleCell = oldSongRow.view(atColumn: 0) as? NSTableCellView else { return } setRowFont(rowView: oldSongRow, font: systemFontRegular) oldSongTitleCell.imageView?.image = nil } - guard let songRow = queueView.rowView(atRow: newSongRowPos, makeIfNecessary: true) - else { return } - guard let newSongTitleCell = songRow.view(atColumn: 0) as? NSTableCellView + guard let songRow = queueView.rowView(atRow: newSongRowPos, makeIfNecessary: true), + let newSongTitleCell = songRow.view(atColumn: 0) as? NSTableCellView else { return } setRowFont(rowView: songRow, font: systemFontBold) @@ -114,9 +117,8 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV } func setRowFont(rowView: NSTableRowView, font: NSFont) { - guard let songTitleCell = rowView.view(atColumn: 0) as? NSTableCellView - else { return } - guard let songArtistCell = rowView.view(atColumn: 1) as? NSTableCellView + guard let songTitleCell = rowView.view(atColumn: 0) as? NSTableCellView, + let songArtistCell = rowView.view(atColumn: 1) as? NSTableCellView else { return } songTitleCell.textField?.font = font @@ -144,7 +146,7 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV switch tableColumn?.identifier.rawValue { case "songTitleColumn": let cellView = outlineView.makeView( - withIdentifier: NSUserInterfaceItemIdentifier("songTitleCell"), + withIdentifier: .queueSongTitle, owner: self ) as! NSTableCellView @@ -153,7 +155,7 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV return cellView case "songArtistColumn": let cellView = outlineView.makeView( - withIdentifier: NSUserInterfaceItemIdentifier("songArtistCell"), + withIdentifier: .queueSongArtist, owner: self ) as! NSTableCellView @@ -166,7 +168,7 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV } else { if tableColumn?.identifier.rawValue == "songTitleColumn" { let cellView = outlineView.makeView( - withIdentifier: NSUserInterfaceItemIdentifier("queueHeadingCell"), + withIdentifier: .queueHeading, owner: self ) as! NSTableCellView diff --git a/Persephone/Controllers/WindowController.swift b/Persephone/Controllers/WindowController.swift index aa303f7..903485c 100644 --- a/Persephone/Controllers/WindowController.swift +++ b/Persephone/Controllers/WindowController.swift @@ -7,7 +7,6 @@ // import Cocoa -import mpdclient class WindowController: NSWindowController { enum TransportAction: Int { @@ -17,6 +16,9 @@ class WindowController: NSWindowController { case nextTrack = 3 } + let playIcon = NSImage(named: NSImage.Name("playButton")) + let pauseIcon = NSImage(named: NSImage.Name("pauseButton")) + override func windowDidLoad() { super.windowDidLoad() window?.titleVisibility = .hidden @@ -24,28 +26,28 @@ class WindowController: NSWindowController { NotificationCenter.default.addObserver( self, selector: #selector(stateChanged(_:)), - name: MPDClient.stateChanged, + name: Notification.stateChanged, object: AppDelegate.mpdClient ) } @objc func stateChanged(_ notification: Notification) { - guard let state = notification.userInfo?[MPDClient.stateKey] as? mpd_state + guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.Status.State else { return } setTransportControlState(state) } - func setTransportControlState(_ state: mpd_state) { - transportControls.setEnabled([MPD_STATE_PLAY, MPD_STATE_PAUSE].contains(state), forSegment: 0) - transportControls.setEnabled([MPD_STATE_PLAY, MPD_STATE_PAUSE, MPD_STATE_STOP].contains(state), forSegment: 1) - transportControls.setEnabled([MPD_STATE_PLAY, MPD_STATE_PAUSE].contains(state), forSegment: 2) - transportControls.setEnabled([MPD_STATE_PLAY, MPD_STATE_PAUSE].contains(state), forSegment: 3) + func setTransportControlState(_ state: MPDClient.Status.State) { + transportControls.setEnabled(state.isOneOf([.playing, .paused]), forSegment: 0) + transportControls.setEnabled(state.isOneOf([.playing, .paused, .stopped]), forSegment: 1) + transportControls.setEnabled(state.isOneOf([.playing, .paused]), forSegment: 2) + transportControls.setEnabled(state.isOneOf([.playing, .paused]), forSegment: 3) - if [MPD_STATE_PAUSE, MPD_STATE_STOP, MPD_STATE_UNKNOWN].contains(state) { - transportControls.setImage(NSImage(named: NSImage.Name("playButton")), forSegment: 1) + if state.isOneOf([.paused, .stopped, .unknown]) { + transportControls.setImage(playIcon, forSegment: 1) } else { - transportControls.setImage(NSImage(named: NSImage.Name("pauseButton")), forSegment: 1) + transportControls.setImage(pauseIcon, forSegment: 1) } } diff --git a/Persephone/Extensions/NSUserInterfaceItemIdentifier.swift b/Persephone/Extensions/NSUserInterfaceItemIdentifier.swift new file mode 100644 index 0000000..feaf63e --- /dev/null +++ b/Persephone/Extensions/NSUserInterfaceItemIdentifier.swift @@ -0,0 +1,18 @@ +// +// NSUserInterfaceItemIdentifier.swift +// Persephone +// +// Created by Daniel Barber on 2019/2/08. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import Cocoa + +extension NSUserInterfaceItemIdentifier { + static let queueSongTitleColumn = NSUserInterfaceItemIdentifier("songTitleColumn") + static let queueSongArtistColumn = NSUserInterfaceItemIdentifier("songArtistColumn") + + static let queueHeading = NSUserInterfaceItemIdentifier("queueHeadingCell") + static let queueSongArtist = NSUserInterfaceItemIdentifier("songArtistCell") + static let queueSongTitle = NSUserInterfaceItemIdentifier("songTitleCell") +} diff --git a/Persephone/Extensions/Notification.swift b/Persephone/Extensions/Notification.swift new file mode 100644 index 0000000..ad8e275 --- /dev/null +++ b/Persephone/Extensions/Notification.swift @@ -0,0 +1,19 @@ +// +// Notification.swift +// Persephone +// +// Created by Daniel Barber on 2019/2/08. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import Foundation + +extension Notification { + static let stateChanged = Notification.Name("MPDClientStateChanged") + static let queueChanged = Notification.Name("MPDClientQueueChanged") + static let queuePosChanged = Notification.Name("MPDClientQueuePosChanged") + + static let stateKey = "state" + static let queueKey = "queue" + static let queuePosKey = "song" +} diff --git a/Persephone/MPDClient/MPDClient.swift b/Persephone/MPDClient/MPDClient.swift index 074310d..19d1c39 100644 --- a/Persephone/MPDClient/MPDClient.swift +++ b/Persephone/MPDClient/MPDClient.swift @@ -11,19 +11,12 @@ import mpdclient class MPDClient { var delegate: MPDClientDelegate? - static let stateChanged = Notification.Name("MPDClientStateChanged") - static let queueChanged = Notification.Name("MPDClientQueueChanged") - static let queuePosChanged = Notification.Name("MPDClientQueuePosChanged") - - static let stateKey = "state" - static let queueKey = "queue" - static let queuePosKey = "song" let HOST = "localhost" let PORT: UInt32 = 6600 private var connection: OpaquePointer? - var status: Status? + private var status: Status? private var queue: [Song] = [] private let commandQueue = DispatchQueue(label: "commandQueue") @@ -32,13 +25,6 @@ class MPDClient { case prevTrack, nextTrack, playPause, stop, fetchStatus, fetchQueue } - enum State: UInt32 { - case unknown = 0 - case stopped = 1 - case playing = 2 - case paused = 3 - } - struct Idle: OptionSet { let rawValue: UInt32 @@ -71,9 +57,9 @@ class MPDClient { fetchQueue() - self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state()) + self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state) self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue) - self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song()) + self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song) idle() } @@ -145,15 +131,19 @@ class MPDClient { } func sendNextTrack() { - if [MPD_STATE_PLAY, MPD_STATE_PAUSE].contains(status?.state()) { - mpd_run_next(connection) - } + guard let state = status?.state, + state.isOneOf([.playing, .paused]) + else { return } + + mpd_run_next(connection) } func sendPreviousTrack() { - if [MPD_STATE_PLAY, MPD_STATE_PAUSE].contains(status?.state()) { - mpd_run_previous(connection) - } + guard let state = status?.state, + state.isOneOf([.playing, .paused]) + else { return } + + mpd_run_previous(connection) } func sendStop() { @@ -161,7 +151,7 @@ class MPDClient { } func sendPlay() { - if status?.state() == MPD_STATE_STOP { + if status?.state == .stopped { mpd_run_play(connection) } else { mpd_run_toggle_pause(connection) @@ -190,8 +180,8 @@ class MPDClient { } if mpdIdle.contains(.player) { self.fetchStatus() - self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state()) - self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song()) + self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state) + self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song) } if !mpdIdle.isEmpty { self.idle() diff --git a/Persephone/MPDClient/Models/Status.swift b/Persephone/MPDClient/Models/Status.swift index df07fd3..46d271f 100644 --- a/Persephone/MPDClient/Models/Status.swift +++ b/Persephone/MPDClient/Models/Status.swift @@ -9,9 +9,22 @@ import Foundation import mpdclient +extension RawRepresentable where Self: Equatable { + func isOneOf(_ options: Options) -> Bool where Options.Element == Self { + return options.contains(self) + } +} + extension MPDClient { class Status { - let mpdStatus: OpaquePointer + private let mpdStatus: OpaquePointer + + enum State: UInt { + case unknown = 0 + case stopped = 1 + case playing = 2 + case paused = 3 + } init(_ mpdStatus: OpaquePointer) { self.mpdStatus = mpdStatus @@ -21,12 +34,15 @@ extension MPDClient { mpd_status_free(mpdStatus) } - func state() -> mpd_state { - return mpd_status_get_state(mpdStatus) + var state: State { + let mpdState = mpd_status_get_state(mpdStatus) + + return State(rawValue: UInt(mpdState.rawValue))! } - func song() -> Int32 { - return mpd_status_get_song_pos(mpdStatus) + var song: Int { + return Int(mpd_status_get_song_pos(mpdStatus)) } + } } diff --git a/Persephone/MPDClient/Protocols/Delegate.swift b/Persephone/MPDClient/Protocols/Delegate.swift index 7ee5911..a9745c3 100644 --- a/Persephone/MPDClient/Protocols/Delegate.swift +++ b/Persephone/MPDClient/Protocols/Delegate.swift @@ -10,7 +10,7 @@ import Foundation import mpdclient protocol MPDClientDelegate { - func didUpdateState(mpdClient: MPDClient, state: mpd_state) + func didUpdateState(mpdClient: MPDClient, state: MPDClient.Status.State) func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song]) - func didUpdateQueuePos(mpdClient: MPDClient, song: Int32) + func didUpdateQueuePos(mpdClient: MPDClient, song: Int) }