internal: use FileSystemModel for FileDialog

This commit is contained in:
2 * r + 2 * t 2025-09-02 18:40:31 +10:00
parent dccdcd417f
commit e70cb285ac
6 changed files with 44 additions and 46 deletions

View file

@ -79,7 +79,7 @@ Item {
anchors.rightMargin: Appearance.padding.larger - Appearance.padding.small anchors.rightMargin: Appearance.padding.larger - Appearance.padding.small
anchors.bottomMargin: Appearance.padding.normal - Appearance.padding.small anchors.bottomMargin: Appearance.padding.normal - Appearance.padding.small
text: qsTr(`"%1" selected`).arg(root.currentItem?.fileName) text: qsTr(`"%1" selected`).arg(root.currentItem?.modelData.name)
} }
} }

View file

@ -52,7 +52,7 @@ StyledRect {
disabled: !root.dialog.selectionValid disabled: !root.dialog.selectionValid
function onClicked(): void { function onClicked(): void {
root.dialog.accepted(root.folder.currentItem.filePath); root.dialog.accepted(root.folder.currentItem.modelData.path);
} }
} }

View file

@ -36,8 +36,8 @@ LazyLoader {
property list<string> filters: loader.filters property list<string> filters: loader.filters
readonly property bool selectionValid: { readonly property bool selectionValid: {
const item = folderContents.currentItem; const file = folderContents.currentItem?.modelData;
return item && !item.fileIsDir && (filters.includes("*") || filters.includes(item.fileSuffix)); return file && !file.isDir && (filters.includes("*") || filters.includes(file.suffix));
} }
function accepted(path: string): void { function accepted(path: string): void {

View file

@ -6,13 +6,12 @@ import "../images"
import qs.services import qs.services
import qs.config import qs.config
import qs.utils import qs.utils
import Caelestia
import Quickshell import Quickshell
import Quickshell.Io
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.folderlistmodel
Item { Item {
id: root id: root
@ -86,37 +85,30 @@ Item {
Keys.onReturnPressed: { Keys.onReturnPressed: {
if (root.dialog.selectionValid) if (root.dialog.selectionValid)
root.dialog.accepted(currentItem.filePath); root.dialog.accepted(currentItem.modelData.path);
} }
Keys.onEnterPressed: { Keys.onEnterPressed: {
if (root.dialog.selectionValid) if (root.dialog.selectionValid)
root.dialog.accepted(currentItem.filePath); root.dialog.accepted(currentItem.modelData.path);
} }
ScrollBar.vertical: StyledScrollBar {} ScrollBar.vertical: StyledScrollBar {}
model: FolderListModel { model: FileSystemModel {
showDirsFirst: true path: {
folder: {
let url = "file://";
if (root.dialog.cwd[0] === "Home") if (root.dialog.cwd[0] === "Home")
url += `${Paths.strip(Paths.home)}/${root.dialog.cwd.slice(1).join("/")}`; return `${Paths.strip(Paths.home)}/${root.dialog.cwd.slice(1).join("/")}`;
else else
url += root.dialog.cwd.join("/"); return root.dialog.cwd.join("/");
return url;
} }
onFolderChanged: view.currentIndex = -1 onPathChanged: view.currentIndex = -1
} }
delegate: StyledRect { delegate: StyledRect {
id: item id: item
required property int index required property int index
required property string fileName required property FileSystemEntry modelData
required property string filePath
required property url fileUrl
required property string fileSuffix
required property bool fileIsDir
readonly property real nonAnimHeight: icon.implicitHeight + name.anchors.topMargin + name.implicitHeight + Appearance.padding.normal * 2 readonly property real nonAnimHeight: icon.implicitHeight + name.anchors.topMargin + name.implicitHeight + Appearance.padding.normal * 2
@ -130,10 +122,10 @@ Item {
StateLayer { StateLayer {
onDoubleClicked: { onDoubleClicked: {
if (item.fileIsDir) if (item.modelData.isDir)
root.dialog.cwd.push(item.fileName); root.dialog.cwd.push(item.modelData.name);
else if (root.dialog.selectionValid) else if (root.dialog.selectionValid)
root.dialog.accepted(item.filePath); root.dialog.accepted(item.modelData.path);
} }
function onClicked(): void { function onClicked(): void {
@ -150,31 +142,18 @@ Item {
implicitSize: Sizes.itemWidth - Appearance.padding.normal * 2 implicitSize: Sizes.itemWidth - Appearance.padding.normal * 2
source: { source: {
if (!item.fileIsDir) if (item.modelData.isImage)
return Quickshell.iconPath("application-x-zerosize"); return Qt.resolvedUrl(item.modelData.path);
const name = item.fileName; if (!item.modelData.isDir)
return Quickshell.iconPath(item.modelData.mimeType.replace("/", "-"), "application-x-zerosize");
const name = item.modelData.name;
if (root.dialog.cwd.length === 1 && ["Desktop", "Documents", "Downloads", "Music", "Pictures", "Public", "Templates", "Videos"].includes(name)) if (root.dialog.cwd.length === 1 && ["Desktop", "Documents", "Downloads", "Music", "Pictures", "Public", "Templates", "Videos"].includes(name))
return Quickshell.iconPath(`folder-${name.toLowerCase()}`); return Quickshell.iconPath(`folder-${name.toLowerCase()}`);
return Quickshell.iconPath("inode-directory"); return Quickshell.iconPath("inode-directory");
} }
onStatusChanged: {
if (status === Image.Error)
source = Quickshell.iconPath("error");
}
Process {
running: !item.fileIsDir
command: ["file", "--mime", "-b", item.filePath]
stdout: StdioCollector {
onStreamFinished: {
const mime = text.split(";")[0].replace("/", "-");
icon.source = Images.validImageTypes.some(t => mime === `image-${t}`) ? item.fileUrl : Quickshell.iconPath(mime, "image-missing");
}
}
}
} }
StyledText { StyledText {
@ -187,7 +166,7 @@ Item {
anchors.margins: Appearance.padding.normal anchors.margins: Appearance.padding.normal
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: item.fileName text: item.modelData.name
elide: item.GridView.isCurrentItem ? Text.ElideNone : Text.ElideRight elide: item.GridView.isCurrentItem ? Text.ElideNone : Text.ElideRight
wrapMode: item.GridView.isCurrentItem ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap wrapMode: item.GridView.isCurrentItem ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap
} }

View file

@ -6,6 +6,7 @@
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QFuture> #include <QFuture>
#include <QImageReader> #include <QImageReader>
#include <QMimeDatabase>
#include <QObject> #include <QObject>
#include <qqmlintegration.h> #include <qqmlintegration.h>
@ -18,22 +19,27 @@ class FileSystemEntry : public QObject {
Q_PROPERTY(QString relativePath READ relativePath CONSTANT) Q_PROPERTY(QString relativePath READ relativePath CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString parentDir READ parentDir CONSTANT) Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
Q_PROPERTY(QString suffix READ suffix CONSTANT)
Q_PROPERTY(qint64 size READ size CONSTANT) Q_PROPERTY(qint64 size READ size CONSTANT)
Q_PROPERTY(bool isDir READ isDir CONSTANT) Q_PROPERTY(bool isDir READ isDir CONSTANT)
Q_PROPERTY(bool isImage READ isImage CONSTANT) Q_PROPERTY(bool isImage READ isImage CONSTANT)
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
public: public:
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr) explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr)
: QObject(parent) : QObject(parent)
, m_fileInfo(QFileInfo(path)) , m_fileInfo(QFileInfo(path))
, m_path(path) , m_path(path)
, m_relativePath(relativePath) {} , m_relativePath(relativePath)
, m_isImageInitialised(false)
, m_mimeTypeInitialised(false) {}
QString path() const { return m_path; }; QString path() const { return m_path; };
QString relativePath() const { return m_relativePath; }; QString relativePath() const { return m_relativePath; };
QString name() const { return m_fileInfo.fileName(); }; QString name() const { return m_fileInfo.fileName(); };
QString parentDir() const { return m_fileInfo.absolutePath(); }; QString parentDir() const { return m_fileInfo.absolutePath(); };
QString suffix() const { return m_fileInfo.completeSuffix(); };
qint64 size() const { return m_fileInfo.size(); }; qint64 size() const { return m_fileInfo.size(); };
bool isDir() const { return m_fileInfo.isDir(); }; bool isDir() const { return m_fileInfo.isDir(); };
@ -46,6 +52,15 @@ public:
return m_isImage; return m_isImage;
} }
QString mimeType() {
if (!m_mimeTypeInitialised) {
const QMimeDatabase db;
m_mimeType = db.mimeTypeForFile(m_path).name();
m_mimeTypeInitialised = true;
}
return m_mimeType;
}
private: private:
const QFileInfo m_fileInfo; const QFileInfo m_fileInfo;
@ -54,6 +69,9 @@ private:
bool m_isImage; bool m_isImage;
bool m_isImageInitialised; bool m_isImageInitialised;
QString m_mimeType;
bool m_mimeTypeInitialised;
}; };
class FileSystemModel : public QAbstractListModel { class FileSystemModel : public QAbstractListModel {
@ -79,7 +97,7 @@ public:
explicit FileSystemModel(QObject* parent = nullptr) explicit FileSystemModel(QObject* parent = nullptr)
: QAbstractListModel(parent) : QAbstractListModel(parent)
, m_recursive(true) , m_recursive(false)
, m_watchChanges(true) , m_watchChanges(true)
, m_showHidden(false) , m_showHidden(false)
, m_filter(NoFilter) { , m_filter(NoFilter) {

View file

@ -73,6 +73,7 @@ Searcher {
FileSystemModel { FileSystemModel {
id: wallpapers id: wallpapers
recursive: true
path: Paths.expandTilde(Paths.wallsdir) path: Paths.expandTilde(Paths.wallsdir)
filter: FileSystemModel.Images filter: FileSystemModel.Images
} }