mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-02-09 21:13:08 +00:00
Media: add per-game screenshots manager and gallery
This commit is contained in:
parent
27f5a31561
commit
3ec2badcd7
8 changed files with 483 additions and 0 deletions
|
|
@ -8,6 +8,8 @@ add_executable(alakarte
|
||||||
gamesortfiltermodel.cpp
|
gamesortfiltermodel.cpp
|
||||||
game.cpp
|
game.cpp
|
||||||
gamepadmanager.cpp
|
gamepadmanager.cpp
|
||||||
|
mediamanager.cpp
|
||||||
|
screenshotmodel.cpp
|
||||||
platformimporter.cpp
|
platformimporter.cpp
|
||||||
steamimporter.cpp
|
steamimporter.cpp
|
||||||
lutrisimporter.cpp
|
lutrisimporter.cpp
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ App::App(QObject *parent)
|
||||||
, m_gameModel(new GameModel(this))
|
, m_gameModel(new GameModel(this))
|
||||||
, m_launcher(new GameLauncher(this))
|
, m_launcher(new GameLauncher(this))
|
||||||
, m_steamGridDB(new SteamGridDB(this))
|
, m_steamGridDB(new SteamGridDB(this))
|
||||||
|
, m_mediaManager(new MediaManager(this))
|
||||||
, m_config(new Config(this))
|
, m_config(new Config(this))
|
||||||
{
|
{
|
||||||
loadLibrary();
|
loadLibrary();
|
||||||
|
|
@ -172,6 +173,11 @@ SteamGridDB *App::steamGridDB() const
|
||||||
return m_steamGridDB;
|
return m_steamGridDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MediaManager *App::mediaManager() const
|
||||||
|
{
|
||||||
|
return m_mediaManager;
|
||||||
|
}
|
||||||
|
|
||||||
Config *App::config() const
|
Config *App::config() const
|
||||||
{
|
{
|
||||||
return m_config;
|
return m_config;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "gamelauncher.h"
|
#include "gamelauncher.h"
|
||||||
#include "gamemodel.h"
|
#include "gamemodel.h"
|
||||||
|
#include "mediamanager.h"
|
||||||
#include "steamgriddb.h"
|
#include "steamgriddb.h"
|
||||||
|
|
||||||
class App : public QObject
|
class App : public QObject
|
||||||
|
|
@ -22,6 +23,7 @@ class App : public QObject
|
||||||
Q_PROPERTY(GameModel *gameModel READ gameModel CONSTANT)
|
Q_PROPERTY(GameModel *gameModel READ gameModel CONSTANT)
|
||||||
Q_PROPERTY(GameLauncher *launcher READ launcher CONSTANT)
|
Q_PROPERTY(GameLauncher *launcher READ launcher CONSTANT)
|
||||||
Q_PROPERTY(SteamGridDB *steamGridDB READ steamGridDB CONSTANT)
|
Q_PROPERTY(SteamGridDB *steamGridDB READ steamGridDB CONSTANT)
|
||||||
|
Q_PROPERTY(MediaManager *mediaManager READ mediaManager CONSTANT)
|
||||||
Q_PROPERTY(Config *config READ config CONSTANT)
|
Q_PROPERTY(Config *config READ config CONSTANT)
|
||||||
Q_PROPERTY(bool importing READ importing NOTIFY importingChanged)
|
Q_PROPERTY(bool importing READ importing NOTIFY importingChanged)
|
||||||
Q_PROPERTY(QString importStatus READ importStatus NOTIFY importStatusChanged)
|
Q_PROPERTY(QString importStatus READ importStatus NOTIFY importStatusChanged)
|
||||||
|
|
@ -33,6 +35,7 @@ public:
|
||||||
GameModel *gameModel() const;
|
GameModel *gameModel() const;
|
||||||
GameLauncher *launcher() const;
|
GameLauncher *launcher() const;
|
||||||
SteamGridDB *steamGridDB() const;
|
SteamGridDB *steamGridDB() const;
|
||||||
|
MediaManager *mediaManager() const;
|
||||||
Config *config() const;
|
Config *config() const;
|
||||||
|
|
||||||
bool importing() const;
|
bool importing() const;
|
||||||
|
|
@ -72,6 +75,7 @@ private:
|
||||||
GameModel *m_gameModel;
|
GameModel *m_gameModel;
|
||||||
GameLauncher *m_launcher;
|
GameLauncher *m_launcher;
|
||||||
SteamGridDB *m_steamGridDB;
|
SteamGridDB *m_steamGridDB;
|
||||||
|
MediaManager *m_mediaManager;
|
||||||
Config *m_config;
|
Config *m_config;
|
||||||
|
|
||||||
bool m_importing = false;
|
bool m_importing = false;
|
||||||
|
|
|
||||||
145
src/mediamanager.cpp
Normal file
145
src/mediamanager.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include "mediamanager.h"
|
||||||
|
|
||||||
|
#include "game.h"
|
||||||
|
#include "screenshotmodel.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
MediaManager::MediaManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaManager::screenshotsBasePath() const
|
||||||
|
{
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/media/screenshots");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaManager::gameId(Game *game) const
|
||||||
|
{
|
||||||
|
return game ? game->id() : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaManager::ensureDirectory(const QString &path) const
|
||||||
|
{
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
QDir parent;
|
||||||
|
parent.mkpath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QDir(path).absolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaManager::gameScreenshotsFolderPath(Game *game) const
|
||||||
|
{
|
||||||
|
const QString id = gameId(game);
|
||||||
|
if (id.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return screenshotsBasePath() + QLatin1Char('/') + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaManager::screenshotsFolderPath(Game *game) const
|
||||||
|
{
|
||||||
|
const QString path = gameScreenshotsFolderPath(game);
|
||||||
|
return ensureDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl MediaManager::screenshotsFolderUrl(Game *game) const
|
||||||
|
{
|
||||||
|
const QString path = screenshotsFolderPath(game);
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return QUrl::fromLocalFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject *MediaManager::screenshotsModel(Game *game)
|
||||||
|
{
|
||||||
|
const QString id = gameId(game);
|
||||||
|
if (id.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_models.contains(id) && m_models.value(id)) {
|
||||||
|
return m_models.value(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *model = new ScreenshotModel(this);
|
||||||
|
model->setDirectoryPath(screenshotsFolderPath(game));
|
||||||
|
m_models.insert(id, model);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaManager::uniqueDestinationPath(const QString &folderPath, const QString &fileName) const
|
||||||
|
{
|
||||||
|
const QString baseName = QFileInfo(fileName).completeBaseName();
|
||||||
|
const QString suffix = QFileInfo(fileName).suffix();
|
||||||
|
|
||||||
|
const QString cleanBase = baseName.isEmpty() ? QStringLiteral("screenshot") : baseName;
|
||||||
|
const QString cleanSuffix = suffix.isEmpty() ? QStringLiteral("png") : suffix;
|
||||||
|
|
||||||
|
QString candidate = folderPath + QLatin1Char('/') + cleanBase + QLatin1Char('.') + cleanSuffix;
|
||||||
|
if (!QFileInfo::exists(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < 10000; ++i) {
|
||||||
|
candidate = folderPath + QLatin1Char('/') + cleanBase + QLatin1Char('_') + QString::number(i) + QLatin1Char('.') + cleanSuffix;
|
||||||
|
if (!QFileInfo::exists(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderPath + QLatin1Char('/') + cleanBase + QLatin1Char('_') + QString::number(QDateTime::currentMSecsSinceEpoch()) + QLatin1Char('.') + cleanSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaManager::importScreenshot(Game *game, const QUrl &sourceUrl)
|
||||||
|
{
|
||||||
|
if (!game) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sourceUrl.isLocalFile()) {
|
||||||
|
Q_EMIT importError(game, tr("Only local files are supported"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString srcPath = sourceUrl.toLocalFile();
|
||||||
|
QFileInfo srcInfo(srcPath);
|
||||||
|
if (!srcInfo.exists() || !srcInfo.isFile()) {
|
||||||
|
Q_EMIT importError(game, tr("Source file does not exist"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString folder = screenshotsFolderPath(game);
|
||||||
|
if (folder.isEmpty()) {
|
||||||
|
Q_EMIT importError(game, tr("Failed to create screenshots folder"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString destPath = uniqueDestinationPath(folder, srcInfo.fileName());
|
||||||
|
|
||||||
|
if (!QFile::copy(srcPath, destPath)) {
|
||||||
|
Q_EMIT importError(game, tr("Failed to copy file"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString id = gameId(game);
|
||||||
|
if (m_models.contains(id) && m_models.value(id)) {
|
||||||
|
m_models.value(id)->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT screenshotsImported(game, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
41
src/mediamanager.h
Normal file
41
src/mediamanager.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class Game;
|
||||||
|
class ScreenshotModel;
|
||||||
|
|
||||||
|
class MediaManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
Q_PROPERTY(QString screenshotsBasePath READ screenshotsBasePath CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MediaManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString screenshotsBasePath() const;
|
||||||
|
|
||||||
|
Q_INVOKABLE QString screenshotsFolderPath(Game *game) const;
|
||||||
|
Q_INVOKABLE QUrl screenshotsFolderUrl(Game *game) const;
|
||||||
|
|
||||||
|
Q_INVOKABLE QObject *screenshotsModel(Game *game);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool importScreenshot(Game *game, const QUrl &sourceUrl);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void screenshotsImported(Game *game, int count);
|
||||||
|
void importError(Game *game, const QString &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString gameId(Game *game) const;
|
||||||
|
QString gameScreenshotsFolderPath(Game *game) const;
|
||||||
|
QString ensureDirectory(const QString &path) const;
|
||||||
|
QString uniqueDestinationPath(const QString &folderPath, const QString &fileName) const;
|
||||||
|
|
||||||
|
QHash<QString, QPointer<ScreenshotModel>> m_models;
|
||||||
|
};
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Dialogs
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
@ -13,6 +14,8 @@ Kirigami.OverlaySheet {
|
||||||
|
|
||||||
property var game
|
property var game
|
||||||
|
|
||||||
|
readonly property var screenshotsModel: game ? App.mediaManager.screenshotsModel(game) : null
|
||||||
|
|
||||||
readonly property bool isMobile: {
|
readonly property bool isMobile: {
|
||||||
let w = applicationWindow()
|
let w = applicationWindow()
|
||||||
if (w && w.isMobile !== undefined) return w.isMobile
|
if (w && w.isMobile !== undefined) return w.isMobile
|
||||||
|
|
@ -422,6 +425,94 @@ Kirigami.OverlaySheet {
|
||||||
Kirigami.FormData.label: i18n("Install path:")
|
Kirigami.FormData.label: i18n("Install path:")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Kirigami.Heading {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
level: 2
|
||||||
|
text: i18n("Screenshots")
|
||||||
|
visible: !!game
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: !!game
|
||||||
|
|
||||||
|
QQC2.Button {
|
||||||
|
text: i18n("Open Folder")
|
||||||
|
icon.name: "folder-open"
|
||||||
|
enabled: !!game
|
||||||
|
onClicked: if (game) {
|
||||||
|
Qt.openUrlExternally(App.mediaManager.screenshotsFolderUrl(game))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Button {
|
||||||
|
text: i18n("Import Screenshot")
|
||||||
|
icon.name: "document-open"
|
||||||
|
enabled: !!game
|
||||||
|
onClicked: screenshotImportDialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: !!game && (!detailsSheet.screenshotsModel || detailsSheet.screenshotsModel.count === 0)
|
||||||
|
text: i18n("No screenshots yet")
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
}
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
id: screenshotsGrid
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 12
|
||||||
|
visible: detailsSheet.screenshotsModel && detailsSheet.screenshotsModel.count > 0
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
cellWidth: Kirigami.Units.gridUnit * 6
|
||||||
|
cellHeight: Kirigami.Units.gridUnit * 4
|
||||||
|
model: detailsSheet.screenshotsModel
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
width: screenshotsGrid.cellWidth
|
||||||
|
height: screenshotsGrid.cellHeight
|
||||||
|
|
||||||
|
Kirigami.ShadowedRectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Kirigami.Units.smallSpacing
|
||||||
|
radius: Kirigami.Units.smallSpacing
|
||||||
|
color: Kirigami.Theme.alternateBackgroundColor
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: url
|
||||||
|
asynchronous: true
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
smooth: true
|
||||||
|
mipmap: App.config.highQualityImages
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: Qt.openUrlExternally(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDialog {
|
||||||
|
id: screenshotImportDialog
|
||||||
|
title: i18n("Import Screenshot")
|
||||||
|
fileMode: FileDialog.OpenFile
|
||||||
|
nameFilters: [i18n("Image files (*.png *.jpg *.jpeg *.webp)"), i18n("All files (*)")]
|
||||||
|
onAccepted: {
|
||||||
|
if (detailsSheet.game) {
|
||||||
|
App.mediaManager.importScreenshot(detailsSheet.game, selectedFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlatformColor(platform) {
|
function getPlatformColor(platform) {
|
||||||
|
|
|
||||||
135
src/screenshotmodel.cpp
Normal file
135
src/screenshotmodel.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
#include "screenshotmodel.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
ScreenshotModel::ScreenshotModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
connect(&m_watcher, &QFutureWatcher<QVector<ScreenshotEntry>>::finished, this, [this]() {
|
||||||
|
const int token = m_watcher.property("refreshToken").toInt();
|
||||||
|
if (token != m_refreshToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setEntries(m_watcher.result());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenshotModel::~ScreenshotModel()
|
||||||
|
{
|
||||||
|
m_refreshToken++;
|
||||||
|
m_watcher.cancel();
|
||||||
|
m_watcher.waitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScreenshotModel::directoryPath() const
|
||||||
|
{
|
||||||
|
return m_directoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotModel::setDirectoryPath(const QString &path)
|
||||||
|
{
|
||||||
|
if (m_directoryPath == path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_directoryPath = path;
|
||||||
|
Q_EMIT directoryPathChanged();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScreenshotModel::count() const
|
||||||
|
{
|
||||||
|
return rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScreenshotModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScreenshotModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0 || index.row() >= m_entries.size()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScreenshotEntry &e = m_entries.at(index.row());
|
||||||
|
switch (role) {
|
||||||
|
case UrlRole:
|
||||||
|
return QUrl::fromLocalFile(e.filePath);
|
||||||
|
case FileNameRole:
|
||||||
|
return e.fileName;
|
||||||
|
case CreatedRole:
|
||||||
|
return e.created;
|
||||||
|
case SizeRole:
|
||||||
|
return e.size;
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> ScreenshotModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[UrlRole] = "url";
|
||||||
|
roles[FileNameRole] = "fileName";
|
||||||
|
roles[CreatedRole] = "created";
|
||||||
|
roles[SizeRole] = "size";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotModel::refresh()
|
||||||
|
{
|
||||||
|
const QString path = m_directoryPath;
|
||||||
|
const int token = ++m_refreshToken;
|
||||||
|
|
||||||
|
m_watcher.setProperty("refreshToken", token);
|
||||||
|
|
||||||
|
auto future = QtConcurrent::run([path]() -> QVector<ScreenshotEntry> {
|
||||||
|
QVector<ScreenshotEntry> result;
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList nameFilters = {
|
||||||
|
QStringLiteral("*.png"),
|
||||||
|
QStringLiteral("*.jpg"),
|
||||||
|
QStringLiteral("*.jpeg"),
|
||||||
|
QStringLiteral("*.webp"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const QFileInfoList files = dir.entryInfoList(nameFilters, QDir::Files | QDir::Readable, QDir::Time | QDir::Reversed);
|
||||||
|
result.reserve(files.size());
|
||||||
|
|
||||||
|
for (const QFileInfo &fi : files) {
|
||||||
|
ScreenshotEntry e;
|
||||||
|
e.filePath = fi.absoluteFilePath();
|
||||||
|
e.fileName = fi.fileName();
|
||||||
|
e.created = fi.lastModified();
|
||||||
|
e.size = fi.size();
|
||||||
|
result.push_back(std::move(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_watcher.setFuture(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotModel::setEntries(QVector<ScreenshotEntry> entries)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_entries = std::move(entries);
|
||||||
|
endResetModel();
|
||||||
|
Q_EMIT countChanged();
|
||||||
|
}
|
||||||
59
src/screenshotmodel.h
Normal file
59
src/screenshotmodel.h
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class ScreenshotModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
Q_PROPERTY(QString directoryPath READ directoryPath WRITE setDirectoryPath NOTIFY directoryPathChanged)
|
||||||
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
UrlRole = Qt::UserRole + 1,
|
||||||
|
FileNameRole,
|
||||||
|
CreatedRole,
|
||||||
|
SizeRole,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ScreenshotModel(QObject *parent = nullptr);
|
||||||
|
~ScreenshotModel() override;
|
||||||
|
|
||||||
|
QString directoryPath() const;
|
||||||
|
void setDirectoryPath(const QString &path);
|
||||||
|
|
||||||
|
int count() const;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void refresh();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void directoryPathChanged();
|
||||||
|
void countChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ScreenshotEntry {
|
||||||
|
QString filePath;
|
||||||
|
QString fileName;
|
||||||
|
QDateTime created;
|
||||||
|
qint64 size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void setEntries(QVector<ScreenshotEntry> entries);
|
||||||
|
|
||||||
|
QString m_directoryPath;
|
||||||
|
QVector<ScreenshotEntry> m_entries;
|
||||||
|
|
||||||
|
QFutureWatcher<QVector<ScreenshotEntry>> m_watcher;
|
||||||
|
int m_refreshToken = 0;
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue