Factorized sort mode, now using a more compact dropdown menu

This commit is contained in:
emeric
2024-05-01 11:27:08 +02:00
parent 553787b595
commit 71e0e6e411
24 changed files with 425 additions and 380 deletions
+23 -26
View File
@@ -3,32 +3,9 @@
<!--FORMS message blocks--> <!--FORMS message blocks-->
<message id="Lms.Explore.Artists.template"> <message id="Lms.Explore.Artists.template">
<div class="mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<ul class="nav nav-pills mb-2"> ${link-type}
<li class="nav-item"> ${sort-mode}
${random class="nav-link"}
</li>
<li class="nav-item">
${starred class="nav-link"}
</li>
<li class="nav-item">
${recently-played class="nav-link"}
</li>
<li class="nav-item">
${most-played class="nav-link"}
</li>
<li class="nav-item">
${recently-added class="nav-link"}
</li>
<li class="nav-item">
${all class="nav-link"}
</li>
</ul>
<div class="row">
<div class="col-lg-3">
${link-type class="form-select"}
</div>
</div>
</div> </div>
${artists} ${artists}
</message> </message>
@@ -42,4 +19,24 @@
${loading-indicator} ${loading-indicator}
</message> </message>
<message id="Lms.Explore.Artists.template.track-artist-link-type-selector">
<div class="dropdown">
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
${link-type}
</button>
<ul class="dropdown-menu">
<li>${link-type-all class="dropdown-item"}</li>
<li>${link-type-artist class="dropdown-item"}</li>
<li>${link-type-releaseartist class="dropdown-item"}</li>
<li>${link-type-composer class="dropdown-item"}</li>
<li>${link-type-conductor class="dropdown-item"}</li>
<li>${link-type-lyricist class="dropdown-item"}</li>
<li>${link-type-mixer class="dropdown-item"}</li>
<li>${link-type-performer class="dropdown-item"}</li>
<li>${link-type-producer class="dropdown-item"}</li>
<li>${link-type-remixer class="dropdown-item"}</li>
</ul>
</div>
</message>
</messages> </messages>
+15
View File
@@ -52,5 +52,20 @@
</div> </div>
</message> </message>
<message id="Lms.Explore.template.sort-mode-selector">
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
${sort-mode}
</button>
<ul class="dropdown-menu">
<li>${random class="dropdown-item"}</li>
<li>${starred class="dropdown-item"}</li>
<li>${recently-played class="dropdown-item"}</li>
<li>${most-played class="dropdown-item"}</li>
<li>${recently-added class="dropdown-item"}</li>
<li>${all class="dropdown-item"}</li>
</ul>
</div>
</message>
</messages> </messages>
+2 -21
View File
@@ -3,27 +3,7 @@
<messages xmlns:if="Wt.WTemplate.conditions"> <messages xmlns:if="Wt.WTemplate.conditions">
<message id="Lms.Explore.Releases.template"> <message id="Lms.Explore.Releases.template">
<div class="mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<ul class="nav nav-pills mb-2">
<li class="nav-item">
${random class="nav-link"}
</li>
<li class="nav-item">
${starred class="nav-link"}
</li>
<li class="nav-item">
${recently-played class="nav-link"}
</li>
<li class="nav-item">
${most-played class="nav-link"}
</li>
<li class="nav-item">
${recently-added class="nav-link"}
</li>
<li class="nav-item">
${all class="nav-link"}
</li>
</ul>
<div class="btn-group"> <div class="btn-group">
${play-btn class="btn btn-primary"} ${play-btn class="btn btn-primary"}
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/> <button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
@@ -33,6 +13,7 @@
<li>${play-shuffled class="dropdown-item"}</li> <li>${play-shuffled class="dropdown-item"}</li>
</ul> </ul>
</div> </div>
${sort-mode}
</div> </div>
${releases} ${releases}
</message> </message>
+2 -4
View File
@@ -20,10 +20,8 @@
</message> </message>
<message id="Lms.Explore.Search.template.artists"> <message id="Lms.Explore.Search.template.artists">
<div class="row mb-3"> <div class="mb-3">
<div class="col-lg-3"> ${link-type}
${link-type class="form-select"}
</div>
</div> </div>
${artists} ${artists}
</message> </message>
+2 -21
View File
@@ -4,27 +4,7 @@
<!--FORMS message blocks--> <!--FORMS message blocks-->
<message id="Lms.Explore.Tracks.template"> <message id="Lms.Explore.Tracks.template">
<div class="mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<ul class="nav nav-pills mb-2">
<li class="nav-item">
${random class="nav-link"}
</li>
<li class="nav-item">
${starred class="nav-link"}
</li>
<li class="nav-item">
${recently-played class="nav-link"}
</li>
<li class="nav-item">
${most-played class="nav-link"}
</li>
<li class="nav-item">
${recently-added class="nav-link"}
</li>
<li class="nav-item">
${all class="nav-link"}
</li>
</ul>
<div class="btn-group"> <div class="btn-group">
${play-btn class="btn btn-primary"} ${play-btn class="btn btn-primary"}
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/> <button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
@@ -34,6 +14,7 @@
<li>${play-shuffled class="dropdown-item"}</li> <li>${play-shuffled class="dropdown-item"}</li>
</ul> </ul>
</div> </div>
${sort-mode}
</div> </div>
${tracks} ${tracks}
</message> </message>
@@ -105,20 +105,6 @@ namespace lms::db
}); });
} }
core::EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session)
{
session.checkReadTransaction();
const auto query{ session.getDboSession()->query<TrackArtistLinkType>("SELECT DISTINCT type from track_artist_link") };
core::EnumSet<TrackArtistLinkType> res;
utils::forEachQueryResult(query, [&](TrackArtistLinkType linkType)
{
res.insert(linkType);
});
return res;
}
core::EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session, ArtistId artistId) core::EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session, ArtistId artistId)
{ {
session.checkReadTransaction(); session.checkReadTransaction();
@@ -66,7 +66,6 @@ namespace lms::db
static void find(Session& session, const FindParameters& parameters, const std::function<void(const TrackArtistLink::pointer&)>&); static void find(Session& session, const FindParameters& parameters, const std::function<void(const TrackArtistLink::pointer&)>&);
static pointer find(Session& session, TrackArtistLinkId linkId); static pointer find(Session& session, TrackArtistLinkId linkId);
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType = {}); static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType = {});
static core::EnumSet<TrackArtistLinkType> findUsedTypes(Session& session);
static core::EnumSet<TrackArtistLinkType> findUsedTypes(Session& session, ArtistId _artist); static core::EnumSet<TrackArtistLinkType> findUsedTypes(Session& session, ArtistId _artist);
ObjectPtr<Track> getTrack() const { return _track; } ObjectPtr<Track> getTrack() const { return _track; }
+2
View File
@@ -43,6 +43,8 @@ add_executable(lms
ui/explore/ReleaseView.cpp ui/explore/ReleaseView.cpp
ui/explore/ReleaseTypes.cpp ui/explore/ReleaseTypes.cpp
ui/explore/SearchView.cpp ui/explore/SearchView.cpp
ui/explore/SortModeSelector.cpp
ui/explore/TrackArtistLinkTypeSelector.cpp
ui/explore/TrackCollector.cpp ui/explore/TrackCollector.cpp
ui/explore/TrackListHelpers.cpp ui/explore/TrackListHelpers.cpp
ui/explore/TrackListView.cpp ui/explore/TrackListView.cpp
+2 -46
View File
@@ -18,62 +18,18 @@
*/ */
#include "ArtistListHelpers.hpp" #include "ArtistListHelpers.hpp"
#include <Wt/WAnchor.h>
#include "database/Artist.hpp" #include "database/Artist.hpp"
#include "database/Session.hpp" #include "database/Session.hpp"
#include "database/TrackArtistLink.hpp"
#include "core/EnumSet.hpp"
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
#include "Utils.hpp" #include "Utils.hpp"
namespace lms::ui::ArtistListHelpers namespace lms::ui::ArtistListHelpers
{ {
std::unique_ptr<Wt::WTemplate> std::unique_ptr<Wt::WTemplate> createEntry(const db::ObjectPtr<db::Artist>& artist)
createEntry(const db::ObjectPtr<db::Artist>& artist)
{ {
auto res {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.entry"))}; auto res{ std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.entry")) };
res->bindWidget("name", utils::createArtistAnchor(artist)); res->bindWidget("name", utils::createArtistAnchor(artist));
return res; return res;
} }
std::unique_ptr<ArtistLinkTypesModel>
createArtistLinkTypesModel()
{
using namespace db;
std::unique_ptr<ArtistLinkTypesModel> linkTypesModel {std::make_unique<ArtistLinkTypesModel>()};
core::EnumSet<TrackArtistLinkType> usedLinkTypes;
{
auto transaction {LmsApp->getDbSession().createReadTransaction()};
usedLinkTypes = TrackArtistLink::findUsedTypes(LmsApp->getDbSession());
}
auto addTypeIfUsed {[&](TrackArtistLinkType linkType, std::string_view stringKey)
{
if (!usedLinkTypes.contains(linkType))
return;
linkTypesModel->add(Wt::WString::trn(std::string {stringKey}, 2), linkType);
}};
// add default one first (none)
linkTypesModel->add(Wt::WString::tr("Lms.Explore.Artists.linktype-all"), std::nullopt);
// TODO: sort by translated strings
addTypeIfUsed(TrackArtistLinkType::Artist, "Lms.Explore.Artists.linktype-artist");
addTypeIfUsed(TrackArtistLinkType::ReleaseArtist, "Lms.Explore.Artists.linktype-releaseartist");
addTypeIfUsed(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
addTypeIfUsed(TrackArtistLinkType::Conductor, "Lms.Explore.Artists.linktype-conductor");
addTypeIfUsed(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
addTypeIfUsed(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
addTypeIfUsed(TrackArtistLinkType::Performer, "Lms.Explore.Artists.linktype-performer");
addTypeIfUsed(TrackArtistLinkType::Producer, "Lms.Explore.Artists.linktype-producer");
addTypeIfUsed(TrackArtistLinkType::Remixer, "Lms.Explore.Artists.linktype-remixer");
return linkTypesModel;
}
} }
-1
View File
@@ -39,7 +39,6 @@ namespace lms::ui
namespace ArtistListHelpers namespace ArtistListHelpers
{ {
std::unique_ptr<Wt::WTemplate> createEntry(const db::ObjectPtr<db::Artist>& artist); std::unique_ptr<Wt::WTemplate> createEntry(const db::ObjectPtr<db::Artist>& artist);
std::unique_ptr<ArtistLinkTypesModel> createArtistLinkTypesModel();
} }
} }
+9 -34
View File
@@ -21,15 +21,17 @@
#include <Wt/WPushButton.h> #include <Wt/WPushButton.h>
#include "core/ILogger.hpp"
#include "database/Artist.hpp" #include "database/Artist.hpp"
#include "database/Session.hpp" #include "database/Session.hpp"
#include "database/TrackArtistLink.hpp" #include "database/TrackArtistLink.hpp"
#include "core/ILogger.hpp"
#include "common/InfiniteScrollingContainer.hpp" #include "common/InfiniteScrollingContainer.hpp"
#include "ArtistListHelpers.hpp" #include "ArtistListHelpers.hpp"
#include "Filters.hpp" #include "Filters.hpp"
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
#include "SortModeSelector.hpp"
#include "TrackArtistLinkTypeSelector.hpp"
namespace lms::ui namespace lms::ui
{ {
@@ -37,49 +39,22 @@ namespace lms::ui
Artists::Artists(Filters& filters) Artists::Artists(Filters& filters)
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.Artists.template") } : Wt::WTemplate{ Wt::WString::tr("Lms.Explore.Artists.template") }
, _artistCollector{ filters, _defaultMode, _maxCount } , _artistCollector{ filters, _defaultSortMode, _maxCount }
{ {
addFunction("tr", &Wt::WTemplate::Functions::tr); addFunction("tr", &Wt::WTemplate::Functions::tr);
auto bindMenuItem{ [this](const std::string& var, const Wt::WString& title, ArtistCollector::Mode mode) SortModeSelector* sortModeSelector{ bindNew<SortModeSelector>("sort-mode", _defaultSortMode) };
sortModeSelector->sortModeChanged.connect([this](ArtistCollector::Mode sortMode)
{ {
auto* menuItem {bindNew<Wt::WPushButton>(var, title)}; refreshView(sortMode);
menuItem->clicked().connect([=, this]
{
refreshView(mode);
_currentActiveItem->removeStyleClass("active");
menuItem->addStyleClass("active");
_currentActiveItem = menuItem;
}); });
if (mode == _defaultMode) TrackArtistLinkTypeSelector* linkTypeSelector{ bindNew<TrackArtistLinkTypeSelector>("link-type", _defaultLinkType) };
linkTypeSelector->linkTypeChanged.connect([this](std::optional<TrackArtistLinkType> linkType)
{ {
_currentActiveItem = menuItem;
_currentActiveItem->addStyleClass("active");
}
} };
bindMenuItem("random", Wt::WString::tr("Lms.Explore.random"), ArtistCollector::Mode::Random);
bindMenuItem("starred", Wt::WString::tr("Lms.Explore.starred"), ArtistCollector::Mode::Starred);
bindMenuItem("recently-played", Wt::WString::tr("Lms.Explore.recently-played"), ArtistCollector::Mode::RecentlyPlayed);
bindMenuItem("most-played", Wt::WString::tr("Lms.Explore.most-played"), ArtistCollector::Mode::MostPlayed);
bindMenuItem("recently-added", Wt::WString::tr("Lms.Explore.recently-added"), ArtistCollector::Mode::RecentlyAdded);
bindMenuItem("all", Wt::WString::tr("Lms.Explore.all"), ArtistCollector::Mode::All);
_linkType = bindNew<Wt::WComboBox>("link-type");
_linkType->setModel(ArtistListHelpers::createArtistLinkTypesModel());
_linkType->changed().connect([this]
{
const std::optional<TrackArtistLinkType> linkType{ static_cast<ArtistLinkTypesModel*>(_linkType->model().get())->getValue(_linkType->currentIndex()) };
refreshView(linkType); refreshView(linkType);
}); });
LmsApp->getScannerEvents().scanComplete.connect(this, [this](const scanner::ScanStats& stats)
{
if (stats.nbChanges())
_linkType->setModel(ArtistListHelpers::createArtistLinkTypesModel());
});
_container = bindNew<InfiniteScrollingContainer>("artists", Wt::WString::tr("Lms.Explore.Artists.template.container")); _container = bindNew<InfiniteScrollingContainer>("artists", Wt::WString::tr("Lms.Explore.Artists.template.container"));
_container->onRequestElements.connect([this] _container->onRequestElements.connect([this]
{ {
+6 -6
View File
@@ -44,14 +44,14 @@ namespace lms::ui
void refreshView(std::optional<db::TrackArtistLinkType> linkType); void refreshView(std::optional<db::TrackArtistLinkType> linkType);
void addSome(); void addSome();
static constexpr std::size_t _batchSize {30}; static constexpr std::size_t _batchSize{ 30 };
static constexpr std::size_t _maxCount {8000}; static constexpr std::size_t _maxCount{ 8000 };
Wt::WWidget* _currentActiveItem {}; Wt::WWidget* _currentLinkTypeActiveItem{};
InfiniteScrollingContainer* _container {}; InfiniteScrollingContainer* _container{};
ArtistCollector _artistCollector; ArtistCollector _artistCollector;
Wt::WComboBox* _linkType {}; static constexpr ArtistCollector::Mode _defaultSortMode{ ArtistCollector::Mode::Random };
static constexpr ArtistCollector::Mode _defaultMode {ArtistCollector::Mode::Random}; static constexpr std::optional<db::TrackArtistLinkType> _defaultLinkType;
}; };
} // namespace lms::ui } // namespace lms::ui
+12 -15
View File
@@ -32,12 +32,11 @@
#include "TrackListsView.hpp" #include "TrackListsView.hpp"
#include "TracksView.hpp" #include "TracksView.hpp"
namespace lms::ui { namespace lms::ui
{
namespace { namespace
{
void void handleContentsPathChange(Wt::WStackedWidget* stack)
handleContentsPathChange(Wt::WStackedWidget* stack)
{ {
enum Idx enum Idx
{ {
@@ -76,13 +75,13 @@ namespace lms::ui {
} // namespace } // namespace
Explore::Explore(Filters& filters, PlayQueue& playQueue) Explore::Explore(Filters& filters, PlayQueue& playQueue)
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.template")} : Wt::WTemplate{ Wt::WString::tr("Lms.Explore.template") }
, _playQueueController {filters, playQueue} , _playQueueController{ filters, playQueue }
{ {
addFunction("tr", &Functions::tr); addFunction("tr", &Functions::tr);
// Contents // Contents
Wt::WStackedWidget* contentsStack {bindNew<Wt::WStackedWidget>("contents")}; Wt::WStackedWidget* contentsStack{ bindNew<Wt::WStackedWidget>("contents") };
contentsStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default contentsStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
// same order as enum Idx // same order as enum Idx
@@ -92,8 +91,8 @@ namespace lms::ui {
auto artist = std::make_unique<Artist>(filters, _playQueueController); auto artist = std::make_unique<Artist>(filters, _playQueueController);
contentsStack->addWidget(std::move(artist)); contentsStack->addWidget(std::move(artist));
auto trackLists {std::make_unique<TrackLists>(filters)}; auto trackLists{ std::make_unique<TrackLists>(filters) };
auto trackList {std::make_unique<TrackList>(filters, _playQueueController)}; auto trackList{ std::make_unique<TrackList>(filters, _playQueueController) };
trackList->trackListDeleted.connect(trackLists.get(), &TrackLists::onTrackListDeleted); trackList->trackListDeleted.connect(trackLists.get(), &TrackLists::onTrackListDeleted);
contentsStack->addWidget(std::move(trackLists)); contentsStack->addWidget(std::move(trackLists));
contentsStack->addWidget(std::move(trackList)); contentsStack->addWidget(std::move(trackList));
@@ -111,7 +110,7 @@ namespace lms::ui {
auto tracks = std::make_unique<Tracks>(filters, _playQueueController); auto tracks = std::make_unique<Tracks>(filters, _playQueueController);
contentsStack->addWidget(std::move(tracks)); contentsStack->addWidget(std::move(tracks));
wApp->internalPathChanged().connect(this, [=] wApp->internalPathChanged().connect(this, [contentsStack]
{ {
handleContentsPathChange(contentsStack); handleContentsPathChange(contentsStack);
}); });
@@ -119,10 +118,8 @@ namespace lms::ui {
handleContentsPathChange(contentsStack); handleContentsPathChange(contentsStack);
} }
void void Explore::search(const Wt::WString& searchText)
Explore::search(const Wt::WString& searchText)
{ {
_search->refreshView(searchText); _search->refreshView(searchText);
} }
} // namespace lms::ui } // namespace lms::ui
+2 -1
View File
@@ -20,6 +20,7 @@
#pragma once #pragma once
#include <Wt/WTemplate.h> #include <Wt/WTemplate.h>
#include "PlayQueueController.hpp" #include "PlayQueueController.hpp"
namespace lms::ui namespace lms::ui
@@ -38,7 +39,7 @@ namespace lms::ui
private: private:
PlayQueueController _playQueueController; PlayQueueController _playQueueController;
SearchView* _search {}; SearchView* _search{};
}; };
} // namespace lms::ui } // namespace lms::ui
+4 -22
View File
@@ -30,6 +30,7 @@
#include "explore/PlayQueueController.hpp" #include "explore/PlayQueueController.hpp"
#include "explore/ReleaseHelpers.hpp" #include "explore/ReleaseHelpers.hpp"
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
#include "SortModeSelector.hpp"
namespace lms::ui namespace lms::ui
{ {
@@ -43,31 +44,12 @@ namespace lms::ui
addFunction("tr", &Wt::WTemplate::Functions::tr); addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("id", &Wt::WTemplate::Functions::id); addFunction("id", &Wt::WTemplate::Functions::id);
auto bindMenuItem{ [this](const std::string& var, const Wt::WString& title, ReleaseCollector::Mode mode) SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
sortMode->sortModeChanged.connect([this](ReleaseCollector::Mode sortMode)
{ {
auto* menuItem {bindNew<Wt::WPushButton>(var, title)}; refreshView(sortMode);
menuItem->clicked().connect([this, mode, menuItem]
{
refreshView(mode);
_currentActiveItem->removeStyleClass("active");
menuItem->addStyleClass("active");
_currentActiveItem = menuItem;
}); });
if (mode == _defaultMode)
{
_currentActiveItem = menuItem;
_currentActiveItem->addStyleClass("active");
}
} };
bindMenuItem("random", Wt::WString::tr("Lms.Explore.random"), ReleaseCollector::Mode::Random);
bindMenuItem("starred", Wt::WString::tr("Lms.Explore.starred"), ReleaseCollector::Mode::Starred);
bindMenuItem("recently-played", Wt::WString::tr("Lms.Explore.recently-played"), ReleaseCollector::Mode::RecentlyPlayed);
bindMenuItem("most-played", Wt::WString::tr("Lms.Explore.most-played"), ReleaseCollector::Mode::MostPlayed);
bindMenuItem("recently-added", Wt::WString::tr("Lms.Explore.recently-added"), ReleaseCollector::Mode::RecentlyAdded);
bindMenuItem("all", Wt::WString::tr("Lms.Explore.all"), ReleaseCollector::Mode::All);
Wt::WPushButton* playBtn{ bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML) }; Wt::WPushButton* playBtn{ bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML) };
playBtn->clicked().connect([this] playBtn->clicked().connect([this]
{ {
+5 -6
View File
@@ -42,15 +42,14 @@ namespace lms::ui
void addSome(); void addSome();
std::vector<db::ReleaseId> getAllReleases(); std::vector<db::ReleaseId> getAllReleases();
static constexpr std::size_t _maxItemsPerLine {6}; static constexpr std::size_t _maxItemsPerLine{ 6 };
static constexpr std::size_t _batchSize {_maxItemsPerLine}; static constexpr std::size_t _batchSize{ _maxItemsPerLine };
static constexpr std::size_t _maxCount {_maxItemsPerLine * 500}; static constexpr std::size_t _maxCount{ _maxItemsPerLine * 500 };
PlayQueueController& _playQueueController; PlayQueueController& _playQueueController;
Wt::WWidget* _currentActiveItem {}; InfiniteScrollingContainer* _container{};
InfiniteScrollingContainer* _container {};
ReleaseCollector _releaseCollector; ReleaseCollector _releaseCollector;
static constexpr ReleaseCollector::Mode _defaultMode {ReleaseCollector::Mode::Random}; static constexpr ReleaseCollector::Mode _defaultMode{ ReleaseCollector::Mode::Random };
}; };
} // namespace lms::ui } // namespace lms::ui
+3 -10
View File
@@ -31,6 +31,7 @@
#include "Filters.hpp" #include "Filters.hpp"
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
#include "ReleaseHelpers.hpp" #include "ReleaseHelpers.hpp"
#include "TrackArtistLinkTypeSelector.hpp"
#include "TrackListHelpers.hpp" #include "TrackListHelpers.hpp"
namespace lms::ui namespace lms::ui
@@ -58,11 +59,9 @@ namespace lms::ui
{ {
Wt::WTemplate* artistResults{ _stack->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Search.template.artists")) }; Wt::WTemplate* artistResults{ _stack->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Search.template.artists")) };
_artistLinkType = artistResults->bindNew<Wt::WComboBox>("link-type"); TrackArtistLinkTypeSelector* linkTypeSelector{ artistResults->bindNew<TrackArtistLinkTypeSelector>("link-type", std::nullopt)};
_artistLinkType->setModel(ArtistListHelpers::createArtistLinkTypesModel()); linkTypeSelector->linkTypeChanged.connect([this](std::optional<TrackArtistLinkType> linkType)
_artistLinkType->changed().connect([this]
{ {
const std::optional<TrackArtistLinkType> linkType{ static_cast<ArtistLinkTypesModel*>(_artistLinkType->model().get())->getValue(_artistLinkType->currentIndex()) };
refreshView(linkType); refreshView(linkType);
}); });
@@ -101,12 +100,6 @@ namespace lms::ui
{ {
refreshView(); refreshView();
}); });
LmsApp->getScannerEvents().scanComplete.connect(this, [this](const scanner::ScanStats& stats)
{
if (stats.nbChanges())
_artistLinkType->setModel(ArtistListHelpers::createArtistLinkTypesModel());
});
} }
std::size_t SearchView::getBatchSize(Mode mode) const std::size_t SearchView::getBatchSize(Mode mode) const
-2
View File
@@ -85,8 +85,6 @@ namespace lms::ui
InfiniteScrollingContainer* _artists{}; InfiniteScrollingContainer* _artists{};
InfiniteScrollingContainer* _releases{}; InfiniteScrollingContainer* _releases{};
InfiniteScrollingContainer* _tracks{}; InfiniteScrollingContainer* _tracks{};
Wt::WComboBox* _artistLinkType{};
}; };
} // namespace lms::ui } // namespace lms::ui
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 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 "SortModeSelector.hpp"
#include <Wt/WPushButton.h>
namespace lms::ui
{
SortModeSelector::SortModeSelector(DatabaseCollectorBase::Mode defaultMode)
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.template.sort-mode-selector") }
{
auto* sortMode{ bindNew<Wt::WText>("sort-mode") };
auto bindMenuItem{ [this, sortMode, defaultMode](const std::string& var, const Wt::WString& title, DatabaseCollectorBase::Mode mode)
{
auto* menuItem {bindNew<Wt::WPushButton>(var, title)};
menuItem->clicked().connect([this, mode, menuItem, sortMode, title]
{
_currentActiveItem->removeStyleClass("active");
menuItem->addStyleClass("active");
_currentActiveItem = menuItem;
sortMode->setText(title);
sortModeChanged.emit(mode);
});
if (mode == defaultMode)
{
_currentActiveItem = menuItem;
_currentActiveItem->addStyleClass("active");
sortMode->setText(title);
}
} };
bindMenuItem("random", Wt::WString::tr("Lms.Explore.random"), DatabaseCollectorBase::Mode::Random);
bindMenuItem("starred", Wt::WString::tr("Lms.Explore.starred"), DatabaseCollectorBase::Mode::Starred);
bindMenuItem("recently-played", Wt::WString::tr("Lms.Explore.recently-played"), DatabaseCollectorBase::Mode::RecentlyPlayed);
bindMenuItem("most-played", Wt::WString::tr("Lms.Explore.most-played"), DatabaseCollectorBase::Mode::MostPlayed);
bindMenuItem("recently-added", Wt::WString::tr("Lms.Explore.recently-added"), DatabaseCollectorBase::Mode::RecentlyAdded);
bindMenuItem("all", Wt::WString::tr("Lms.Explore.all"), DatabaseCollectorBase::Mode::All);
}
} // namespace lms::ui
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2024 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/WSignal.h>
#include <Wt/WTemplate.h>
#include "DatabaseCollectorBase.hpp"
namespace lms::ui
{
class SortModeSelector : public Wt::WTemplate
{
public:
SortModeSelector(DatabaseCollectorBase::Mode defaultMode);
Wt::Signal<DatabaseCollectorBase::Mode> sortModeChanged;
private:
Wt::WWidget* _currentActiveItem{};
};
} // namespace lms::ui
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2024 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 "TrackArtistLinkTypeSelector.hpp"
#include <Wt/WPushButton.h>
namespace lms::ui
{
TrackArtistLinkTypeSelector::TrackArtistLinkTypeSelector(std::optional<db::TrackArtistLinkType> defaultLinkType)
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.Artists.template.track-artist-link-type-selector") }
{
Wt::WText* linkTypeTxt{ bindNew<Wt::WText>("link-type") };
auto bindMenuItem{ [this, linkTypeTxt, defaultLinkType](const std::string& var, const Wt::WString& title, std::optional<db::TrackArtistLinkType> linkType)
{
auto* menuItem{ bindNew<Wt::WPushButton>(var, title) };
menuItem->clicked().connect([=]
{
_currentActiveItem->removeStyleClass("active");
menuItem->addStyleClass("active");
_currentActiveItem = menuItem;
linkTypeTxt->setText(title);
linkTypeChanged.emit(linkType);
});
if (linkType == defaultLinkType)
{
_currentActiveItem = menuItem;
_currentActiveItem->addStyleClass("active");
linkTypeTxt->setText(title);
}
} };
bindMenuItem("link-type-all", Wt::WString::tr("Lms.Explore.Artists.linktype-all"), std::nullopt);
bindMenuItem("link-type-artist", Wt::WString::trn("Lms.Explore.Artists.linktype-artist", 2), db::TrackArtistLinkType::Artist);
bindMenuItem("link-type-releaseartist", Wt::WString::trn("Lms.Explore.Artists.linktype-releaseartist", 2), db::TrackArtistLinkType::ReleaseArtist);
bindMenuItem("link-type-composer", Wt::WString::trn("Lms.Explore.Artists.linktype-composer", 2), db::TrackArtistLinkType::Composer);
bindMenuItem("link-type-conductor", Wt::WString::trn("Lms.Explore.Artists.linktype-conductor", 2), db::TrackArtistLinkType::Conductor);
bindMenuItem("link-type-lyricist", Wt::WString::trn("Lms.Explore.Artists.linktype-lyricist", 2), db::TrackArtistLinkType::Lyricist);
bindMenuItem("link-type-mixer", Wt::WString::trn("Lms.Explore.Artists.linktype-mixer", 2), db::TrackArtistLinkType::Mixer);
bindMenuItem("link-type-performer", Wt::WString::trn("Lms.Explore.Artists.linktype-performer", 2), db::TrackArtistLinkType::Performer);
bindMenuItem("link-type-producer", Wt::WString::trn("Lms.Explore.Artists.linktype-producer", 2), db::TrackArtistLinkType::Producer);
bindMenuItem("link-type-remixer", Wt::WString::trn("Lms.Explore.Artists.linktype-remixer", 2), db::TrackArtistLinkType::Remixer);
}
} // namespace lms::ui
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2024 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 <optional>
#include <Wt/WSignal.h>
#include <Wt/WTemplate.h>
#include "database/Types.hpp"
namespace lms::ui
{
class TrackArtistLinkTypeSelector : public Wt::WTemplate
{
public:
TrackArtistLinkTypeSelector(std::optional<db::TrackArtistLinkType> defaultLinkType);
Wt::Signal<std::optional<db::TrackArtistLinkType>> linkTypeChanged;
private:
Wt::WWidget* _currentActiveItem{};
};
} // namespace lms::ui
+3 -21
View File
@@ -30,6 +30,7 @@
#include "explore/PlayQueueController.hpp" #include "explore/PlayQueueController.hpp"
#include "explore/TrackListHelpers.hpp" #include "explore/TrackListHelpers.hpp"
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
#include "SortModeSelector.hpp"
namespace lms::ui namespace lms::ui
{ {
@@ -44,31 +45,12 @@ namespace lms::ui
addFunction("tr", &Wt::WTemplate::Functions::tr); addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("id", &Wt::WTemplate::Functions::id); addFunction("id", &Wt::WTemplate::Functions::id);
auto bindMenuItem{ [this](const std::string& var, const Wt::WString& title, TrackCollector::Mode mode) SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
{ sortMode->sortModeChanged.connect([this](TrackCollector::Mode mode)
auto* menuItem {bindNew<Wt::WPushButton>(var, title)};
menuItem->clicked().connect([this, mode, menuItem]
{ {
refreshView(mode); refreshView(mode);
_currentActiveItem->removeStyleClass("active");
menuItem->addStyleClass("active");
_currentActiveItem = menuItem;
}); });
if (mode == _defaultMode)
{
_currentActiveItem = menuItem;
_currentActiveItem->addStyleClass("active");
}
} };
bindMenuItem("random", Wt::WString::tr("Lms.Explore.random"), TrackCollector::Mode::Random);
bindMenuItem("starred", Wt::WString::tr("Lms.Explore.starred"), TrackCollector::Mode::Starred);
bindMenuItem("recently-played", Wt::WString::tr("Lms.Explore.recently-played"), TrackCollector::Mode::RecentlyPlayed);
bindMenuItem("most-played", Wt::WString::tr("Lms.Explore.most-played"), TrackCollector::Mode::MostPlayed);
bindMenuItem("recently-added", Wt::WString::tr("Lms.Explore.recently-added"), TrackCollector::Mode::RecentlyAdded);
bindMenuItem("all", Wt::WString::tr("Lms.Explore.all"), TrackCollector::Mode::All);
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML) bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
->clicked().connect([this] ->clicked().connect([this]
{ {
+4 -5
View File
@@ -42,14 +42,13 @@ namespace lms::ui
std::vector<db::TrackId> getAllTracks(); std::vector<db::TrackId> getAllTracks();
static constexpr TrackCollector::Mode _defaultMode {TrackCollector::Mode::Random}; static constexpr TrackCollector::Mode _defaultMode{ TrackCollector::Mode::Random };
static constexpr std::size_t _batchSize {6}; static constexpr std::size_t _batchSize{ 6 };
static constexpr std::size_t _maxCount {8000}; static constexpr std::size_t _maxCount{ 8000 };
Filters& _filters; Filters& _filters;
PlayQueueController& _playQueueController; PlayQueueController& _playQueueController;
Wt::WWidget* _currentActiveItem {}; InfiniteScrollingContainer* _container{};
InfiniteScrollingContainer* _container {};
TrackCollector _trackCollector; TrackCollector _trackCollector;
}; };
} // namespace lms::ui } // namespace lms::ui