forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -46,6 +46,10 @@ Item {
|
|||||||
// Array of supported externally dropped files that trigger custom import process
|
// Array of supported externally dropped files that trigger custom import process
|
||||||
property var dropComplexExtFiles: []
|
property var dropComplexExtFiles: []
|
||||||
|
|
||||||
|
AssetsContextMenu {
|
||||||
|
id: contextMenu
|
||||||
|
}
|
||||||
|
|
||||||
function clearSearchFilter()
|
function clearSearchFilter()
|
||||||
{
|
{
|
||||||
searchBox.clear();
|
searchBox.clear();
|
||||||
@@ -125,307 +129,11 @@ Item {
|
|||||||
root.selectedAssetsChanged()
|
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 {
|
RegExpValidator {
|
||||||
id: folderNameValidator
|
id: folderNameValidator
|
||||||
regExp: /^(\w[^*/><?\\|:]*)$/
|
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 {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: 5
|
anchors.topMargin: 5
|
||||||
@@ -529,220 +237,10 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView { // TODO: experiment using ListView instead of ScrollView + Column
|
AssetsView {
|
||||||
id: assetsView
|
id: assetsView
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - y
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
246
share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
Normal file
246
share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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()
|
||||||
|
}
|
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
@@ -57,6 +57,25 @@ static Q_LOGGING_CATEGORY(assetsLibraryBenchmark, "qtc.assetsLibrary.setRoot", Q
|
|||||||
|
|
||||||
namespace QmlDesigner {
|
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)
|
void AssetsLibraryModel::saveExpandedState(bool expanded, const QString &assetPath)
|
||||||
{
|
{
|
||||||
m_expandedStateHash.insert(assetPath, expanded);
|
m_expandedStateHash.insert(assetPath, expanded);
|
||||||
@@ -208,66 +227,18 @@ QObject *AssetsLibraryModel::rootDir() const
|
|||||||
return m_assetsDir;
|
return m_assetsDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QStringList &AssetsLibraryModel::supportedImageSuffixes()
|
bool AssetsLibraryModel::isEmpty() const
|
||||||
{
|
{
|
||||||
static QStringList retList;
|
return m_isEmpty;
|
||||||
if (retList.isEmpty()) {
|
};
|
||||||
const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
|
|
||||||
for (const QByteArray &suffix : suffixes)
|
void AssetsLibraryModel::setIsEmpty(bool empty)
|
||||||
retList.append("*." + QString::fromUtf8(suffix));
|
{
|
||||||
|
if (m_isEmpty != empty) {
|
||||||
|
m_isEmpty = empty;
|
||||||
|
emit isEmptyChanged();
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant AssetsLibraryModel::data(const QModelIndex &index, int role) const
|
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();
|
qCInfo(assetsLibraryBenchmark) << "model reset:" << time.elapsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLibraryModel::setSearchText(const QString &searchText)
|
const QStringList &AssetsLibraryModel::supportedImageSuffixes()
|
||||||
{
|
{
|
||||||
if (m_searchText != searchText) {
|
static QStringList retList;
|
||||||
m_searchText = searchText;
|
if (retList.isEmpty()) {
|
||||||
refresh();
|
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()
|
const QSet<QString> &AssetsLibraryModel::supportedSuffixes()
|
||||||
@@ -403,19 +416,6 @@ const QSet<QString> &AssetsLibraryModel::supportedSuffixes()
|
|||||||
return allSuffixes;
|
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
|
const QSet<QString> &AssetsLibraryModel::previewableSuffixes() const
|
||||||
{
|
{
|
||||||
static QSet<QString> previewableSuffixes;
|
static QSet<QString> previewableSuffixes;
|
||||||
|
Reference in New Issue
Block a user