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

Drag songs with custom drag image

This commit is contained in:
Daniel Barber 2019-06-21 14:18:41 -04:00
parent ba64fed16a
commit f687e89f9d
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
10 changed files with 282 additions and 57 deletions

View File

@ -57,6 +57,8 @@
E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* CoverArtQueue.swift */; };
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD96222633920091BED3 /* Alamofire.framework.dSYM */; };
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */; };
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */; };
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */; };
E45878382296173C00586A1C /* AlbumDetailSongRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */; };
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* MPDCommand.swift */; };
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD722515D87004B537F /* CHANGELOG.md */; };
@ -72,6 +74,9 @@
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FDC2220A6D100F747E6 /* Time.swift */; };
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; };
E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39822B85D0400CA8CBD /* NSPasteboard.swift */; };
E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39C22B9CF0000CA8CBD /* NSView.swift */; };
E489E3A422B9D31800CA8CBD /* DraggedSongView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */; };
E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */; };
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
E4A3A6A122A457B600EA2C40 /* AlbumDetailSongListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */; };
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* MPDStatus.swift */; };
@ -277,6 +282,8 @@
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = PMKFoundation.framework.dSYM; path = Carthage/Build/Mac/PMKFoundation.framework.dSYM; sourceTree = "<group>"; };
E450ADA02229E7C90091BED3 /* PMKFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PMKFoundation.framework; path = Carthage/Build/Mac/PMKFoundation.framework; sourceTree = "<group>"; };
E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongType.swift; sourceTree = "<group>"; };
E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSong.swift; sourceTree = "<group>"; };
E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongRowView.swift; sourceTree = "<group>"; };
E45962C52241A78500FC1A1E /* MPDCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDCommand.swift; sourceTree = "<group>"; };
E45E4FD722515D87004B537F /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = SOURCE_ROOT; };
@ -293,6 +300,9 @@
E47E2FDC2220A6D100F747E6 /* Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = "<group>"; };
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = "<group>"; };
E489E39822B85D0400CA8CBD /* NSPasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboard.swift; sourceTree = "<group>"; };
E489E39C22B9CF0000CA8CBD /* NSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSView.swift; sourceTree = "<group>"; };
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongView.swift; sourceTree = "<group>"; };
E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DraggedSongView.xib; sourceTree = "<group>"; };
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongListView.swift; sourceTree = "<group>"; };
E4A642D922090CBE00067D21 /* MPDStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDStatus.swift; sourceTree = "<group>"; };
@ -449,6 +459,7 @@
E435E3E3221CD75D00184CFC /* NSImage.swift */,
E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */,
E489E39822B85D0400CA8CBD /* NSPasteboard.swift */,
E489E39C22B9CF0000CA8CBD /* NSView.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -473,12 +484,13 @@
E408D3C3220E138B0006D9BE /* Views */ = {
isa = PBXGroup;
children = (
E47E2FD62220720300F747E6 /* AlbumItemView.swift */,
E47E2FD222205D2500F747E6 /* MainWindow.swift */,
E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */,
E423563F228623D2001216D6 /* QueueSongTitleView.swift */,
E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */,
E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */,
E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */,
E47E2FD62220720300F747E6 /* AlbumItemView.swift */,
E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */,
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */,
E47E2FD222205D2500F747E6 /* MainWindow.swift */,
E423563F228623D2001216D6 /* QueueSongTitleView.swift */,
E4120D6B22AD8139004CB1F8 /* QueueView.swift */,
);
path = Views;
@ -709,9 +721,10 @@
E4D1B598220BA3C90026F233 /* Resources */ = {
isa = PBXGroup;
children = (
E40786212110CE70006887B1 /* Main.storyboard */,
E43B67A922909793007DCF55 /* AlbumDetailView.xib */,
E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */,
E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */,
E40786212110CE70006887B1 /* Main.storyboard */,
);
path = Resources;
sourceTree = "<group>";
@ -736,6 +749,8 @@
E419E2862249B96600216A8C /* Song.swift */,
E47E2FDC2220A6D100F747E6 /* Time.swift */,
E4B11B72226A6C770075461B /* TrackTimer.swift */,
E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */,
E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */,
);
path = Models;
sourceTree = "<group>";
@ -865,6 +880,7 @@
E43B67AB22909793007DCF55 /* AlbumDetailView.xib in Resources */,
E45E4FDC22515D87004B537F /* Cartfile in Resources */,
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */,
E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */,
E40786202110CE70006887B1 /* Assets.xcassets in Resources */,
E45E4FDF225168DA004B537F /* CryptoSwift.framework.dSYM in Resources */,
E42A8F3C22176D6400A13ED9 /* README.md in Resources */,
@ -940,6 +956,7 @@
E440519822787CB40090CD6F /* MPDState.swift in Sources */,
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */,
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */,
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
E4B11BC02275EE150075461B /* QueueActions.swift in Sources */,
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
@ -951,6 +968,7 @@
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */,
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */,
E489E3A422B9D31800CA8CBD /* DraggedSongView.swift in Sources */,
E440519E227BB0720090CD6F /* UIReducer.swift in Sources */,
E43B67AA22909793007DCF55 /* AlbumDetailView.swift in Sources */,
E4E7A6AD22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift in Sources */,
@ -965,6 +983,7 @@
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
E419E2872249B96600216A8C /* Song.swift in Sources */,
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */,
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */,
E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */,
E439109822640213002982E9 /* SongNotifierService.swift in Sources */,
@ -1004,6 +1023,7 @@
E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */,
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -22,6 +22,7 @@ class QueueViewController: NSViewController {
$0.select { $0.queueState }
}
queueView.dataSource = dataSource
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
queueView.registerForDraggedTypes([.songPasteboardType])
queueView.draggingDestinationFeedbackStyle = .regular
@ -66,32 +67,6 @@ class QueueViewController: NSViewController {
}
}
extension QueueViewController: NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
return dataSource.outlineView(outlineView, numberOfChildrenOfItem: item)
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
return dataSource.outlineView(outlineView, isItemExpandable: item)
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
return dataSource.outlineView(outlineView, child: index, ofItem: item)
}
func outlineView(_ outlineView: NSOutlineView, pasteboardWriterForItem item: Any) -> NSPasteboardWriting? {
return dataSource.outlineView(outlineView, pasteboardWriterForItem: item)
}
func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
return dataSource.outlineView(outlineView, validateDrop: info, proposedItem: item, proposedChildIndex: index)
}
func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
return dataSource.outlineView(outlineView, acceptDrop: info, item: item, childIndex: index)
}
}
extension QueueViewController: StoreSubscriber {
typealias StoreSubscriberStateType = QueueState

View File

@ -40,11 +40,23 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
}
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
let item = albumSongs[row]
let albumSongItem = albumSongs[row]
guard let song = albumSongItem.song
else { return nil }
let pasteboardItem = NSPasteboardItem()
pasteboardItem.setPropertyList(["songUri": item.song?.mpdSong.uriString], forType: .songPasteboardType)
let draggedSong = DraggedSong(
type: .albumSongItem(song.mpdSong.uriString),
title: song.title,
artist: song.artist
)
let encoder = PropertyListEncoder()
let data = try! encoder.encode(draggedSong)
pasteboardItem.setData(data, forType: .songPasteboardType)
return pasteboardItem
}
@ -52,4 +64,28 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return albumSongs.count
}
func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forRowIndexes rowIndexes: IndexSet) {
session.enumerateDraggingItems(
options: [],
for: tableView,
classes: [NSPasteboardItem.self],
searchOptions: [:]
) { draggingItem, index, stop in
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)
else { return }
draggingItem.imageComponentsProvider = {
let component = NSDraggingImageComponent(key: NSDraggingItem.ImageComponentKey.icon)
let draggedSongView = DraggedSongView(title: title, artist: artist)
component.contents = draggedSongView.view.image()
component.frame = NSRect(origin: CGPoint(), size: draggedSongView.view.image().size)
return [component]
}
}
}
}

View File

@ -45,7 +45,16 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
let pasteboardItem = NSPasteboardItem()
pasteboardItem.setPropertyList(["queuePos": queueItem.queuePos], forType: .songPasteboardType)
let draggedSong = DraggedSong(
type: .queueItem(queueItem.queuePos),
title: queueItem.song.title,
artist: queueItem.song.artist
)
let encoder = PropertyListEncoder()
let data = try! encoder.encode(draggedSong)
pasteboardItem.setData(data, forType: .songPasteboardType)
return pasteboardItem
}
@ -53,51 +62,74 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
var newQueuePos = index - 1
guard let draggingTypes = info.draggingPasteboard.types,
draggingTypes.contains(.songPasteboardType)
guard newQueuePos >= 0,
let draggingTypes = info.draggingPasteboard.types,
draggingTypes.contains(.songPasteboardType),
let data = info.draggingPasteboard.data(forType: .songPasteboardType),
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data)
else { return [] }
if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: Int],
let queuePos = payload["queuePos"],
newQueuePos >= 0 {
switch draggedSong.type {
case let .queueItem(queuePos):
if newQueuePos > queuePos { newQueuePos -= 1 }
guard queuePos != newQueuePos
else { return [] }
return .move
} else if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: String],
let _ = payload["songUri"],
newQueuePos >= 0 {
case .albumSongItem:
return .copy
}
return []
}
func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
var newQueuePos = index - 1
if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: Int],
let queuePos = payload["queuePos"] {
guard let draggingTypes = info.draggingPasteboard.types,
draggingTypes.contains(.songPasteboardType),
let data = info.draggingPasteboard.data(forType: .songPasteboardType),
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data)
else { return false }
switch draggedSong.type {
case let .queueItem(queuePos):
if newQueuePos > queuePos { newQueuePos -= 1 }
guard queuePos != newQueuePos
else { return false }
App.store.dispatch(MPDMoveSongInQueue(oldQueuePos: queuePos, newQueuePos: newQueuePos))
return true
} else if let payload = info.draggingPasteboard.propertyList(forType: .songPasteboardType) as? [String: String],
let songUri = payload["songUri"] {
App.store.dispatch(MPDAddSongToQueue(songUri: songUri, queuePos: newQueuePos))
case let .albumSongItem(uri):
App.store.dispatch(MPDAddSongToQueue(songUri: uri, queuePos: newQueuePos))
return true
}
return false
}
func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItems draggedItems: [Any]) {
session.enumerateDraggingItems(
options: [],
for: outlineView,
classes: [NSPasteboardItem.self],
searchOptions: [:]
) { draggingItem, index, stop in
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)
else { return }
draggingItem.imageComponentsProvider = {
let component = NSDraggingImageComponent(key: NSDraggingItem.ImageComponentKey.icon)
let draggedSongView = DraggedSongView(title: title, artist: artist)
component.contents = draggedSongView.view.image()
component.frame = NSRect(origin: CGPoint(), size: draggedSongView.view.image().size)
return [component]
}
}
}
}

View File

@ -0,0 +1,17 @@
//
// NSView.swift
// Persephone
//
// Created by Daniel Barber on 2019/6/18.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
extension NSView {
func image() -> NSImage {
let imageRepresentation = bitmapImageRepForCachingDisplay(in: bounds)!
cacheDisplay(in: bounds, to: imageRepresentation)
return NSImage(cgImage: imageRepresentation.cgImage!, size: bounds.size)
}
}

View File

@ -0,0 +1,13 @@
//
// DraggedSong.swift
// Persephone
//
// Created by Daniel Barber on 2019/6/21.
// Copyright © 2019 Dan Barber. All rights reserved.
//
struct DraggedSong: Codable {
var type: DraggedSongType
var title: String?
var artist: String?
}

View File

@ -0,0 +1,50 @@
//
// DraggedSongType.swift
// Persephone
//
// Created by Daniel Barber on 2019/6/21.
// Copyright © 2019 Dan Barber. All rights reserved.
//
enum DraggedSongType {
case queueItem(Int)
case albumSongItem(String)
}
private enum Discriminator: Int, Codable {
case queueItem
case albumSongItem
}
extension DraggedSongType: Codable {
enum CodingKeys: String, CodingKey {
case type
case value
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .queueItem(queuePos):
try container.encode(Discriminator.queueItem, forKey: .type)
try container.encode(queuePos, forKey: .value)
case let .albumSongItem(uri):
try container.encode(Discriminator.albumSongItem, forKey: .type)
try container.encode(uri, forKey: .value)
}
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(Discriminator.self, forKey: .type)
switch type {
case .queueItem:
let queuePos = try container.decode(Int.self, forKey: .value)
self = .queueItem(queuePos)
case .albumSongItem:
let uri = try container.decode(String.self, forKey: .value)
self = .albumSongItem(uri)
}
}
}

View File

@ -578,14 +578,14 @@
<rect key="frame" x="0.0" y="0.0" width="328" height="548"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="22" horizontalPageScroll="10" verticalLineScroll="22" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="17" horizontalPageScroll="10" verticalLineScroll="17" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" drawsBackground="NO" id="WI8-Pw-03L">
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowHeight="22" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="14" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll" customClass="QueueView" customModule="Persephone" customModuleProvider="target">
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="14" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll" customClass="QueueView" customModule="Persephone" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="0.0"/>
@ -713,7 +713,6 @@
</tableColumns>
<connections>
<action trigger="doubleAction" selector="playTrack:" target="KIP-rq-4dM" id="opa-6G-OW0"/>
<outlet property="dataSource" destination="KIP-rq-4dM" id="AL2-j1-SVE"/>
<outlet property="delegate" destination="KIP-rq-4dM" id="60F-6x-bUE"/>
<outlet property="menu" destination="dYA-Jm-eOa" id="9s2-7K-tVx"/>
</connections>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="DraggedSongView" customModule="Persephone" customModuleProvider="target">
<connections>
<outlet property="titleLabel" destination="tsD-ub-h7I" id="EVO-Z7-tZY"/>
<outlet property="view" destination="Hz6-mo-xeY" id="Wf9-hm-E7M"/>
</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="181" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<box boxType="custom" borderType="line" cornerRadius="4" translatesAutoresizingMaskIntoConstraints="NO" id="G2x-p5-RM6">
<rect key="frame" x="0.0" y="0.0" width="181" height="27"/>
<view key="contentView" id="qbG-wn-N4h">
<rect key="frame" x="1" y="1" width="179" height="25"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tsD-ub-h7I">
<rect key="frame" x="11" y="4" width="156" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Title" id="rMn-D0-PG7">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<color key="borderColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="fillColor" name="unemphasizedSelectedContentBackgroundColor" catalog="System" colorSpace="catalog"/>
</box>
</subviews>
<constraints>
<constraint firstItem="G2x-p5-RM6" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="Gcj-M0-oDt"/>
<constraint firstAttribute="bottom" secondItem="G2x-p5-RM6" secondAttribute="bottom" id="Tmu-AD-yFM"/>
<constraint firstItem="G2x-p5-RM6" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="gF3-y6-cc5"/>
<constraint firstAttribute="trailing" secondItem="G2x-p5-RM6" secondAttribute="trailing" id="rax-yO-zz2"/>
</constraints>
<point key="canvasLocation" x="-10.5" y="31.5"/>
</customView>
</objects>
</document>

View File

@ -0,0 +1,31 @@
//
// DraggedSong.swift
// Persephone
//
// Created by Daniel Barber on 2019/6/18.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Cocoa
class DraggedSongView: NSViewController {
@IBOutlet var titleLabel: NSTextField!
private let songTitle: String
private let songArtist: String
init(title: String, artist: String) {
songTitle = title
songArtist = artist
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.stringValue = songTitle
}
}