123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- /****************************************************************************
- **
- ** Copyright (C) 2016 The Qt Company Ltd.
- ** Contact: https://www.qt.io/licensing/
- **
- ** This file is part of the Qt Quick Dialogs module of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:LGPL$
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and The Qt Company. For licensing terms
- ** and conditions see https://www.qt.io/terms-conditions. For further
- ** information use the contact form at https://www.qt.io/contact-us.
- **
- ** GNU Lesser General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU Lesser
- ** General Public License version 3 as published by the Free Software
- ** Foundation and appearing in the file LICENSE.LGPL3 included in the
- ** packaging of this file. Please review the following information to
- ** ensure the GNU Lesser General Public License version 3 requirements
- ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
- **
- ** GNU General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU
- ** General Public License version 2.0 or (at your option) the GNU General
- ** Public license version 3 or any later version approved by the KDE Free
- ** Qt Foundation. The licenses are as published by the Free Software
- ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
- ** included in the packaging of this file. Please review the following
- ** information to ensure the GNU General Public License requirements will
- ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
- ** https://www.gnu.org/licenses/gpl-3.0.html.
- **
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
- import QtQml 2.14 as Qml
- import QtQuick 2.2
- import QtQuick.Controls 1.2
- import QtQuick.Controls.Private 1.0 as ControlsPrivate
- import QtQuick.Dialogs 1.1
- import QtQuick.Dialogs.Private 1.1
- import QtQuick.Layouts 1.1
- import QtQuick.Window 2.1
- import Qt.labs.folderlistmodel 2.1
- import Qt.labs.settings 1.0
- import "qml"
- AbstractFileDialog {
- id: root
- property Component modelComponent: Component {
- FolderListModel {
- showFiles: !root.selectFolder
- nameFilters: root.selectedNameFilterExtensions
- sortField: (view.sortIndicatorColumn === 0 ? FolderListModel.Name :
- (view.sortIndicatorColumn === 1 ? FolderListModel.Type :
- (view.sortIndicatorColumn === 2 ? FolderListModel.Size : FolderListModel.LastModified)))
- sortReversed: view.sortIndicatorOrder === Qt.DescendingOrder
- }
- }
- onVisibleChanged: {
- if (visible) {
- // If the TableView doesn't have a model yet, create it asynchronously to avoid a UI freeze
- if (!view.model) {
- var incubator = modelComponent.incubateObject(null, { })
- function init(model) {
- view.model = model
- model.nameFilters = root.selectedNameFilterExtensions
- root.folder = model.folder
- }
- if (incubator.status === Component.Ready) {
- init(incubator.object)
- } else {
- incubator.onStatusChanged = function(status) {
- if (status === Component.Ready)
- init(incubator.object)
- }
- }
- }
- view.needsWidthAdjustment = true
- view.selection.clear()
- view.focus = true
- }
- }
- Component.onCompleted: {
- filterField.currentIndex = root.selectedNameFilterIndex
- root.favoriteFolders = settings.favoriteFolders
- }
- Component.onDestruction: {
- settings.favoriteFolders = root.favoriteFolders
- }
- property Settings settings: Settings {
- category: "QQControlsFileDialog"
- property alias width: root.width
- property alias height: root.height
- property alias sidebarWidth: sidebar.width
- property alias sidebarSplit: shortcutsScroll.height
- property alias sidebarVisible: root.sidebarVisible
- property variant favoriteFolders: []
- }
- property bool showFocusHighlight: false
- property SystemPalette palette: SystemPalette { }
- property var favoriteFolders: []
- function dirDown(path) {
- view.selection.clear()
- root.folder = "file://" + path
- }
- function dirUp() {
- view.selection.clear()
- if (view.model.parentFolder != "")
- root.folder = view.model.parentFolder
- }
- function acceptSelection() {
- // transfer the view's selections to QQuickFileDialog
- clearSelection()
- if (selectFolder && view.selection.count === 0)
- addSelection(folder)
- else {
- view.selection.forEach(function(idx) {
- if (view.model.isFolder(idx)) {
- if (selectFolder)
- addSelection(view.model.get(idx, "fileURL"))
- } else {
- if (!selectFolder)
- addSelection(view.model.get(idx, "fileURL"))
- }
- })
- }
- accept()
- }
- property Action dirUpAction: Action {
- text: "\ue810"
- shortcut: "Ctrl+U"
- onTriggered: dirUp()
- tooltip: qsTr("Go up to the folder containing this one")
- }
- Rectangle {
- id: window
- implicitWidth: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 100, splitter.implicitWidth))
- implicitHeight: Math.min(root.__maximumDimension, Screen.pixelDensity * 80)
- color: root.palette.window
- Qml.Binding {
- target: view.model
- property: "folder"
- value: root.folder
- restoreMode: Binding.RestoreBinding
- }
- Qml.Binding {
- target: currentPathField
- property: "text"
- value: root.urlToPath(root.folder)
- restoreMode: Binding.RestoreBinding
- }
- Keys.onPressed: {
- event.accepted = true
- switch (event.key) {
- case Qt.Key_Back:
- case Qt.Key_Escape:
- reject()
- break
- default:
- event.accepted = false
- break
- }
- }
- Keys.forwardTo: [view.flickableItem]
- SplitView {
- id: splitter
- x: 0
- width: parent.width
- anchors.top: titleBar.bottom
- anchors.bottom: bottomBar.top
- Column {
- id: sidebar
- Component.onCompleted: if (width < 1) width = sidebarSplitter.maxShortcutWidth
- height: parent.height
- width: 0 // initial width only; settings and onCompleted will override it
- visible: root.sidebarVisible
- SplitView {
- id: sidebarSplitter
- orientation: Qt.Vertical
- property real rowHeight: 10
- property real maxShortcutWidth: 80
- width: parent.width
- height: parent.height - favoritesButtons.height
- ScrollView {
- id: shortcutsScroll
- Component.onCompleted: {
- if (height < 1)
- height = sidebarSplitter.rowHeight * 4.65
- Layout.minimumHeight = sidebarSplitter.rowHeight * 2.65
- }
- height: 0 // initial width only; settings and onCompleted will override it
- ListView {
- id: shortcutsView
- model: __shortcuts.length
- anchors.bottomMargin: ControlsPrivate.Settings.hasTouchScreen ? Screen.pixelDensity * 3.5 : anchors.margins
- implicitHeight: model.count * sidebarSplitter.rowHeight
- delegate: Item {
- id: shortcutItem
- width: sidebarSplitter.width
- height: shortcutLabel.implicitHeight * 1.5
- Text {
- id: shortcutLabel
- text: __shortcuts[index].name
- anchors {
- verticalCenter: parent.verticalCenter
- left: parent.left
- right: parent.right
- margins: 4
- }
- elide: Text.ElideLeft
- renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering
- Component.onCompleted: {
- sidebarSplitter.rowHeight = parent.height
- if (implicitWidth * 1.2 > sidebarSplitter.maxShortcutWidth)
- sidebarSplitter.maxShortcutWidth = implicitWidth * 1.2
- }
- }
- MouseArea {
- anchors.fill: parent
- onClicked: root.folder = __shortcuts[index].url
- }
- }
- }
- }
- ScrollView {
- Layout.minimumHeight: sidebarSplitter.rowHeight * 2.5
- ListView {
- id: favorites
- model: root.favoriteFolders
- anchors.topMargin: ControlsPrivate.Settings.hasTouchScreen ? Screen.pixelDensity * 3.5 : anchors.margins
- delegate: Item {
- width: favorites.width
- height: folderLabel.implicitHeight * 1.5
- Text {
- id: folderLabel
- text: root.favoriteFolders[index]
- anchors {
- verticalCenter: parent.verticalCenter
- left: parent.left
- right: parent.right
- margins: 4
- }
- elide: Text.ElideLeft
- renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering
- }
- Menu {
- id: favoriteCtxMenu
- title: root.favoriteFolders[index]
- MenuItem {
- text: qsTr("Remove favorite")
- onTriggered: {
- root.favoriteFolders.splice(index, 1)
- favorites.model = root.favoriteFolders
- }
- }
- }
- MouseArea {
- id: favoriteArea
- anchors.fill: parent
- acceptedButtons: Qt.LeftButton | Qt.RightButton
- hoverEnabled: true
- onClicked: {
- if (mouse.button == Qt.LeftButton)
- root.folder = root.favoriteFolders[index]
- else if (mouse.button == Qt.RightButton)
- favoriteCtxMenu.popup()
- }
- onExited: ControlsPrivate.Tooltip.hideText()
- onCanceled: ControlsPrivate.Tooltip.hideText()
- Timer {
- interval: 1000
- running: favoriteArea.containsMouse && !favoriteArea.pressed && folderLabel.truncated
- onTriggered: ControlsPrivate.Tooltip.showText(favoriteArea,
- Qt.point(favoriteArea.mouseX, favoriteArea.mouseY), urlToPath(root.favoriteFolders[index]))
- }
- }
- }
- }
- }
- }
- Row {
- id: favoritesButtons
- height: plusButton.height + 1
- anchors.right: parent.right
- anchors.rightMargin: 6
- layoutDirection: Qt.RightToLeft
- Button {
- id: plusButton
- style: IconButtonStyle { }
- text: "\ue83e"
- tooltip: qsTr("Add the current directory as a favorite")
- width: height
- onClicked: {
- root.favoriteFolders.push(root.folder)
- favorites.model = root.favoriteFolders
- }
- }
- }
- }
- TableView {
- id: view
- sortIndicatorVisible: true
- Layout.fillWidth: true
- Layout.minimumWidth: 40
- property bool needsWidthAdjustment: true
- selectionMode: root.selectMultiple ?
- (ControlsPrivate.Settings.hasTouchScreen ? SelectionMode.MultiSelection : SelectionMode.ExtendedSelection) :
- SelectionMode.SingleSelection
- onRowCountChanged: if (needsWidthAdjustment && rowCount > 0) {
- resizeColumnsToContents()
- needsWidthAdjustment = false
- }
- model: null
- onActivated: if (view.focus) {
- if (view.selection.count > 0 && view.model.isFolder(row)) {
- dirDown(view.model.get(row, "filePath"))
- } else {
- root.acceptSelection()
- }
- }
- onClicked: currentPathField.text = view.model.get(row, "filePath")
- TableViewColumn {
- id: fileNameColumn
- role: "fileName"
- title: qsTr("Filename")
- delegate: Item {
- implicitWidth: pathText.implicitWidth + pathText.anchors.leftMargin + pathText.anchors.rightMargin
- IconGlyph {
- id: fileIcon
- x: 4
- height: parent.height - 2
- unicode: view.model.isFolder(styleData.row) ? "\ue804" : "\ue802"
- }
- Text {
- id: pathText
- text: styleData.value
- anchors {
- left: parent.left
- right: parent.right
- leftMargin: fileIcon.width + 6
- rightMargin: 4
- verticalCenter: parent.verticalCenter
- }
- color: styleData.textColor
- elide: Text.ElideRight
- renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering
- }
- }
- }
- TableViewColumn {
- role: "fileSuffix"
- title: qsTr("Type", "file type (extension)")
- // TODO should not need to create a whole new component just to customize the text value
- // something like textFormat: function(text) { return view.model.get(styleData.row, "fileIsDir") ? "folder" : text }
- delegate: Item {
- implicitWidth: sizeText.implicitWidth + sizeText.anchors.leftMargin + sizeText.anchors.rightMargin
- Text {
- id: sizeText
- text: view.model.get(styleData.row, "fileIsDir") ? "folder" : styleData.value
- anchors {
- left: parent.left
- right: parent.right
- leftMargin: 4
- rightMargin: 4
- verticalCenter: parent.verticalCenter
- }
- color: styleData.textColor
- elide: Text.ElideRight
- renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering
- }
- }
- }
- TableViewColumn {
- role: "fileSize"
- title: qsTr("Size", "file size")
- horizontalAlignment: Text.AlignRight
- }
- TableViewColumn { id: modifiedColumn; role: "fileModified" ; title: qsTr("Modified", "last-modified time") }
- TableViewColumn { id: accessedColumn; role: "fileAccessed" ; title: qsTr("Accessed", "last-accessed time") }
- }
- }
- ToolBar {
- id: titleBar
- RowLayout {
- anchors.fill: parent
- ToolButton {
- action: dirUpAction
- style: IconButtonStyle { }
- Layout.maximumWidth: height * 1.5
- }
- TextField {
- id: currentPathField
- Layout.fillWidth: true
- function doAccept() {
- root.clearSelection()
- if (root.addSelection(root.pathToUrl(text)))
- root.accept()
- else
- root.folder = root.pathFolder(text)
- }
- onAccepted: doAccept()
- }
- }
- }
- Item {
- id: bottomBar
- width: parent.width
- height: buttonRow.height + buttonRow.spacing * 2
- anchors.bottom: parent.bottom
- Row {
- id: buttonRow
- anchors.right: parent.right
- anchors.rightMargin: spacing
- anchors.verticalCenter: parent.verticalCenter
- spacing: 4
- Button {
- id: toggleSidebarButton
- checkable: true
- style: IconButtonStyle { }
- text: "\u25E7"
- height: cancelButton.height
- width: height
- checked: root.sidebarVisible
- onClicked: {
- root.sidebarVisible = !root.sidebarVisible
- }
- }
- ComboBox {
- id: filterField
- model: root.nameFilters
- visible: !selectFolder
- width: bottomBar.width - toggleSidebarButton.width - cancelButton.width - okButton.width - parent.spacing * 6
- anchors.verticalCenter: parent.verticalCenter
- onCurrentTextChanged: {
- root.selectNameFilter(currentText)
- if (view.model)
- view.model.nameFilters = root.selectedNameFilterExtensions
- }
- }
- Button {
- id: cancelButton
- text: qsTr("Cancel")
- onClicked: root.reject()
- }
- Button {
- id: okButton
- text: root.selectFolder ? qsTr("Choose") : (selectExisting ? qsTr("Open") : qsTr("Save"))
- onClicked: {
- if (view.model.isFolder(view.currentRow) && !selectFolder)
- dirDown(view.model.get(view.currentRow, "filePath"))
- else if (!(root.selectExisting))
- currentPathField.doAccept()
- else
- root.acceptSelection()
- }
- }
- }
- }
- }
- }
|