mirror of
https://github.com/danbee/persephone
synced 2025-03-04 08:39:11 +00:00
Double click on a track to play it
This commit is contained in:
parent
998ddb60bc
commit
338dde08d3
@ -61,6 +61,21 @@ class AlbumDetailView: NSViewController {
|
|||||||
albumCoverView.image = .defaultCoverArt
|
albumCoverView.image = .defaultCoverArt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func playAlbum(_ sender: NSButton) {
|
||||||
|
guard let album = album else { return }
|
||||||
|
|
||||||
|
App.store.dispatch(MPDPlayAlbum(album: album.mpdAlbum))
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func playSong(_ sender: AlbumDetailSongListView) {
|
||||||
|
guard let song = dataSource.albumSongs[sender.selectedRow].song
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let queueLength = App.store.state.queueState.queue.count
|
||||||
|
App.store.dispatch(MPDAppendTrack(song: song.mpdSong))
|
||||||
|
App.store.dispatch(MPDPlayTrack(queuePos: queueLength))
|
||||||
|
}
|
||||||
|
|
||||||
func getAlbumSongs(for album: Album) {
|
func getAlbumSongs(for album: Album) {
|
||||||
App.mpdClient.getAlbumSongs(for: album.mpdAlbum) { [weak self] (mpdSongs: [MPDClient.MPDSong]) in
|
App.mpdClient.getAlbumSongs(for: album.mpdAlbum) { [weak self] (mpdSongs: [MPDClient.MPDSong]) in
|
||||||
self?.dataSource.setAlbumSongs(
|
self?.dataSource.setAlbumSongs(
|
||||||
@ -135,6 +150,10 @@ extension AlbumDetailView: NSTableViewDelegate {
|
|||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||||||
|
return dataSource.albumSongs[row].disc == nil
|
||||||
|
}
|
||||||
|
|
||||||
func cellForDiscNumber(_ tableView: NSTableView, with disc: String) -> NSView {
|
func cellForDiscNumber(_ tableView: NSTableView, with disc: String) -> NSView {
|
||||||
let cellView = tableView.makeView(
|
let cellView = tableView.makeView(
|
||||||
withIdentifier: .discNumber,
|
withIdentifier: .discNumber,
|
||||||
|
|||||||
@ -58,12 +58,6 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func playAlbum(_ sender: NSButton) {
|
|
||||||
guard let album = album else { return }
|
|
||||||
|
|
||||||
App.store.dispatch(MPDPlayAlbum(album: album.mpdAlbum))
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func showAlbumDetail(_ sender: NSButton) {
|
@IBAction func showAlbumDetail(_ sender: NSButton) {
|
||||||
guard let album = album else { return }
|
guard let album = album else { return }
|
||||||
|
|
||||||
|
|||||||
@ -58,6 +58,10 @@ extension MPDClient {
|
|||||||
guard let songs = userData["songs"] as? [MPDSong]
|
guard let songs = userData["songs"] as? [MPDSong]
|
||||||
else { return }
|
else { return }
|
||||||
sendReplaceQueue(songs)
|
sendReplaceQueue(songs)
|
||||||
|
case .appendSong:
|
||||||
|
guard let song = userData["song"] as? MPDSong
|
||||||
|
else { return }
|
||||||
|
sendAppendSong(song)
|
||||||
|
|
||||||
// Album commands
|
// Album commands
|
||||||
case .fetchAllAlbums:
|
case .fetchAllAlbums:
|
||||||
|
|||||||
@ -18,6 +18,10 @@ extension MPDClient {
|
|||||||
enqueueCommand(command: .playTrack, userData: ["queuePos": queuePos])
|
enqueueCommand(command: .playTrack, userData: ["queuePos": queuePos])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendSong(_ song: MPDSong) {
|
||||||
|
enqueueCommand(command: .appendSong, userData: ["song": song])
|
||||||
|
}
|
||||||
|
|
||||||
func sendPlayTrack(at queuePos: Int) {
|
func sendPlayTrack(at queuePos: Int) {
|
||||||
mpd_run_play_pos(self.connection, UInt32(queuePos))
|
mpd_run_play_pos(self.connection, UInt32(queuePos))
|
||||||
}
|
}
|
||||||
@ -40,4 +44,8 @@ extension MPDClient {
|
|||||||
}
|
}
|
||||||
mpd_run_play_pos(self.connection, 0)
|
mpd_run_play_pos(self.connection, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendAppendSong(_ song: MPDSong) {
|
||||||
|
mpd_run_add(self.connection, song.uri)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,7 @@ extension MPDClient {
|
|||||||
case fetchQueue
|
case fetchQueue
|
||||||
case playTrack
|
case playTrack
|
||||||
case replaceQueue
|
case replaceQueue
|
||||||
|
case appendSong
|
||||||
|
|
||||||
// Album commands
|
// Album commands
|
||||||
case fetchAllAlbums
|
case fetchAllAlbums
|
||||||
|
|||||||
@ -59,6 +59,9 @@
|
|||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="playAlbum:" target="-2" id="LTw-Lg-yH2"/>
|
||||||
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BOb-Lr-10M">
|
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BOb-Lr-10M">
|
||||||
<rect key="frame" x="359" y="33" width="444" height="418"/>
|
<rect key="frame" x="359" y="33" width="444" height="418"/>
|
||||||
@ -209,6 +212,9 @@
|
|||||||
</prototypeCellViews>
|
</prototypeCellViews>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
</tableColumns>
|
</tableColumns>
|
||||||
|
<connections>
|
||||||
|
<action trigger="doubleAction" selector="playSong:" target="-2" id="HmG-Nf-n4c"/>
|
||||||
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<nil key="backgroundColor"/>
|
<nil key="backgroundColor"/>
|
||||||
|
|||||||
@ -51,20 +51,6 @@
|
|||||||
<action selector="showAlbumDetail:" target="-2" id="nO1-4H-LHS"/>
|
<action selector="showAlbumDetail:" target="-2" id="nO1-4H-LHS"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="n8W-do-HyG">
|
|
||||||
<rect key="frame" x="43" y="81" width="42" height="43"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="42" id="XXC-YE-Ego"/>
|
|
||||||
<constraint firstAttribute="width" constant="42" id="zcR-GT-zym"/>
|
|
||||||
</constraints>
|
|
||||||
<buttonCell key="cell" type="inline" bezelStyle="inline" image="playButtonLarge" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" inset="2" id="T1p-LZ-RpJ">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="smallSystemBold"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="playAlbum:" target="-2" id="gNt-Rn-kte"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="x5e-56-uVO" firstAttribute="leading" secondItem="Kfb-8f-ean" secondAttribute="leading" id="1Hi-Uk-rkL"/>
|
<constraint firstItem="x5e-56-uVO" firstAttribute="leading" secondItem="Kfb-8f-ean" secondAttribute="leading" id="1Hi-Uk-rkL"/>
|
||||||
@ -73,7 +59,6 @@
|
|||||||
<constraint firstItem="x5e-56-uVO" firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="BYd-Fg-DVb"/>
|
<constraint firstItem="x5e-56-uVO" firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="BYd-Fg-DVb"/>
|
||||||
<constraint firstItem="Kfb-8f-ean" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="JMi-4i-dgs"/>
|
<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 firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="KQC-Wz-Bsg"/>
|
||||||
<constraint firstItem="n8W-do-HyG" firstAttribute="centerX" secondItem="Kfb-8f-ean" secondAttribute="centerX" id="Kf1-ws-d4q"/>
|
|
||||||
<constraint firstItem="5Uu-j1-qyT" firstAttribute="leading" secondItem="KEh-NL-c2W" secondAttribute="leading" id="MUo-0i-fX9"/>
|
<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="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="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="U0w-G4-ggX"/>
|
||||||
@ -81,12 +66,10 @@
|
|||||||
<constraint firstAttribute="bottom" secondItem="5Uu-j1-qyT" secondAttribute="bottom" id="gci-4h-pDZ"/>
|
<constraint firstAttribute="bottom" secondItem="5Uu-j1-qyT" secondAttribute="bottom" id="gci-4h-pDZ"/>
|
||||||
<constraint firstItem="x5e-56-uVO" firstAttribute="top" secondItem="Kfb-8f-ean" secondAttribute="top" id="hw2-ik-6VW"/>
|
<constraint firstItem="x5e-56-uVO" firstAttribute="top" secondItem="Kfb-8f-ean" secondAttribute="top" id="hw2-ik-6VW"/>
|
||||||
<constraint firstItem="x5e-56-uVO" firstAttribute="bottom" secondItem="Kfb-8f-ean" secondAttribute="bottom" id="iVQ-Vn-dSV"/>
|
<constraint firstItem="x5e-56-uVO" firstAttribute="bottom" secondItem="Kfb-8f-ean" secondAttribute="bottom" id="iVQ-Vn-dSV"/>
|
||||||
<constraint firstItem="n8W-do-HyG" firstAttribute="centerY" secondItem="Kfb-8f-ean" secondAttribute="centerY" id="pgP-oA-Nxa"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="Kfb-8f-ean" secondAttribute="bottom" constant="39" id="sid-zJ-YMA"/>
|
<constraint firstAttribute="bottom" secondItem="Kfb-8f-ean" secondAttribute="bottom" constant="39" id="sid-zJ-YMA"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="imageView" destination="Kfb-8f-ean" id="T7Z-En-dU3"/>
|
<outlet property="imageView" destination="Kfb-8f-ean" id="T7Z-En-dU3"/>
|
||||||
<outlet property="playButton" destination="n8W-do-HyG" id="Xw0-iI-svx"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="-22" y="125.5"/>
|
<point key="canvasLocation" x="-22" y="125.5"/>
|
||||||
</customView>
|
</customView>
|
||||||
@ -94,6 +77,5 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="defaultCoverArt" width="128" height="128"/>
|
<image name="defaultCoverArt" width="128" height="128"/>
|
||||||
<image name="playButtonLarge" width="22" height="22"/>
|
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -16,6 +16,10 @@ struct MPDStopAction: Action {}
|
|||||||
struct MPDNextTrackAction: Action {}
|
struct MPDNextTrackAction: Action {}
|
||||||
struct MPDPrevTrackAction: Action {}
|
struct MPDPrevTrackAction: Action {}
|
||||||
|
|
||||||
|
struct MPDAppendTrack: Action {
|
||||||
|
let song: MPDClient.MPDSong
|
||||||
|
}
|
||||||
|
|
||||||
struct MPDPlayTrack: Action {
|
struct MPDPlayTrack: Action {
|
||||||
let queuePos: Int
|
let queuePos: Int
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,9 @@ func mpdReducer(action: Action, state: MPDState?) -> MPDState {
|
|||||||
case is MPDPrevTrackAction:
|
case is MPDPrevTrackAction:
|
||||||
App.mpdClient.prevTrack()
|
App.mpdClient.prevTrack()
|
||||||
|
|
||||||
|
case let action as MPDAppendTrack:
|
||||||
|
App.mpdClient.appendSong(action.song)
|
||||||
|
|
||||||
case let action as MPDPlayTrack:
|
case let action as MPDPlayTrack:
|
||||||
App.mpdClient.playTrack(at: action.queuePos)
|
App.mpdClient.playTrack(at: action.queuePos)
|
||||||
|
|
||||||
|
|||||||
@ -9,80 +9,9 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
class AlbumItemView: NSView {
|
class AlbumItemView: NSView {
|
||||||
var trackingArea: NSTrackingArea?
|
|
||||||
|
|
||||||
override func updateTrackingAreas() {
|
|
||||||
super.updateTrackingAreas()
|
|
||||||
|
|
||||||
guard let albumImageView = imageView else { return }
|
|
||||||
|
|
||||||
if let trackingArea = self.trackingArea {
|
|
||||||
self.removeTrackingArea(trackingArea)
|
|
||||||
}
|
|
||||||
|
|
||||||
let trackingArea = NSTrackingArea(
|
|
||||||
rect: albumImageView.frame,
|
|
||||||
options: [.mouseEnteredAndExited, .activeAlways],
|
|
||||||
owner: self,
|
|
||||||
userInfo: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
self.trackingArea = trackingArea
|
|
||||||
addTrackingArea(trackingArea)
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder decoder: NSCoder) {
|
required init?(coder decoder: NSCoder) {
|
||||||
super.init(coder: decoder)
|
super.init(coder: decoder)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(viewWillScroll(_:)),
|
|
||||||
name: NSScrollView.willStartLiveScrollNotification,
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(viewDidScroll(_:)),
|
|
||||||
name: NSScrollView.didLiveScrollNotification,
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForReuse() {
|
|
||||||
super.prepareForReuse()
|
|
||||||
|
|
||||||
hidePlayButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func viewWillScroll(_ notification: Notification) {
|
|
||||||
hidePlayButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func viewDidScroll(_ notification: Notification) {
|
|
||||||
hidePlayButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func resize(withOldSuperviewSize oldSize: NSSize) {
|
|
||||||
hidePlayButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func mouseEntered(with event: NSEvent) {
|
|
||||||
showPlayButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func mouseExited(with event: NSEvent) {
|
|
||||||
hidePlayButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
func showPlayButton() {
|
|
||||||
playButton.isHidden = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func hidePlayButton() {
|
|
||||||
playButton.isHidden = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet var imageView: NSImageView!
|
@IBOutlet var imageView: NSImageView!
|
||||||
@IBOutlet var playButton: NSButton!
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user