diff options
author | Mahmoud Badri <mahmoud.badri@qt.io> | 2022-02-03 19:49:27 +0200 |
---|---|---|
committer | Mahmoud Badri <mahmoud.badri@qt.io> | 2022-02-08 17:09:40 +0000 |
commit | 9c5e6e2d8c6a6415c294004a14eb3650221d9329 (patch) | |
tree | 4dc0e335456f90f5b1ff11822ba61cd2d5ab0c8a | |
parent | 93f46595336ad406566472f17e28740337ff16a4 (diff) |
QmlDesigner: Split components and assets libraries
Fixes: QDS-6151
Change-Id: Id6f79a2d1fcff5fb55d3ba3f1b1394f1f55ca36b
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
68 files changed, 1581 insertions, 1105 deletions
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddImport.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml index 71399afa01..492ef767a6 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddImport.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml @@ -32,28 +32,40 @@ import StudioTheme 1.0 as StudioTheme Column { id: root - Text { - id: header - text: qsTr("Select a Module to Add") - color: StudioTheme.Values.themeTextColor - font.pixelSize: 16 - width: parent.width - height: 50 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + spacing: 5 + + signal back() + + Row { + spacing: 5 + anchors.horizontalCenter: parent.horizontalCenter + + Button { + anchors.verticalCenter: parent.verticalCenter + text: "<" + width: 25 + height: 25 + onClicked: back() + } + + Text { + text: qsTr("Select a Module to Add") + color: StudioTheme.Values.themeTextColor + font.pixelSize: 16 + } } ScrollView { // ListView not used because of QTBUG-52941 id: listView width: parent.width - height: parent.height - header.height + height: parent.height - y clip: true Column { spacing: 2 Repeater { - model: addImportModel + model: addModuleModel delegate: Rectangle { id: itemBackground diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml index 115309c127..2efe499f0f 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml @@ -40,6 +40,11 @@ Item { property var contextDir: undefined property bool isDirContextMenu: false + function clearSearchFilter() + { + searchBox.text = ""; + } + DropArea { id: dropArea @@ -90,6 +95,70 @@ Item { selectedAssetsChanged() } + StudioControls.Menu { + id: contextMenu + + StudioControls.MenuItem { + text: qsTr("Expand All") + enabled: allExpandedState !== 1 + visible: isDirContextMenu + height: visible ? implicitHeight : 0 + onTriggered: assetsModel.toggleExpandAll(true) + } + + StudioControls.MenuItem { + text: qsTr("Collapse All") + enabled: allExpandedState !== 2 + visible: isDirContextMenu + height: visible ? implicitHeight : 0 + onTriggered: assetsModel.toggleExpandAll(false) + } + + StudioControls.MenuSeparator { + visible: isDirContextMenu + height: visible ? StudioTheme.Values.border : 0 + } + + StudioControls.MenuItem { + text: qsTr("Delete File") + visible: contextFilePath + height: visible ? implicitHeight : 0 + onTriggered: assetsModel.deleteFile(contextFilePath) + } + + StudioControls.MenuSeparator { + visible: contextFilePath + height: visible ? StudioTheme.Values.border : 0 + } + + StudioControls.MenuItem { + text: qsTr("Rename Folder") + visible: isDirContextMenu + height: visible ? implicitHeight : 0 + onTriggered: renameFolderDialog.open() + } + + StudioControls.MenuItem { + text: qsTr("New Folder") + onTriggered: newFolderDialog.open() + } + + StudioControls.MenuItem { + text: qsTr("Delete Folder") + visible: isDirContextMenu + height: visible ? implicitHeight : 0 + onTriggered: { + var dirEmpty = !(contextDir.dirsModel && contextDir.dirsModel.rowCount() > 0) + && !(contextDir.filesModel && contextDir.filesModel.rowCount() > 0); + + if (dirEmpty) + assetsModel.deleteFolder(contextDir.dirPath) + else + confirmDeleteFolderDialog.open() + } + } + } + RegExpValidator { id: folderNameValidator regExp: /^(\w[^*/><?\\|:]*)$/ @@ -317,245 +386,213 @@ Item { onOpened: folderNotEmpty.forceActiveFocus() } - ScrollView { // TODO: experiment using ListView instead of ScrollView + Column - id: assetsView + Column { anchors.fill: parent - interactive: assetsView.verticalScrollBarVisible - - Item { - StudioControls.Menu { - id: contextMenu - - StudioControls.MenuItem { - text: qsTr("Expand All") - enabled: allExpandedState !== 1 - visible: isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: assetsModel.toggleExpandAll(true) - } - - StudioControls.MenuItem { - text: qsTr("Collapse All") - enabled: allExpandedState !== 2 - visible: isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: assetsModel.toggleExpandAll(false) - } + anchors.topMargin: 5 + spacing: 5 - StudioControls.MenuSeparator { - visible: isDirContextMenu - height: visible ? StudioTheme.Values.border : 0 - } - - StudioControls.MenuItem { - text: qsTr("Delete File") - visible: contextFilePath - height: visible ? implicitHeight : 0 - onTriggered: assetsModel.deleteFile(contextFilePath) - } + Row { + width: parent.width - StudioControls.MenuSeparator { - visible: contextFilePath - height: visible ? StudioTheme.Values.border : 0 - } + SearchBox { + id: searchBox - StudioControls.MenuItem { - text: qsTr("Rename Folder") - visible: isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: renameFolderDialog.open() - } + width: parent.width - addAssetButton.width - 5 + } - StudioControls.MenuItem { - text: qsTr("New Folder") - onTriggered: newFolderDialog.open() - } + PlusButton { + id: addAssetButton + anchors.verticalCenter: parent.verticalCenter + tooltip: qsTr("Add a new asset to the project.") - StudioControls.MenuItem { - text: qsTr("Delete Folder") - visible: isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: { - var dirEmpty = !(contextDir.dirsModel && contextDir.dirsModel.rowCount() > 0) - && !(contextDir.filesModel && contextDir.filesModel.rowCount() > 0); - - if (dirEmpty) - assetsModel.deleteFolder(contextDir.dirPath) - else - confirmDeleteFolderDialog.open() - } - } + onClicked: rootView.handleAddAsset() } } - Column { - Repeater { - model: assetsModel // context property - delegate: dirSection - } - - Component { - id: dirSection - - 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: !assetsModel.isEmpty && dirVisible - expandOnClick: false - useDefaulContextMenu: false - - onToggleExpand: { - dirExpanded = !dirExpanded - } - - onShowContextMenu: { - contextFilePath = "" - contextDir = model - isDirContextMenu = true - allExpandedState = assetsModel.getAllExpandedState() - contextMenu.popup() - } + Text { + text: qsTr("No match found.") + leftPadding: 10 + color: StudioTheme.Values.themeTextColor + font.pixelSize: 12 + visible: assetsModel.isEmpty && !searchBox.isEmpty() + } - Column { - spacing: 5 - leftPadding: 5 + ScrollView { // TODO: experiment using ListView instead of ScrollView + Column + id: assetsView + width: parent.width + height: parent.height - y + clip: true + interactive: assetsView.verticalScrollBarVisible + + Column { + Repeater { + model: assetsModel // context property + delegate: dirSection + } - Repeater { - model: dirsModel - delegate: dirSection + Component { + id: dirSection + + 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: !assetsModel.isEmpty && dirVisible + expandOnClick: false + useDefaulContextMenu: false + + onToggleExpand: { + dirExpanded = !dirExpanded } - Repeater { - model: filesModel - delegate: fileSection + onShowContextMenu: { + contextFilePath = "" + contextDir = model + isDirContextMenu = true + allExpandedState = assetsModel.getAllExpandedState() + contextMenu.popup() } - 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: { - contextFilePath = "" - contextDir = model - isDirContextMenu = true - 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: { + contextFilePath = "" + contextDir = model + isDirContextMenu = true + contextMenu.popup() + } } } } } } - } - Component { - id: fileSection - - Rectangle { - width: assetsView.width - - (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - height: img.height - color: selectedAssets[filePath] ? StudioTheme.Values.themeInteraction - : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground - : "transparent") - - Row { - spacing: 5 - - Image { - id: img - asynchronous: true - width: 48 - height: 48 - source: "image://qmldesigner_assets/" + filePath - } + Component { + id: fileSection + + Rectangle { + width: assetsView.width - + (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) + height: img.height + color: selectedAssets[filePath] ? StudioTheme.Values.themeInteraction + : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground + : "transparent") + + Row { + spacing: 5 + + Image { + id: img + asynchronous: true + width: 48 + height: 48 + source: "image://qmldesigner_assets/" + filePath + } - Text { - text: fileName - color: StudioTheme.Values.themeTextColor - font.pixelSize: 14 - anchors.verticalCenter: parent.verticalCenter + 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 - - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - - onExited: tooltipBackend.hideTooltip() - onCanceled: tooltipBackend.hideTooltip() - onPositionChanged: tooltipBackend.reposition() - onPressed: (mouse)=> { - forceActiveFocus() - if (mouse.button === Qt.LeftButton) { - var ctrlDown = mouse.modifiers & Qt.ControlModifier - if (!selectedAssets[filePath] && !ctrlDown) - selectedAssets = {} - currFileSelected = ctrlDown ? !selectedAssets[filePath] : true - selectedAssets[filePath] = currFileSelected - selectedAssetsChanged() - - var selectedAssetsArr = [] - for (var assetPath in selectedAssets) { - if (selectedAssets[assetPath]) - selectedAssetsArr.push(assetPath) + readonly property string suffix: fileName.substr(-4) + readonly property bool isFont: suffix === ".ttf" || suffix === ".otf" + property bool currFileSelected: false + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onExited: tooltipBackend.hideTooltip() + onCanceled: tooltipBackend.hideTooltip() + onPositionChanged: tooltipBackend.reposition() + onPressed: (mouse)=> { + forceActiveFocus() + if (mouse.button === Qt.LeftButton) { + var ctrlDown = mouse.modifiers & Qt.ControlModifier + if (!selectedAssets[filePath] && !ctrlDown) + selectedAssets = {} + currFileSelected = ctrlDown ? !selectedAssets[filePath] : true + selectedAssets[filePath] = currFileSelected + selectedAssetsChanged() + + var selectedAssetsArr = [] + for (var assetPath in selectedAssets) { + if (selectedAssets[assetPath]) + selectedAssetsArr.push(assetPath) + } + + if (currFileSelected) + rootView.startDragAsset(selectedAssetsArr, mapToGlobal(mouse.x, mouse.y)) + } else { + contextFilePath = filePath + contextDir = model.fileDir + + tooltipBackend.hideTooltip() + isDirContextMenu = false + contextMenu.popup() } - - if (currFileSelected) - rootView.startDragAsset(selectedAssetsArr, mapToGlobal(mouse.x, mouse.y)) - } else { - contextFilePath = filePath - contextDir = model.fileDir - - tooltipBackend.hideTooltip() - isDirContextMenu = false - contextMenu.popup() } - } - onReleased: (mouse)=> { - if (mouse.button === Qt.LeftButton) { - if (!(mouse.modifiers & Qt.ControlModifier)) - selectedAssets = {} - selectedAssets[filePath] = currFileSelected - selectedAssetsChanged() + onReleased: (mouse)=> { + if (mouse.button === Qt.LeftButton) { + if (!(mouse.modifiers & Qt.ControlModifier)) + selectedAssets = {} + selectedAssets[filePath] = currFileSelected + selectedAssetsChanged() + } } - } - ToolTip { - visible: !isFont && mouseArea.containsMouse && !contextMenu.visible - text: filePath - delay: 1000 - } + ToolTip { + visible: !isFont && mouseArea.containsMouse && !contextMenu.visible + text: filePath + delay: 1000 + } - Timer { - interval: 1000 - running: mouseArea.containsMouse - onTriggered: { - if (suffix === ".ttf" || suffix === ".otf") { - tooltipBackend.name = fileName - tooltipBackend.path = filePath - tooltipBackend.showTooltip() + Timer { + interval: 1000 + running: mouseArea.containsMouse + onTriggered: { + if (suffix === ".ttf" || suffix === ".otf") { + tooltipBackend.name = fileName + tooltipBackend.path = filePath + tooltipBackend.showTooltip() + } } } } @@ -568,7 +605,7 @@ Item { // Placeholder when the assets panel is empty Column { id: colNoAssets - visible: assetsModel.isEmpty && !rootView.searchActive + visible: assetsModel.isEmpty && searchBox.isEmpty() spacing: 20 x: 20 @@ -612,13 +649,4 @@ Item { wrapMode: Text.WordWrap } } - - Text { - text: qsTr("No match found.") - x: 20 - y: 10 - color: StudioTheme.Values.themeTextColor - font.pixelSize: 12 - visible: assetsModel.isEmpty && rootView.searchActive - } } diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index 6f7fbc454f..17d187c241 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -81,6 +81,7 @@ Item { property var currentCategory: null property var currentImport: null property bool isHorizontalView: false + property bool isAddModuleView: false // Called also from C++ to close context menu on focus out function closeContextMenu() @@ -89,6 +90,12 @@ Item { itemContextMenu.close() } + // Called also from C++ + function switchToComponentsView() + { + isAddModuleView = false + } + onWidthChanged: { itemsView.isHorizontalView = itemsView.width > widthLimit } @@ -188,9 +195,38 @@ Item { } } - Loader { - anchors.fill: parent - sourceComponent: itemsView.isHorizontalView ? horizontalView : verticalView + Column { + id: col + width: parent.width + height: parent.height + y: 5 + spacing: 5 + + Row { + width: parent.width + + SearchBox { + id: searchBox + + width: parent.width - addAssetButton.width - 5 + } + + PlusButton { + id: addAssetButton + tooltip: qsTr("Add a module.") + + onClicked: isAddModuleView = true + } + } + + Loader { + id: loader + + width: col.width + height: col.height - y - 5 + sourceComponent: isAddModuleView ? addModuleView + : itemsView.isHorizontalView ? horizontalView : verticalView + } } Component { @@ -198,8 +234,9 @@ Item { ScrollView { id: verticalScrollView - width: itemsView.width - height: itemsView.height + anchors.fill: parent + clip: true + onContentHeightChanged: { var maxPosition = Math.max(contentHeight - verticalScrollView.height, 0) if (contentY > maxPosition) @@ -312,7 +349,9 @@ Item { ScrollView { id: horizontalScrollView width: 270 - height: itemsView.height + height: parent.height + clip: true + onContentHeightChanged: { var maxPosition = Math.max(contentHeight - horizontalScrollView.height, 0) if (contentY > maxPosition) @@ -452,4 +491,12 @@ Item { } } } + + Component { + id: addModuleView + + AddModuleView { + onBack: isAddModuleView = false + } + } } diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/LibraryHeader.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/LibraryHeader.qml deleted file mode 100644 index c68891f331..0000000000 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/LibraryHeader.qml +++ /dev/null @@ -1,266 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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 2.15 -import QtQuick.Controls 2.15 -import QtQuickDesignerTheme 1.0 -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme - -Item { - id: root - - function setTab(index) - { - tabBar.setCurrentIndex(index); - } - - function clearSearchFilter() - { - searchFilterText.text = ""; - } - - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: 9 - - TabBar { - id: tabBar - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 5 - anchors.rightMargin: 5 - spacing: 40 - - background: Rectangle { - color: StudioTheme.Values.themePanelBackground - } - - Repeater { - model: [{title: qsTr("Components"), addToolTip: qsTr("Add Module")}, - {title: qsTr("Assets"), addToolTip: qsTr("Add new assets to project.")}] - - TabButton { - topPadding: 4 - bottomPadding: 4 - contentItem: Item { - implicitHeight: plusButton.height - - Text { // TabButton text - text: modelData.title - font.pixelSize: 13 - font.bold: false - color: tabBar.currentIndex === index ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeTextColor - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: plusButton.left - anchors.bottomMargin: 2 - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignBottom - elide: Text.ElideRight - } - - Rectangle { // + button - id: plusButton - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: 1 - width: 24 - height: 24 - color: mouseArea.containsMouse && enabled - ? StudioTheme.Values.themeControlBackgroundHover - : StudioTheme.Values.themeControlBackground - - Behavior on color { - ColorAnimation { - duration: StudioTheme.Values.hoverDuration - easing.type: StudioTheme.Values.hoverEasing - } - } - - enabled: index !== 0 || !rootView.subCompEditMode - - Label { // + sign - text: StudioTheme.Constants.plus - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.myIconFontSize - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - anchors.centerIn: parent - color: !plusButton.enabled - ? StudioTheme.Values.themeIconColorDisabled - : tabBar.currentIndex === index - ? StudioTheme.Values.themeIconColorSelected - : StudioTheme.Values.themeIconColor - } - - HelperWidgets.ToolTipArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: index === 0 ? rootView.handleAddModule() - : rootView.handleAddAsset() - tooltip: modelData.addToolTip - } - } - } - - background: Item { // TabButton background - Rectangle { // bottom strip - anchors.bottom: parent.bottom - width: parent.width - height: 2 - color: tabBar.currentIndex === index ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeTextColor - } - } - - onClicked: rootView.handleTabChanged(index); - } - } - } - - TextField { // filter - id: searchFilterText - placeholderText: qsTr("Search") - placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor - color: StudioTheme.Values.themeTextColor - selectionColor: StudioTheme.Values.themeTextSelectionColor - selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor - background: Rectangle { - id: textFieldBackground - color: StudioTheme.Values.themeControlBackground - border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border - - Behavior on color { - ColorAnimation { - duration: StudioTheme.Values.hoverDuration - easing.type: StudioTheme.Values.hoverEasing - } - } - } - - height: StudioTheme.Values.defaultControlHeight - - leftPadding: 32 - rightPadding: 30 - topPadding: 6 - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 5 - anchors.rightMargin: 5 - selectByMouse: true - hoverEnabled: true - - onTextChanged: rootView.handleSearchfilterChanged(text) - - Label { - text: StudioTheme.Constants.search - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: 16 - anchors.left: parent.left - anchors.leftMargin: 7 - anchors.verticalCenter: parent.verticalCenter - color: StudioTheme.Values.themeIconColor - } - - Rectangle { // x button - width: 16 - height: 15 - anchors.right: parent.right - anchors.rightMargin: 5 - anchors.verticalCenter: parent.verticalCenter - visible: searchFilterText.text !== "" - color: xMouseArea.containsMouse ? StudioTheme.Values.themePanelBackground - : "transparent" - - Label { - text: StudioTheme.Constants.closeCross - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.myIconFontSize - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - anchors.centerIn: parent - color: StudioTheme.Values.themeIconColor - } - - MouseArea { - id: xMouseArea - hoverEnabled: true - anchors.fill: parent - onClicked: searchFilterText.text = "" - } - } - - states: [ - State { - name: "default" - when: !searchFilterText.hovered && !searchFilterText.activeFocus - PropertyChanges { - target: textFieldBackground - color: StudioTheme.Values.themeControlBackground - border.color: StudioTheme.Values.themeControlOutline - } - PropertyChanges { - target: searchFilterText - placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor - } - }, - State { - name: "hover" - when: searchFilterText.hovered && !searchFilterText.activeFocus - PropertyChanges { - target: textFieldBackground - color: StudioTheme.Values.themeControlBackgroundHover - border.color: StudioTheme.Values.themeControlOutline - } - - PropertyChanges { - target: searchFilterText - placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor - } - }, - State { - name: "edit" - when: searchFilterText.activeFocus - PropertyChanges { - target: textFieldBackground - color: StudioTheme.Values.themeControlBackgroundInteraction - border.color: StudioTheme.Values.themeControlOutlineInteraction - } - PropertyChanges { - target: searchFilterText - placeholderTextColor: StudioTheme.Values.themePlaceholderTextColorInteraction - } - } - ] - } - } -} diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/PlusButton.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/PlusButton.qml new file mode 100644 index 0000000000..31f69be996 --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/PlusButton.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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 2.15 +import QtQuick.Controls 2.15 +import QtQuickDesignerTheme 1.0 +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme + +Rectangle { + id: root + + property string tooltip: "" + + signal clicked() + + implicitWidth: 29 + implicitHeight: 29 + color: mouseArea.containsMouse && enabled + ? StudioTheme.Values.themeControlBackgroundHover + : StudioTheme.Values.themeControlBackground + + Behavior on color { + ColorAnimation { + duration: StudioTheme.Values.hoverDuration + easing.type: StudioTheme.Values.hoverEasing + } + } + + Label { // + sign + text: StudioTheme.Constants.plus + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.myIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.centerIn: parent + color: root.enabled ? StudioTheme.Values.themeIconColor + : StudioTheme.Values.themeIconColorDisabled + scale: mouseArea.containsMouse ? 1.4 : 1 + + Behavior on scale { + NumberAnimation { + duration: 300 + easing.type: Easing.OutExpo + } + } + } + + HelperWidgets.ToolTipArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: root.clicked() + tooltip: root.tooltip + } +} diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/SearchBox.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/SearchBox.qml new file mode 100644 index 0000000000..81ed5c11e4 --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/SearchBox.qml @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** 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 2.15 +import QtQuick.Controls 2.15 +import QtQuickDesignerTheme 1.0 +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme + +Item { + id: root + + property alias text: searchFilterText.text + + function clearSearchFilter() + { + searchFilterText.text = ""; + } + + function isEmpty() + { + return searchFilterText.text === ""; + } + + implicitWidth: searchFilterText.width + implicitHeight: searchFilterText.height + + TextField { + id: searchFilterText + + placeholderText: qsTr("Search") + placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor + color: StudioTheme.Values.themeTextColor + selectionColor: StudioTheme.Values.themeTextSelectionColor + selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor + background: Rectangle { + id: textFieldBackground + color: StudioTheme.Values.themeControlBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + Behavior on color { + ColorAnimation { + duration: StudioTheme.Values.hoverDuration + easing.type: StudioTheme.Values.hoverEasing + } + } + } + + height: StudioTheme.Values.defaultControlHeight + + leftPadding: 32 + rightPadding: 30 + topPadding: 6 + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 5 + anchors.rightMargin: 5 + selectByMouse: true + hoverEnabled: true + + onTextChanged: rootView.handleSearchfilterChanged(text) + + Label { + text: StudioTheme.Constants.search + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: 16 + anchors.left: parent.left + anchors.leftMargin: 7 + anchors.verticalCenter: parent.verticalCenter + color: StudioTheme.Values.themeIconColor + } + + Rectangle { // x button + width: 16 + height: 15 + anchors.right: parent.right + anchors.rightMargin: 5 + anchors.verticalCenter: parent.verticalCenter + visible: searchFilterText.text !== "" + color: xMouseArea.containsMouse ? StudioTheme.Values.themePanelBackground + : "transparent" + + Label { + text: StudioTheme.Constants.closeCross + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.myIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.centerIn: parent + color: StudioTheme.Values.themeIconColor + } + + MouseArea { + id: xMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: searchFilterText.text = "" + } + } + + states: [ + State { + name: "default" + when: !searchFilterText.hovered && !searchFilterText.activeFocus + PropertyChanges { + target: textFieldBackground + color: StudioTheme.Values.themeControlBackground + border.color: StudioTheme.Values.themeControlOutline + } + PropertyChanges { + target: searchFilterText + placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor + } + }, + State { + name: "hover" + when: searchFilterText.hovered && !searchFilterText.activeFocus + PropertyChanges { + target: textFieldBackground + color: StudioTheme.Values.themeControlBackgroundHover + border.color: StudioTheme.Values.themeControlOutline + } + + PropertyChanges { + target: searchFilterText + placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor + } + }, + State { + name: "edit" + when: searchFilterText.activeFocus + PropertyChanges { + target: textFieldBackground + color: StudioTheme.Values.themeControlBackgroundInteraction + border.color: StudioTheme.Values.themeControlOutlineInteraction + } + PropertyChanges { + target: searchFilterText + placeholderTextColor: StudioTheme.Values.themePlaceholderTextColorInteraction + } + } + ] + } +} diff --git a/share/qtcreator/qmldesigner/workspacePresets/3D_Preset.wrk b/share/qtcreator/qmldesigner/workspacePresets/3D_Preset.wrk index 62c823b147..fba5f4a822 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/3D_Preset.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/3D_Preset.wrk @@ -3,8 +3,9 @@ <container floating="false"> <splitter orientation="Horizontal" count="3"> <splitter orientation="Vertical" count="2"> - <area tabs="3" current="Library"> - <widget name="Library" closed="false"/> + <area tabs="4" current="Components"> + <widget name="Components" closed="false"/> + <widget name="Assets" closed="false"/> <widget name="Navigator" closed="false"/> <widget name="DebugView" closed="false"/> </area> diff --git a/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk b/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk index 0b1a7c2d52..2bdbc74f22 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk @@ -3,8 +3,9 @@ <container floating="false"> <splitter orientation="Horizontal" count="3"> <splitter orientation="Vertical" count="3"> - <area tabs="2" current="Library"> - <widget name="Library" closed="false"/> + <area tabs="3" current="Components"> + <widget name="Components" closed="false"/> + <widget name="Assets" closed="false"/> <widget name="DebugView" closed="false"/> </area> <area tabs="3" current="Projects"> diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 68532c1a53..b501a98bdd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -251,11 +251,19 @@ extend_qtc_plugin(QmlDesigner itemlibraryimport.cpp itemlibraryimport.h itemlibrarycategoriesmodel.cpp itemlibrarycategoriesmodel.h itemlibraryaddimportmodel.cpp itemlibraryaddimportmodel.h - itemlibraryassetsmodel.cpp itemlibraryassetsmodel.h - itemlibraryassetsiconprovider.cpp itemlibraryassetsiconprovider.h - itemlibraryassetsdir.cpp itemlibraryassetsdir.h - itemlibraryassetsdirsmodel.cpp itemlibraryassetsdirsmodel.h - itemlibraryassetsfilesmodel.cpp itemlibraryassetsfilesmodel.h +) + +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/assetslibrary + SOURCES + assetslibrary.qrc + assetslibraryview.cpp assetslibraryview.h + assetslibrarywidget.cpp assetslibrarywidget.h + assetslibrarymodel.cpp assetslibrarymodel.h + assetslibraryiconprovider.cpp assetslibraryiconprovider.h + assetslibrarydir.cpp assetslibrarydir.h + assetslibrarydirsmodel.cpp assetslibrarydirsmodel.h + assetslibraryfilesmodel.cpp assetslibraryfilesmodel.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc b/src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc new file mode 100644 index 0000000000..f8cea61298 --- /dev/null +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc @@ -0,0 +1,16 @@ +<RCC> + <qresource prefix="/AssetsLibrary"> + <file>images/asset_default.png</file> + <file>images/asset_default@2x.png</file> + <file>images/asset_shader.png</file> + <file>images/asset_shader@2x.png</file> + <file>images/asset_shader_128.png</file> + <file>images/asset_sound.png</file> + <file>images/asset_sound@2x.png</file> + <file>images/asset_sound_128.png</file> + <file>images/asset_video.png</file> + <file>images/asset_video@2x.png</file> + <file>images/browse.png</file> + <file>images/browse@2x.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydir.cpp index e5bbaf792d..2c965d702f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydir.cpp @@ -23,13 +23,13 @@ ** ****************************************************************************/ -#include "itemlibraryassetsdir.h" -#include "itemlibraryassetsdirsmodel.h" -#include "itemlibraryassetsfilesmodel.h" +#include "assetslibrarydir.h" +#include "assetslibrarydirsmodel.h" +#include "assetslibraryfilesmodel.h" namespace QmlDesigner { -ItemLibraryAssetsDir::ItemLibraryAssetsDir(const QString &path, int depth, bool expanded, QObject *parent) +AssetsLibraryDir::AssetsLibraryDir(const QString &path, int depth, bool expanded, QObject *parent) : QObject(parent) , m_dirPath(path) , m_dirDepth(depth) @@ -38,13 +38,13 @@ ItemLibraryAssetsDir::ItemLibraryAssetsDir(const QString &path, int depth, bool } -QString ItemLibraryAssetsDir::dirName() const { return m_dirPath.split('/').last(); } -QString ItemLibraryAssetsDir::dirPath() const { return m_dirPath; } -int ItemLibraryAssetsDir::dirDepth() const { return m_dirDepth; } -bool ItemLibraryAssetsDir::dirExpanded() const { return m_dirExpanded; } -bool ItemLibraryAssetsDir::dirVisible() const { return m_dirVisible; } +QString AssetsLibraryDir::dirName() const { return m_dirPath.split('/').last(); } +QString AssetsLibraryDir::dirPath() const { return m_dirPath; } +int AssetsLibraryDir::dirDepth() const { return m_dirDepth; } +bool AssetsLibraryDir::dirExpanded() const { return m_dirExpanded; } +bool AssetsLibraryDir::dirVisible() const { return m_dirVisible; } -void ItemLibraryAssetsDir::setDirExpanded(bool expand) +void AssetsLibraryDir::setDirExpanded(bool expand) { if (m_dirExpanded != expand) { m_dirExpanded = expand; @@ -52,7 +52,7 @@ void ItemLibraryAssetsDir::setDirExpanded(bool expand) } } -void ItemLibraryAssetsDir::setDirVisible(bool visible) +void AssetsLibraryDir::setDirVisible(bool visible) { if (m_dirVisible != visible) { m_dirVisible = visible; @@ -60,17 +60,17 @@ void ItemLibraryAssetsDir::setDirVisible(bool visible) } } -QObject *ItemLibraryAssetsDir::filesModel() const +QObject *AssetsLibraryDir::filesModel() const { return m_filesModel; } -QObject *ItemLibraryAssetsDir::dirsModel() const +QObject *AssetsLibraryDir::dirsModel() const { return m_dirsModel; } -QList<ItemLibraryAssetsDir *> ItemLibraryAssetsDir::childAssetsDirs() const +QList<AssetsLibraryDir *> AssetsLibraryDir::childAssetsDirs() const { if (m_dirsModel) return m_dirsModel->assetsDirs(); @@ -78,18 +78,18 @@ QList<ItemLibraryAssetsDir *> ItemLibraryAssetsDir::childAssetsDirs() const return {}; } -void ItemLibraryAssetsDir::addDir(ItemLibraryAssetsDir *assetsDir) +void AssetsLibraryDir::addDir(AssetsLibraryDir *assetsDir) { if (!m_dirsModel) - m_dirsModel = new ItemLibraryAssetsDirsModel(this); + m_dirsModel = new AssetsLibraryDirsModel(this); m_dirsModel->addDir(assetsDir); } -void ItemLibraryAssetsDir::addFile(const QString &filePath) +void AssetsLibraryDir::addFile(const QString &filePath) { if (!m_filesModel) - m_filesModel = new ItemLibraryAssetsFilesModel(this); + m_filesModel = new AssetsLibraryFilesModel(this); m_filesModel->addFile(filePath); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydir.h index 66cc5509d7..07e1338938 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydir.h @@ -29,10 +29,10 @@ namespace QmlDesigner { -class ItemLibraryAssetsDirsModel; -class ItemLibraryAssetsFilesModel; +class AssetsLibraryDirsModel; +class AssetsLibraryFilesModel; -class ItemLibraryAssetsDir : public QObject +class AssetsLibraryDir : public QObject { Q_OBJECT @@ -45,7 +45,7 @@ class ItemLibraryAssetsDir : public QObject Q_PROPERTY(QObject *dirsModel READ dirsModel NOTIFY dirsModelChanged) public: - ItemLibraryAssetsDir(const QString &path, int depth, bool expanded = true, QObject *parent = nullptr); + AssetsLibraryDir(const QString &path, int depth, bool expanded = true, QObject *parent = nullptr); QString dirName() const; QString dirPath() const; @@ -59,9 +59,9 @@ public: QObject *filesModel() const; QObject *dirsModel() const; - QList<ItemLibraryAssetsDir *> childAssetsDirs() const; + QList<AssetsLibraryDir *> childAssetsDirs() const; - void addDir(ItemLibraryAssetsDir *assetsDir); + void addDir(AssetsLibraryDir *assetsDir); void addFile(const QString &filePath); signals: @@ -78,8 +78,8 @@ private: int m_dirDepth = 0; bool m_dirExpanded = true; bool m_dirVisible = true; - ItemLibraryAssetsDirsModel *m_dirsModel = nullptr; - ItemLibraryAssetsFilesModel *m_filesModel = nullptr; + AssetsLibraryDirsModel *m_dirsModel = nullptr; + AssetsLibraryFilesModel *m_filesModel = nullptr; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydirsmodel.cpp index 6af4049d87..563e0f111b 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydirsmodel.cpp @@ -23,24 +23,24 @@ ** ****************************************************************************/ -#include "itemlibraryassetsdirsmodel.h" -#include "itemlibraryassetsmodel.h" +#include "assetslibrarydirsmodel.h" +#include "assetslibrarymodel.h" #include <QDebug> #include <QMetaProperty> namespace QmlDesigner { -ItemLibraryAssetsDirsModel::ItemLibraryAssetsDirsModel(QObject *parent) +AssetsLibraryDirsModel::AssetsLibraryDirsModel(QObject *parent) : QAbstractListModel(parent) { // add roles - const QMetaObject meta = ItemLibraryAssetsDir::staticMetaObject; + const QMetaObject meta = AssetsLibraryDir::staticMetaObject; for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i) m_roleNames.insert(i, meta.property(i).name()); } -QVariant ItemLibraryAssetsDirsModel::data(const QModelIndex &index, int role) const +QVariant AssetsLibraryDirsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row()); @@ -54,7 +54,7 @@ QVariant ItemLibraryAssetsDirsModel::data(const QModelIndex &index, int role) co return {}; } -bool ItemLibraryAssetsDirsModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool AssetsLibraryDirsModel::setData(const QModelIndex &index, const QVariant &value, int role) { // currently only dirExpanded property is updatable if (index.isValid() && m_roleNames.contains(role)) { @@ -62,7 +62,7 @@ bool ItemLibraryAssetsDirsModel::setData(const QModelIndex &index, const QVarian if (currValue != value) { m_dirs.at(index.row())->setProperty(m_roleNames.value(role), value); if (m_roleNames.value(role) == "dirExpanded") - ItemLibraryAssetsModel::saveExpandedState(value.toBool(), m_dirs.at(index.row())->dirPath()); + AssetsLibraryModel::saveExpandedState(value.toBool(), m_dirs.at(index.row())->dirPath()); emit dataChanged(index, index, {role}); return true; } @@ -70,24 +70,24 @@ bool ItemLibraryAssetsDirsModel::setData(const QModelIndex &index, const QVarian return false; } -int ItemLibraryAssetsDirsModel::rowCount(const QModelIndex &parent) const +int AssetsLibraryDirsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return m_dirs.size(); } -QHash<int, QByteArray> ItemLibraryAssetsDirsModel::roleNames() const +QHash<int, QByteArray> AssetsLibraryDirsModel::roleNames() const { return m_roleNames; } -void ItemLibraryAssetsDirsModel::addDir(ItemLibraryAssetsDir *assetsDir) +void AssetsLibraryDirsModel::addDir(AssetsLibraryDir *assetsDir) { m_dirs.append(assetsDir); } -const QList<ItemLibraryAssetsDir *> ItemLibraryAssetsDirsModel::assetsDirs() const +const QList<AssetsLibraryDir *> AssetsLibraryDirsModel::assetsDirs() const { return m_dirs; } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydirsmodel.h index b139d2dd83..44e3f91ad8 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarydirsmodel.h @@ -26,28 +26,28 @@ #pragma once #include <QAbstractListModel> -#include "itemlibraryassetsdir.h" +#include "assetslibrarydir.h" namespace QmlDesigner { -class ItemLibraryAssetsDirsModel : public QAbstractListModel +class AssetsLibraryDirsModel : public QAbstractListModel { Q_OBJECT public: - ItemLibraryAssetsDirsModel(QObject *parent = nullptr); + AssetsLibraryDirsModel(QObject *parent = nullptr); QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; QHash<int, QByteArray> roleNames() const override; - void addDir(ItemLibraryAssetsDir *assetsDir); + void addDir(AssetsLibraryDir *assetsDir); - const QList<ItemLibraryAssetsDir *> assetsDirs() const; + const QList<AssetsLibraryDir *> assetsDirs() const; private: - QList<ItemLibraryAssetsDir *> m_dirs; + QList<AssetsLibraryDir *> m_dirs; QHash<int, QByteArray> m_roleNames; }; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryfilesmodel.cpp index 271f330fd2..ab9b6d9fcb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryfilesmodel.cpp @@ -22,13 +22,13 @@ ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ -#include "itemlibraryassetsfilesmodel.h" +#include "assetslibraryfilesmodel.h" #include <QDebug> namespace QmlDesigner { -ItemLibraryAssetsFilesModel::ItemLibraryAssetsFilesModel(QObject *parent) +AssetsLibraryFilesModel::AssetsLibraryFilesModel(QObject *parent) : QAbstractListModel(parent) { // add roles @@ -37,7 +37,7 @@ ItemLibraryAssetsFilesModel::ItemLibraryAssetsFilesModel(QObject *parent) m_roleNames.insert(FileDirRole, "fileDir"); } -QVariant ItemLibraryAssetsFilesModel::data(const QModelIndex &index, int role) const +QVariant AssetsLibraryFilesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row()); @@ -57,19 +57,19 @@ QVariant ItemLibraryAssetsFilesModel::data(const QModelIndex &index, int role) c return {}; } -int ItemLibraryAssetsFilesModel::rowCount(const QModelIndex &parent) const +int AssetsLibraryFilesModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return m_files.size(); } -QHash<int, QByteArray> ItemLibraryAssetsFilesModel::roleNames() const +QHash<int, QByteArray> AssetsLibraryFilesModel::roleNames() const { return m_roleNames; } -void ItemLibraryAssetsFilesModel::addFile(const QString &filePath) +void AssetsLibraryFilesModel::addFile(const QString &filePath) { m_files.append(filePath); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryfilesmodel.h index 5b44878eef..ca010bae60 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryfilesmodel.h @@ -29,12 +29,12 @@ namespace QmlDesigner { -class ItemLibraryAssetsFilesModel : public QAbstractListModel +class AssetsLibraryFilesModel : public QAbstractListModel { Q_OBJECT public: - ItemLibraryAssetsFilesModel(QObject *parent = nullptr); + AssetsLibraryFilesModel(QObject *parent = nullptr); QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index ad6f313683..9d11d26400 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -23,8 +23,8 @@ ** ****************************************************************************/ -#include "itemlibraryassetsiconprovider.h" -#include "itemlibraryassetsmodel.h" +#include "assetslibraryiconprovider.h" +#include "assetslibrarymodel.h" #include <hdrimage.h> #include <theme.h> @@ -32,40 +32,47 @@ namespace QmlDesigner { -ItemLibraryAssetsIconProvider::ItemLibraryAssetsIconProvider(SynchronousImageCache &fontImageCache) +AssetsLibraryIconProvider::AssetsLibraryIconProvider(SynchronousImageCache &fontImageCache) : QQuickImageProvider(QQuickImageProvider::Pixmap) , m_fontImageCache(fontImageCache) { } -QPixmap ItemLibraryAssetsIconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +QPixmap AssetsLibraryIconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) { QPixmap pixmap; const QString suffix = "*." + id.split('.').last().toLower(); - if (id == "browse") - pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/browse.png"); - else if (ItemLibraryAssetsModel::supportedFontSuffixes().contains(suffix)) - pixmap = generateFontIcons(id); - else if (ItemLibraryAssetsModel::supportedImageSuffixes().contains(suffix)) + if (id == "browse") { + pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/browse.png"); + } else if (AssetsLibraryModel::supportedFontSuffixes().contains(suffix)) { + pixmap = generateFontIcons(id, requestedSize); + } else if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) { pixmap = Utils::StyleHelper::dpiSpecificImageFile(id); - else if (ItemLibraryAssetsModel::supportedTexture3DSuffixes().contains(suffix)) + } else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) { pixmap = HdrImage{id}.toPixmap(); - else if (ItemLibraryAssetsModel::supportedShaderSuffixes().contains(suffix)) - pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/asset_shader_48.png"); - else if (ItemLibraryAssetsModel::supportedAudioSuffixes().contains(suffix)) - pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/asset_sound_48.png"); - else if (ItemLibraryAssetsModel::supportedVideoSuffixes().contains(suffix)) - pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-video-icon.png"); + } else { + QString type; + if (AssetsLibraryModel::supportedShaderSuffixes().contains(suffix)) + type = "shader"; + else if (AssetsLibraryModel::supportedAudioSuffixes().contains(suffix)) + type = "sound"; + else if (AssetsLibraryModel::supportedVideoSuffixes().contains(suffix)) + type = "video"; + + QString pathTemplate = QString(":/AssetsLibrary/images/asset_%1%2.png").arg(type); + QString path = pathTemplate.arg('_' + QString::number(requestedSize.width())); + + pixmap = Utils::StyleHelper::dpiSpecificImageFile(QFileInfo::exists(path) ? path + : pathTemplate.arg("")); + } if (size) { size->setWidth(pixmap.width()); size->setHeight(pixmap.height()); } - if (pixmap.isNull()) { - pixmap = QPixmap(Utils::StyleHelper::dpiSpecificImageFile( - QStringLiteral(":/ItemLibrary/images/item-default-icon.png"))); - } + if (pixmap.isNull()) + pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/assets_default.png"); if (requestedSize.isValid()) return pixmap.scaled(requestedSize); @@ -73,12 +80,13 @@ QPixmap ItemLibraryAssetsIconProvider::requestPixmap(const QString &id, QSize *s return pixmap; } -QPixmap ItemLibraryAssetsIconProvider::generateFontIcons(const QString &filePath) const +QPixmap AssetsLibraryIconProvider::generateFontIcons(const QString &filePath, const QSize &requestedSize) const { - return m_fontImageCache.icon(filePath, {}, + QSize reqSize = requestedSize.isValid() ? requestedSize : QSize{48, 48}; + return m_fontImageCache.icon(filePath, {}, ImageCache::FontCollectorSizesAuxiliaryData{Utils::span{iconSizes}, Theme::getColor(Theme::DStextColor).name(), - "Abc"}).pixmap({48, 48}); + "Abc"}).pixmap(reqSize); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h index 3c5bc4081e..83f4a63f96 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h @@ -31,28 +31,23 @@ namespace QmlDesigner { -class ItemLibraryAssetsIconProvider : public QQuickImageProvider +class AssetsLibraryIconProvider : public QQuickImageProvider { public: - ItemLibraryAssetsIconProvider(SynchronousImageCache &fontImageCache); + AssetsLibraryIconProvider(SynchronousImageCache &fontImageCache); QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override; private: - QPixmap generateFontIcons(const QString &filePath) const; + QPixmap generateFontIcons(const QString &filePath, const QSize &requestedSize) const; SynchronousImageCache &m_fontImageCache; // Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their // x2 versions for HDPI sceens - std::vector<QSize> iconSizes = {{384, 384}, - {192, 192}, // Large - {256, 256}, - {128, 128}, // Drag - {96, 96}, // Medium - {48, 48}, // Small - {64, 64}, - {32, 32}}; // List + std::vector<QSize> iconSizes = {{128, 128}, // Drag + {96, 96}, // list @2x + {48, 48}}; // list }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index c7c8db4534..0470c81ee3 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -23,9 +23,9 @@ ** ****************************************************************************/ -#include "itemlibraryassetsmodel.h" -#include "itemlibraryassetsdirsmodel.h" -#include "itemlibraryassetsfilesmodel.h" +#include "assetslibrarymodel.h" +#include "assetslibrarydirsmodel.h" +#include "assetslibraryfilesmodel.h" #include <synchronousimagecache.h> #include <theme.h> @@ -50,17 +50,17 @@ namespace QmlDesigner { -void ItemLibraryAssetsModel::saveExpandedState(bool expanded, const QString &assetPath) +void AssetsLibraryModel::saveExpandedState(bool expanded, const QString &assetPath) { m_expandedStateHash.insert(assetPath, expanded); } -bool ItemLibraryAssetsModel::loadExpandedState(const QString &assetPath) +bool AssetsLibraryModel::loadExpandedState(const QString &assetPath) { return m_expandedStateHash.value(assetPath, true); } -ItemLibraryAssetsModel::DirExpandState ItemLibraryAssetsModel::getAllExpandedState() const +AssetsLibraryModel::DirExpandState AssetsLibraryModel::getAllExpandedState() const { const auto keys = m_expandedStateHash.keys(); bool allExpanded = true; @@ -81,16 +81,16 @@ ItemLibraryAssetsModel::DirExpandState ItemLibraryAssetsModel::getAllExpandedSta : DirExpandState::SomeExpanded; } -void ItemLibraryAssetsModel::toggleExpandAll(bool expand) +void AssetsLibraryModel::toggleExpandAll(bool expand) { - std::function<void(ItemLibraryAssetsDir *)> expandDirRecursive; - expandDirRecursive = [&](ItemLibraryAssetsDir *currAssetsDir) { + std::function<void(AssetsLibraryDir *)> expandDirRecursive; + expandDirRecursive = [&](AssetsLibraryDir *currAssetsDir) { if (currAssetsDir->dirDepth() > 0) { currAssetsDir->setDirExpanded(expand); saveExpandedState(expand, currAssetsDir->dirPath()); } - const QList<ItemLibraryAssetsDir *> childDirs = currAssetsDir->childAssetsDirs(); + const QList<AssetsLibraryDir *> childDirs = currAssetsDir->childAssetsDirs(); for (const auto childDir : childDirs) expandDirRecursive(childDir); }; @@ -100,7 +100,7 @@ void ItemLibraryAssetsModel::toggleExpandAll(bool expand) endResetModel(); } -void ItemLibraryAssetsModel::deleteFile(const QString &filePath) +void AssetsLibraryModel::deleteFile(const QString &filePath) { bool askBeforeDelete = DesignerSettings::getValue( DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET).toBool(); @@ -135,7 +135,7 @@ void ItemLibraryAssetsModel::deleteFile(const QString &filePath) } } -bool ItemLibraryAssetsModel::renameFolder(const QString &folderPath, const QString &newName) +bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &newName) { QDir dir{folderPath}; QString oldName = dir.dirName(); @@ -147,7 +147,7 @@ bool ItemLibraryAssetsModel::renameFolder(const QString &folderPath, const QStri return dir.rename(oldName, newName); } -void ItemLibraryAssetsModel::addNewFolder(const QString &folderPath) +void AssetsLibraryModel::addNewFolder(const QString &folderPath) { QString iterPath = folderPath; QRegularExpression rgx("\\d+$"); // matches a number at the end of a string @@ -183,17 +183,17 @@ void ItemLibraryAssetsModel::addNewFolder(const QString &folderPath) dir.mkpath(iterPath); } -void ItemLibraryAssetsModel::deleteFolder(const QString &folderPath) +void AssetsLibraryModel::deleteFolder(const QString &folderPath) { QDir{folderPath}.removeRecursively(); } -QObject *ItemLibraryAssetsModel::rootDir() const +QObject *AssetsLibraryModel::rootDir() const { return m_assetsDir; } -const QStringList &ItemLibraryAssetsModel::supportedImageSuffixes() +const QStringList &AssetsLibraryModel::supportedImageSuffixes() { static QStringList retList; if (retList.isEmpty()) { @@ -204,13 +204,13 @@ const QStringList &ItemLibraryAssetsModel::supportedImageSuffixes() return retList; } -const QStringList &ItemLibraryAssetsModel::supportedFragmentShaderSuffixes() +const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes() { static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"}; return retList; } -const QStringList &ItemLibraryAssetsModel::supportedShaderSuffixes() +const QStringList &AssetsLibraryModel::supportedShaderSuffixes() { static const QStringList retList {"*.frag", "*.vert", "*.glsl", "*.glslv", "*.glslf", @@ -218,32 +218,32 @@ const QStringList &ItemLibraryAssetsModel::supportedShaderSuffixes() return retList; } -const QStringList &ItemLibraryAssetsModel::supportedFontSuffixes() +const QStringList &AssetsLibraryModel::supportedFontSuffixes() { static const QStringList retList {"*.ttf", "*.otf"}; return retList; } -const QStringList &ItemLibraryAssetsModel::supportedAudioSuffixes() +const QStringList &AssetsLibraryModel::supportedAudioSuffixes() { static const QStringList retList {"*.wav", "*.mp3"}; return retList; } -const QStringList &ItemLibraryAssetsModel::supportedVideoSuffixes() +const QStringList &AssetsLibraryModel::supportedVideoSuffixes() { static const QStringList retList {"*.mp4"}; return retList; } -const QStringList &ItemLibraryAssetsModel::supportedTexture3DSuffixes() +const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes() { // These are file types only supported by 3D textures static QStringList retList {"*.hdr", "*.ktx"}; return retList; } -ItemLibraryAssetsModel::ItemLibraryAssetsModel(SynchronousImageCache &fontImageCache, +AssetsLibraryModel::AssetsLibraryModel(SynchronousImageCache &fontImageCache, Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent) : QAbstractListModel(parent) @@ -252,12 +252,12 @@ ItemLibraryAssetsModel::ItemLibraryAssetsModel(SynchronousImageCache &fontImageC { // add role names int role = 0; - const QMetaObject meta = ItemLibraryAssetsDir::staticMetaObject; + const QMetaObject meta = AssetsLibraryDir::staticMetaObject; for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i) m_roleNames.insert(role++, meta.property(i).name()); } -QVariant ItemLibraryAssetsModel::data(const QModelIndex &index, int role) const +QVariant AssetsLibraryModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row()); @@ -271,32 +271,32 @@ QVariant ItemLibraryAssetsModel::data(const QModelIndex &index, int role) const return {}; } -int ItemLibraryAssetsModel::rowCount(const QModelIndex &parent) const +int AssetsLibraryModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 1; } -QHash<int, QByteArray> ItemLibraryAssetsModel::roleNames() const +QHash<int, QByteArray> AssetsLibraryModel::roleNames() const { return m_roleNames; } // called when a directory is changed to refresh the model for this directory -void ItemLibraryAssetsModel::refresh() +void AssetsLibraryModel::refresh() { setRootPath(m_assetsDir->dirPath()); } -void ItemLibraryAssetsModel::setRootPath(const QString &path) +void AssetsLibraryModel::setRootPath(const QString &path) { static const QStringList ignoredTopLevelDirs {"imports", "asset_imports"}; m_fileSystemWatcher->clear(); - std::function<bool(ItemLibraryAssetsDir *, int)> parseDirRecursive; - parseDirRecursive = [this, &parseDirRecursive](ItemLibraryAssetsDir *currAssetsDir, int currDepth) { + std::function<bool(AssetsLibraryDir *, int)> parseDirRecursive; + parseDirRecursive = [this, &parseDirRecursive](AssetsLibraryDir *currAssetsDir, int currDepth) { m_fileSystemWatcher->addDirectory(currAssetsDir->dirPath(), Utils::FileSystemWatcher::WatchAllChanges); QDir dir(currAssetsDir->dirPath()); @@ -323,7 +323,8 @@ void ItemLibraryAssetsModel::setRootPath(const QString &path) if (currDepth == 1 && ignoredTopLevelDirs.contains(subDir.dirName())) continue; - ItemLibraryAssetsDir *assetsDir = new ItemLibraryAssetsDir(subDir.path(), currDepth, loadExpandedState(subDir.path()), currAssetsDir); + auto assetsDir = new AssetsLibraryDir(subDir.path(), currDepth, + loadExpandedState(subDir.path()), currAssetsDir); currAssetsDir->addDir(assetsDir); saveExpandedState(loadExpandedState(assetsDir->dirPath()), assetsDir->dirPath()); isEmpty &= parseDirRecursive(assetsDir, currDepth + 1); @@ -339,13 +340,13 @@ void ItemLibraryAssetsModel::setRootPath(const QString &path) delete m_assetsDir; beginResetModel(); - m_assetsDir = new ItemLibraryAssetsDir(path, 0, true, this); + m_assetsDir = new AssetsLibraryDir(path, 0, true, this); bool noAssets = parseDirRecursive(m_assetsDir, 1); setIsEmpty(noAssets); endResetModel(); } -void ItemLibraryAssetsModel::setSearchText(const QString &searchText) +void AssetsLibraryModel::setSearchText(const QString &searchText) { if (m_searchText != searchText) { m_searchText = searchText; @@ -353,7 +354,7 @@ void ItemLibraryAssetsModel::setSearchText(const QString &searchText) } } -const QSet<QString> &ItemLibraryAssetsModel::supportedSuffixes() const +const QSet<QString> &AssetsLibraryModel::supportedSuffixes() const { static QSet<QString> allSuffixes; if (allSuffixes.isEmpty()) { @@ -371,12 +372,12 @@ const QSet<QString> &ItemLibraryAssetsModel::supportedSuffixes() const return allSuffixes; } -bool ItemLibraryAssetsModel::isEmpty() const +bool AssetsLibraryModel::isEmpty() const { return m_isEmpty; }; -void ItemLibraryAssetsModel::setIsEmpty(bool empty) +void AssetsLibraryModel::setIsEmpty(bool empty) { if (m_isEmpty != empty) { m_isEmpty = empty; @@ -384,7 +385,7 @@ void ItemLibraryAssetsModel::setIsEmpty(bool empty) } }; -const QSet<QString> &ItemLibraryAssetsModel::previewableSuffixes() const +const QSet<QString> &AssetsLibraryModel::previewableSuffixes() const { static QSet<QString> previewableSuffixes; if (previewableSuffixes.isEmpty()) { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 989f9f7226..7651ae1a22 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -32,24 +32,22 @@ #include <QIcon> #include <QPair> #include <QSet> -#include <QTimer> - -#include "itemlibraryassetsdir.h" namespace Utils { class FileSystemWatcher; } namespace QmlDesigner { class SynchronousImageCache; +class AssetsLibraryDir; -class ItemLibraryAssetsModel : public QAbstractListModel +class AssetsLibraryModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(bool isEmpty READ isEmpty WRITE setIsEmpty NOTIFY isEmptyChanged) public: - ItemLibraryAssetsModel(QmlDesigner::SynchronousImageCache &fontImageCache, + AssetsLibraryModel(QmlDesigner::SynchronousImageCache &fontImageCache, Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent = nullptr); @@ -103,7 +101,7 @@ private: QString m_searchText; Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr; - ItemLibraryAssetsDir *m_assetsDir = nullptr; + AssetsLibraryDir *m_assetsDir = nullptr; bool m_isEmpty = true; QHash<int, QByteArray> m_roleNames; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp new file mode 100644 index 0000000000..937587d3c1 --- /dev/null +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "assetslibraryview.h" +#include "assetslibrarywidget.h" +#include "metainfo.h" +#include <asynchronousimagecache.h> +#include <bindingproperty.h> +#include <coreplugin/icore.h> +#include <imagecache/imagecachecollector.h> +#include <imagecache/imagecacheconnectionmanager.h> +#include <imagecache/imagecachefontcollector.h> +#include <imagecache/imagecachegenerator.h> +#include <imagecache/imagecachestorage.h> +#include <imagecache/timestampprovider.h> +#include <nodelistproperty.h> +#include <projectexplorer/kit.h> +#include <projectexplorer/project.h> +#include <projectexplorer/session.h> +#include <projectexplorer/target.h> +#include <rewriterview.h> +#include <sqlitedatabase.h> +#include <synchronousimagecache.h> +#include <utils/algorithm.h> +#include <qmldesignerplugin.h> +#include <qmlitemnode.h> +#include <qmldesignerconstants.h> + +namespace QmlDesigner { + +namespace { +ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project) +{ + if (project) + return project->activeTarget(); + + return {}; +} +} // namespace + +class ImageCacheData +{ +public: + Sqlite::Database database{Utils::PathString{ + Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, + Sqlite::JournalMode::Wal, + Sqlite::LockingMode::Normal}; + ImageCacheStorage<Sqlite::Database> storage{database}; + ImageCacheConnectionManager connectionManager; + ImageCacheCollector collector{connectionManager}; + ImageCacheFontCollector fontCollector; + ImageCacheGenerator generator{collector, storage}; + ImageCacheGenerator fontGenerator{fontCollector, storage}; + TimeStampProvider timeStampProvider; + AsynchronousImageCache cache{storage, generator, timeStampProvider}; + AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider}; + SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector}; +}; + +AssetsLibraryView::AssetsLibraryView(QObject *parent) + : AbstractView(parent) +{} + +AssetsLibraryView::~AssetsLibraryView() +{} + +bool AssetsLibraryView::hasWidget() const +{ + return true; +} + +WidgetInfo AssetsLibraryView::widgetInfo() +{ + if (m_widget.isNull()) { + m_widget = new AssetsLibraryWidget{imageCacheData()->cache, + imageCacheData()->asynchronousFontImageCache, + imageCacheData()->synchronousFontImageCache}; + } + + return createWidgetInfo(m_widget.data(), + new WidgetInfo::ToolBarWidgetDefaultFactory<AssetsLibraryWidget>(m_widget.data()), + "Assets", + WidgetInfo::LeftPane, + 0, + tr("Assets")); +} + +void AssetsLibraryView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + + m_widget->clearSearchFilter(); + m_widget->setModel(model); + + setResourcePath(DocumentManager::currentResourcePath().toFileInfo().absoluteFilePath()); +} + +void AssetsLibraryView::modelAboutToBeDetached(Model *model) +{ + AbstractView::modelAboutToBeDetached(model); + + m_widget->setModel(nullptr); +} + +void AssetsLibraryView::setResourcePath(const QString &resourcePath) +{ + if (m_widget.isNull()) { + m_widget = new AssetsLibraryWidget{m_imageCacheData->cache, + m_imageCacheData->asynchronousFontImageCache, + m_imageCacheData->synchronousFontImageCache}; + } + + m_widget->setResourcePath(resourcePath); +} + +ImageCacheData *AssetsLibraryView::imageCacheData() +{ + std::call_once(imageCacheFlag, [this]() { + m_imageCacheData = std::make_unique<ImageCacheData>(); + auto setTargetInImageCache = + [imageCacheData = m_imageCacheData.get()](ProjectExplorer::Target *target) { + if (target == imageCacheData->collector.target()) + return; + + if (target) + imageCacheData->cache.clean(); + + imageCacheData->collector.setTarget(target); + }; + + if (auto project = ProjectExplorer::SessionManager::startupProject(); project) { + m_imageCacheData->collector.setTarget(project->activeTarget()); + connect(project, + &ProjectExplorer::Project::activeTargetChanged, + this, + setTargetInImageCache); + } + connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, + [=](ProjectExplorer::Project *project) { + setTargetInImageCache(activeTarget(project)); + }); + }); + return m_imageCacheData.get(); +} + +AsynchronousImageCache &AssetsLibraryView::imageCache() +{ + return imageCacheData()->cache; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h new file mode 100644 index 0000000000..79fbde852f --- /dev/null +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include <abstractview.h> + +#include <QPointer> + +#include <mutex> + +namespace QmlDesigner { + +class AssetsLibraryWidget; +class ImageCacheData; +class AsynchronousImageCache; + +class AssetsLibraryView : public AbstractView +{ + Q_OBJECT + +public: + AssetsLibraryView(QObject* parent = nullptr); + ~AssetsLibraryView() override; + + bool hasWidget() const override; + WidgetInfo widgetInfo() override; + + // AbstractView + void modelAttached(Model *model) override; + void modelAboutToBeDetached(Model *model) override; + + void setResourcePath(const QString &resourcePath); + + AsynchronousImageCache &imageCache(); + +private: + ImageCacheData *imageCacheData(); + + std::once_flag imageCacheFlag; + std::unique_ptr<ImageCacheData> m_imageCacheData; + QPointer<AssetsLibraryWidget> m_widget; +}; + +} diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp new file mode 100644 index 0000000000..f51e070bf6 --- /dev/null +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -0,0 +1,407 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "assetslibrarywidget.h" + +#include "assetslibrarymodel.h" +#include "assetslibraryiconprovider.h" + +#include <theme.h> + +#include <designeractionmanager.h> +#include "modelnodeoperations.h" +#include <model.h> +#include <navigatorwidget.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> + +#include <utils/algorithm.h> +#include <utils/fileutils.h> +#include <utils/filesystemwatcher.h> +#include <utils/stylehelper.h> +#include <utils/qtcassert.h> +#include <utils/utilsicons.h> + +#include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/messagebox.h> + +#include <QApplication> +#include <QDrag> +#include <QFileDialog> +#include <QFileInfo> +#include <QFileSystemModel> +#include <QVBoxLayout> +#include <QImageReader> +#include <QMenu> +#include <QMimeData> +#include <QMouseEvent> +#include <QShortcut> +#include <QTimer> +#include <QToolButton> +#include <QQmlContext> +#include <QQuickItem> + +namespace QmlDesigner { + +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::FocusOut) { + if (obj == m_assetsWidget.data()) + QMetaObject::invokeMethod(m_assetsWidget->rootObject(), "handleViewFocusOut"); + } else if (event->type() == QMouseEvent::MouseMove) { + if (!m_assetsToDrag.isEmpty()) { + QMouseEvent *me = static_cast<QMouseEvent *>(event); + if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) { + auto drag = new QDrag(this); + drag->setPixmap(m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128})); + QMimeData *mimeData = new QMimeData; + mimeData->setData("application/vnd.bauhaus.libraryresource", m_assetsToDrag.join(',').toUtf8()); + drag->setMimeData(mimeData); + drag->exec(); + drag->deleteLater(); + + m_assetsToDrag.clear(); + } + } + } else if (event->type() == QMouseEvent::MouseButtonRelease) { + m_assetsToDrag.clear(); + QWidget *view = QmlDesignerPlugin::instance()->viewManager().widget("Navigator"); + if (view) { + NavigatorWidget *navView = qobject_cast<NavigatorWidget *>(view); + if (navView) { + navView->setDragType(""); + navView->update(); + } + } + } + + return QObject::eventFilter(obj, event); +} + +AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &imageCache, + AsynchronousImageCache &asynchronousFontImageCache, + SynchronousImageCache &synchronousFontImageCache) + : m_itemIconSize(24, 24) + , m_fontImageCache(synchronousFontImageCache) + , m_assetsIconProvider(new AssetsLibraryIconProvider(synchronousFontImageCache)) + , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) + , m_assetsModel(new AssetsLibraryModel(synchronousFontImageCache, m_fileSystemWatcher, this)) + , m_assetsWidget(new QQuickWidget(this)) + , m_imageCache{imageCache} +{ + m_assetCompressionTimer.setInterval(200); + m_assetCompressionTimer.setSingleShot(true); + + setWindowTitle(tr("Assets Library", "Title of assets library widget")); + setMinimumWidth(250); + + m_assetsWidget->installEventFilter(this); + + m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache); + // Note: Though the text specified here appears in UI, it shouldn't be translated, as it's + // a commonly used sentence to preview the font glyphs in latin fonts. + // For fonts that do not have latin glyphs, the font family name will have to suffice for preview. + m_fontPreviewTooltipBackend->setAuxiliaryData( + ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300}, + Theme::getColor(Theme::DStextColor).name(), + QStringLiteral("The quick brown fox jumps\n" + "over the lazy dog\n" + "1234567890")}); + // create assets widget + m_assetsWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + Theme::setupTheme(m_assetsWidget->engine()); + m_assetsWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_assetsWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + m_assetsWidget->engine()->addImageProvider("qmldesigner_assets", m_assetsIconProvider); + m_assetsWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{ + {{"assetsModel"}, QVariant::fromValue(m_assetsModel.data())}, + {{"rootView"}, QVariant::fromValue(this)}, + {{"tooltipBackend"}, QVariant::fromValue(m_fontPreviewTooltipBackend.get())} + }); + + // If project directory contents change, or one of the asset files is modified, we must + // reconstruct the model to update the icons + connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this](const QString & changedDirPath) { + Q_UNUSED(changedDirPath) + m_assetCompressionTimer.start(); + }); + + auto layout = new QVBoxLayout(this); + layout->setContentsMargins({}); + layout->setSpacing(0); + layout->addWidget(m_assetsWidget.data()); + + updateSearch(); + + setStyleSheet(Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); + + m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F6), this); + connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &AssetsLibraryWidget::reloadQmlSource); + + connect(&m_assetCompressionTimer, &QTimer::timeout, this, [this]() { + // TODO: find a clever way to only refresh the changed directory part of the model + + // Don't bother with asset updates after model has detached, project is probably closing + if (!m_model.isNull()) { + if (QApplication::activeModalWidget()) { + // Retry later, as updating file system watchers can crash when there is an active + // modal widget + m_assetCompressionTimer.start(); + } else { + m_assetsModel->refresh(); + // reload assets qml so that an overridden file's image shows the new image + QTimer::singleShot(100, this, &AssetsLibraryWidget::reloadQmlSource); + } + } + }); + + // init the first load of the QML UI elements + reloadQmlSource(); +} + +AssetsLibraryWidget::~AssetsLibraryWidget() = default; + +QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets() +{ + return {}; +} + +void AssetsLibraryWidget::handleSearchfilterChanged(const QString &filterText) +{ + if (filterText != m_filterText) { + m_filterText = filterText; + updateSearch(); + } +} + +void AssetsLibraryWidget::handleAddAsset() +{ + addResources({}); +} + +void AssetsLibraryWidget::handleFilesDrop(const QStringList &filesPaths) +{ + addResources(filesPaths); +} + +QSet<QString> AssetsLibraryWidget::supportedDropSuffixes() +{ + const QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager() + .designerActionManager().addResourceHandler(); + + QSet<QString> suffixes; + for (const AddResourceHandler &handler : handlers) + suffixes.insert(handler.filter); + + return suffixes; +} + +void AssetsLibraryWidget::setModel(Model *model) +{ + m_model = model; +} + +QString AssetsLibraryWidget::qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/itemLibraryQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/itemLibraryQmlSources").toString(); +} + +void AssetsLibraryWidget::clearSearchFilter() +{ + QMetaObject::invokeMethod(m_assetsWidget->rootObject(), "clearSearchFilter"); +} + +void AssetsLibraryWidget::reloadQmlSource() +{ + const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml"; + QTC_ASSERT(QFileInfo::exists(assetsQmlPath), return); + m_assetsWidget->engine()->clearComponentCache(); + m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath)); +} + +void AssetsLibraryWidget::updateSearch() +{ + m_assetsModel->setSearchText(m_filterText); +} + +void AssetsLibraryWidget::setResourcePath(const QString &resourcePath) +{ + m_assetsModel->setRootPath(resourcePath); + updateSearch(); +} + +void AssetsLibraryWidget::startDragAsset(const QStringList &assetPaths, const QPointF &mousePos) +{ + // Actual drag is created after mouse has moved to avoid a QDrag bug that causes drag to stay + // active (and blocks mouse release) if mouse is released at the same spot of the drag start. + m_assetsToDrag = assetPaths; + m_dragStartPoint = mousePos.toPoint(); +} + +QPair<QString, QByteArray> AssetsLibraryWidget::getAssetTypeAndData(const QString &assetPath) +{ + QString suffix = "*." + assetPath.split('.').last().toLower(); + if (!suffix.isEmpty()) { + if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) { + // Data: Image format (suffix) + return {"application/vnd.bauhaus.libraryresource.image", suffix.toUtf8()}; + } else if (AssetsLibraryModel::supportedFontSuffixes().contains(suffix)) { + // Data: Font family name + QRawFont font(assetPath, 10); + QString fontFamily = font.isValid() ? font.familyName() : ""; + return {"application/vnd.bauhaus.libraryresource.font", fontFamily.toUtf8()}; + } else if (AssetsLibraryModel::supportedShaderSuffixes().contains(suffix)) { + // Data: shader type, frament (f) or vertex (v) + return {"application/vnd.bauhaus.libraryresource.shader", + AssetsLibraryModel::supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"}; + } else if (AssetsLibraryModel::supportedAudioSuffixes().contains(suffix)) { + // No extra data for sounds + return {"application/vnd.bauhaus.libraryresource.sound", {}}; + } else if (AssetsLibraryModel::supportedVideoSuffixes().contains(suffix)) { + // No extra data for videos + return {"application/vnd.bauhaus.libraryresource.video", {}}; + } else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) { + // Data: Image format (suffix) + return {"application/vnd.bauhaus.libraryresource.texture3d", suffix.toUtf8()}; + } + } + return {}; +} + +static QHash<QByteArray, QStringList> allImageFormats() +{ + const QList<QByteArray> mimeTypes = QImageReader::supportedMimeTypes(); + auto transformer = [](const QByteArray& format) -> QString { return QString("*.") + format; }; + QHash<QByteArray, QStringList> imageFormats; + for (const auto &mimeType : mimeTypes) + imageFormats.insert(mimeType, Utils::transform(QImageReader::imageFormatsForMimeType(mimeType), transformer)); + imageFormats.insert("image/vnd.radiance", {"*.hdr"}); + imageFormats.insert("image/ktx", {"*.ktx"}); + + return imageFormats; +} + +void AssetsLibraryWidget::addResources(const QStringList &files) +{ + DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); + + QTC_ASSERT(document, return); + + const QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager() + .designerActionManager().addResourceHandler(); + + QStringList fileNames = files; + if (fileNames.isEmpty()) { // if no files, show the "add assets" dialog + QMultiMap<QString, QString> map; + QHash<QString, int> priorities; + for (const AddResourceHandler &handler : handlers) { + map.insert(handler.category, handler.filter); + priorities.insert(handler.category, handler.piority); + } + + QStringList sortedKeys = map.uniqueKeys(); + Utils::sort(sortedKeys, [&priorities](const QString &first, const QString &second) { + return priorities.value(first) < priorities.value(second); + }); + + QStringList filters { tr("All Files (%1)").arg("*.*") }; + QString filterTemplate = "%1 (%2)"; + for (const QString &key : qAsConst(sortedKeys)) { + const QStringList values = map.values(key); + if (values.contains("*.png")) { // Avoid long filter for images by splitting + const QHash<QByteArray, QStringList> imageFormats = allImageFormats(); + QHash<QByteArray, QStringList>::const_iterator i = imageFormats.constBegin(); + while (i != imageFormats.constEnd()) { + filters.append(filterTemplate.arg(key + QString::fromLatin1(i.key()), i.value().join(' '))); + ++i; + } + } else { + filters.append(filterTemplate.arg(key, values.join(' '))); + } + } + + static QString lastDir; + const QString currentDir = lastDir.isEmpty() ? document->fileName().parentDir().toString() : lastDir; + + fileNames = QFileDialog::getOpenFileNames(Core::ICore::dialogParent(), + tr("Add Assets"), + currentDir, + filters.join(";;")); + + if (!fileNames.isEmpty()) + lastDir = QFileInfo(fileNames.first()).absolutePath(); + } + + QHash<QString, QString> filterToCategory; + QHash<QString, AddResourceOperation> categoryToOperation; + for (const AddResourceHandler &handler : handlers) { + filterToCategory.insert(handler.filter, handler.category); + categoryToOperation.insert(handler.category, handler.operation); + } + + QMultiMap<QString, QString> categoryFileNames; // filenames grouped by category + + for (const QString &fileName : qAsConst(fileNames)) { + const QString suffix = "*." + QFileInfo(fileName).suffix().toLower(); + const QString category = filterToCategory.value(suffix); + categoryFileNames.insert(category, fileName); + } + + for (const QString &category : categoryFileNames.uniqueKeys()) { + QStringList fileNames = categoryFileNames.values(category); + AddResourceOperation operation = categoryToOperation.value(category); + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_RESOURCE_IMPORTED + category); + if (operation) { + AddFilesResult result = operation(fileNames, + document->fileName().parentDir().toString()); + if (result == AddFilesResult::Failed) { + Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), + tr("Could not add %1 to project.") + .arg(fileNames.join(' '))); + } + } else { + Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), + tr("Could not add %1 to project. Unsupported file format.") + .arg(fileNames.join(' '))); + } + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h new file mode 100644 index 0000000000..6c27be54a6 --- /dev/null +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include <previewtooltip/previewtooltipbackend.h> +#include "assetslibrarymodel.h" + +#include <QFrame> +#include <QToolButton> +#include <QFileIconProvider> +#include <QQuickWidget> +#include <QQmlPropertyMap> +#include <QTimer> +#include <QPointF> + +#include <memory> + +QT_BEGIN_NAMESPACE +class QShortcut; +QT_END_NAMESPACE + +namespace Utils { class FileSystemWatcher; } + +namespace QmlDesigner { + +class MetaInfo; +class Model; + +class AssetsLibraryIconProvider; +class AssetsLibraryModel; +class SynchronousImageCache; +class AsynchronousImageCache; +class ImageCacheCollector; + +class AssetsLibraryWidget : public QFrame +{ + Q_OBJECT + +public: + AssetsLibraryWidget(AsynchronousImageCache &imageCache, + AsynchronousImageCache &asynchronousFontImageCache, + SynchronousImageCache &synchronousFontImageCache); + ~AssetsLibraryWidget(); + + QList<QToolButton *> createToolBarWidgets(); + + static QString qmlSourcesPath(); + void clearSearchFilter(); + + void delayedUpdateModel(); + void updateModel(); + + void setResourcePath(const QString &resourcePath); + void setModel(Model *model); + static QPair<QString, QByteArray> getAssetTypeAndData(const QString &assetPath); + + Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos); + Q_INVOKABLE void handleAddAsset(); + Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText); + Q_INVOKABLE void handleFilesDrop(const QStringList &filesPaths); + Q_INVOKABLE QSet<QString> supportedDropSuffixes(); + +signals: + void itemActivated(const QString &itemName); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void reloadQmlSource(); + + void addResources(const QStringList &files); + void updateSearch(); + + QTimer m_assetCompressionTimer; + QSize m_itemIconSize; + + SynchronousImageCache &m_fontImageCache; + + AssetsLibraryIconProvider *m_assetsIconProvider = nullptr; + Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr; + QPointer<AssetsLibraryModel> m_assetsModel; + + QScopedPointer<QQuickWidget> m_assetsWidget; + std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend; + + QShortcut *m_qmlSourceUpdateShortcut = nullptr; + AsynchronousImageCache &m_imageCache; + QPointer<Model> m_model; + QStringList m_assetsToDrag; + bool m_updateRetry = false; + QString m_filterText; + QPoint m_dragStartPoint; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/images/asset_default.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_default.png Binary files differnew file mode 100644 index 0000000000..ef59d89279 --- /dev/null +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_default.png diff --git a/src/plugins/qmldesigner/components/assetslibrary/images/asset_default@2x.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_default@2x.png Binary files differnew file mode 100644 index 0000000000..6540cc859c --- /dev/null +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_default@2x.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_48.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_shader.png Binary files differindex e17bd4d264..e17bd4d264 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_48.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_shader.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_96.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_shader@2x.png Binary files differindex e8d80ff732..e8d80ff732 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_96.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_shader@2x.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_128.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_shader_128.png Binary files differindex 506339350f..506339350f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_128.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_shader_128.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_48.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_sound.png Binary files differindex fbf50679c8..fbf50679c8 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_48.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_sound.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_96.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_sound@2x.png Binary files differindex 1cf8147190..1cf8147190 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_96.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_sound@2x.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_128.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_sound_128.png Binary files differindex 1bd7dc3272..1bd7dc3272 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_128.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_sound_128.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/item-video-icon.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_video.png Binary files differindex df1b84e5c9..df1b84e5c9 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/item-video-icon.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_video.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/item-video-icon@2x.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_video@2x.png Binary files differindex 4b9f31faf3..4b9f31faf3 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/item-video-icon@2x.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_video@2x.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/browse.png b/src/plugins/qmldesigner/components/assetslibrary/images/browse.png Binary files differindex 003b9e5d81..003b9e5d81 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/browse.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/browse.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/browse@2x.png b/src/plugins/qmldesigner/components/assetslibrary/images/browse@2x.png Binary files differindex b13400f788..b13400f788 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/images/browse@2x.png +++ b/src/plugins/qmldesigner/components/assetslibrary/images/browse@2x.png diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp index 4d658deb8d..f9d05307aa 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -27,7 +27,7 @@ #include "formeditorview.h" #include "formeditorwidget.h" #include "formeditorscene.h" -#include "itemlibrarywidget.h" +#include "assetslibrarywidget.h" #include <modelnodecontextmenu.h> @@ -240,7 +240,7 @@ void AbstractFormEditorTool::dragEnterEvent(const QList<QGraphicsItem*> &itemLis const QStringList assetPaths = QString::fromUtf8(event->mimeData() ->data("application/vnd.bauhaus.libraryresource")).split(","); for (const QString &assetPath : assetPaths) { - QString assetType = ItemLibraryWidget::getAssetTypeAndData(assetPath).first; + QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; if (assetType == "application/vnd.bauhaus.libraryresource.image" || assetType == "application/vnd.bauhaus.libraryresource.font") { hasValidAssets = true; diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index f2eb584406..b3650aaae0 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -27,7 +27,7 @@ #include "formeditorscene.h" #include "formeditorview.h" -#include "itemlibrarywidget.h" +#include "assetslibrarywidget.h" #include <metainfo.h> #include <nodehints.h> #include <rewritingexception.h> @@ -142,7 +142,7 @@ void DragTool::createQmlItemNodeFromFont(const QString &fontPath, QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform() .inverted().map(scenePos); - const auto typeAndData = ItemLibraryWidget::getAssetTypeAndData(fontPath); + const auto typeAndData = AssetsLibraryWidget::getAssetTypeAndData(fontPath); QString fontFamily = QString::fromUtf8(typeAndData.second); m_dragNodes.append(QmlItemNode::createQmlItemNodeFromFont(view(), fontFamily, @@ -328,7 +328,7 @@ void DragTool::createDragNodes(const QMimeData *mimeData, const QPointF &scenePo const QStringList assetPaths = QString::fromUtf8(mimeData ->data("application/vnd.bauhaus.libraryresource")).split(","); for (const QString &assetPath : assetPaths) { - QString assetType = ItemLibraryWidget::getAssetTypeAndData(assetPath).first; + QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; if (assetType == "application/vnd.bauhaus.libraryresource.image") createQmlItemNodeFromImage(assetPath, targetContainerQmlItemNode, scenePosition); else if (assetType == "application/vnd.bauhaus.libraryresource.font") diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h index 7e1b4cb481..63d3de6e8f 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.h +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h @@ -99,7 +99,7 @@ private: QList<QmlItemNode> m_dragNodes; bool m_blockMove; QPointF m_startPoint; - bool m_isAborted; + bool m_isAborted = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_128.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_128.png Binary files differdeleted file mode 100644 index 44dd517b35..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_128.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_192.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_192.png Binary files differdeleted file mode 100644 index f6f394fdb0..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_192.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_256.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_256.png Binary files differdeleted file mode 100644 index 2a830a216a..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_256.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_32.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_32.png Binary files differdeleted file mode 100644 index d939fe8533..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_32.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_384.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_384.png Binary files differdeleted file mode 100644 index 4cd1e73133..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_384.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_48.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_48.png Binary files differdeleted file mode 100644 index e8691a2976..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_48.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_64.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_64.png Binary files differdeleted file mode 100644 index e70c8d55d9..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_64.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_96.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_96.png Binary files differdeleted file mode 100644 index 5e4c62b798..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_font_96.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_192.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_192.png Binary files differdeleted file mode 100644 index 5f8ee5151d..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_192.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_256.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_256.png Binary files differdeleted file mode 100644 index c7e20e0c97..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_256.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_32.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_32.png Binary files differdeleted file mode 100644 index 1c256cca42..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_32.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_384.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_384.png Binary files differdeleted file mode 100644 index 8604a7f7b7..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_384.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_64.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_64.png Binary files differdeleted file mode 100644 index 16023491cf..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_shader_64.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_192.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_192.png Binary files differdeleted file mode 100644 index 8dd93eb33d..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_192.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_256.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_256.png Binary files differdeleted file mode 100644 index e73588220f..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_256.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_32.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_32.png Binary files differdeleted file mode 100644 index c681c6d4dc..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_32.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_384.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_384.png Binary files differdeleted file mode 100644 index 571de696e3..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_384.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_64.png b/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_64.png Binary files differdeleted file mode 100644 index 21d15eee87..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/asset_sound_64.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/x.png b/src/plugins/qmldesigner/components/itemlibrary/images/x.png Binary files differdeleted file mode 100644 index 323d2a2911..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/x.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/x@2x.png b/src/plugins/qmldesigner/components/itemlibrary/images/x@2x.png Binary files differdeleted file mode 100644 index 18fa9a35f7..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/images/x@2x.png +++ /dev/null diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc index c6d60413d0..eb927c2aa9 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc @@ -1,39 +1,9 @@ <RCC> <qresource prefix="/ItemLibrary"> - <file>images/item-default-icon.png</file> <file>images/item-invalid-icon.png</file> + <file>images/item-default-icon.png</file> <file>images/item-default-icon@2x.png</file> <file>images/item-3D_model-icon.png</file> <file>images/item-3D_model-icon@2x.png</file> - <file>images/asset_font_32.png</file> - <file>images/asset_font_48.png</file> - <file>images/asset_font_64.png</file> - <file>images/asset_font_96.png</file> - <file>images/asset_font_128.png</file> - <file>images/asset_font_192.png</file> - <file>images/asset_font_256.png</file> - <file>images/asset_font_384.png</file> - <file>images/asset_shader_32.png</file> - <file>images/asset_shader_48.png</file> - <file>images/asset_shader_64.png</file> - <file>images/asset_shader_96.png</file> - <file>images/asset_shader_128.png</file> - <file>images/asset_shader_192.png</file> - <file>images/asset_shader_256.png</file> - <file>images/asset_shader_384.png</file> - <file>images/asset_sound_32.png</file> - <file>images/asset_sound_48.png</file> - <file>images/asset_sound_64.png</file> - <file>images/asset_sound_96.png</file> - <file>images/asset_sound_128.png</file> - <file>images/asset_sound_192.png</file> - <file>images/asset_sound_256.png</file> - <file>images/asset_sound_384.png</file> - <file>images/x.png</file> - <file>images/x@2x.png</file> - <file>images/browse.png</file> - <file>images/browse@2x.png</file> - <file>images/item-video-icon.png</file> - <file>images/item-video-icon@2x.png</file> </qresource> </RCC> diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 151be411ad..6596a10b4a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -105,10 +105,10 @@ WidgetInfo ItemLibraryView::widgetInfo() return createWidgetInfo(m_widget.data(), new WidgetInfo::ToolBarWidgetDefaultFactory<ItemLibraryWidget>(m_widget.data()), - QStringLiteral("Library"), + "Components", WidgetInfo::LeftPane, 0, - tr("Library")); + tr("Components")); } void ItemLibraryView::modelAttached(Model *model) @@ -122,7 +122,6 @@ void ItemLibraryView::modelAttached(Model *model) m_widget->updatePossibleImports(model->possibleImports()); m_hasErrors = !rewriterView()->errors().isEmpty(); m_widget->setFlowMode(QmlItemNode(rootModelNode()).isFlowView()); - setResourcePath(DocumentManager::currentResourcePath().toFileInfo().absoluteFilePath()); } void ItemLibraryView::modelAboutToBeDetached(Model *model) @@ -183,16 +182,6 @@ void ItemLibraryView::usedImportsChanged(const QList<Import> &usedImports) m_widget->updateUsedImports(usedImports); } -void ItemLibraryView::setResourcePath(const QString &resourcePath) -{ - if (m_widget.isNull()) - m_widget = new ItemLibraryWidget{m_imageCacheData->cache, - m_imageCacheData->asynchronousFontImageCache, - m_imageCacheData->synchronousFontImageCache}; - - m_widget->setResourcePath(resourcePath); -} - ImageCacheData *ItemLibraryView::imageCacheData() { std::call_once(imageCacheFlag, [this]() { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h index 3741df65c3..8d85383d5d 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h @@ -59,8 +59,6 @@ public: void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; - void setResourcePath(const QString &resourcePath); - AsynchronousImageCache &imageCache(); protected: diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index f71c629a43..edca09720f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -25,7 +25,6 @@ #include "itemlibrarywidget.h" -#include "itemlibraryassetsmodel.h" #include "itemlibraryiconimageprovider.h" #include "itemlibraryimport.h" @@ -37,7 +36,6 @@ #include <itemlibraryinfo.h> #include <itemlibrarymodel.h> #include <itemlibraryaddimportmodel.h> -#include "itemlibraryassetsiconprovider.h" #include "modelnodeoperations.h" #include <metainfo.h> #include <model.h> @@ -91,10 +89,8 @@ static QString propertyEditorResourcesPath() bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::FocusOut) { - if (obj == m_itemViewQuickWidget.data()) - QMetaObject::invokeMethod(m_itemViewQuickWidget->rootObject(), "closeContextMenu"); - else if (obj == m_assetsWidget.data()) - QMetaObject::invokeMethod(m_assetsWidget->rootObject(), "handleViewFocusOut"); + if (obj == m_itemsWidget.data()) + QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "closeContextMenu"); } else if (event->type() == QMouseEvent::MouseMove) { if (m_itemToDrag.isValid()) { QMouseEvent *me = static_cast<QMouseEvent *>(event); @@ -134,23 +130,9 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) m_itemToDrag = {}; } - } else if (!m_assetsToDrag.isEmpty()) { - QMouseEvent *me = static_cast<QMouseEvent *>(event); - if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) { - auto drag = new QDrag(this); - drag->setPixmap(m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128})); - QMimeData *mimeData = new QMimeData; - mimeData->setData("application/vnd.bauhaus.libraryresource", m_assetsToDrag.join(',').toUtf8()); - drag->setMimeData(mimeData); - drag->exec(); - drag->deleteLater(); - - m_assetsToDrag.clear(); - } } } else if (event->type() == QMouseEvent::MouseButtonRelease) { m_itemToDrag = {}; - m_assetsToDrag.clear(); QWidget *view = QmlDesignerPlugin::instance()->viewManager().widget("Navigator"); if (view) { NavigatorWidget *navView = qobject_cast<NavigatorWidget *>(view); @@ -175,113 +157,47 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, : m_itemIconSize(24, 24) , m_fontImageCache(synchronousFontImageCache) , m_itemLibraryModel(new ItemLibraryModel(this)) - , m_itemLibraryAddImportModel(new ItemLibraryAddImportModel(this)) - , m_assetsIconProvider(new ItemLibraryAssetsIconProvider(synchronousFontImageCache)) - , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) - , m_assetsModel(new ItemLibraryAssetsModel(synchronousFontImageCache, m_fileSystemWatcher, this)) - , m_headerWidget(new QQuickWidget(this)) - , m_addImportWidget(new QQuickWidget(this)) - , m_itemViewQuickWidget(new QQuickWidget(this)) - , m_assetsWidget(new QQuickWidget(this)) + , m_addModuleModel(new ItemLibraryAddImportModel(this)) + , m_itemsWidget(new QQuickWidget(this)) , m_imageCache{imageCache} { m_compressionTimer.setInterval(200); m_compressionTimer.setSingleShot(true); - m_assetCompressionTimer.setInterval(200); - m_assetCompressionTimer.setSingleShot(true); ItemLibraryModel::registerQmlTypes(); - setWindowTitle(tr("Library", "Title of library view")); + setWindowTitle(tr("Components Library", "Title of library view")); setMinimumWidth(100); - // create header widget - m_headerWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - m_headerWidget->setMinimumHeight(75); - m_headerWidget->setMinimumWidth(100); - Theme::setupTheme(m_headerWidget->engine()); - m_headerWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_headerWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - m_headerWidget->rootContext()->setContextProperty("rootView", QVariant::fromValue(this)); - - // create add imports widget - m_addImportWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - Theme::setupTheme(m_addImportWidget->engine()); - m_addImportWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_addImportWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - m_addImportWidget->rootContext()->setContextProperties({ - {"addImportModel", QVariant::fromValue(m_itemLibraryAddImportModel.data())}, - {"rootView", QVariant::fromValue(this)}, - }); + // set up Component Library view and model + m_itemsWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_itemsWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - // set up Item Library view and model - m_itemViewQuickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - m_itemViewQuickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - - m_itemViewQuickWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{ - {{"itemLibraryModel"}, QVariant::fromValue(m_itemLibraryModel.data())}, - {{"itemLibraryIconWidth"}, m_itemIconSize.width()}, - {{"itemLibraryIconHeight"}, m_itemIconSize.height()}, - {{"rootView"}, QVariant::fromValue(this)}, - {{"widthLimit"}, HORIZONTAL_LAYOUT_WIDTH_LIMIT}, - {{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()}, + m_itemsWidget->rootContext()->setContextProperties({ + {"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.data())}, + {"addModuleModel", QVariant::fromValue(m_addModuleModel.data())}, + {"itemLibraryIconWidth", m_itemIconSize.width()}, + {"itemLibraryIconHeight", m_itemIconSize.height()}, + {"rootView", QVariant::fromValue(this)}, + {"widthLimit", HORIZONTAL_LAYOUT_WIDTH_LIMIT}, + {"highlightColor", Utils::StyleHelper::notTooBrightHighlightColor()}, }); m_previewTooltipBackend = std::make_unique<PreviewTooltipBackend>(m_imageCache); - m_itemViewQuickWidget->rootContext()->setContextProperty("tooltipBackend", - m_previewTooltipBackend.get()); + m_itemsWidget->rootContext()->setContextProperty("tooltipBackend", m_previewTooltipBackend.get()); - m_itemViewQuickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - m_itemViewQuickWidget->engine()->addImageProvider(QStringLiteral("qmldesigner_itemlibrary"), + m_itemsWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); + m_itemsWidget->engine()->addImageProvider(QStringLiteral("qmldesigner_itemlibrary"), new Internal::ItemLibraryImageProvider); - Theme::setupTheme(m_itemViewQuickWidget->engine()); - m_itemViewQuickWidget->installEventFilter(this); - m_assetsWidget->installEventFilter(this); - - m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache); - // Note: Though the text specified here appears in UI, it shouldn't be translated, as it's - // a commonly used sentence to preview the font glyphs in latin fonts. - // For fonts that do not have latin glyphs, the font family name will have to suffice for preview. - m_fontPreviewTooltipBackend->setAuxiliaryData( - ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300}, - Theme::getColor(Theme::DStextColor).name(), - QStringLiteral("The quick brown fox jumps\n" - "over the lazy dog\n" - "1234567890")}); - // create assets widget - m_assetsWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - Theme::setupTheme(m_assetsWidget->engine()); - m_assetsWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_assetsWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); - m_assetsWidget->engine()->addImageProvider("qmldesigner_assets", m_assetsIconProvider); - m_assetsWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{ - {{"assetsModel"}, QVariant::fromValue(m_assetsModel.data())}, - {{"rootView"}, QVariant::fromValue(this)}, - {{"tooltipBackend"}, QVariant::fromValue(m_fontPreviewTooltipBackend.get())} - }); - - // If project directory contents change, or one of the asset files is modified, we must - // reconstruct the model to update the icons - connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this](const QString & changedDirPath) { - Q_UNUSED(changedDirPath) - m_assetCompressionTimer.start(); - }); - - m_stackedWidget = new QStackedWidget(this); - m_stackedWidget->addWidget(m_itemViewQuickWidget.data()); - m_stackedWidget->addWidget(m_assetsWidget.data()); - m_stackedWidget->addWidget(m_addImportWidget.data()); - m_stackedWidget->setMinimumHeight(30); - m_stackedWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + Theme::setupTheme(m_itemsWidget->engine()); + m_itemsWidget->installEventFilter(this); auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_headerWidget.data()); - layout->addWidget(m_stackedWidget.data()); + layout->addWidget(m_itemsWidget.data()); updateSearch(); - /* style sheets */ setStyleSheet(Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); @@ -289,28 +205,8 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource); connect(&m_compressionTimer, &QTimer::timeout, this, &ItemLibraryWidget::updateModel); - connect(&m_assetCompressionTimer, &QTimer::timeout, this, [this]() { - // TODO: find a clever way to only refresh the changed directory part of the model - - // Don't bother with asset updates after model has detached, project is probably closing - if (!m_model.isNull()) { - if (QApplication::activeModalWidget()) { - // Retry later, as updating file system watchers can crash when there is an active - // modal widget - m_assetCompressionTimer.start(); - } else { - m_assetsModel->refresh(); - // reload assets qml so that an overridden file's image shows the new image - QTimer::singleShot(100, this, [this] { - const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml"; - m_assetsWidget->engine()->clearComponentCache(); - m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath)); - }); - } - } - }); - m_itemViewQuickWidget->engine()->addImageProvider("itemlibrary_preview", + m_itemsWidget->engine()->addImageProvider("itemlibrary_preview", new ItemLibraryIconImageProvider{m_imageCache}); // init the first load of the QML UI elements @@ -336,42 +232,28 @@ void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo) this, &ItemLibraryWidget::delayedUpdateModel); connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::priorityImportsChanged, this, &ItemLibraryWidget::handlePriorityImportsChanged); - m_itemLibraryAddImportModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); + m_addModuleModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); } delayedUpdateModel(); } QList<QToolButton *> ItemLibraryWidget::createToolBarWidgets() { -// TODO: implement - QList<QToolButton *> buttons; - return buttons; + return {}; } + void ItemLibraryWidget::handleSearchfilterChanged(const QString &filterText) { if (filterText != m_filterText) { m_filterText = filterText; - emit searchActiveChanged(); + updateSearch(); } - - updateSearch(); -} - -void ItemLibraryWidget::handleAddModule() -{ - QMetaObject::invokeMethod(m_headerWidget->rootObject(), "setTab", Q_ARG(QVariant, 0)); - handleTabChanged(2); -} - -void ItemLibraryWidget::handleAddAsset() -{ - addResources({}); } void ItemLibraryWidget::handleAddImport(int index) { - Import import = m_itemLibraryAddImportModel->getImportAt(index); + Import import = m_addModuleModel->getImportAt(index); if (import.isLibraryImport() && (import.url().startsWith("QtQuick") || import.url().startsWith("SimulinkConnector"))) { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_IMPORT_ADDED @@ -381,27 +263,10 @@ void ItemLibraryWidget::handleAddImport(int index) auto document = QmlDesignerPlugin::instance()->currentDesignDocument(); document->documentModel()->changeImports({import}, {}); - m_stackedWidget->setCurrentIndex(0); // switch to the Components view after import is added + QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "switchToComponentsView"); updateSearch(); } -void ItemLibraryWidget::handleFilesDrop(const QStringList &filesPaths) -{ - addResources(filesPaths); -} - -QSet<QString> ItemLibraryWidget::supportedDropSuffixes() -{ - const QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager() - .designerActionManager().addResourceHandler(); - - QSet<QString> suffixes; - for (const AddResourceHandler &handler : handlers) - suffixes.insert(handler.filter); - - return suffixes; -} - void ItemLibraryWidget::delayedUpdateModel() { static bool disableTimer = DesignerSettings::getValue(DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER).toBool(); @@ -423,20 +288,14 @@ void ItemLibraryWidget::setModel(Model *model) const bool subCompEditMode = document->inFileComponentModelActive(); if (m_subCompEditMode != subCompEditMode) { m_subCompEditMode = subCompEditMode; - // Switch out of module add panel if it's active - if (m_subCompEditMode && m_stackedWidget->currentIndex() == 2) - m_stackedWidget->setCurrentIndex(0); + // Switch out of add module view if it's active + if (m_subCompEditMode) + QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "switchToComponentsView"); emit subCompEditModeChanged(); } } } -void ItemLibraryWidget::handleTabChanged(int index) -{ - m_stackedWidget->setCurrentIndex(index); - updateSearch(); -} - QString ItemLibraryWidget::qmlSourcesPath() { #ifdef SHARE_QML_PATH @@ -448,30 +307,15 @@ QString ItemLibraryWidget::qmlSourcesPath() void ItemLibraryWidget::clearSearchFilter() { - QMetaObject::invokeMethod(m_headerWidget->rootObject(), "clearSearchFilter"); + QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "clearSearchFilter"); } void ItemLibraryWidget::reloadQmlSource() { - const QString libraryHeaderQmlPath = qmlSourcesPath() + "/LibraryHeader.qml"; - QTC_ASSERT(QFileInfo::exists(libraryHeaderQmlPath), return); - m_headerWidget->engine()->clearComponentCache(); - m_headerWidget->setSource(QUrl::fromLocalFile(libraryHeaderQmlPath)); - - const QString addImportQmlPath = qmlSourcesPath() + "/AddImport.qml"; - QTC_ASSERT(QFileInfo::exists(addImportQmlPath), return); - m_addImportWidget->engine()->clearComponentCache(); - m_addImportWidget->setSource(QUrl::fromLocalFile(addImportQmlPath)); - const QString itemLibraryQmlPath = qmlSourcesPath() + "/ItemsView.qml"; QTC_ASSERT(QFileInfo::exists(itemLibraryQmlPath), return); - m_itemViewQuickWidget->engine()->clearComponentCache(); - m_itemViewQuickWidget->setSource(QUrl::fromLocalFile(itemLibraryQmlPath)); - - const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml"; - QTC_ASSERT(QFileInfo::exists(assetsQmlPath), return); - m_assetsWidget->engine()->clearComponentCache(); - m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath)); + m_itemsWidget->engine()->clearComponentCache(); + m_itemsWidget->setSource(QUrl::fromLocalFile(itemLibraryQmlPath)); } void ItemLibraryWidget::updateModel() @@ -496,7 +340,7 @@ void ItemLibraryWidget::updateModel() void ItemLibraryWidget::updatePossibleImports(const QList<Import> &possibleImports) { - m_itemLibraryAddImportModel->update(possibleImports); + m_addModuleModel->update(possibleImports); delayedUpdateModel(); } @@ -507,30 +351,19 @@ void ItemLibraryWidget::updateUsedImports(const QList<Import> &usedImports) void ItemLibraryWidget::updateSearch() { - if (m_stackedWidget->currentIndex() == 0) { // Item Library tab selected - m_itemLibraryModel->setSearchText(m_filterText); - m_itemViewQuickWidget->update(); - } else if (m_stackedWidget->currentIndex() == 1) { // Assets tab selected - m_assetsModel->setSearchText(m_filterText); - } else if (m_stackedWidget->currentIndex() == 2) { // QML imports tab selected - m_itemLibraryAddImportModel->setSearchText(m_filterText); - } + m_itemLibraryModel->setSearchText(m_filterText); + m_itemsWidget->update(); + m_addModuleModel->setSearchText(m_filterText); } void ItemLibraryWidget::handlePriorityImportsChanged() { if (!m_itemLibraryInfo.isNull()) { - m_itemLibraryAddImportModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); - m_itemLibraryAddImportModel->update(m_model->possibleImports()); + m_addModuleModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); + m_addModuleModel->update(m_model->possibleImports()); } } -void ItemLibraryWidget::setResourcePath(const QString &resourcePath) -{ - m_assetsModel->setRootPath(resourcePath); - updateSearch(); -} - void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos) { // Actual drag is created after mouse has moved to avoid a QDrag bug that causes drag to stay @@ -539,54 +372,11 @@ void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry, const QPo m_dragStartPoint = mousePos.toPoint(); } -void ItemLibraryWidget::startDragAsset(const QStringList &assetPaths, const QPointF &mousePos) -{ - // Actual drag is created after mouse has moved to avoid a QDrag bug that causes drag to stay - // active (and blocks mouse release) if mouse is released at the same spot of the drag start. - m_assetsToDrag = assetPaths; - m_dragStartPoint = mousePos.toPoint(); -} - -QPair<QString, QByteArray> ItemLibraryWidget::getAssetTypeAndData(const QString &assetPath) -{ - QString suffix = "*." + assetPath.split('.').last().toLower(); - if (!suffix.isEmpty()) { - if (ItemLibraryAssetsModel::supportedImageSuffixes().contains(suffix)) { - // Data: Image format (suffix) - return {"application/vnd.bauhaus.libraryresource.image", suffix.toUtf8()}; - } else if (ItemLibraryAssetsModel::supportedFontSuffixes().contains(suffix)) { - // Data: Font family name - QRawFont font(assetPath, 10); - QString fontFamily = font.isValid() ? font.familyName() : ""; - return {"application/vnd.bauhaus.libraryresource.font", fontFamily.toUtf8()}; - } else if (ItemLibraryAssetsModel::supportedShaderSuffixes().contains(suffix)) { - // Data: shader type, frament (f) or vertex (v) - return {"application/vnd.bauhaus.libraryresource.shader", - ItemLibraryAssetsModel::supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"}; - } else if (ItemLibraryAssetsModel::supportedAudioSuffixes().contains(suffix)) { - // No extra data for sounds - return {"application/vnd.bauhaus.libraryresource.sound", {}}; - } else if (ItemLibraryAssetsModel::supportedVideoSuffixes().contains(suffix)) { - // No extra data for videos - return {"application/vnd.bauhaus.libraryresource.video", {}}; - } else if (ItemLibraryAssetsModel::supportedTexture3DSuffixes().contains(suffix)) { - // Data: Image format (suffix) - return {"application/vnd.bauhaus.libraryresource.texture3d", suffix.toUtf8()}; - } - } - return {}; -} - bool ItemLibraryWidget::subCompEditMode() const { return m_subCompEditMode; } -bool ItemLibraryWidget::searchActive() const -{ - return !m_filterText.isEmpty(); -} - void ItemLibraryWidget::setFlowMode(bool b) { m_itemLibraryModel->setFlowMode(b); @@ -608,111 +398,8 @@ void ItemLibraryWidget::addImportForItem(const QString &importUrl) QTC_ASSERT(m_itemLibraryModel, return); QTC_ASSERT(m_model, return); - Import import = m_itemLibraryAddImportModel->getImport(importUrl); + Import import = m_addModuleModel->getImport(importUrl); m_model->changeImports({import}, {}); } -static QHash<QByteArray, QStringList> allImageFormats() -{ - const QList<QByteArray> mimeTypes = QImageReader::supportedMimeTypes(); - auto transformer = [](const QByteArray& format) -> QString { return QString("*.") + format; }; - QHash<QByteArray, QStringList> imageFormats; - for (const auto &mimeType : mimeTypes) - imageFormats.insert(mimeType, Utils::transform(QImageReader::imageFormatsForMimeType(mimeType), transformer)); - imageFormats.insert("image/vnd.radiance", {"*.hdr"}); - imageFormats.insert("image/ktx", {"*.ktx"}); - - return imageFormats; -} - -void ItemLibraryWidget::addResources(const QStringList &files) -{ - DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); - - QTC_ASSERT(document, return); - - const QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager() - .designerActionManager().addResourceHandler(); - - QStringList fileNames = files; - if (fileNames.isEmpty()) { // if no files, show the "add assets" dialog - QMultiMap<QString, QString> map; - QHash<QString, int> priorities; - for (const AddResourceHandler &handler : handlers) { - map.insert(handler.category, handler.filter); - priorities.insert(handler.category, handler.piority); - } - - QStringList sortedKeys = map.uniqueKeys(); - Utils::sort(sortedKeys, [&priorities](const QString &first, const QString &second) { - return priorities.value(first) < priorities.value(second); - }); - - QStringList filters { tr("All Files (%1)").arg("*.*") }; - QString filterTemplate = "%1 (%2)"; - for (const QString &key : qAsConst(sortedKeys)) { - const QStringList values = map.values(key); - if (values.contains("*.png")) { // Avoid long filter for images by splitting - const QHash<QByteArray, QStringList> imageFormats = allImageFormats(); - QHash<QByteArray, QStringList>::const_iterator i = imageFormats.constBegin(); - while (i != imageFormats.constEnd()) { - filters.append(filterTemplate.arg(key + QString::fromLatin1(i.key()), i.value().join(' '))); - ++i; - } - } else { - filters.append(filterTemplate.arg(key, values.join(' '))); - } - } - - static QString lastDir; - const QString currentDir = lastDir.isEmpty() ? document->fileName().parentDir().toString() : lastDir; - - fileNames = QFileDialog::getOpenFileNames(Core::ICore::dialogParent(), - tr("Add Assets"), - currentDir, - filters.join(";;")); - - if (!fileNames.isEmpty()) { - lastDir = QFileInfo(fileNames.first()).absolutePath(); - // switch to assets view after an asset is added - m_stackedWidget->setCurrentIndex(1); - QMetaObject::invokeMethod(m_headerWidget->rootObject(), "setTab", Q_ARG(QVariant, 1)); - } - } - - QHash<QString, QString> filterToCategory; - QHash<QString, AddResourceOperation> categoryToOperation; - for (const AddResourceHandler &handler : handlers) { - filterToCategory.insert(handler.filter, handler.category); - categoryToOperation.insert(handler.category, handler.operation); - } - - QMultiMap<QString, QString> categoryFileNames; // filenames grouped by category - - for (const QString &fileName : qAsConst(fileNames)) { - const QString suffix = "*." + QFileInfo(fileName).suffix().toLower(); - const QString category = filterToCategory.value(suffix); - categoryFileNames.insert(category, fileName); - } - - for (const QString &category : categoryFileNames.uniqueKeys()) { - QStringList fileNames = categoryFileNames.values(category); - AddResourceOperation operation = categoryToOperation.value(category); - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_RESOURCE_IMPORTED + category); - if (operation) { - AddFilesResult result = operation(fileNames, - document->fileName().parentDir().toString()); - if (result == AddFilesResult::Failed) { - Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), - tr("Could not add %1 to project.") - .arg(fileNames.join(' '))); - } - } else { - Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), - tr("Could not add %1 to project. Unsupported file format.") - .arg(fileNames.join(' '))); - } - } -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index d67c3df71d..85b95ce23f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -31,7 +31,6 @@ #include <utils/fancylineedit.h> #include <utils/dropsupport.h> #include <previewtooltip/previewtooltipbackend.h> -#include "itemlibraryassetsmodel.h" #include <QFrame> #include <QToolButton> @@ -48,18 +47,13 @@ class QStackedWidget; class QShortcut; QT_END_NAMESPACE -namespace Utils { class FileSystemWatcher; } - namespace QmlDesigner { class MetaInfo; class ItemLibraryEntry; class Model; -class CustomFileSystemModel; class ItemLibraryModel; -class ItemLibraryAssetsIconProvider; -class ItemLibraryAssetsModel; class ItemLibraryAddImportModel; class ItemLibraryResourceView; class SynchronousImageCache; @@ -72,7 +66,6 @@ class ItemLibraryWidget : public QFrame public: Q_PROPERTY(bool subCompEditMode READ subCompEditMode NOTIFY subCompEditModeChanged) - Q_PROPERTY(bool searchActive READ searchActive NOTIFY searchActiveChanged) ItemLibraryWidget(AsynchronousImageCache &imageCache, AsynchronousImageCache &asynchronousFontImageCache, @@ -90,32 +83,22 @@ public: void updatePossibleImports(const QList<Import> &possibleImports); void updateUsedImports(const QList<Import> &usedImports); - void setResourcePath(const QString &resourcePath); void setModel(Model *model); void setFlowMode(bool b); - static QPair<QString, QByteArray> getAssetTypeAndData(const QString &assetPath); inline static bool isHorizontalLayout = false; bool subCompEditMode() const; - bool searchActive() const; Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos); - Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos); Q_INVOKABLE void removeImport(const QString &importUrl); Q_INVOKABLE void addImportForItem(const QString &importUrl); - Q_INVOKABLE void handleTabChanged(int index); - Q_INVOKABLE void handleAddModule(); - Q_INVOKABLE void handleAddAsset(); Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText); Q_INVOKABLE void handleAddImport(int index); - Q_INVOKABLE void handleFilesDrop(const QStringList &filesPaths); - Q_INVOKABLE QSet<QString> supportedDropSuffixes(); signals: void itemActivated(const QString &itemName); void subCompEditModeChanged(); - void searchActiveChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -124,38 +107,25 @@ protected: private: void reloadQmlSource(); - void addResources(const QStringList &files); void updateSearch(); void handlePriorityImportsChanged(); QTimer m_compressionTimer; - QTimer m_assetCompressionTimer; QSize m_itemIconSize; SynchronousImageCache &m_fontImageCache; QPointer<ItemLibraryInfo> m_itemLibraryInfo; QPointer<ItemLibraryModel> m_itemLibraryModel; - QPointer<ItemLibraryAddImportModel> m_itemLibraryAddImportModel; - ItemLibraryAssetsIconProvider *m_assetsIconProvider = nullptr; - Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr; - QPointer<ItemLibraryAssetsModel> m_assetsModel; - - QPointer<QStackedWidget> m_stackedWidget; + QPointer<ItemLibraryAddImportModel> m_addModuleModel; - QScopedPointer<QQuickWidget> m_headerWidget; - QScopedPointer<QQuickWidget> m_addImportWidget; - QScopedPointer<QQuickWidget> m_itemViewQuickWidget; - QScopedPointer<QQuickWidget> m_assetsWidget; + QScopedPointer<QQuickWidget> m_itemsWidget; std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend; - std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut; AsynchronousImageCache &m_imageCache; QPointer<Model> m_model; QVariant m_itemToDrag; - QStringList m_assetsToDrag; - QPair<QString, QByteArray> m_assetToDragTypeAndData; bool m_updateRetry = false; QString m_filterText; QPoint m_dragStartPoint; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 8a8547a84a..5e22fcec42 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -28,7 +28,7 @@ #include "navigatorwidget.h" #include "choosefrompropertylistdialog.h" #include "qmldesignerplugin.h" -#include "itemlibrarywidget.h" +#include "assetslibrarywidget.h" #include <bindingproperty.h> #include <designersettings.h> @@ -568,7 +568,7 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, if (document && !document->inFileComponentModelActive()) { QSet<QString> neededImports; for (const QString &assetPath : assetsPaths) { - QString assetType = ItemLibraryWidget::getAssetTypeAndData(assetPath).first; + QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; if (assetType == "application/vnd.bauhaus.libraryresource.shader") neededImports.insert("QtQuick3D"); else if (assetType == "application/vnd.bauhaus.libraryresource.sound") @@ -584,7 +584,7 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, m_view->executeInTransaction("NavigatorTreeModel::dropMimeData", [&] { for (const QString &assetPath : assetsPaths) { - auto assetTypeAndData = ItemLibraryWidget::getAssetTypeAndData(assetPath); + auto assetTypeAndData = AssetsLibraryWidget::getAssetTypeAndData(assetPath); QString assetType = assetTypeAndData.first; QString assetData = QString::fromUtf8(assetTypeAndData.second); if (assetType == "application/vnd.bauhaus.libraryresource.image") diff --git a/src/plugins/qmldesigner/designercore/include/viewmanager.h b/src/plugins/qmldesigner/designercore/include/viewmanager.h index ecfec36860..04f25b84f8 100644 --- a/src/plugins/qmldesigner/designercore/include/viewmanager.h +++ b/src/plugins/qmldesigner/designercore/include/viewmanager.h @@ -64,7 +64,6 @@ public: void attachViewsExceptRewriterAndComponetView(); void detachViewsExceptRewriterAndComponetView(); - void setItemLibraryViewResourcePath(const QString &resourcePath); void setComponentNode(const ModelNode &componentNode); void setComponentViewToMaster(); void setNodeInstanceViewTarget(ProjectExplorer::Target *target); @@ -116,7 +115,6 @@ private: // functions void addView(std::unique_ptr<AbstractView> &&view); void attachNodeInstanceView(); - void attachItemLibraryView(); void attachAdditionalViews(); void detachAdditionalViews(); void detachStandardViews(); diff --git a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp index 127925df1b..a2565d0c41 100644 --- a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp +++ b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp @@ -38,6 +38,7 @@ #include <edit3dview.h> #include <formeditorview.h> #include <itemlibraryview.h> +#include <assetslibraryview.h> #include <navigatorview.h> #include <nodeinstanceview.h> #include <propertyeditorview.h> @@ -71,6 +72,7 @@ public: Edit3DView edit3DView; FormEditorView formEditorView; TextEditorView textEditorView; + AssetsLibraryView assetsLibraryView; ItemLibraryView itemLibraryView; NavigatorView navigatorView; PropertyEditorView propertyEditorView; @@ -177,6 +179,7 @@ QList<AbstractView *> ViewManager::standardViews() const QList<AbstractView *> list = {&d->edit3DView, &d->formEditorView, &d->textEditorView, + &d->assetsLibraryView, &d->itemLibraryView, &d->navigatorView, &d->propertyEditorView, @@ -210,11 +213,6 @@ void ViewManager::detachViewsExceptRewriterAndComponetView() currentModel()->setNodeInstanceView(nullptr); } -void ViewManager::attachItemLibraryView() -{ - currentModel()->attachView(&d->itemLibraryView); -} - void ViewManager::attachAdditionalViews() { for (auto &view : d->additionalViews) @@ -292,11 +290,6 @@ void ViewManager::attachViewsExceptRewriterAndComponetView() switchStateEditorViewToSavedState(); } -void ViewManager::setItemLibraryViewResourcePath(const QString &resourcePath) -{ - d->itemLibraryView.setResourcePath(resourcePath); -} - void ViewManager::setComponentNode(const ModelNode &componentNode) { d->componentView.setComponentNode(componentNode); @@ -319,6 +312,7 @@ QList<WidgetInfo> ViewManager::widgetInfos() const widgetInfoList.append(d->edit3DView.widgetInfo()); widgetInfoList.append(d->formEditorView.widgetInfo()); widgetInfoList.append(d->textEditorView.widgetInfo()); + widgetInfoList.append(d->assetsLibraryView.widgetInfo()); widgetInfoList.append(d->itemLibraryView.widgetInfo()); widgetInfoList.append(d->navigatorView.widgetInfo()); widgetInfoList.append(d->propertyEditorView.widgetInfo()); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index e8bd9e68a0..a3f5649680 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -45,6 +45,7 @@ Project { "../../../share/qtcreator/qml/qmlpuppet/types", "components", "components/annotationeditor", + "components/assetslibrary", "components/componentcore", "components/curveeditor", "components/connectioneditor", @@ -449,6 +450,21 @@ Project { Group { prefix: "components/" files: [ + "assetslibrary/assetslibrary.qrc", + "assetslibrary/assetslibraryview.cpp", + "assetslibrary/assetslibraryview.h", + "assetslibrary/assetslibrarywidget.cpp", + "assetslibrary/assetslibrarywidget.h", + "assetslibrary/assetslibrarymodel.cpp", + "assetslibrary/assetslibrarymodel.h", + "assetslibrary/assetslibraryiconprovider.cpp", + "assetslibrary/assetslibraryiconprovider.h", + "assetslibrary/assetslibrarydir.cpp", + "assetslibrary/assetslibrarydir.h", + "assetslibrary/assetslibrarydirsmodel.cpp", + "assetslibrary/assetslibrarydirsmodel.h", + "assetslibrary/assetslibraryfilesmodel.cpp", + "assetslibrary/assetslibraryfilesmodel.h", "componentcore/addimagesdialog.cpp", "componentcore/addimagesdialog.h", "componentcore/abstractaction.cpp", @@ -472,7 +488,7 @@ Project { "componentcore/formatoperation.h", "componentcore/layoutingridlayout.cpp", "componentcore/layoutingridlayout.h", - "componentcore/theme.cpp", + "componentcore/theme.cpp", "componentcore/theme.h", "componentcore/modelnodecontextmenu.cpp", "componentcore/modelnodecontextmenu.h", @@ -639,16 +655,6 @@ Project { "itemlibrary/itemlibraryassetimportdialog.ui", "itemlibrary/itemlibraryassetimporter.cpp", "itemlibrary/itemlibraryassetimporter.h", - "itemlibrary/itemlibraryassetsdir.cpp", - "itemlibrary/itemlibraryassetsdir.h", - "itemlibrary/itemlibraryassetsdirsmodel.cpp", - "itemlibrary/itemlibraryassetsdirsmodel.h", - "itemlibrary/itemlibraryassetsfilesmodel.cpp", - "itemlibrary/itemlibraryassetsfilesmodel.h", - "itemlibrary/itemlibraryassetsiconprovider.cpp", - "itemlibrary/itemlibraryassetsiconprovider.h", - "itemlibrary/itemlibraryassetsmodel.cpp", - "itemlibrary/itemlibraryassetsmodel.h", "itemlibrary/itemlibrarycategoriesmodel.cpp", "itemlibrary/itemlibrarycategoriesmodel.h", "itemlibrary/itemlibrarycategory.cpp", |