mirror of
https://github.com/danbee/persephone
synced 2025-03-04 08:39:11 +00:00
Implement top level song menu
This commit is contained in:
parent
e05698e766
commit
cd094c3a64
@ -111,6 +111,7 @@
|
||||
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; };
|
||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
||||
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
|
||||
E4E7A6AD22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */; };
|
||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
|
||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
|
||||
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC9922075D370024217A /* MPDSong.swift */; };
|
||||
@ -314,6 +315,7 @@
|
||||
E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = "<group>"; };
|
||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
|
||||
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumDetailView+NSTableViewDelegate.swift"; sourceTree = "<group>"; };
|
||||
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
|
||||
E4E8CC912204F4B80024217A /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
|
||||
E4E8CC9922075D370024217A /* MPDSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDSong.swift; sourceTree = "<group>"; };
|
||||
@ -683,6 +685,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E43B67A822909793007DCF55 /* AlbumDetailView.swift */,
|
||||
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */,
|
||||
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
|
||||
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */,
|
||||
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */,
|
||||
@ -940,6 +943,7 @@
|
||||
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */,
|
||||
E440519E227BB0720090CD6F /* UIReducer.swift in Sources */,
|
||||
E43B67AA22909793007DCF55 /* AlbumDetailView.swift in Sources */,
|
||||
E4E7A6AD22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift in Sources */,
|
||||
E4FF7190227601B400D4C412 /* PreferencesReducer.swift in Sources */,
|
||||
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */,
|
||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
|
||||
|
||||
@ -16,6 +16,11 @@ class AppDelegate: NSObject,
|
||||
MediaKeyTapDelegate {
|
||||
var mediaKeyTap: MediaKeyTap?
|
||||
|
||||
@IBOutlet weak var mainWindowMenuItem: NSMenuItem!
|
||||
@IBOutlet weak var updateDatabaseMenuItem: NSMenuItem!
|
||||
@IBOutlet weak var playSelectedSongMenuItem: NSMenuItem!
|
||||
@IBOutlet weak var addSelectedSongToQueueMenuItem: NSMenuItem!
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
App.mpdServerController.connect()
|
||||
instantiateUserNotificationsController()
|
||||
@ -97,6 +102,11 @@ class AppDelegate: NSObject,
|
||||
}
|
||||
}
|
||||
|
||||
func setSongMenuItemsState(selectedSong: Song?) {
|
||||
playSelectedSongMenuItem.isEnabled = selectedSong != nil
|
||||
addSelectedSongToQueueMenuItem.isEnabled = selectedSong != nil
|
||||
}
|
||||
|
||||
func handle(mediaKey: MediaKey, event: KeyEvent) {
|
||||
switch mediaKey {
|
||||
case .playPause:
|
||||
@ -125,8 +135,20 @@ class AppDelegate: NSObject,
|
||||
App.store.dispatch(MPDPrevTrackAction())
|
||||
}
|
||||
|
||||
@IBOutlet weak var mainWindowMenuItem: NSMenuItem!
|
||||
@IBOutlet weak var updateDatabaseMenuItem: NSMenuItem!
|
||||
@IBAction func playSelectedSongAction(_ sender: NSMenuItem) {
|
||||
guard let song = App.store.state.uiState.selectedSong
|
||||
else { return }
|
||||
|
||||
let queueLength = App.store.state.queueState.queue.count
|
||||
App.store.dispatch(MPDAppendTrack(song: song.mpdSong))
|
||||
App.store.dispatch(MPDPlayTrack(queuePos: queueLength))
|
||||
}
|
||||
@IBAction func addSelectedSongToQueueAction(_ sender: NSMenuItem) {
|
||||
guard let song = App.store.state.uiState.selectedSong
|
||||
else { return }
|
||||
|
||||
App.store.dispatch(MPDAppendTrack(song: song.mpdSong))
|
||||
}
|
||||
}
|
||||
|
||||
extension AppDelegate: StoreSubscriber {
|
||||
@ -135,5 +157,6 @@ extension AppDelegate: StoreSubscriber {
|
||||
func newState(state: UIState) {
|
||||
updateDatabaseMenuItem.isEnabled = !state.databaseUpdating
|
||||
setMainWindowStateMenuItem(state: state.mainWindowState)
|
||||
setSongMenuItemsState(selectedSong: state.selectedSong)
|
||||
}
|
||||
}
|
||||
|
||||
102
Persephone/Controllers/AlbumDetailView+NSTableViewDelegate.swift
Normal file
102
Persephone/Controllers/AlbumDetailView+NSTableViewDelegate.swift
Normal file
@ -0,0 +1,102 @@
|
||||
//
|
||||
// AlbumDetailView+NSTableViewDelegate.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Daniel Barber on 2019/6/07.
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
extension AlbumDetailView: NSTableViewDelegate {
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
if let song = dataSource.albumSongs[row].song {
|
||||
switch tableColumn?.identifier.rawValue {
|
||||
case "trackNumberColumn":
|
||||
return cellForTrackNumber(tableView, with: song)
|
||||
case "trackTitleColumn":
|
||||
return cellForSongTitle(tableView, with: song)
|
||||
case "trackDurationColumn":
|
||||
return cellForSongDuration(tableView, with: song)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
} else if let disc = dataSource.albumSongs[row].disc {
|
||||
return cellForDiscNumber(tableView, with: disc)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, isGroupRow row: Int) -> Bool {
|
||||
return dataSource.albumSongs[row].disc != nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
||||
let view = AlbumDetailSongRowView()
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||||
return dataSource.albumSongs[row].disc == nil
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
guard let tableView = notification.object as? NSTableView
|
||||
else { return }
|
||||
|
||||
if tableView.selectedRow >= 0 {
|
||||
let song = dataSource.albumSongs[tableView.selectedRow].song
|
||||
|
||||
App.store.dispatch(SetSelectedSong(selectedSong: song))
|
||||
} else {
|
||||
App.store.dispatch(SetSelectedSong(selectedSong: nil))
|
||||
}
|
||||
}
|
||||
|
||||
func cellForDiscNumber(_ tableView: NSTableView, with disc: String) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .discNumber,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = "Disc \(disc)"
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
func cellForTrackNumber(_ tableView: NSTableView, with song: Song) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .trackNumber,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = "\(song.trackNumber)."
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
func cellForSongTitle(_ tableView: NSTableView, with song: Song) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .songTitle,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = song.title
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
func cellForSongDuration(_ tableView: NSTableView, with song: Song) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .songDuration,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.font = .timerFont
|
||||
cellView.textField?.stringValue = song.duration.formattedTime
|
||||
|
||||
return cellView
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,7 @@
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import AppKit
|
||||
|
||||
class AlbumDetailView: NSViewController {
|
||||
var album: Album?
|
||||
@ -60,6 +60,8 @@ class AlbumDetailView: NSViewController {
|
||||
albumTitle.stringValue = ""
|
||||
albumArtist.stringValue = ""
|
||||
albumCoverView.image = .defaultCoverArt
|
||||
|
||||
App.store.dispatch(SetSelectedSong(selectedSong: nil))
|
||||
}
|
||||
|
||||
@IBAction func playAlbum(_ sender: NSButton) {
|
||||
@ -140,83 +142,3 @@ class AlbumDetailView: NSViewController {
|
||||
self.album = album
|
||||
}
|
||||
}
|
||||
|
||||
extension AlbumDetailView: NSTableViewDelegate {
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
if let song = dataSource.albumSongs[row].song {
|
||||
switch tableColumn?.identifier.rawValue {
|
||||
case "trackNumberColumn":
|
||||
return cellForTrackNumber(tableView, with: song)
|
||||
case "trackTitleColumn":
|
||||
return cellForSongTitle(tableView, with: song)
|
||||
case "trackDurationColumn":
|
||||
return cellForSongDuration(tableView, with: song)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
} else if let disc = dataSource.albumSongs[row].disc {
|
||||
return cellForDiscNumber(tableView, with: disc)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, isGroupRow row: Int) -> Bool {
|
||||
return dataSource.albumSongs[row].disc != nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
||||
let view = AlbumDetailSongRowView()
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||||
return dataSource.albumSongs[row].disc == nil
|
||||
}
|
||||
|
||||
func cellForDiscNumber(_ tableView: NSTableView, with disc: String) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .discNumber,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = "Disc \(disc)"
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
func cellForTrackNumber(_ tableView: NSTableView, with song: Song) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .trackNumber,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = "\(song.trackNumber)."
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
func cellForSongTitle(_ tableView: NSTableView, with song: Song) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .songTitle,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = song.title
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
func cellForSongDuration(_ tableView: NSTableView, with song: Song) -> NSView {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .songDuration,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.font = .timerFont
|
||||
cellView.textField?.stringValue = song.duration.formattedTime
|
||||
|
||||
return cellView
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +75,24 @@
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Song" id="elk-xW-VXb">
|
||||
<menu key="submenu" title="Song" autoenablesItems="NO" id="RuT-kk-xTu">
|
||||
<items>
|
||||
<menuItem title="Play selected song" enabled="NO" id="dyT-9E-DRY">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="playSelectedSongAction:" target="Voe-Tx-rLC" id="jIo-ux-Mhr"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Add selected song to queue" enabled="NO" id="JFH-jT-sBp">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="addSelectedSongToQueueAction:" target="Voe-Tx-rLC" id="9j9-Xd-g0D"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
@ -132,7 +150,9 @@
|
||||
</application>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Persephone" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="addSelectedSongToQueueMenuItem" destination="JFH-jT-sBp" id="9dy-sJ-XYS"/>
|
||||
<outlet property="mainWindowMenuItem" destination="1Sq-L7-znT" id="dC6-yY-6Ss"/>
|
||||
<outlet property="playSelectedSongMenuItem" destination="dyT-9E-DRY" id="UY2-SN-YMF"/>
|
||||
<outlet property="updateDatabaseMenuItem" destination="EJg-93-1F6" id="gMf-SQ-lyI"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
|
||||
@ -17,3 +17,7 @@ struct MainWindowDidMinimizeAction: Action {}
|
||||
struct DatabaseUpdateStartedAction: Action {}
|
||||
|
||||
struct DatabaseUpdateFinishedAction: Action {}
|
||||
|
||||
struct SetSelectedSong: Action {
|
||||
let selectedSong: Song?
|
||||
}
|
||||
|
||||
@ -27,6 +27,9 @@ func uiReducer(action: Action, state: UIState?) -> UIState {
|
||||
case is DatabaseUpdateFinishedAction:
|
||||
state.databaseUpdating = false
|
||||
|
||||
case let action as SetSelectedSong:
|
||||
state.selectedSong = action.selectedSong
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -18,4 +18,6 @@ struct UIState: StateType {
|
||||
var mainWindowState: MainWindowState = .closed
|
||||
|
||||
var databaseUpdating: Bool = false
|
||||
|
||||
var selectedSong: Song?
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user