diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 17d8337..964a05b 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C1220E134F0006D9BE /* AlbumViewController.swift */; }; E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */; }; E40FE71B221B904300A4223F /* NSEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40FE71A221B904300A4223F /* NSEvent.swift */; }; - E4120D6C22AD8139004CB1F8 /* QueueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4120D6B22AD8139004CB1F8 /* QueueView.swift */; }; E419E2872249B96600216A8C /* Song.swift in Sources */ = {isa = PBXBuildFile; fileRef = E419E2862249B96600216A8C /* Song.swift */; }; E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; }; E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FC223BF87300173814 /* MPDClient+Connection.swift */; }; @@ -30,7 +29,7 @@ E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5308223C020400173814 /* MPDClient+Command.swift */; }; E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530A223C033700173814 /* MPDClient+Album.swift */; }; E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; }; - E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongTitleView.swift */; }; + E4235640228623D2001216D6 /* QueueSongInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongInfoView.swift */; }; E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42410B52241B956005ED6DF /* MPDClient+Database.swift */; }; E42A4D4F22E20D7D001C6CAD /* MPDTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */; }; E42A4D5122E2167E001C6CAD /* MPDClient+Songs.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */; }; @@ -99,6 +98,7 @@ E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBD2275EDAA0075461B /* PlayerActions.swift */; }; E4B11BC02275EE150075461B /* QueueActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBF2275EE150075461B /* QueueActions.swift */; }; E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; }; + E4B3DF6523D66A4400728F6B /* QueueSongCoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */; }; E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */; }; E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BBD2F223357C0700702C16 /* ArtistListState.swift */; }; E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; }; @@ -183,7 +183,6 @@ E408D3C1220E134F0006D9BE /* AlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = ""; }; E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumViewItem.xib; sourceTree = ""; }; E40FE71A221B904300A4223F /* NSEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEvent.swift; sourceTree = ""; }; - E4120D6B22AD8139004CB1F8 /* QueueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueView.swift; sourceTree = ""; }; E419E2862249B96600216A8C /* Song.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Song.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 = ""; }; @@ -232,7 +231,7 @@ E41E5308223C020400173814 /* MPDClient+Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Command.swift"; sourceTree = ""; }; E41E530A223C033700173814 /* MPDClient+Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Album.swift"; sourceTree = ""; }; E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = ""; }; - E423563F228623D2001216D6 /* QueueSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongTitleView.swift; sourceTree = ""; }; + E423563F228623D2001216D6 /* QueueSongInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongInfoView.swift; sourceTree = ""; }; E42410B52241B956005ED6DF /* MPDClient+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Database.swift"; sourceTree = ""; }; E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDTag.swift; sourceTree = ""; }; E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Songs.swift"; sourceTree = ""; }; @@ -293,6 +292,7 @@ E4B11BBD2275EDAA0075461B /* PlayerActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerActions.swift; sourceTree = ""; }; E4B11BBF2275EE150075461B /* QueueActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueActions.swift; sourceTree = ""; }; E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = ""; }; + E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongCoverView.swift; sourceTree = ""; }; E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerDelegate.swift; sourceTree = ""; }; E4BBD2F223357C0700702C16 /* ArtistListState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistListState.swift; sourceTree = ""; }; E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; @@ -537,8 +537,8 @@ children = ( E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */, E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */, - E423563F228623D2001216D6 /* QueueSongTitleView.swift */, - E4120D6B22AD8139004CB1F8 /* QueueView.swift */, + E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */, + E423563F228623D2001216D6 /* QueueSongInfoView.swift */, E4E8CC912204F4B80024217A /* QueueViewController.swift */, E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */, ); @@ -935,7 +935,7 @@ E442CCCD2347E73C00004E0C /* Artist.swift in Sources */, E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */, E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */, - E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */, + E4235640228623D2001216D6 /* QueueSongInfoView.swift in Sources */, E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */, E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */, E4B11BC02275EE150075461B /* QueueActions.swift in Sources */, @@ -971,6 +971,7 @@ E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */, E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */, E439109822640213002982E9 /* SongNotifierService.swift in Sources */, + E4B3DF6523D66A4400728F6B /* QueueSongCoverView.swift in Sources */, E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */, E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */, E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */, @@ -985,7 +986,6 @@ E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */, E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */, E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */, - E4120D6C22AD8139004CB1F8 /* QueueView.swift in Sources */, E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */, E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */, E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */, diff --git a/Persephone/Assets.xcassets/queuePauseButton.imageset/Contents.json b/Persephone/Assets.xcassets/queuePauseButton.imageset/Contents.json new file mode 100644 index 0000000..0a573ab --- /dev/null +++ b/Persephone/Assets.xcassets/queuePauseButton.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "pauseButton.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "pauseButtonWhite.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "pauseButton@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "pauseButtonWhite@2x.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + }, + { + "idiom" : "universal", + "scale" : "3x", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButton.png b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButton.png new file mode 100644 index 0000000..54fcb13 Binary files /dev/null and b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButton.png differ diff --git a/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButton@2x.png b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButton@2x.png new file mode 100644 index 0000000..a4c3320 Binary files /dev/null and b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButton@2x.png differ diff --git a/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButtonWhite.png b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButtonWhite.png new file mode 100644 index 0000000..e33ae5e Binary files /dev/null and b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButtonWhite.png differ diff --git a/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButtonWhite@2x.png b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButtonWhite@2x.png new file mode 100644 index 0000000..2d11367 Binary files /dev/null and b/Persephone/Assets.xcassets/queuePauseButton.imageset/pauseButtonWhite@2x.png differ diff --git a/Persephone/Assets.xcassets/queuePlayButton.imageset/Contents.json b/Persephone/Assets.xcassets/queuePlayButton.imageset/Contents.json new file mode 100644 index 0000000..0ca38da --- /dev/null +++ b/Persephone/Assets.xcassets/queuePlayButton.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "playButton.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "playButtonWhite.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "playButton@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "playButtonWhite@2x.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + }, + { + "idiom" : "universal", + "scale" : "3x", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Persephone/Assets.xcassets/queuePlayButton.imageset/playButton.png b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButton.png new file mode 100644 index 0000000..95e8a43 Binary files /dev/null and b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButton.png differ diff --git a/Persephone/Assets.xcassets/queuePlayButton.imageset/playButton@2x.png b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButton@2x.png new file mode 100644 index 0000000..08635c5 Binary files /dev/null and b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButton@2x.png differ diff --git a/Persephone/Assets.xcassets/queuePlayButton.imageset/playButtonWhite.png b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButtonWhite.png new file mode 100644 index 0000000..ebd4466 Binary files /dev/null and b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButtonWhite.png differ diff --git a/Persephone/Assets.xcassets/queuePlayButton.imageset/playButtonWhite@2x.png b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButtonWhite@2x.png new file mode 100644 index 0000000..d8d59a1 Binary files /dev/null and b/Persephone/Assets.xcassets/queuePlayButton.imageset/playButtonWhite@2x.png differ diff --git a/Persephone/Components/Browser/Album Detail/AlbumDetailView.swift b/Persephone/Components/Browser/Album Detail/AlbumDetailView.swift index 883999b..4e16196 100644 --- a/Persephone/Components/Browser/Album Detail/AlbumDetailView.swift +++ b/Persephone/Components/Browser/Album Detail/AlbumDetailView.swift @@ -145,6 +145,18 @@ class AlbumDetailView: NSViewController { .scaleFactor(2), ] ) + + cacheSmallCover(provider: provider) + } + + func cacheSmallCover(provider: ImageDataProvider) { + _ = KingfisherManager.shared.retrieveImage( + with: .provider(provider), + options: [ + .processor(DownsamplingImageProcessor(size: .queueSongCoverSize)), + .scaleFactor(2), + ] + ) { result in } } func setAppearance() { diff --git a/Persephone/Components/Browser/Album Detail/AlbumTracksDataSource.swift b/Persephone/Components/Browser/Album Detail/AlbumTracksDataSource.swift index d87570f..55360be 100644 --- a/Persephone/Components/Browser/Album Detail/AlbumTracksDataSource.swift +++ b/Persephone/Components/Browser/Album Detail/AlbumTracksDataSource.swift @@ -49,7 +49,8 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource { draggedSong: DraggedSong( type: .albumSongItem(song.mpdSong.uriString), title: song.title, - artist: song.artist + artist: song.artist, + cover: song.album.coverArtFilePath ), ofType: .songPasteboardType ) @@ -68,12 +69,20 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource { ) { draggingItem, index, stop in guard let item = draggingItem.item as? NSPasteboardItem, let draggedSong = item.draggedSong(forType: .songPasteboardType), - case let (title?, artist?) = (draggedSong.title, draggedSong.artist) + case let (title?, artist?, cover?) = ( + draggedSong.title, + draggedSong.artist, + draggedSong.cover + ) else { return } draggingItem.imageComponentsProvider = { let component = NSDraggingImageComponent(key: NSDraggingItem.ImageComponentKey.icon) - let draggedSongView = DraggedSongView(title: title, artist: artist) + let draggedSongView = DraggedSongView( + title: title, + artist: artist, + cover: cover + ) component.contents = draggedSongView.view.image() component.frame = NSRect(origin: CGPoint(), size: draggedSongView.view.image().size) diff --git a/Persephone/Components/Queue/QueueDataSource.swift b/Persephone/Components/Queue/QueueDataSource.swift index 2c1b7ee..c58c819 100644 --- a/Persephone/Components/Queue/QueueDataSource.swift +++ b/Persephone/Components/Queue/QueueDataSource.swift @@ -15,16 +15,16 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { func setQueueIcon(_ state: QueueState) { switch state.state { case .playing?: - queueIcon = .playIcon + queueIcon = .queuePlayIcon case .paused?: - queueIcon = .pauseIcon + queueIcon = .queuePauseIcon default: queueIcon = nil } } func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { - return queue.count + 1 + return queue.count } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { @@ -32,11 +32,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { } func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { - if index > 0 { - return queue[index - 1] - } else { - return "" - } + return queue[index] } func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? { @@ -47,14 +43,15 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { draggedSong: DraggedSong( type: .queueItem(queueItem.queuePos), title: queueItem.song.title, - artist: queueItem.song.artist + artist: queueItem.song.artist, + cover: queueItem.song.album.coverArtFilePath ), ofType: .songPasteboardType ) } func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { - var newQueuePos = index - 1 + var newQueuePos = index guard newQueuePos >= 0, let draggingTypes = info.draggingPasteboard.types @@ -84,7 +81,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { } func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool { - var newQueuePos = index - 1 + var newQueuePos = index guard let draggingTypes = info.draggingPasteboard.types else { return false } @@ -131,12 +128,20 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { guard let item = draggingItem.item as? NSPasteboardItem, let data = item.data(forType: .songPasteboardType), let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data), - case let (title?, artist?) = (draggedSong.title, draggedSong.artist) + case let (title?, artist?, cover?) = ( + draggedSong.title, + draggedSong.artist, + draggedSong.cover + ) else { return } draggingItem.imageComponentsProvider = { let component = NSDraggingImageComponent(key: NSDraggingItem.ImageComponentKey.icon) - let draggedSongView = DraggedSongView(title: title, artist: artist) + let draggedSongView = DraggedSongView( + title: title, + artist: artist, + cover: cover + ) let view = draggedSongView.view let image = view.image() diff --git a/Persephone/Components/Queue/QueueSongCoverView.swift b/Persephone/Components/Queue/QueueSongCoverView.swift new file mode 100644 index 0000000..1392b06 --- /dev/null +++ b/Persephone/Components/Queue/QueueSongCoverView.swift @@ -0,0 +1,86 @@ +// +// QueueSongCoverView.swift +// Persephone +// +// Created by Daniel Barber on 1/20/20. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import AppKit +import Kingfisher + +class QueueSongCoverView: NSTableCellView { + @IBOutlet var queueSongCover: NSImageView! + @IBOutlet var queueSongButton: NSButton! + + let playingFilters = [ + CIFilter(name: "CIExposureAdjust", parameters: ["inputEV": -2])! + ] + + var isPlaying: Bool = false + + override func viewDidMoveToSuperview() { + super.viewDidMoveToSuperview() + + queueSongCover.wantsLayer = true + queueSongCover.layer?.backgroundColor = CGColor.black + queueSongCover.layer?.cornerRadius = 2 + queueSongCover.layer?.borderWidth = 1 + queueSongCover.layer?.masksToBounds = true + queueSongButton.wantsLayer = true + setAppearance() + + if isPlaying { + queueSongCover.layer?.filters = playingFilters + } else { + queueSongCover.layer?.filters = nil + } + } + + override func prepareForReuse() { + super.prepareForReuse() + + queueSongCover.image = .defaultCoverArt + } + + func setAppearance() { + guard let viewLayer = queueSongCover.layer + else { return } + + if #available(OSX 10.14, *) { + let darkMode = NSApp.effectiveAppearance.bestMatch(from: + [.darkAqua, .aqua]) == .darkAqua + + viewLayer.borderColor = darkMode ? .albumBorderColorDark : .albumBorderColorLight + } else { + viewLayer.borderColor = .albumBorderColorLight + } + } + + func setSong(_ queueItem: QueueItem, queueIcon: NSImage?) { + guard let imagePath = queueItem.song.album.coverArtFilePath + else { return } + + isPlaying = queueItem.isPlaying + + let imageURL = URL(fileURLWithPath: imagePath) + let provider = LocalFileImageDataProvider(fileURL: imageURL) + + queueSongCover.kf.setImage( + with: .provider(provider), + placeholder: NSImage.defaultCoverArt, + options: [ + .processor(DownsamplingImageProcessor(size: .queueSongCoverSize)), + .scaleFactor(2), + ] + ) + + if isPlaying && queueIcon != nil { + queueSongCover.layer?.filters = playingFilters + queueSongButton.image = queueIcon + } else { + queueSongCover.layer?.filters = nil + queueSongButton.image = nil + } + } +} diff --git a/Persephone/Components/Queue/QueueSongTitleView.swift b/Persephone/Components/Queue/QueueSongInfoView.swift similarity index 50% rename from Persephone/Components/Queue/QueueSongTitleView.swift rename to Persephone/Components/Queue/QueueSongInfoView.swift index 011bfbd..7f7f750 100644 --- a/Persephone/Components/Queue/QueueSongTitleView.swift +++ b/Persephone/Components/Queue/QueueSongInfoView.swift @@ -8,23 +8,18 @@ import AppKit -class QueueSongTitleView: NSTableCellView { - @IBOutlet var queuePlayerStateImage: NSImageView! - @IBOutlet var queuePosition: NSTextField! +class QueueSongInfoView: NSTableCellView { @IBOutlet var queueSongTitle: NSTextField! - - func setQueueSong(_ queueItem: QueueItem, queueIcon: NSImage?) { - queuePosition?.font = .timerFont + @IBOutlet var queueSongArtist: NSTextField! + + func setSong(_ queueItem: QueueItem, queueIcon: NSImage?) { queueSongTitle?.stringValue = queueItem.song.title + queueSongArtist?.stringValue = queueItem.song.artist if queueItem.isPlaying && queueIcon != nil { queueSongTitle?.font = .systemFontBold - queuePlayerStateImage?.image = queueIcon - queuePosition?.stringValue = "" } else { queueSongTitle?.font = .systemFontRegular - queuePlayerStateImage?.image = nil - queuePosition?.stringValue = "\(queueItem.queuePos + 1)." } } } diff --git a/Persephone/Components/Queue/QueueView.swift b/Persephone/Components/Queue/QueueView.swift deleted file mode 100644 index 0a4006c..0000000 --- a/Persephone/Components/Queue/QueueView.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// QueueView.swift -// Persephone -// -// Created by Daniel Barber on 2019/6/09. -// Copyright © 2019 Dan Barber. All rights reserved. -// - -import AppKit - -class QueueView: NSOutlineView { - override func menu(for event: NSEvent) -> NSMenu? { - let point = convert(event.locationInWindow, from: nil) - - let currentRow = row(at: point) - - if currentRow > 0 { - return super.menu(for: event) - } else { - return nil - } - } -} diff --git a/Persephone/Components/Queue/QueueViewController+NSOutlineViewDelegate.swift b/Persephone/Components/Queue/QueueViewController+NSOutlineViewDelegate.swift index b56e387..c2f5025 100644 --- a/Persephone/Components/Queue/QueueViewController+NSOutlineViewDelegate.swift +++ b/Persephone/Components/Queue/QueueViewController+NSOutlineViewDelegate.swift @@ -9,78 +9,59 @@ import AppKit extension QueueViewController: NSOutlineViewDelegate { - func outlineView( - _ outlineView: NSOutlineView, - selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet - ) -> IndexSet { - if proposedSelectionIndexes.contains(0) { - return IndexSet() - } else { - return proposedSelectionIndexes - } - } - func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { if let queueItem = item as? QueueItem { switch tableColumn?.identifier.rawValue { - case "songTitleColumn": - return cellForSongTitle(outlineView, with: queueItem) - case "songArtistColumn": - return cellForSongArtist(outlineView, with: queueItem) + case "songCoverColumn": + return cellForSongCover(outlineView, with: queueItem) + case "songInfoColumn": + return cellForSongInfo(outlineView, with: queueItem) + case "songDurationColumn": + return cellForSongDuration(outlineView, with: queueItem) default: return nil } - } else if tableColumn?.identifier.rawValue == "songTitleColumn" { - return cellForQueueHeading(outlineView) } else { return nil } } func outlineViewSelectionDidChange(_ notification: Notification) { - if queueView.selectedRow >= 1 { - let queueItem = dataSource.queue[queueView.selectedRow - 1] + let queueItem = dataSource.queue[queueView.selectedRow] - App.store.dispatch(SetSelectedQueueItem(selectedQueueItem: queueItem)) - } else { - App.store.dispatch(SetSelectedQueueItem(selectedQueueItem: nil)) - } + App.store.dispatch(SetSelectedQueueItem(selectedQueueItem: queueItem)) } - - func cellForSongTitle(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView { - let cellView = outlineView.makeView( - withIdentifier: .queueSongTitle, + + func cellForSongCover(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView { + let cellView = outlineView.makeView( + withIdentifier: .queueSongCover, owner: self - ) as! QueueSongTitleView - - cellView.setQueueSong(queueItem, queueIcon: dataSource.queueIcon) - + ) as! QueueSongCoverView + + cellView.setSong(queueItem, queueIcon: dataSource.queueIcon) + return cellView } - func cellForSongArtist(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView { + func cellForSongInfo(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView { let cellView = outlineView.makeView( - withIdentifier: .queueSongArtist, + withIdentifier: .queueSongInfo, owner: self - ) as! NSTableCellView + ) as! QueueSongInfoView - cellView.textField?.stringValue = queueItem.song.artist - if queueItem.isPlaying { - cellView.textField?.font = .systemFontBold - } else { - cellView.textField?.font = .systemFontRegular - } + cellView.setSong(queueItem, queueIcon: dataSource.queueIcon) return cellView } - - func cellForQueueHeading(_ outlineView: NSOutlineView) -> NSView { + + func cellForSongDuration(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView { let cellView = outlineView.makeView( - withIdentifier: .queueHeading, + withIdentifier: .queueSongDuration, owner: self - ) as! NSTableCellView - - cellView.textField?.stringValue = "QUEUE" + ) as! NSTableCellView + + cellView.textField?.font = .timerFont + cellView.textField?.stringValue = queueItem.song.duration.formattedTime return cellView } diff --git a/Persephone/Components/Queue/QueueViewController.swift b/Persephone/Components/Queue/QueueViewController.swift index 0f4df03..e34c73a 100644 --- a/Persephone/Components/Queue/QueueViewController.swift +++ b/Persephone/Components/Queue/QueueViewController.swift @@ -40,11 +40,9 @@ class QueueViewController: NSViewController { case NSEvent.keyCodeSpace: nextResponder?.keyDown(with: event) case NSEvent.keyCodeBS: - let queuePos = queueView.selectedRow - 1 + let queuePos = queueView.selectedRow - if queuePos >= 0 { - App.mpdClient.removeSong(at: queuePos) - } + App.mpdClient.removeSong(at: queuePos) default: super.keyDown(with: event) } @@ -62,26 +60,20 @@ class QueueViewController: NSViewController { } @IBAction func playTrack(_ sender: Any) { - let queuePos = queueView.selectedRow - 1 + let queuePos = queueView.selectedRow - if queuePos >= 0 { - App.mpdClient.playTrack(at: queuePos) - } + App.mpdClient.playTrack(at: queuePos) } @IBAction func playSongMenuAction(_ sender: NSMenuItem) { - let queuePos = queueView.clickedRow - 1 + let queuePos = queueView.clickedRow - if queuePos >= 0 { - App.mpdClient.playTrack(at: queuePos) - } + App.mpdClient.playTrack(at: queuePos) } @IBAction func removeSongMenuAction(_ sender: NSMenuItem) { - let queuePos = queueView.clickedRow - 1 + let queuePos = queueView.clickedRow - if queuePos >= 0 { - App.mpdClient.removeSong(at: queuePos) - } + App.mpdClient.removeSong(at: queuePos) } } diff --git a/Persephone/Components/Shared/DraggedSongView.swift b/Persephone/Components/Shared/DraggedSongView.swift index 7c9f1c0..49bcee6 100644 --- a/Persephone/Components/Shared/DraggedSongView.swift +++ b/Persephone/Components/Shared/DraggedSongView.swift @@ -7,17 +7,22 @@ // import Cocoa +import Kingfisher class DraggedSongView: NSViewController { @IBOutlet var titleLabel: NSTextField! @IBOutlet var artistLabel: NSTextField! - + @IBOutlet var coverImage: NSImageView! + private let songTitle: String private let songArtist: String + private let songCover: String? - init(title: String, artist: String) { + init(title: String, artist: String, cover: String? = nil) { songTitle = title songArtist = artist + songCover = cover + super.init(nibName: nil, bundle: nil) } @@ -27,7 +32,44 @@ class DraggedSongView: NSViewController { override func viewDidLoad() { super.viewDidLoad() + + coverImage.wantsLayer = true + coverImage.layer?.backgroundColor = CGColor.black + coverImage.layer?.cornerRadius = 2 + coverImage.layer?.borderWidth = 1 + coverImage.layer?.masksToBounds = true + + setAppearance() + titleLabel.stringValue = songTitle artistLabel.stringValue = songArtist + setCoverArt() + } + + func setAppearance() { + if #available(OSX 10.14, *) { + let darkMode = NSApp.effectiveAppearance.bestMatch(from: + [.darkAqua, .aqua]) == .darkAqua + + coverImage.layer?.borderColor = darkMode ? .albumBorderColorDark : .albumBorderColorLight + } else { + coverImage.layer?.borderColor = .albumBorderColorLight + } + } + + func setCoverArt() { + guard let imagePath = songCover else { return } + + let imageURL = URL(fileURLWithPath: imagePath) + let provider = LocalFileImageDataProvider(fileURL: imageURL) + + coverImage.kf.setImage( + with: .provider(provider), + placeholder: NSImage.defaultCoverArt, + options: [ + .processor(DownsamplingImageProcessor(size: .queueSongCoverSize)), + .scaleFactor(2), + ] + ) } } diff --git a/Persephone/Components/Shared/DraggedSongView.xib b/Persephone/Components/Shared/DraggedSongView.xib index e1837c4..d792d18 100644 --- a/Persephone/Components/Shared/DraggedSongView.xib +++ b/Persephone/Components/Shared/DraggedSongView.xib @@ -1,14 +1,15 @@ - + - + + @@ -16,53 +17,64 @@ - + - + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - @@ -74,7 +86,7 @@ - + @@ -83,10 +95,10 @@ - + - + diff --git a/Persephone/Components/Shared/Extensions/NSImage.swift b/Persephone/Components/Shared/Extensions/NSImage.swift index a0eafea..6f4ca3f 100644 --- a/Persephone/Components/Shared/Extensions/NSImage.swift +++ b/Persephone/Components/Shared/Extensions/NSImage.swift @@ -11,7 +11,9 @@ import AppKit extension NSImage { static let playIcon = NSImage(named: "playButton") static let pauseIcon = NSImage(named: "pauseButton") - + static let queuePlayIcon = NSImage(named: "queuePlayButton") + static let queuePauseIcon = NSImage(named: "queuePauseButton") + static let defaultCoverArt = NSImage(named: "defaultCoverArt") func toFitBox(size: NSSize) -> NSImage { diff --git a/Persephone/Components/Shared/Extensions/NSSize.swift b/Persephone/Components/Shared/Extensions/NSSize.swift index 8ee5f02..4c7efab 100644 --- a/Persephone/Components/Shared/Extensions/NSSize.swift +++ b/Persephone/Components/Shared/Extensions/NSSize.swift @@ -9,6 +9,7 @@ import AppKit extension NSSize { + static let queueSongCoverSize = NSSize(width: 32, height: 32) static let albumListCoverSize = NSSize(width: 180, height: 180) static let albumDetailCoverSize = NSSize(width: 500, height: 500) static let currentlyPlayingCoverSize = albumDetailCoverSize diff --git a/Persephone/Components/Shared/Extensions/NSUserInterfaceItemIdentifier.swift b/Persephone/Components/Shared/Extensions/NSUserInterfaceItemIdentifier.swift index eda347d..45370dd 100644 --- a/Persephone/Components/Shared/Extensions/NSUserInterfaceItemIdentifier.swift +++ b/Persephone/Components/Shared/Extensions/NSUserInterfaceItemIdentifier.swift @@ -9,15 +9,13 @@ import AppKit extension NSUserInterfaceItemIdentifier { - static let queueSongTitleColumn = NSUserInterfaceItemIdentifier("songTitleColumn") - static let queueSongArtistColumn = NSUserInterfaceItemIdentifier("songArtistColumn") + static let queueSongInfoColumn = NSUserInterfaceItemIdentifier("songInfoColumn") - static let queueHeading = NSUserInterfaceItemIdentifier("queueHeadingCell") - static let queueSongArtist = NSUserInterfaceItemIdentifier("songArtistCell") - static let queueSongTitle = NSUserInterfaceItemIdentifier("songTitleCell") + static let queueSongCover = NSUserInterfaceItemIdentifier("songCoverCell") + static let queueSongInfo = NSUserInterfaceItemIdentifier("songInfoCell") + static let queueSongDuration = NSUserInterfaceItemIdentifier("songDurationCell") static let albumViewItem = NSUserInterfaceItemIdentifier("AlbumViewItem") - static let artistViewItem = NSUserInterfaceItemIdentifier("ArtistViewItem") static let discNumber = NSUserInterfaceItemIdentifier("discNumberCell") static let trackNumber = NSUserInterfaceItemIdentifier("trackNumberCell") diff --git a/Persephone/Components/Window/Base.lproj/Main.storyboard b/Persephone/Components/Window/Base.lproj/Main.storyboard index c1f8f16..c894832 100644 --- a/Persephone/Components/Window/Base.lproj/Main.storyboard +++ b/Persephone/Components/Window/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -646,27 +646,27 @@ - - + + - - - + + + - + - - + + - - - + + + @@ -677,107 +677,131 @@ - - + + - - + + - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -797,7 +821,7 @@ - + - + @@ -851,7 +875,7 @@ - + @@ -920,6 +944,7 @@ + diff --git a/Persephone/Models/DraggedSong.swift b/Persephone/Models/DraggedSong.swift index 251d3b5..544fec9 100644 --- a/Persephone/Models/DraggedSong.swift +++ b/Persephone/Models/DraggedSong.swift @@ -10,4 +10,5 @@ struct DraggedSong: Codable { var type: DraggedSongType var title: String? var artist: String? + var cover: String? } diff --git a/Resources/export/pauseButtonWhite.pdf b/Resources/export/pauseButtonWhite.pdf new file mode 100644 index 0000000..64290b2 Binary files /dev/null and b/Resources/export/pauseButtonWhite.pdf differ diff --git a/Resources/export/pauseButtonWhite.png b/Resources/export/pauseButtonWhite.png new file mode 100644 index 0000000..e33ae5e Binary files /dev/null and b/Resources/export/pauseButtonWhite.png differ diff --git a/Resources/export/pauseButtonWhite@2x.png b/Resources/export/pauseButtonWhite@2x.png new file mode 100644 index 0000000..2d11367 Binary files /dev/null and b/Resources/export/pauseButtonWhite@2x.png differ diff --git a/Resources/export/playButtonWhite.pdf b/Resources/export/playButtonWhite.pdf new file mode 100644 index 0000000..8c94c34 Binary files /dev/null and b/Resources/export/playButtonWhite.pdf differ diff --git a/Resources/export/playButtonWhite.png b/Resources/export/playButtonWhite.png new file mode 100644 index 0000000..ebd4466 Binary files /dev/null and b/Resources/export/playButtonWhite.png differ diff --git a/Resources/export/playButtonWhite@2x.png b/Resources/export/playButtonWhite@2x.png new file mode 100644 index 0000000..d8d59a1 Binary files /dev/null and b/Resources/export/playButtonWhite@2x.png differ diff --git a/Resources/icons.sketch b/Resources/icons.sketch index 0a3f564..a7732b4 100644 Binary files a/Resources/icons.sketch and b/Resources/icons.sketch differ