Changed the add button to a contextual menu

This commit is contained in:
emeric
2020-08-20 13:10:54 +02:00
parent 09f5ea5b0f
commit bc87e16d08
31 changed files with 304 additions and 94 deletions
+3 -3
View File
@@ -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: <kbd>Space</bbd>
* Previous track: <kbd>Ctrl</kbd> + <kbd>Left</kbd>
* Next track: <kbd>Ctrl</kbd> + <kbd>Right</kbd>
## Installation
+3 -1
View File
@@ -6,18 +6,20 @@
<div class="Lms-header">
<h2><div class="Lms-artistname text-primary">${name}</div></h2>
${clusters}
${play-btn}${add-btn}
${play-btn}${more-btn}
</div>
<div class="row">
${releases class="Lms-explore-artist-entries-container"}
</div>
<div class="row">
${<if-has-similar-artists>}
<div class="col-md-4">
<div class="well well-sm">
<h3>${tr:Lms.Explore.Artist.similar-artists}</h3>
${similar-artists}
</div>
</div>
${</if-has-similar-artists>}
${<if-has-mbid>}
<div class="col-md-4">
<div class="well well-sm">
+1 -1
View File
@@ -5,7 +5,7 @@
${contents class="Lms-explore-contents"}
</message>
<message id="Lms.Explore.template.add-btn"><i class="fa fa-plus-circle Lms-explore-btn Lms-control-btn"></i></message>
<message id="Lms.Explore.template.more-btn"><i class="fa fa-plus-circle Lms-explore-btn Lms-control-btn"></i></message>
<message id="Lms.Explore.template.play-btn"><i class="fa fa-play-circle Lms-explore-btn Lms-control-btn"></i></message>
<message id="Lms.Explore.template.filters">
+3 -4
View File
@@ -121,6 +121,8 @@
<message id="Lms.Explore.most-played">Most played</message>
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
<message id="Lms.Explore.play-last">Play last</message>
<message id="Lms.Explore.play-shuffled">Play shuffled</message>
<message id="Lms.Explore.random">Random</message>
<message id="Lms.Explore.recently-added">Recently added</message>
<message id="Lms.Explore.recently-played">Recently played</message>
@@ -157,11 +159,8 @@
<plural case="0">Added {1} track</plural>
<plural case="1">Added {1} tracks</plural>
</message>
<message id="Lms.PlayQueue.nb-tracks-playing">
<plural case="0">Playing {1} track</plural>
<plural case="1">Playing {1} tracks</plural>
</message>
<message id="Lms.PlayQueue.playqueue">Play Queue</message>
<message id="Lms.PlayQueue.playqueue-full">Play Queue full!</message>
<message id="Lms.PlayQueue.radio-mode">Radio mode</message>
<message id="Lms.PlayQueue.repeat">Repeat</message>
<message id="Lms.PlayQueue.shuffle">Shuffle</message>
+3 -4
View File
@@ -121,6 +121,8 @@
<message id="Lms.Explore.most-played">Plus joués</message>
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
<message id="Lms.Explore.play-last">Jouer en dernier</message>
<message id="Lms.Explore.play-shuffled">Jouer aléatoirement</message>
<message id="Lms.Explore.random">Aléatoire</message>
<message id="Lms.Explore.recently-added">Ajoutés récemment</message>
<message id="Lms.Explore.recently-played">Joués récemment</message>
@@ -157,11 +159,8 @@
<plural case="0">{1} piste ajoutée</plural>
<plural case="1">{1} pistes ajoutées</plural>
</message>
<message id="Lms.PlayQueue.nb-tracks-playing">
<plural case="0">{1} piste ajoutée</plural>
<plural case="1">{1} pistes ajoutées</plural>
</message>
<message id="Lms.PlayQueue.playqueue">Liste de lecture</message>
<message id="Lms.PlayQueue.playqueue-full">Liste de lecture pleine!</message>
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
<message id="Lms.PlayQueue.repeat">Repéter</message>
<message id="Lms.PlayQueue.shuffle">Mélanger</message>
+4 -2
View File
@@ -17,12 +17,13 @@
${<if-has-artist>}<h4>${artist class="Lms-artistname text-muted"}</h4>${</if-has-artist>}
<div class="text-muted"><small>${<if-has-year>}${year}${</if-has-year>}${<if-has-orig-year>} (${orig-year})${</if-has-orig-year>}</small></div>
${clusters}
${play-btn}${add-btn}
${play-btn}${more-btn}
</div>
${container}
</div>
</div>
<div class="row">
${<if-has-similar-releases>}
<div class="col-xs-12 col-md-8">
<div class="well well-sm">
<h3>${tr:Lms.Explore.Release.similar-releases}</h3>
@@ -31,6 +32,7 @@
</div>
</div>
</div>
${</if-has-similar-releases>}
${<if-has-copyright>}
<div class="col-xs-12 col-md-4">
<div class="well well-sm">
@@ -66,7 +68,7 @@
</div>
<div class="col-xs-3">
<div class="Lms-explore-release-entry-controls">
${play-btn}${add-btn}
${play-btn}${more-btn}
</div>
<div class="Lms-explore-release-entry-track-duration text-muted">
${duration}
+1 -1
View File
@@ -6,7 +6,7 @@
<div class="Lms-header">
<h2>${tr:Lms.Explore.releases}</h2>
${mode class="nav nav-pills Lms-explore-mode-container"}
${play-btn}${add-btn}
${play-btn}${more-btn}
</div>
<div class="row">
${releases}
+2 -2
View File
@@ -9,7 +9,7 @@
<div class="Lms-header">
<h2>${tr:Lms.Explore.tracks}</h2>
${mode class="nav nav-pills Lms-explore-mode-container"}
${play-btn}${add-btn}
${play-btn}${more-btn}
</div>
${tracks}
${loading-indicator class="Lms-horizontal-center Lms-loading-indicator"}
@@ -33,7 +33,7 @@
</div>
<div class="col-xs-2 col-sm-2 col-md-2">
<div class="Lms-explore-tracks-entry-controls">
${play-btn}${add-btn}
${play-btn}${more-btn}
</div>
<div class="Lms-explore-tracks-entry-duration text-muted">
${duration}
@@ -39,7 +39,6 @@ namespace Av
static constexpr std::size_t _chunkSize {262144};
const std::filesystem::path _trackPath;
Transcoder _transcoder;
};
}
+1
View File
@@ -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
+9 -5
View File
@@ -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<Wt::WPopupMenu>();
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<Explore>(filters);
_playQueue = mainStack->addNew<PlayQueue>();
auto* search {mainStack->addNew<SearchView>(filters)};
mainStack->addNew<SettingsView>();
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
+3
View File
@@ -22,6 +22,7 @@
#include <optional>
#include <Wt/WApplication.h>
#include <Wt/WPopupMenu.h>
#include "database/Db.hpp"
#include "database/Session.hpp"
@@ -101,6 +102,7 @@ class LmsApplication : public Wt::WApplication
static Wt::WLink createReleaseLink(Wt::Dbo::ptr<Database::Release> release);
static std::unique_ptr<Wt::WAnchor> createReleaseAnchor(Wt::Dbo::ptr<Database::Release> release, bool addText = true);
static std::unique_ptr<Wt::WText> createCluster(Wt::Dbo::ptr<Database::Cluster> cluster, bool canDelete = false);
Wt::WPopupMenu* createPopupMenu();
MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; }
PlayQueue& getPlayQueue() const { return *_playQueue; }
@@ -135,6 +137,7 @@ class LmsApplication : public Wt::WApplication
std::shared_ptr<ImageResource> _imageResource;
MediaPlayer* _mediaPlayer {};
PlayQueue* _playQueue {};
std::unique_ptr<Wt::WPopupMenu> _popupMenu {};
};
+44 -15
View File
@@ -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<Database::IdType>& 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<Database::IdType>& 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<Database::IdType> 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
+4 -2
View File
@@ -60,10 +60,10 @@ class PlayQueue : public Wt::WTemplate
private:
Wt::Dbo::ptr<Database::TrackList> getTrackList() const;
bool isFull() const;
void clearTracks();
void enqueueTracks(const std::vector<Database::IdType>& trackIds);
void enqueueTrack(Database::IdType trackId);
std::size_t enqueueTracks(const std::vector<Database::IdType>& trackIds);
void addSome();
void enqueueRadioTrack();
void updateInfo();
@@ -80,6 +80,8 @@ class PlayQueue : public Wt::WTemplate
void addRadioTrackFromClusters();
std::optional<float> getReplayGain(std::size_t pos, const Wt::Dbo::ptr<Database::Track>& track) const;
static inline constexpr std::size_t _nbMaxEntries {1000};
bool _repeatAll {};
bool _radioMode {};
bool _mediaPlayerSettingsLoaded {};
+9 -2
View File
@@ -19,15 +19,22 @@
#pragma once
#include <vector>
#include <Wt/WSignal.h>
#include "database/Types.hpp"
namespace UserInterface
{
enum class PlayQueueAction
{
Play,
AddNext,
AddLast,
PlayLast,
PlayShuffled,
};
using PlayQueueActionSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&>;
}
+20 -3
View File
@@ -115,11 +115,24 @@ Artist::refreshView()
}
{
Wt::WText* addBtn = bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
Wt::WText* moreBtn = bindNew<Wt::WText>("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<Database::IdType>& similarArtistsId)
{
if (similarArtistsId.empty())
return;
setCondition("if-has-similar-artists", true);
Wt::WContainerWidget* similarArtistsContainer {bindNew<Wt::WContainerWidget>("similar-artists")};
for (Database::IdType artistId : similarArtistsId)
+1 -1
View File
@@ -42,7 +42,7 @@ class Artist : public Wt::WTemplate
public:
Artist(Filters* filters);
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> artistsAction;
PlayQueueActionSignal artistsAction;
private:
void refreshView();
+14
View File
@@ -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<SearchView>(_filters);
search->tracksAction.connect(this, &Explore::handleTracksAction);
_search = search.get();
contentsStack->addWidget(std::move(search));
auto tracks = std::make_unique<Tracks>(_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<Database::IdType>
getArtistsTracks(Database::Session& session, const std::vector<Database::IdType>& artistsId, const std::set<Database::IdType>&)
+5 -1
View File
@@ -27,13 +27,16 @@
namespace UserInterface {
class Filters;
class SearchView;
class Explore : public Wt::WTemplate
{
public:
Explore(Filters* filters);
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> 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<Database::IdType>& tracksId);
Filters* _filters {};
SearchView* _search {};
};
} // namespace UserInterface
+49
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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
+34
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Wt/WWidget.h>
#include <Wt/WSignal.h>
#include "database/Types.hpp"
#include "PlayQueueAction.hpp"
namespace UserInterface
{
void displayReleasePopupMenu(Wt::WInteractWidget& target,
Database::IdType releaseId,
PlayQueueActionSignal& releasesAction);
} // namespace UserInterface
+19 -7
View File
@@ -22,7 +22,6 @@
#include <Wt/WAnchor.h>
#include <Wt/WApplication.h>
#include <Wt/WImage.h>
#include <Wt/WPopupMenu.h>
#include <Wt/WTemplate.h>
#include <Wt/WText.h>
@@ -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<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)};
addBtn->clicked().connect([=]
Wt::WText* moreBtn {bindNew<Wt::WText>("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<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)};
addBtn->clicked().connect([=]()
Wt::WText* moreBtn {entry->bindNew<Wt::WText>("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<Database::IdType>& similarReleasesId)
{
if (similarReleasesId.empty())
return;
setCondition("if-has-similar-releases", true);
auto* similarReleasesContainer {bindNew<Wt::WContainerWidget>("similar-releases")};
for (Database::IdType id : similarReleasesId)
+2 -6
View File
@@ -19,7 +19,6 @@
#pragma once
#include <Wt/WSignal.h>
#include <Wt/WTemplate.h>
#include "database/Types.hpp"
@@ -39,11 +38,8 @@ namespace UserInterface
public:
Release(Filters* filters);
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> releasesAction;
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> tracksAction;
Wt::Signal<Wt::WWidget&, Database::IdType> createReleaseActionMenu;
Wt::Signal<Wt::WWidget&, Database::IdType> createTrackActionMenu;
PlayQueueActionSignal releasesAction;
PlayQueueActionSignal tracksAction;
private:
void refreshView();
+16 -3
View File
@@ -72,10 +72,23 @@ _filters {filters}
{
releasesAction.emit(PlayQueueAction::Play, getAllReleases());
});
Wt::WText* addBtn {bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)};
addBtn->clicked().connect([this]
Wt::WText* moreBtn {bindNew<Wt::WText>("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<Wt::WContainerWidget>("releases");
+1 -1
View File
@@ -41,7 +41,7 @@ class Releases : public Wt::WTemplate
public:
Releases(Filters* filters);
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> releasesAction;
PlayQueueActionSignal releasesAction;
private:
+3 -3
View File
@@ -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<Wt::WContainerWidget>("tracks")};
for (const Database::Track::pointer& track : tracks)
container->addWidget(TrackListHelpers::createEntry(track));
container->addWidget(TrackListHelpers::createEntry(track, tracksAction));
}
}
+5 -1
View File
@@ -24,6 +24,8 @@
#include <Wt/WTemplate.h>
#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();
+14 -7
View File
@@ -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<Wt::WTemplate>
createEntry(const Wt::Dbo::ptr<Database::Track>& track)
createEntry(const Wt::Dbo::ptr<Database::Track>& track, PlayQueueActionSignal& tracksAction)
{
auto entry {std::make_unique<Wt::WTemplate>(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<Wt::WText>("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<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
addBtn->clicked().connect([=]
Wt::WText* moreBtn = entry->bindNew<Wt::WText>("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)
+2 -1
View File
@@ -22,6 +22,7 @@
#include <memory>
#include <Wt/WTemplate.h>
#include "PlayQueueAction.hpp"
namespace Database
{
@@ -30,6 +31,6 @@ namespace Database
namespace UserInterface::TrackListHelpers
{
std::unique_ptr<Wt::WTemplate> createEntry(const Wt::Dbo::ptr<Database::Track>& track);
std::unique_ptr<Wt::WTemplate> createEntry(const Wt::Dbo::ptr<Database::Track>& track, PlayQueueActionSignal& tracksAction);
} // namespace UserInterface
+18 -6
View File
@@ -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<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
addBtn->clicked().connect([=]
Wt::WText* moreBtn = bindNew<Wt::WText>("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<Wt::WContainerWidget>("tracks");
@@ -219,7 +231,7 @@ Tracks::addSome()
bool moreResults;
for (const Track::pointer& track : getTracks(Range {static_cast<std::size_t>(_tracksContainer->count()), batchSize}, moreResults))
{
_tracksContainer->addWidget(TrackListHelpers::createEntry(track));
_tracksContainer->addWidget(TrackListHelpers::createEntry(track, tracksAction));
}
if (moreResults)
+1 -1
View File
@@ -40,7 +40,7 @@ class Tracks : public Wt::WTemplate
public:
Tracks(Filters* filters);
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> tracksAction;
PlayQueueActionSignal tracksAction;
private: