From ddfd17c67c229634ed2513457814aa3d50d1532a Mon Sep 17 00:00:00 2001 From: Joshua Strobl Date: Sun, 27 Oct 2024 19:16:30 +0200 Subject: [PATCH] start implementing model for koto artist --- desktop/CMakeLists.txt | 7 +- desktop/datalake/album.cpp | 4 ++ desktop/datalake/artist.cpp | 8 +++ desktop/datalake/cartographer.cpp | 28 +++++--- desktop/datalake/cartographer.hpp | 37 +++++++--- desktop/datalake/models.cpp | 45 ++++++++++++ desktop/datalake/structs.hpp | 111 ++++++++++++++++++++++++++++-- desktop/datalake/track.cpp | 17 +++++ desktop/main.cpp | 4 +- desktop/qml/HomePage.qml | 33 +++++++-- desktop/qml/PrimaryNavigation.qml | 2 +- desktop/qml/Root.qml | 5 +- 12 files changed, 267 insertions(+), 34 deletions(-) create mode 100644 desktop/datalake/models.cpp diff --git a/desktop/CMakeLists.txt b/desktop/CMakeLists.txt index bf95279..4dd9a9a 100644 --- a/desktop/CMakeLists.txt +++ b/desktop/CMakeLists.txt @@ -1,5 +1,5 @@ find_package(Qt6 6.4 REQUIRED COMPONENTS Quick QuickControls2 Sql) -find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) +find_package(ECM REQUIRED NO_MODULE) find_package(KF6Baloo) find_package(KF6FileMetaData) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) @@ -19,10 +19,15 @@ qt_add_executable(com.github.joshstrobl.koto datalake/cartographer.cpp datalake/database.cpp datalake/indexer.cpp + datalake/models.cpp datalake/track.cpp + datalake/cartographer.hpp + datalake/structs.hpp main.cpp + datalake/models.cpp ) +target_include_directories(com.github.joshstrobl.koto PUBLIC datalake includes) ecm_add_qml_module(com.github.joshstrobl.koto URI "com.github.joshstrobl.koto" GENERATE_PLUGIN_SOURCE) ecm_target_qml_sources(com.github.joshstrobl.koto diff --git a/desktop/datalake/album.cpp b/desktop/datalake/album.cpp index 1550206..3e80def 100644 --- a/desktop/datalake/album.cpp +++ b/desktop/datalake/album.cpp @@ -82,6 +82,10 @@ std::optional KotoAlbum::getYear() { return this->year; } +int KotoAlbum::getYearQml() { + return this->year.value_or(0); +} + void KotoAlbum::removeTrack(KotoTrack* track) { this->tracks.removeOne(track); } diff --git a/desktop/datalake/artist.cpp b/desktop/datalake/artist.cpp index a72f6d8..45dd72f 100644 --- a/desktop/datalake/artist.cpp +++ b/desktop/datalake/artist.cpp @@ -62,10 +62,18 @@ QString KotoArtist::getName() { return QString {this->name}; } +QString KotoArtist::getPath() { + return QString {this->path}; +} + QList KotoArtist::getTracks() { return QList {this->tracks}; } +QUuid KotoArtist::getUuid() { + return this->uuid; +} + void KotoArtist::removeAlbum(KotoAlbum* album) { this->albums.removeOne(album); } diff --git a/desktop/datalake/cartographer.cpp b/desktop/datalake/cartographer.cpp index fe1eccb..a5ae4b0 100644 --- a/desktop/datalake/cartographer.cpp +++ b/desktop/datalake/cartographer.cpp @@ -1,8 +1,11 @@ #include "cartographer.hpp" -Cartographer::Cartographer() - : i_albums(QHash()), - i_artists(QHash()), +#include + +Cartographer::Cartographer(QObject* parent) + : QObject(parent), + i_albums(QHash()), + i_artists_model(new KotoArtistModel(QList())), i_artists_by_name(QHash()), i_tracks(QHash()) {} @@ -16,7 +19,7 @@ void Cartographer::addAlbum(KotoAlbum* album) { } void Cartographer::addArtist(KotoArtist* artist) { - this->i_artists.insert(artist->uuid, artist); + this->i_artists_model->addArtist(artist); this->i_artists_by_name.insert(artist->getName(), artist); } @@ -29,17 +32,20 @@ std::optional Cartographer::getAlbum(QUuid uuid) { return album ? std::optional {album} : std::nullopt; } -QList Cartographer::getAlbums() { - return this->i_albums.values(); -} - std::optional Cartographer::getArtist(QUuid uuid) { - auto artist = this->i_artists.value(uuid, nullptr); - return artist ? std::optional {artist} : std::nullopt; + for (auto artist : this->i_artists_model->getArtists()) { + if (artist->uuid == uuid) { return std::optional {artist}; } + } + return std::nullopt; } QList Cartographer::getArtists() { - return this->i_artists.values(); + return this->i_artists_model->getArtists(); +} + +KotoArtistModel* Cartographer::getArtistsModel() { + // if (this->i_artists_model == nullptr) { this->i_artists_model = new KotoArtistModel(this->i_artists); } + return this->i_artists_model; } std::optional Cartographer::getArtist(QString name) { diff --git a/desktop/datalake/cartographer.hpp b/desktop/datalake/cartographer.hpp index 27f5813..baad892 100644 --- a/desktop/datalake/cartographer.hpp +++ b/desktop/datalake/cartographer.hpp @@ -1,21 +1,40 @@ #pragma once +#include + #include +#include +#include #include #include #include #include "structs.hpp" -class Cartographer { - public: - Cartographer(); - static Cartographer& instance(); - static Cartographer* create() { return &instance(); } +class Cartographer : public QObject { + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + // Q_PROPERTY(QQmlListProperty albums READ getAlbumsQml) + Q_PROPERTY(KotoArtistModel* artists READ getArtistsModel) + // Q_PROPERTY(QQmlListProperty tracks READ getTracksQml) + + public: + Cartographer(QObject* parent = nullptr); + static Cartographer& instance(); + // static Cartographer* create(QQmlEngine* engine, QJSEngine*) { + // engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership); + // return &instance(); + // } + + void addAlbum(KotoAlbum* album); + void addArtist(KotoArtist* artist); + void addTrack(KotoTrack* track); + + // QQmlListProperty getAlbumsQml(); + KotoArtistModel* getArtistsModel(); + // QQmlListProperty getTracksQml(); - void addAlbum(KotoAlbum* album); - void addArtist(KotoArtist* artist); - void addTrack(KotoTrack* track); std::optional getAlbum(QUuid uuid); QList getAlbums(); std::optional getArtist(QUuid uuid); @@ -26,7 +45,7 @@ class Cartographer { private: QHash i_albums; - QHash i_artists; + KotoArtistModel* i_artists_model; QHash i_artists_by_name; QHash i_tracks; }; diff --git a/desktop/datalake/models.cpp b/desktop/datalake/models.cpp new file mode 100644 index 0000000..09ade97 --- /dev/null +++ b/desktop/datalake/models.cpp @@ -0,0 +1,45 @@ +#include "structs.hpp" + +KotoArtistModel::~KotoArtistModel() { + this->beginResetModel(); + this->m_artists.clear(); + this->endResetModel(); +} + +void KotoArtistModel::addArtist(KotoArtist* artist) { + this->beginInsertRows(QModelIndex(), this->m_artists.count(), this->m_artists.count()); + this->m_artists.append(artist); + this->endInsertRows(); +} + +int KotoArtistModel::rowCount(const QModelIndex& parent) const { + return this->m_artists.count(); +} + +QVariant KotoArtistModel::data(const QModelIndex& index, int role) const { + if (!index.isValid()) { return {}; } + + if (index.row() >= this->m_artists.size()) { return {}; } + + if (role == KotoArtistRoles::NameRole) { + return this->m_artists.at(index.row())->getName(); + } else if (role == KotoArtistRoles::PathRole) { + return this->m_artists.at(index.row())->getPath(); + } else if (role == KotoArtistRoles::UuidRole) { + return this->m_artists.at(index.row())->uuid; + } else { + return {}; + } +} + +QList KotoArtistModel::getArtists() { + return this->m_artists; +} + +QHash KotoArtistModel::roleNames() const { + QHash roles; + roles[NameRole] = QByteArrayLiteral("name"); + roles[PathRole] = QByteArrayLiteral("path"); + roles[UuidRole] = QByteArrayLiteral("uuid"); + return roles; +} diff --git a/desktop/datalake/structs.hpp b/desktop/datalake/structs.hpp index 5514d64..fcb9501 100644 --- a/desktop/datalake/structs.hpp +++ b/desktop/datalake/structs.hpp @@ -1,5 +1,8 @@ #pragma once +#include + #include +#include #include #include #include @@ -8,14 +11,25 @@ #include class KotoArtist; +class KotoArtistModel; class KotoAlbum; +class KotoAlbumModel; class KotoTrack; +class KotoTrackModel; + +class KotoArtist : public QObject { + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QList albums READ getAlbums NOTIFY albumsChanged) + Q_PROPERTY(QList tracks READ getTracks NOTIFY tracksChanged) + Q_PROPERTY(QUuid uuid READ getUuid) -class KotoArtist { public: KotoArtist(); static KotoArtist* fromDb(const QSqlQuery& query, const QSqlRecord& record); - ~KotoArtist(); + virtual ~KotoArtist(); QUuid uuid; @@ -27,11 +41,18 @@ class KotoArtist { QString getName(); QString getPath(); QList getTracks(); + QUuid getUuid(); void removeAlbum(KotoAlbum* album); void removeTrack(KotoTrack* track); void setName(QString str); void setPath(QString str); + signals: + void albumsChanged(); + void nameChanged(); + void pathChanged(); + void tracksChanged(); + private: QString path; QString name; @@ -40,11 +61,46 @@ class KotoArtist { QList tracks; }; -class KotoAlbum { +class KotoArtistModel : public QAbstractListModel { + Q_OBJECT + + public: + explicit KotoArtistModel(const QList& artists, QObject* parent = nullptr) : QAbstractListModel(parent), m_artists(artists) {} + + void addArtist(KotoArtist* artist); + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + virtual ~KotoArtistModel(); + + QList getArtists(); + + enum KotoArtistRoles { + NameRole = Qt::UserRole + 1, + PathRole, + UuidRole, + }; + + private: + QList m_artists; +}; + +class KotoAlbum : public QObject { + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QString albumArtPath READ getAlbumArtPath WRITE setAlbumArtPath NOTIFY albumArtChanged) + Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged) + Q_PROPERTY(QList genres READ getGenres WRITE setGenres NOTIFY genresChanged) + Q_PROPERTY(QString narrator READ getNarrator WRITE setNarrator NOTIFY narratorChanged) + Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(QList tracks READ getTracks NOTIFY tracksChanged) + Q_PROPERTY(int year READ getYearQml WRITE setYear NOTIFY yearChanged) + public: KotoAlbum(); static KotoAlbum* fromDb(const QSqlQuery& query, const QSqlRecord& record); - ~KotoAlbum(); + virtual ~KotoAlbum(); QUuid uuid; QUuid artist_uuid; @@ -58,6 +114,7 @@ class KotoAlbum { QString getTitle(); QList getTracks(); std::optional getYear(); + int getYearQml(); void addTrack(KotoTrack* track); void removeTrack(KotoTrack* track); @@ -69,6 +126,16 @@ class KotoAlbum { void setTitle(QString str); void setYear(int num); + signals: + void albumArtChanged(); + void descriptionChanged(); + void genresChanged(); + void narratorChanged(); + void pathChanged(); + void titleChanged(); + void tracksChanged(); + void yearChanged(); + private: QString title; QString description; @@ -82,18 +149,36 @@ class KotoAlbum { QString album_art_path; }; -class KotoTrack { +class KotoTrack : public QObject { + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QString album_uuid READ getAlbumUuid NOTIFY albumChanged) + Q_PROPERTY(QUuid artist_uuid READ getArtistUuid NOTIFY artistChanged) + Q_PROPERTY(QUuid uuid READ getUuid) + Q_PROPERTY(int discNumber READ getDiscNumber WRITE setDiscNumber NOTIFY discNumberChanged) + Q_PROPERTY(int duration READ getDuration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QStringList genres READ getGenres WRITE setGenres NOTIFY genresChanged) + Q_PROPERTY(QString lyrics READ getLyrics WRITE setLyrics NOTIFY lyricsChanged) + Q_PROPERTY(QString narrator READ getNarrator WRITE setNarrator NOTIFY narratorChanged) + Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(int trackNumber READ getTrackNumber WRITE setTrackNumber NOTIFY trackNumberChanged) + Q_PROPERTY(int year READ getYear WRITE setYear NOTIFY yearChanged) + public: KotoTrack(); // No-op constructor static KotoTrack* fromDb(const QSqlQuery& query, const QSqlRecord& record); static KotoTrack* fromMetadata(const KFileMetaData::SimpleExtractionResult& metadata, const QFileInfo& info); - ~KotoTrack(); + virtual ~KotoTrack(); std::optional album_uuid; QUuid artist_uuid; QUuid uuid; void commit(); + QString getAlbumUuid(); + QUuid getArtistUuid(); + int getDiscNumber(); int getDuration(); QStringList getGenres(); QString getLyrics(); @@ -101,6 +186,7 @@ class KotoTrack { QString getPath(); QString getTitle(); int getTrackNumber(); + QUuid getUuid(); int getYear(); void setAlbum(KotoAlbum* album); @@ -115,6 +201,19 @@ class KotoTrack { void setTrackNumber(int num); void setYear(int num); + signals: + void albumChanged(); + void artistChanged(); + void discNumberChanged(); + void durationChanged(); + void genresChanged(); + void lyricsChanged(); + void narratorChanged(); + void pathChanged(); + void titleChanged(); + void trackNumberChanged(); + void yearChanged(); + private: int disc_number; int duration; diff --git a/desktop/datalake/track.cpp b/desktop/datalake/track.cpp index 3aedcdf..5e5ee29 100644 --- a/desktop/datalake/track.cpp +++ b/desktop/datalake/track.cpp @@ -102,6 +102,19 @@ void KotoTrack::commit() { query.exec(); } +QString KotoTrack::getAlbumUuid() { + if (!this->album_uuid.has_value()) return this->album_uuid.value().toString(); + return {}; +} + +QUuid KotoTrack::getArtistUuid() { + return this->artist_uuid; +} + +int KotoTrack::getDiscNumber() { + return this->disc_number; +} + int KotoTrack::getDuration() { return this->duration; } @@ -130,6 +143,10 @@ int KotoTrack::getTrackNumber() { return this->track_number; } +QUuid KotoTrack::getUuid() { + return this->uuid; +} + int KotoTrack::getYear() { return this->year; } diff --git a/desktop/main.cpp b/desktop/main.cpp index f4d6b01..6a3dfcc 100644 --- a/desktop/main.cpp +++ b/desktop/main.cpp @@ -21,12 +21,14 @@ int main(int argc, char* argv[]) { if (engine.rootObjects().isEmpty()) { return -1; } std::thread([]() { - Cartographer::create(); + // Cartographer::create(); KotoConfig::create(); KotoDatabase::create(); KotoDatabase::instance().connect(); + std::cout << "???" << std::endl; + // If we needed to bootstrap, index all libraries, otherwise load the database if (KotoDatabase::instance().requiredBootstrap()) { indexAllLibraries(); diff --git a/desktop/qml/HomePage.qml b/desktop/qml/HomePage.qml index 963f53d..b98e179 100644 --- a/desktop/qml/HomePage.qml +++ b/desktop/qml/HomePage.qml @@ -3,10 +3,35 @@ import QtQuick.Controls as Controls import QtQuick.Layouts import org.kde.kirigami as Kirigami -Kirigami.Page { - ColumnLayout { - Controls.Label { - text: qsTr("blah!") +Kirigami.ScrollablePage { + Component { + id: listDelegate + + Controls.ItemDelegate { + required property string name + + text: name + width: ListView.view.width } } + // ListModel { + // id: blah + // + // ListElement { + // name: "blah1" + // } + // ListElement { + // name: "blah2" + // } + // ListElement { + // name: "blah3" + // } + // } + ListView { + Layout.fillHeight: true + Layout.fillWidth: true + delegate: listDelegate + //model: blah + model: Cartographer.artists + } } diff --git a/desktop/qml/PrimaryNavigation.qml b/desktop/qml/PrimaryNavigation.qml index 835a041..4892a40 100644 --- a/desktop/qml/PrimaryNavigation.qml +++ b/desktop/qml/PrimaryNavigation.qml @@ -20,7 +20,7 @@ Kirigami.GlobalDrawer { drawerOpen: !isMobile() edge: Qt.LeftEdge height: parent.height - modal: isMobile() + modal: false actions: [ Kirigami.Action { diff --git a/desktop/qml/Root.qml b/desktop/qml/Root.qml index 9016e11..96d72ea 100644 --- a/desktop/qml/Root.qml +++ b/desktop/qml/Root.qml @@ -9,11 +9,14 @@ Kirigami.Page { ColumnLayout { id: rootLayout - Layout.fillWidth: true + anchors.fill: parent Controls.StackView { id: rootStack + Layout.fillHeight: true + Layout.fillWidth: true + initialItem: HomePage { } }