plugin: add FileSystemModel
This commit is contained in:
parent
4838e803a7
commit
7729546469
6 changed files with 288 additions and 50 deletions
|
|
@ -3,6 +3,7 @@ import qs.components.effects
|
|||
import qs.components.images
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Caelestia
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
|
|
@ -10,7 +11,7 @@ import QtQuick
|
|||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property Wallpapers.Wallpaper modelData
|
||||
required property FileSystemEntry modelData
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
scale: 0.5
|
||||
|
|
@ -75,7 +76,7 @@ StyledRect {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
renderType: Text.QtRendering
|
||||
text: root.modelData.name
|
||||
text: root.modelData.relativePath
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
bash,
|
||||
hyprland,
|
||||
coreutils,
|
||||
findutils,
|
||||
file,
|
||||
material-symbols,
|
||||
rubik,
|
||||
|
|
@ -56,7 +55,6 @@
|
|||
bash
|
||||
hyprland
|
||||
coreutils
|
||||
findutils
|
||||
file
|
||||
]
|
||||
++ extraRuntimeDeps
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ qt_add_qml_module(caelestia
|
|||
SOURCES
|
||||
cutils.hpp cutils.cpp
|
||||
cachingimagemanager.hpp cachingimagemanager.cpp
|
||||
filesystemmodel.hpp filesystemmodel.cpp
|
||||
)
|
||||
|
||||
qt_query_qml_module(caelestia
|
||||
|
|
|
|||
171
plugin/src/Caelestia/filesystemmodel.cpp
Normal file
171
plugin/src/Caelestia/filesystemmodel.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
#include "filesystemmodel.hpp"
|
||||
|
||||
#include <QObject>
|
||||
#include <qqmlintegration.h>
|
||||
#include <QAbstractListModel>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QImageReader>
|
||||
|
||||
int FileSystemModel::rowCount(const QModelIndex& parent) const {
|
||||
Q_UNUSED(parent);
|
||||
return m_files.size();
|
||||
}
|
||||
|
||||
QVariant FileSystemModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid() || index.row() >= m_files.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const FileSystemEntry* file = m_files.at(index.row());
|
||||
switch (role) {
|
||||
case FilePathRole:
|
||||
return file->path();
|
||||
case RelativeFilePathRole:
|
||||
return file->relativePath();
|
||||
case FileNameRole:
|
||||
return file->name();
|
||||
case ParentDirRole:
|
||||
return file->parentDir();
|
||||
case FileSizeRole:
|
||||
return file->size();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> FileSystemModel::roleNames() const {
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[FilePathRole] = "filePath";
|
||||
roles[RelativeFilePathRole] = "relativeFilePath";
|
||||
roles[FileNameRole] = "fileName";
|
||||
roles[ParentDirRole] = "parentDir";
|
||||
roles[FileSizeRole] = "fileSize";
|
||||
return roles;
|
||||
}
|
||||
|
||||
QString FileSystemModel::path() const {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void FileSystemModel::setPath(const QString& path) {
|
||||
if (m_path == path) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_path = path;
|
||||
emit pathChanged();
|
||||
|
||||
m_dir.setPath(m_path);
|
||||
update();
|
||||
}
|
||||
|
||||
bool FileSystemModel::recursive() const {
|
||||
return m_recursive;
|
||||
}
|
||||
|
||||
void FileSystemModel::setRecursive(bool recursive) {
|
||||
if (m_recursive == recursive) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_recursive = recursive;
|
||||
emit recursiveChanged();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
FileSystemModel::Filter FileSystemModel::filter() const {
|
||||
return m_filter;
|
||||
}
|
||||
|
||||
void FileSystemModel::setFilter(Filter filter) {
|
||||
if (m_filter == filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_filter = filter;
|
||||
emit filterChanged();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
QList<FileSystemEntry*> FileSystemModel::files() const {
|
||||
return m_files;
|
||||
}
|
||||
|
||||
void FileSystemModel::watchDirIfRecursive(const QString& path) {
|
||||
if (m_recursive) {
|
||||
QDirIterator iter(path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
|
||||
while (iter.hasNext()) {
|
||||
m_watcher.addPath(iter.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemModel::update() {
|
||||
updateWatcher();
|
||||
updateFiles();
|
||||
}
|
||||
|
||||
void FileSystemModel::updateWatcher() {
|
||||
if (!m_watcher.directories().isEmpty()) {
|
||||
m_watcher.removePaths(m_watcher.directories());
|
||||
}
|
||||
|
||||
if (m_path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_watcher.addPath(m_path);
|
||||
watchDirIfRecursive(m_path);
|
||||
}
|
||||
|
||||
void FileSystemModel::updateFiles() {
|
||||
if (m_path.isEmpty()) {
|
||||
beginResetModel();
|
||||
qDeleteAll(m_files);
|
||||
m_files.clear();
|
||||
emit filesChanged();
|
||||
endResetModel();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
qDeleteAll(m_files);
|
||||
m_files.clear();
|
||||
|
||||
const auto flags = m_recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
||||
|
||||
std::optional<QDirIterator> iter;
|
||||
|
||||
if (m_filter == ImagesOnly) {
|
||||
QStringList filters;
|
||||
for (const auto& format : QImageReader::supportedImageFormats()) {
|
||||
filters << "*." + format;
|
||||
}
|
||||
|
||||
iter.emplace(m_path, filters, QDir::Files, flags);
|
||||
} else {
|
||||
iter.emplace(m_path, QDir::Files, flags);
|
||||
}
|
||||
|
||||
while (iter.value().hasNext()) {
|
||||
QString file = iter.value().next();
|
||||
|
||||
if (m_filter == ImagesOnly) {
|
||||
QImageReader reader(file);
|
||||
if (reader.canRead()) {
|
||||
m_files << new FileSystemEntry(file, m_dir.relativeFilePath(file), this);
|
||||
}
|
||||
} else {
|
||||
m_files << new FileSystemEntry(file, m_dir.relativeFilePath(file), this);
|
||||
}
|
||||
}
|
||||
|
||||
emit filesChanged();
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
104
plugin/src/Caelestia/filesystemmodel.hpp
Normal file
104
plugin/src/Caelestia/filesystemmodel.hpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <qqmlintegration.h>
|
||||
#include <QAbstractListModel>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
|
||||
class FileSystemEntry : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT;
|
||||
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel");
|
||||
|
||||
Q_PROPERTY(QString path READ path CONSTANT);
|
||||
Q_PROPERTY(QString relativePath READ relativePath CONSTANT);
|
||||
Q_PROPERTY(QString name READ name CONSTANT);
|
||||
Q_PROPERTY(QString parentDir READ parentDir CONSTANT);
|
||||
Q_PROPERTY(qint64 size READ size CONSTANT);
|
||||
|
||||
public:
|
||||
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr)
|
||||
: QObject(parent), m_fileInfo(QFileInfo(path)), m_path(path), m_relativePath(relativePath) {}
|
||||
|
||||
QString path() const { return m_path; };
|
||||
QString relativePath() const { return m_relativePath; };
|
||||
|
||||
QString name() const { return m_fileInfo.fileName(); };
|
||||
QString parentDir() const { return m_fileInfo.absolutePath(); };
|
||||
qint64 size() const { return m_fileInfo.size(); };
|
||||
|
||||
private:
|
||||
const QFileInfo m_fileInfo;
|
||||
|
||||
const QString m_path;
|
||||
const QString m_relativePath;
|
||||
};
|
||||
|
||||
class FileSystemModel : public QAbstractListModel {
|
||||
Q_OBJECT;
|
||||
QML_ELEMENT;
|
||||
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged);
|
||||
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged);
|
||||
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged);
|
||||
|
||||
Q_PROPERTY(QList<FileSystemEntry*> files READ files NOTIFY filesChanged);
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
FilePathRole = Qt::UserRole + 1,
|
||||
RelativeFilePathRole,
|
||||
FileNameRole,
|
||||
ParentDirRole,
|
||||
FileSizeRole
|
||||
};
|
||||
|
||||
enum Filter {
|
||||
NoFilter,
|
||||
ImagesOnly
|
||||
};
|
||||
Q_ENUM(Filter)
|
||||
|
||||
explicit FileSystemModel(QObject* parent = nullptr)
|
||||
: QAbstractListModel(parent), m_recursive(true), m_filter(NoFilter) {
|
||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive);
|
||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateFiles);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
QString path() const;
|
||||
void setPath(const QString& path);
|
||||
|
||||
bool recursive() const;
|
||||
void setRecursive(bool recursive);
|
||||
|
||||
Filter filter() const;
|
||||
void setFilter(Filter filter);
|
||||
|
||||
QList<FileSystemEntry*> files() const;
|
||||
|
||||
signals:
|
||||
void pathChanged();
|
||||
void recursiveChanged();
|
||||
void filterChanged();
|
||||
void filesChanged();
|
||||
|
||||
private:
|
||||
QDir m_dir;
|
||||
QFileSystemWatcher m_watcher;
|
||||
QList<FileSystemEntry*> m_files;
|
||||
|
||||
QString m_path;
|
||||
bool m_recursive;
|
||||
Filter m_filter;
|
||||
|
||||
void watchDirIfRecursive(const QString& path);
|
||||
void update();
|
||||
void updateWatcher();
|
||||
void updateFiles();
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@ pragma Singleton
|
|||
|
||||
import qs.config
|
||||
import qs.utils
|
||||
import Caelestia
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
|
@ -39,7 +40,7 @@ Searcher {
|
|||
|
||||
reloadableId: "wallpapers"
|
||||
|
||||
list: wallpapers.instances
|
||||
list: wallpapers.files
|
||||
useFuzzy: Config.launcher.useFuzzy.wallpapers
|
||||
extraOpts: useFuzzy ? ({}) : ({
|
||||
forward: false
|
||||
|
|
@ -71,6 +72,13 @@ Searcher {
|
|||
}
|
||||
}
|
||||
|
||||
FileSystemModel {
|
||||
id: wallpapers
|
||||
|
||||
path: Paths.expandTilde(Paths.wallsdir)
|
||||
filter: FileSystemModel.ImagesOnly
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getPreviewColoursProc
|
||||
|
||||
|
|
@ -82,49 +90,4 @@ Searcher {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getWallsProc
|
||||
|
||||
running: true
|
||||
command: ["find", "-L", Paths.expandTilde(Paths.wallsdir), "-type", "d", "-path", '*/.*', "-prune", "-o", "-not", "-name", '.*', "-type", "f", "-print"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: wallpapers.model = text.trim().split("\n").filter(w => Images.isValidImageByName(w)).sort()
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: watchWallsProc
|
||||
|
||||
running: true
|
||||
command: ["inotifywait", "-r", "-e", "close_write,moved_to,create", "-m", Paths.expandTilde(Paths.wallsdir)]
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
if (Images.isValidImageByName(data))
|
||||
getWallsProc.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Config.paths
|
||||
|
||||
function onWallpaperDirChanged(): void {
|
||||
getWallsProc.running = true;
|
||||
watchWallsProc.running = false;
|
||||
watchWallsProc.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
id: wallpapers
|
||||
|
||||
Wallpaper {}
|
||||
}
|
||||
|
||||
component Wallpaper: QtObject {
|
||||
required property string modelData
|
||||
readonly property string path: modelData
|
||||
readonly property string name: path.slice(Paths.expandTilde(Paths.wallsdir).length + 1, path.lastIndexOf("."))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue