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-->
<message id="Lms.Explore.Artists.template">
<div class="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="row">
<div class="col-lg-3">
${link-type class="form-select"}
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-3">
${link-type}
${sort-mode}
</div>
${artists}
</message>
@@ -42,4 +19,24 @@
${loading-indicator}
</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>
+15
View File
@@ -52,5 +52,20 @@
</div>
</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>
+2 -21
View File
@@ -3,27 +3,7 @@
<messages xmlns:if="Wt.WTemplate.conditions">
<message id="Lms.Explore.Releases.template">
<div class="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="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group">
${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"/>
@@ -33,6 +13,7 @@
<li>${play-shuffled class="dropdown-item"}</li>
</ul>
</div>
${sort-mode}
</div>
${releases}
</message>
+2 -4
View File
@@ -20,10 +20,8 @@
</message>
<message id="Lms.Explore.Search.template.artists">
<div class="row mb-3">
<div class="col-lg-3">
${link-type class="form-select"}
</div>
<div class="mb-3">
${link-type}
</div>
${artists}
</message>
+2 -21
View File
@@ -4,27 +4,7 @@
<!--FORMS message blocks-->
<message id="Lms.Explore.Tracks.template">
<div class="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="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group">
${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"/>
@@ -34,6 +14,7 @@
<li>${play-shuffled class="dropdown-item"}</li>
</ul>
</div>
${sort-mode}
</div>
${tracks}
</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)
{
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 pointer find(Session& session, TrackArtistLinkId linkId);
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);
ObjectPtr<Track> getTrack() const { return _track; }
+2
View File
@@ -43,6 +43,8 @@ add_executable(lms
ui/explore/ReleaseView.cpp
ui/explore/ReleaseTypes.cpp
ui/explore/SearchView.cpp
ui/explore/SortModeSelector.cpp
ui/explore/TrackArtistLinkTypeSelector.cpp
ui/explore/TrackCollector.cpp
ui/explore/TrackListHelpers.cpp
ui/explore/TrackListView.cpp
+7 -51
View File
@@ -18,62 +18,18 @@
*/
#include "ArtistListHelpers.hpp"
#include <Wt/WAnchor.h>
#include "database/Artist.hpp"
#include "database/Session.hpp"
#include "database/TrackArtistLink.hpp"
#include "core/EnumSet.hpp"
#include "LmsApplication.hpp"
#include "Utils.hpp"
namespace lms::ui::ArtistListHelpers
{
std::unique_ptr<Wt::WTemplate>
createEntry(const db::ObjectPtr<db::Artist>& artist)
{
auto res {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.entry"))};
res->bindWidget("name", utils::createArtistAnchor(artist));
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;
}
}
std::unique_ptr<Wt::WTemplate> createEntry(const db::ObjectPtr<db::Artist>& artist)
{
auto res{ std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.entry")) };
res->bindWidget("name", utils::createArtistAnchor(artist));
return res;
}
}
-1
View File
@@ -39,7 +39,6 @@ namespace lms::ui
namespace ArtistListHelpers
{
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 "core/ILogger.hpp"
#include "database/Artist.hpp"
#include "database/Session.hpp"
#include "database/TrackArtistLink.hpp"
#include "core/ILogger.hpp"
#include "common/InfiniteScrollingContainer.hpp"
#include "ArtistListHelpers.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"
#include "SortModeSelector.hpp"
#include "TrackArtistLinkTypeSelector.hpp"
namespace lms::ui
{
@@ -37,49 +39,22 @@ namespace lms::ui
Artists::Artists(Filters& filters)
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.Artists.template") }
, _artistCollector{ filters, _defaultMode, _maxCount }
, _artistCollector{ filters, _defaultSortMode, _maxCount }
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
auto bindMenuItem{ [this](const std::string& var, const Wt::WString& title, ArtistCollector::Mode mode)
{
auto* menuItem {bindNew<Wt::WPushButton>(var, title)};
menuItem->clicked().connect([=, this]
SortModeSelector* sortModeSelector{ bindNew<SortModeSelector>("sort-mode", _defaultSortMode) };
sortModeSelector->sortModeChanged.connect([this](ArtistCollector::Mode sortMode)
{
refreshView(mode);
_currentActiveItem->removeStyleClass("active");
menuItem->addStyleClass("active");
_currentActiveItem = menuItem;
refreshView(sortMode);
});
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);
});
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->onRequestElements.connect([this]
{
+19 -19
View File
@@ -30,28 +30,28 @@
namespace lms::ui
{
class Filters;
class InfiniteScrollingContainer;
class Filters;
class InfiniteScrollingContainer;
class Artists : public Wt::WTemplate
{
public:
Artists(Filters& filters);
class Artists : public Wt::WTemplate
{
public:
Artists(Filters& filters);
private:
void refreshView();
void refreshView(ArtistCollector::Mode mode);
void refreshView(std::optional<db::TrackArtistLinkType> linkType);
void addSome();
private:
void refreshView();
void refreshView(ArtistCollector::Mode mode);
void refreshView(std::optional<db::TrackArtistLinkType> linkType);
void addSome();
static constexpr std::size_t _batchSize {30};
static constexpr std::size_t _maxCount {8000};
static constexpr std::size_t _batchSize{ 30 };
static constexpr std::size_t _maxCount{ 8000 };
Wt::WWidget* _currentActiveItem {};
InfiniteScrollingContainer* _container {};
ArtistCollector _artistCollector;
Wt::WComboBox* _linkType {};
static constexpr ArtistCollector::Mode _defaultMode {ArtistCollector::Mode::Random};
};
Wt::WWidget* _currentLinkTypeActiveItem{};
InfiniteScrollingContainer* _container{};
ArtistCollector _artistCollector;
static constexpr ArtistCollector::Mode _defaultSortMode{ ArtistCollector::Mode::Random };
static constexpr std::optional<db::TrackArtistLinkType> _defaultLinkType;
};
} // namespace lms::ui
+75 -78
View File
@@ -32,97 +32,94 @@
#include "TrackListsView.hpp"
#include "TracksView.hpp"
namespace lms::ui {
namespace lms::ui
{
namespace
{
void handleContentsPathChange(Wt::WStackedWidget* stack)
{
enum Idx
{
IdxArtists = 0,
IdxArtist,
IdxTrackLists,
IdxTrackList,
IdxReleases,
IdxRelease,
IdxSearch,
IdxTracks,
};
namespace {
static const std::map<std::string, int> indexes =
{
{ "/artists", IdxArtists },
{ "/artist", IdxArtist },
{ "/tracklists", IdxTrackLists },
{ "/tracklist", IdxTrackList },
{ "/releases", IdxReleases },
{ "/release", IdxRelease },
{ "/search", IdxSearch },
{ "/tracks", IdxTracks },
};
void
handleContentsPathChange(Wt::WStackedWidget* stack)
{
enum Idx
{
IdxArtists = 0,
IdxArtist,
IdxTrackLists,
IdxTrackList,
IdxReleases,
IdxRelease,
IdxSearch,
IdxTracks,
};
for (const auto& index : indexes)
{
if (wApp->internalPathMatches(index.first))
{
stack->setCurrentIndex(index.second);
return;
}
}
}
static const std::map<std::string, int> indexes =
{
{ "/artists", IdxArtists },
{ "/artist", IdxArtist },
{ "/tracklists", IdxTrackLists },
{ "/tracklist", IdxTrackList },
{ "/releases", IdxReleases },
{ "/release", IdxRelease },
{ "/search", IdxSearch },
{ "/tracks", IdxTracks },
};
} // namespace
for (const auto& index : indexes)
{
if (wApp->internalPathMatches(index.first))
{
stack->setCurrentIndex(index.second);
return;
}
}
}
Explore::Explore(Filters& filters, PlayQueue& playQueue)
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.template") }
, _playQueueController{ filters, playQueue }
{
addFunction("tr", &Functions::tr);
} // namespace
// Contents
Wt::WStackedWidget* contentsStack{ bindNew<Wt::WStackedWidget>("contents") };
contentsStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
Explore::Explore(Filters& filters, PlayQueue& playQueue)
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.template")}
, _playQueueController {filters, playQueue}
{
addFunction("tr", &Functions::tr);
// same order as enum Idx
auto artists = std::make_unique<Artists>(filters);
contentsStack->addWidget(std::move(artists));
// Contents
Wt::WStackedWidget* contentsStack {bindNew<Wt::WStackedWidget>("contents")};
contentsStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
auto artist = std::make_unique<Artist>(filters, _playQueueController);
contentsStack->addWidget(std::move(artist));
// same order as enum Idx
auto artists = std::make_unique<Artists>(filters);
contentsStack->addWidget(std::move(artists));
auto trackLists{ std::make_unique<TrackLists>(filters) };
auto trackList{ std::make_unique<TrackList>(filters, _playQueueController) };
trackList->trackListDeleted.connect(trackLists.get(), &TrackLists::onTrackListDeleted);
contentsStack->addWidget(std::move(trackLists));
contentsStack->addWidget(std::move(trackList));
auto artist = std::make_unique<Artist>(filters, _playQueueController);
contentsStack->addWidget(std::move(artist));
auto releases = std::make_unique<Releases>(filters, _playQueueController);
contentsStack->addWidget(std::move(releases));
auto trackLists {std::make_unique<TrackLists>(filters)};
auto trackList {std::make_unique<TrackList>(filters, _playQueueController)};
trackList->trackListDeleted.connect(trackLists.get(), &TrackLists::onTrackListDeleted);
contentsStack->addWidget(std::move(trackLists));
contentsStack->addWidget(std::move(trackList));
auto release = std::make_unique<Release>(filters, _playQueueController);
contentsStack->addWidget(std::move(release));
auto releases = std::make_unique<Releases>(filters, _playQueueController);
contentsStack->addWidget(std::move(releases));
auto search = std::make_unique<SearchView>(filters, _playQueueController);
_search = search.get();
contentsStack->addWidget(std::move(search));
auto release = std::make_unique<Release>(filters, _playQueueController);
contentsStack->addWidget(std::move(release));
auto tracks = std::make_unique<Tracks>(filters, _playQueueController);
contentsStack->addWidget(std::move(tracks));
auto search = std::make_unique<SearchView>(filters, _playQueueController);
_search = search.get();
contentsStack->addWidget(std::move(search));
wApp->internalPathChanged().connect(this, [contentsStack]
{
handleContentsPathChange(contentsStack);
});
auto tracks = std::make_unique<Tracks>(filters, _playQueueController);
contentsStack->addWidget(std::move(tracks));
wApp->internalPathChanged().connect(this, [=]
{
handleContentsPathChange(contentsStack);
});
handleContentsPathChange(contentsStack);
}
void
Explore::search(const Wt::WString& searchText)
{
_search->refreshView(searchText);
}
handleContentsPathChange(contentsStack);
}
void Explore::search(const Wt::WString& searchText)
{
_search->refreshView(searchText);
}
} // namespace lms::ui
+14 -13
View File
@@ -20,25 +20,26 @@
#pragma once
#include <Wt/WTemplate.h>
#include "PlayQueueController.hpp"
namespace lms::ui
{
class Filters;
class SearchView;
class PlayQueue;
class Filters;
class SearchView;
class PlayQueue;
class Explore : public Wt::WTemplate
{
public:
Explore(Filters& filters, PlayQueue& playQueue);
class Explore : public Wt::WTemplate
{
public:
Explore(Filters& filters, PlayQueue& playQueue);
void search(const Wt::WString& searchText);
PlayQueueController& getPlayQueueController() { return _playQueueController; }
void search(const Wt::WString& searchText);
PlayQueueController& getPlayQueueController() { return _playQueueController; }
private:
PlayQueueController _playQueueController;
SearchView* _search {};
};
private:
PlayQueueController _playQueueController;
SearchView* _search{};
};
} // namespace lms::ui
+4 -22
View File
@@ -30,6 +30,7 @@
#include "explore/PlayQueueController.hpp"
#include "explore/ReleaseHelpers.hpp"
#include "LmsApplication.hpp"
#include "SortModeSelector.hpp"
namespace lms::ui
{
@@ -43,31 +44,12 @@ namespace lms::ui
addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("id", &Wt::WTemplate::Functions::id);
auto bindMenuItem{ [this](const std::string& var, const Wt::WString& title, ReleaseCollector::Mode mode)
{
auto* menuItem {bindNew<Wt::WPushButton>(var, title)};
menuItem->clicked().connect([this, mode, menuItem]
SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
sortMode->sortModeChanged.connect([this](ReleaseCollector::Mode sortMode)
{
refreshView(mode);
_currentActiveItem->removeStyleClass("active");
menuItem->addStyleClass("active");
_currentActiveItem = menuItem;
refreshView(sortMode);
});
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) };
playBtn->clicked().connect([this]
{
+20 -21
View File
@@ -26,31 +26,30 @@
namespace lms::ui
{
class Filters;
class InfiniteScrollingContainer;
class PlayQueueController;
class Filters;
class InfiniteScrollingContainer;
class PlayQueueController;
class Releases : public Template
{
public:
Releases(Filters& filters, PlayQueueController& playQueueController);
class Releases : public Template
{
public:
Releases(Filters& filters, PlayQueueController& playQueueController);
private:
void refreshView();
void refreshView(ReleaseCollector::Mode mode);
private:
void refreshView();
void refreshView(ReleaseCollector::Mode mode);
void addSome();
std::vector<db::ReleaseId> getAllReleases();
void addSome();
std::vector<db::ReleaseId> getAllReleases();
static constexpr std::size_t _maxItemsPerLine {6};
static constexpr std::size_t _batchSize {_maxItemsPerLine};
static constexpr std::size_t _maxCount {_maxItemsPerLine * 500};
static constexpr std::size_t _maxItemsPerLine{ 6 };
static constexpr std::size_t _batchSize{ _maxItemsPerLine };
static constexpr std::size_t _maxCount{ _maxItemsPerLine * 500 };
PlayQueueController& _playQueueController;
Wt::WWidget* _currentActiveItem {};
InfiniteScrollingContainer* _container {};
ReleaseCollector _releaseCollector;
static constexpr ReleaseCollector::Mode _defaultMode {ReleaseCollector::Mode::Random};
};
PlayQueueController& _playQueueController;
InfiniteScrollingContainer* _container{};
ReleaseCollector _releaseCollector;
static constexpr ReleaseCollector::Mode _defaultMode{ ReleaseCollector::Mode::Random };
};
} // namespace lms::ui
+3 -10
View File
@@ -31,6 +31,7 @@
#include "Filters.hpp"
#include "LmsApplication.hpp"
#include "ReleaseHelpers.hpp"
#include "TrackArtistLinkTypeSelector.hpp"
#include "TrackListHelpers.hpp"
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")) };
_artistLinkType = artistResults->bindNew<Wt::WComboBox>("link-type");
_artistLinkType->setModel(ArtistListHelpers::createArtistLinkTypesModel());
_artistLinkType->changed().connect([this]
TrackArtistLinkTypeSelector* linkTypeSelector{ artistResults->bindNew<TrackArtistLinkTypeSelector>("link-type", std::nullopt)};
linkTypeSelector->linkTypeChanged.connect([this](std::optional<TrackArtistLinkType> linkType)
{
const std::optional<TrackArtistLinkType> linkType{ static_cast<ArtistLinkTypesModel*>(_artistLinkType->model().get())->getValue(_artistLinkType->currentIndex()) };
refreshView(linkType);
});
@@ -101,12 +100,6 @@ namespace lms::ui
{
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
-2
View File
@@ -85,8 +85,6 @@ namespace lms::ui
InfiniteScrollingContainer* _artists{};
InfiniteScrollingContainer* _releases{};
InfiniteScrollingContainer* _tracks{};
Wt::WComboBox* _artistLinkType{};
};
} // 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/TrackListHelpers.hpp"
#include "LmsApplication.hpp"
#include "SortModeSelector.hpp"
namespace lms::ui
{
@@ -44,31 +45,12 @@ namespace lms::ui
addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("id", &Wt::WTemplate::Functions::id);
auto bindMenuItem{ [this](const std::string& var, const Wt::WString& title, TrackCollector::Mode mode)
{
auto* menuItem {bindNew<Wt::WPushButton>(var, title)};
menuItem->clicked().connect([this, mode, menuItem]
SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
sortMode->sortModeChanged.connect([this](TrackCollector::Mode 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)
->clicked().connect([this]
{
+20 -21
View File
@@ -26,31 +26,30 @@
namespace lms::ui
{
class Filters;
class InfiniteScrollingContainer;
class PlayQueueController;
class Filters;
class InfiniteScrollingContainer;
class PlayQueueController;
class Tracks : public Template
{
public:
Tracks(Filters& filters, PlayQueueController& playQueueController);
class Tracks : public Template
{
public:
Tracks(Filters& filters, PlayQueueController& playQueueController);
private:
void refreshView();
void refreshView(TrackCollector::Mode mode);
void addSome();
private:
void refreshView();
void refreshView(TrackCollector::Mode mode);
void addSome();
std::vector<db::TrackId> getAllTracks();
std::vector<db::TrackId> getAllTracks();
static constexpr TrackCollector::Mode _defaultMode {TrackCollector::Mode::Random};
static constexpr std::size_t _batchSize {6};
static constexpr std::size_t _maxCount {8000};
static constexpr TrackCollector::Mode _defaultMode{ TrackCollector::Mode::Random };
static constexpr std::size_t _batchSize{ 6 };
static constexpr std::size_t _maxCount{ 8000 };
Filters& _filters;
PlayQueueController& _playQueueController;
Wt::WWidget* _currentActiveItem {};
InfiniteScrollingContainer* _container {};
TrackCollector _trackCollector;
};
Filters& _filters;
PlayQueueController& _playQueueController;
InfiniteScrollingContainer* _container{};
TrackCollector _trackCollector;
};
} // namespace lms::ui