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:
parent
ba64fed16a
commit
f687e89f9d
@ -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;
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
17
Persephone/Extensions/NSView.swift
Normal file
17
Persephone/Extensions/NSView.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
13
Persephone/Models/DraggedSong.swift
Normal file
13
Persephone/Models/DraggedSong.swift
Normal 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?
|
||||
}
|
||||
50
Persephone/Models/DraggedSongType.swift
Normal file
50
Persephone/Models/DraggedSongType.swift
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
|
||||
52
Persephone/Resources/DraggedSongView.xib
Normal file
52
Persephone/Resources/DraggedSongView.xib
Normal 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>
|
||||
31
Persephone/Views/DraggedSongView.swift
Normal file
31
Persephone/Views/DraggedSongView.swift
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user