QmlDesigner: Allow removing a content library material

Fixes: QDS-12541
Change-Id: I8efdd5c5f6185961bd8440e06d0adb60ad9d79f2
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Mahmoud Badri
2024-04-25 11:59:01 +03:00
parent 9b4aa051d4
commit 17a28ea850
10 changed files with 140 additions and 7 deletions

View File

@@ -130,10 +130,14 @@ Item {
} }
} }
UnimportBundleMaterialDialog { UnimportBundleItemDialog {
id: confirmUnimportDialog id: confirmUnimportDialog
} }
DeleteBundleItemDialog {
id: confirmDeleteDialog
}
StackLayout { StackLayout {
id: stackLayout id: stackLayout
width: root.width width: root.width
@@ -246,6 +250,12 @@ Item {
confirmUnimportDialog.open() confirmUnimportDialog.open()
} }
onRemoveFromContentLib: (bundleItem) => {
confirmDeleteDialog.targetBundleItem = bundleItem
confirmDeleteDialog.targetBundleLabel = "material"
confirmDeleteDialog.open()
}
onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height)
} }
} }

View File

@@ -12,12 +12,14 @@ StudioControls.Menu {
property var targetMaterial: null property var targetMaterial: null
property bool hasModelSelection: false property bool hasModelSelection: false
property bool importerRunning: false property bool importerRunning: false
property bool enableRemove: false // true: adds an option to remove targetMaterial
readonly property bool targetAvailable: targetMaterial && !importerRunning readonly property bool targetAvailable: targetMaterial && !importerRunning
signal unimport(); signal unimport();
signal addToProject() signal addToProject()
signal applyToSelected(bool add) signal applyToSelected(bool add)
signal removeFromContentLib()
function popupMenu(targetMaterial = null) function popupMenu(targetMaterial = null)
{ {
@@ -56,4 +58,11 @@ StudioControls.Menu {
onTriggered: root.unimport() onTriggered: root.unimport()
} }
StudioControls.MenuItem {
text: qsTr("Remove from Content Library")
visible: root.enableRemove && root.targetAvailable
height: visible ? implicitHeight : 0
onTriggered: root.removeFromContentLib()
}
} }

View File

@@ -31,6 +31,7 @@ HelperWidgets.ScrollView {
required property var searchBox required property var searchBox
signal unimport(var bundleItem); signal unimport(var bundleItem);
signal removeFromContentLib(var bundleItem);
function closeContextMenu() { function closeContextMenu() {
ctxMenuMaterial.close() ctxMenuMaterial.close()
@@ -49,6 +50,7 @@ HelperWidgets.ScrollView {
ContentLibraryMaterialContextMenu { ContentLibraryMaterialContextMenu {
id: ctxMenuMaterial id: ctxMenuMaterial
enableRemove: true
hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection
importerRunning: ContentLibraryBackend.userModel.importerRunning importerRunning: ContentLibraryBackend.userModel.importerRunning
@@ -56,6 +58,7 @@ HelperWidgets.ScrollView {
onUnimport: root.unimport(ctxMenuMaterial.targetMaterial) onUnimport: root.unimport(ctxMenuMaterial.targetMaterial)
onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuMaterial.targetMaterial) onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuMaterial.targetMaterial)
onRemoveFromContentLib: root.removeFromContentLib(ctxMenuMaterial.targetMaterial)
} }
ContentLibraryTextureContextMenu { ContentLibraryTextureContextMenu {

View File

@@ -0,0 +1,65 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
import ContentLibraryBackend
StudioControls.Dialog {
id: root
property var targetBundleItem
property var targetBundleModel
property string targetBundleLabel // "effect" or "material"
title: qsTr("Remove bundle %1").arg(root.targetBundleLabel)
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
implicitWidth: 300
modal: true
onOpened: warningText.forceActiveFocus()
contentItem: Column {
spacing: 20
width: parent.width
Text {
id: warningText
text: qsTr("Are you sure you? The action cannot be undone")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
anchors.right: parent.right
anchors.left: parent.left
leftPadding: 10
rightPadding: 10
Keys.onEnterPressed: btnRemove.onClicked()
Keys.onReturnPressed: btnRemove.onClicked()
}
Row {
anchors.right: parent.right
Button {
id: btnRemove
text: qsTr("Remove")
onClicked: {
ContentLibraryBackend.userModel.removeFromContentLib(root.targetBundleItem)
root.accept()
}
}
Button {
text: qsTr("Cancel")
onClicked: root.reject()
}
}
}
}

View File

@@ -13,8 +13,8 @@ StudioControls.Dialog {
id: root id: root
property var targetBundleItem property var targetBundleItem
property var targetBundleLabel // "effect" or "material"
property var targetBundleModel property var targetBundleModel
property string targetBundleLabel // "effect" or "material"
title: qsTr("Bundle %1 might be in use").arg(root.targetBundleLabel) title: qsTr("Bundle %1 might be in use").arg(root.targetBundleLabel)
anchors.centerIn: parent anchors.centerIn: parent

View File

@@ -32,6 +32,11 @@ bool ContentLibraryMaterial::filter(const QString &searchText)
return m_visible; return m_visible;
} }
QString ContentLibraryMaterial::name() const
{
return m_name;
}
QUrl ContentLibraryMaterial::icon() const QUrl ContentLibraryMaterial::icon() const
{ {
return m_icon; return m_icon;

View File

@@ -37,6 +37,7 @@ public:
Q_INVOKABLE bool isDownloaded() const; Q_INVOKABLE bool isDownloaded() const;
QString name() const;
QUrl icon() const; QUrl icon() const;
QString qml() const; QString qml() const;
TypeName type() const; TypeName type() const;

View File

@@ -33,9 +33,6 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent)
, m_widget(parent) , m_widget(parent)
{ {
m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO
loadMaterialBundle();
loadTextureBundle();
} }
int ContentLibraryUserModel::rowCount(const QModelIndex &) const int ContentLibraryUserModel::rowCount(const QModelIndex &) const
@@ -136,6 +133,44 @@ void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex)
emit dataChanged(index(texSectionIdx), index(texSectionIdx)); emit dataChanged(index(texSectionIdx), index(texSectionIdx));
} }
void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat)
{
auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/");
QJsonObject matsObj = m_bundleObj.value("materials").toObject();
// remove qml and icon files
Utils::FilePath::fromString(mat->qmlFilePath()).removeFile();
Utils::FilePath::fromUrl(mat->icon()).removeFile();
// remove from the bundle json file
matsObj.remove(mat->name());
m_bundleObj.insert("materials", matsObj);
auto result = bundlePath.pathAppended("user_materials_bundle.json")
.writeFileContents(QJsonDocument(m_bundleObj).toJson());
if (!result)
qWarning() << __FUNCTION__ << result.error();
// delete dependency files if they are only used by the deleted material
QStringList allFiles;
for (const QJsonValueConstRef &mat : std::as_const(matsObj))
allFiles.append(mat.toObject().value("files").toVariant().toStringList());
const QStringList matFiles = mat->files();
for (const QString &matFile : matFiles) {
if (allFiles.count(matFile) == 0) // only used by the deleted material
bundlePath.pathAppended(matFile).removeFile();
}
// remove from model
m_userMaterials.removeOne(mat);
mat->deleteLater();
// update model
int matSectionIdx = 0;
emit dataChanged(index(matSectionIdx), index(matSectionIdx));
}
// returns unique library material's name and qml component // returns unique library material's name and qml component
QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const
{ {

View File

@@ -67,12 +67,16 @@ public:
void setBundleObj(const QJsonObject &newBundleObj); void setBundleObj(const QJsonObject &newBundleObj);
QJsonObject &bundleJsonObjectRef(); QJsonObject &bundleJsonObjectRef();
void loadMaterialBundle();
void loadTextureBundle();
Internal::ContentLibraryBundleImporter *bundleImporter() const; Internal::ContentLibraryBundleImporter *bundleImporter() const;
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat);
Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat);
Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex);
Q_INVOKABLE void removeFromContentLib(QmlDesigner::ContentLibraryMaterial *mat);
signals: signals:
void isEmptyChanged(); void isEmptyChanged();
@@ -96,8 +100,6 @@ signals:
void matBundleExistsChanged(); void matBundleExistsChanged();
private: private:
void loadMaterialBundle();
void loadTextureBundle();
bool isValidIndex(int idx) const; bool isValidIndex(int idx) const;
void createImporter(const QString &bundlePath, const QString &bundleId, void createImporter(const QString &bundlePath, const QString &bundleId,
const QStringList &sharedFiles); const QStringList &sharedFiles);

View File

@@ -304,6 +304,9 @@ void ContentLibraryView::modelAttached(Model *model)
m_widget->effectsModel()->loadBundle(); m_widget->effectsModel()->loadBundle();
updateBundleEffectsImportedState(); updateBundleEffectsImportedState();
m_widget->userModel()->loadMaterialBundle();
m_widget->userModel()->loadTextureBundle();
} }
void ContentLibraryView::modelAboutToBeDetached(Model *model) void ContentLibraryView::modelAboutToBeDetached(Model *model)