summaryrefslogtreecommitdiff
path: root/widgets/filedialog/FolderContents.qml
blob: f8e10d94406711c1c5a6cc708130c60ac9d7d3cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
pragma ComponentBehavior: Bound

import ".."
import qs.services
import qs.config
import qs.utils
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import QtQuick
import Qt.labs.folderlistmodel

GridView {
    id: root

    required property var dialog

    property var mimes: ({})

    clip: true
    focus: true
    Keys.onEscapePressed: root.currentIndex = -1

    model: FolderListModel {
        folder: {
            let url = "file://";
            if (root.dialog.cwd[0] === "Home")
                url += `${Paths.strip(Paths.home)}/${root.dialog.cwd.slice(1).join("/")}`;
            else
                url += root.dialog.cwd.join("/");
            return url;
        }
        onFolderChanged: root.currentIndex = -1
    }

    delegate: StyledRect {
        id: item

        required property int index
        required property string fileName
        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

        implicitWidth: Sizes.itemWidth
        implicitHeight: nonAnimHeight

        radius: Appearance.rounding.normal
        color: root.currentItem === item ? Colours.palette.m3primary : "transparent"
        z: root.currentItem === item || implicitHeight !== nonAnimHeight ? 1 : 0
        clip: true

        StateLayer {
            color: root.currentItem === item ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface

            onDoubleClicked: {
                if (item.fileIsDir)
                    root.dialog.cwd.push(item.fileName);
                else
                    root.dialog.accepted(item.filePath);
            }

            function onClicked(): void {
                root.currentIndex = item.index;
            }
        }

        IconImage {
            id: icon

            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: parent.top
            anchors.topMargin: Appearance.spacing.normal

            asynchronous: true
            implicitSize: Sizes.itemWidth - Appearance.padding.normal * 2
            source: {
                const mime = root.mimes[item.fileSuffix];

                if (mime?.startsWith("image-"))
                    return item.fileUrl;

                return Quickshell.iconPath(item.fileIsDir ? "inode-directory" : root.mimes[item.fileSuffix] ?? "application-x-zerosize", "image-missing");
            }
            onStatusChanged: {
                if (status === Image.Error)
                    source = Quickshell.iconPath("error");
            }
        }

        StyledText {
            id: name

            anchors.left: parent.left
            anchors.right: parent.right
            anchors.top: icon.bottom
            anchors.topMargin: Appearance.spacing.small
            anchors.margins: Appearance.padding.normal

            horizontalAlignment: Text.AlignHCenter
            text: item.fileName
            color: root.currentItem === item ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
            elide: root.currentItem === item ? Text.ElideNone : Text.ElideRight
            wrapMode: root.currentItem === item ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap
        }

        Behavior on implicitHeight {
            NumberAnimation {
                duration: Appearance.anim.durations.normal
                easing.type: Easing.BezierSpline
                easing.bezierCurve: Appearance.anim.curves.standard
            }
        }
    }

    FileView {
        path: "/etc/mime.types"
        onLoaded: {
            root.mimes = text().split("\n").filter(l => !l.startsWith("#")).reduce((mimes, line) => {
                const [type, ext] = line.split(/\s+/);
                if (ext)
                    mimes[ext] = type.replace("/", "-");
                return mimes;
            }, {});
        }
    }
}