feat: add playlist, recent files and MPRIS backends
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6dc9377de1
commit
54ad4eecf3
18 files changed
+2240
-4
No files matched your search
@@ -0,0 +1,122 @@
|
||||
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "recentfilesmodel.h"
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Stored even when fewer are shown, so raising the limit brings old entries back.
|
||||
constexpr int StoredFiles = 50;
|
||||
|
||||
KConfigGroup configGroup()
|
||||
{
|
||||
return KSharedConfig::openStateConfig()->group(u"RecentFiles"_s);
|
||||
}
|
||||
}
|
||||
|
||||
RecentFilesModel::RecentFilesModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
const QStringList urls = configGroup().readEntry(u"Urls"_s, QStringList());
|
||||
for (const QString &url : urls) {
|
||||
m_urls.append(QUrl(url));
|
||||
}
|
||||
}
|
||||
|
||||
int RecentFilesModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : count();
|
||||
}
|
||||
|
||||
QVariant RecentFilesModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!checkIndex(index, CheckIndexOption::IndexIsValid)) {
|
||||
return {};
|
||||
}
|
||||
const QUrl &url = m_urls.at(index.row());
|
||||
switch (role) {
|
||||
case UrlRole:
|
||||
return url;
|
||||
case Qt::DisplayRole:
|
||||
case NameRole:
|
||||
return url.isLocalFile() ? url.fileName() : url.toDisplayString();
|
||||
case PathRole:
|
||||
return url.isLocalFile() ? url.toLocalFile() : url.toDisplayString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> RecentFilesModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{UrlRole, "url"},
|
||||
{NameRole, "name"},
|
||||
{PathRole, "path"},
|
||||
};
|
||||
}
|
||||
|
||||
int RecentFilesModel::count() const
|
||||
{
|
||||
return int(std::min<qsizetype>(m_urls.size(), m_maxCount));
|
||||
}
|
||||
|
||||
int RecentFilesModel::maxCount() const
|
||||
{
|
||||
return m_maxCount;
|
||||
}
|
||||
|
||||
void RecentFilesModel::setMaxCount(int maxCount)
|
||||
{
|
||||
maxCount = std::clamp(maxCount, 0, StoredFiles);
|
||||
if (maxCount == m_maxCount) {
|
||||
return;
|
||||
}
|
||||
beginResetModel();
|
||||
m_maxCount = maxCount;
|
||||
endResetModel();
|
||||
Q_EMIT maxCountChanged();
|
||||
Q_EMIT countChanged();
|
||||
}
|
||||
|
||||
void RecentFilesModel::add(const QUrl &url)
|
||||
{
|
||||
if (!url.isValid()) {
|
||||
return;
|
||||
}
|
||||
beginResetModel();
|
||||
m_urls.removeAll(url);
|
||||
m_urls.prepend(url);
|
||||
if (m_urls.size() > StoredFiles) {
|
||||
m_urls.resize(StoredFiles);
|
||||
}
|
||||
endResetModel();
|
||||
Q_EMIT countChanged();
|
||||
save();
|
||||
}
|
||||
|
||||
void RecentFilesModel::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
m_urls.clear();
|
||||
endResetModel();
|
||||
Q_EMIT countChanged();
|
||||
save();
|
||||
}
|
||||
|
||||
void RecentFilesModel::save() const
|
||||
{
|
||||
QStringList urls;
|
||||
for (const QUrl &url : m_urls) {
|
||||
urls.append(url.toString());
|
||||
}
|
||||
KConfigGroup group = configGroup();
|
||||
group.writeEntry(u"Urls"_s, urls);
|
||||
group.sync();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2026 Tri Vo Khac
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
|
||||
// Recently opened files, newest first, kept in the application's state file.
|
||||
class RecentFilesModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
// Rows beyond this are hidden (but kept, in case it is raised again).
|
||||
Q_PROPERTY(int maxCount READ maxCount WRITE setMaxCount NOTIFY maxCountChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
UrlRole = Qt::UserRole + 1,
|
||||
NameRole,
|
||||
PathRole,
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit RecentFilesModel(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = {}) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
int count() const;
|
||||
int maxCount() const;
|
||||
void setMaxCount(int maxCount);
|
||||
|
||||
// Moves \a url to the top, adding it if needed.
|
||||
Q_INVOKABLE void add(const QUrl &url);
|
||||
Q_INVOKABLE void clear();
|
||||
|
||||
Q_SIGNALS:
|
||||
void countChanged();
|
||||
void maxCountChanged();
|
||||
|
||||
private:
|
||||
void save() const;
|
||||
|
||||
QList<QUrl> m_urls;
|
||||
int m_maxCount = 10;
|
||||
};
|
||||
Reference in new issue
Block a user