forked from qt-creator/qt-creator
QmlDesigner: Implement Texture Editor view
Fixes: QDS-8209 Change-Id: Ief0c9f56da79841c745595024dbcd9219072b681 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
@@ -42,5 +42,6 @@ Rectangle {
|
|||||||
sourceSize.height: root.height - 10
|
sourceSize.height: root.height - 10
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
cache: false
|
cache: false
|
||||||
|
smooth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ Column {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
source: "image://materialEditor/preview"
|
source: "image://materialEditor/preview"
|
||||||
cache: false
|
cache: false
|
||||||
|
smooth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import HelperWidgets 2.0
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
PropertyEditorPane {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal toolBarAction(int action)
|
||||||
|
|
||||||
|
// Called from C++, dummy method to avoid warnings
|
||||||
|
function closeContextMenu() {}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: col
|
||||||
|
|
||||||
|
TextureEditorToolBar {
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
onToolBarAction: (action) => root.toolBarAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: root.width - 2 * col.padding
|
||||||
|
height: 150
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: {
|
||||||
|
if (!hasQuick3DImport)
|
||||||
|
qsTr("To use <b>Texture Editor</b>, first add the QtQuick3D module in the <b>Components</b> view.")
|
||||||
|
else if (!hasMaterialLibrary)
|
||||||
|
qsTr("<b>Texture Editor</b> is disabled inside a non-visual component.")
|
||||||
|
else
|
||||||
|
qsTr("There are no textures in this project.<br>Select '<b>+</b>' to create one.")
|
||||||
|
}
|
||||||
|
textFormat: Text.RichText
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: root.width
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import HelperWidgets 2.0
|
||||||
|
|
||||||
|
PropertyEditorPane {
|
||||||
|
id: itemPane
|
||||||
|
|
||||||
|
signal toolBarAction(int action)
|
||||||
|
|
||||||
|
// invoked from C++ to refresh material preview image
|
||||||
|
function refreshPreview()
|
||||||
|
{
|
||||||
|
topSection.refreshPreview()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called also from C++ to close context menu on focus out
|
||||||
|
function closeContextMenu()
|
||||||
|
{
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEditorTopSection {
|
||||||
|
id: topSection
|
||||||
|
|
||||||
|
onToolBarAction: (action) => itemPane.toolBarAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 1; height: 10 }
|
||||||
|
|
||||||
|
DynamicPropertiesSection {
|
||||||
|
propertiesModel: TextureEditorDynamicPropertiesModel {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: specificsTwo
|
||||||
|
|
||||||
|
property string theSource: specificQmlData
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
visible: theSource !== ""
|
||||||
|
sourceComponent: specificQmlComponent
|
||||||
|
|
||||||
|
onTheSourceChanged: {
|
||||||
|
active = false
|
||||||
|
active = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: 10
|
||||||
|
visible: specificsTwo.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: specificsOne
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
source: specificsUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import HelperWidgets 2.0
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
import ToolBarAction 1.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
color: StudioTheme.Values.themeSectionHeadBackground
|
||||||
|
width: row.width
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
signal toolBarAction(int action)
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
leftPadding: 6
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: StudioTheme.Constants.applyMaterialToSelected
|
||||||
|
|
||||||
|
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasTexture && hasModelSelection && hasQuick3DImport && hasMaterialLibrary
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
|
||||||
|
tooltip: qsTr("Apply texture to selected model's material.")
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: StudioTheme.Constants.newMaterial
|
||||||
|
|
||||||
|
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasQuick3DImport && hasMaterialLibrary
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.AddNewTexture)
|
||||||
|
tooltip: qsTr("Create new texture.")
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: StudioTheme.Constants.deleteMaterial
|
||||||
|
|
||||||
|
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasTexture && hasQuick3DImport && hasMaterialLibrary
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentTexture)
|
||||||
|
tooltip: qsTr("Delete current texture.")
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: StudioTheme.Constants.openMaterialBrowser
|
||||||
|
|
||||||
|
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasQuick3DImport && hasMaterialLibrary
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
|
||||||
|
tooltip: qsTr("Open material browser.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal toolBarAction(int action)
|
||||||
|
|
||||||
|
function refreshPreview()
|
||||||
|
{
|
||||||
|
texturePreview.source = ""
|
||||||
|
texturePreview.source = "image://textureEditor/" + backendValues.source.valueToString
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
TextureEditorToolBar {
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
onToolBarAction: (action) => root.toolBarAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 1; height: 10 } // spacer
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: previewRect
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: 152
|
||||||
|
height: 152
|
||||||
|
color: "#000000"
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: texturePreview
|
||||||
|
|
||||||
|
sourceSize.width: 150
|
||||||
|
sourceSize.height: 150
|
||||||
|
anchors.centerIn: parent
|
||||||
|
source: "image://textureEditor/" + backendValues.source.valueToString
|
||||||
|
cache: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -410,6 +410,7 @@ add_qtc_plugin(QmlDesigner
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/components/itemlibrary
|
${CMAKE_CURRENT_LIST_DIR}/components/itemlibrary
|
||||||
${CMAKE_CURRENT_LIST_DIR}/components/materialbrowser
|
${CMAKE_CURRENT_LIST_DIR}/components/materialbrowser
|
||||||
${CMAKE_CURRENT_LIST_DIR}/components/materialeditor
|
${CMAKE_CURRENT_LIST_DIR}/components/materialeditor
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/components/textureeditor
|
||||||
${CMAKE_CURRENT_LIST_DIR}/components/navigator
|
${CMAKE_CURRENT_LIST_DIR}/components/navigator
|
||||||
${CMAKE_CURRENT_LIST_DIR}/components/propertyeditor
|
${CMAKE_CURRENT_LIST_DIR}/components/propertyeditor
|
||||||
${CMAKE_CURRENT_LIST_DIR}/components/stateseditor
|
${CMAKE_CURRENT_LIST_DIR}/components/stateseditor
|
||||||
@@ -814,6 +815,17 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
materialeditor.qrc
|
materialeditor.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
SOURCES_PREFIX components/textureeditor
|
||||||
|
SOURCES
|
||||||
|
textureeditorcontextobject.cpp textureeditorcontextobject.h
|
||||||
|
textureeditordynamicpropertiesproxymodel.cpp textureeditordynamicpropertiesproxymodel.h
|
||||||
|
textureeditorqmlbackend.cpp textureeditorqmlbackend.h
|
||||||
|
textureeditortransaction.cpp textureeditortransaction.h
|
||||||
|
textureeditorview.cpp textureeditorview.h
|
||||||
|
textureeditor.qrc
|
||||||
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(QmlDesigner
|
extend_qtc_plugin(QmlDesigner
|
||||||
SOURCES_PREFIX components/materialbrowser
|
SOURCES_PREFIX components/materialbrowser
|
||||||
SOURCES
|
SOURCES
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <stateseditornew/stateseditorview.h>
|
#include <stateseditornew/stateseditorview.h>
|
||||||
#include <stateseditorview.h>
|
#include <stateseditorview.h>
|
||||||
#include <texteditorview.h>
|
#include <texteditorview.h>
|
||||||
|
#include <textureeditorview.h>
|
||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
@@ -69,6 +70,7 @@ public:
|
|||||||
, propertyEditorView(imageCache, externalDependencies)
|
, propertyEditorView(imageCache, externalDependencies)
|
||||||
, materialEditorView{externalDependencies}
|
, materialEditorView{externalDependencies}
|
||||||
, materialBrowserView{externalDependencies}
|
, materialBrowserView{externalDependencies}
|
||||||
|
, textureEditorView{externalDependencies}
|
||||||
, statesEditorView{externalDependencies}
|
, statesEditorView{externalDependencies}
|
||||||
, newStatesEditorView{externalDependencies}
|
, newStatesEditorView{externalDependencies}
|
||||||
{}
|
{}
|
||||||
@@ -90,6 +92,7 @@ public:
|
|||||||
PropertyEditorView propertyEditorView;
|
PropertyEditorView propertyEditorView;
|
||||||
MaterialEditorView materialEditorView;
|
MaterialEditorView materialEditorView;
|
||||||
MaterialBrowserView materialBrowserView;
|
MaterialBrowserView materialBrowserView;
|
||||||
|
TextureEditorView textureEditorView;
|
||||||
StatesEditorView statesEditorView;
|
StatesEditorView statesEditorView;
|
||||||
Experimental::StatesEditorView newStatesEditorView;
|
Experimental::StatesEditorView newStatesEditorView;
|
||||||
|
|
||||||
@@ -219,6 +222,7 @@ QList<AbstractView *> ViewManager::standardViews() const
|
|||||||
&d->contentLibraryView,
|
&d->contentLibraryView,
|
||||||
&d->materialEditorView,
|
&d->materialEditorView,
|
||||||
&d->materialBrowserView,
|
&d->materialBrowserView,
|
||||||
|
&d->textureEditorView,
|
||||||
&d->statesEditorView,
|
&d->statesEditorView,
|
||||||
&d->newStatesEditorView, // TODO
|
&d->newStatesEditorView, // TODO
|
||||||
&d->designerActionManagerView};
|
&d->designerActionManagerView};
|
||||||
@@ -400,6 +404,7 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
|
|||||||
widgetInfoList.append(d->contentLibraryView.widgetInfo());
|
widgetInfoList.append(d->contentLibraryView.widgetInfo());
|
||||||
widgetInfoList.append(d->materialEditorView.widgetInfo());
|
widgetInfoList.append(d->materialEditorView.widgetInfo());
|
||||||
widgetInfoList.append(d->materialBrowserView.widgetInfo());
|
widgetInfoList.append(d->materialBrowserView.widgetInfo());
|
||||||
|
widgetInfoList.append(d->textureEditorView.widgetInfo());
|
||||||
if (useOldStatesEditor())
|
if (useOldStatesEditor())
|
||||||
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -274,6 +274,8 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view,
|
|||||||
{
|
{
|
||||||
if (identifier == "asset_import_update")
|
if (identifier == "asset_import_update")
|
||||||
resetPuppet();
|
resetPuppet();
|
||||||
|
else if (identifier == "apply_texture_to_model3D")
|
||||||
|
applyTextureToModel3D(nodeList.at(0), nodeList.at(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Edit3DView::eventFilter(QObject *obj, QEvent *event)
|
bool Edit3DView::eventFilter(QObject *obj, QEvent *event)
|
||||||
@@ -320,51 +322,7 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
|
|||||||
} else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) {
|
} else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) {
|
||||||
emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView
|
emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView
|
||||||
} else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) {
|
} else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) {
|
||||||
if (m_droppedModelNode.isValid() && modelNode.isValid() && modelNode.metaInfo().isQtQuick3DModel()) {
|
applyTextureToModel3D(modelNode, m_droppedModelNode);
|
||||||
// get model's material list
|
|
||||||
BindingProperty matsProp = modelNode.bindingProperty("materials");
|
|
||||||
QList<ModelNode> materials;
|
|
||||||
if (hasId(matsProp.expression()))
|
|
||||||
materials.append(modelNodeForId(matsProp.expression()));
|
|
||||||
else
|
|
||||||
materials = matsProp.resolveToModelNodeList();
|
|
||||||
|
|
||||||
if (materials.size() > 0) {
|
|
||||||
m_textureModels.clear();
|
|
||||||
QStringList materialsModel;
|
|
||||||
for (const ModelNode &mat : std::as_const(materials)) {
|
|
||||||
QString matName = mat.variantProperty("objectName").value().toString();
|
|
||||||
materialsModel.append(QLatin1String("%1 (%2)").arg(matName, mat.id()));
|
|
||||||
QList<PropertyName> texProps;
|
|
||||||
for (const PropertyMetaInfo &p : mat.metaInfo().properties()) {
|
|
||||||
if (p.propertyType().isQtQuick3DTexture())
|
|
||||||
texProps.append(p.name());
|
|
||||||
}
|
|
||||||
m_textureModels.insert(mat.id(), texProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString path = MaterialBrowserWidget::qmlSourcesPath() + "/ChooseMaterialProperty.qml";
|
|
||||||
|
|
||||||
m_chooseMatPropsView = new QQuickView;
|
|
||||||
m_chooseMatPropsView->setTitle(tr("Select a material property"));
|
|
||||||
m_chooseMatPropsView->setResizeMode(QQuickView::SizeRootObjectToView);
|
|
||||||
m_chooseMatPropsView->setMinimumSize({150, 100});
|
|
||||||
m_chooseMatPropsView->setMaximumSize({600, 400});
|
|
||||||
m_chooseMatPropsView->setWidth(450);
|
|
||||||
m_chooseMatPropsView->setHeight(300);
|
|
||||||
m_chooseMatPropsView->setFlags(Qt::Widget);
|
|
||||||
m_chooseMatPropsView->setModality(Qt::ApplicationModal);
|
|
||||||
m_chooseMatPropsView->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
|
||||||
m_chooseMatPropsView->rootContext()->setContextProperties({
|
|
||||||
{"rootView", QVariant::fromValue(this)},
|
|
||||||
{"materialsModel", QVariant::fromValue(materialsModel)},
|
|
||||||
{"propertiesModel", QVariant::fromValue(m_textureModels.value(materials.at(0).id()))},
|
|
||||||
});
|
|
||||||
m_chooseMatPropsView->setSource(QUrl::fromLocalFile(path));
|
|
||||||
m_chooseMatPropsView->installEventFilter(this);
|
|
||||||
m_chooseMatPropsView->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_nodeAtPosReqType != NodeAtPosReqType::TextureDrop)
|
if (m_nodeAtPosReqType != NodeAtPosReqType::TextureDrop)
|
||||||
@@ -372,6 +330,57 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
|
|||||||
m_nodeAtPosReqType = NodeAtPosReqType::None;
|
m_nodeAtPosReqType = NodeAtPosReqType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Edit3DView::applyTextureToModel3D(const ModelNode &model3D, const ModelNode &texture)
|
||||||
|
{
|
||||||
|
if (!texture.isValid() || !model3D.isValid() || !model3D.metaInfo().isQtQuick3DModel())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_droppedModelNode = texture;
|
||||||
|
|
||||||
|
// get model's material list
|
||||||
|
BindingProperty matsProp = model3D.bindingProperty("materials");
|
||||||
|
QList<ModelNode> materials;
|
||||||
|
if (hasId(matsProp.expression()))
|
||||||
|
materials.append(modelNodeForId(matsProp.expression()));
|
||||||
|
else
|
||||||
|
materials = matsProp.resolveToModelNodeList();
|
||||||
|
|
||||||
|
if (materials.size() > 0) {
|
||||||
|
m_textureModels.clear();
|
||||||
|
QStringList materialsModel;
|
||||||
|
for (const ModelNode &mat : std::as_const(materials)) {
|
||||||
|
QString matName = mat.variantProperty("objectName").value().toString();
|
||||||
|
materialsModel.append(QLatin1String("%1 (%2)").arg(matName, mat.id()));
|
||||||
|
QList<PropertyName> texProps;
|
||||||
|
for (const PropertyMetaInfo &p : mat.metaInfo().properties()) {
|
||||||
|
if (p.propertyType().isQtQuick3DTexture())
|
||||||
|
texProps.append(p.name());
|
||||||
|
}
|
||||||
|
m_textureModels.insert(mat.id(), texProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path = MaterialBrowserWidget::qmlSourcesPath() + "/ChooseMaterialProperty.qml";
|
||||||
|
|
||||||
|
m_chooseMatPropsView = new QQuickView;
|
||||||
|
m_chooseMatPropsView->setTitle(tr("Select a material property"));
|
||||||
|
m_chooseMatPropsView->setResizeMode(QQuickView::SizeRootObjectToView);
|
||||||
|
m_chooseMatPropsView->setMinimumSize({150, 100});
|
||||||
|
m_chooseMatPropsView->setMaximumSize({600, 400});
|
||||||
|
m_chooseMatPropsView->setWidth(450);
|
||||||
|
m_chooseMatPropsView->setHeight(300);
|
||||||
|
m_chooseMatPropsView->setFlags(Qt::Widget);
|
||||||
|
m_chooseMatPropsView->setModality(Qt::ApplicationModal);
|
||||||
|
m_chooseMatPropsView->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||||
|
m_chooseMatPropsView->rootContext()->setContextProperties({
|
||||||
|
{"rootView", QVariant::fromValue(this)},
|
||||||
|
{"materialsModel", QVariant::fromValue(materialsModel)},
|
||||||
|
{"propertiesModel", QVariant::fromValue(m_textureModels.value(materials.at(0).id()))},
|
||||||
|
});
|
||||||
|
m_chooseMatPropsView->setSource(QUrl::fromLocalFile(path));
|
||||||
|
m_chooseMatPropsView->installEventFilter(this);
|
||||||
|
m_chooseMatPropsView->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
void Edit3DView::sendInputEvent(QInputEvent *e) const
|
void Edit3DView::sendInputEvent(QInputEvent *e) const
|
||||||
{
|
{
|
||||||
if (nodeInstanceView())
|
if (nodeInstanceView())
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
void dropMaterial(const ModelNode &matNode, const QPointF &pos);
|
void dropMaterial(const ModelNode &matNode, const QPointF &pos);
|
||||||
void dropBundleMaterial(const QPointF &pos);
|
void dropBundleMaterial(const QPointF &pos);
|
||||||
void dropTexture(const ModelNode &textureNode, const QPointF &pos);
|
void dropTexture(const ModelNode &textureNode, const QPointF &pos);
|
||||||
|
void applyTextureToModel3D(const ModelNode &model3D, const ModelNode &texture);
|
||||||
|
|
||||||
Q_INVOKABLE void updatePropsModel(const QString &matId);
|
Q_INVOKABLE void updatePropsModel(const QString &matId);
|
||||||
Q_INVOKABLE void applyTextureToMaterial(const QString &matId, const QString &propName);
|
Q_INVOKABLE void applyTextureToMaterial(const QString &matId, const QString &propName);
|
||||||
|
|||||||
@@ -180,6 +180,20 @@ ModelNode MaterialBrowserTexturesModel::textureAt(int idx) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MaterialBrowserTexturesModel::hasSingleModelSelection() const
|
||||||
|
{
|
||||||
|
return m_hasSingleModelSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserTexturesModel::setHasSingleModelSelection(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasSingleModelSelection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasSingleModelSelection = b;
|
||||||
|
emit hasSingleModelSelectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::resetModel()
|
void MaterialBrowserTexturesModel::resetModel()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class MaterialBrowserTexturesModel : public QAbstractListModel
|
|||||||
|
|
||||||
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
||||||
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
|
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
|
||||||
|
Q_PROPERTY(bool hasSingleModelSelection READ hasSingleModelSelection
|
||||||
|
WRITE setHasSingleModelSelection NOTIFY hasSingleModelSelectionChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MaterialBrowserTexturesModel(QObject *parent = nullptr);
|
MaterialBrowserTexturesModel(QObject *parent = nullptr);
|
||||||
@@ -35,6 +37,9 @@ public:
|
|||||||
int textureIndex(const ModelNode &material) const;
|
int textureIndex(const ModelNode &material) const;
|
||||||
ModelNode textureAt(int idx) const;
|
ModelNode textureAt(int idx) const;
|
||||||
|
|
||||||
|
bool hasSingleModelSelection() const;
|
||||||
|
void setHasSingleModelSelection(bool b);
|
||||||
|
|
||||||
void resetModel();
|
void resetModel();
|
||||||
|
|
||||||
Q_INVOKABLE void selectTexture(int idx, bool force = false);
|
Q_INVOKABLE void selectTexture(int idx, bool force = false);
|
||||||
@@ -43,6 +48,7 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void isEmptyChanged();
|
void isEmptyChanged();
|
||||||
|
void hasSingleModelSelectionChanged();
|
||||||
void materialSectionsChanged();
|
void materialSectionsChanged();
|
||||||
void selectedIndexChanged(int idx);
|
void selectedIndexChanged(int idx);
|
||||||
void duplicateTextureTriggered(const QmlDesigner::ModelNode &material);
|
void duplicateTextureTriggered(const QmlDesigner::ModelNode &material);
|
||||||
@@ -58,6 +64,7 @@ private:
|
|||||||
|
|
||||||
int m_selectedIndex = 0;
|
int m_selectedIndex = 0;
|
||||||
bool m_isEmpty = true;
|
bool m_isEmpty = true;
|
||||||
|
bool m_hasSingleModelSelection = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
|||||||
auto matEditorContext = new Internal::MaterialBrowserContext(m_widget.data());
|
auto matEditorContext = new Internal::MaterialBrowserContext(m_widget.data());
|
||||||
Core::ICore::addContextObject(matEditorContext);
|
Core::ICore::addContextObject(matEditorContext);
|
||||||
|
|
||||||
MaterialBrowserModel *matBrowserModel = m_widget->materialBrowserModel().data();
|
|
||||||
|
|
||||||
// custom notifications below are sent to the MaterialEditor
|
// custom notifications below are sent to the MaterialEditor
|
||||||
|
MaterialBrowserModel *matBrowserModel = m_widget->materialBrowserModel().data();
|
||||||
|
|
||||||
connect(matBrowserModel, &MaterialBrowserModel::selectedIndexChanged, this, [&] (int idx) {
|
connect(matBrowserModel, &MaterialBrowserModel::selectedIndexChanged, this, [&] (int idx) {
|
||||||
ModelNode matNode = m_widget->materialBrowserModel()->materialAt(idx);
|
ModelNode matNode = m_widget->materialBrowserModel()->materialAt(idx);
|
||||||
@@ -139,6 +139,13 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// custom notifications below are sent to the TextureEditor
|
||||||
|
MaterialBrowserTexturesModel *texturesModel = m_widget->materialBrowserTexturesModel().data();
|
||||||
|
connect(texturesModel, &MaterialBrowserTexturesModel::selectedIndexChanged, this, [&] (int idx) {
|
||||||
|
ModelNode texNode = m_widget->materialBrowserTexturesModel()->textureAt(idx);
|
||||||
|
emitCustomNotification("selected_texture_changed", {texNode}, {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return createWidgetInfo(m_widget.data(),
|
return createWidgetInfo(m_widget.data(),
|
||||||
@@ -230,6 +237,7 @@ void MaterialBrowserView::selectedNodesChanged(const QList<ModelNode> &selectedN
|
|||||||
});
|
});
|
||||||
|
|
||||||
m_widget->materialBrowserModel()->setHasModelSelection(!m_selectedModels.isEmpty());
|
m_widget->materialBrowserModel()->setHasModelSelection(!m_selectedModels.isEmpty());
|
||||||
|
m_widget->materialBrowserTexturesModel()->setHasSingleModelSelection(m_selectedModels.size() == 1);
|
||||||
|
|
||||||
// the logic below selects the material of the first selected model if auto selection is on
|
// the logic below selects the material of the first selected model if auto selection is on
|
||||||
if (!m_autoSelectModelMaterial)
|
if (!m_autoSelectModelMaterial)
|
||||||
@@ -299,20 +307,21 @@ void MaterialBrowserView::nodeReparented(const ModelNode &node,
|
|||||||
|
|
||||||
void MaterialBrowserView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
void MaterialBrowserView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
||||||
{
|
{
|
||||||
// removing the material editor node
|
// removing the material lib node
|
||||||
if (removedNode.id() == Constants::MATERIAL_LIB_ID) {
|
if (removedNode.id() == Constants::MATERIAL_LIB_ID) {
|
||||||
m_widget->materialBrowserModel()->setMaterials({}, m_hasQuick3DImport);
|
m_widget->materialBrowserModel()->setMaterials({}, m_hasQuick3DImport);
|
||||||
m_widget->clearPreviewCache();
|
m_widget->clearPreviewCache();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not a material under the material editor
|
// not under the material lib
|
||||||
if (!isMaterial(removedNode)
|
if (removedNode.parentProperty().parentModelNode().id() != Constants::MATERIAL_LIB_ID)
|
||||||
|| removedNode.parentProperty().parentModelNode().id() != Constants::MATERIAL_LIB_ID) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
m_widget->materialBrowserModel()->removeMaterial(removedNode);
|
if (isMaterial(removedNode))
|
||||||
|
m_widget->materialBrowserModel()->removeMaterial(removedNode);
|
||||||
|
else if (isTexture(removedNode))
|
||||||
|
m_widget->materialBrowserTexturesModel()->removeTexture(removedNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode,
|
void MaterialBrowserView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode,
|
||||||
@@ -323,6 +332,7 @@ void MaterialBrowserView::nodeRemoved([[maybe_unused]] const ModelNode &removedN
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_widget->materialBrowserModel()->updateSelectedMaterial();
|
m_widget->materialBrowserModel()->updateSelectedMaterial();
|
||||||
|
m_widget->materialBrowserTexturesModel()->updateSelectedTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
|
void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public:
|
|||||||
pixmap = Utils::StyleHelper::dpiSpecificImageFile(id);
|
pixmap = Utils::StyleHelper::dpiSpecificImageFile(id);
|
||||||
|
|
||||||
if (pixmap.isNull())
|
if (pixmap.isNull())
|
||||||
pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/materialeditor/images/texture_default.png");
|
pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png");
|
||||||
|
|
||||||
if (size)
|
if (size)
|
||||||
*size = pixmap.size();
|
*size = pixmap.size();
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB |
@@ -1,7 +1,5 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/materialeditor">
|
<qresource prefix="/materialeditor">
|
||||||
<file>images/defaultmaterialpreview.png</file>
|
<file>images/defaultmaterialpreview.png</file>
|
||||||
<file>images/texture_default.png</file>
|
|
||||||
<file>images/texture_default@2x.png</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -825,6 +825,12 @@ void MaterialEditorView::variantPropertiesChanged(const QList<VariantProperty> &
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!changed && node.metaInfo().isQtQuick3DTexture()
|
||||||
|
&& m_selectedMaterial.bindingProperties().size() > 0) {
|
||||||
|
// update preview when editing texture properties if the material has binding properties
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
dynamicPropertiesModel()->dispatchPropertyChanges(property);
|
dynamicPropertiesModel()->dispatchPropertyChanges(property);
|
||||||
}
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/textureeditor">
|
||||||
|
<file>images/texture_default.png</file>
|
||||||
|
<file>images/texture_default@2x.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "textureeditorcontextobject.h"
|
||||||
|
|
||||||
|
#include "abstractview.h"
|
||||||
|
#include "documentmanager.h"
|
||||||
|
#include "model.h"
|
||||||
|
#include "qmldesignerplugin.h"
|
||||||
|
#include "qmlobjectnode.h"
|
||||||
|
#include "qmltimeline.h"
|
||||||
|
|
||||||
|
#include <coreplugin/messagebox.h>
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QCursor>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
TextureEditorContextObject::TextureEditorContextObject(QQmlContext *context, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_qmlContext(context)
|
||||||
|
{
|
||||||
|
qmlRegisterUncreatableType<TextureEditorContextObject>("ToolBarAction", 1, 0, "ToolBarAction", "Enum type");
|
||||||
|
}
|
||||||
|
|
||||||
|
QQmlComponent *TextureEditorContextObject::specificQmlComponent()
|
||||||
|
{
|
||||||
|
if (m_specificQmlComponent)
|
||||||
|
return m_specificQmlComponent;
|
||||||
|
|
||||||
|
m_specificQmlComponent = new QQmlComponent(m_qmlContext->engine(), this);
|
||||||
|
m_specificQmlComponent->setData(m_specificQmlData.toUtf8(), QUrl::fromLocalFile("specifics.qml"));
|
||||||
|
|
||||||
|
return m_specificQmlComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TextureEditorContextObject::convertColorToString(const QVariant &color)
|
||||||
|
{
|
||||||
|
QString colorString;
|
||||||
|
QColor theColor;
|
||||||
|
if (color.canConvert(QVariant::Color)) {
|
||||||
|
theColor = color.value<QColor>();
|
||||||
|
} else if (color.canConvert(QVariant::Vector3D)) {
|
||||||
|
auto vec = color.value<QVector3D>();
|
||||||
|
theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
colorString = theColor.name();
|
||||||
|
|
||||||
|
if (theColor.alpha() != 255) {
|
||||||
|
QString hexAlpha = QString("%1").arg(theColor.alpha(), 2, 16, QLatin1Char('0'));
|
||||||
|
colorString.remove(0, 1);
|
||||||
|
colorString.prepend(hexAlpha);
|
||||||
|
colorString.prepend(QStringLiteral("#"));
|
||||||
|
}
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this method is used by the ColorEditor helper widget, check if at all needed?
|
||||||
|
QColor TextureEditorContextObject::colorFromString(const QString &colorString)
|
||||||
|
{
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::insertKeyframe(const QString &propertyName)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_model && m_model->rewriterView(), return);
|
||||||
|
QTC_ASSERT(m_selectedTexture.isValid(), return);
|
||||||
|
|
||||||
|
// Ideally we should not missuse the rewriterView
|
||||||
|
// If we add more code here we have to forward the material editor view
|
||||||
|
RewriterView *rewriterView = m_model->rewriterView();
|
||||||
|
|
||||||
|
QmlTimeline timeline = rewriterView->currentTimeline();
|
||||||
|
|
||||||
|
QTC_ASSERT(timeline.isValid(), return);
|
||||||
|
|
||||||
|
rewriterView->executeInTransaction("TextureEditorContextObject::insertKeyframe", [&] {
|
||||||
|
timeline.insertKeyframe(m_selectedTexture, propertyName.toUtf8());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextureEditorContextObject::majorVersion() const
|
||||||
|
{
|
||||||
|
return m_majorVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setMajorVersion(int majorVersion)
|
||||||
|
{
|
||||||
|
if (m_majorVersion == majorVersion)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_majorVersion = majorVersion;
|
||||||
|
|
||||||
|
emit majorVersionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorContextObject::hasActiveTimeline() const
|
||||||
|
{
|
||||||
|
return m_hasActiveTimeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setHasActiveTimeline(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasActiveTimeline)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasActiveTimeline = b;
|
||||||
|
emit hasActiveTimelineChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorContextObject::hasQuick3DImport() const
|
||||||
|
{
|
||||||
|
return m_hasQuick3DImport;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setHasQuick3DImport(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasQuick3DImport)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasQuick3DImport = b;
|
||||||
|
emit hasQuick3DImportChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorContextObject::hasMaterialLibrary() const
|
||||||
|
{
|
||||||
|
return m_hasMaterialLibrary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setHasMaterialLibrary(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasMaterialLibrary)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasMaterialLibrary = b;
|
||||||
|
emit hasMaterialLibraryChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorContextObject::hasModelSelection() const
|
||||||
|
{
|
||||||
|
return m_hasModelSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setHasModelSelection(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasModelSelection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasModelSelection = b;
|
||||||
|
emit hasModelSelectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setSelectedMaterial(const ModelNode &matNode)
|
||||||
|
{
|
||||||
|
m_selectedTexture = matNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setSpecificsUrl(const QUrl &newSpecificsUrl)
|
||||||
|
{
|
||||||
|
if (newSpecificsUrl == m_specificsUrl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_specificsUrl = newSpecificsUrl;
|
||||||
|
emit specificsUrlChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setSpecificQmlData(const QString &newSpecificQmlData)
|
||||||
|
{
|
||||||
|
if (newSpecificQmlData == m_specificQmlData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_specificQmlData = newSpecificQmlData;
|
||||||
|
|
||||||
|
delete m_specificQmlComponent;
|
||||||
|
m_specificQmlComponent = nullptr;
|
||||||
|
|
||||||
|
emit specificQmlComponentChanged();
|
||||||
|
emit specificQmlDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setStateName(const QString &newStateName)
|
||||||
|
{
|
||||||
|
if (newStateName == m_stateName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_stateName = newStateName;
|
||||||
|
emit stateNameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setAllStateNames(const QStringList &allStates)
|
||||||
|
{
|
||||||
|
if (allStates == m_allStateNames)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_allStateNames = allStates;
|
||||||
|
emit allStateNamesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setIsBaseState(bool newIsBaseState)
|
||||||
|
{
|
||||||
|
if (newIsBaseState == m_isBaseState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_isBaseState = newIsBaseState;
|
||||||
|
emit isBaseStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setSelectionChanged(bool newSelectionChanged)
|
||||||
|
{
|
||||||
|
if (newSelectionChanged == m_selectionChanged)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_selectionChanged = newSelectionChanged;
|
||||||
|
emit selectionChangedChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setBackendValues(QQmlPropertyMap *newBackendValues)
|
||||||
|
{
|
||||||
|
if (newBackendValues == m_backendValues)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_backendValues = newBackendValues;
|
||||||
|
emit backendValuesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setModel(Model *model)
|
||||||
|
{
|
||||||
|
m_model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::triggerSelectionChanged()
|
||||||
|
{
|
||||||
|
setSelectionChanged(!m_selectionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::setHasAliasExport(bool hasAliasExport)
|
||||||
|
{
|
||||||
|
if (m_aliasExport == hasAliasExport)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_aliasExport = hasAliasExport;
|
||||||
|
emit hasAliasExportChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::hideCursor()
|
||||||
|
{
|
||||||
|
if (QApplication::overrideCursor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
m_lastPos = QCursor::pos(w->screen());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::restoreCursor()
|
||||||
|
{
|
||||||
|
if (!QApplication::overrideCursor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
QCursor::setPos(w->screen(), m_lastPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::holdCursorInPlace()
|
||||||
|
{
|
||||||
|
if (!QApplication::overrideCursor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
QCursor::setPos(w->screen(), m_lastPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextureEditorContextObject::devicePixelRatio()
|
||||||
|
{
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
return w->devicePixelRatio();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TextureEditorContextObject::allStatesForId(const QString &id)
|
||||||
|
{
|
||||||
|
if (m_model && m_model->rewriterView()) {
|
||||||
|
const QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id);
|
||||||
|
if (node.isValid())
|
||||||
|
return node.allStateNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorContextObject::isBlocked(const QString &propName) const
|
||||||
|
{
|
||||||
|
if (!m_selectedTexture.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_model || !m_model->rewriterView())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (QmlObjectNode(m_selectedTexture).isBlocked(propName.toUtf8()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorContextObject::goIntoComponent()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_model, return);
|
||||||
|
DocumentManager::goIntoComponent(m_selectedTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // QmlDesigner
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <modelnode.h>
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlComponent>
|
||||||
|
#include <QQmlPropertyMap>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class Model;
|
||||||
|
|
||||||
|
class TextureEditorContextObject : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QUrl specificsUrl READ specificsUrl WRITE setSpecificsUrl NOTIFY specificsUrlChanged)
|
||||||
|
Q_PROPERTY(QString specificQmlData READ specificQmlData WRITE setSpecificQmlData NOTIFY specificQmlDataChanged)
|
||||||
|
Q_PROPERTY(QQmlComponent *specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString stateName READ stateName WRITE setStateName NOTIFY stateNameChanged)
|
||||||
|
Q_PROPERTY(QStringList allStateNames READ allStateNames WRITE setAllStateNames NOTIFY allStateNamesChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool isBaseState READ isBaseState WRITE setIsBaseState NOTIFY isBaseStateChanged)
|
||||||
|
Q_PROPERTY(bool selectionChanged READ selectionChanged WRITE setSelectionChanged NOTIFY selectionChangedChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(int majorVersion READ majorVersion WRITE setMajorVersion NOTIFY majorVersionChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool hasAliasExport READ hasAliasExport NOTIFY hasAliasExportChanged)
|
||||||
|
Q_PROPERTY(bool hasActiveTimeline READ hasActiveTimeline NOTIFY hasActiveTimelineChanged)
|
||||||
|
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
|
||||||
|
Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged)
|
||||||
|
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureEditorContextObject(QQmlContext *context, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QUrl specificsUrl() const { return m_specificsUrl; }
|
||||||
|
QString specificQmlData() const {return m_specificQmlData; }
|
||||||
|
QQmlComponent *specificQmlComponent();
|
||||||
|
QString stateName() const { return m_stateName; }
|
||||||
|
QStringList allStateNames() const { return m_allStateNames; }
|
||||||
|
|
||||||
|
bool isBaseState() const { return m_isBaseState; }
|
||||||
|
bool selectionChanged() const { return m_selectionChanged; }
|
||||||
|
|
||||||
|
QQmlPropertyMap *backendValues() const { return m_backendValues; }
|
||||||
|
|
||||||
|
Q_INVOKABLE QString convertColorToString(const QVariant &color);
|
||||||
|
Q_INVOKABLE QColor colorFromString(const QString &colorString);
|
||||||
|
|
||||||
|
Q_INVOKABLE void insertKeyframe(const QString &propertyName);
|
||||||
|
|
||||||
|
Q_INVOKABLE void hideCursor();
|
||||||
|
Q_INVOKABLE void restoreCursor();
|
||||||
|
Q_INVOKABLE void holdCursorInPlace();
|
||||||
|
|
||||||
|
Q_INVOKABLE int devicePixelRatio();
|
||||||
|
|
||||||
|
Q_INVOKABLE QStringList allStatesForId(const QString &id);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool isBlocked(const QString &propName) const;
|
||||||
|
Q_INVOKABLE void goIntoComponent();
|
||||||
|
|
||||||
|
enum ToolBarAction {
|
||||||
|
ApplyToSelected,
|
||||||
|
AddNewTexture,
|
||||||
|
DeleteCurrentTexture,
|
||||||
|
OpenMaterialBrowser
|
||||||
|
};
|
||||||
|
Q_ENUM(ToolBarAction)
|
||||||
|
|
||||||
|
int majorVersion() const;
|
||||||
|
void setMajorVersion(int majorVersion);
|
||||||
|
|
||||||
|
bool hasActiveTimeline() const;
|
||||||
|
void setHasActiveTimeline(bool b);
|
||||||
|
|
||||||
|
bool hasQuick3DImport() const;
|
||||||
|
void setHasQuick3DImport(bool b);
|
||||||
|
|
||||||
|
bool hasMaterialLibrary() const;
|
||||||
|
void setHasMaterialLibrary(bool b);
|
||||||
|
|
||||||
|
bool hasModelSelection() const;
|
||||||
|
void setHasModelSelection(bool b);
|
||||||
|
|
||||||
|
bool hasAliasExport() const { return m_aliasExport; }
|
||||||
|
|
||||||
|
void setSelectedMaterial(const ModelNode &matNode);
|
||||||
|
|
||||||
|
void setSpecificsUrl(const QUrl &newSpecificsUrl);
|
||||||
|
void setSpecificQmlData(const QString &newSpecificQmlData);
|
||||||
|
void setStateName(const QString &newStateName);
|
||||||
|
void setAllStateNames(const QStringList &allStates);
|
||||||
|
void setIsBaseState(bool newIsBaseState);
|
||||||
|
void setSelectionChanged(bool newSelectionChanged);
|
||||||
|
void setBackendValues(QQmlPropertyMap *newBackendValues);
|
||||||
|
void setModel(Model *model);
|
||||||
|
|
||||||
|
void triggerSelectionChanged();
|
||||||
|
void setHasAliasExport(bool hasAliasExport);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void specificsUrlChanged();
|
||||||
|
void specificQmlDataChanged();
|
||||||
|
void specificQmlComponentChanged();
|
||||||
|
void stateNameChanged();
|
||||||
|
void allStateNamesChanged();
|
||||||
|
void isBaseStateChanged();
|
||||||
|
void selectionChangedChanged();
|
||||||
|
void backendValuesChanged();
|
||||||
|
void majorVersionChanged();
|
||||||
|
void hasAliasExportChanged();
|
||||||
|
void hasActiveTimelineChanged();
|
||||||
|
void hasQuick3DImportChanged();
|
||||||
|
void hasMaterialLibraryChanged();
|
||||||
|
void hasModelSelectionChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUrl m_specificsUrl;
|
||||||
|
QString m_specificQmlData;
|
||||||
|
QQmlComponent *m_specificQmlComponent = nullptr;
|
||||||
|
QQmlContext *m_qmlContext = nullptr;
|
||||||
|
|
||||||
|
QString m_stateName;
|
||||||
|
QStringList m_allStateNames;
|
||||||
|
|
||||||
|
int m_majorVersion = 1;
|
||||||
|
|
||||||
|
QQmlPropertyMap *m_backendValues = nullptr;
|
||||||
|
Model *m_model = nullptr;
|
||||||
|
|
||||||
|
QPoint m_lastPos;
|
||||||
|
|
||||||
|
bool m_isBaseState = false;
|
||||||
|
bool m_selectionChanged = false;
|
||||||
|
bool m_aliasExport = false;
|
||||||
|
bool m_hasActiveTimeline = false;
|
||||||
|
bool m_hasQuick3DImport = false;
|
||||||
|
bool m_hasMaterialLibrary = false;
|
||||||
|
bool m_hasModelSelection = false;
|
||||||
|
|
||||||
|
ModelNode m_selectedTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // QmlDesigner
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "textureeditordynamicpropertiesproxymodel.h"
|
||||||
|
|
||||||
|
#include "dynamicpropertiesmodel.h"
|
||||||
|
#include "textureeditorview.h"
|
||||||
|
|
||||||
|
using namespace QmlDesigner;
|
||||||
|
|
||||||
|
TextureEditorDynamicPropertiesProxyModel::TextureEditorDynamicPropertiesProxyModel(QObject *parent)
|
||||||
|
: DynamicPropertiesProxyModel(parent)
|
||||||
|
{
|
||||||
|
if (TextureEditorView::instance())
|
||||||
|
initModel(TextureEditorView::instance()->dynamicPropertiesModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorDynamicPropertiesProxyModel::registerDeclarativeType()
|
||||||
|
{
|
||||||
|
DynamicPropertiesProxyModel::registerDeclarativeType();
|
||||||
|
qmlRegisterType<TextureEditorDynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "TextureEditorDynamicPropertiesModel");
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dynamicpropertiesproxymodel.h"
|
||||||
|
|
||||||
|
class TextureEditorDynamicPropertiesProxyModel : public DynamicPropertiesProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TextureEditorDynamicPropertiesProxyModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static void registerDeclarativeType();
|
||||||
|
};
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "textureeditorqmlbackend.h"
|
||||||
|
|
||||||
|
#include "bindingproperty.h"
|
||||||
|
#include "documentmanager.h"
|
||||||
|
#include "nodemetainfo.h"
|
||||||
|
#include "propertyeditorvalue.h"
|
||||||
|
#include "qmldesignerconstants.h"
|
||||||
|
#include "qmlobjectnode.h"
|
||||||
|
#include "qmltimeline.h"
|
||||||
|
#include "textureeditortransaction.h"
|
||||||
|
#include "textureeditorcontextobject.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/environment.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/hdrimage.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/stylehelper.h>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QQuickImageProvider>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QQuickWidget>
|
||||||
|
#include <QVector2D>
|
||||||
|
#include <QVector3D>
|
||||||
|
#include <QVector4D>
|
||||||
|
|
||||||
|
static QObject *variantToQObject(const QVariant &value)
|
||||||
|
{
|
||||||
|
if (value.userType() == QMetaType::QObjectStar || value.userType() > QMetaType::User)
|
||||||
|
return *(QObject **)value.constData();
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class TextureEditorImageProvider : public QQuickImageProvider
|
||||||
|
{
|
||||||
|
QPixmap m_previewPixmap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureEditorImageProvider()
|
||||||
|
: QQuickImageProvider(Pixmap) {}
|
||||||
|
|
||||||
|
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override
|
||||||
|
{
|
||||||
|
QPixmap pixmap;
|
||||||
|
const QString suffix = id.split('.').last().toLower();
|
||||||
|
const QString path = DocumentManager::currentResourcePath().path() + '/' + id;
|
||||||
|
if (suffix == "hdr")
|
||||||
|
pixmap = HdrImage{path}.toPixmap();
|
||||||
|
else
|
||||||
|
pixmap = Utils::StyleHelper::dpiSpecificImageFile(path);
|
||||||
|
|
||||||
|
if (pixmap.isNull())
|
||||||
|
pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png");
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
*size = pixmap.size();
|
||||||
|
|
||||||
|
if (requestedSize.isValid())
|
||||||
|
return pixmap.scaled(requestedSize, Qt::KeepAspectRatio);
|
||||||
|
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor)
|
||||||
|
: m_view(new QQuickWidget)
|
||||||
|
, m_textureEditorTransaction(new TextureEditorTransaction(textureEditor))
|
||||||
|
, m_contextObject(new TextureEditorContextObject(m_view->rootContext()))
|
||||||
|
, m_textureEditorImageProvider(new TextureEditorImageProvider())
|
||||||
|
{
|
||||||
|
m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
|
m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||||
|
m_view->engine()->addImageProvider("textureEditor", m_textureEditorImageProvider);
|
||||||
|
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
|
||||||
|
m_contextObject->setModel(textureEditor->model());
|
||||||
|
context()->setContextObject(m_contextObject.data());
|
||||||
|
|
||||||
|
QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged,
|
||||||
|
textureEditor, &TextureEditorView::changeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEditorQmlBackend::~TextureEditorQmlBackend()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyName TextureEditorQmlBackend::auxNamePostFix(const PropertyName &propertyName)
|
||||||
|
{
|
||||||
|
return propertyName + "__AUX";
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
|
||||||
|
const PropertyName &name,
|
||||||
|
const QVariant &value,
|
||||||
|
TextureEditorView *textureEditor)
|
||||||
|
{
|
||||||
|
PropertyName propertyName(name);
|
||||||
|
propertyName.replace('.', '_');
|
||||||
|
auto valueObject = qobject_cast<PropertyEditorValue *>(variantToQObject(backendValuesPropertyMap().value(QString::fromUtf8(propertyName))));
|
||||||
|
if (!valueObject) {
|
||||||
|
valueObject = new PropertyEditorValue(&backendValuesPropertyMap());
|
||||||
|
QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged);
|
||||||
|
QObject::connect(valueObject, &PropertyEditorValue::expressionChanged, textureEditor, &TextureEditorView::changeExpression);
|
||||||
|
QObject::connect(valueObject, &PropertyEditorValue::exportPropertyAsAliasRequested, textureEditor, &TextureEditorView::exportPropertyAsAlias);
|
||||||
|
QObject::connect(valueObject, &PropertyEditorValue::removeAliasExportRequested, textureEditor, &TextureEditorView::removeAliasExport);
|
||||||
|
backendValuesPropertyMap().insert(QString::fromUtf8(propertyName), QVariant::fromValue(valueObject));
|
||||||
|
}
|
||||||
|
valueObject->setName(name);
|
||||||
|
valueObject->setModelNode(qmlObjectNode);
|
||||||
|
|
||||||
|
if (qmlObjectNode.propertyAffectedByCurrentState(name) && !(qmlObjectNode.modelNode().property(name).isBindingProperty()))
|
||||||
|
valueObject->setValue(qmlObjectNode.modelValue(name));
|
||||||
|
else
|
||||||
|
valueObject->setValue(value);
|
||||||
|
|
||||||
|
if (propertyName != "id" && qmlObjectNode.currentState().isBaseState()
|
||||||
|
&& qmlObjectNode.modelNode().property(propertyName).isBindingProperty()) {
|
||||||
|
valueObject->setExpression(qmlObjectNode.modelNode().bindingProperty(propertyName).expression());
|
||||||
|
} else {
|
||||||
|
if (qmlObjectNode.hasBindingProperty(name))
|
||||||
|
valueObject->setExpression(qmlObjectNode.expression(name));
|
||||||
|
else
|
||||||
|
valueObject->setExpression(qmlObjectNode.instanceValue(name).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorQmlBackend::setValue(const QmlObjectNode &, const PropertyName &name, const QVariant &value)
|
||||||
|
{
|
||||||
|
// Vector*D values need to be split into their subcomponents
|
||||||
|
if (value.type() == QVariant::Vector2D) {
|
||||||
|
const char *suffix[2] = {"_x", "_y"};
|
||||||
|
auto vecValue = value.value<QVector2D>();
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
PropertyName subPropName(name.size() + 2, '\0');
|
||||||
|
subPropName.replace(0, name.size(), name);
|
||||||
|
subPropName.replace(name.size(), 2, suffix[i]);
|
||||||
|
auto propertyValue = qobject_cast<PropertyEditorValue *>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(subPropName))));
|
||||||
|
if (propertyValue)
|
||||||
|
propertyValue->setValue(QVariant(vecValue[i]));
|
||||||
|
}
|
||||||
|
} else if (value.type() == QVariant::Vector3D) {
|
||||||
|
const char *suffix[3] = {"_x", "_y", "_z"};
|
||||||
|
auto vecValue = value.value<QVector3D>();
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
PropertyName subPropName(name.size() + 2, '\0');
|
||||||
|
subPropName.replace(0, name.size(), name);
|
||||||
|
subPropName.replace(name.size(), 2, suffix[i]);
|
||||||
|
auto propertyValue = qobject_cast<PropertyEditorValue *>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(subPropName))));
|
||||||
|
if (propertyValue)
|
||||||
|
propertyValue->setValue(QVariant(vecValue[i]));
|
||||||
|
}
|
||||||
|
} else if (value.type() == QVariant::Vector4D) {
|
||||||
|
const char *suffix[4] = {"_x", "_y", "_z", "_w"};
|
||||||
|
auto vecValue = value.value<QVector4D>();
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
PropertyName subPropName(name.size() + 2, '\0');
|
||||||
|
subPropName.replace(0, name.size(), name);
|
||||||
|
subPropName.replace(name.size(), 2, suffix[i]);
|
||||||
|
auto propertyValue = qobject_cast<PropertyEditorValue *>(
|
||||||
|
variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(subPropName))));
|
||||||
|
if (propertyValue)
|
||||||
|
propertyValue->setValue(QVariant(vecValue[i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PropertyName propertyName = name;
|
||||||
|
propertyName.replace('.', '_');
|
||||||
|
auto propertyValue = qobject_cast<PropertyEditorValue *>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(propertyName))));
|
||||||
|
if (propertyValue)
|
||||||
|
propertyValue->setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQmlContext *TextureEditorQmlBackend::context() const
|
||||||
|
{
|
||||||
|
return m_view->rootContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEditorContextObject *TextureEditorQmlBackend::contextObject() const
|
||||||
|
{
|
||||||
|
return m_contextObject.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickWidget *TextureEditorQmlBackend::widget() const
|
||||||
|
{
|
||||||
|
return m_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorQmlBackend::setSource(const QUrl &url)
|
||||||
|
{
|
||||||
|
m_view->setSource(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::QmlAnchorBindingProxy &TextureEditorQmlBackend::backendAnchorBinding()
|
||||||
|
{
|
||||||
|
return m_backendAnchorBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesignerPropertyMap &TextureEditorQmlBackend::backendValuesPropertyMap()
|
||||||
|
{
|
||||||
|
return m_backendValuesPropertyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEditorTransaction *TextureEditorQmlBackend::textureEditorTransaction() const
|
||||||
|
{
|
||||||
|
return m_textureEditorTransaction.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyEditorValue *TextureEditorQmlBackend::propertyValueForName(const QString &propertyName)
|
||||||
|
{
|
||||||
|
return qobject_cast<PropertyEditorValue *>(variantToQObject(backendValuesPropertyMap().value(propertyName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, const QString &stateName,
|
||||||
|
const QUrl &qmlSpecificsFile, TextureEditorView *textureEditor)
|
||||||
|
{
|
||||||
|
if (selectedTextureNode.isValid()) {
|
||||||
|
m_contextObject->setModel(textureEditor->model());
|
||||||
|
|
||||||
|
for (const auto &property : selectedTextureNode.modelNode().metaInfo().properties()) {
|
||||||
|
createPropertyEditorValue(selectedTextureNode,
|
||||||
|
property.name(),
|
||||||
|
selectedTextureNode.instanceValue(property.name()),
|
||||||
|
textureEditor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// model node
|
||||||
|
m_backendModelNode.setup(selectedTextureNode.modelNode());
|
||||||
|
context()->setContextProperty("modelNodeBackend", &m_backendModelNode);
|
||||||
|
context()->setContextProperty("hasTexture", QVariant(true));
|
||||||
|
|
||||||
|
// className
|
||||||
|
auto valueObject = qobject_cast<PropertyEditorValue *>(variantToQObject(
|
||||||
|
m_backendValuesPropertyMap.value(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY)));
|
||||||
|
if (!valueObject)
|
||||||
|
valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap);
|
||||||
|
valueObject->setName(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY);
|
||||||
|
valueObject->setModelNode(selectedTextureNode.modelNode());
|
||||||
|
valueObject->setValue(m_backendModelNode.simplifiedTypeName());
|
||||||
|
QObject::connect(valueObject,
|
||||||
|
&PropertyEditorValue::valueChanged,
|
||||||
|
&backendValuesPropertyMap(),
|
||||||
|
&DesignerPropertyMap::valueChanged);
|
||||||
|
m_backendValuesPropertyMap.insert(Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY,
|
||||||
|
QVariant::fromValue(valueObject));
|
||||||
|
|
||||||
|
// anchors
|
||||||
|
m_backendAnchorBinding.setup(selectedTextureNode.modelNode());
|
||||||
|
context()->setContextProperties(
|
||||||
|
QVector<QQmlContext::PropertyPair>{
|
||||||
|
{{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
|
||||||
|
{{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.data())}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
contextObject()->setSpecificsUrl(qmlSpecificsFile);
|
||||||
|
contextObject()->setStateName(stateName);
|
||||||
|
|
||||||
|
QStringList stateNames = selectedTextureNode.allStateNames();
|
||||||
|
stateNames.prepend("base state");
|
||||||
|
contextObject()->setAllStateNames(stateNames);
|
||||||
|
contextObject()->setSelectedMaterial(selectedTextureNode);
|
||||||
|
contextObject()->setIsBaseState(selectedTextureNode.isInBaseState());
|
||||||
|
contextObject()->setHasAliasExport(selectedTextureNode.isAliasExported());
|
||||||
|
contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(selectedTextureNode.view()));
|
||||||
|
|
||||||
|
contextObject()->setSelectionChanged(false);
|
||||||
|
|
||||||
|
NodeMetaInfo metaInfo = selectedTextureNode.modelNode().metaInfo();
|
||||||
|
contextObject()->setMajorVersion(metaInfo.isValid() ? metaInfo.majorVersion() : -1);
|
||||||
|
} else {
|
||||||
|
context()->setContextProperty("hasTexture", QVariant(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TextureEditorQmlBackend::propertyEditorResourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorQmlBackend::emitSelectionToBeChanged()
|
||||||
|
{
|
||||||
|
m_backendModelNode.emitSelectionToBeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorQmlBackend::emitSelectionChanged()
|
||||||
|
{
|
||||||
|
m_backendModelNode.emitSelectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorQmlBackend::setValueforAuxiliaryProperties(const QmlObjectNode &qmlObjectNode,
|
||||||
|
AuxiliaryDataKeyView key)
|
||||||
|
{
|
||||||
|
const PropertyName propertyName = auxNamePostFix(PropertyName(key.name));
|
||||||
|
setValue(qmlObjectNode, propertyName, qmlObjectNode.modelNode().auxiliaryDataWithDefault(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "designerpropertymap.h"
|
||||||
|
#include "qmlanchorbindingproxy.h"
|
||||||
|
#include "qmlmodelnodeproxy.h"
|
||||||
|
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
|
||||||
|
class PropertyEditorValue;
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QQuickWidget;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class TextureEditorContextObject;
|
||||||
|
class TextureEditorImageProvider;
|
||||||
|
class TextureEditorTransaction;
|
||||||
|
class TextureEditorView;
|
||||||
|
|
||||||
|
class TextureEditorQmlBackend
|
||||||
|
{
|
||||||
|
Q_DISABLE_COPY(TextureEditorQmlBackend)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureEditorQmlBackend(TextureEditorView *materialEditor);
|
||||||
|
~TextureEditorQmlBackend();
|
||||||
|
|
||||||
|
void setup(const QmlObjectNode &selectedTextureNode, const QString &stateName, const QUrl &qmlSpecificsFile,
|
||||||
|
TextureEditorView *textureEditor);
|
||||||
|
void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value);
|
||||||
|
|
||||||
|
QQmlContext *context() const;
|
||||||
|
TextureEditorContextObject *contextObject() const;
|
||||||
|
QQuickWidget *widget() const;
|
||||||
|
void setSource(const QUrl &url);
|
||||||
|
Internal::QmlAnchorBindingProxy &backendAnchorBinding();
|
||||||
|
DesignerPropertyMap &backendValuesPropertyMap();
|
||||||
|
TextureEditorTransaction *textureEditorTransaction() const;
|
||||||
|
|
||||||
|
PropertyEditorValue *propertyValueForName(const QString &propertyName);
|
||||||
|
|
||||||
|
static QString propertyEditorResourcesPath();
|
||||||
|
|
||||||
|
void emitSelectionToBeChanged();
|
||||||
|
void emitSelectionChanged();
|
||||||
|
|
||||||
|
void setValueforAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, AuxiliaryDataKeyView key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
|
||||||
|
const PropertyName &name, const QVariant &value,
|
||||||
|
TextureEditorView *textureEditor);
|
||||||
|
PropertyName auxNamePostFix(const PropertyName &propertyName);
|
||||||
|
|
||||||
|
QQuickWidget *m_view = nullptr;
|
||||||
|
Internal::QmlAnchorBindingProxy m_backendAnchorBinding;
|
||||||
|
QmlModelNodeProxy m_backendModelNode;
|
||||||
|
DesignerPropertyMap m_backendValuesPropertyMap;
|
||||||
|
QScopedPointer<TextureEditorTransaction> m_textureEditorTransaction;
|
||||||
|
QScopedPointer<TextureEditorContextObject> m_contextObject;
|
||||||
|
TextureEditorImageProvider *m_textureEditorImageProvider = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "textureeditortransaction.h"
|
||||||
|
|
||||||
|
#include <QTimerEvent>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
TextureEditorTransaction::TextureEditorTransaction(TextureEditorView *textureEditor)
|
||||||
|
: QObject(textureEditor),
|
||||||
|
m_textureEditor(textureEditor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorTransaction::start()
|
||||||
|
{
|
||||||
|
if (!m_textureEditor->model())
|
||||||
|
return;
|
||||||
|
if (m_rewriterTransaction.isValid())
|
||||||
|
m_rewriterTransaction.commit();
|
||||||
|
m_rewriterTransaction = m_textureEditor->beginRewriterTransaction(QByteArrayLiteral("MaterialEditorTransaction::start"));
|
||||||
|
m_timerId = startTimer(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorTransaction::end()
|
||||||
|
{
|
||||||
|
if (m_rewriterTransaction.isValid() && m_textureEditor->model()) {
|
||||||
|
killTimer(m_timerId);
|
||||||
|
m_rewriterTransaction.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorTransaction::active() const
|
||||||
|
{
|
||||||
|
return m_rewriterTransaction.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorTransaction::timerEvent(QTimerEvent *timerEvent)
|
||||||
|
{
|
||||||
|
if (timerEvent->timerId() != m_timerId)
|
||||||
|
return;
|
||||||
|
killTimer(timerEvent->timerId());
|
||||||
|
if (m_rewriterTransaction.isValid())
|
||||||
|
m_rewriterTransaction.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "textureeditorview.h"
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class TextureEditorTransaction : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureEditorTransaction(TextureEditorView *textureEditor);
|
||||||
|
|
||||||
|
Q_INVOKABLE void start();
|
||||||
|
Q_INVOKABLE void end();
|
||||||
|
|
||||||
|
Q_INVOKABLE bool active() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TextureEditorView *m_textureEditor = nullptr;
|
||||||
|
RewriterTransaction m_rewriterTransaction;
|
||||||
|
int m_timerId = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,866 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "textureeditorview.h"
|
||||||
|
|
||||||
|
#include "textureeditorqmlbackend.h"
|
||||||
|
#include "textureeditorcontextobject.h"
|
||||||
|
#include "textureeditordynamicpropertiesproxymodel.h"
|
||||||
|
#include "propertyeditorvalue.h"
|
||||||
|
#include "textureeditortransaction.h"
|
||||||
|
#include "assetslibrarywidget.h"
|
||||||
|
|
||||||
|
#include <auxiliarydataproperties.h>
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
#include <dynamicpropertiesmodel.h>
|
||||||
|
#include <metainfo.h>
|
||||||
|
#include <nodeinstanceview.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
#include <nodeproperty.h>
|
||||||
|
#include <rewritingexception.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
#include <qmltimeline.h>
|
||||||
|
|
||||||
|
#include <theme.h>
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/messagebox.h>
|
||||||
|
#include <designmodewidget.h>
|
||||||
|
#include <propertyeditorqmlbackend.h>
|
||||||
|
#include <utils/environment.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QQuickWidget>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QColorDialog>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
TextureEditorView::TextureEditorView(ExternalDependenciesInterface &externalDependencies)
|
||||||
|
: AbstractView{externalDependencies}
|
||||||
|
, m_stackedWidget(new QStackedWidget)
|
||||||
|
, m_dynamicPropertiesModel(new Internal::DynamicPropertiesModel(true, this))
|
||||||
|
{
|
||||||
|
m_updateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F12), m_stackedWidget);
|
||||||
|
connect(m_updateShortcut, &QShortcut::activated, this, &TextureEditorView::reloadQml);
|
||||||
|
|
||||||
|
m_ensureMatLibTimer.callOnTimeout([this] {
|
||||||
|
if (model() && model()->rewriterView() && !model()->rewriterView()->hasIncompleteTypeInformation()
|
||||||
|
&& model()->rewriterView()->errors().isEmpty()) {
|
||||||
|
ensureMaterialLibraryNode();
|
||||||
|
if (m_qmlBackEnd && m_qmlBackEnd->contextObject())
|
||||||
|
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
|
||||||
|
m_ensureMatLibTimer.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_stackedWidget->setStyleSheet(Theme::replaceCssColors(
|
||||||
|
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
|
||||||
|
m_stackedWidget->setMinimumWidth(250);
|
||||||
|
QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_TEXTUREEDITOR_TIME);
|
||||||
|
|
||||||
|
TextureEditorDynamicPropertiesProxyModel::registerDeclarativeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEditorView::~TextureEditorView()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_qmlBackendHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from texture editor to model
|
||||||
|
void TextureEditorView::changeValue(const QString &name)
|
||||||
|
{
|
||||||
|
PropertyName propertyName = name.toUtf8();
|
||||||
|
|
||||||
|
if (propertyName.isNull() || locked() || noValidSelection() || propertyName == "id"
|
||||||
|
|| propertyName == Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyName underscoreName(propertyName);
|
||||||
|
underscoreName.replace('.', '_');
|
||||||
|
PropertyEditorValue *value = m_qmlBackEnd->propertyValueForName(QString::fromLatin1(underscoreName));
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (propertyName.endsWith("__AUX")) {
|
||||||
|
commitAuxValueToModel(propertyName, value->value());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeMetaInfo metaInfo = m_selectedTexture.metaInfo();
|
||||||
|
|
||||||
|
QVariant castedValue;
|
||||||
|
|
||||||
|
if (auto property = metaInfo.property(propertyName)) {
|
||||||
|
castedValue = property.castedValue(value->value());
|
||||||
|
} else {
|
||||||
|
qWarning() << __FUNCTION__ << propertyName << "cannot be casted (metainfo)";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value->value().isValid() && !castedValue.isValid()) {
|
||||||
|
qWarning() << __FUNCTION__ << propertyName << "not properly casted (metainfo)";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool propertyTypeUrl = false;
|
||||||
|
|
||||||
|
if (auto property = metaInfo.property(propertyName)) {
|
||||||
|
if (property.propertyType().isUrl()) {
|
||||||
|
// turn absolute local file paths into relative paths
|
||||||
|
propertyTypeUrl = true;
|
||||||
|
QString filePath = castedValue.toUrl().toString();
|
||||||
|
QFileInfo fi(filePath);
|
||||||
|
if (fi.exists() && fi.isAbsolute()) {
|
||||||
|
QDir fileDir(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath());
|
||||||
|
castedValue = QUrl(fileDir.relativeFilePath(filePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == "state" && castedValue.toString() == "base state")
|
||||||
|
castedValue = "";
|
||||||
|
|
||||||
|
if (castedValue.type() == QVariant::Color) {
|
||||||
|
QColor color = castedValue.value<QColor>();
|
||||||
|
QColor newColor = QColor(color.name());
|
||||||
|
newColor.setAlpha(color.alpha());
|
||||||
|
castedValue = QVariant(newColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value->value().isValid() || (propertyTypeUrl && value->value().toString().isEmpty())) { // reset
|
||||||
|
removePropertyFromModel(propertyName);
|
||||||
|
} else {
|
||||||
|
// QVector*D(0, 0, 0) detects as null variant though it is valid value
|
||||||
|
if (castedValue.isValid()
|
||||||
|
&& (!castedValue.isNull() || castedValue.type() == QVariant::Vector2D
|
||||||
|
|| castedValue.type() == QVariant::Vector3D
|
||||||
|
|| castedValue.type() == QVariant::Vector4D)) {
|
||||||
|
commitVariantValueToModel(propertyName, castedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTrueFalseLiteral(const QString &expression)
|
||||||
|
{
|
||||||
|
return (expression.compare("false", Qt::CaseInsensitive) == 0)
|
||||||
|
|| (expression.compare("true", Qt::CaseInsensitive) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::changeExpression(const QString &propertyName)
|
||||||
|
{
|
||||||
|
PropertyName name = propertyName.toUtf8();
|
||||||
|
|
||||||
|
if (name.isNull() || locked() || noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
executeInTransaction("TextureEditorView::changeExpression", [this, name] {
|
||||||
|
PropertyName underscoreName(name);
|
||||||
|
underscoreName.replace('.', '_');
|
||||||
|
|
||||||
|
QmlObjectNode qmlObjectNode(m_selectedTexture);
|
||||||
|
PropertyEditorValue *value = m_qmlBackEnd->propertyValueForName(QString::fromLatin1(underscoreName));
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
qWarning() << __FUNCTION__ << "no value for " << underscoreName;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto property = m_selectedTexture.metaInfo().property(name)) {
|
||||||
|
auto propertyTypeName = property.propertyType().typeName();
|
||||||
|
if (propertyTypeName == "QColor") {
|
||||||
|
if (QColor(value->expression().remove('"')).isValid()) {
|
||||||
|
qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"')));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (propertyTypeName == "bool") {
|
||||||
|
if (isTrueFalseLiteral(value->expression())) {
|
||||||
|
if (value->expression().compare("true", Qt::CaseInsensitive) == 0)
|
||||||
|
qmlObjectNode.setVariantProperty(name, true);
|
||||||
|
else
|
||||||
|
qmlObjectNode.setVariantProperty(name, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (propertyTypeName == "int") {
|
||||||
|
bool ok;
|
||||||
|
int intValue = value->expression().toInt(&ok);
|
||||||
|
if (ok) {
|
||||||
|
qmlObjectNode.setVariantProperty(name, intValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (propertyTypeName == "qreal") {
|
||||||
|
bool ok;
|
||||||
|
qreal realValue = value->expression().toDouble(&ok);
|
||||||
|
if (ok) {
|
||||||
|
qmlObjectNode.setVariantProperty(name, realValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (propertyTypeName == "QVariant") {
|
||||||
|
bool ok;
|
||||||
|
qreal realValue = value->expression().toDouble(&ok);
|
||||||
|
if (ok) {
|
||||||
|
qmlObjectNode.setVariantProperty(name, realValue);
|
||||||
|
return;
|
||||||
|
} else if (isTrueFalseLiteral(value->expression())) {
|
||||||
|
if (value->expression().compare("true", Qt::CaseInsensitive) == 0)
|
||||||
|
qmlObjectNode.setVariantProperty(name, true);
|
||||||
|
else
|
||||||
|
qmlObjectNode.setVariantProperty(name, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value->expression().isEmpty()) {
|
||||||
|
value->resetValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qmlObjectNode.expression(name) != value->expression() || !qmlObjectNode.propertyAffectedByCurrentState(name))
|
||||||
|
qmlObjectNode.setBindingProperty(name, value->expression());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::exportPropertyAsAlias(const QString &name)
|
||||||
|
{
|
||||||
|
if (name.isNull() || locked() || noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
executeInTransaction("TextureEditorView::exportPopertyAsAlias", [this, name] {
|
||||||
|
const QString id = m_selectedTexture.validId();
|
||||||
|
QString upperCasePropertyName = name;
|
||||||
|
upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper());
|
||||||
|
QString aliasName = id + upperCasePropertyName;
|
||||||
|
aliasName.replace(".", ""); //remove all dots
|
||||||
|
|
||||||
|
PropertyName propertyName = aliasName.toUtf8();
|
||||||
|
if (rootModelNode().hasProperty(propertyName)) {
|
||||||
|
Core::AsynchronousMessageBox::warning(tr("Cannot Export Property as Alias"),
|
||||||
|
tr("Property %1 does already exist for root component.").arg(aliasName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rootModelNode().bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::removeAliasExport(const QString &name)
|
||||||
|
{
|
||||||
|
if (name.isNull() || locked() || noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
executeInTransaction("TextureEditorView::removeAliasExport", [this, name] {
|
||||||
|
const QString id = m_selectedTexture.validId();
|
||||||
|
|
||||||
|
const QList<BindingProperty> bindingProps = rootModelNode().bindingProperties();
|
||||||
|
for (const BindingProperty &property : bindingProps) {
|
||||||
|
if (property.expression() == (id + "." + name)) {
|
||||||
|
rootModelNode().removeProperty(property.name());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorView::locked() const
|
||||||
|
{
|
||||||
|
return m_locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::currentTimelineChanged(const ModelNode &)
|
||||||
|
{
|
||||||
|
m_qmlBackEnd->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::DynamicPropertiesModel *TextureEditorView::dynamicPropertiesModel() const
|
||||||
|
{
|
||||||
|
return m_dynamicPropertiesModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEditorView *TextureEditorView::instance()
|
||||||
|
{
|
||||||
|
static TextureEditorView *s_instance = nullptr;
|
||||||
|
|
||||||
|
if (s_instance)
|
||||||
|
return s_instance;
|
||||||
|
|
||||||
|
const auto views = QmlDesignerPlugin::instance()->viewManager().views();
|
||||||
|
for (auto *view : views) {
|
||||||
|
TextureEditorView *myView = qobject_cast<TextureEditorView *>(view);
|
||||||
|
if (myView)
|
||||||
|
s_instance = myView;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTC_ASSERT(s_instance, return nullptr);
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::timerEvent(QTimerEvent *timerEvent)
|
||||||
|
{
|
||||||
|
if (m_timerId == timerEvent->timerId())
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::resetView()
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
if (m_timerId)
|
||||||
|
killTimer(m_timerId);
|
||||||
|
|
||||||
|
setupQmlBackend();
|
||||||
|
|
||||||
|
if (m_qmlBackEnd)
|
||||||
|
m_qmlBackEnd->emitSelectionChanged();
|
||||||
|
|
||||||
|
m_locked = false;
|
||||||
|
|
||||||
|
if (m_timerId)
|
||||||
|
m_timerId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QString TextureEditorView::textureEditorResourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/textureEditorQmlSource";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/textureEditorQmlSource").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::applyTextureToSelectedModel(const ModelNode &texture)
|
||||||
|
{
|
||||||
|
if (!m_selectedModel.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTC_ASSERT(texture.isValid(), return);
|
||||||
|
|
||||||
|
emitCustomNotification("apply_texture_to_model3D", {m_selectedModel, m_selectedTexture});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::handleToolBarAction(int action)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_hasQuick3DImport, return);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case TextureEditorContextObject::ApplyToSelected: {
|
||||||
|
applyTextureToSelectedModel(m_selectedTexture);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TextureEditorContextObject::AddNewTexture: {
|
||||||
|
if (!model())
|
||||||
|
break;
|
||||||
|
executeInTransaction("TextureEditorView:handleToolBarAction", [&] {
|
||||||
|
ModelNode matLib = materialLibraryNode();
|
||||||
|
if (!matLib.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.Texture");
|
||||||
|
ModelNode newTextureNode = createModelNode("QtQuick3D.Texture", metaInfo.majorVersion(),
|
||||||
|
metaInfo.minorVersion());
|
||||||
|
newTextureNode.validId();
|
||||||
|
matLib.defaultNodeListProperty().reparentHere(newTextureNode);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TextureEditorContextObject::DeleteCurrentTexture: {
|
||||||
|
if (m_selectedTexture.isValid())
|
||||||
|
m_selectedTexture.destroy();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TextureEditorContextObject::OpenMaterialBrowser: {
|
||||||
|
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser", true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::setupQmlBackend()
|
||||||
|
{
|
||||||
|
QUrl qmlPaneUrl;
|
||||||
|
QUrl qmlSpecificsUrl;
|
||||||
|
QString specificQmlData;
|
||||||
|
|
||||||
|
if (m_selectedTexture.isValid() && m_hasQuick3DImport && (materialLibraryNode().isValid() || m_hasTextureRoot)) {
|
||||||
|
qmlPaneUrl = QUrl::fromLocalFile(textureEditorResourcesPath() + "/TextureEditorPane.qml");
|
||||||
|
|
||||||
|
TypeName diffClassName;
|
||||||
|
if (NodeMetaInfo metaInfo = m_selectedTexture.metaInfo()) {
|
||||||
|
diffClassName = metaInfo.typeName();
|
||||||
|
for (const NodeMetaInfo &metaInfo : metaInfo.classHierarchy()) {
|
||||||
|
if (PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsUrl))
|
||||||
|
break;
|
||||||
|
qmlSpecificsUrl = PropertyEditorQmlBackend::getQmlFileUrl(metaInfo.typeName()
|
||||||
|
+ "Specifics", metaInfo);
|
||||||
|
diffClassName = metaInfo.typeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffClassName != m_selectedTexture.type()) {
|
||||||
|
specificQmlData = PropertyEditorQmlBackend::templateGeneration(metaInfo,
|
||||||
|
model()->metaInfo(
|
||||||
|
diffClassName),
|
||||||
|
m_selectedTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qmlPaneUrl = QUrl::fromLocalFile(textureEditorResourcesPath() + "/EmptyTextureEditorPane.qml");
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEditorQmlBackend *currentQmlBackend = m_qmlBackendHash.value(qmlPaneUrl.toString());
|
||||||
|
|
||||||
|
QString currentStateName = currentState().isBaseState() ? currentState().name() : "invalid state";
|
||||||
|
|
||||||
|
if (!currentQmlBackend) {
|
||||||
|
currentQmlBackend = new TextureEditorQmlBackend(this);
|
||||||
|
|
||||||
|
m_stackedWidget->addWidget(currentQmlBackend->widget());
|
||||||
|
m_qmlBackendHash.insert(qmlPaneUrl.toString(), currentQmlBackend);
|
||||||
|
|
||||||
|
currentQmlBackend->setup(m_selectedTexture, currentStateName, qmlSpecificsUrl, this);
|
||||||
|
|
||||||
|
currentQmlBackend->setSource(qmlPaneUrl);
|
||||||
|
|
||||||
|
QObject *rootObj = currentQmlBackend->widget()->rootObject();
|
||||||
|
QObject::connect(rootObj, SIGNAL(toolBarAction(int)), this, SLOT(handleToolBarAction(int)));
|
||||||
|
} else {
|
||||||
|
currentQmlBackend->setup(m_selectedTexture, currentStateName, qmlSpecificsUrl, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentQmlBackend->widget()->installEventFilter(this);
|
||||||
|
currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
|
||||||
|
currentQmlBackend->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
|
||||||
|
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
|
||||||
|
|
||||||
|
m_qmlBackEnd = currentQmlBackend;
|
||||||
|
|
||||||
|
if (m_hasTextureRoot)
|
||||||
|
m_dynamicPropertiesModel->setSelectedNode(m_selectedTexture);
|
||||||
|
else
|
||||||
|
m_dynamicPropertiesModel->reset();
|
||||||
|
|
||||||
|
m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
executeInTransaction("TextureEditorView:commitVariantValueToModel", [&] {
|
||||||
|
QmlObjectNode(m_selectedTexture).setVariantProperty(propertyName, value);
|
||||||
|
});
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::commitAuxValueToModel(const PropertyName &propertyName, const QVariant &value)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
PropertyName name = propertyName;
|
||||||
|
name.chop(5);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (value.isValid())
|
||||||
|
m_selectedTexture.setAuxiliaryData(AuxiliaryDataType::Document, name, value);
|
||||||
|
else
|
||||||
|
m_selectedTexture.removeAuxiliaryData(AuxiliaryDataType::Document, name);
|
||||||
|
}
|
||||||
|
catch (const Exception &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::removePropertyFromModel(const PropertyName &propertyName)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
executeInTransaction("MaterialEditorView:removePropertyFromModel", [&] {
|
||||||
|
QmlObjectNode(m_selectedTexture).removeProperty(propertyName);
|
||||||
|
});
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorView::noValidSelection() const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_qmlBackEnd, return true);
|
||||||
|
return !QmlObjectNode::isValidQmlObjectNode(m_selectedTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::modelAttached(Model *model)
|
||||||
|
{
|
||||||
|
AbstractView::modelAttached(model);
|
||||||
|
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
m_hasQuick3DImport = model->hasImport("QtQuick3D");
|
||||||
|
m_hasTextureRoot = rootModelNode().metaInfo().isQtQuick3DTexture();
|
||||||
|
|
||||||
|
if (m_hasTextureRoot) {
|
||||||
|
m_selectedTexture = rootModelNode();
|
||||||
|
} else if (m_hasQuick3DImport) {
|
||||||
|
// Creating the material library node on model attach causes errors as long as the type
|
||||||
|
// information is not complete yet, so we keep checking until type info is complete.
|
||||||
|
m_ensureMatLibTimer.start(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_setupCompleted) {
|
||||||
|
reloadQml();
|
||||||
|
m_setupCompleted = true;
|
||||||
|
}
|
||||||
|
resetView();
|
||||||
|
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::modelAboutToBeDetached(Model *model)
|
||||||
|
{
|
||||||
|
AbstractView::modelAboutToBeDetached(model);
|
||||||
|
m_dynamicPropertiesModel->reset();
|
||||||
|
m_qmlBackEnd->textureEditorTransaction()->end();
|
||||||
|
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
|
||||||
|
{
|
||||||
|
if (noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const AbstractProperty &property : propertyList) {
|
||||||
|
ModelNode node(property.parentModelNode());
|
||||||
|
|
||||||
|
if (node.isRootNode())
|
||||||
|
m_qmlBackEnd->contextObject()->setHasAliasExport(QmlObjectNode(m_selectedTexture).isAliasExported());
|
||||||
|
|
||||||
|
if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) {
|
||||||
|
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicPropertiesModel()->dispatchPropertyChanges(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const VariantProperty &property : propertyList) {
|
||||||
|
ModelNode node(property.parentModelNode());
|
||||||
|
if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) {
|
||||||
|
if (property.isDynamic())
|
||||||
|
m_dynamicPropertiesModel->variantPropertyChanged(property);
|
||||||
|
if (m_selectedTexture.property(property.name()).isBindingProperty())
|
||||||
|
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name()));
|
||||||
|
else
|
||||||
|
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).modelValue(property.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicPropertiesModel()->dispatchPropertyChanges(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const BindingProperty &property : propertyList) {
|
||||||
|
ModelNode node(property.parentModelNode());
|
||||||
|
|
||||||
|
if (property.isAliasExport())
|
||||||
|
m_qmlBackEnd->contextObject()->setHasAliasExport(QmlObjectNode(m_selectedTexture).isAliasExported());
|
||||||
|
|
||||||
|
if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) {
|
||||||
|
if (property.isDynamic())
|
||||||
|
m_dynamicPropertiesModel->bindingPropertyChanged(property);
|
||||||
|
if (QmlObjectNode(m_selectedTexture).modelNode().property(property.name()).isBindingProperty())
|
||||||
|
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name()));
|
||||||
|
else
|
||||||
|
setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).modelValue(property.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicPropertiesModel()->dispatchPropertyChanges(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::auxiliaryDataChanged(const ModelNode &node,
|
||||||
|
AuxiliaryDataKeyView key,
|
||||||
|
const QVariant &)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (noValidSelection() || !node.isSelected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_qmlBackEnd->setValueforAuxiliaryProperties(m_selectedTexture, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
|
||||||
|
{
|
||||||
|
for (const auto &property : propertyList) {
|
||||||
|
if (property.isBindingProperty())
|
||||||
|
m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty());
|
||||||
|
else if (property.isVariantProperty())
|
||||||
|
m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::nodeReparented(const ModelNode &node,
|
||||||
|
const NodeAbstractProperty &newPropertyParent,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
PropertyChangeFlags propertyChange)
|
||||||
|
{
|
||||||
|
if (node.id() == Constants::MATERIAL_LIB_ID && m_qmlBackEnd && m_qmlBackEnd->contextObject())
|
||||||
|
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
||||||
|
{
|
||||||
|
if (removedNode.id() == Constants::MATERIAL_LIB_ID && m_qmlBackEnd && m_qmlBackEnd->contextObject())
|
||||||
|
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorView::hasWidget() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetInfo TextureEditorView::widgetInfo()
|
||||||
|
{
|
||||||
|
return createWidgetInfo(m_stackedWidget,
|
||||||
|
"TextureEditor",
|
||||||
|
WidgetInfo::RightPane,
|
||||||
|
0,
|
||||||
|
tr("Texture Editor"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
||||||
|
[[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
|
||||||
|
{
|
||||||
|
m_selectedModel = {};
|
||||||
|
|
||||||
|
if (selectedNodeList.size() == 1 && selectedNodeList.at(0).metaInfo().isQtQuick3DModel())
|
||||||
|
m_selectedModel = selectedNodeList.at(0);
|
||||||
|
|
||||||
|
m_qmlBackEnd->contextObject()->setHasModelSelection(m_selectedModel.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::currentStateChanged(const ModelNode &node)
|
||||||
|
{
|
||||||
|
QmlModelState newQmlModelState(node);
|
||||||
|
Q_ASSERT(newQmlModelState.isValid());
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::instancePropertyChanged(const QList<QPair<ModelNode, PropertyName>> &propertyList)
|
||||||
|
{
|
||||||
|
if (!m_selectedTexture.isValid() || !m_qmlBackEnd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
for (const QPair<ModelNode, PropertyName> &propertyPair : propertyList) {
|
||||||
|
const ModelNode modelNode = propertyPair.first;
|
||||||
|
const QmlObjectNode qmlObjectNode(modelNode);
|
||||||
|
const PropertyName propertyName = propertyPair.second;
|
||||||
|
|
||||||
|
if (qmlObjectNode.isValid() && modelNode == m_selectedTexture && qmlObjectNode.currentState().isValid()) {
|
||||||
|
const AbstractProperty property = modelNode.property(propertyName);
|
||||||
|
if (!modelNode.hasProperty(propertyName) || modelNode.property(property.name()).isBindingProperty())
|
||||||
|
setValue(modelNode, property.name(), qmlObjectNode.instanceValue(property.name()));
|
||||||
|
else
|
||||||
|
setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::importsChanged([[maybe_unused]] const QList<Import> &addedImports,
|
||||||
|
[[maybe_unused]] const QList<Import> &removedImports)
|
||||||
|
{
|
||||||
|
m_hasQuick3DImport = model()->hasImport("QtQuick3D");
|
||||||
|
m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
|
||||||
|
|
||||||
|
if (m_hasQuick3DImport)
|
||||||
|
m_ensureMatLibTimer.start(500);
|
||||||
|
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::duplicateTexture(const ModelNode &texture)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(texture.isValid(), return);
|
||||||
|
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
TypeName matType = texture.type();
|
||||||
|
QmlObjectNode sourceTexture(texture);
|
||||||
|
ModelNode duplicateTextureNode;
|
||||||
|
QList<AbstractProperty> dynamicProps;
|
||||||
|
|
||||||
|
executeInTransaction(__FUNCTION__, [&] {
|
||||||
|
ModelNode matLib = materialLibraryNode();
|
||||||
|
if (!matLib.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// create the duplicate texture
|
||||||
|
NodeMetaInfo metaInfo = model()->metaInfo(matType);
|
||||||
|
QmlObjectNode duplicateTex = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion());
|
||||||
|
|
||||||
|
duplicateTextureNode = duplicateTex .modelNode();
|
||||||
|
duplicateTextureNode.validId();
|
||||||
|
|
||||||
|
// sync properties. Only the base state is duplicated.
|
||||||
|
const QList<AbstractProperty> props = texture.properties();
|
||||||
|
for (const AbstractProperty &prop : props) {
|
||||||
|
if (prop.name() == "objectName" || prop.name() == "data")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (prop.isVariantProperty()) {
|
||||||
|
if (prop.isDynamic()) {
|
||||||
|
dynamicProps.append(prop);
|
||||||
|
} else {
|
||||||
|
duplicateTextureNode.variantProperty(prop.name())
|
||||||
|
.setValue(prop.toVariantProperty().value());
|
||||||
|
}
|
||||||
|
} else if (prop.isBindingProperty()) {
|
||||||
|
if (prop.isDynamic()) {
|
||||||
|
dynamicProps.append(prop);
|
||||||
|
} else {
|
||||||
|
duplicateTextureNode.bindingProperty(prop.name())
|
||||||
|
.setExpression(prop.toBindingProperty().expression());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matLib.defaultNodeListProperty().reparentHere(duplicateTex);
|
||||||
|
});
|
||||||
|
|
||||||
|
// For some reason, creating dynamic properties in the same transaction doesn't work, so
|
||||||
|
// let's do it in separate transaction.
|
||||||
|
// TODO: Fix the issue and merge transactions (QDS-8094)
|
||||||
|
if (!dynamicProps.isEmpty()) {
|
||||||
|
executeInTransaction(__FUNCTION__, [&] {
|
||||||
|
for (const AbstractProperty &prop : std::as_const(dynamicProps)) {
|
||||||
|
if (prop.isVariantProperty()) {
|
||||||
|
duplicateTextureNode.variantProperty(prop.name())
|
||||||
|
.setDynamicTypeNameAndValue(prop.dynamicTypeName(),
|
||||||
|
prop.toVariantProperty().value());
|
||||||
|
} else if (prop.isBindingProperty()) {
|
||||||
|
duplicateTextureNode.bindingProperty(prop.name())
|
||||||
|
.setDynamicTypeNameAndExpression(prop.dynamicTypeName(),
|
||||||
|
prop.toBindingProperty().expression());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::customNotification([[maybe_unused]] const AbstractView *view,
|
||||||
|
const QString &identifier,
|
||||||
|
const QList<ModelNode> &nodeList,
|
||||||
|
const QList<QVariant> &data)
|
||||||
|
{
|
||||||
|
if (identifier == "selected_texture_changed") {
|
||||||
|
if (!m_hasTextureRoot) {
|
||||||
|
m_selectedTexture = nodeList.first();
|
||||||
|
m_dynamicPropertiesModel->setSelectedNode(m_selectedTexture);
|
||||||
|
QTimer::singleShot(0, this, &TextureEditorView::resetView);
|
||||||
|
}
|
||||||
|
} else if (identifier == "apply_texture_to_selected_triggered") {
|
||||||
|
applyTextureToSelectedModel(nodeList.first());
|
||||||
|
} else if (identifier == "add_new_texture") {
|
||||||
|
handleToolBarAction(TextureEditorContextObject::AddNewTexture);
|
||||||
|
} else if (identifier == "duplicate_texture") {
|
||||||
|
duplicateTexture(nodeList.first());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlDesigner::TextureEditorView::highlightSupportedProperties(bool highlight)
|
||||||
|
{
|
||||||
|
if (!m_selectedTexture.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
DesignerPropertyMap &propMap = m_qmlBackEnd->backendValuesPropertyMap();
|
||||||
|
const QStringList propNames = propMap.keys();
|
||||||
|
NodeMetaInfo metaInfo = m_selectedTexture.metaInfo();
|
||||||
|
QTC_ASSERT(metaInfo.isValid(), return);
|
||||||
|
|
||||||
|
for (const QString &propName : propNames) {
|
||||||
|
if (metaInfo.property(propName.toUtf8()).propertyType().isQtQuick3DTexture()) { // TODO: support dropping to texture source
|
||||||
|
QObject *propEditorValObj = propMap.value(propName).value<QObject *>();
|
||||||
|
PropertyEditorValue *propEditorVal = qobject_cast<PropertyEditorValue *>(propEditorValObj);
|
||||||
|
propEditorVal->setHasActiveDrag(highlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::dragStarted(QMimeData *mimeData)
|
||||||
|
{
|
||||||
|
if (!mimeData->hasFormat(Constants::MIME_TYPE_ASSETS))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QString assetPath = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(',')[0];
|
||||||
|
QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first;
|
||||||
|
|
||||||
|
if (assetType != Constants::MIME_TYPE_ASSET_IMAGE) // currently only image assets have dnd-supported properties
|
||||||
|
return;
|
||||||
|
|
||||||
|
highlightSupportedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::dragEnded()
|
||||||
|
{
|
||||||
|
highlightSupportedProperties(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from model to texture editor
|
||||||
|
void TextureEditorView::setValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
m_qmlBackEnd->setValue(qmlObjectNode, name, value);
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureEditorView::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::FocusOut) {
|
||||||
|
if (m_qmlBackEnd && m_qmlBackEnd->widget() == obj)
|
||||||
|
QMetaObject::invokeMethod(m_qmlBackEnd->widget()->rootObject(), "closeContextMenu");
|
||||||
|
}
|
||||||
|
return QObject::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureEditorView::reloadQml()
|
||||||
|
{
|
||||||
|
m_qmlBackendHash.clear();
|
||||||
|
while (QWidget *widget = m_stackedWidget->widget(0)) {
|
||||||
|
m_stackedWidget->removeWidget(widget);
|
||||||
|
delete widget;
|
||||||
|
}
|
||||||
|
m_qmlBackEnd = nullptr;
|
||||||
|
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <abstractview.h>
|
||||||
|
#include <itemlibraryinfo.h>
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QShortcut;
|
||||||
|
class QStackedWidget;
|
||||||
|
class QTimer;
|
||||||
|
class QColorDialog;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class ModelNode;
|
||||||
|
class TextureEditorQmlBackend;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
class DynamicPropertiesModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextureEditorView : public AbstractView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureEditorView(ExternalDependenciesInterface &externalDependencies);
|
||||||
|
~TextureEditorView() override;
|
||||||
|
|
||||||
|
bool hasWidget() const override;
|
||||||
|
WidgetInfo widgetInfo() override;
|
||||||
|
|
||||||
|
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
||||||
|
const QList<ModelNode> &lastSelectedNodeList) override;
|
||||||
|
|
||||||
|
void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
|
||||||
|
|
||||||
|
void modelAttached(Model *model) override;
|
||||||
|
void modelAboutToBeDetached(Model *model) override;
|
||||||
|
|
||||||
|
void variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags propertyChange) override;
|
||||||
|
void bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags propertyChange) override;
|
||||||
|
void auxiliaryDataChanged(const ModelNode &node,
|
||||||
|
AuxiliaryDataKeyView key,
|
||||||
|
const QVariant &data) override;
|
||||||
|
void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
|
||||||
|
void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
AbstractView::PropertyChangeFlags propertyChange) override;
|
||||||
|
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
|
||||||
|
|
||||||
|
void resetView();
|
||||||
|
void currentStateChanged(const ModelNode &node) override;
|
||||||
|
void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
|
||||||
|
|
||||||
|
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
|
||||||
|
void customNotification(const AbstractView *view, const QString &identifier,
|
||||||
|
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
|
||||||
|
|
||||||
|
void dragStarted(QMimeData *mimeData) override;
|
||||||
|
void dragEnded() override;
|
||||||
|
|
||||||
|
void changeValue(const QString &name);
|
||||||
|
void changeExpression(const QString &name);
|
||||||
|
void exportPropertyAsAlias(const QString &name);
|
||||||
|
void removeAliasExport(const QString &name);
|
||||||
|
|
||||||
|
bool locked() const;
|
||||||
|
|
||||||
|
void currentTimelineChanged(const ModelNode &node) override;
|
||||||
|
|
||||||
|
Internal::DynamicPropertiesModel *dynamicPropertiesModel() const;
|
||||||
|
|
||||||
|
static TextureEditorView *instance();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void handleToolBarAction(int action);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value);
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QString textureEditorResourcesPath();
|
||||||
|
|
||||||
|
void reloadQml();
|
||||||
|
void highlightSupportedProperties(bool highlight = true);
|
||||||
|
|
||||||
|
void applyTextureToSelectedModel(const ModelNode &texture);
|
||||||
|
|
||||||
|
void setupQmlBackend();
|
||||||
|
|
||||||
|
void commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value);
|
||||||
|
void commitAuxValueToModel(const PropertyName &propertyName, const QVariant &value);
|
||||||
|
void removePropertyFromModel(const PropertyName &propertyName);
|
||||||
|
void duplicateTexture(const ModelNode &texture);
|
||||||
|
|
||||||
|
bool noValidSelection() const;
|
||||||
|
|
||||||
|
ModelNode m_selectedTexture;
|
||||||
|
QTimer m_ensureMatLibTimer;
|
||||||
|
QShortcut *m_updateShortcut = nullptr;
|
||||||
|
int m_timerId = 0;
|
||||||
|
QStackedWidget *m_stackedWidget = nullptr;
|
||||||
|
ModelNode m_selectedModel;
|
||||||
|
QHash<QString, TextureEditorQmlBackend *> m_qmlBackendHash;
|
||||||
|
TextureEditorQmlBackend *m_qmlBackEnd = nullptr;
|
||||||
|
bool m_locked = false;
|
||||||
|
bool m_setupCompleted = false;
|
||||||
|
bool m_hasQuick3DImport = false;
|
||||||
|
bool m_hasTextureRoot = false;
|
||||||
|
bool m_initializingPreviewData = false;
|
||||||
|
|
||||||
|
QPointer<QColorDialog> m_colorDialog;
|
||||||
|
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
|
||||||
|
Internal::DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -113,6 +113,7 @@ const char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor";
|
|||||||
const char EVENT_CURVEDITOR_TIME[] = "curveEditor";
|
const char EVENT_CURVEDITOR_TIME[] = "curveEditor";
|
||||||
const char EVENT_STATESEDITOR_TIME[] = "statesEditor";
|
const char EVENT_STATESEDITOR_TIME[] = "statesEditor";
|
||||||
const char EVENT_TEXTEDITOR_TIME[] = "textEditor";
|
const char EVENT_TEXTEDITOR_TIME[] = "textEditor";
|
||||||
|
const char EVENT_TEXTUREEDITOR_TIME[] = "textureEditor";
|
||||||
const char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor";
|
const char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor";
|
||||||
const char EVENT_ASSETSLIBRARY_TIME[] = "assetsLibrary";
|
const char EVENT_ASSETSLIBRARY_TIME[] = "assetsLibrary";
|
||||||
const char EVENT_ITEMLIBRARY_TIME[] = "itemLibrary";
|
const char EVENT_ITEMLIBRARY_TIME[] = "itemLibrary";
|
||||||
|
|||||||
Reference in New Issue
Block a user