1
1
mirror of https://github.com/danbee/persephone synced 2025-03-04 08:39:11 +00:00

Compare commits

...

23 Commits

Author SHA1 Message Date
9bedbec407
Calling init() is implied 2019-02-16 18:56:13 -05:00
cc7d8d3c88
Clean up after rebase 2019-02-16 18:56:13 -05:00
3e735893a2
Double clicking the header no longer plays a track 2019-02-16 18:56:12 -05:00
e6e020d23c
Better window size limits 2019-02-16 18:56:12 -05:00
d4164dfa72
Double clicking on a track in the queue plays it 2019-02-16 18:56:12 -05:00
a30c6ffc09
Upgrade to Swift 4.2 2019-02-16 18:56:12 -05:00
ffedba5c84
We should only be able to select songs 2019-02-16 18:56:12 -05:00
ba51e99e41
Improve display of blank albums
* Add dark and light blank album image variants
* Use CALayer to draw rounded corners and a tinted border on each album
  cover
2019-02-16 18:56:12 -05:00
33baaf3a64
Auto set width of albums and restrict queue view width 2019-02-16 18:56:11 -05:00
9216cfc25d
Add album view 2019-02-16 18:56:11 -05:00
a441310966
Better AppIcon naming 2019-02-16 18:55:54 -05:00
f0e815962a
These got lost somewhere 2019-02-16 18:45:36 -05:00
eb32f8cdf6
Address PR feedback 2019-02-16 18:42:18 -05:00
2f36891053
Remove debugging 2019-02-13 22:20:43 -05:00
1181198999
Remove some unused code 2019-02-13 22:20:42 -05:00
b8ed0b1ca6
Change colour of borders depending on dark/light mode 2019-02-13 22:20:42 -05:00
d2151558ce
Update screenshot 2019-02-13 22:20:42 -05:00
eeb776199b
Improve display of blank albums
* Add dark and light blank album image variants
* Use CALayer to draw rounded corners and a tinted border on each album
  cover
2019-02-13 22:20:42 -05:00
891d08cfff
Auto set width of albums and restrict queue view width 2019-02-13 22:20:26 -05:00
7b921021e8
Add up to date screenshot 2019-02-13 22:20:26 -05:00
7714b2609b
Refactor a bunch of things 2019-02-13 22:20:25 -05:00
71805dde15
Truncate long names with an ellipsis 2019-02-13 22:20:25 -05:00
447432120a
Add album view 2019-02-13 22:20:25 -05:00
56 changed files with 473 additions and 61 deletions

View File

@ -15,15 +15,21 @@
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3B5220DD8970006D9BE /* Notification.swift */; };
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */; };
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */; };
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C1220E134F0006D9BE /* AlbumViewController.swift */; };
E408D3CA220E341D0006D9BE /* AlbumItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C8220E341D0006D9BE /* AlbumItem.swift */; };
E408D3CB220E341D0006D9BE /* AlbumItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = E408D3C9220E341D0006D9BE /* AlbumItem.xib */; };
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 */; };
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E465049921E94DF500A70F4C /* WindowController.swift */; };
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
E4A642DA22090CBE00067D21 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* Status.swift */; };
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC932206097F0024217A /* NotificationsController.swift */; };
E4E8CC9A22075D370024217A /* Song.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC9922075D370024217A /* Song.swift */; };
E4EB2379220F10B8008C70C0 /* Pair.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB2378220F10B8008C70C0 /* Pair.swift */; };
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB237A220F7CF1008C70C0 /* Album.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -73,6 +79,9 @@
E408D3B5220DD8970006D9BE /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSUserInterfaceItemIdentifier.swift; sourceTree = "<group>"; };
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentable.swift; sourceTree = "<group>"; };
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = "<group>"; };
E408D3C8220E341D0006D9BE /* AlbumItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumItem.swift; sourceTree = "<group>"; };
E408D3C9220E341D0006D9BE /* AlbumItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumItem.xib; sourceTree = "<group>"; };
E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpdclient.2.dylib; path = libmpdclient/output/libmpdclient.2.dylib; sourceTree = "<group>"; };
E41B22C421FB715A00D544F6 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
E41B22C521FB932700D544F6 /* MPDClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDClient.swift; sourceTree = "<group>"; };
@ -112,11 +121,14 @@
E41B22EA21FB966C00D544F6 /* queue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = queue.h; sourceTree = "<group>"; };
E41B22EB21FB966C00D544F6 /* playlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = playlist.h; sourceTree = "<group>"; };
E465049921E94DF500A70F4C /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = "<group>"; };
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
E4A642D922090CBE00067D21 /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.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>"; };
E4E8CC932206097F0024217A /* NotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; };
E4E8CC9922075D370024217A /* Song.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Song.swift; sourceTree = "<group>"; };
E4EB2378220F10B8008C70C0 /* Pair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pair.swift; sourceTree = "<group>"; };
E4EB237A220F7CF1008C70C0 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -169,9 +181,11 @@
E407861A2110CE6E006887B1 /* Persephone */ = {
isa = PBXGroup;
children = (
E407861F2110CE70006887B1 /* Assets.xcassets */,
E408D3B7220DE8CC0006D9BE /* Extensions */,
E4D1B598220BA3C90026F233 /* Resources */,
E4D1B597220BA3A20026F233 /* Controllers */,
E408D3C3220E138B0006D9BE /* Views */,
E41B22C721FB966C00D544F6 /* include */,
E407861B2110CE6E006887B1 /* AppDelegate.swift */,
E40786242110CE70006887B1 /* Info.plist */,
@ -202,6 +216,7 @@
E408D3B7220DE8CC0006D9BE /* Extensions */ = {
isa = PBXGroup;
children = (
E4928E0A2218D62A001D4BEA /* CGColor.swift */,
E408D3B5220DD8970006D9BE /* Notification.swift */,
E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */,
);
@ -216,6 +231,14 @@
path = Extensions;
sourceTree = "<group>";
};
E408D3C3220E138B0006D9BE /* Views */ = {
isa = PBXGroup;
children = (
E408D3C8220E341D0006D9BE /* AlbumItem.swift */,
);
path = Views;
sourceTree = "<group>";
};
E41B22BE21FB6B3300D544F6 /* Frameworks */ = {
isa = PBXGroup;
children = (
@ -291,6 +314,8 @@
children = (
E4E8CC9922075D370024217A /* Song.swift */,
E4A642D922090CBE00067D21 /* Status.swift */,
E4EB2378220F10B8008C70C0 /* Pair.swift */,
E4EB237A220F7CF1008C70C0 /* Album.swift */,
);
path = Models;
sourceTree = "<group>";
@ -306,6 +331,7 @@
E4D1B597220BA3A20026F233 /* Controllers */ = {
isa = PBXGroup;
children = (
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
E4E8CC932206097F0024217A /* NotificationsController.swift */,
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
E465049921E94DF500A70F4C /* WindowController.swift */,
@ -316,8 +342,8 @@
E4D1B598220BA3C90026F233 /* Resources */ = {
isa = PBXGroup;
children = (
E407861F2110CE70006887B1 /* Assets.xcassets */,
E40786212110CE70006887B1 /* Main.storyboard */,
E408D3C9220E341D0006D9BE /* AlbumItem.xib */,
);
path = Resources;
sourceTree = "<group>";
@ -391,6 +417,7 @@
TargetAttributes = {
E40786172110CE6E006887B1 = {
CreatedOnToolsVersion = 9.4.1;
LastSwiftMigration = 1010;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 0;
@ -399,10 +426,12 @@
};
E40786292110CE70006887B1 = {
CreatedOnToolsVersion = 9.4.1;
LastSwiftMigration = 1010;
TestTargetID = E40786172110CE6E006887B1;
};
E40786342110CE70006887B1 = {
CreatedOnToolsVersion = 9.4.1;
LastSwiftMigration = 1010;
TestTargetID = E40786172110CE6E006887B1;
};
};
@ -433,6 +462,7 @@
buildActionMask = 2147483647;
files = (
E40786202110CE70006887B1 /* Assets.xcassets in Resources */,
E408D3CB220E341D0006D9BE /* AlbumItem.xib in Resources */,
E40786232110CE70006887B1 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -458,17 +488,22 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
E4A642DA22090CBE00067D21 /* Status.swift in Sources */,
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */,
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
E4EB2379220F10B8008C70C0 /* Pair.swift in Sources */,
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
E4E8CC9A22075D370024217A /* Song.swift in Sources */,
E408D3CA220E341D0006D9BE /* AlbumItem.swift in Sources */,
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -649,7 +684,7 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
};
@ -676,7 +711,7 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
};
@ -704,7 +739,7 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Persephone.app/Contents/MacOS/Persephone";
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
@ -733,7 +768,7 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Persephone.app/Contents/MacOS/Persephone";
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
@ -757,7 +792,7 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TEST_TARGET_NAME = Persephone;
};
name = Debug;
@ -779,7 +814,7 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TEST_TARGET_NAME = Persephone;
};
name = Release;

View File

@ -23,36 +23,37 @@
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "appIcon@0.5x.png",
"filename" : "appIcon128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "appIcon-1.png",
"filename" : "appIcon128@2x.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "appIcon.png",
"filename" : "appIcon256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "appIcon@2x.png",
"filename" : "appIcon256@2x.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "appIcon@2x-1.png",
"filename" : "appIcon512.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"idiom" : "mac",
"filename" : "appIcon512@2x.png",
"scale" : "2x"
}
],

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 195 KiB

View File

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

View File

@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "universal"
},
{
"idiom" : "universal",
"filename" : "blankAlbumLight.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
]
},
{
"idiom" : "universal",
"filename" : "blankAlbumDark.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
]
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "original",
"preserves-vector-representation" : true
}
}

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 109 B

After

Width:  |  Height:  |  Size: 109 B

View File

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 126 B

View File

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 154 B

View File

Before

Width:  |  Height:  |  Size: 242 B

After

Width:  |  Height:  |  Size: 242 B

View File

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 92 B

View File

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 129 B

View File

@ -0,0 +1,77 @@
//
// AlbumViewController.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/08.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Cocoa
class AlbumViewController: NSViewController,
NSCollectionViewDataSource,
NSCollectionViewDelegate,
NSCollectionViewDelegateFlowLayout {
var albums: [MPDClient.Album] = []
let paddingWidth: CGFloat = 40
let gutterWidth: CGFloat = 20
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(updateAlbums(_:)),
name: Notification.loadedAlbums,
object: AppDelegate.mpdClient
)
}
override func viewWillLayout() {
super.viewWillLayout()
albumCollectionView.collectionViewLayout?.invalidateLayout()
}
@objc func updateAlbums(_ notification: Notification) {
guard let albums = notification.userInfo?[Notification.albumsKey] as? [MPDClient.Album]
else { return }
self.albums = albums
albumCollectionView.reloadData()
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return albums.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: .albumItem, for: indexPath)
guard let albumItem = item as? AlbumItem else { return item }
albumItem.view.wantsLayer = true
albumItem.setAlbum(albums[indexPath.item])
return albumItem
}
func collectionView(_ collectionView: NSCollectionView, layout: NSCollectionViewLayout, sizeForItemAt: IndexPath) -> NSSize {
let width = collectionView.frame.size.width
var divider: CGFloat = 1
var itemWidth: CGFloat = 0
repeat {
let totalPaddingWidth = paddingWidth * 2
let totalGutterWidth = (divider - 1) * (gutterWidth + 1)
itemWidth = (width - totalPaddingWidth - totalGutterWidth) / divider
divider = divider + 1
} while itemWidth > 180
let itemHeight = itemWidth + 39
return NSSize(width: itemWidth, height: itemHeight)
}
@IBOutlet var albumCollectionView: NSCollectionView!
}

View File

@ -32,6 +32,13 @@ class NotificationsController: MPDClientDelegate {
)
}
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.Album]) {
sendNotification(
name: Notification.loadedAlbums,
userInfo: [Notification.albumsKey: albums]
)
}
private func sendNotification(name: Notification.Name, userInfo: [AnyHashable : Any]) {
self.notificationQueue.async {
NotificationCenter.default.post(

View File

@ -17,8 +17,8 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV
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"))
let playIcon = NSImage(named: "playButton")
let pauseIcon = NSImage(named: "pauseButton")
struct SongItem {
var song: MPDClient.Song
@ -28,7 +28,7 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV
override func viewDidLoad() {
super.viewDidLoad()
queueView.columnAutoresizingStyle = .uniformColumnAutoresizingStyle
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
NotificationCenter.default.addObserver(
self,
@ -52,6 +52,17 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV
)
}
@IBAction func playTrack(_ sender: Any) {
guard let view = sender as? NSOutlineView
else { return }
let queuePos = view.selectedRow - 1
if queuePos >= 0 {
AppDelegate.mpdClient.playTrack(queuePos: queuePos)
}
}
@objc func stateChanged(_ notification: Notification) {
guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.Status.State
else { return }
@ -178,5 +189,16 @@ class QueueViewController: NSViewController, NSOutlineViewDataSource, NSOutlineV
}
}
func outlineView(
_ outlineView: NSOutlineView,
selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet
) -> IndexSet {
if proposedSelectionIndexes.contains(0) {
return IndexSet()
} else {
return proposedSelectionIndexes
}
}
@IBOutlet var queueView: NSOutlineView!
}

View File

@ -13,8 +13,8 @@ class WindowController: NSWindowController {
case prevTrack, playPause, stop, nextTrack
}
let playIcon = NSImage(named: NSImage.Name("playButton"))
let pauseIcon = NSImage(named: NSImage.Name("pauseButton"))
let playIcon = NSImage(named: "playButton")
let pauseIcon = NSImage(named: "pauseButton")
override func windowDidLoad() {
super.windowDidLoad()

View File

@ -0,0 +1,14 @@
//
// NSColor.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/16.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Cocoa
extension CGColor {
static let albumBorderColorLight = NSColor.black.withAlphaComponent(0.1).cgColor
static let albumBorderColorDark = NSColor.white.withAlphaComponent(0.1).cgColor
}

View File

@ -15,4 +15,6 @@ extension NSUserInterfaceItemIdentifier {
static let queueHeading = NSUserInterfaceItemIdentifier("queueHeadingCell")
static let queueSongArtist = NSUserInterfaceItemIdentifier("songArtistCell")
static let queueSongTitle = NSUserInterfaceItemIdentifier("songTitleCell")
static let albumItem = NSUserInterfaceItemIdentifier("AlbumItem")
}

View File

@ -12,8 +12,10 @@ extension Notification {
static let stateChanged = Notification.Name("MPDClientStateChanged")
static let queueChanged = Notification.Name("MPDClientQueueChanged")
static let queuePosChanged = Notification.Name("MPDClientQueuePosChanged")
static let loadedAlbums = Notification.Name("MPDClientLoadedAlbums")
static let stateKey = "state"
static let queueKey = "queue"
static let queuePosKey = "song"
static let albumsKey = "albums"
}

View File

@ -22,7 +22,8 @@ class MPDClient {
private let commandQueue = DispatchQueue(label: "commandQueue")
enum Command {
case prevTrack, nextTrack, playPause, stop, fetchStatus, fetchQueue
case prevTrack, nextTrack, playPause, stop,
fetchStatus, fetchQueue, fetchAllAlbums
}
struct Idle: OptionSet {
@ -57,6 +58,8 @@ class MPDClient {
fetchQueue()
fetchAllAlbums()
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)
@ -78,6 +81,10 @@ class MPDClient {
sendCommand(command: .fetchQueue)
}
func fetchAllAlbums() {
sendCommand(command: .fetchAllAlbums)
}
func playPause() {
queueCommand(command: .playPause)
}
@ -94,6 +101,14 @@ class MPDClient {
queueCommand(command: .nextTrack)
}
func playTrack(queuePos: Int) {
noIdle()
commandQueue.async { [unowned self] in
mpd_run_play_pos(self.connection, UInt32(queuePos))
}
idle()
}
func queueCommand(command: Command) {
noIdle()
commandQueue.async { [unowned self] in
@ -114,19 +129,12 @@ class MPDClient {
sendStop()
case .playPause:
sendPlay()
case .fetchStatus:
guard let status = mpd_run_status(connection) else { break }
self.status = Status(status)
sendRunStatus()
case .fetchQueue:
self.queue = []
mpd_send_list_queue_meta(connection)
while let mpdSong = mpd_recv_song(connection) {
let song = Song(mpdSong)
self.queue.append(song)
}
sendFetchQueue()
case .fetchAllAlbums:
allAlbums()
}
}
@ -158,6 +166,46 @@ class MPDClient {
}
}
func sendRunStatus() {
guard let status = mpd_run_status(connection) else { return }
self.status = Status(status)
}
func sendFetchQueue() {
self.queue = []
mpd_send_list_queue_meta(connection)
while let mpdSong = mpd_recv_song(connection) {
let song = Song(mpdSong)
self.queue.append(song)
}
}
func allAlbums() {
var albums: [Album] = []
var artist: String = ""
mpd_search_db_tags(connection, MPD_TAG_ALBUM)
mpd_search_add_group_tag(connection, MPD_TAG_ALBUM_ARTIST)
mpd_search_commit(connection)
while let mpdPair = mpd_recv_pair(connection) {
let pair = Pair(mpdPair)
switch pair.name {
case "AlbumArtist":
artist = pair.value
case "Album":
albums.append(Album(title: pair.value, artist: artist))
default:
break
}
mpd_return_pair(connection, pair.mpdPair)
}
delegate?.didLoadAlbums(mpdClient: self, albums: albums)
}
func noIdle() {
mpd_send_noidle(connection)
}

View File

@ -0,0 +1,16 @@
//
// Album.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/09.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
extension MPDClient {
struct Album {
let title: String
let artist: String
}
}

View File

@ -0,0 +1,26 @@
//
// Pair.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/09.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import mpdclient
class Pair {
let mpdPair: UnsafeMutablePointer<mpd_pair>
init(_ mpdPair: UnsafeMutablePointer<mpd_pair>) {
self.mpdPair = mpdPair
}
var name: String {
get { return String(cString: mpdPair.pointee.name) }
}
var value: String {
get { return String(cString: mpdPair.pointee.value) }
}
}

View File

@ -12,4 +12,5 @@ protocol MPDClientDelegate {
func didUpdateState(mpdClient: MPDClient, state: MPDClient.Status.State)
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song])
func didUpdateQueuePos(mpdClient: MPDClient, song: Int)
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.Album])
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AlbumItem" customModule="Persephone" customModuleProvider="target">
<connections>
<outlet property="albumArtist" destination="5Uu-j1-qyT" id="2Et-tX-InT"/>
<outlet property="albumCoverView" destination="Kfb-8f-ean" id="ZAL-jD-lHj"/>
<outlet property="albumTitle" destination="KEh-NL-c2W" id="SI3-hm-H2B"/>
<outlet property="imageView" destination="Kfb-8f-ean" id="Ur0-hX-wJm"/>
<outlet property="view" destination="Hz6-mo-xeY" id="v7W-XA-Emc"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="128" height="167"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField identifier="albumTitle" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEh-NL-c2W">
<rect key="frame" x="-2" y="18" width="132" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="pDs-0t-e1j">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="albumArtist" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5Uu-j1-qyT">
<rect key="frame" x="-2" y="0.0" width="132" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="yZn-e9-zyP">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView identifier="albumArtwork" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Kfb-8f-ean">
<rect key="frame" x="0.0" y="39" width="128" height="128"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="blankAlbum" id="FsA-JX-BFh"/>
</imageView>
</subviews>
<constraints>
<constraint firstItem="5Uu-j1-qyT" firstAttribute="trailing" secondItem="KEh-NL-c2W" secondAttribute="trailing" id="64z-uz-4nY"/>
<constraint firstAttribute="bottom" secondItem="KEh-NL-c2W" secondAttribute="bottom" constant="18" id="8Kg-1r-wNp"/>
<constraint firstItem="Kfb-8f-ean" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="JMi-4i-dgs"/>
<constraint firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="KQC-Wz-Bsg"/>
<constraint firstItem="5Uu-j1-qyT" firstAttribute="leading" secondItem="KEh-NL-c2W" secondAttribute="leading" id="MUo-0i-fX9"/>
<constraint firstItem="Kfb-8f-ean" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="Qbk-jx-zAi"/>
<constraint firstItem="KEh-NL-c2W" firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="U0w-G4-ggX"/>
<constraint firstItem="KEh-NL-c2W" firstAttribute="leading" secondItem="Kfb-8f-ean" secondAttribute="leading" id="V8r-Rc-Dx7"/>
<constraint firstAttribute="bottom" secondItem="5Uu-j1-qyT" secondAttribute="bottom" id="gci-4h-pDZ"/>
<constraint firstAttribute="bottom" secondItem="Kfb-8f-ean" secondAttribute="bottom" constant="39" id="sid-zJ-YMA"/>
</constraints>
<point key="canvasLocation" x="-22" y="125.5"/>
</customView>
<collectionViewItem id="Qgu-aI-55A" customClass="AlbumItem" customModule="Persephone" customModuleProvider="target"/>
</objects>
<resources>
<image name="blankAlbum" width="128" height="128"/>
</resources>
</document>

View File

@ -689,8 +689,9 @@
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="207" y="570" width="480" height="270"/>
<rect key="contentRect" x="207" y="570" width="960" height="560"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<value key="minSize" type="size" width="800" height="400"/>
<toolbar key="toolbar" implicitIdentifier="33DCB92B-74AF-496A-99FE-1F88002CB88A" autosavesConfiguration="NO" displayMode="iconOnly" sizeMode="regular" id="eNB-TI-KUS">
<allowedToolbarItems>
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="mhg-16-CNM"/>
@ -737,7 +738,7 @@
<objects>
<splitViewController id="fnD-7K-pHK" sceneMemberID="viewController">
<splitViewItems>
<splitViewItem canCollapse="YES" holdingPriority="260" behavior="sidebar" id="CWo-v7-gd2"/>
<splitViewItem holdingPriority="255" behavior="contentList" id="CWo-v7-gd2"/>
<splitViewItem id="y8g-4F-czS"/>
</splitViewItems>
<splitView key="splitView" dividerStyle="thin" vertical="YES" id="g34-ef-XN0">
@ -755,30 +756,30 @@
</splitViewController>
<customObject id="Dag-kO-ps3" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="74" y="677"/>
<point key="canvasLocation" x="74" y="873"/>
</scene>
<!--Queue View Controller-->
<scene sceneID="QcX-dC-cTZ">
<objects>
<viewController id="KIP-rq-4dM" customClass="QueueViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="2su-YT-hba">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="S3o-nF-NN7">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
<clipView key="contentView" drawsBackground="NO" id="WI8-Pw-03L">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="16" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="songTitleColumn" width="222" minWidth="128" maxWidth="1000" id="0Co-uF-CCB">
<tableColumn identifier="songTitleColumn" width="200" minWidth="128" maxWidth="1000" id="0Co-uF-CCB">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Title">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@ -792,7 +793,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
<prototypeCellViews>
<tableCellView identifier="queueHeadingCell" id="GOd-cg-juD">
<rect key="frame" x="1" y="1" width="222" height="17"/>
<rect key="frame" x="1" y="1" width="200" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xgd-Cz-np3">
@ -807,7 +808,7 @@
</subviews>
</tableCellView>
<tableCellView identifier="songTitleCell" id="5rR-Gz-AcP">
<rect key="frame" x="1" y="20" width="222" height="17"/>
<rect key="frame" x="1" y="20" width="200" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="o8i-cz-hIP">
@ -818,7 +819,7 @@
<imageCell key="cell" refusesFirstResponder="YES" imageScaling="proportionallyDown" id="ckK-gW-Vhx"/>
</imageView>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ">
<rect key="frame" x="25" y="0.0" width="197" height="17"/>
<rect key="frame" x="25" y="0.0" width="175" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="ei8-1e-ErK">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -841,7 +842,7 @@
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="songArtistColumn" width="222" minWidth="128" maxWidth="1000" id="SPM-QP-DX8">
<tableColumn identifier="songArtistColumn" width="144" minWidth="128" maxWidth="1000" id="SPM-QP-DX8">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Artist">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@ -855,7 +856,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
<prototypeCellViews>
<tableCellView identifier="songArtistCell" id="JSk-Vc-Y7e">
<rect key="frame" x="226" y="1" width="222" height="17"/>
<rect key="frame" x="204" y="1" width="144" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tBe-Q9-3Rw">
@ -876,6 +877,7 @@
</tableColumn>
</tableColumns>
<connections>
<action trigger="doubleAction" selector="playTrack:" target="KIP-rq-4dM" id="opa-6G-OW0"/>
<outlet property="dataSource" destination="KIP-rq-4dM" id="K1Q-7o-xXW"/>
<outlet property="delegate" destination="KIP-rq-4dM" id="60F-6x-bUE"/>
</connections>
@ -883,8 +885,12 @@
</subviews>
<nil key="backgroundColor"/>
</clipView>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="tgW-46-U0V"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="ynf-58-b0B"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="7mx-v9-DSr">
<rect key="frame" x="0.0" y="284" width="450" height="16"/>
<rect key="frame" x="0.0" y="284" width="350" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="p5z-C0-FUJ">
@ -906,53 +912,61 @@
</viewController>
<customObject id="du4-e9-TfX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="744" y="459"/>
<point key="canvasLocation" x="848" y="635"/>
</scene>
<!--View Controller-->
<!--Album View Controller-->
<scene sceneID="7Ua-Hj-zWt">
<objects>
<viewController id="gPn-fP-LFc" sceneMemberID="viewController">
<viewController id="gPn-fP-LFc" customClass="AlbumViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="v1d-6d-Fl3">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView wantsLayer="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wia-Qk-FEw">
<scrollView wantsLayer="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="i5f-35-7x8">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<clipView key="contentView" id="leP-Jn-VMY">
<clipView key="contentView" id="iXE-Xl-zu6">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView id="2XB-gk-sPO">
<collectionView id="lfq-AB-epE">
<rect key="frame" x="0.0" y="0.0" width="450" height="158"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" id="OKj-Q2-LS1">
<size key="itemSize" width="50" height="50"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="20" minimumLineSpacing="20" id="K0l-P8-OW0">
<size key="itemSize" width="206" height="248"/>
<edgeInsets key="sectionInset" left="40" right="40" top="20" bottom="60"/>
</collectionViewFlowLayout>
<color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<connections>
<outlet property="dataSource" destination="gPn-fP-LFc" id="2VB-5V-ltv"/>
<outlet property="delegate" destination="gPn-fP-LFc" id="LQ2-Vl-r08"/>
</connections>
</collectionView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Dqn-NC-UFS">
<rect key="frame" x="1" y="144" width="233" height="15"/>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="bK3-Ei-Odz">
<rect key="frame" x="1" y="213" width="348" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="Enn-qw-4lt">
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="SkF-fO-hfN">
<rect key="frame" x="234" y="1" width="15" height="143"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<constraints>
<constraint firstItem="wia-Qk-FEw" firstAttribute="top" secondItem="v1d-6d-Fl3" secondAttribute="top" id="NG4-cV-AAv"/>
<constraint firstAttribute="bottom" secondItem="wia-Qk-FEw" secondAttribute="bottom" id="emh-fE-n6N"/>
<constraint firstAttribute="trailing" secondItem="wia-Qk-FEw" secondAttribute="trailing" id="izX-0D-aEA"/>
<constraint firstItem="wia-Qk-FEw" firstAttribute="leading" secondItem="v1d-6d-Fl3" secondAttribute="leading" id="v7v-S1-h5Z"/>
<constraint firstItem="i5f-35-7x8" firstAttribute="top" secondItem="v1d-6d-Fl3" secondAttribute="top" id="BXd-LQ-OWX"/>
<constraint firstItem="i5f-35-7x8" firstAttribute="leading" secondItem="v1d-6d-Fl3" secondAttribute="leading" id="Bgm-NV-0Ft"/>
<constraint firstAttribute="bottom" secondItem="i5f-35-7x8" secondAttribute="bottom" id="G8K-PG-9Zq"/>
<constraint firstAttribute="trailing" secondItem="i5f-35-7x8" secondAttribute="trailing" id="vcm-wb-Ghi"/>
</constraints>
</view>
<connections>
<outlet property="albumCollectionView" destination="lfq-AB-epE" id="p69-Fs-hCN"/>
</connections>
</viewController>
<customObject id="uex-Ws-5X4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="744" y="831"/>
<point key="canvasLocation" x="744" y="1077"/>
</scene>
</scenes>
<resources>

View File

@ -0,0 +1,48 @@
//
// AlbumItem.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/08.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Cocoa
class AlbumItem: NSCollectionViewItem {
var observer: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
albumCoverView.wantsLayer = true
albumCoverView.layer?.cornerRadius = 3
albumCoverView.layer?.borderWidth = 1
setAppearance()
if #available(OSX 10.14, *) {
observer = NSApp.observe(\.effectiveAppearance) { (app, _) in
self.setAppearance()
}
}
}
func setAlbum(_ album: MPDClient.Album) {
albumTitle.stringValue = album.title
albumArtist.stringValue = album.artist
}
func setAppearance() {
if #available(OSX 10.14, *) {
let darkMode = NSApp.effectiveAppearance.bestMatch(from:
[.darkAqua, .aqua]) == .darkAqua
albumCoverView.layer?.borderColor = darkMode ? .albumBorderColorDark : .albumBorderColorLight
} else {
albumCoverView.layer?.borderColor = .albumBorderColorLight
}
}
@IBOutlet var albumCoverView: NSImageView!
@IBOutlet var albumTitle: NSTextField!
@IBOutlet var albumArtist: NSTextField!
}

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 168 KiB