diff --git a/README.md b/README.md index 34712ce8..7318abd0 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,9 @@ The Subsonic API is enabled by default. __Note__: since _LMS_ stores hashed and salted passwords, it cannot handle the __token authentication__ method defined from version 1.13.0. ## Keyboard shortcuts -* Play/pause: Space -* Previous track: Ctrl + Left -* Next track: Ctrl + Right +* Play/pause: Space +* Previous track: Ctrl + Left +* Next track: Ctrl + Right ## Installation diff --git a/approot/artist.xml b/approot/artist.xml index 8534eb07..3e7bca0a 100644 --- a/approot/artist.xml +++ b/approot/artist.xml @@ -6,18 +6,20 @@

${name}

${clusters} - ${play-btn}${add-btn} + ${play-btn}${more-btn}
${releases class="Lms-explore-artist-entries-container"}
+ ${}

${tr:Lms.Explore.Artist.similar-artists}

${similar-artists}
+ ${
} ${}
diff --git a/approot/explore.xml b/approot/explore.xml index 2cd86c7c..80881197 100644 --- a/approot/explore.xml +++ b/approot/explore.xml @@ -5,7 +5,7 @@ ${contents class="Lms-explore-contents"} - + diff --git a/approot/messages.xml b/approot/messages.xml index 88354bf4..146afb5e 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -121,6 +121,8 @@ Most played MusicBrainz Artist MusicBrainz Release +Play last +Play shuffled Random Recently added Recently played @@ -157,11 +159,8 @@ Added {1} track Added {1} tracks - - Playing {1} track - Playing {1} tracks - Play Queue +Play Queue full! Radio mode Repeat Shuffle diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml index 88b5c4d6..11dc324e 100644 --- a/approot/messages_fr.xml +++ b/approot/messages_fr.xml @@ -121,6 +121,8 @@ Plus joués MusicBrainz Artist MusicBrainz Release +Jouer en dernier +Jouer aléatoirement Aléatoire Ajoutés récemment Joués récemment @@ -157,11 +159,8 @@ {1} piste ajoutée {1} pistes ajoutées - - {1} piste ajoutée - {1} pistes ajoutées - Liste de lecture +Liste de lecture pleine! Mode radio Repéter Mélanger diff --git a/approot/release.xml b/approot/release.xml index 86e6d9c2..cb49ffd7 100644 --- a/approot/release.xml +++ b/approot/release.xml @@ -17,12 +17,13 @@ ${}

${artist class="Lms-artistname text-muted"}

${
}
${}${year}${}${} (${orig-year})${}
${clusters} - ${play-btn}${add-btn} + ${play-btn}${more-btn}
${container}
+ ${}

${tr:Lms.Explore.Release.similar-releases}

@@ -31,6 +32,7 @@
+ ${} ${}
@@ -66,7 +68,7 @@
- ${play-btn}${add-btn} + ${play-btn}${more-btn}
${duration} diff --git a/approot/releases.xml b/approot/releases.xml index 288ac0a0..430e48dd 100644 --- a/approot/releases.xml +++ b/approot/releases.xml @@ -6,7 +6,7 @@

${tr:Lms.Explore.releases}

${mode class="nav nav-pills Lms-explore-mode-container"} - ${play-btn}${add-btn} + ${play-btn}${more-btn}
${releases} diff --git a/approot/tracks.xml b/approot/tracks.xml index ff664056..4c34bab7 100644 --- a/approot/tracks.xml +++ b/approot/tracks.xml @@ -9,7 +9,7 @@

${tr:Lms.Explore.tracks}

${mode class="nav nav-pills Lms-explore-mode-container"} - ${play-btn}${add-btn} + ${play-btn}${more-btn}
${tracks} ${loading-indicator class="Lms-horizontal-center Lms-loading-indicator"} @@ -33,7 +33,7 @@
- ${play-btn}${add-btn} + ${play-btn}${more-btn}
${duration} diff --git a/src/libs/av/impl/AvTranscodeResourceHandler.hpp b/src/libs/av/impl/AvTranscodeResourceHandler.hpp index 351e6e45..543ca74b 100644 --- a/src/libs/av/impl/AvTranscodeResourceHandler.hpp +++ b/src/libs/av/impl/AvTranscodeResourceHandler.hpp @@ -39,7 +39,6 @@ namespace Av static constexpr std::size_t _chunkSize {262144}; const std::filesystem::path _trackPath; Transcoder _transcoder; - }; } diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt index 269986c3..88905053 100644 --- a/src/lms/CMakeLists.txt +++ b/src/lms/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(lms ui/explore/Explore.cpp ui/explore/Filters.cpp ui/explore/ReleaseListHelpers.cpp + ui/explore/ReleasePopup.cpp ui/explore/ReleasesView.cpp ui/explore/ReleaseView.cpp ui/explore/SearchView.cpp diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 3b6edf29..de5ad4c3 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -35,7 +35,6 @@ #include "database/User.hpp" #include "explore/Explore.hpp" #include "explore/Filters.hpp" -#include "explore/SearchView.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" #include "utils/String.hpp" @@ -308,6 +307,13 @@ LmsApplication::createCluster(Database::Cluster::pointer cluster, bool canDelete return res; } +Wt::WPopupMenu* +LmsApplication::createPopupMenu() +{ + _popupMenu = std::make_unique(); + return _popupMenu.get(); +} + void LmsApplication::handleException(LmsApplicationException& e) { @@ -334,7 +340,6 @@ enum IdxRoot { IdxExplore = 0, IdxPlayQueue, - IdxSearch, IdxSettings, IdxAdminDatabase, IdxAdminUsers, @@ -355,9 +360,9 @@ handlePathChange(Wt::WStackedWidget* stack, bool isAdmin) { "/artist", IdxExplore, false }, { "/releases", IdxExplore, false }, { "/release", IdxExplore, false }, + { "/search", IdxExplore, false }, { "/tracks", IdxExplore, false }, { "/playqueue", IdxPlayQueue, false }, - { "/search", IdxSearch, false }, { "/settings", IdxSettings, false }, { "/admin/database", IdxAdminDatabase, true }, { "/admin/users", IdxAdminUsers, true }, @@ -471,7 +476,6 @@ LmsApplication::createHome() Explore* explore = mainStack->addNew(filters); _playQueue = mainStack->addNew(); - auto* search {mainStack->addNew(filters)}; mainStack->addNew(); searchEdit->enterPressed().connect([=] @@ -482,7 +486,7 @@ LmsApplication::createHome() searchEdit->textInput().connect([=] { setInternalPath("/search", true); - search->refreshView(searchEdit->text().toUTF8()); + explore->search(searchEdit->text()); }); // Admin stuff diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index ad0945ef..9cf31760 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -22,6 +22,7 @@ #include #include +#include #include "database/Db.hpp" #include "database/Session.hpp" @@ -101,6 +102,7 @@ class LmsApplication : public Wt::WApplication static Wt::WLink createReleaseLink(Wt::Dbo::ptr release); static std::unique_ptr createReleaseAnchor(Wt::Dbo::ptr release, bool addText = true); static std::unique_ptr createCluster(Wt::Dbo::ptr cluster, bool canDelete = false); + Wt::WPopupMenu* createPopupMenu(); MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; } PlayQueue& getPlayQueue() const { return *_playQueue; } @@ -124,17 +126,18 @@ class LmsApplication : public Wt::WApplication void createHome(); - Wt::Signal<> _preQuit; - Database::Session _dbSession; - LmsApplicationGroupContainer& _appGroups; - Events _events; - std::optional _userId; - std::optional _userAuthStrong; + Wt::Signal<> _preQuit; + Database::Session _dbSession; + LmsApplicationGroupContainer& _appGroups; + Events _events; + std::optional _userId; + std::optional _userAuthStrong; std::shared_ptr _audioTranscodeResource; - std::shared_ptr _audioFileResource; - std::shared_ptr _imageResource; - MediaPlayer* _mediaPlayer {}; - PlayQueue* _playQueue {}; + std::shared_ptr _audioFileResource; + std::shared_ptr _imageResource; + MediaPlayer* _mediaPlayer {}; + PlayQueue* _playQueue {}; + std::unique_ptr _popupMenu {}; }; diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index ccfd5486..ab3d06da 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -209,6 +209,13 @@ PlayQueue::getTrackList() const return Database::TrackList::getById(LmsApp->getDbSession(), _tracklistId); } +bool +PlayQueue::isFull() const +{ + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + return getTrackList()->getCount() == _nbMaxEntries; +} + void PlayQueue::clearTracks() { @@ -321,55 +328,77 @@ PlayQueue::updateCurrentTrack(bool selected) entry->bindString("is-selected", selected ? "Lms-playqueue-selected" : ""); } -void +std::size_t PlayQueue::enqueueTracks(const std::vector& trackIds) { + std::size_t nbTracksQueued {}; + { auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; - auto tracklist = getTrackList(); + auto tracklist {getTrackList()}; + + std::size_t nbTracksToEnqueue {tracklist->getCount() + trackIds.size() > _nbMaxEntries ? _nbMaxEntries - tracklist->getCount() : trackIds.size()}; for (Database::IdType trackId : trackIds) { Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; if (!track) continue; + if (nbTracksQueued == nbTracksToEnqueue) + break; + Database::TrackListEntry::create(LmsApp->getDbSession(), track, tracklist); + nbTracksQueued++; } } updateInfo(); addSome(); -} -void -PlayQueue::enqueueTrack(Database::IdType trackId) -{ - enqueueTracks({trackId}); + return nbTracksQueued; } void PlayQueue::processTracks(PlayQueueAction action, const std::vector& trackIds) { + std::size_t nbAddedTracks {}; + switch (action) { - case PlayQueueAction::AddLast: - enqueueTracks(trackIds); - LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", trackIds.size()).arg(trackIds.size()), std::chrono::milliseconds(2000)); - break; - - case PlayQueueAction::AddNext: + case PlayQueueAction::PlayLast: + nbAddedTracks = enqueueTracks(trackIds); + if (!_trackPos) + loadTrack(0, true); break; case PlayQueueAction::Play: clearTracks(); - enqueueTracks(trackIds); + nbAddedTracks = enqueueTracks(trackIds); loadTrack(0, true); - LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-playing", trackIds.size()).arg(trackIds.size()), std::chrono::milliseconds(2000)); break; + case PlayQueueAction::PlayShuffled: + { + clearTracks(); + { + std::vector shuffledTrackIds {trackIds}; + Random::shuffleContainer(shuffledTrackIds); + nbAddedTracks = enqueueTracks(shuffledTrackIds); + } + loadTrack(0, true); + break; + } } + + if (nbAddedTracks > 0) + LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", nbAddedTracks).arg(nbAddedTracks), std::chrono::milliseconds(2000)); + + if (isFull()) + LmsApp->notifyMsg(MsgType::Warning, Wt::WString::tr("Lms.PlayQueue.playqueue-full"), std::chrono::milliseconds(2000)); + + } void diff --git a/src/lms/ui/PlayQueue.hpp b/src/lms/ui/PlayQueue.hpp index 211ef960..8cfae3e6 100644 --- a/src/lms/ui/PlayQueue.hpp +++ b/src/lms/ui/PlayQueue.hpp @@ -60,10 +60,10 @@ class PlayQueue : public Wt::WTemplate private: Wt::Dbo::ptr getTrackList() const; + bool isFull() const; void clearTracks(); - void enqueueTracks(const std::vector& trackIds); - void enqueueTrack(Database::IdType trackId); + std::size_t enqueueTracks(const std::vector& trackIds); void addSome(); void enqueueRadioTrack(); void updateInfo(); @@ -80,6 +80,8 @@ class PlayQueue : public Wt::WTemplate void addRadioTrackFromClusters(); std::optional getReplayGain(std::size_t pos, const Wt::Dbo::ptr& track) const; + static inline constexpr std::size_t _nbMaxEntries {1000}; + bool _repeatAll {}; bool _radioMode {}; bool _mediaPlayerSettingsLoaded {}; diff --git a/src/lms/ui/PlayQueueAction.hpp b/src/lms/ui/PlayQueueAction.hpp index 2892537b..29a91351 100644 --- a/src/lms/ui/PlayQueueAction.hpp +++ b/src/lms/ui/PlayQueueAction.hpp @@ -19,15 +19,22 @@ #pragma once +#include +#include + +#include "database/Types.hpp" + namespace UserInterface { enum class PlayQueueAction { Play, - AddNext, - AddLast, + PlayLast, + PlayShuffled, }; + using PlayQueueActionSignal = Wt::Signal&>; + } diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index bdbe5602..082f0634 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -115,11 +115,24 @@ Artist::refreshView() } { - Wt::WText* addBtn = bindNew("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML); + Wt::WText* moreBtn = bindNew("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML); - addBtn->clicked().connect([=] + moreBtn->clicked().connect([=] { - artistsAction.emit(PlayQueueAction::AddLast, {*artistId}); + Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; + + popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled")) + ->triggered().connect(this, [=] + { + artistsAction.emit(PlayQueueAction::PlayShuffled, {*artistId}); + }); + popup->addItem(Wt::WString::tr("Lms.Explore.play-last")) + ->triggered().connect(this, [=] + { + artistsAction.emit(PlayQueueAction::PlayLast, {*artistId}); + }); + + popup->exec(moreBtn); }); } @@ -135,6 +148,10 @@ Artist::refreshView() void Artist::refreshSimilarArtists(const std::vector& similarArtistsId) { + if (similarArtistsId.empty()) + return; + + setCondition("if-has-similar-artists", true); Wt::WContainerWidget* similarArtistsContainer {bindNew("similar-artists")}; for (Database::IdType artistId : similarArtistsId) diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index 944c9504..54639412 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -42,7 +42,7 @@ class Artist : public Wt::WTemplate public: Artist(Filters* filters); - Wt::Signal&> artistsAction; + PlayQueueActionSignal artistsAction; private: void refreshView(); diff --git a/src/lms/ui/explore/Explore.cpp b/src/lms/ui/explore/Explore.cpp index 463ea8fd..6e89977d 100644 --- a/src/lms/ui/explore/Explore.cpp +++ b/src/lms/ui/explore/Explore.cpp @@ -35,6 +35,7 @@ #include "Filters.hpp" #include "ReleasesView.hpp" #include "ReleaseView.hpp" +#include "SearchView.hpp" #include "TracksView.hpp" namespace UserInterface { @@ -50,6 +51,7 @@ handleContentsPathChange(Wt::WStackedWidget* stack) IdxArtist, IdxReleases, IdxRelease, + IdxSearch, IdxTracks, }; @@ -59,6 +61,7 @@ handleContentsPathChange(Wt::WStackedWidget* stack) { "/artist", IdxArtist }, { "/releases", IdxReleases }, { "/release", IdxRelease }, + { "/search", IdxSearch }, { "/tracks", IdxTracks }, }; @@ -100,6 +103,11 @@ Explore::Explore(Filters* filters) release->tracksAction.connect(this, &Explore::handleTracksAction); contentsStack->addWidget(std::move(release)); + auto search = std::make_unique(_filters); + search->tracksAction.connect(this, &Explore::handleTracksAction); + _search = search.get(); + contentsStack->addWidget(std::move(search)); + auto tracks = std::make_unique(_filters); tracks->tracksAction.connect(this, &Explore::handleTracksAction); contentsStack->addWidget(std::move(tracks)); @@ -112,6 +120,12 @@ Explore::Explore(Filters* filters) handleContentsPathChange(contentsStack); } +void +Explore::search(const Wt::WString& searchText) +{ + _search->refreshView(searchText); +} + static std::vector getArtistsTracks(Database::Session& session, const std::vector& artistsId, const std::set&) diff --git a/src/lms/ui/explore/Explore.hpp b/src/lms/ui/explore/Explore.hpp index 7f476f64..2db65540 100644 --- a/src/lms/ui/explore/Explore.hpp +++ b/src/lms/ui/explore/Explore.hpp @@ -27,13 +27,16 @@ namespace UserInterface { class Filters; +class SearchView; class Explore : public Wt::WTemplate { public: Explore(Filters* filters); - Wt::Signal&> tracksAction; + void search(const Wt::WString& searchText); + + PlayQueueActionSignal tracksAction; private: @@ -42,6 +45,7 @@ class Explore : public Wt::WTemplate void handleTracksAction(PlayQueueAction action, const std::vector& tracksId); Filters* _filters {}; + SearchView* _search {}; }; } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp new file mode 100644 index 00000000..7ce4d099 --- /dev/null +++ b/src/lms/ui/explore/ReleasePopup.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include "ReleasePopup.hpp" + +#include "LmsApplication.hpp" + +namespace UserInterface +{ + + void + displayReleasePopupMenu(Wt::WInteractWidget& target, + Database::IdType releaseId, + PlayQueueActionSignal& releasesAction) + { + Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; + + popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled")) + ->triggered().connect(&target, [&releasesAction, releaseId] + { + releasesAction.emit(PlayQueueAction::PlayShuffled, {releaseId}); + }); + popup->addItem(Wt::WString::tr("Lms.Explore.play-last")) + ->triggered().connect(&target, [&releasesAction, releaseId] + { + releasesAction.emit(PlayQueueAction::PlayLast, {releaseId}); + }); + + popup->popup(&target); + } + +} // namespace UserInterface + diff --git a/src/lms/ui/explore/ReleasePopup.hpp b/src/lms/ui/explore/ReleasePopup.hpp new file mode 100644 index 00000000..23e576b2 --- /dev/null +++ b/src/lms/ui/explore/ReleasePopup.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include + +#include "database/Types.hpp" +#include "PlayQueueAction.hpp" + +namespace UserInterface +{ + void displayReleasePopupMenu(Wt::WInteractWidget& target, + Database::IdType releaseId, + PlayQueueActionSignal& releasesAction); +} // namespace UserInterface + diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index e19f83f9..aa2d4350 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -39,6 +38,7 @@ #include "LmsApplicationException.hpp" #include "MediaPlayer.hpp" #include "ReleaseListHelpers.hpp" +#include "ReleasePopup.hpp" #include "TrackStringUtils.hpp" using namespace Database; @@ -157,10 +157,10 @@ Release::refreshView() } { - Wt::WText* addBtn {bindNew("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)}; - addBtn->clicked().connect([=] + Wt::WText* moreBtn {bindNew("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML)}; + moreBtn->clicked().connect([=] { - releasesAction.emit(PlayQueueAction::AddLast, {*releaseId}); + displayReleasePopupMenu(*moreBtn, *releaseId, releasesAction); }); } @@ -237,10 +237,18 @@ Release::refreshView() tracksAction.emit(PlayQueueAction::Play, {trackId}); }); - Wt::WText* addBtn {entry->bindNew("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)}; - addBtn->clicked().connect([=]() + Wt::WText* moreBtn {entry->bindNew("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML)}; + moreBtn->clicked().connect([=]() { - tracksAction.emit(PlayQueueAction::AddLast, {trackId}); + Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; + + popup->addItem(Wt::WString::tr("Lms.Explore.play-last")) + ->triggered().connect(moreBtn, [=] + { + tracksAction.emit(PlayQueueAction::PlayLast, {trackId}); + }); + + popup->popup(moreBtn); }); entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain); @@ -306,6 +314,10 @@ Release::refreshLinks(const Database::Release::pointer& release) void Release::refreshSimilarReleases(const std::vector& similarReleasesId) { + if (similarReleasesId.empty()) + return; + + setCondition("if-has-similar-releases", true); auto* similarReleasesContainer {bindNew("similar-releases")}; for (Database::IdType id : similarReleasesId) diff --git a/src/lms/ui/explore/ReleaseView.hpp b/src/lms/ui/explore/ReleaseView.hpp index 5f1db58e..7908c405 100644 --- a/src/lms/ui/explore/ReleaseView.hpp +++ b/src/lms/ui/explore/ReleaseView.hpp @@ -19,7 +19,6 @@ #pragma once -#include #include #include "database/Types.hpp" @@ -39,11 +38,8 @@ namespace UserInterface public: Release(Filters* filters); - Wt::Signal&> releasesAction; - Wt::Signal&> tracksAction; - - Wt::Signal createReleaseActionMenu; - Wt::Signal createTrackActionMenu; + PlayQueueActionSignal releasesAction; + PlayQueueActionSignal tracksAction; private: void refreshView(); diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index e21bb0f5..dd713fef 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -72,10 +72,23 @@ _filters {filters} { releasesAction.emit(PlayQueueAction::Play, getAllReleases()); }); - Wt::WText* addBtn {bindNew("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)}; - addBtn->clicked().connect([this] + Wt::WText* moreBtn {bindNew("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML)}; + moreBtn->clicked().connect([=] { - releasesAction.emit(PlayQueueAction::AddLast, getAllReleases()); + Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; + + popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled")) + ->triggered().connect([this] + { + releasesAction.emit(PlayQueueAction::PlayShuffled, getAllReleases()); + }); + popup->addItem(Wt::WString::tr("Lms.Explore.play-last")) + ->triggered().connect([this] + { + releasesAction.emit(PlayQueueAction::PlayLast, getAllReleases()); + }); + + popup->popup(moreBtn); }); _container = bindNew("releases"); diff --git a/src/lms/ui/explore/ReleasesView.hpp b/src/lms/ui/explore/ReleasesView.hpp index 6cc8a67b..479002c3 100644 --- a/src/lms/ui/explore/ReleasesView.hpp +++ b/src/lms/ui/explore/ReleasesView.hpp @@ -41,7 +41,7 @@ class Releases : public Wt::WTemplate public: Releases(Filters* filters); - Wt::Signal&> releasesAction; + PlayQueueActionSignal releasesAction; private: diff --git a/src/lms/ui/explore/SearchView.cpp b/src/lms/ui/explore/SearchView.cpp index 6f37b4c6..bd8a8e09 100644 --- a/src/lms/ui/explore/SearchView.cpp +++ b/src/lms/ui/explore/SearchView.cpp @@ -51,9 +51,9 @@ namespace UserInterface } void - SearchView::refreshView(const std::string& searchText) + SearchView::refreshView(const Wt::WString& searchText) { - _keywords = StringUtils::splitString(searchText, " "); + _keywords = StringUtils::splitString(searchText.toUTF8(), " "); refreshView(); } @@ -127,7 +127,7 @@ namespace UserInterface auto* container {bindNew("tracks")}; for (const Database::Track::pointer& track : tracks) - container->addWidget(TrackListHelpers::createEntry(track)); + container->addWidget(TrackListHelpers::createEntry(track, tracksAction)); } } diff --git a/src/lms/ui/explore/SearchView.hpp b/src/lms/ui/explore/SearchView.hpp index a7b2460a..8711547d 100644 --- a/src/lms/ui/explore/SearchView.hpp +++ b/src/lms/ui/explore/SearchView.hpp @@ -24,6 +24,8 @@ #include +#include "PlayQueueAction.hpp" + namespace UserInterface { class Filters; @@ -33,7 +35,9 @@ class SearchView : public Wt::WTemplate public: SearchView(Filters* filters); - void refreshView(const std::string& searchText); + PlayQueueActionSignal tracksAction; + + void refreshView(const Wt::WString& searchText); private: void refreshView(); diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index 3d4ec648..649f92d5 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -30,7 +30,6 @@ #include "resource/ImageResource.hpp" #include "LmsApplication.hpp" #include "MediaPlayer.hpp" -#include "PlayQueue.hpp" #include "TrackStringUtils.hpp" using namespace Database; @@ -38,7 +37,7 @@ using namespace Database; namespace UserInterface::TrackListHelpers { std::unique_ptr - createEntry(const Wt::Dbo::ptr& track) + createEntry(const Wt::Dbo::ptr& track, PlayQueueActionSignal& tracksAction) { auto entry {std::make_unique(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))}; auto* entryPtr {entry.get()}; @@ -89,15 +88,23 @@ namespace UserInterface::TrackListHelpers entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain); Wt::WText* playBtn = entry->bindNew("play-btn", Wt::WString::tr("Lms.Explore.template.play-btn"), Wt::TextFormat::XHTML); - playBtn->clicked().connect([=] + playBtn->clicked().connect([trackId, &tracksAction] { - LmsApp->getPlayQueue().processTracks(PlayQueueAction::Play, {trackId}); + tracksAction.emit(PlayQueueAction::Play, {trackId}); }); - Wt::WText* addBtn = entry->bindNew("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML); - addBtn->clicked().connect([=] + Wt::WText* moreBtn = entry->bindNew("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML); + moreBtn->clicked().connect([=, &tracksAction] { - LmsApp->getPlayQueue().processTracks(PlayQueueAction::AddLast, {trackId}); + Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; + + popup->addItem(Wt::WString::tr("Lms.Explore.play-last")) + ->triggered().connect(moreBtn, [=, &tracksAction] + { + tracksAction.emit(PlayQueueAction::PlayLast, {trackId}); + }); + + popup->popup(moreBtn); }); LmsApp->getMediaPlayer().trackLoaded.connect(entryPtr, [=] (Database::IdType loadedTrackId) diff --git a/src/lms/ui/explore/TrackListHelpers.hpp b/src/lms/ui/explore/TrackListHelpers.hpp index 2766e3e8..32ac5b12 100644 --- a/src/lms/ui/explore/TrackListHelpers.hpp +++ b/src/lms/ui/explore/TrackListHelpers.hpp @@ -22,6 +22,7 @@ #include #include +#include "PlayQueueAction.hpp" namespace Database { @@ -30,6 +31,6 @@ namespace Database namespace UserInterface::TrackListHelpers { - std::unique_ptr createEntry(const Wt::Dbo::ptr& track); + std::unique_ptr createEntry(const Wt::Dbo::ptr& track, PlayQueueActionSignal& tracksAction); } // namespace UserInterface diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index 640ab4df..c9451d85 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -32,9 +32,8 @@ #include "utils/Logger.hpp" #include "utils/String.hpp" -#include "resource/ImageResource.hpp" - #include "common/LoadingIndicator.hpp" +#include "resource/ImageResource.hpp" #include "Filters.hpp" #include "LmsApplication.hpp" #include "MediaPlayer.hpp" @@ -76,10 +75,23 @@ _filters {filters} tracksAction.emit(PlayQueueAction::Play, getAllTracks()); }); - Wt::WText* addBtn = bindNew("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML); - addBtn->clicked().connect([=] + Wt::WText* moreBtn = bindNew("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML); + moreBtn->clicked().connect([=] { - tracksAction.emit(PlayQueueAction::AddLast, getAllTracks()); + Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; + + popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled")) + ->triggered().connect(this, [this] + { + tracksAction.emit(PlayQueueAction::PlayShuffled, getAllTracks()); + }); + popup->addItem(Wt::WString::tr("Lms.Explore.play-last")) + ->triggered().connect(this, [this] + { + tracksAction.emit(PlayQueueAction::PlayLast, getAllTracks()); + }); + + popup->popup(moreBtn); }); _tracksContainer = bindNew("tracks"); @@ -219,7 +231,7 @@ Tracks::addSome() bool moreResults; for (const Track::pointer& track : getTracks(Range {static_cast(_tracksContainer->count()), batchSize}, moreResults)) { - _tracksContainer->addWidget(TrackListHelpers::createEntry(track)); + _tracksContainer->addWidget(TrackListHelpers::createEntry(track, tracksAction)); } if (moreResults) diff --git a/src/lms/ui/explore/TracksView.hpp b/src/lms/ui/explore/TracksView.hpp index 771292d7..d66f3b14 100644 --- a/src/lms/ui/explore/TracksView.hpp +++ b/src/lms/ui/explore/TracksView.hpp @@ -40,7 +40,7 @@ class Tracks : public Wt::WTemplate public: Tracks(Filters* filters); - Wt::Signal&> tracksAction; + PlayQueueActionSignal tracksAction; private: