import QtQuick import QtQuick.Controls import QtCore import Qt.labs.folderlistmodel import Quickshell.Io import qs.Common import qs.Widgets Rectangle { id: root property bool showFileInfo: false property int selectedIndex: -1 property var sourceFolderModel: null property string currentPath: "" height: 200 radius: Theme.cornerRadius color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) border.color: Theme.secondary border.width: 2 opacity: showFileInfo ? 1 : 0 z: 100 onShowFileInfoChanged: { if (showFileInfo && currentFileName && currentPath) { var fullPath = currentPath + "/" + currentFileName fileStatProcess.selectedFilePath = fullPath fileStatProcess.running = true } } Process { id: fileStatProcess command: ["stat", "-c", "%y|%A|%s|%n", selectedFilePath] property string selectedFilePath: "" property var fileStats: null running: false stdout: StdioCollector { onStreamFinished: { if (text && text.trim()) { var parts = text.trim().split('|') if (parts.length >= 4) { fileStatProcess.fileStats = { modifiedTime: parts[0], permissions: parts[1], size: parseInt(parts[2]) || 0, fullPath: parts[3] } } } } } onExited: function(exitCode) {} } readonly property var currentFileData: { if (!sourceFolderModel || selectedIndex < 0 || selectedIndex >= sourceFolderModel.count) { return { exists: false, name: "No selection", type: "", size: "", modified: "", permissions: "", extension: "", position: selectedIndex >= 0 ? (selectedIndex + 1) + " of " + (sourceFolderModel ? sourceFolderModel.count : 0) : "N/A" } } var path = currentPath if (path && selectedIndex >= 0) { return { exists: true, name: "Loading...", type: "file", size: "Calculating...", modified: "Loading...", permissions: "Loading...", extension: "", position: (selectedIndex + 1) + " of " + sourceFolderModel.count } } return { exists: false, name: "No selection", type: "", size: "", modified: "", permissions: "", extension: "", position: "N/A" } } property string currentFileName: "" property bool currentFileIsDir: false property string currentFileExtension: "" onCurrentFileNameChanged: { if (showFileInfo && currentFileName && currentPath) { var fullPath = currentPath + "/" + currentFileName if (fullPath !== fileStatProcess.selectedFilePath) { fileStatProcess.selectedFilePath = fullPath fileStatProcess.running = true } } } function updateFileInfo(filePath, fileName, isDirectory) { if (filePath && filePath !== fileStatProcess.selectedFilePath) { fileStatProcess.selectedFilePath = filePath currentFileName = fileName || "" currentFileIsDir = isDirectory || false var ext = "" if (!isDirectory && fileName) { var lastDot = fileName.lastIndexOf('.') if (lastDot > 0) { ext = fileName.substring(lastDot + 1).toLowerCase() } } currentFileExtension = ext if (showFileInfo) { fileStatProcess.running = true } } } readonly property var currentFileDisplayData: { if (selectedIndex < 0 || !sourceFolderModel) { return { exists: false, name: "No selection", type: "", size: "", modified: "", permissions: "", extension: "", position: "N/A" } } var hasValidFile = currentFileName !== "" return { exists: hasValidFile, name: hasValidFile ? currentFileName : "Loading...", type: currentFileIsDir ? "Directory" : "File", size: fileStatProcess.fileStats ? formatFileSize(fileStatProcess.fileStats.size) : "Calculating...", modified: fileStatProcess.fileStats ? formatDateTime(fileStatProcess.fileStats.modifiedTime) : "Loading...", permissions: fileStatProcess.fileStats ? fileStatProcess.fileStats.permissions : "Loading...", extension: currentFileExtension, position: sourceFolderModel ? ((selectedIndex + 1) + " of " + sourceFolderModel.count) : "N/A" } } Column { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.margins: Theme.spacingM spacing: Theme.spacingXS Row { width: parent.width spacing: Theme.spacingS DankIcon { name: "info" size: Theme.iconSize color: Theme.secondary } StyledText { text: "File Information" font.pixelSize: Theme.fontSizeMedium color: Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter } } Column { width: parent.width spacing: Theme.spacingXS StyledText { text: currentFileDisplayData.name font.pixelSize: Theme.fontSizeMedium color: Theme.surfaceText width: parent.width elide: Text.ElideMiddle wrapMode: Text.NoWrap font.weight: Font.Medium } StyledText { text: currentFileDisplayData.type + (currentFileDisplayData.extension ? " (." + currentFileDisplayData.extension + ")" : "") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium width: parent.width } StyledText { text: currentFileDisplayData.size font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium width: parent.width visible: currentFileDisplayData.exists && !currentFileIsDir } StyledText { text: currentFileDisplayData.modified font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium width: parent.width elide: Text.ElideRight visible: currentFileDisplayData.exists } StyledText { text: currentFileDisplayData.permissions font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium visible: currentFileDisplayData.exists } StyledText { text: currentFileDisplayData.position font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium width: parent.width } } } StyledText { text: "F1/I: Toggle • F10: Help" font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right anchors.margins: Theme.spacingM horizontalAlignment: Text.AlignHCenter } function formatFileSize(bytes) { if (bytes === 0 || !bytes) return "0 B" var k = 1024 var sizes = ['B', 'KB', 'MB', 'GB', 'TB'] var i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i] } function formatDateTime(dateTimeString) { if (!dateTimeString) return "Unknown" var parts = dateTimeString.split(' ') if (parts.length >= 2) { return parts[0] + " " + parts[1].split('.')[0] } return dateTimeString } Behavior on opacity { NumberAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } }