diff options
author | Dennis Oberst <dennis.oberst@qt.io> | 2024-01-08 17:27:18 +0100 |
---|---|---|
committer | Dennis Oberst <dennis.oberst@qt.io> | 2024-01-23 15:50:39 +0100 |
commit | 13537fa1f0d4546b4001a9f61a70e9081cd9db7d (patch) | |
tree | 200009fd03c61e561be429216fff8ec192e718b0 /examples | |
parent | fcf1d51c3d6e2d8afa02a2e69665b7df9a6a97ca (diff) |
Filesystemexplorer-example: port v2
Since we're touching the example: improve the visualization of
files inside the tree-view and provide a minimum width and height.
Task-number: PYSIDE-2576
Task-number: QTBUG-119785
Change-Id: I47c6d3eb02436a9ab4213e9b8195b32eaaf37573
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'examples')
20 files changed, 834 insertions, 451 deletions
diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml index 3987acc9e..7f7798ed8 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml @@ -6,47 +6,68 @@ import QtQuick.Controls.Basic import QtQuick.Layouts import FileSystemModule +pragma ComponentBehavior: Bound + ApplicationWindow { id: root + + property bool expandPath: false + property bool showLineNumbers: true + property string currentFilePath: "" + width: 1100 height: 600 + minimumWidth: 200 + minimumHeight: 100 visible: true + color: Colors.background flags: Qt.Window | Qt.FramelessWindowHint - title: qsTr("Qt Quick Controls - File System Explorer") + title: qsTr("File System Explorer Example") - property string currentFilePath: "" - property bool expandPath: false + function getInfoText() : string { + let out = root.currentFilePath + if (!out) + return qsTr("File System Explorer") + return root.expandPath ? out : out.substring(out.lastIndexOf("/") + 1, out.length) + } menuBar: MyMenuBar { - rootWindow: root - - infoText: currentFilePath - ? (expandPath ? currentFilePath - : currentFilePath.substring(currentFilePath.lastIndexOf("/") + 1, currentFilePath.length)) - : "File System Explorer" - + dragWindow: root + infoText: root.getInfoText() MyMenu { title: qsTr("File") Action { text: qsTr("Increase Font") shortcut: StandardKey.ZoomIn - onTriggered: textArea.font.pixelSize += 1 + onTriggered: editor.text.font.pixelSize += 1 } Action { text: qsTr("Decrease Font") shortcut: StandardKey.ZoomOut - onTriggered: textArea.font.pixelSize -= 1 + onTriggered: editor.text.font.pixelSize -= 1 } Action { - text: expandPath ? qsTr("Toggle Short Path") : qsTr("Toggle Expand Path") - enabled: currentFilePath - onTriggered: expandPath = !expandPath + text: root.showLineNumbers ? qsTr("Toggle Line Numbers OFF") + : qsTr("Toggle Line Numbers ON") + shortcut: "Ctrl+L" + onTriggered: root.showLineNumbers = !root.showLineNumbers + } + Action { + text: root.expandPath ? qsTr("Toggle Short Path") + : qsTr("Toggle Expand Path") + enabled: root.currentFilePath + onTriggered: root.expandPath = !root.expandPath + } + Action { + text: qsTr("Reset Filesystem") + enabled: sidebar.currentTabIndex === 1 + onTriggered: fileSystemView.rootIndex = undefined } Action { text: qsTr("Exit") onTriggered: Qt.exit(0) - shortcut: "Ctrl+Q" + shortcut: StandardKey.Quit } } @@ -56,134 +77,109 @@ ApplicationWindow { Action { text: qsTr("Cut") shortcut: StandardKey.Cut - enabled: textArea.selectedText.length > 0 - onTriggered: textArea.cut() + enabled: editor.text.selectedText.length > 0 + onTriggered: editor.text.cut() } Action { text: qsTr("Copy") shortcut: StandardKey.Copy - enabled: textArea.selectedText.length > 0 - onTriggered: textArea.copy() + enabled: editor.text.selectedText.length > 0 + onTriggered: editor.text.copy() } Action { text: qsTr("Paste") shortcut: StandardKey.Paste - enabled: textArea.canPaste - onTriggered: textArea.paste() + enabled: editor.text.canPaste + onTriggered: editor.text.paste() } Action { text: qsTr("Select All") shortcut: StandardKey.SelectAll - enabled: textArea.length > 0 - onTriggered: textArea.selectAll() + enabled: editor.text.length > 0 + onTriggered: editor.text.selectAll() } Action { text: qsTr("Undo") shortcut: StandardKey.Undo - enabled: textArea.canUndo - onTriggered: textArea.undo() + enabled: editor.text.canUndo + onTriggered: editor.text.undo() } } } - - Rectangle { + // Set up the layout of the main components in a row: + // [ Sidebar, Navigation, Editor ] + RowLayout { anchors.fill: parent - color: Colors.background - - RowLayout { - anchors.fill: parent - spacing: 0 - - // Stores the buttons that navigate the application. - Sidebar { - id: sidebar - rootWindow: root - - Layout.preferredWidth: 60 - Layout.fillHeight: true - } + spacing: 0 + + // Stores the buttons that navigate the application. + Sidebar { + id: sidebar + dragWindow: root + Layout.preferredWidth: 50 + Layout.fillHeight: true + } - // Allows resizing parts of the UI. - SplitView { - Layout.fillWidth: true - Layout.fillHeight: true - - handle: Rectangle { - implicitWidth: 10 - color: SplitHandle.pressed ? Colors.color2 : Colors.background - border.color: Colors.color2 - opacity: SplitHandle.hovered || SplitHandle.pressed ? 1.0 : 0.0 - - Behavior on opacity { - OpacityAnimator { - duration: 900 - } + // Allows resizing parts of the UI. + SplitView { + Layout.fillWidth: true + Layout.fillHeight: true + // Customized handle to drag between the Navigation and the Editor. + handle: Rectangle { + implicitWidth: 10 + color: SplitHandle.pressed ? Colors.color2 : Colors.background + border.color: SplitHandle.hovered ? Colors.color2 : Colors.background + opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0 + + Behavior on opacity { + OpacityAnimator { + duration: 1400 } } + } - // We use an inline component to make a reusable TextArea component. - // This is convenient when the component is only used in one file. - component MyTextArea: TextArea { - antialiasing: true - color: Colors.textFile - selectedTextColor: Colors.textFile - selectionColor: Colors.selection - renderType: Text.QtRendering - textFormat: TextEdit.PlainText - - background: null - } - - Rectangle { - color: Colors.surface1 - - SplitView.preferredWidth: 250 - SplitView.fillHeight: true - - StackLayout { - currentIndex: sidebar.currentTabIndex - - anchors.fill: parent - - // Shows the help text. - MyTextArea { - readOnly: true - text: qsTr("This example shows how to use and visualize the file system.\n\n" - + "Customized Qt Quick Components have been used to achieve this look.\n\n" - + "You can edit the files but they won't be changed on the file system.\n\n" - + "Click on the folder icon to the left to get started.") - wrapMode: TextArea.Wrap - } - - // Shows the files on the file system. - FileSystemView { - id: fileSystemView - color: Colors.surface1 - - onFileClicked: (path) => root.currentFilePath = path - } + Rectangle { + id: navigationView + color: Colors.surface1 + SplitView.preferredWidth: 250 + SplitView.fillHeight: true + // The stack-layout provides different views, based on the + // selected buttons inside the sidebar. + StackLayout { + anchors.fill: parent + currentIndex: sidebar.currentTabIndex + + // Shows the help text. + Text { + text: qsTr("This example shows how to use and visualize the file system.\n\n" + + "Customized Qt Quick Components have been used to achieve this look.\n\n" + + "You can edit the files but they won't be changed on the file system.\n\n" + + "Click on the folder icon to the left to get started.") + wrapMode: TextArea.Wrap + color: Colors.text } - } - - // The ScrollView that contains the TextArea which shows the file's content. - ScrollView { - leftPadding: 20 - topPadding: 20 - bottomPadding: 20 - clip: true - - SplitView.fillWidth: true - SplitView.fillHeight: true - - property alias textArea: textArea - MyTextArea { - id: textArea - text: FileSystemModel.readFile(root.currentFilePath) + // Shows the files on the file system. + FileSystemView { + id: fileSystemView + color: Colors.surface1 + onFileClicked: path => root.currentFilePath = path } } } + + // The main view that contains the editor. + Editor { + id: editor + showLineNumbers: root.showLineNumbers + currentFilePath: root.currentFilePath + SplitView.fillWidth: true + SplitView.fillHeight: true + } } - ResizeButton {} + } + + ResizeButton { + resizeWindow: root } } diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/app.qrc b/examples/quickcontrols/filesystemexplorer/FileSystemModule/app.qrc index 05fc728e7..fec76fe67 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/app.qrc +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/app.qrc @@ -3,9 +3,9 @@ <file>qmldir</file> <file>Main.qml</file> <file>qml/About.qml</file> + <file>qml/Editor.qml</file> <file>qml/Colors.qml</file> <file>qml/FileSystemView.qml</file> - <file>qml/Icon.qml</file> <file>qml/MyMenu.qml</file> <file>qml/MyMenuBar.qml</file> <file>qml/ResizeButton.qml</file> diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/icons.qrc b/examples/quickcontrols/filesystemexplorer/FileSystemModule/icons.qrc index 5793a62cf..97d8a3d79 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/icons.qrc +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/icons.qrc @@ -1,5 +1,6 @@ <RCC> <qresource> + <file>icons/app_icon.svg</file> <file>icons/folder_closed.svg</file> <file>icons/folder_open.svg</file> <file>icons/generic_file.svg</file> diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/app_icon.svg b/examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/app_icon.svg new file mode 100644 index 000000000..5aae4221f --- /dev/null +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/app_icon.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#EBDBB2" d="M13.25 8.5a.75.75 0 1 1-.75-.75.75.75 0 0 1 .75.75zM9.911 21.35l.816.578C10.819 21.798 13 18.666 13 13h-1a15.503 15.503 0 0 1-2.089 8.35zM4 6.703V10a2.002 2.002 0 0 1-2 2v1a2.002 2.002 0 0 1 2 2v3.297A3.707 3.707 0 0 0 7.703 22H9v-1H7.703A2.706 2.706 0 0 1 5 18.297V15a2.999 2.999 0 0 0-1.344-2.5A2.999 2.999 0 0 0 5 10V6.703A2.706 2.706 0 0 1 7.703 4H9V3H7.703A3.707 3.707 0 0 0 4 6.703zM20 10V6.703A3.707 3.707 0 0 0 16.297 3H15v1h1.297A2.706 2.706 0 0 1 19 6.703V10a2.999 2.999 0 0 0 1.344 2.5A2.999 2.999 0 0 0 19 15v3.297A2.706 2.706 0 0 1 16.297 21H15v1h1.297A3.707 3.707 0 0 0 20 18.297V15a2.002 2.002 0 0 1 2-2v-1a2.002 2.002 0 0 1-2-2z"/><path fill="none" d="M0 0h24v24H0z"/></svg> diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml index b7bc0ac6f..178bf03e4 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml @@ -7,53 +7,87 @@ import FileSystemModule ApplicationWindow { id: root - width: 500 - height: 360 + width: 650 + height: 550 flags: Qt.Window | Qt.FramelessWindowHint color: Colors.surface1 menuBar: MyMenuBar { id: menuBar - implicitHeight: 20 - rootWindow: root + + dragWindow: root + implicitHeight: 27 infoText: "About Qt" } Image { id: logo + anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.margins: 20 + source: "../icons/qt_logo.svg" - sourceSize: Qt.size(80, 80) + sourceSize.width: 80 + sourceSize.height: 80 fillMode: Image.PreserveAspectFit + smooth: true antialiasing: true asynchronous: true } - TextArea { - anchors.top: logo.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 20 - antialiasing: true - wrapMode: Text.WrapAnywhere - color: Colors.textFile - horizontalAlignment: Text.AlignHCenter - readOnly: true - selectionColor: Colors.selection - text: qsTr("Qt Group (Nasdaq Helsinki: QTCOM) is a global software company with a strong \ -presence in more than 70 industries and is the leading independent technology behind 1+ billion \ -devices and applications. Qt is used by major global companies and developers worldwide, and the \ -technology enables its customers to deliver exceptional user experiences and advance their digital \ -transformation initiatives. Qt achieves this through its cross-platform software framework for the \ -development of apps and devices, under both commercial and open-source licenses.") - background: Rectangle { - color: "transparent" - } + ScrollView { + anchors.top: logo.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 20 + + TextArea { + selectedTextColor: Colors.textFile + selectionColor: Colors.selection + horizontalAlignment: Text.AlignHCenter + textFormat: Text.RichText + + text: qsTr("<h3>About Qt</h3>" + + "<p>This program uses Qt version %1.</p>" + + "<p>Qt is a C++ toolkit for cross-platform application " + + "development.</p>" + + "<p>Qt provides single-source portability across all major desktop " + + "operating systems. It is also available for embedded Linux and other " + + "embedded and mobile operating systems.</p>" + + "<p>Qt is available under multiple licensing options designed " + + "to accommodate the needs of our various users.</p>" + + "<p>Qt licensed under our commercial license agreement is appropriate " + + "for development of proprietary/commercial software where you do not " + + "want to share any source code with third parties or otherwise cannot " + + "comply with the terms of GNU (L)GPL.</p>" + + "<p>Qt licensed under GNU (L)GPL is appropriate for the " + + "development of Qt applications provided you can comply with the terms " + + "and conditions of the respective licenses.</p>" + + "<p>Please see <a href=\"http://%2/\">%2</a> " + + "for an overview of Qt licensing.</p>" + + "<p>Copyright (C) %3 The Qt Company Ltd and other " + + "contributors.</p>" + + "<p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p>" + + "<p>Qt is The Qt Company Ltd product developed as an open source " + + "project. See <a href=\"http://%4/\">%4</a> for more information.</p>") + .arg(Application.version).arg("qt.io/licensing").arg("2023").arg("qt.io") + color: Colors.textFile + wrapMode: Text.WordWrap + readOnly: true + antialiasing: true + background: null + + onLinkActivated: function(link) { + Qt.openUrlExternally(link) + } + } + } + + ResizeButton { + resizeWindow: root } - ResizeButton {} } diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Colors.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Colors.qml index 280f89286..285667773 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Colors.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Colors.qml @@ -1,22 +1,23 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -pragma Singleton import QtQuick +pragma Singleton + QtObject { - readonly property color background: "#23272E" - readonly property color surface1: "#1E2227" + readonly property color background: "#292828" + readonly property color surface1: "#171819" readonly property color surface2: "#090A0C" - readonly property color text: "#ABB2BF" - readonly property color textFile: "#C5CAD3" - readonly property color disabledText: "#454D5F" - readonly property color selection: "#2C313A" - readonly property color active: "#23272E" - readonly property color inactive: "#3E4452" - readonly property color folder: "#3D4451" - readonly property color icon: "#3D4451" - readonly property color iconIndicator: "#E5C07B" - readonly property color color1: "#E06B74" - readonly property color color2: "#62AEEF" + readonly property color text: "#D4BE98" + readonly property color textFile: "#E1D2B7" + readonly property color disabledText: "#2C313A" + readonly property color selection: "#4B4A4A" + readonly property color active: "#292828" + readonly property color inactive: "#383737" + readonly property color folder: "#383737" + readonly property color icon: "#383737" + readonly property color iconIndicator: "#D5B35D" + readonly property color color1: "#A7B464" + readonly property color color2: "#D3869B" } diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml new file mode 100644 index 000000000..80f7c04c5 --- /dev/null +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml @@ -0,0 +1,160 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import FileSystemModule + +pragma ComponentBehavior: Bound + +// This is the text editor that displays the currently open file, including +// their corresponding line numbers. +Rectangle { + id: root + + required property string currentFilePath + required property bool showLineNumbers + property alias text: textArea + property int currentLineNumber: -1 + property int rowHeight: Math.ceil(fontMetrics.lineSpacing) + + color: Colors.background + + onWidthChanged: textArea.update() + onHeightChanged: textArea.update() + + RowLayout { + anchors.fill: parent + // We use a flickable to synchronize the position of the editor and + // the line numbers. This is necessary because the line numbers can + // extend the available height. + Flickable { + id: lineNumbers + + // Calculate the width based on the logarithmic scale. + Layout.preferredWidth: fontMetrics.averageCharacterWidth + * (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10 + Layout.fillHeight: true + + interactive: false + contentY: editorFlickable.contentY + visible: textArea.text !== "" && root.showLineNumbers + + Column { + anchors.fill: parent + Repeater { + id: repeatedLineNumbers + + model: LineNumberModel { + lineCount: textArea.text !== "" ? textArea.lineCount : 0 + } + + delegate: Item { + required property int index + + width: parent.width + height: root.rowHeight + Label { + id: numbers + + text: parent.index + 1 + + width: parent.width + height: parent.height + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + color: (root.currentLineNumber === parent.index) + ? Colors.iconIndicator : Qt.darker(Colors.text, 2) + font: textArea.font + } + Rectangle { + id: indicator + + anchors.left: numbers.right + width: 1 + height: parent.height + color: Qt.darker(Colors.text, 3) + } + } + } + } + } + + Flickable { + id: editorFlickable + + property alias textArea: textArea + + // We use an inline component to customize the horizontal and vertical + // scroll-bars. This is convenient when the component is only used in one file. + component MyScrollBar: ScrollBar { + id: scrollBar + background: Rectangle { + implicitWidth: scrollBar.interactive ? 8 : 4 + implicitHeight: scrollBar.interactive ? 8 : 4 + + opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0 + color: Colors.background + Behavior on opacity { + OpacityAnimator { + duration: 500 + } + } + } + contentItem: Rectangle { + implicitWidth: scrollBar.interactive ? 8 : 4 + implicitHeight: scrollBar.interactive ? 8 : 4 + opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0 + color: Colors.color1 + Behavior on opacity { + OpacityAnimator { + duration: 1000 + } + } + } + } + + Layout.fillHeight: true + Layout.fillWidth: true + ScrollBar.horizontal: MyScrollBar {} + ScrollBar.vertical: MyScrollBar {} + + boundsBehavior: Flickable.StopAtBounds + + TextArea.flickable: TextArea { + id: textArea + anchors.fill: parent + + focus: false + topPadding: 0 + leftPadding: 10 + + text: FileSystemModel.readFile(root.currentFilePath) + tabStopDistance: fontMetrics.averageCharacterWidth * 4 + + // Grab the current line number from the C++ interface. + onCursorPositionChanged: { + root.currentLineNumber = FileSystemModel.currentLineNumber( + textArea.textDocument, textArea.cursorPosition) + } + + color: Colors.textFile + selectedTextColor: Colors.textFile + selectionColor: Colors.selection + + textFormat: TextEdit.PlainText + renderType: Text.QtRendering + selectByMouse: true + antialiasing: true + background: null + } + + FontMetrics { + id: fontMetrics + font: textArea.font + } + } + } +} diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/FileSystemView.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/FileSystemView.qml index ade2e48c1..db955168c 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/FileSystemView.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/FileSystemView.qml @@ -2,26 +2,31 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick -import QtQuick.Layouts +import QtQuick.Effects import QtQuick.Controls.Basic import FileSystemModule +pragma ComponentBehavior: Bound + // This is the file system view which gets populated by the C++ model. Rectangle { id: root signal fileClicked(string filePath) + property alias rootIndex: fileSystemTreeView.rootIndex TreeView { id: fileSystemTreeView + + property int lastIndex: -1 + anchors.fill: parent model: FileSystemModel + rootIndex: FileSystemModel.rootIndex boundsBehavior: Flickable.StopAtBounds boundsMovement: Flickable.StopAtBounds clip: true - property int lastIndex: -1 - Component.onCompleted: fileSystemTreeView.toggleExpanded(0) // The delegate represents a single entry in the filesystem. @@ -31,50 +36,101 @@ Rectangle { implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250 implicitHeight: 25 + // Since we have the 'ComponentBehavior Bound' pragma, we need to + // require these properties from our model. This is a convenient way + // to bind the properties provided by the model's role names. required property int index required property url filePath + required property string fileName - indicator: null - - contentItem: Item { - anchors.fill: parent + indicator: Image { + id: directoryIcon - Icon { - id: directoryIcon - x: leftMargin + (depth * indentation) - anchors.verticalCenter: parent.verticalCenter - path: treeDelegate.hasChildren - ? (treeDelegate.expanded ? "../icons/folder_open.svg" : "../icons/folder_closed.svg") + x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation) + anchors.verticalCenter: parent.verticalCenter + source: treeDelegate.hasChildren ? (treeDelegate.expanded + ? "../icons/folder_open.svg" : "../icons/folder_closed.svg") : "../icons/generic_file.svg" - iconColor: (treeDelegate.expanded && treeDelegate.hasChildren) ? Colors.color2 : Colors.folder - } - Text { - anchors.left: directoryIcon.right - anchors.verticalCenter: parent.verticalCenter - width: parent.width - text: model.fileName - color: Colors.text - } + sourceSize.width: 20 + sourceSize.height: 20 + fillMode: Image.PreserveAspectFit + + smooth: true + antialiasing: true + asynchronous: true + } + + contentItem: Text { + text: treeDelegate.fileName + color: Colors.text } background: Rectangle { - color: treeDelegate.index === fileSystemTreeView.lastIndex + color: (treeDelegate.index === fileSystemTreeView.lastIndex) ? Colors.selection : (hoverHandler.hovered ? Colors.active : "transparent") } - TapHandler { - onSingleTapped: { - fileSystemTreeView.toggleExpanded(row) - fileSystemTreeView.lastIndex = index - // If this model item doesn't have children, it means it's representing a file. - if (!treeDelegate.hasChildren) - root.fileClicked(filePath) + // We color the directory icons with this MultiEffect, where we overlay + // the colorization color ontop of the SVG icons. + MultiEffect { + id: iconOverlay + + anchors.fill: directoryIcon + source: directoryIcon + colorization: 1.0 + brightness: 1.0 + colorizationColor: { + const isFile = treeDelegate.index === fileSystemTreeView.lastIndex + && !treeDelegate.hasChildren; + if (isFile) + return Qt.lighter(Colors.folder, 3) + + const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren; + if (isExpandedFolder) + return Colors.color2 + else + return Colors.folder } } + HoverHandler { id: hoverHandler } + + TapHandler { + acceptedButtons: Qt.LeftButton | Qt.RightButton + onSingleTapped: (eventPoint, button) => { + switch (button) { + case Qt.LeftButton: + fileSystemTreeView.toggleExpanded(treeDelegate.row) + fileSystemTreeView.lastIndex = treeDelegate.index + // If this model item doesn't have children, it means it's + // representing a file. + if (!treeDelegate.hasChildren) + root.fileClicked(treeDelegate.filePath) + break; + case Qt.RightButton: + if (treeDelegate.hasChildren) + contextMenu.popup(); + break; + } + } + } + + MyMenu { + id: contextMenu + Action { + text: qsTr("Set as root index") + onTriggered: { + fileSystemTreeView.rootIndex = fileSystemTreeView.index(treeDelegate.row, 0) + } + } + Action { + text: qsTr("Reset root index") + onTriggered: fileSystemTreeView.rootIndex = undefined + } + } } // Provide our own custom ScrollIndicator for the TreeView. @@ -85,6 +141,7 @@ Rectangle { contentItem: Rectangle { implicitWidth: 6 implicitHeight: 6 + color: Colors.color1 opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0 diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Icon.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Icon.qml deleted file mode 100644 index 25162d9d3..000000000 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Icon.qml +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import QtQuick.Effects - -// Custom Component for displaying Icons -Item { - id: root - - required property url path - property real padding: 5 - property real size: 30 - property alias iconColor: overlay.colorizationColor - property alias hovered: mouse.hovered - - width: size - height: size - - Image { - id: icon - anchors.fill: root - anchors.margins: padding - source: path - sourceSize: Qt.size(size, size) - fillMode: Image.PreserveAspectFit - smooth: true - antialiasing: true - asynchronous: true - } - - MultiEffect { - id: overlay - anchors.fill: icon - source: icon - colorization: 1.0 - brightness: 1.0 - } - - HoverHandler { - id: mouse - acceptedDevices: PointerDevice.Mouse - } -} diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenu.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenu.qml index 99795b5e5..1f1d30c56 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenu.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenu.qml @@ -8,35 +8,38 @@ import FileSystemModule Menu { id: root - background: Rectangle { - implicitWidth: 200 - implicitHeight: 40 - color: Colors.surface2 - } - delegate: MenuItem { id: menuItem - implicitWidth: 200 - implicitHeight: 40 contentItem: Item { Text { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 5 + text: menuItem.text color: enabled ? Colors.text : Colors.disabledText } Rectangle { + id: indicator + anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right width: 6 height: parent.height + visible: menuItem.highlighted color: Colors.color2 } } background: Rectangle { + implicitWidth: 210 + implicitHeight: 35 color: menuItem.highlighted ? Colors.active : "transparent" } } + background: Rectangle { + implicitWidth: 210 + implicitHeight: 35 + color: Colors.surface2 + } } diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenuBar.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenuBar.qml index a2a3fea88..4874a2c03 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenuBar.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenuBar.qml @@ -6,130 +6,172 @@ import QtQuick.Layouts import QtQuick.Controls.Basic import FileSystemModule -// The MenuBar also serves as a controller for our Window as we don't use any decorations. +// The MenuBar also serves as a controller for our window as we don't use any decorations. MenuBar { id: root - required property ApplicationWindow rootWindow + required property ApplicationWindow dragWindow property alias infoText: windowInfo.text - implicitHeight: 25 - - // The top level menus on the left side + // Customization of the top level menus inside the MenuBar delegate: MenuBarItem { id: menuBarItem - implicitHeight: 25 contentItem: Text { horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter - color: menuBarItem.highlighted ? Colors.textFile : Colors.text - opacity: enabled ? 1.0 : 0.3 + text: menuBarItem.text - elide: Text.ElideRight font: menuBarItem.font + elide: Text.ElideRight + color: menuBarItem.highlighted ? Colors.textFile : Colors.text + opacity: enabled ? 1.0 : 0.3 } background: Rectangle { + id: background + color: menuBarItem.highlighted ? Colors.selection : "transparent" Rectangle { id: indicator + width: 0; height: 3 anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom - color: Colors.color1 + color: Colors.color1 states: State { - name: "active"; when: menuBarItem.highlighted - PropertyChanges { target: indicator; width: parent.width } + name: "active" + when: menuBarItem.highlighted + PropertyChanges { + indicator.width: background.width - 2 + } } - transitions: Transition { NumberAnimation { properties: "width" - duration: 300 + duration: 175 } } - } } } + // We use the contentItem property as a place to attach our window decorations. Beneath + // the usual menu entries within a MenuBar, it includes a centered information text, along + // with the minimize, maximize, and close buttons. + contentItem: RowLayout { + id: windowBar - // The background property contains an information text in the middle as well as the - // Minimize, Maximize and Close Buttons. - background: Rectangle { - color: Colors.surface2 - // Make the empty space drag the specified root window. - WindowDragHandler { dragWindow: rootWindow } + Layout.fillWidth: true + Layout.fillHeight: true + + spacing: root.spacing + Repeater { + id: menuBarItems - Text { - id: windowInfo - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - color: Colors.text + Layout.alignment: Qt.AlignLeft + model: root.contentModel } - component InteractionButton: Rectangle { - signal action; - property alias hovered: hoverHandler.hovered + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Text { + id: windowInfo + + width: parent.width; height: parent.height + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + leftPadding: windowActions.width + color: Colors.text + clip: true + } + } - width: root.height - anchors.top: parent.top - anchors.bottom: parent.bottom - color: hovered ? Colors.background : "transparent" + RowLayout { + id: windowActions - HoverHandler { id: hoverHandler } - TapHandler { onTapped: action() } - } + Layout.alignment: Qt.AlignRight + Layout.fillHeight: true - InteractionButton { - id: minimize + spacing: 0 - anchors.right: maximize.left - onAction: rootWindow.showMinimized() - Rectangle { - width: parent.height - 10; height: 2 - anchors.centerIn: parent - color: parent.hovered ? Colors.iconIndicator : Colors.icon + component InteractionButton: Rectangle { + id: interactionButton + + signal action() + property alias hovered: hoverHandler.hovered + + Layout.fillHeight: true + Layout.preferredWidth: height + + color: hovered ? Colors.background : "transparent" + HoverHandler { + id: hoverHandler + } + TapHandler { + id: tapHandler + onTapped: interactionButton.action() + } } - } - InteractionButton { - id: maximize + InteractionButton { + id: minimize - anchors.right: close.left - onAction: rootWindow.showMaximized() - Rectangle { - anchors.fill: parent - anchors.margins: 5 - border.width: 2 - color: "transparent" - border.color: parent.hovered ? Colors.iconIndicator : Colors.icon + onAction: root.dragWindow.showMinimized() + Rectangle { + anchors.centerIn: parent + color: parent.hovered ? Colors.iconIndicator : Colors.icon + height: 2 + width: parent.height - 14 + } } - } - InteractionButton { - id: close + InteractionButton { + id: maximize - color: hovered ? "#ec4143" : "transparent" - anchors.right: parent.right - onAction: rootWindow.close() - Rectangle { - width: parent.height - 8; height: 2 - anchors.centerIn: parent - color: parent.hovered ? Colors.iconIndicator : Colors.icon - rotation: 45 - transformOrigin: Item.Center - antialiasing: true + onAction: root.dragWindow.showMaximized() + Rectangle { + anchors.fill: parent + anchors.margins: 7 + border.color: parent.hovered ? Colors.iconIndicator : Colors.icon + border.width: 2 + color: "transparent" + } + } + + InteractionButton { + id: close + + color: hovered ? "#ec4143" : "transparent" + onAction: root.dragWindow.close() Rectangle { - width: parent.height - height: parent.width anchors.centerIn: parent - color: parent.color + width: parent.height - 8; height: 2 + + rotation: 45 antialiasing: true + transformOrigin: Item.Center + color: parent.hovered ? Colors.iconIndicator : Colors.icon + + Rectangle { + anchors.centerIn: parent + width: parent.height + height: parent.width + + antialiasing: true + color: parent.color + } } } } } + background: Rectangle { + color: Colors.surface2 + // Make the empty space drag the specified root window. + WindowDragHandler { + dragWindow: root.dragWindow + } + } } diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml index eb2e5bc02..0df65bf82 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml @@ -5,6 +5,8 @@ import QtQuick.Controls import FileSystemModule Button { + required property ApplicationWindow resizeWindow + icon.width: 20; icon.height: 20 anchors.right: parent.right anchors.bottom: parent.bottom @@ -12,12 +14,10 @@ Button { bottomPadding: 3 icon.source: "../icons/resize.svg" - icon.color: down || checked ? Colors.iconIndicator : Colors.icon + icon.color: hovered ? Colors.iconIndicator : Colors.icon + background: null checkable: false display: AbstractButton.IconOnly - background: null - onPressed: { - root.startSystemResize(Qt.BottomEdge | Qt.RightEdge) - } + onPressed: resizeWindow.startSystemResize(Qt.BottomEdge | Qt.RightEdge) } diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml index 9d08562d9..aac530394 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml @@ -8,77 +8,92 @@ import FileSystemModule Rectangle { id: root + + property alias currentTabIndex: topBar.currentIndex + required property ApplicationWindow dragWindow + readonly property int tabBarSpacing: 10 + color: Colors.surface2 - required property ApplicationWindow rootWindow - property alias currentTabIndex: tabBar.currentIndex + component SidebarEntry: Button { + id: sidebarButton - ColumnLayout { - anchors.fill: root - anchors.topMargin: 10 - anchors.bottomMargin: 10 - spacing: 10 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true - // TabBar is designed to be horizontal, whereas we need a vertical bar. - // We can easily achieve that by using a Container. - Container { - id: tabBar + icon.color: down || checked ? Colors.iconIndicator : Colors.icon + icon.width: 27 + icon.height: 27 - Layout.fillWidth: true + topPadding: 0 + rightPadding: 0 + bottomPadding: 0 + leftPadding: 0 + background: null - // ButtonGroup ensures that only one button can be checked at a time. - ButtonGroup { - buttons: tabBar.contentItem.children - // We have to manage the currentIndex ourselves, which we do by setting it to the - // index of the currently checked button. - // We use setCurrentIndex instead of setting the currentIndex property to avoid breaking bindings. - // See "Managing the Current Index" in Container's documentation for more information. - onCheckedButtonChanged: tabBar.setCurrentIndex(Math.max(0, buttons.indexOf(checkedButton))) - } + Rectangle { + id: indicator - contentItem: ColumnLayout { - spacing: tabBar.spacing + anchors.verticalCenter: parent.verticalCenter + x: 2 + width: 4 + height: sidebarButton.icon.height * 1.2 - Repeater { - model: tabBar.contentModel - } - } + visible: sidebarButton.checked + color: Colors.color1 + } + } + + // TabBar is designed to be horizontal, whereas we need a vertical bar. + // We can easily achieve that by using a Container. + component TabBar: Container { + id: tabBarComponent + + Layout.fillWidth: true + // ButtonGroup ensures that only one button can be checked at a time. + ButtonGroup { + buttons: tabBarComponent.contentChildren + + // We have to manage the currentIndex ourselves, which we do by setting it to the index + // of the currently checked button. We use setCurrentIndex instead of setting the + // currentIndex property to avoid breaking bindings. See "Managing the Current Index" + // in Container's documentation for more information. + onCheckedButtonChanged: tabBarComponent.setCurrentIndex( + Math.max(0, buttons.indexOf(checkedButton))) + } - component SidebarEntry: Button { - id: sidebarButton - icon.color: down || checked ? Colors.iconIndicator : Colors.icon - icon.width: 35 - icon.height: 35 - leftPadding: 8 + indicator.width - - background: null - - Rectangle { - id: indicator - x: 4 - anchors.verticalCenter: parent.verticalCenter - width: 4 - height: sidebarButton.icon.width - color: Colors.color1 - visible: sidebarButton.checked - } + contentItem: ColumnLayout { + spacing: tabBarComponent.spacing + Repeater { + model: tabBarComponent.contentModel } + } + } + ColumnLayout { + anchors.fill: root + anchors.topMargin: root.tabBarSpacing + anchors.bottomMargin: root.tabBarSpacing + + spacing: root.tabBarSpacing + TabBar { + id: topBar + + spacing: root.tabBarSpacing // Shows help text when clicked. SidebarEntry { + id: infoTab icon.source: "../icons/light_bulb.svg" checkable: true checked: true - - Layout.alignment: Qt.AlignHCenter } // Shows the file system when clicked. SidebarEntry { + id: filesystemTab + icon.source: "../icons/read.svg" checkable: true - - Layout.alignment: Qt.AlignHCenter } } @@ -88,25 +103,31 @@ Rectangle { Layout.fillWidth: true // Make the empty space drag our main window. - WindowDragHandler { dragWindow: rootWindow } + WindowDragHandler { + dragWindow: root.dragWindow + } } - // Opens the Qt website in the system's web browser. - SidebarEntry { - id: qtWebsiteButton - icon.source: "../icons/globe.svg" - checkable: false + TabBar { + id: bottomBar - onClicked: Qt.openUrlExternally("https://www.qt.io/") - } + spacing: root.tabBarSpacing + // Opens the Qt website in the system's web browser. + SidebarEntry { + id: qtWebsiteButton + icon.source: "../icons/globe.svg" + checkable: false + onClicked: Qt.openUrlExternally("https://www.qt.io/") + } - // Opens the About Qt Window. - SidebarEntry { - id: aboutQtButton - icon.source: "../icons/info_sign.svg" - checkable: false + // Opens the About Qt Window. + SidebarEntry { + id: aboutQtButton - onClicked: aboutQtWindow.visible = !aboutQtWindow.visible + icon.source: "../icons/info_sign.svg" + checkable: false + onClicked: aboutQtWindow.visible = !aboutQtWindow.visible + } } } diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir index ff7ecb757..b1f684600 100644 --- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir +++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir @@ -1,7 +1,7 @@ module FileSystemModule Main 1.0 Main.qml -Icon 1.0 qml/Icon.qml About 1.0 qml/About.qml +Editor 1.0 qml/Editor.qml MyMenu 1.0 qml/MyMenu.qml Sidebar 1.0 qml/Sidebar.qml MyMenuBar 1.0 qml/MyMenuBar.qml diff --git a/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.rst b/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.rst index 0260928b6..b46cbec79 100644 --- a/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.rst +++ b/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.rst @@ -37,3 +37,16 @@ pleasing UIs. .. image:: filesystemexplorer.webp :target: filesystemexplorer.webp :alt: QtQuickControls Filesystem Explorer Screenshot + +References +---------- + +If you're interested in the C++ version of this example, you can find it +`here <https://doc-snapshots.qt.io/qt6-dev/qtquickcontrols-filesystemexplorer-example.html>`_. + +Additionally, there is a detailed +`tutorial <https://doc.qt.io/qtforpython-6/tutorials/extendedexplorer/extendedexplorer.html>`_ +available that provides step-by-step instructions on how to extend this example +with additional features. This tutorial can be helpful if you want to explore +and learn more about building upon the existing functionality of the filesystem +explorer. diff --git a/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.webp b/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.webp Binary files differindex cce7e1daf..10ad0d26e 100644 --- a/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.webp +++ b/examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.webp diff --git a/examples/quickcontrols/filesystemexplorer/editormodels.py b/examples/quickcontrols/filesystemexplorer/editormodels.py new file mode 100644 index 000000000..688147726 --- /dev/null +++ b/examples/quickcontrols/filesystemexplorer/editormodels.py @@ -0,0 +1,116 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtWidgets import QFileSystemModel +from PySide6.QtQuick import QQuickTextDocument +from PySide6.QtQml import QmlElement, QmlSingleton +from PySide6.QtCore import (Qt, QDir, QAbstractListModel, Slot, QFile, QTextStream, + QMimeDatabase, QFileInfo, QStandardPaths, QModelIndex, + Signal, Property) + +QML_IMPORT_NAME = "FileSystemModule" +QML_IMPORT_MAJOR_VERSION = 1 + + +@QmlElement +@QmlSingleton +class FileSystemModel(QFileSystemModel): + + rootIndexChanged = Signal() + + def getDefaultRootDir(): + return QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation) + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.mRootIndex = QModelIndex() + self.mDb = QMimeDatabase() + self.setFilter(QDir.Filter.AllEntries | QDir.Filter.Hidden | QDir.Filter.NoDotAndDotDot) + self.setInitialDirectory() + + # check for the correct mime type and then read the file. + # returns the text file's content or an error message on failure + @Slot(str, result=str) + def readFile(self, path): + if path == "": + return "" + + file = QFile(path) + + mime = self.mDb.mimeTypeForFile(QFileInfo(file)) + if ('text' in mime.comment().lower() + or any('text' in s.lower() for s in mime.parentMimeTypes())): + if file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text): + stream = QTextStream(file).readAll() + file.close() + return stream + else: + return self.tr("Error opening the file!") + return self.tr("File type not supported!") + + @Slot(QQuickTextDocument, int, result=int) + def currentLineNumber(self, textDocument, cursorPosition): + td = textDocument.textDocument() + tb = td.findBlock(cursorPosition) + return tb.blockNumber() + + def setInitialDirectory(self, path=getDefaultRootDir()): + dir = QDir(path) + if dir.makeAbsolute(): + self.setRootPath(dir.path()) + else: + self.setRootPath(self.getDefaultRootDir()) + self.setRootIndex(self.index(dir.path())) + + # we only need one column in this example + def columnCount(self, parent): + return 1 + + @Property(QModelIndex, notify=rootIndexChanged) + def rootIndex(self): + return self.mRootIndex + + def setRootIndex(self, index): + if (index == self.mRootIndex): + return + self.mRootIndex = index + self.rootIndexChanged.emit() + + +@QmlElement +class LineNumberModel(QAbstractListModel): + + lineCountChanged = Signal() + + def __init__(self, parent=None): + self.mLineCount = 0 + super().__init__(parent=parent) + + @Property(int, notify=lineCountChanged) + def lineCount(self): + return self.mLineCount + + @lineCount.setter + def lineCount(self, n): + if n < 0: + print("lineCount must be greater then zero") + return + if self.mLineCount == n: + return + + if self.mLineCount < n: + self.beginInsertRows(QModelIndex(), self.mLineCount, n - 1) + self.mLineCount = n + self.endInsertRows() + else: + self.beginRemoveRows(QModelIndex(), n, self.mLineCount - 1) + self.mLineCount = n + self.endRemoveRows() + + def rowCount(self, parent): + return self.mLineCount + + def data(self, index, role): + if not self.checkIndex(index) or role != Qt.ItemDataRole.DisplayRole: + return + return index.row() diff --git a/examples/quickcontrols/filesystemexplorer/filesystemexplorer.py b/examples/quickcontrols/filesystemexplorer/filesystemexplorer.py deleted file mode 100644 index 2bc733c37..000000000 --- a/examples/quickcontrols/filesystemexplorer/filesystemexplorer.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) 2023 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -""" -This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer. -""" - -# Compile both resource files app.qrc and icons.qrc and include them here if you wish -# to load them from the resource system. Currently, all resources are loaded locally -# import FileSystemModule.rc_icons -# import FileSystemModule.rc_app - -from PySide6.QtWidgets import QFileSystemModel -from PySide6.QtGui import QGuiApplication -from PySide6.QtQml import (QQmlApplicationEngine, QmlElement, QmlSingleton) -from PySide6.QtCore import (Slot, QFile, QTextStream, QMimeDatabase, QFileInfo, QStandardPaths) - -import sys - - -QML_IMPORT_NAME = "FileSystemModule" -QML_IMPORT_MAJOR_VERSION = 1 - - -@QmlElement -@QmlSingleton -class FileSystemModel(QFileSystemModel): - def __init__(self, parent=None): - super().__init__(parent=parent) - self.setRootPath(QStandardPaths.writableLocation(QStandardPaths.HomeLocation)) - self.db = QMimeDatabase() - - # we only need one column in this example - def columnCount(self, parent): - return 1 - - # check for the correct mime type and then read the file. - # returns the text file's content or an error message on failure - @Slot(str, result=str) - def readFile(self, path): - if path == "": - return "" - - file = QFile(path) - - mime = self.db.mimeTypeForFile(QFileInfo(file)) - if ('text' in mime.comment().lower() - or any('text' in s.lower() for s in mime.parentMimeTypes())): - if file.open(QFile.ReadOnly | QFile.Text): - stream = QTextStream(file).readAll() - return stream - else: - return self.tr("Error opening the file!") - return self.tr("File type not supported!") - - -if __name__ == '__main__': - app = QGuiApplication(sys.argv) - app.setOrganizationName("QtProject") - app.setApplicationName("File System Explorer") - engine = QQmlApplicationEngine() - # Include the path of this file to search for the 'qmldir' module - engine.addImportPath(sys.path[0]) - - engine.loadFromModule("FileSystemModule", "Main") - - if not engine.rootObjects(): - sys.exit(-1) - - sys.exit(app.exec()) diff --git a/examples/quickcontrols/filesystemexplorer/filesystemexplorer.pyproject b/examples/quickcontrols/filesystemexplorer/filesystemexplorer.pyproject index 1e1aa2ad8..8053cfab0 100644 --- a/examples/quickcontrols/filesystemexplorer/filesystemexplorer.pyproject +++ b/examples/quickcontrols/filesystemexplorer/filesystemexplorer.pyproject @@ -1,19 +1,21 @@ { "files": [ - "filesystemexplorer.py", + "main.py", + "editormodels.py", "FileSystemModule/qmldir", "FileSystemModule/app.qrc", "FileSystemModule/qmldir", "FileSystemModule/Main.qml", "FileSystemModule/qml/About.qml", "FileSystemModule/qml/Colors.qml", + "FileSystemModule/qml/Editor.qml", "FileSystemModule/qml/FileSystemView.qml", - "FileSystemModule/qml/Icon.qml", "FileSystemModule/qml/MyMenu.qml", "FileSystemModule/qml/MyMenuBar.qml", "FileSystemModule/qml/ResizeButton.qml", "FileSystemModule/qml/Sidebar.qml", "FileSystemModule/qml/WindowDragHandler.qml", + "FileSystemModule/icons/app_icon.svg", "FileSystemModule/icons/folder_closed.svg", "FileSystemModule/icons/folder_open.svg", "FileSystemModule/icons/generic_file.svg", diff --git a/examples/quickcontrols/filesystemexplorer/main.py b/examples/quickcontrols/filesystemexplorer/main.py new file mode 100644 index 000000000..ebf196ca7 --- /dev/null +++ b/examples/quickcontrols/filesystemexplorer/main.py @@ -0,0 +1,49 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +""" +This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer. +""" + +# Compile both resource files app.qrc and icons.qrc and include them here if you wish +# to load them from the resource system. Currently, all resources are loaded locally +# import FileSystemModule.rc_icons +# import FileSystemModule.rc_app + +from editormodels import FileSystemModel +import PySide6 +from PySide6.QtGui import QGuiApplication, QIcon +from PySide6.QtQml import QQmlApplicationEngine +from PySide6.QtCore import QCommandLineParser + +import sys + +if __name__ == '__main__': + app = QGuiApplication(sys.argv) + app.setOrganizationName("QtProject") + app.setApplicationName("File System Explorer") + app.setApplicationVersion(PySide6.__version__) + app.setWindowIcon(QIcon("FileSystemModule/icons/app_icon.svg")) + + parser = QCommandLineParser() + parser.setApplicationDescription("Qt Filesystemexplorer Example") + parser.addHelpOption() + parser.addVersionOption() + parser.addPositionalArgument("", "Initial directory", "[path]") + parser.process(app) + args = parser.positionalArguments() + + engine = QQmlApplicationEngine() + # Include the path of this file to search for the 'qmldir' module + engine.addImportPath(sys.path[0]) + + engine.loadFromModule("FileSystemModule", "Main") + + if not engine.rootObjects(): + sys.exit(-1) + + if (len(args) == 1): + fsm = engine.singletonInstance("FileSystemModule", "FileSystemModel") + fsm.setInitialDirectory(args[0]) + + sys.exit(app.exec()) |