internal: file dialog part 1
This commit is contained in:
parent
6826f71d34
commit
7c991dee0a
4 changed files with 297 additions and 0 deletions
40
widgets/filedialog/FileDialog.qml
Normal file
40
widgets/filedialog/FileDialog.qml
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import qs.services
|
||||||
|
import qs.config
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
FloatingWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property list<string> cwd: ["Home", "Downloads"]
|
||||||
|
|
||||||
|
implicitWidth: 1000
|
||||||
|
implicitHeight: 600
|
||||||
|
color: Colours.palette.m3surface
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.normal
|
||||||
|
|
||||||
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
HeaderBar {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
cwd: root.cwd
|
||||||
|
}
|
||||||
|
|
||||||
|
FolderContents {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
cwd: root.cwd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
widgets/filedialog/FolderContents.qml
Normal file
122
widgets/filedialog/FolderContents.qml
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
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 list<string> cwd
|
||||||
|
|
||||||
|
property var mimes: ({})
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
focus: true
|
||||||
|
Keys.onEscapePressed: root.currentIndex = -1
|
||||||
|
|
||||||
|
model: FolderListModel {
|
||||||
|
folder: {
|
||||||
|
let url = "file://";
|
||||||
|
if (root.cwd[0] === "Home")
|
||||||
|
url += `${Paths.strip(Paths.home)}/${root.cwd.slice(1).join("/")}`;
|
||||||
|
else
|
||||||
|
url += root.cwd.join("/");
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: StyledRect {
|
||||||
|
id: item
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
required property string fileName
|
||||||
|
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: console.log("double clicked", item)
|
||||||
|
|
||||||
|
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;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
widgets/filedialog/HeaderBar.qml
Normal file
128
widgets/filedialog/HeaderBar.qml
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import ".."
|
||||||
|
import qs.services
|
||||||
|
import qs.config
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property list<string> cwd
|
||||||
|
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
Item {
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
root.cwd.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: upIcon
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "drive_folder_upload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
color: Colours.palette.m3surfaceContainer
|
||||||
|
|
||||||
|
implicitHeight: pathComponents.implicitHeight + pathComponents.anchors.margins * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: pathComponents
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.cwd
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: folder
|
||||||
|
|
||||||
|
required property string modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
Layout.rightMargin: Appearance.spacing.small
|
||||||
|
active: folder.index > 0
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: StyledText {
|
||||||
|
text: "/"
|
||||||
|
color: Colours.palette.m3onSurfaceVariant
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
implicitWidth: homeIcon.implicitWidth + (homeIcon.active ? Appearance.padding.small : 0) + folderName.implicitWidth + Appearance.padding.normal * 2
|
||||||
|
implicitHeight: Math.max(homeIcon.implicitHeight, folderName.implicitHeight) + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: folder.index < root.cwd.length - 1
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: StateLayer {
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
root.cwd = root.cwd.slice(0, folder.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: homeIcon
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: Appearance.padding.normal
|
||||||
|
|
||||||
|
active: folder.index === 0 && folder.modelData === "Home"
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: MaterialIcon {
|
||||||
|
text: "home"
|
||||||
|
color: root.cwd.length === 1 ? Colours.palette.m3onSurface : Colours.palette.m3onSurfaceVariant
|
||||||
|
fill: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: folderName
|
||||||
|
|
||||||
|
anchors.left: homeIcon.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: homeIcon.active ? Appearance.padding.small : 0
|
||||||
|
|
||||||
|
text: folder.modelData
|
||||||
|
color: folder.index < root.cwd.length - 1 ? Colours.palette.m3onSurfaceVariant : Colours.palette.m3onSurface
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
widgets/filedialog/Sizes.qml
Normal file
7
widgets/filedialog/Sizes.qml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property int itemWidth: 100
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue