aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Ghinet <samuel.ghinet@qt.io>2022-10-07 19:49:52 +0300
committerSamuel Ghinet <samuel.ghinet@qt.io>2022-10-10 12:00:23 +0000
commit7319eac2835b21d9a8c3be72016071de07d608d2 (patch)
treee0460993a620166f7f87730c87e945f811a3ce9f
parentfcc7edc135012ed5853e7afc7c56c811d95d2464 (diff)
Refactor Assets Library
Extracted the dialogs, the context menu, and the assets view into separate qml files. Also, reordered some functions in the assets library model Task-number: QDS-7344 Change-Id: Ida21b60d30f34723c07b2659a138e14b95598421 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml512
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml120
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml246
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml92
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml102
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml124
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp148
7 files changed, 763 insertions, 581 deletions
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml
index 6578f96b7d..1a91e2f492 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml
@@ -46,6 +46,10 @@ Item {
// Array of supported externally dropped files that trigger custom import process
property var dropComplexExtFiles: []
+ AssetsContextMenu {
+ id: contextMenu
+ }
+
function clearSearchFilter()
{
searchBox.clear();
@@ -125,307 +129,11 @@ Item {
root.selectedAssetsChanged()
}
- StudioControls.Menu {
- id: contextMenu
-
- closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
-
- onOpened: {
- var numSelected = Object.values(root.selectedAssets).filter(p => p).length
- deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File")
- }
-
- StudioControls.MenuItem {
- text: qsTr("Expand All")
- enabled: root.allExpandedState !== 1
- visible: root.isDirContextMenu
- height: visible ? implicitHeight : 0
- onTriggered: assetsModel.toggleExpandAll(true)
- }
-
- StudioControls.MenuItem {
- text: qsTr("Collapse All")
- enabled: root.allExpandedState !== 2
- visible: root.isDirContextMenu
- height: visible ? implicitHeight : 0
- onTriggered: assetsModel.toggleExpandAll(false)
- }
-
- StudioControls.MenuSeparator {
- visible: root.isDirContextMenu
- height: visible ? StudioTheme.Values.border : 0
- }
-
- StudioControls.MenuItem {
- id: deleteFileItem
- text: qsTr("Delete File")
- visible: root.contextFilePath
- height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
- onTriggered: {
- assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]))
- }
- }
-
- StudioControls.MenuSeparator {
- visible: root.contextFilePath
- height: visible ? StudioTheme.Values.border : 0
- }
-
- StudioControls.MenuItem {
- text: qsTr("Rename Folder")
- visible: root.isDirContextMenu
- height: visible ? implicitHeight : 0
- onTriggered: renameFolderDialog.open()
- }
-
- StudioControls.MenuItem {
- text: qsTr("New Folder")
- onTriggered: newFolderDialog.open()
- }
-
- StudioControls.MenuItem {
- text: qsTr("Delete Folder")
- visible: root.isDirContextMenu
- height: visible ? implicitHeight : 0
- onTriggered: {
- var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0)
- && !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0);
-
- if (dirEmpty)
- assetsModel.deleteFolder(root.contextDir.dirPath)
- else
- confirmDeleteFolderDialog.open()
- }
- }
- }
-
RegExpValidator {
id: folderNameValidator
regExp: /^(\w[^*/><?\\|:]*)$/
}
- Dialog {
- id: renameFolderDialog
-
- title: qsTr("Rename Folder")
- anchors.centerIn: parent
- closePolicy: Popup.CloseOnEscape
- implicitWidth: 280
- modal: true
-
- property bool renameError: false
-
- contentItem: Column {
- spacing: 2
-
- StudioControls.TextField {
- id: folderRename
-
- actionIndicator.visible: false
- translationIndicator.visible: false
- width: renameFolderDialog.width - 12
- validator: folderNameValidator
-
- onEditChanged: renameFolderDialog.renameError = false
- Keys.onEnterPressed: btnRename.onClicked()
- Keys.onReturnPressed: btnRename.onClicked()
- }
-
- Text {
- text: qsTr("Folder name cannot be empty.")
- color: "#ff0000"
- visible: folderRename.text === "" && !renameFolderDialog.renameError
- }
-
- Text {
- text: qsTr("Could not rename folder. Make sure no folder with the same name exists.")
- wrapMode: Text.WordWrap
- width: renameFolderDialog.width - 12
- color: "#ff0000"
- visible: renameFolderDialog.renameError
- }
-
- Item { // spacer
- width: 1
- height: 10
- }
-
- Text {
- text: qsTr("If the folder has assets in use, renaming it might cause the project to not work correctly.")
- color: StudioTheme.Values.themeTextColor
- wrapMode: Text.WordWrap
- width: renameFolderDialog.width
- leftPadding: 10
- rightPadding: 10
- }
-
- Item { // spacer
- width: 1
- height: 20
- }
-
- Row {
- anchors.right: parent.right
-
- Button {
- id: btnRename
-
- text: qsTr("Rename")
- enabled: folderRename.text !== ""
- onClicked: {
- var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text)
- if (success)
- renameFolderDialog.accept()
-
- renameFolderDialog.renameError = !success
- }
- }
-
- Button {
- text: qsTr("Cancel")
- onClicked: renameFolderDialog.reject()
- }
- }
- }
-
- onOpened: {
- folderRename.text = root.contextDir.dirName
- folderRename.selectAll()
- folderRename.forceActiveFocus()
- renameFolderDialog.renameError = false
- }
- }
-
- Dialog {
- id: newFolderDialog
-
- title: qsTr("Create New Folder")
- anchors.centerIn: parent
- closePolicy: Popup.CloseOnEscape
- modal: true
-
- contentItem: Column {
- spacing: 2
-
- Row {
- Text {
- text: qsTr("Folder name: ")
- anchors.verticalCenter: parent.verticalCenter
- color: StudioTheme.Values.themeTextColor
- }
-
- StudioControls.TextField {
- id: folderName
-
- actionIndicator.visible: false
- translationIndicator.visible: false
- validator: folderNameValidator
-
- Keys.onEnterPressed: btnCreate.onClicked()
- Keys.onReturnPressed: btnCreate.onClicked()
- }
- }
-
- Text {
- text: qsTr("Folder name cannot be empty.")
- color: "#ff0000"
- anchors.right: parent.right
- visible: folderName.text === ""
- }
-
- Item { // spacer
- width: 1
- height: 20
- }
-
- Row {
- anchors.right: parent.right
-
- Button {
- id: btnCreate
-
- text: qsTr("Create")
- enabled: folderName.text !== ""
- onClicked: {
- assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text)
- newFolderDialog.accept()
- }
- }
-
- Button {
- text: qsTr("Cancel")
- onClicked: newFolderDialog.reject()
- }
- }
- }
-
- onOpened: {
- folderName.text = "New folder"
- folderName.selectAll()
- folderName.forceActiveFocus()
- }
- }
-
- Dialog {
- id: confirmDeleteFolderDialog
-
- title: qsTr("Folder Not Empty")
- anchors.centerIn: parent
- closePolicy: Popup.CloseOnEscape
- implicitWidth: 300
- modal: true
-
- contentItem: Column {
- spacing: 20
- width: parent.width
-
- Text {
- id: folderNotEmpty
-
- text: qsTr("Folder \"%1\" is not empty. Delete it anyway?")
- .arg(root.contextDir ? root.contextDir.dirName : "")
- color: StudioTheme.Values.themeTextColor
- wrapMode: Text.WordWrap
- width: confirmDeleteFolderDialog.width
- leftPadding: 10
- rightPadding: 10
-
- Keys.onEnterPressed: btnDelete.onClicked()
- Keys.onReturnPressed: btnDelete.onClicked()
- }
-
- Text {
- text: qsTr("If the folder has assets in use, deleting it might cause the project to not work correctly.")
- color: StudioTheme.Values.themeTextColor
- wrapMode: Text.WordWrap
- width: confirmDeleteFolderDialog.width
- leftPadding: 10
- rightPadding: 10
- }
-
- Row {
- anchors.right: parent.right
- Button {
- id: btnDelete
-
- text: qsTr("Delete")
-
- onClicked: {
- assetsModel.deleteFolder(root.contextDir.dirPath)
- confirmDeleteFolderDialog.accept()
- }
- }
-
- Button {
- text: qsTr("Cancel")
- onClicked: confirmDeleteFolderDialog.reject()
- }
- }
- }
-
- onOpened: folderNotEmpty.forceActiveFocus()
- }
-
Column {
anchors.fill: parent
anchors.topMargin: 5
@@ -529,220 +237,10 @@ Item {
}
}
- ScrollView { // TODO: experiment using ListView instead of ScrollView + Column
+ AssetsView {
id: assetsView
width: parent.width
height: parent.height - y
- clip: true
- interactive: assetsView.verticalScrollBarVisible && !contextMenu.opened
-
- Column {
- Repeater {
- model: assetsModel // context property
- delegate: dirSection
- }
-
- Component {
- id: dirSection
-
- Section {
- id: section
-
- width: assetsView.width -
- (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5
- caption: dirName
- sectionHeight: 30
- sectionFontSize: 15
- leftPadding: 0
- topPadding: dirDepth > 0 ? 5 : 0
- bottomPadding: 0
- hideHeader: dirDepth === 0
- showLeftBorder: dirDepth > 0
- expanded: dirExpanded
- visible: dirVisible
- expandOnClick: false
- useDefaulContextMenu: false
- dropEnabled: true
-
- onToggleExpand: {
- dirExpanded = !dirExpanded
- }
-
- onDropEnter: (drag)=> {
- root.updateDropExtFiles(drag)
- section.highlight = drag.accepted && root.dropSimpleExtFiles.length > 0
- }
-
- onDropExit: {
- section.highlight = false
- }
-
- onDrop: {
- section.highlight = false
- rootView.handleExtFilesDrop(root.dropSimpleExtFiles,
- root.dropComplexExtFiles,
- dirPath)
- }
-
- onShowContextMenu: {
- root.contextFilePath = ""
- root.contextDir = model
- root.isDirContextMenu = true
- root.allExpandedState = assetsModel.getAllExpandedState()
- contextMenu.popup()
- }
-
- Column {
- spacing: 5
- leftPadding: 5
-
- Repeater {
- model: dirsModel
- delegate: dirSection
- }
-
- Repeater {
- model: filesModel
- delegate: fileSection
- }
-
- Text {
- text: qsTr("Empty folder")
- color: StudioTheme.Values.themeTextColorDisabled
- font.pixelSize: 12
- visible: !(dirsModel && dirsModel.rowCount() > 0)
- && !(filesModel && filesModel.rowCount() > 0)
-
- MouseArea {
- anchors.fill: parent
- acceptedButtons: Qt.RightButton
- onClicked: {
- root.contextFilePath = ""
- root.contextDir = model
- root.isDirContextMenu = true
- contextMenu.popup()
- }
- }
- }
- }
- }
- }
-
- Component {
- id: fileSection
-
- Rectangle {
- width: assetsView.width -
- (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
- height: img.height
- color: root.selectedAssets[filePath]
- ? StudioTheme.Values.themeInteraction
- : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
- : "transparent")
-
- Row {
- spacing: 5
-
- Image {
- id: img
- asynchronous: true
- fillMode: Image.PreserveAspectFit
- width: 48
- height: 48
- source: "image://qmldesigner_assets/" + filePath
- }
-
- Text {
- text: fileName
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: 14
- anchors.verticalCenter: parent.verticalCenter
- }
- }
-
- readonly property string suffix: fileName.substr(-4)
- readonly property bool isFont: suffix === ".ttf" || suffix === ".otf"
- property bool currFileSelected: false
-
- MouseArea {
- id: mouseArea
-
- property bool allowTooltip: true
-
- anchors.fill: parent
- hoverEnabled: true
- acceptedButtons: Qt.LeftButton | Qt.RightButton
-
- onExited: tooltipBackend.hideTooltip()
- onEntered: allowTooltip = true
- onCanceled: {
- tooltipBackend.hideTooltip()
- allowTooltip = true
- }
- onPositionChanged: tooltipBackend.reposition()
- onPressed: (mouse)=> {
- forceActiveFocus()
- allowTooltip = false
- tooltipBackend.hideTooltip()
- var ctrlDown = mouse.modifiers & Qt.ControlModifier
- if (mouse.button === Qt.LeftButton) {
- if (!root.selectedAssets[filePath] && !ctrlDown)
- root.selectedAssets = {}
- currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true
- root.selectedAssets[filePath] = currFileSelected
- root.selectedAssetsChanged()
-
- if (currFileSelected) {
- rootView.startDragAsset(
- Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]),
- mapToGlobal(mouse.x, mouse.y))
- }
- } else {
- if (!root.selectedAssets[filePath] && !ctrlDown)
- root.selectedAssets = {}
- currFileSelected = root.selectedAssets[filePath] || !ctrlDown
- root.selectedAssets[filePath] = currFileSelected
- root.selectedAssetsChanged()
-
- root.contextFilePath = filePath
- root.contextDir = model.fileDir
- root.isDirContextMenu = false
-
- contextMenu.popup()
- }
- }
-
- onReleased: (mouse)=> {
- allowTooltip = true
- if (mouse.button === Qt.LeftButton) {
- if (!(mouse.modifiers & Qt.ControlModifier))
- root.selectedAssets = {}
- root.selectedAssets[filePath] = currFileSelected
- root.selectedAssetsChanged()
- }
- }
-
- ToolTip {
- visible: !isFont && mouseArea.containsMouse && !contextMenu.visible
- text: filePath
- delay: 1000
- }
-
- Timer {
- interval: 1000
- running: mouseArea.containsMouse && mouseArea.allowTooltip
- onTriggered: {
- if (suffix === ".ttf" || suffix === ".otf") {
- tooltipBackend.name = fileName
- tooltipBackend.path = filePath
- tooltipBackend.showTooltip()
- }
- }
- }
- }
- }
- }
- }
}
}
}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml
new file mode 100644
index 0000000000..5caa139651
--- /dev/null
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuickDesignerTheme
+import HelperWidgets as HelperWidgets
+import StudioControls as StudioControls
+import StudioTheme as StudioTheme
+
+StudioControls.Menu {
+ id: contextMenu
+
+ closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
+
+ onOpened: {
+ var numSelected = Object.values(root.selectedAssets).filter(p => p).length
+ deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File")
+ }
+
+ StudioControls.MenuItem {
+ text: qsTr("Expand All")
+ enabled: root.allExpandedState !== 1
+ visible: root.isDirContextMenu
+ height: visible ? implicitHeight : 0
+ onTriggered: assetsModel.toggleExpandAll(true)
+ }
+
+ StudioControls.MenuItem {
+ text: qsTr("Collapse All")
+ enabled: root.allExpandedState !== 2
+ visible: root.isDirContextMenu
+ height: visible ? implicitHeight : 0
+ onTriggered: assetsModel.toggleExpandAll(false)
+ }
+
+ StudioControls.MenuSeparator {
+ visible: root.isDirContextMenu
+ height: visible ? StudioTheme.Values.border : 0
+ }
+
+ StudioControls.MenuItem {
+ id: deleteFileItem
+ text: qsTr("Delete File")
+ visible: root.contextFilePath
+ height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
+ onTriggered: {
+ assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]))
+ }
+ }
+
+ StudioControls.MenuSeparator {
+ visible: root.contextFilePath
+ height: visible ? StudioTheme.Values.border : 0
+ }
+
+ StudioControls.MenuItem {
+ text: qsTr("Rename Folder")
+ visible: root.isDirContextMenu
+ height: visible ? implicitHeight : 0
+ onTriggered: renameFolderDialog.open()
+
+ RenameFolderDialog {
+ id: renameFolderDialog
+ }
+ }
+
+ StudioControls.MenuItem {
+ text: qsTr("New Folder")
+
+ NewFolderDialog {
+ id: newFolderDialog
+ }
+
+ onTriggered: newFolderDialog.open()
+ }
+
+ StudioControls.MenuItem {
+ text: qsTr("Delete Folder")
+ visible: root.isDirContextMenu
+ height: visible ? implicitHeight : 0
+
+ ConfirmDeleteFolderDialog {
+ id: confirmDeleteFolderDialog
+ }
+
+ onTriggered: {
+ var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0)
+ && !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0);
+
+ if (dirEmpty)
+ assetsModel.deleteFolder(root.contextDir.dirPath)
+ else
+ confirmDeleteFolderDialog.open()
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
new file mode 100644
index 0000000000..e1c017814d
--- /dev/null
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuickDesignerTheme
+import HelperWidgets
+import StudioControls as StudioControls
+import StudioTheme as StudioTheme
+
+ScrollView { // TODO: experiment using ListView instead of ScrollView + Column
+ id: assetsView
+ clip: true
+ interactive: assetsView.verticalScrollBarVisible && !contextMenu.opened
+
+ Column {
+ Repeater {
+ model: assetsModel // context property
+ delegate: dirSection
+ }
+
+ Component {
+ id: dirSection
+
+ Section {
+ id: section
+
+ width: assetsView.width -
+ (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5
+ caption: dirName
+ sectionHeight: 30
+ sectionFontSize: 15
+ leftPadding: 0
+ topPadding: dirDepth > 0 ? 5 : 0
+ bottomPadding: 0
+ hideHeader: dirDepth === 0
+ showLeftBorder: dirDepth > 0
+ expanded: dirExpanded
+ visible: dirVisible
+ expandOnClick: false
+ useDefaulContextMenu: false
+ dropEnabled: true
+
+ onToggleExpand: {
+ dirExpanded = !dirExpanded
+ }
+
+ onDropEnter: (drag)=> {
+ root.updateDropExtFiles(drag)
+ section.highlight = drag.accepted && root.dropSimpleExtFiles.length > 0
+ }
+
+ onDropExit: {
+ section.highlight = false
+ }
+
+ onDrop: {
+ section.highlight = false
+ rootView.handleExtFilesDrop(root.dropSimpleExtFiles,
+ root.dropComplexExtFiles,
+ dirPath)
+ }
+
+ onShowContextMenu: {
+ root.contextFilePath = ""
+ root.contextDir = model
+ root.isDirContextMenu = true
+ root.allExpandedState = assetsModel.getAllExpandedState()
+ contextMenu.popup()
+ }
+
+ Column {
+ spacing: 5
+ leftPadding: 5
+
+ Repeater {
+ model: dirsModel
+ delegate: dirSection
+ }
+
+ Repeater {
+ model: filesModel
+ delegate: fileSection
+ }
+
+ Text {
+ text: qsTr("Empty folder")
+ color: StudioTheme.Values.themeTextColorDisabled
+ font.pixelSize: 12
+ visible: !(dirsModel && dirsModel.rowCount() > 0)
+ && !(filesModel && filesModel.rowCount() > 0)
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton
+ onClicked: {
+ root.contextFilePath = ""
+ root.contextDir = model
+ root.isDirContextMenu = true
+ contextMenu.popup()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: fileSection
+
+ Rectangle {
+ width: assetsView.width -
+ (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
+ height: img.height
+ color: root.selectedAssets[filePath]
+ ? StudioTheme.Values.themeInteraction
+ : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
+ : "transparent")
+
+ Row {
+ spacing: 5
+
+ Image {
+ id: img
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ width: 48
+ height: 48
+ source: "image://qmldesigner_assets/" + filePath
+ }
+
+ Text {
+ text: fileName
+ color: StudioTheme.Values.themeTextColor
+ font.pixelSize: 14
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+
+ readonly property string suffix: fileName.substr(-4)
+ readonly property bool isFont: suffix === ".ttf" || suffix === ".otf"
+ property bool currFileSelected: false
+
+ MouseArea {
+ id: mouseArea
+
+ property bool allowTooltip: true
+
+ anchors.fill: parent
+ hoverEnabled: true
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+
+ onExited: tooltipBackend.hideTooltip()
+ onEntered: allowTooltip = true
+ onCanceled: {
+ tooltipBackend.hideTooltip()
+ allowTooltip = true
+ }
+ onPositionChanged: tooltipBackend.reposition()
+ onPressed: (mouse)=> {
+ forceActiveFocus()
+ allowTooltip = false
+ tooltipBackend.hideTooltip()
+ var ctrlDown = mouse.modifiers & Qt.ControlModifier
+ if (mouse.button === Qt.LeftButton) {
+ if (!root.selectedAssets[filePath] && !ctrlDown)
+ root.selectedAssets = {}
+ currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true
+ root.selectedAssets[filePath] = currFileSelected
+ root.selectedAssetsChanged()
+
+ if (currFileSelected) {
+ rootView.startDragAsset(
+ Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]),
+ mapToGlobal(mouse.x, mouse.y))
+ }
+ } else {
+ if (!root.selectedAssets[filePath] && !ctrlDown)
+ root.selectedAssets = {}
+ currFileSelected = root.selectedAssets[filePath] || !ctrlDown
+ root.selectedAssets[filePath] = currFileSelected
+ root.selectedAssetsChanged()
+
+ root.contextFilePath = filePath
+ root.contextDir = model.fileDir
+ root.isDirContextMenu = false
+
+ contextMenu.popup()
+ }
+ }
+
+ onReleased: (mouse)=> {
+ allowTooltip = true
+ if (mouse.button === Qt.LeftButton) {
+ if (!(mouse.modifiers & Qt.ControlModifier))
+ root.selectedAssets = {}
+ root.selectedAssets[filePath] = currFileSelected
+ root.selectedAssetsChanged()
+ }
+ }
+
+ ToolTip {
+ visible: !isFont && mouseArea.containsMouse && !contextMenu.visible
+ text: filePath
+ delay: 1000
+ }
+
+ Timer {
+ interval: 1000
+ running: mouseArea.containsMouse && mouseArea.allowTooltip
+ onTriggered: {
+ if (suffix === ".ttf" || suffix === ".otf") {
+ tooltipBackend.name = fileName
+ tooltipBackend.path = filePath
+ tooltipBackend.showTooltip()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml
new file mode 100644
index 0000000000..a4fd300975
--- /dev/null
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuickDesignerTheme
+import HelperWidgets
+import StudioControls as StudioControls
+import StudioTheme as StudioTheme
+
+Dialog {
+ id: confirmDeleteFolderDialog
+
+ title: qsTr("Folder Not Empty")
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape
+ implicitWidth: 300
+ modal: true
+
+ contentItem: Column {
+ spacing: 20
+ width: parent.width
+
+ Text {
+ id: folderNotEmpty
+
+ text: qsTr("Folder \"%1\" is not empty. Delete it anyway?")
+ .arg(root.contextDir ? root.contextDir.dirName : "")
+ color: StudioTheme.Values.themeTextColor
+ wrapMode: Text.WordWrap
+ width: confirmDeleteFolderDialog.width
+ leftPadding: 10
+ rightPadding: 10
+
+ Keys.onEnterPressed: btnDelete.onClicked()
+ Keys.onReturnPressed: btnDelete.onClicked()
+ }
+
+ Text {
+ text: qsTr("If the folder has assets in use, deleting it might cause the project to not work correctly.")
+ color: StudioTheme.Values.themeTextColor
+ wrapMode: Text.WordWrap
+ width: confirmDeleteFolderDialog.width
+ leftPadding: 10
+ rightPadding: 10
+ }
+
+ Row {
+ anchors.right: parent.right
+ Button {
+ id: btnDelete
+
+ text: qsTr("Delete")
+
+ onClicked: {
+ assetsModel.deleteFolder(root.contextDir.dirPath)
+ confirmDeleteFolderDialog.accept()
+ }
+ }
+
+ Button {
+ text: qsTr("Cancel")
+ onClicked: confirmDeleteFolderDialog.reject()
+ }
+ }
+ }
+
+ onOpened: folderNotEmpty.forceActiveFocus()
+}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml
new file mode 100644
index 0000000000..130026ddce
--- /dev/null
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuickDesignerTheme
+import HelperWidgets
+import StudioControls as StudioControls
+import StudioTheme as StudioTheme
+
+Dialog {
+ id: newFolderDialog
+
+ title: qsTr("Create New Folder")
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape
+ modal: true
+
+ contentItem: Column {
+ spacing: 2
+
+ Row {
+ Text {
+ text: qsTr("Folder name: ")
+ anchors.verticalCenter: parent.verticalCenter
+ color: StudioTheme.Values.themeTextColor
+ }
+
+ StudioControls.TextField {
+ id: folderName
+
+ actionIndicator.visible: false
+ translationIndicator.visible: false
+ validator: folderNameValidator
+
+ Keys.onEnterPressed: btnCreate.onClicked()
+ Keys.onReturnPressed: btnCreate.onClicked()
+ }
+ }
+
+ Text {
+ text: qsTr("Folder name cannot be empty.")
+ color: "#ff0000"
+ anchors.right: parent.right
+ visible: folderName.text === ""
+ }
+
+ Item { // spacer
+ width: 1
+ height: 20
+ }
+
+ Row {
+ anchors.right: parent.right
+
+ Button {
+ id: btnCreate
+
+ text: qsTr("Create")
+ enabled: folderName.text !== ""
+ onClicked: {
+ assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text)
+ newFolderDialog.accept()
+ }
+ }
+
+ Button {
+ text: qsTr("Cancel")
+ onClicked: newFolderDialog.reject()
+ }
+ }
+ }
+
+ onOpened: {
+ folderName.text = qsTr("New folder")
+ folderName.selectAll()
+ folderName.forceActiveFocus()
+ }
+}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml
new file mode 100644
index 0000000000..351c0a35fc
--- /dev/null
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuickDesignerTheme
+import HelperWidgets
+import StudioControls as StudioControls
+import StudioTheme as StudioTheme
+
+Dialog {
+ id: renameFolderDialog
+
+ title: qsTr("Rename Folder")
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape
+ implicitWidth: 280
+ modal: true
+
+ property bool renameError: false
+
+ contentItem: Column {
+ spacing: 2
+
+ StudioControls.TextField {
+ id: folderRename
+
+ actionIndicator.visible: false
+ translationIndicator.visible: false
+ width: renameFolderDialog.width - 12
+ validator: folderNameValidator
+
+ onEditChanged: renameFolderDialog.renameError = false
+ Keys.onEnterPressed: btnRename.onClicked()
+ Keys.onReturnPressed: btnRename.onClicked()
+ }
+
+ Text {
+ text: qsTr("Folder name cannot be empty.")
+ color: "#ff0000"
+ visible: folderRename.text === "" && !renameFolderDialog.renameError
+ }
+
+ Text {
+ text: qsTr("Could not rename folder. Make sure no folder with the same name exists.")
+ wrapMode: Text.WordWrap
+ width: renameFolderDialog.width - 12
+ color: "#ff0000"
+ visible: renameFolderDialog.renameError
+ }
+
+ Item { // spacer
+ width: 1
+ height: 10
+ }
+
+ Text {
+ text: qsTr("If the folder has assets in use, renaming it might cause the project to not work correctly.")
+ color: StudioTheme.Values.themeTextColor
+ wrapMode: Text.WordWrap
+ width: renameFolderDialog.width
+ leftPadding: 10
+ rightPadding: 10
+ }
+
+ Item { // spacer
+ width: 1
+ height: 20
+ }
+
+ Row {
+ anchors.right: parent.right
+
+ Button {
+ id: btnRename
+
+ text: qsTr("Rename")
+ enabled: folderRename.text !== ""
+ onClicked: {
+ var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text)
+ if (success)
+ renameFolderDialog.accept()
+
+ renameFolderDialog.renameError = !success
+ }
+ }
+
+ Button {
+ text: qsTr("Cancel")
+ onClicked: renameFolderDialog.reject()
+ }
+ }
+ }
+
+ onOpened: {
+ folderRename.text = root.contextDir.dirName
+ folderRename.selectAll()
+ folderRename.forceActiveFocus()
+ renameFolderDialog.renameError = false
+ }
+}
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
index 18967a5fe1..c538b45272 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
@@ -57,6 +57,25 @@ static Q_LOGGING_CATEGORY(assetsLibraryBenchmark, "qtc.assetsLibrary.setRoot", Q
namespace QmlDesigner {
+AssetsLibraryModel::AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_fileSystemWatcher(fileSystemWatcher)
+{
+ // add role names
+ int role = 0;
+ const QMetaObject meta = AssetsLibraryDir::staticMetaObject;
+ for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i)
+ m_roleNames.insert(role++, meta.property(i).name());
+}
+
+void AssetsLibraryModel::setSearchText(const QString &searchText)
+{
+ if (m_searchText != searchText) {
+ m_searchText = searchText;
+ refresh();
+ }
+}
+
void AssetsLibraryModel::saveExpandedState(bool expanded, const QString &assetPath)
{
m_expandedStateHash.insert(assetPath, expanded);
@@ -208,66 +227,18 @@ QObject *AssetsLibraryModel::rootDir() const
return m_assetsDir;
}
-const QStringList &AssetsLibraryModel::supportedImageSuffixes()
-{
- static QStringList retList;
- if (retList.isEmpty()) {
- const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
- for (const QByteArray &suffix : suffixes)
- retList.append("*." + QString::fromUtf8(suffix));
- }
- return retList;
-}
-
-const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes()
-{
- static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"};
- return retList;
-}
-
-const QStringList &AssetsLibraryModel::supportedShaderSuffixes()
-{
- static const QStringList retList {"*.frag", "*.vert",
- "*.glsl", "*.glslv", "*.glslf",
- "*.vsh", "*.fsh"};
- return retList;
-}
-
-const QStringList &AssetsLibraryModel::supportedFontSuffixes()
-{
- static const QStringList retList {"*.ttf", "*.otf"};
- return retList;
-}
-
-const QStringList &AssetsLibraryModel::supportedAudioSuffixes()
-{
- static const QStringList retList {"*.wav", "*.mp3"};
- return retList;
-}
-
-const QStringList &AssetsLibraryModel::supportedVideoSuffixes()
-{
- static const QStringList retList {"*.mp4"};
- return retList;
-}
-
-const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes()
+bool AssetsLibraryModel::isEmpty() const
{
- // These are file types only supported by 3D textures
- static QStringList retList {"*.hdr", "*.ktx"};
- return retList;
-}
+ return m_isEmpty;
+};
-AssetsLibraryModel::AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent)
- : QAbstractListModel(parent)
- , m_fileSystemWatcher(fileSystemWatcher)
+void AssetsLibraryModel::setIsEmpty(bool empty)
{
- // add role names
- int role = 0;
- const QMetaObject meta = AssetsLibraryDir::staticMetaObject;
- for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i)
- m_roleNames.insert(role++, meta.property(i).name());
-}
+ if (m_isEmpty != empty) {
+ m_isEmpty = empty;
+ emit isEmptyChanged();
+ }
+};
QVariant AssetsLibraryModel::data(const QModelIndex &index, int role) const
{
@@ -377,12 +348,54 @@ void AssetsLibraryModel::setRootPath(const QString &path)
qCInfo(assetsLibraryBenchmark) << "model reset:" << time.elapsed();
}
-void AssetsLibraryModel::setSearchText(const QString &searchText)
+const QStringList &AssetsLibraryModel::supportedImageSuffixes()
{
- if (m_searchText != searchText) {
- m_searchText = searchText;
- refresh();
+ static QStringList retList;
+ if (retList.isEmpty()) {
+ const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
+ for (const QByteArray &suffix : suffixes)
+ retList.append("*." + QString::fromUtf8(suffix));
}
+ return retList;
+}
+
+const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes()
+{
+ static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"};
+ return retList;
+}
+
+const QStringList &AssetsLibraryModel::supportedShaderSuffixes()
+{
+ static const QStringList retList {"*.frag", "*.vert",
+ "*.glsl", "*.glslv", "*.glslf",
+ "*.vsh", "*.fsh"};
+ return retList;
+}
+
+const QStringList &AssetsLibraryModel::supportedFontSuffixes()
+{
+ static const QStringList retList {"*.ttf", "*.otf"};
+ return retList;
+}
+
+const QStringList &AssetsLibraryModel::supportedAudioSuffixes()
+{
+ static const QStringList retList {"*.wav", "*.mp3"};
+ return retList;
+}
+
+const QStringList &AssetsLibraryModel::supportedVideoSuffixes()
+{
+ static const QStringList retList {"*.mp4"};
+ return retList;
+}
+
+const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes()
+{
+ // These are file types only supported by 3D textures
+ static QStringList retList {"*.hdr", "*.ktx"};
+ return retList;
}
const QSet<QString> &AssetsLibraryModel::supportedSuffixes()
@@ -403,19 +416,6 @@ const QSet<QString> &AssetsLibraryModel::supportedSuffixes()
return allSuffixes;
}
-bool AssetsLibraryModel::isEmpty() const
-{
- return m_isEmpty;
-};
-
-void AssetsLibraryModel::setIsEmpty(bool empty)
-{
- if (m_isEmpty != empty) {
- m_isEmpty = empty;
- emit isEmptyChanged();
- }
-};
-
const QSet<QString> &AssetsLibraryModel::previewableSuffixes() const
{
static QSet<QString> previewableSuffixes;