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.components.images
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.config
|
import qs.config
|
||||||
|
import Caelestia
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
@ -10,7 +11,7 @@ import QtQuick
|
||||||
StyledRect {
|
StyledRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property Wallpapers.Wallpaper modelData
|
required property FileSystemEntry modelData
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
scale: 0.5
|
scale: 0.5
|
||||||
|
|
@ -75,7 +76,7 @@ StyledRect {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
renderType: Text.QtRendering
|
renderType: Text.QtRendering
|
||||||
text: root.modelData.name
|
text: root.modelData.relativePath
|
||||||
font.pointSize: Appearance.font.size.normal
|
font.pointSize: Appearance.font.size.normal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
bash,
|
bash,
|
||||||
hyprland,
|
hyprland,
|
||||||
coreutils,
|
coreutils,
|
||||||
findutils,
|
|
||||||
file,
|
file,
|
||||||
material-symbols,
|
material-symbols,
|
||||||
rubik,
|
rubik,
|
||||||
|
|
@ -56,7 +55,6 @@
|
||||||
bash
|
bash
|
||||||
hyprland
|
hyprland
|
||||||
coreutils
|
coreutils
|
||||||
findutils
|
|
||||||
file
|
file
|
||||||
]
|
]
|
||||||
++ extraRuntimeDeps
|
++ extraRuntimeDeps
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ qt_add_qml_module(caelestia
|
||||||
SOURCES
|
SOURCES
|
||||||
cutils.hpp cutils.cpp
|
cutils.hpp cutils.cpp
|
||||||
cachingimagemanager.hpp cachingimagemanager.cpp
|
cachingimagemanager.hpp cachingimagemanager.cpp
|
||||||
|
filesystemmodel.hpp filesystemmodel.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_query_qml_module(caelestia
|
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.config
|
||||||
import qs.utils
|
import qs.utils
|
||||||
|
import Caelestia
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
@ -39,7 +40,7 @@ Searcher {
|
||||||
|
|
||||||
reloadableId: "wallpapers"
|
reloadableId: "wallpapers"
|
||||||
|
|
||||||
list: wallpapers.instances
|
list: wallpapers.files
|
||||||
useFuzzy: Config.launcher.useFuzzy.wallpapers
|
useFuzzy: Config.launcher.useFuzzy.wallpapers
|
||||||
extraOpts: useFuzzy ? ({}) : ({
|
extraOpts: useFuzzy ? ({}) : ({
|
||||||
forward: false
|
forward: false
|
||||||
|
|
@ -71,6 +72,13 @@ Searcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileSystemModel {
|
||||||
|
id: wallpapers
|
||||||
|
|
||||||
|
path: Paths.expandTilde(Paths.wallsdir)
|
||||||
|
filter: FileSystemModel.ImagesOnly
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: getPreviewColoursProc
|
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