forked from qt-creator/qt-creator
QmlDesigner: Implement Material Editor
Task-number: QDS-6438 Task-number: QDS-6439 Change-Id: I04e899a68aea665f0df8b65e21523632174ec76b Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -55,6 +55,20 @@ private:
|
|||||||
qint32 m_renderItemId;
|
qint32 m_renderItemId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool operator==(const RequestModelNodePreviewImageCommand &first,
|
||||||
|
const RequestModelNodePreviewImageCommand &second)
|
||||||
|
{
|
||||||
|
return first.instanceId() == second.instanceId()
|
||||||
|
&& first.size() == second.size()
|
||||||
|
&& first.componentPath() == second.componentPath()
|
||||||
|
&& first.renderItemId() == second.renderItemId();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t qHash(const RequestModelNodePreviewImageCommand &key, size_t seed)
|
||||||
|
{
|
||||||
|
return qHashMulti(seed, key.instanceId(), key.size(), key.componentPath(), key.renderItemId());
|
||||||
|
}
|
||||||
|
|
||||||
QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command);
|
QDataStream &operator<<(QDataStream &out, const RequestModelNodePreviewImageCommand &command);
|
||||||
QDataStream &operator>>(QDataStream &in, RequestModelNodePreviewImageCommand &command);
|
QDataStream &operator>>(QDataStream &in, RequestModelNodePreviewImageCommand &command);
|
||||||
|
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
<file>mockfiles/images/directional@2x.png</file>
|
<file>mockfiles/images/directional@2x.png</file>
|
||||||
<file>mockfiles/images/point.png</file>
|
<file>mockfiles/images/point.png</file>
|
||||||
<file>mockfiles/images/point@2x.png</file>
|
<file>mockfiles/images/point@2x.png</file>
|
||||||
|
<file>mockfiles/images/floor_tex.png</file>
|
||||||
<file>mockfiles/images/spot.png</file>
|
<file>mockfiles/images/spot.png</file>
|
||||||
<file>mockfiles/images/spot@2x.png</file>
|
<file>mockfiles/images/spot@2x.png</file>
|
||||||
<file>mockfiles/qt5/AdjustableArrow.qml</file>
|
<file>mockfiles/qt5/AdjustableArrow.qml</file>
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
<file>mockfiles/images/directional@2x.png</file>
|
<file>mockfiles/images/directional@2x.png</file>
|
||||||
<file>mockfiles/images/point.png</file>
|
<file>mockfiles/images/point.png</file>
|
||||||
<file>mockfiles/images/point@2x.png</file>
|
<file>mockfiles/images/point@2x.png</file>
|
||||||
|
<file>mockfiles/images/floor_tex.png</file>
|
||||||
<file>mockfiles/images/spot.png</file>
|
<file>mockfiles/images/spot.png</file>
|
||||||
<file>mockfiles/images/spot@2x.png</file>
|
<file>mockfiles/images/spot@2x.png</file>
|
||||||
<file>mockfiles/qt6/AdjustableArrow.qml</file>
|
<file>mockfiles/qt6/AdjustableArrow.qml</file>
|
||||||
|
BIN
share/qtcreator/qml/qmlpuppet/mockfiles/images/floor_tex.png
Normal file
BIN
share/qtcreator/qml/qmlpuppet/mockfiles/images/floor_tex.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
@@ -45,20 +45,48 @@ View3D {
|
|||||||
|
|
||||||
Node {
|
Node {
|
||||||
DirectionalLight {
|
DirectionalLight {
|
||||||
eulerRotation.x: -30
|
shadowMapQuality: Light.ShadowMapQualityMedium
|
||||||
eulerRotation.y: -30
|
shadowFilter: 20
|
||||||
|
shadowFactor: 21
|
||||||
|
castsShadow: true
|
||||||
|
eulerRotation.x: -26
|
||||||
|
eulerRotation.y: -57
|
||||||
}
|
}
|
||||||
|
|
||||||
PerspectiveCamera {
|
PerspectiveCamera {
|
||||||
|
y: 125.331
|
||||||
z: 120
|
z: 120
|
||||||
clipFar: 1000
|
eulerRotation.x: -31
|
||||||
clipNear: 1
|
clipNear: 1
|
||||||
|
clipFar: 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
Model {
|
Model {
|
||||||
id: model
|
id: model
|
||||||
|
|
||||||
|
y: 50
|
||||||
source: "#Sphere"
|
source: "#Sphere"
|
||||||
materials: previewMaterial
|
materials: previewMaterial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Model {
|
||||||
|
id: floorModel
|
||||||
|
source: "#Rectangle"
|
||||||
|
scale.y: 8
|
||||||
|
scale.x: 8
|
||||||
|
eulerRotation.x: -90
|
||||||
|
materials: floorMaterial
|
||||||
|
DefaultMaterial {
|
||||||
|
id: floorMaterial
|
||||||
|
diffuseMap: floorTex
|
||||||
|
|
||||||
|
Texture {
|
||||||
|
id: floorTex
|
||||||
|
source: "../images/floor_tex.png"
|
||||||
|
scaleU: floorModel.scale.x
|
||||||
|
scaleV: floorModel.scale.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -45,21 +45,49 @@ View3D {
|
|||||||
|
|
||||||
Node {
|
Node {
|
||||||
DirectionalLight {
|
DirectionalLight {
|
||||||
eulerRotation.x: -30
|
shadowMapQuality: Light.ShadowMapQualityMedium
|
||||||
eulerRotation.y: -30
|
shadowFilter: 20
|
||||||
|
shadowFactor: 21
|
||||||
|
castsShadow: true
|
||||||
|
eulerRotation.x: -26
|
||||||
|
eulerRotation.y: -57
|
||||||
}
|
}
|
||||||
|
|
||||||
PerspectiveCamera {
|
PerspectiveCamera {
|
||||||
|
y: 125.331
|
||||||
z: 120
|
z: 120
|
||||||
clipFar: 1000
|
eulerRotation.x: -31
|
||||||
clipNear: 1
|
clipNear: 1
|
||||||
|
clipFar: 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
Model {
|
Model {
|
||||||
id: model
|
id: model
|
||||||
readonly property bool _edit3dLocked: true // Make this non-pickable
|
readonly property bool _edit3dLocked: true // Make this non-pickable
|
||||||
|
|
||||||
|
y: 50
|
||||||
source: "#Sphere"
|
source: "#Sphere"
|
||||||
materials: previewMaterial
|
materials: previewMaterial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Model {
|
||||||
|
id: floorModel
|
||||||
|
source: "#Rectangle"
|
||||||
|
scale.y: 8
|
||||||
|
scale.x: 8
|
||||||
|
eulerRotation.x: -90
|
||||||
|
materials: floorMaterial
|
||||||
|
DefaultMaterial {
|
||||||
|
id: floorMaterial
|
||||||
|
diffuseMap: floorTex
|
||||||
|
|
||||||
|
Texture {
|
||||||
|
id: floorTex
|
||||||
|
source: "../images/floor_tex.png"
|
||||||
|
scaleU: floorModel.scale.x
|
||||||
|
scaleV: floorModel.scale.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1030,27 +1030,32 @@ void Qt5InformationNodeInstanceServer::doRender3DEditView()
|
|||||||
void Qt5InformationNodeInstanceServer::renderModelNodeImageView()
|
void Qt5InformationNodeInstanceServer::renderModelNodeImageView()
|
||||||
{
|
{
|
||||||
if (!m_renderModelNodeImageViewTimer.isActive())
|
if (!m_renderModelNodeImageViewTimer.isActive())
|
||||||
m_renderModelNodeImageViewTimer.start(0);
|
m_renderModelNodeImageViewTimer.start(17);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView()
|
void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView()
|
||||||
{
|
{
|
||||||
// This crashes on Qt 6.0.x due to QtQuick3D issue, so the preview generation is disabled
|
// This crashes on Qt 6.0.x due to QtQuick3D issue, so the preview generation is disabled
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||||
|
RequestModelNodePreviewImageCommand cmd = *m_modelNodePreviewImageCommands.begin();
|
||||||
ServerNodeInstance instance;
|
ServerNodeInstance instance;
|
||||||
if (m_modelNodePreviewImageCommand.renderItemId() >= 0)
|
if (cmd.renderItemId() >= 0)
|
||||||
instance = instanceForId(m_modelNodePreviewImageCommand.renderItemId());
|
instance = instanceForId(cmd.renderItemId());
|
||||||
else
|
else
|
||||||
instance = instanceForId(m_modelNodePreviewImageCommand.instanceId());
|
instance = instanceForId(cmd.instanceId());
|
||||||
|
|
||||||
if (instance.isSubclassOf("QQuick3DObject"))
|
if (instance.isSubclassOf("QQuick3DObject"))
|
||||||
doRenderModelNode3DImageView();
|
doRenderModelNode3DImageView(cmd);
|
||||||
else if (instance.isSubclassOf("QQuickItem"))
|
else if (instance.isSubclassOf("QQuickItem"))
|
||||||
doRenderModelNode2DImageView();
|
doRenderModelNode2DImageView(cmd);
|
||||||
|
|
||||||
|
m_modelNodePreviewImageCommands.remove(cmd);
|
||||||
|
if (!m_modelNodePreviewImageCommands.isEmpty())
|
||||||
|
m_renderModelNodeImageViewTimer.start(17);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
|
void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView(const RequestModelNodePreviewImageCommand &cmd)
|
||||||
{
|
{
|
||||||
#ifdef QUICK3D_MODULE
|
#ifdef QUICK3D_MODULE
|
||||||
if (m_modelNode3DImageViewData.rootItem) {
|
if (m_modelNode3DImageViewData.rootItem) {
|
||||||
@@ -1059,19 +1064,19 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
|
|||||||
m_modelNode3DImageViewData.contentItem = getContentItemForRendering(m_modelNode3DImageViewData.rootItem);
|
m_modelNode3DImageViewData.contentItem = getContentItemForRendering(m_modelNode3DImageViewData.rootItem);
|
||||||
|
|
||||||
QImage renderImage;
|
QImage renderImage;
|
||||||
if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) {
|
if (m_modelNodePreviewImageCache.contains(cmd.componentPath())) {
|
||||||
renderImage = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()];
|
renderImage = m_modelNodePreviewImageCache[cmd.componentPath()];
|
||||||
} else {
|
} else {
|
||||||
bool createdFromComponent = false;
|
bool createdFromComponent = false;
|
||||||
QObject *instanceObj = nullptr;
|
QObject *instanceObj = nullptr;
|
||||||
ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId());
|
ServerNodeInstance instance = instanceForId(cmd.instanceId());
|
||||||
if (!m_modelNodePreviewImageCommand.componentPath().isEmpty()
|
if (!cmd.componentPath().isEmpty()
|
||||||
&& instance.isSubclassOf("QQuick3DNode")) {
|
&& instance.isSubclassOf("QQuick3DNode")) {
|
||||||
// Create a new instance for Node components, as using Nodes in multiple
|
// Create a new instance for Node components, as using Nodes in multiple
|
||||||
// import scenes simultaneously isn't supported. And even if it was, we still
|
// import scenes simultaneously isn't supported. And even if it was, we still
|
||||||
// wouldn't want the children of the Node to appear in the preview.
|
// wouldn't want the children of the Node to appear in the preview.
|
||||||
QQmlComponent component(engine());
|
QQmlComponent component(engine());
|
||||||
component.loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath()));
|
component.loadUrl(QUrl::fromLocalFile(cmd.componentPath()));
|
||||||
instanceObj = qobject_cast<QQuick3DObject *>(component.create());
|
instanceObj = qobject_cast<QQuick3DObject *>(component.create());
|
||||||
if (!instanceObj) {
|
if (!instanceObj) {
|
||||||
qWarning() << "Could not create preview component: " << component.errors();
|
qWarning() << "Could not create preview component: " << component.errors();
|
||||||
@@ -1081,7 +1086,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
|
|||||||
} else {
|
} else {
|
||||||
instanceObj = instance.internalObject();
|
instanceObj = instance.internalObject();
|
||||||
}
|
}
|
||||||
QSize renderSize = m_modelNodePreviewImageCommand.size();
|
QSize renderSize = cmd.size();
|
||||||
if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) {
|
if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) {
|
||||||
// Requested size is already adjusted for target pixel ratio, so we have to adjust
|
// Requested size is already adjusted for target pixel ratio, so we have to adjust
|
||||||
// back if ratio is not default for our window.
|
// back if ratio is not default for our window.
|
||||||
@@ -1138,13 +1143,13 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
|
|||||||
|
|
||||||
if (createdFromComponent) {
|
if (createdFromComponent) {
|
||||||
// If component changes, puppet will need a reset anyway, so we can cache the image
|
// If component changes, puppet will need a reset anyway, so we can cache the image
|
||||||
m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(),
|
m_modelNodePreviewImageCache.insert(cmd.componentPath(),
|
||||||
renderImage);
|
renderImage);
|
||||||
delete instanceObj;
|
delete instanceObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Key number is selected so that it is unlikely to conflict other ImageContainer use.
|
// Key number is selected so that it is unlikely to conflict other ImageContainer use.
|
||||||
ImageContainer imgContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001);
|
ImageContainer imgContainer(cmd.instanceId(), {}, 2100000001 + cmd.instanceId());
|
||||||
imgContainer.setImage(renderImage);
|
imgContainer.setImage(renderImage);
|
||||||
|
|
||||||
// send the rendered image to creator process
|
// send the rendered image to creator process
|
||||||
@@ -1175,23 +1180,23 @@ static QRectF itemBoundingRect(QQuickItem *item)
|
|||||||
return itemRect;
|
return itemRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView()
|
void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView(const RequestModelNodePreviewImageCommand &cmd)
|
||||||
{
|
{
|
||||||
if (m_modelNode2DImageViewData.rootItem) {
|
if (m_modelNode2DImageViewData.rootItem) {
|
||||||
if (!m_modelNode2DImageViewData.contentItem)
|
if (!m_modelNode2DImageViewData.contentItem)
|
||||||
m_modelNode2DImageViewData.contentItem = getContentItemForRendering(m_modelNode2DImageViewData.rootItem);
|
m_modelNode2DImageViewData.contentItem = getContentItemForRendering(m_modelNode2DImageViewData.rootItem);
|
||||||
|
|
||||||
// Key number is the same as in 3D case as they produce image for same purpose
|
// Key number is the same as in 3D case as they produce image for same purpose
|
||||||
auto imgContainer = ImageContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001);
|
auto imgContainer = ImageContainer(cmd.instanceId(), {}, 2100000001 + cmd.instanceId());
|
||||||
QImage renderImage;
|
QImage renderImage;
|
||||||
if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) {
|
if (m_modelNodePreviewImageCache.contains(cmd.componentPath())) {
|
||||||
renderImage = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()];
|
renderImage = m_modelNodePreviewImageCache[cmd.componentPath()];
|
||||||
} else {
|
} else {
|
||||||
QQuickItem *instanceItem = nullptr;
|
QQuickItem *instanceItem = nullptr;
|
||||||
|
|
||||||
if (!m_modelNodePreviewImageCommand.componentPath().isEmpty()) {
|
if (!cmd.componentPath().isEmpty()) {
|
||||||
QQmlComponent component(engine());
|
QQmlComponent component(engine());
|
||||||
component.loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath()));
|
component.loadUrl(QUrl::fromLocalFile(cmd.componentPath()));
|
||||||
instanceItem = qobject_cast<QQuickItem *>(component.create());
|
instanceItem = qobject_cast<QQuickItem *>(component.create());
|
||||||
if (!instanceItem) {
|
if (!instanceItem) {
|
||||||
qWarning() << "Could not create preview component: " << component.errors();
|
qWarning() << "Could not create preview component: " << component.errors();
|
||||||
@@ -1207,7 +1212,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView()
|
|||||||
// Some component may expect to always be shown at certain size, so their layouts may
|
// Some component may expect to always be shown at certain size, so their layouts may
|
||||||
// not support scaling, so let's always render at the default size if item has one and
|
// not support scaling, so let's always render at the default size if item has one and
|
||||||
// scale the resulting image instead.
|
// scale the resulting image instead.
|
||||||
QSize finalSize = m_modelNodePreviewImageCommand.size();
|
QSize finalSize = cmd.size();
|
||||||
QRectF renderRect = itemBoundingRect(instanceItem);
|
QRectF renderRect = itemBoundingRect(instanceItem);
|
||||||
QSize renderSize = renderRect.size().toSize();
|
QSize renderSize = renderRect.size().toSize();
|
||||||
if (renderSize.isEmpty()) {
|
if (renderSize.isEmpty()) {
|
||||||
@@ -1247,7 +1252,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView()
|
|||||||
delete instanceItem;
|
delete instanceItem;
|
||||||
|
|
||||||
// If component changes, puppet will need a reset anyway, so we can cache the image
|
// If component changes, puppet will need a reset anyway, so we can cache the image
|
||||||
m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(), renderImage);
|
m_modelNodePreviewImageCache.insert(cmd.componentPath(), renderImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!renderImage.isNull()) {
|
if (!renderImage.isNull()) {
|
||||||
@@ -2229,7 +2234,7 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
|
|||||||
|
|
||||||
void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command)
|
void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command)
|
||||||
{
|
{
|
||||||
m_modelNodePreviewImageCommand = command;
|
m_modelNodePreviewImageCommands.insert(command);
|
||||||
renderModelNodeImageView();
|
renderModelNodeImageView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -139,8 +139,8 @@ private:
|
|||||||
void doRender3DEditView();
|
void doRender3DEditView();
|
||||||
void renderModelNodeImageView();
|
void renderModelNodeImageView();
|
||||||
void doRenderModelNodeImageView();
|
void doRenderModelNodeImageView();
|
||||||
void doRenderModelNode3DImageView();
|
void doRenderModelNode3DImageView(const RequestModelNodePreviewImageCommand &cmd);
|
||||||
void doRenderModelNode2DImageView();
|
void doRenderModelNode2DImageView(const RequestModelNodePreviewImageCommand &cmd);
|
||||||
void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances);
|
void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances);
|
||||||
void handleInputEvents();
|
void handleInputEvents();
|
||||||
void resolveImportSupport();
|
void resolveImportSupport();
|
||||||
@@ -159,7 +159,7 @@ private:
|
|||||||
RenderViewData m_modelNode2DImageViewData;
|
RenderViewData m_modelNode2DImageViewData;
|
||||||
|
|
||||||
bool m_editView3DSetupDone = false;
|
bool m_editView3DSetupDone = false;
|
||||||
RequestModelNodePreviewImageCommand m_modelNodePreviewImageCommand;
|
QSet<RequestModelNodePreviewImageCommand> m_modelNodePreviewImageCommands;
|
||||||
QHash<QString, QImage> m_modelNodePreviewImageCache;
|
QHash<QString, QImage> m_modelNodePreviewImageCache;
|
||||||
QSet<QObject *> m_view3Ds;
|
QSet<QObject *> m_view3Ds;
|
||||||
QMultiHash<QObject *, QObject *> m_3DSceneMap; // key: scene root, value: node
|
QMultiHash<QObject *, QObject *> m_3DSceneMap; // key: scene root, value: node
|
||||||
|
@@ -442,10 +442,12 @@ Item {
|
|||||||
width: parent.width - addAssetButton.width - 5
|
width: parent.width - addAssetButton.width - 5
|
||||||
}
|
}
|
||||||
|
|
||||||
PlusButton {
|
IconButton {
|
||||||
id: addAssetButton
|
id: addAssetButton
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
tooltip: qsTr("Add a new asset to the project.")
|
tooltip: qsTr("Add a new asset to the project.")
|
||||||
|
icon: StudioTheme.Constants.plus
|
||||||
|
buttonSize: parent.height
|
||||||
|
|
||||||
onClicked: rootView.handleAddAsset()
|
onClicked: rootView.handleAddAsset()
|
||||||
}
|
}
|
||||||
|
@@ -218,12 +218,15 @@ Item {
|
|||||||
SearchBox {
|
SearchBox {
|
||||||
id: searchBox
|
id: searchBox
|
||||||
|
|
||||||
width: parent.width - addAssetButton.width - 5
|
width: parent.width - addModuleButton.width - 5
|
||||||
}
|
}
|
||||||
|
|
||||||
PlusButton {
|
IconButton {
|
||||||
id: addAssetButton
|
id: addModuleButton
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
tooltip: qsTr("Add a module.")
|
tooltip: qsTr("Add a module.")
|
||||||
|
icon: StudioTheme.Constants.plus
|
||||||
|
buttonSize: parent.height
|
||||||
|
|
||||||
onClicked: isAddModuleView = true
|
onClicked: isAddModuleView = true
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,221 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import HelperWidgets 2.0
|
||||||
|
import StudioControls 1.0 as StudioControls
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property int cellWidth: 100
|
||||||
|
readonly property int cellHeight: 120
|
||||||
|
|
||||||
|
property var currentMaterial: null
|
||||||
|
property int currentMaterialIdx: 0
|
||||||
|
|
||||||
|
// Called also from C++ to close context menu on focus out
|
||||||
|
function closeContextMenu()
|
||||||
|
{
|
||||||
|
contextMenu.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from C++ to refresh a preview material after it changes
|
||||||
|
function refreshPreview(idx)
|
||||||
|
{
|
||||||
|
var item = gridRepeater.itemAt(idx);
|
||||||
|
if (item)
|
||||||
|
item.refreshPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from C++
|
||||||
|
function clearSearchFilter()
|
||||||
|
{
|
||||||
|
searchBox.clearSearchFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: rootMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.currentMaterial = null
|
||||||
|
contextMenu.popup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: materialBrowserModel
|
||||||
|
|
||||||
|
function onSelectedIndexChanged() {
|
||||||
|
// commit rename upon changing selection
|
||||||
|
var item = gridRepeater.itemAt(currentMaterialIdx);
|
||||||
|
if (item)
|
||||||
|
item.commitRename();
|
||||||
|
|
||||||
|
currentMaterialIdx = materialBrowserModel.selectedIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.Menu {
|
||||||
|
id: contextMenu
|
||||||
|
|
||||||
|
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
text: qsTr("Apply to selected (replace)")
|
||||||
|
enabled: currentMaterial
|
||||||
|
onTriggered: materialBrowserModel.applyToSelected(currentMaterial.materialInternalId, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
text: qsTr("Apply to selected (add)")
|
||||||
|
enabled: currentMaterial
|
||||||
|
onTriggered: materialBrowserModel.applyToSelected(currentMaterial.materialInternalId, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
text: qsTr("Rename")
|
||||||
|
enabled: currentMaterial
|
||||||
|
onTriggered: {
|
||||||
|
var item = gridRepeater.itemAt(currentMaterialIdx);
|
||||||
|
if (item)
|
||||||
|
item.startRename();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
text: qsTr("Delete")
|
||||||
|
enabled: currentMaterial
|
||||||
|
|
||||||
|
onTriggered: materialBrowserModel.deleteMaterial(currentMaterial.materialInternalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuSeparator {}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
text: qsTr("New Material")
|
||||||
|
|
||||||
|
onTriggered: materialBrowserModel.addNewMaterial()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: col
|
||||||
|
y: 5
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
SearchBox {
|
||||||
|
id: searchBox
|
||||||
|
|
||||||
|
width: root.width - addMaterialButton.width
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: addMaterialButton
|
||||||
|
|
||||||
|
tooltip: qsTr("Add a material.")
|
||||||
|
|
||||||
|
icon: StudioTheme.Constants.plus
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
buttonSize: searchBox.height
|
||||||
|
onClicked: materialBrowserModel.addNewMaterial()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: qsTr("No match found.");
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
leftPadding: 10
|
||||||
|
visible: materialBrowserModel.hasQuick3DImport && materialBrowserModel.isEmpty && !searchBox.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: qsTr("No materials yet.");
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||||
|
topPadding: 30
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: materialBrowserModel.hasQuick3DImport && materialBrowserModel.isEmpty && searchBox.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: qsTr("Add QtQuick3D module using the Components view to enable the Material Browser.");
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||||
|
topPadding: 30
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: root.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: !materialBrowserModel.hasQuick3DImport
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: scrollView
|
||||||
|
|
||||||
|
width: root.width
|
||||||
|
height: root.height - searchBox.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: grid
|
||||||
|
|
||||||
|
width: scrollView.width
|
||||||
|
leftPadding: 5
|
||||||
|
rightPadding: 5
|
||||||
|
bottomPadding: 5
|
||||||
|
columns: root.width / root.cellWidth
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: gridRepeater
|
||||||
|
|
||||||
|
model: materialBrowserModel
|
||||||
|
delegate: MaterialItem {
|
||||||
|
width: root.cellWidth
|
||||||
|
height: root.cellHeight
|
||||||
|
|
||||||
|
onShowContextMenu: {
|
||||||
|
if (searchBox.isEmpty()) {
|
||||||
|
root.currentMaterial = model
|
||||||
|
contextMenu.popup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,134 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import HelperWidgets 2.0
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal showContextMenu()
|
||||||
|
|
||||||
|
function refreshPreview()
|
||||||
|
{
|
||||||
|
img.source = ""
|
||||||
|
img.source = "image://materialBrowser/" + materialInternalId
|
||||||
|
}
|
||||||
|
|
||||||
|
function startRename()
|
||||||
|
{
|
||||||
|
matName.readOnly = false
|
||||||
|
matName.selectAll()
|
||||||
|
matName.forceActiveFocus()
|
||||||
|
nameMouseArea.enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitRename()
|
||||||
|
{
|
||||||
|
if (matName.readOnly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
matName.readOnly = true
|
||||||
|
nameMouseArea.enabled = true
|
||||||
|
|
||||||
|
materialBrowserModel.renameMaterial(index, matName.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
border.width: materialBrowserModel.selectedIndex === index ? 1 : 0
|
||||||
|
border.color: materialBrowserModel.selectedIndex === index
|
||||||
|
? StudioTheme.Values.themeControlOutlineInteraction
|
||||||
|
: "transparent"
|
||||||
|
color: "transparent"
|
||||||
|
visible: materialVisible
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
materialBrowserModel.selectMaterial(index)
|
||||||
|
if (mouse.button === Qt.RightButton)
|
||||||
|
root.showContextMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
onDoubleClicked: materialBrowserModel.openMaterialEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
Item { width: 1; height: 5 } // spacer
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img
|
||||||
|
|
||||||
|
width: root.width - 10
|
||||||
|
height: img.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
source: "image://materialBrowser/" + materialInternalId
|
||||||
|
cache: false
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: matName
|
||||||
|
|
||||||
|
text: materialName
|
||||||
|
|
||||||
|
width: img.width
|
||||||
|
clip: true
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: TextInput.AlignHCenter
|
||||||
|
|
||||||
|
font.pixelSize: StudioTheme.Values.myFontSize
|
||||||
|
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: !matName.readOnly
|
||||||
|
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
selectionColor: StudioTheme.Values.themeTextSelectionColor
|
||||||
|
selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
|
||||||
|
|
||||||
|
// allow only alphanumeric characters, underscores, no space at start, and 1 space between words
|
||||||
|
validator: RegExpValidator { regExp: /^(\w+\s)*\w+$/ }
|
||||||
|
|
||||||
|
onEditingFinished: root.commitRename()
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: nameMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: materialBrowserModel.selectMaterial(index)
|
||||||
|
onDoubleClicked: root.startRename()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import HelperWidgets 2.0
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
PropertyEditorPane {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal toolBarAction(int action)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: col
|
||||||
|
|
||||||
|
MaterialEditorToolBar {
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
onToolBarAction: (action) => root.toolBarAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: root.width - 2 * col.padding
|
||||||
|
height: 150
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: hasQuick3DImport ? qsTr("No materials yet.\nClick 'Add new material' above to start.")
|
||||||
|
: qsTr("Add QtQuick3D module using the Components view to enable the Material Editor.")
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: root.width
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,55 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialEditorTopSection {
|
||||||
|
id: topSection
|
||||||
|
|
||||||
|
onToolBarAction: (action) => itemPane.toolBarAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 1; height: 10 }
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: specificsOne
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
source: specificsUrl
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,91 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import 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: "transparent"
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasMaterial && hasQuick3DImport
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
|
||||||
|
tooltip: qsTr("Apply material to selected model.")
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: StudioTheme.Constants.newMaterial
|
||||||
|
|
||||||
|
normalColor: "transparent"
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasQuick3DImport
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.AddNewMaterial)
|
||||||
|
tooltip: qsTr("Add a new material.")
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: StudioTheme.Constants.deleteMaterial
|
||||||
|
|
||||||
|
normalColor: "transparent"
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasMaterial && hasQuick3DImport
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentMaterial)
|
||||||
|
tooltip: qsTr("Delete current material.")
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon: StudioTheme.Constants.openMaterialBrowser
|
||||||
|
|
||||||
|
normalColor: "transparent"
|
||||||
|
iconSize: StudioTheme.Values.bigIconFontSize
|
||||||
|
buttonSize: root.height
|
||||||
|
enabled: hasMaterial && hasQuick3DImport
|
||||||
|
onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
|
||||||
|
tooltip: qsTr("Open material browser.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,125 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuickDesignerTheme 1.0
|
||||||
|
import QtQuick.Templates 2.15 as T
|
||||||
|
import HelperWidgets 2.0
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal toolBarAction(int action)
|
||||||
|
|
||||||
|
function refreshPreview()
|
||||||
|
{
|
||||||
|
materialPreview.source = ""
|
||||||
|
materialPreview.source = "image://materialEditor/preview"
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
MaterialEditorToolBar {
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
onToolBarAction: (action) => root.toolBarAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 1; height: 10 } // spacer
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 152
|
||||||
|
height: 152
|
||||||
|
color: "#000000"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: materialPreview
|
||||||
|
width: 150
|
||||||
|
height: 150
|
||||||
|
anchors.centerIn: parent
|
||||||
|
source: "image://materialEditor/preview"
|
||||||
|
cache: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
// Section with hidden header is used so properties are aligned with the other sections' properties
|
||||||
|
hideHeader: true
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
SectionLayout {
|
||||||
|
PropertyLabel { text: qsTr("Name") }
|
||||||
|
|
||||||
|
SecondColumnLayout {
|
||||||
|
Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
|
||||||
|
|
||||||
|
LineEdit {
|
||||||
|
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||||
|
width: StudioTheme.Values.singleControlColumnWidth
|
||||||
|
backendValue: backendValues.objectName
|
||||||
|
placeholderText: qsTr("Material name")
|
||||||
|
|
||||||
|
text: backendValues.id.value
|
||||||
|
showTranslateCheckBox: false
|
||||||
|
showExtendedFunctionButton: false
|
||||||
|
|
||||||
|
// allow only alphanumeric characters, underscores, no space at start, and 1 space between words
|
||||||
|
validator: RegExpValidator { regExp: /^(\w+\s)*\w+$/ }
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpandingSpacer {}
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyLabel { text: qsTr("Type") }
|
||||||
|
|
||||||
|
SecondColumnLayout {
|
||||||
|
Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
currentIndex: {
|
||||||
|
if (backendValues.__classNamePrivateInternal.value === "CustomMaterial")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if (backendValues.__classNamePrivateInternal.value === "PrincipledMaterial")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
model: ["DefaultMaterial", "PrincipledMaterial", "CustomMaterial"]
|
||||||
|
showExtendedFunctionButton: false
|
||||||
|
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||||
|
|
||||||
|
onActivated: changeTypeName(currentValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -74,6 +74,29 @@ StudioControls.ComboBox {
|
|||||||
|
|
||||||
property alias colorLogic: colorLogic
|
property alias colorLogic: colorLogic
|
||||||
|
|
||||||
|
DropArea {
|
||||||
|
id: dropArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property string assetPath: ""
|
||||||
|
|
||||||
|
onEntered: (drag) => {
|
||||||
|
dropArea.assetPath = drag.getDataAsString(drag.keys[0]).split(",")[0]
|
||||||
|
|
||||||
|
drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.isSupportedDrop(dropArea.assetPath)
|
||||||
|
comboBox.hasActiveDrag = drag.accepted
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: comboBox.hasActiveDrag = false
|
||||||
|
|
||||||
|
onDropped: {
|
||||||
|
comboBox.backendValue.commitDrop(dropArea.assetPath)
|
||||||
|
comboBox.hasActiveDrag = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ExtendedFunctionLogic {
|
ExtendedFunctionLogic {
|
||||||
id: extFuncLogic
|
id: extFuncLogic
|
||||||
backendValue: comboBox.backendValue
|
backendValue: comboBox.backendValue
|
||||||
|
@@ -26,54 +26,58 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuickDesignerTheme 1.0
|
import QtQuickDesignerTheme 1.0
|
||||||
import HelperWidgets 2.0 as HelperWidgets
|
|
||||||
import StudioControls 1.0 as StudioControls
|
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string tooltip: ""
|
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
implicitWidth: 29
|
property alias icon: icon.text
|
||||||
implicitHeight: 29
|
property alias enabled: mouseArea.enabled
|
||||||
color: mouseArea.containsMouse && enabled
|
property alias tooltip: toolTip.text
|
||||||
? StudioTheme.Values.themeControlBackgroundHover
|
property alias iconSize: icon.font.pixelSize
|
||||||
: StudioTheme.Values.themeControlBackground
|
|
||||||
|
property int buttonSize: StudioTheme.Values.height
|
||||||
|
property color normalColor: StudioTheme.Values.themeControlBackground
|
||||||
|
property color hoverColor: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
property color pressColor: StudioTheme.Values.themeControlBackgroundInteraction
|
||||||
|
|
||||||
|
width: buttonSize
|
||||||
|
height: buttonSize
|
||||||
|
|
||||||
|
color: mouseArea.pressed ? pressColor
|
||||||
|
: mouseArea.containsMouse ? hoverColor
|
||||||
|
: normalColor
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: StudioTheme.Values.hoverDuration
|
duration: 300
|
||||||
easing.type: StudioTheme.Values.hoverEasing
|
easing.type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label { // + sign
|
Text {
|
||||||
text: StudioTheme.Constants.plus
|
id: icon
|
||||||
|
|
||||||
|
color: root.enabled ? StudioTheme.Values.themeTextColor : StudioTheme.Values.themeTextColorDisabled
|
||||||
font.family: StudioTheme.Constants.iconFont.family
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
font.pixelSize: StudioTheme.Values.myIconFontSize
|
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
||||||
verticalAlignment: Text.AlignVCenter
|
anchors.centerIn: root
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: root.enabled ? StudioTheme.Values.themeIconColor
|
|
||||||
: StudioTheme.Values.themeIconColorDisabled
|
|
||||||
scale: mouseArea.containsMouse ? 1.4 : 1
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.OutExpo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HelperWidgets.ToolTipArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: root.clicked()
|
onClicked: root.clicked()
|
||||||
tooltip: root.tooltip
|
}
|
||||||
|
|
||||||
|
ToolTip {
|
||||||
|
id: toolTip
|
||||||
|
|
||||||
|
visible: mouseArea.containsMouse
|
||||||
|
delay: 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -26,8 +26,6 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuickDesignerTheme 1.0
|
import QtQuickDesignerTheme 1.0
|
||||||
import HelperWidgets 2.0 as HelperWidgets
|
|
||||||
import StudioControls 1.0 as StudioControls
|
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -87,7 +85,7 @@ Item {
|
|||||||
Label {
|
Label {
|
||||||
text: StudioTheme.Constants.search
|
text: StudioTheme.Constants.search
|
||||||
font.family: StudioTheme.Constants.iconFont.family
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
font.pixelSize: 16
|
font.pixelSize: StudioTheme.Values.myIconFontSize
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 7
|
anchors.leftMargin: 7
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
@@ -37,6 +37,7 @@ GradientPropertySpinBox 2.0 GradientPropertySpinBox.qml
|
|||||||
HorizontalScrollBar 2.0 HorizontalScrollBar.qml
|
HorizontalScrollBar 2.0 HorizontalScrollBar.qml
|
||||||
HueSlider 2.0 HueSlider.qml
|
HueSlider 2.0 HueSlider.qml
|
||||||
IconIndicator 2.0 IconIndicator.qml
|
IconIndicator 2.0 IconIndicator.qml
|
||||||
|
IconButton 2.0 IconButton.qml
|
||||||
IconLabel 2.0 IconLabel.qml
|
IconLabel 2.0 IconLabel.qml
|
||||||
ImagePreviewTooltipArea 2.0 ImagePreviewTooltipArea.qml
|
ImagePreviewTooltipArea 2.0 ImagePreviewTooltipArea.qml
|
||||||
ImageSection 2.0 ImageSection.qml
|
ImageSection 2.0 ImageSection.qml
|
||||||
@@ -59,6 +60,7 @@ RoundedPanel 2.0 RoundedPanel.qml
|
|||||||
ScrollView 2.0 ScrollView.qml
|
ScrollView 2.0 ScrollView.qml
|
||||||
SecondColumnLayout 2.0 SecondColumnLayout.qml
|
SecondColumnLayout 2.0 SecondColumnLayout.qml
|
||||||
Section 2.0 Section.qml
|
Section 2.0 Section.qml
|
||||||
|
SearchBox 2.0 SearchBox.qml
|
||||||
SectionLayout 2.0 SectionLayout.qml
|
SectionLayout 2.0 SectionLayout.qml
|
||||||
Spacer 2.0 Spacer.qml
|
Spacer 2.0 Spacer.qml
|
||||||
SpinBox 2.0 SpinBox.qml
|
SpinBox 2.0 SpinBox.qml
|
||||||
|
@@ -39,6 +39,7 @@ T.ComboBox {
|
|||||||
&& myComboBox.enabled
|
&& myComboBox.enabled
|
||||||
property bool edit: myComboBox.activeFocus && myComboBox.editable
|
property bool edit: myComboBox.activeFocus && myComboBox.editable
|
||||||
property bool open: comboBoxPopup.opened
|
property bool open: comboBoxPopup.opened
|
||||||
|
property bool hasActiveDrag: false
|
||||||
|
|
||||||
property bool dirty: false // user modification flag
|
property bool dirty: false // user modification flag
|
||||||
|
|
||||||
@@ -251,7 +252,8 @@ T.ComboBox {
|
|||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: comboBoxBackground
|
target: comboBoxBackground
|
||||||
color: StudioTheme.Values.themeControlBackground
|
color: StudioTheme.Values.themeControlBackground
|
||||||
border.color: StudioTheme.Values.themeControlOutline
|
border.color: hasActiveDrag ? StudioTheme.Values.themeInteraction
|
||||||
|
: StudioTheme.Values.themeControlOutline
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// This state is intended for ComboBoxes which aren't editable, but have focus e.g. via
|
// This state is intended for ComboBoxes which aren't editable, but have focus e.g. via
|
||||||
|
@@ -64,125 +64,130 @@ QtObject {
|
|||||||
readonly property string animatedProperty: "\u003B"
|
readonly property string animatedProperty: "\u003B"
|
||||||
readonly property string annotationBubble: "\u003C"
|
readonly property string annotationBubble: "\u003C"
|
||||||
readonly property string annotationDecal: "\u003D"
|
readonly property string annotationDecal: "\u003D"
|
||||||
readonly property string assign: "\u003E"
|
readonly property string applyMaterialToSelected: "\u003E"
|
||||||
readonly property string bevelAll: "\u003F"
|
readonly property string assign: "\u003F"
|
||||||
readonly property string bevelCorner: "\u0040"
|
readonly property string bevelAll: "\u0040"
|
||||||
readonly property string centerHorizontal: "\u0041"
|
readonly property string bevelCorner: "\u0041"
|
||||||
readonly property string centerVertical: "\u0042"
|
readonly property string centerHorizontal: "\u0042"
|
||||||
readonly property string closeCross: "\u0043"
|
readonly property string centerVertical: "\u0043"
|
||||||
readonly property string colorPopupClose: "\u0044"
|
readonly property string closeCross: "\u0044"
|
||||||
readonly property string columnsAndRows: "\u0045"
|
readonly property string colorPopupClose: "\u0045"
|
||||||
readonly property string copyStyle: "\u0046"
|
readonly property string columnsAndRows: "\u0046"
|
||||||
readonly property string cornerA: "\u0047"
|
readonly property string copyStyle: "\u0047"
|
||||||
readonly property string cornerB: "\u0048"
|
readonly property string cornerA: "\u0048"
|
||||||
readonly property string cornersAll: "\u0049"
|
readonly property string cornerB: "\u0049"
|
||||||
readonly property string curveDesigner: "\u004A"
|
readonly property string cornersAll: "\u004A"
|
||||||
readonly property string curveEditor: "\u004B"
|
readonly property string curveDesigner: "\u004B"
|
||||||
readonly property string decisionNode: "\u004C"
|
readonly property string curveEditor: "\u004C"
|
||||||
readonly property string deleteColumn: "\u004D"
|
readonly property string customMaterialEditor: "\u004D"
|
||||||
readonly property string deleteRow: "\u004E"
|
readonly property string decisionNode: "\u004E"
|
||||||
readonly property string deleteTable: "\u004F"
|
readonly property string deleteColumn: "\u004F"
|
||||||
readonly property string detach: "\u0050"
|
readonly property string deleteMaterial: "\u0050"
|
||||||
readonly property string distributeBottom: "\u0051"
|
readonly property string deleteRow: "\u0051"
|
||||||
readonly property string distributeCenterHorizontal: "\u0052"
|
readonly property string deleteTable: "\u0052"
|
||||||
readonly property string distributeCenterVertical: "\u0053"
|
readonly property string detach: "\u0053"
|
||||||
readonly property string distributeLeft: "\u0054"
|
readonly property string distributeBottom: "\u0054"
|
||||||
readonly property string distributeOriginBottomRight: "\u0055"
|
readonly property string distributeCenterHorizontal: "\u0055"
|
||||||
readonly property string distributeOriginCenter: "\u0056"
|
readonly property string distributeCenterVertical: "\u0056"
|
||||||
readonly property string distributeOriginNone: "\u0057"
|
readonly property string distributeLeft: "\u0057"
|
||||||
readonly property string distributeOriginTopLeft: "\u0058"
|
readonly property string distributeOriginBottomRight: "\u0058"
|
||||||
readonly property string distributeRight: "\u0059"
|
readonly property string distributeOriginCenter: "\u0059"
|
||||||
readonly property string distributeSpacingHorizontal: "\u005A"
|
readonly property string distributeOriginNone: "\u005A"
|
||||||
readonly property string distributeSpacingVertical: "\u005B"
|
readonly property string distributeOriginTopLeft: "\u005B"
|
||||||
readonly property string distributeTop: "\u005C"
|
readonly property string distributeRight: "\u005C"
|
||||||
readonly property string download: "\u005D"
|
readonly property string distributeSpacingHorizontal: "\u005D"
|
||||||
readonly property string downloadUnavailable: "\u005E"
|
readonly property string distributeSpacingVertical: "\u005E"
|
||||||
readonly property string downloadUpdate: "\u005F"
|
readonly property string distributeTop: "\u005F"
|
||||||
readonly property string downloaded: "\u0060"
|
readonly property string download: "\u0060"
|
||||||
readonly property string edit: "\u0061"
|
readonly property string downloadUnavailable: "\u0061"
|
||||||
readonly property string eyeDropper: "\u0062"
|
readonly property string downloadUpdate: "\u0062"
|
||||||
readonly property string favorite: "\u0063"
|
readonly property string downloaded: "\u0063"
|
||||||
readonly property string flowAction: "\u0064"
|
readonly property string edit: "\u0064"
|
||||||
readonly property string flowTransition: "\u0065"
|
readonly property string eyeDropper: "\u0065"
|
||||||
readonly property string fontStyleBold: "\u0066"
|
readonly property string favorite: "\u0066"
|
||||||
readonly property string fontStyleItalic: "\u0067"
|
readonly property string flowAction: "\u0067"
|
||||||
readonly property string fontStyleStrikethrough: "\u0068"
|
readonly property string flowTransition: "\u0068"
|
||||||
readonly property string fontStyleUnderline: "\u0069"
|
readonly property string fontStyleBold: "\u0069"
|
||||||
readonly property string gradient: "\u006A"
|
readonly property string fontStyleItalic: "\u006A"
|
||||||
readonly property string gridView: "\u006B"
|
readonly property string fontStyleStrikethrough: "\u006B"
|
||||||
readonly property string idAliasOff: "\u006C"
|
readonly property string fontStyleUnderline: "\u006C"
|
||||||
readonly property string idAliasOn: "\u006D"
|
readonly property string gradient: "\u006D"
|
||||||
readonly property string infinity: "\u006E"
|
readonly property string gridView: "\u006E"
|
||||||
readonly property string keyframe: "\u006F"
|
readonly property string idAliasOff: "\u006F"
|
||||||
readonly property string linkTriangle: "\u0070"
|
readonly property string idAliasOn: "\u0070"
|
||||||
readonly property string linked: "\u0071"
|
readonly property string infinity: "\u0071"
|
||||||
readonly property string listView: "\u0072"
|
readonly property string keyframe: "\u0072"
|
||||||
readonly property string lockOff: "\u0073"
|
readonly property string linkTriangle: "\u0073"
|
||||||
readonly property string lockOn: "\u0074"
|
readonly property string linked: "\u0074"
|
||||||
readonly property string mergeCells: "\u0075"
|
readonly property string listView: "\u0075"
|
||||||
readonly property string minus: "\u0076"
|
readonly property string lockOff: "\u0076"
|
||||||
readonly property string mirror: "\u0077"
|
readonly property string lockOn: "\u0077"
|
||||||
readonly property string orientation: "\u0078"
|
readonly property string mergeCells: "\u0078"
|
||||||
readonly property string paddingEdge: "\u0079"
|
readonly property string minus: "\u0079"
|
||||||
readonly property string paddingFrame: "\u007A"
|
readonly property string mirror: "\u007A"
|
||||||
readonly property string pasteStyle: "\u007B"
|
readonly property string newMaterial: "\u007B"
|
||||||
readonly property string pause: "\u007C"
|
readonly property string openMaterialBrowser: "\u007C"
|
||||||
readonly property string pin: "\u007D"
|
readonly property string orientation: "\u007D"
|
||||||
readonly property string play: "\u007E"
|
readonly property string paddingEdge: "\u007E"
|
||||||
readonly property string plus: "\u007F"
|
readonly property string paddingFrame: "\u007F"
|
||||||
readonly property string promote: "\u0080"
|
readonly property string pasteStyle: "\u0080"
|
||||||
readonly property string readOnly: "\u0081"
|
readonly property string pause: "\u0081"
|
||||||
readonly property string redo: "\u0082"
|
readonly property string pin: "\u0082"
|
||||||
readonly property string rotationFill: "\u0083"
|
readonly property string play: "\u0083"
|
||||||
readonly property string rotationOutline: "\u0084"
|
readonly property string plus: "\u0084"
|
||||||
readonly property string search: "\u0085"
|
readonly property string promote: "\u0085"
|
||||||
readonly property string sectionToggle: "\u0086"
|
readonly property string readOnly: "\u0086"
|
||||||
readonly property string splitColumns: "\u0087"
|
readonly property string redo: "\u0087"
|
||||||
readonly property string splitRows: "\u0088"
|
readonly property string rotationFill: "\u0088"
|
||||||
readonly property string startNode: "\u0089"
|
readonly property string rotationOutline: "\u0089"
|
||||||
readonly property string testIcon: "\u008A"
|
readonly property string search: "\u008A"
|
||||||
readonly property string textAlignBottom: "\u008B"
|
readonly property string sectionToggle: "\u008B"
|
||||||
readonly property string textAlignCenter: "\u008C"
|
readonly property string splitColumns: "\u008C"
|
||||||
readonly property string textAlignJustified: "\u008D"
|
readonly property string splitRows: "\u008D"
|
||||||
readonly property string textAlignLeft: "\u008E"
|
readonly property string startNode: "\u008E"
|
||||||
readonly property string textAlignMiddle: "\u008F"
|
readonly property string testIcon: "\u008F"
|
||||||
readonly property string textAlignRight: "\u0090"
|
readonly property string textAlignBottom: "\u0090"
|
||||||
readonly property string textAlignTop: "\u0091"
|
readonly property string textAlignCenter: "\u0091"
|
||||||
readonly property string textBulletList: "\u0092"
|
readonly property string textAlignJustified: "\u0092"
|
||||||
readonly property string textFullJustification: "\u0093"
|
readonly property string textAlignLeft: "\u0093"
|
||||||
readonly property string textNumberedList: "\u0094"
|
readonly property string textAlignMiddle: "\u0094"
|
||||||
readonly property string tickIcon: "\u0095"
|
readonly property string textAlignRight: "\u0095"
|
||||||
readonly property string translationCreateFiles: "\u0096"
|
readonly property string textAlignTop: "\u0096"
|
||||||
readonly property string translationCreateReport: "\u0097"
|
readonly property string textBulletList: "\u0097"
|
||||||
readonly property string translationExport: "\u0098"
|
readonly property string textFullJustification: "\u0098"
|
||||||
readonly property string translationImport: "\u0099"
|
readonly property string textNumberedList: "\u0099"
|
||||||
readonly property string translationSelectLanguages: "\u009A"
|
readonly property string tickIcon: "\u009A"
|
||||||
readonly property string translationTest: "\u009B"
|
readonly property string translationCreateFiles: "\u009B"
|
||||||
readonly property string transparent: "\u009D"
|
readonly property string translationCreateReport: "\u009D"
|
||||||
readonly property string triState: "\u009E"
|
readonly property string translationExport: "\u009E"
|
||||||
readonly property string triangleArcA: "\u009F"
|
readonly property string translationImport: "\u009F"
|
||||||
readonly property string triangleArcB: "\u00A0"
|
readonly property string translationSelectLanguages: "\u00A0"
|
||||||
readonly property string triangleCornerA: "\u00A1"
|
readonly property string translationTest: "\u00A1"
|
||||||
readonly property string triangleCornerB: "\u00A2"
|
readonly property string transparent: "\u00A2"
|
||||||
readonly property string unLinked: "\u00A3"
|
readonly property string triState: "\u00A3"
|
||||||
readonly property string undo: "\u00A4"
|
readonly property string triangleArcA: "\u00A4"
|
||||||
readonly property string unpin: "\u00A5"
|
readonly property string triangleArcB: "\u00A5"
|
||||||
readonly property string upDownIcon: "\u00A6"
|
readonly property string triangleCornerA: "\u00A6"
|
||||||
readonly property string upDownSquare2: "\u00A7"
|
readonly property string triangleCornerB: "\u00A7"
|
||||||
readonly property string visibilityOff: "\u00A8"
|
readonly property string unLinked: "\u00A8"
|
||||||
readonly property string visibilityOn: "\u00A9"
|
readonly property string undo: "\u00A9"
|
||||||
readonly property string wildcard: "\u00AA"
|
readonly property string unpin: "\u00AA"
|
||||||
readonly property string wizardsAutomotive: "\u00AB"
|
readonly property string upDownIcon: "\u00AB"
|
||||||
readonly property string wizardsDesktop: "\u00AC"
|
readonly property string upDownSquare2: "\u00AC"
|
||||||
readonly property string wizardsGeneric: "\u00AE"
|
readonly property string visibilityOff: "\u00AE"
|
||||||
readonly property string wizardsMcuEmpty: "\u00AF"
|
readonly property string visibilityOn: "\u00AF"
|
||||||
readonly property string wizardsMcuGraph: "\u00B0"
|
readonly property string wildcard: "\u00B0"
|
||||||
readonly property string wizardsMobile: "\u00B1"
|
readonly property string wizardsAutomotive: "\u00B1"
|
||||||
readonly property string wizardsUnknown: "\u00B2"
|
readonly property string wizardsDesktop: "\u00B2"
|
||||||
readonly property string zoomAll: "\u00B3"
|
readonly property string wizardsGeneric: "\u00B3"
|
||||||
readonly property string zoomIn: "\u00B4"
|
readonly property string wizardsMcuEmpty: "\u00B4"
|
||||||
readonly property string zoomOut: "\u00B5"
|
readonly property string wizardsMcuGraph: "\u00B5"
|
||||||
readonly property string zoomSelection: "\u00B6"
|
readonly property string wizardsMobile: "\u00B6"
|
||||||
|
readonly property string wizardsUnknown: "\u00B7"
|
||||||
|
readonly property string zoomAll: "\u00B8"
|
||||||
|
readonly property string zoomIn: "\u00B9"
|
||||||
|
readonly property string zoomOut: "\u00BA"
|
||||||
|
readonly property string zoomSelection: "\u00BB"
|
||||||
|
|
||||||
readonly property font iconFont: Qt.font({
|
readonly property font iconFont: Qt.font({
|
||||||
"family": controlIcons.name,
|
"family": controlIcons.name,
|
||||||
|
@@ -32,13 +32,21 @@ QtObject {
|
|||||||
|
|
||||||
property real baseHeight: 29
|
property real baseHeight: 29
|
||||||
property real baseFont: 12
|
property real baseFont: 12
|
||||||
|
property real mediumFont: 14
|
||||||
|
property real bigFont: 16
|
||||||
property real baseIconFont: 12
|
property real baseIconFont: 12
|
||||||
|
property real bigIconFont: 26
|
||||||
|
|
||||||
property real scaleFactor: 1.0
|
property real scaleFactor: 1.0
|
||||||
|
|
||||||
property real height: Math.round(values.baseHeight * values.scaleFactor)
|
property real height: Math.round(values.baseHeight * values.scaleFactor)
|
||||||
property real myFontSize: Math.round(values.baseFont * values.scaleFactor)
|
property real baseFontSize: Math.round(values.baseFont * values.scaleFactor)
|
||||||
property real myIconFontSize: Math.round(values.baseIconFont * values.scaleFactor)
|
property real myFontSize: values.baseFontSize // TODO: rename all refs to myFontSize -> baseFontSize then remove myFontSize
|
||||||
|
property real mediumFontSize: Math.round(values.mediumFont * values.scaleFactor)
|
||||||
|
property real bigFontSize: Math.round(values.bigFont * values.scaleFactor)
|
||||||
|
property real baseIconFontSize: Math.round(values.baseIconFont * values.scaleFactor)
|
||||||
|
property real myIconFontSize: values.baseIconFontSize; // TODO: rename all refs to myIconFontSize -> baseIconFontSize then remove myIconFontSize
|
||||||
|
property real bigIconFontSize: Math.round(values.bigIconFont * values.scaleFactor)
|
||||||
|
|
||||||
property real squareComponentWidth: values.height
|
property real squareComponentWidth: values.height
|
||||||
property real smallRectWidth: values.height / 2 * 1.5
|
property real smallRectWidth: values.height / 2 * 1.5
|
||||||
|
Binary file not shown.
@@ -312,6 +312,23 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
quick2propertyeditorview.cpp quick2propertyeditorview.h
|
quick2propertyeditorview.cpp quick2propertyeditorview.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
SOURCES_PREFIX components/materialeditor
|
||||||
|
SOURCES
|
||||||
|
materialeditorcontextobject.cpp materialeditorcontextobject.h
|
||||||
|
materialeditorqmlbackend.cpp materialeditorqmlbackend.h
|
||||||
|
materialeditortransaction.cpp materialeditortransaction.h
|
||||||
|
materialeditorview.cpp materialeditorview.h
|
||||||
|
)
|
||||||
|
|
||||||
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
SOURCES_PREFIX components/materialbrowser
|
||||||
|
SOURCES
|
||||||
|
materialbrowserview.cpp materialbrowserview.h
|
||||||
|
materialbrowserwidget.cpp materialbrowserwidget.h
|
||||||
|
materialbrowsermodel.cpp materialbrowsermodel.h
|
||||||
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(QmlDesigner
|
extend_qtc_plugin(QmlDesigner
|
||||||
SOURCES_PREFIX components
|
SOURCES_PREFIX components
|
||||||
SOURCES resources/resources.qrc
|
SOURCES resources/resources.qrc
|
||||||
|
@@ -80,6 +80,7 @@ const char mergeTemplateCommandId[] = "MergeTemplate";
|
|||||||
const char goToImplementationCommandId[] = "GoToImplementation";
|
const char goToImplementationCommandId[] = "GoToImplementation";
|
||||||
const char addSignalHandlerCommandId[] = "AddSignalHandler";
|
const char addSignalHandlerCommandId[] = "AddSignalHandler";
|
||||||
const char moveToComponentCommandId[] = "MoveToComponent";
|
const char moveToComponentCommandId[] = "MoveToComponent";
|
||||||
|
const char editMaterialCommandId[] = "EditMaterial";
|
||||||
const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
|
const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
|
||||||
const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer";
|
const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer";
|
||||||
const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer";
|
const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer";
|
||||||
@@ -134,6 +135,7 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen
|
|||||||
const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation");
|
const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation");
|
||||||
const char addSignalHandlerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add New Signal Handler");
|
const char addSignalHandlerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add New Signal Handler");
|
||||||
const char moveToComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Move Component into Separate File");
|
const char moveToComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Move Component into Separate File");
|
||||||
|
const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material");
|
||||||
const char editAnnotationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotation");
|
const char editAnnotationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotation");
|
||||||
|
|
||||||
const char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Signal Dialog");
|
const char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Signal Dialog");
|
||||||
|
@@ -1506,6 +1506,17 @@ void DesignerActionManager::createDefaultDesignerActions()
|
|||||||
&singleSelection,
|
&singleSelection,
|
||||||
&singleSelection));
|
&singleSelection));
|
||||||
|
|
||||||
|
addDesignerAction(new ModelNodeContextMenuAction(
|
||||||
|
editMaterialCommandId,
|
||||||
|
editMaterialDisplayName,
|
||||||
|
{},
|
||||||
|
rootCategory,
|
||||||
|
QKeySequence(),
|
||||||
|
44,
|
||||||
|
&editMaterial,
|
||||||
|
&modelHasMaterial,
|
||||||
|
&isModel));
|
||||||
|
|
||||||
addDesignerAction(new ModelNodeContextMenuAction(mergeTemplateCommandId,
|
addDesignerAction(new ModelNodeContextMenuAction(mergeTemplateCommandId,
|
||||||
mergeTemplateDisplayName,
|
mergeTemplateDisplayName,
|
||||||
{},
|
{},
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "modelnodeoperations.h"
|
#include "modelnodeoperations.h"
|
||||||
#include "abstractaction.h"
|
#include "abstractaction.h"
|
||||||
|
#include "bindingproperty.h"
|
||||||
#include "abstractactiongroup.h"
|
#include "abstractactiongroup.h"
|
||||||
#include "qmlitemnode.h"
|
#include "qmlitemnode.h"
|
||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
@@ -63,6 +64,24 @@ inline bool singleSelection(const SelectionContext &selectionState)
|
|||||||
return selectionState.singleNodeIsSelected();
|
return selectionState.singleNodeIsSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isModel(const SelectionContext &selectionState)
|
||||||
|
{
|
||||||
|
ModelNode node = selectionState.currentSingleSelectedNode();
|
||||||
|
return node.isValid() && node.isSubclassOf("QtQuick3D.Model");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool modelHasMaterial(const SelectionContext &selectionState)
|
||||||
|
{
|
||||||
|
ModelNode node = selectionState.currentSingleSelectedNode();
|
||||||
|
|
||||||
|
if (!node.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BindingProperty prop = node.bindingProperty("materials");
|
||||||
|
|
||||||
|
return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty());
|
||||||
|
}
|
||||||
|
|
||||||
inline bool selectionEnabled(const SelectionContext &selectionState)
|
inline bool selectionEnabled(const SelectionContext &selectionState)
|
||||||
{
|
{
|
||||||
return selectionState.showSelectionTools();
|
return selectionState.showSelectionTools();
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "modelnodeoperations.h"
|
#include "modelnodeoperations.h"
|
||||||
|
#include "designmodewidget.h"
|
||||||
#include "modelnodecontextmenu_helper.h"
|
#include "modelnodecontextmenu_helper.h"
|
||||||
#include "addimagesdialog.h"
|
#include "addimagesdialog.h"
|
||||||
#include "layoutingridlayout.h"
|
#include "layoutingridlayout.h"
|
||||||
@@ -789,6 +790,37 @@ void addNewSignalHandler(const SelectionContext &selectionState)
|
|||||||
addSignalHandlerOrGotoImplementation(selectionState, true);
|
addSignalHandlerOrGotoImplementation(selectionState, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open a model's material in the material editor
|
||||||
|
void editMaterial(const SelectionContext &selectionContext)
|
||||||
|
{
|
||||||
|
ModelNode modelNode = selectionContext.currentSingleSelectedNode();
|
||||||
|
QTC_ASSERT(modelNode.isValid(), return);
|
||||||
|
|
||||||
|
BindingProperty prop = modelNode.bindingProperty("materials");
|
||||||
|
if (!prop.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
AbstractView *view = selectionContext.view();
|
||||||
|
|
||||||
|
ModelNode material;
|
||||||
|
|
||||||
|
if (view->hasId(prop.expression())) {
|
||||||
|
material = view->modelNodeForId(prop.expression());
|
||||||
|
} else {
|
||||||
|
QList<ModelNode> materials = prop.resolveToModelNodeList();
|
||||||
|
|
||||||
|
if (materials.size() > 0)
|
||||||
|
material = materials.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material.isValid()) {
|
||||||
|
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor");
|
||||||
|
|
||||||
|
// to MaterialEditor and MaterialBrowser...
|
||||||
|
view->emitCustomNotification("selected_material_changed", {material});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void addItemToStackedContainer(const SelectionContext &selectionContext)
|
void addItemToStackedContainer(const SelectionContext &selectionContext)
|
||||||
{
|
{
|
||||||
AbstractView *view = selectionContext.view();
|
AbstractView *view = selectionContext.view();
|
||||||
|
@@ -67,6 +67,7 @@ void layoutColumnLayout(const SelectionContext &selectionState);
|
|||||||
void layoutGridLayout(const SelectionContext &selectionState);
|
void layoutGridLayout(const SelectionContext &selectionState);
|
||||||
void goImplementation(const SelectionContext &selectionState);
|
void goImplementation(const SelectionContext &selectionState);
|
||||||
void addNewSignalHandler(const SelectionContext &selectionState);
|
void addNewSignalHandler(const SelectionContext &selectionState);
|
||||||
|
void editMaterial(const SelectionContext &selectionContext);
|
||||||
void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot);
|
void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot);
|
||||||
void removeLayout(const SelectionContext &selectionContext);
|
void removeLayout(const SelectionContext &selectionContext);
|
||||||
void removePositioner(const SelectionContext &selectionContext);
|
void removePositioner(const SelectionContext &selectionContext);
|
||||||
|
@@ -72,6 +72,7 @@ public:
|
|||||||
animatedProperty,
|
animatedProperty,
|
||||||
annotationBubble,
|
annotationBubble,
|
||||||
annotationDecal,
|
annotationDecal,
|
||||||
|
applyMaterialToSelected,
|
||||||
assign,
|
assign,
|
||||||
bevelAll,
|
bevelAll,
|
||||||
bevelCorner,
|
bevelCorner,
|
||||||
@@ -86,8 +87,10 @@ public:
|
|||||||
cornersAll,
|
cornersAll,
|
||||||
curveDesigner,
|
curveDesigner,
|
||||||
curveEditor,
|
curveEditor,
|
||||||
|
customMaterialEditor,
|
||||||
decisionNode,
|
decisionNode,
|
||||||
deleteColumn,
|
deleteColumn,
|
||||||
|
deleteMaterial,
|
||||||
deleteRow,
|
deleteRow,
|
||||||
deleteTable,
|
deleteTable,
|
||||||
detach,
|
detach,
|
||||||
@@ -130,6 +133,8 @@ public:
|
|||||||
mergeCells,
|
mergeCells,
|
||||||
minus,
|
minus,
|
||||||
mirror,
|
mirror,
|
||||||
|
newMaterial,
|
||||||
|
openMaterialBrowser,
|
||||||
orientation,
|
orientation,
|
||||||
paddingEdge,
|
paddingEdge,
|
||||||
paddingFrame,
|
paddingFrame,
|
||||||
|
@@ -30,6 +30,11 @@
|
|||||||
#include "nodehints.h"
|
#include "nodehints.h"
|
||||||
#include "qmlvisualnode.h"
|
#include "qmlvisualnode.h"
|
||||||
|
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
@@ -181,13 +186,41 @@ void Edit3DCanvas::dragEnterEvent(QDragEnterEvent *e)
|
|||||||
|
|
||||||
void Edit3DCanvas::dropEvent(QDropEvent *e)
|
void Edit3DCanvas::dropEvent(QDropEvent *e)
|
||||||
{
|
{
|
||||||
Q_UNUSED(e)
|
|
||||||
|
|
||||||
auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode();
|
auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode();
|
||||||
|
QTC_ASSERT(modelNode.isValid(), return);
|
||||||
|
|
||||||
if (modelNode.isValid()) {
|
e->accept();
|
||||||
e->accept();
|
m_parent->view()->setSelectedModelNode(modelNode);
|
||||||
m_parent->view()->setSelectedModelNode(modelNode);
|
|
||||||
|
// if added node is a Model, assign it a material
|
||||||
|
if (modelNode.isSubclassOf("QtQuick3D.Model")) {
|
||||||
|
ModelNode matLib = m_parent->view()->modelNodeForId(Constants::MATERIAL_LIB_ID);
|
||||||
|
QTC_ASSERT(matLib.isValid(), return);
|
||||||
|
|
||||||
|
const QList<ModelNode> materials = matLib.directSubModelNodes();
|
||||||
|
ModelNode material;
|
||||||
|
if (materials.size() > 0) {
|
||||||
|
for (const ModelNode &mat : materials) {
|
||||||
|
if (mat.isSubclassOf("QtQuick3D.Material")) {
|
||||||
|
material = mat;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no valid material, create a new default material
|
||||||
|
if (!material.isValid()) {
|
||||||
|
NodeMetaInfo metaInfo = m_parent->view()->model()->metaInfo("QtQuick3D.DefaultMaterial");
|
||||||
|
material = m_parent->view()->createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(),
|
||||||
|
metaInfo.minorVersion());
|
||||||
|
VariantProperty matNameProp = material.variantProperty("objectName");
|
||||||
|
matNameProp.setValue("New Material");
|
||||||
|
material.validId();
|
||||||
|
matLib.defaultNodeListProperty().reparentHere(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingProperty modelMatsProp = modelNode.bindingProperty("materials");
|
||||||
|
modelMatsProp.setExpression(material.id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -67,16 +67,7 @@ void Edit3DView::createEdit3DWidget()
|
|||||||
|
|
||||||
void Edit3DView::checkImports()
|
void Edit3DView::checkImports()
|
||||||
{
|
{
|
||||||
bool has3dImport = false;
|
edit3DWidget()->showCanvas(model()->hasImport("QtQuick3D"));
|
||||||
const QList<Import> imports = model()->imports();
|
|
||||||
for (const auto &import : imports) {
|
|
||||||
if (import.url() == "QtQuick3D") {
|
|
||||||
has3dImport = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
edit3DWidget()->showCanvas(has3dImport);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetInfo Edit3DView::widgetInfo()
|
WidgetInfo Edit3DView::widgetInfo()
|
||||||
|
@@ -0,0 +1,271 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "materialbrowsermodel.h"
|
||||||
|
#include "variantproperty.h"
|
||||||
|
#include <designmodewidget.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
MaterialBrowserModel::MaterialBrowserModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialBrowserModel::~MaterialBrowserModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int MaterialBrowserModel::rowCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return m_materialList.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= m_materialList.count()) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "invalid index requested";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roleNames().value(role) == "materialName") {
|
||||||
|
QVariant objName = m_materialList.at(index.row()).variantProperty("objectName").value();
|
||||||
|
return objName.isValid() ? objName : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roleNames().value(role) == "materialInternalId")
|
||||||
|
return m_materialList.at(index.row()).internalId();
|
||||||
|
|
||||||
|
if (roleNames().value(role) == "materialVisible")
|
||||||
|
return isMaterialVisible(index.row());
|
||||||
|
|
||||||
|
if (!roleNames().contains(role))
|
||||||
|
qWarning() << Q_FUNC_INFO << "invalid role requested";
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialBrowserModel::isMaterialVisible(int idx) const
|
||||||
|
{
|
||||||
|
if (!isValidIndex(idx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_searchText.isEmpty() || m_materialList.at(idx).variantProperty("objectName")
|
||||||
|
.value().toString().contains(m_searchText, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialBrowserModel::isValidIndex(int idx) const
|
||||||
|
{
|
||||||
|
return idx > -1 && idx < rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> MaterialBrowserModel::roleNames() const
|
||||||
|
{
|
||||||
|
static const QHash<int, QByteArray> roles {
|
||||||
|
{Qt::UserRole + 1, "materialName"},
|
||||||
|
{Qt::UserRole + 2, "materialInternalId"},
|
||||||
|
{Qt::UserRole + 3, "materialVisible"},
|
||||||
|
};
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialBrowserModel::hasQuick3DImport() const
|
||||||
|
{
|
||||||
|
return m_hasQuick3DImport;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::setHasQuick3DImport(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasQuick3DImport)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasQuick3DImport = b;
|
||||||
|
emit hasQuick3DImportChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::setSearchText(const QString &searchText)
|
||||||
|
{
|
||||||
|
QString lowerSearchText = searchText.toLower();
|
||||||
|
|
||||||
|
if (m_searchText == lowerSearchText)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_searchText = lowerSearchText;
|
||||||
|
|
||||||
|
bool isEmpty = false;
|
||||||
|
|
||||||
|
// if selected material goes invisible, select nearest material
|
||||||
|
if (!isMaterialVisible(m_selectedIndex)) {
|
||||||
|
int inc = 1;
|
||||||
|
int incCap = m_materialList.count();
|
||||||
|
while (!isEmpty && inc < incCap) {
|
||||||
|
if (isMaterialVisible(m_selectedIndex - inc)) {
|
||||||
|
selectMaterial(m_selectedIndex - inc);
|
||||||
|
break;
|
||||||
|
} else if (isMaterialVisible(m_selectedIndex + inc)) {
|
||||||
|
selectMaterial(m_selectedIndex + inc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++inc;
|
||||||
|
isEmpty = !isValidIndex(m_selectedIndex + inc)
|
||||||
|
&& !isValidIndex(m_selectedIndex - inc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmpty != m_isEmpty) {
|
||||||
|
m_isEmpty = isEmpty;
|
||||||
|
emit isEmptyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::setMaterials(const QList<ModelNode> &materials, bool hasQuick3DImport)
|
||||||
|
{
|
||||||
|
m_materialList = materials;
|
||||||
|
m_materialIndexHash.clear();
|
||||||
|
for (int i = 0; i < materials.size(); ++i)
|
||||||
|
m_materialIndexHash.insert(materials.at(i).internalId(), i);
|
||||||
|
|
||||||
|
bool isEmpty = materials.size() == 0;
|
||||||
|
if (isEmpty != m_isEmpty) {
|
||||||
|
m_isEmpty = isEmpty;
|
||||||
|
emit isEmptyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasQuick3DImport != m_hasQuick3DImport) {
|
||||||
|
m_hasQuick3DImport = hasQuick3DImport;
|
||||||
|
emit hasQuick3DImportChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectedMaterial();
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::removeMaterial(const ModelNode &material)
|
||||||
|
{
|
||||||
|
if (!m_materialIndexHash.contains(material.internalId()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_materialList.removeOne(material);
|
||||||
|
int idx = m_materialIndexHash.value(material.internalId());
|
||||||
|
m_materialIndexHash.remove(material.internalId());
|
||||||
|
|
||||||
|
// update index hash
|
||||||
|
for (int i = idx; i < rowCount(); ++i)
|
||||||
|
m_materialIndexHash.insert(m_materialList.at(i).internalId(), i);
|
||||||
|
|
||||||
|
resetModel();
|
||||||
|
|
||||||
|
if (m_materialList.isEmpty()) {
|
||||||
|
m_isEmpty = true;
|
||||||
|
emit isEmptyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::updateSelectedMaterial()
|
||||||
|
{
|
||||||
|
selectMaterial(m_selectedIndex, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::updateMaterialName(const ModelNode &material)
|
||||||
|
{
|
||||||
|
int idx = materialIndex(material);
|
||||||
|
if (idx != -1)
|
||||||
|
emit dataChanged(index(idx, 0), index(idx, 0), {roleNames().key("materialName")});
|
||||||
|
}
|
||||||
|
|
||||||
|
int MaterialBrowserModel::materialIndex(const ModelNode &material) const
|
||||||
|
{
|
||||||
|
if (m_materialIndexHash.contains(material.internalId()))
|
||||||
|
return m_materialIndexHash.value(material.internalId());
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelNode MaterialBrowserModel::materialAt(int idx) const
|
||||||
|
{
|
||||||
|
if (isValidIndex(idx))
|
||||||
|
return m_materialList.at(idx);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::resetModel()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::selectMaterial(int idx, bool force)
|
||||||
|
{
|
||||||
|
if (m_materialList.size() == 0) {
|
||||||
|
m_selectedIndex = -1;
|
||||||
|
emit selectedIndexChanged(m_selectedIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = std::max(0, std::min(idx, rowCount() - 1));
|
||||||
|
|
||||||
|
if (idx != m_selectedIndex || force) {
|
||||||
|
m_selectedIndex = idx;
|
||||||
|
emit selectedIndexChanged(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::deleteMaterial(qint32 internalId)
|
||||||
|
{
|
||||||
|
int idx = m_materialIndexHash.value(internalId);
|
||||||
|
if (isValidIndex(idx))
|
||||||
|
m_materialList[idx].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::renameMaterial(int idx, const QString &newName)
|
||||||
|
{
|
||||||
|
ModelNode mat = m_materialList.at(idx);
|
||||||
|
emit renameMaterialTriggered(mat, newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::addNewMaterial()
|
||||||
|
{
|
||||||
|
emit addNewMaterialTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::applyToSelected(qint64 internalId, bool add)
|
||||||
|
{
|
||||||
|
int idx = m_materialIndexHash.value(internalId);
|
||||||
|
if (idx != -1) {
|
||||||
|
ModelNode mat = m_materialList.at(idx);
|
||||||
|
emit applyToSelectedTriggered(mat, add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::openMaterialEditor()
|
||||||
|
{
|
||||||
|
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,94 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <modelnode.h>
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class MaterialBrowserModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
||||||
|
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
|
||||||
|
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialBrowserModel(QObject *parent = nullptr);
|
||||||
|
~MaterialBrowserModel() override;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
void setSearchText(const QString &searchText);
|
||||||
|
|
||||||
|
bool hasQuick3DImport() const;
|
||||||
|
void setHasQuick3DImport(bool b);
|
||||||
|
|
||||||
|
void setMaterials(const QList<ModelNode> &materials, bool hasQuick3DImport);
|
||||||
|
void removeMaterial(const ModelNode &material);
|
||||||
|
void updateMaterialName(const ModelNode &material);
|
||||||
|
void updateSelectedMaterial();
|
||||||
|
int materialIndex(const ModelNode &material) const;
|
||||||
|
ModelNode materialAt(int idx) const;
|
||||||
|
|
||||||
|
void resetModel();
|
||||||
|
|
||||||
|
Q_INVOKABLE void selectMaterial(int idx, bool force = false);
|
||||||
|
Q_INVOKABLE void deleteMaterial(int idx);
|
||||||
|
Q_INVOKABLE void renameMaterial(int idx, const QString &newName);
|
||||||
|
Q_INVOKABLE void addNewMaterial();
|
||||||
|
Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false);
|
||||||
|
Q_INVOKABLE void openMaterialEditor();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void isEmptyChanged();
|
||||||
|
void hasQuick3DImportChanged();
|
||||||
|
void selectedIndexChanged(int idx);
|
||||||
|
void renameMaterialTriggered(const QmlDesigner::ModelNode &material, const QString &newName);
|
||||||
|
void applyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false);
|
||||||
|
void addNewMaterialTriggered();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isMaterialVisible(int idx) const;
|
||||||
|
bool isValidIndex(int idx) const;
|
||||||
|
|
||||||
|
QString m_searchText;
|
||||||
|
QList<ModelNode> m_materialList;
|
||||||
|
QHash<qint32, int> m_materialIndexHash; // internalId -> index
|
||||||
|
|
||||||
|
int m_selectedIndex = 0;
|
||||||
|
bool m_isEmpty = true;
|
||||||
|
bool m_hasQuick3DImport = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,266 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "materialbrowserview.h"
|
||||||
|
#include "materialbrowserwidget.h"
|
||||||
|
#include "materialbrowsermodel.h"
|
||||||
|
#include "nodeabstractproperty.h"
|
||||||
|
#include "qmlobjectnode.h"
|
||||||
|
#include "variantproperty.h"
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <nodeinstanceview.h>
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
MaterialBrowserView::MaterialBrowserView(QObject *parent)
|
||||||
|
: AbstractView(parent)
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
MaterialBrowserView::~MaterialBrowserView()
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool MaterialBrowserView::hasWidget() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetInfo MaterialBrowserView::widgetInfo()
|
||||||
|
{
|
||||||
|
if (m_widget.isNull()) {
|
||||||
|
m_widget = new MaterialBrowserWidget;
|
||||||
|
connect(m_widget->materialBrowserModel().data(), SIGNAL(selectedIndexChanged(int)),
|
||||||
|
this, SLOT(handleSelectedMaterialChanged(int)));
|
||||||
|
connect(m_widget->materialBrowserModel().data(),
|
||||||
|
SIGNAL(applyToSelectedTriggered(const QmlDesigner::ModelNode &, bool)),
|
||||||
|
this, SLOT(handleApplyToSelectedTriggered(const QmlDesigner::ModelNode &, bool)));
|
||||||
|
connect(m_widget->materialBrowserModel().data(),
|
||||||
|
SIGNAL(renameMaterialTriggered(const QmlDesigner::ModelNode &, const QString &)),
|
||||||
|
this, SLOT(handleRenameMaterial(const QmlDesigner::ModelNode &, const QString &)));
|
||||||
|
connect(m_widget->materialBrowserModel().data(), SIGNAL(addNewMaterialTriggered()),
|
||||||
|
this, SLOT(handleAddNewMaterial()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return createWidgetInfo(m_widget.data(),
|
||||||
|
new WidgetInfo::ToolBarWidgetDefaultFactory<MaterialBrowserWidget>(m_widget.data()),
|
||||||
|
"MaterialBrowser",
|
||||||
|
WidgetInfo::LeftPane,
|
||||||
|
0,
|
||||||
|
tr("Material Browser"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::modelAttached(Model *model)
|
||||||
|
{
|
||||||
|
AbstractView::modelAttached(model);
|
||||||
|
|
||||||
|
m_widget->clearSearchFilter();
|
||||||
|
m_hasQuick3DImport = model->hasImport("QtQuick3D");
|
||||||
|
QTimer::singleShot(0, this, &MaterialBrowserView::refreshModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::refreshModel()
|
||||||
|
{
|
||||||
|
ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID);
|
||||||
|
QList <ModelNode> materials;
|
||||||
|
|
||||||
|
if (m_hasQuick3DImport && matLib.isValid()) {
|
||||||
|
const QList <ModelNode> matLibNodes = matLib.directSubModelNodes();
|
||||||
|
for (const ModelNode &node : matLibNodes) {
|
||||||
|
if (isMaterial(node))
|
||||||
|
materials.append(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_widget->materialBrowserModel()->setMaterials(materials, m_hasQuick3DImport);
|
||||||
|
|
||||||
|
for (const ModelNode &node : std::as_const(materials))
|
||||||
|
model()->nodeInstanceView()->previewImageDataForGenericNode(node, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialBrowserView::isMaterial(const ModelNode &node) const
|
||||||
|
{
|
||||||
|
if (!node.isValid() || node.isComponent())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return node.isSubclassOf("QtQuick3D.Material");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::modelAboutToBeDetached(Model *model)
|
||||||
|
{
|
||||||
|
AbstractView::modelAboutToBeDetached(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
||||||
|
const QList<ModelNode> &lastSelectedNodeList)
|
||||||
|
{
|
||||||
|
if (!m_autoSelectModelMaterial)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if selected object is a model, select its material in the material browser and editor
|
||||||
|
ModelNode selectedModel;
|
||||||
|
|
||||||
|
for (const ModelNode &node : selectedNodeList) {
|
||||||
|
if (node.isSubclassOf("QtQuick3D.Model")) {
|
||||||
|
selectedModel = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedNodeList.size() > 1 || !selectedModel.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QmlObjectNode qmlObjNode(selectedModel);
|
||||||
|
QString matExp = qmlObjNode.expression("materials");
|
||||||
|
if (matExp.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString matId = matExp.remove('[').remove(']').split(',', Qt::SkipEmptyParts).at(0);
|
||||||
|
ModelNode mat = modelNodeForId(matId);
|
||||||
|
if (!mat.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int idx = m_widget->materialBrowserModel()->materialIndex(mat);
|
||||||
|
m_widget->materialBrowserModel()->selectMaterial(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
if (isMaterial(node))
|
||||||
|
m_widget->updateMaterialPreview(node, pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::variantPropertiesChanged(const QList<VariantProperty> &propertyList,
|
||||||
|
PropertyChangeFlags propertyChange)
|
||||||
|
{
|
||||||
|
for (const VariantProperty &property : propertyList) {
|
||||||
|
ModelNode node(property.parentModelNode());
|
||||||
|
|
||||||
|
if (isMaterial(node) && property.name() == "objectName")
|
||||||
|
m_widget->materialBrowserModel()->updateMaterialName(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::nodeReparented(const ModelNode &node,
|
||||||
|
const NodeAbstractProperty &newPropertyParent,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
PropertyChangeFlags propertyChange)
|
||||||
|
{
|
||||||
|
if (!isMaterial(node))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ModelNode newParentNode = newPropertyParent.parentModelNode();
|
||||||
|
ModelNode oldParentNode = oldPropertyParent.parentModelNode();
|
||||||
|
bool matAdded = newParentNode.isValid() && newParentNode.id() == Constants::MATERIAL_LIB_ID;
|
||||||
|
bool matRemoved = oldParentNode.isValid() && oldParentNode.id() == Constants::MATERIAL_LIB_ID;
|
||||||
|
|
||||||
|
if (matAdded || matRemoved) {
|
||||||
|
refreshModel();
|
||||||
|
|
||||||
|
int idx = m_widget->materialBrowserModel()->materialIndex(node);
|
||||||
|
m_widget->materialBrowserModel()->selectMaterial(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
||||||
|
{
|
||||||
|
// removing the material editor node
|
||||||
|
if (removedNode.isValid() && removedNode.id() == Constants::MATERIAL_LIB_ID) {
|
||||||
|
m_widget->materialBrowserModel()->setMaterials({}, m_hasQuick3DImport);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not a material under the material editor
|
||||||
|
if (!isMaterial(removedNode)
|
||||||
|
|| removedNode.parentProperty().parentModelNode().id() != Constants::MATERIAL_LIB_ID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_widget->materialBrowserModel()->removeMaterial(removedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::nodeRemoved(const ModelNode &removedNode,
|
||||||
|
const NodeAbstractProperty &parentProperty,
|
||||||
|
PropertyChangeFlags propertyChange)
|
||||||
|
{
|
||||||
|
if (parentProperty.parentModelNode().id() != Constants::MATERIAL_LIB_ID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_widget->materialBrowserModel()->updateSelectedMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports)
|
||||||
|
{
|
||||||
|
bool hasQuick3DImport = model()->hasImport("QtQuick3D");
|
||||||
|
|
||||||
|
if (hasQuick3DImport == m_hasQuick3DImport)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasQuick3DImport = hasQuick3DImport;
|
||||||
|
refreshModel();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::customNotification(const AbstractView *view, const QString &identifier,
|
||||||
|
const QList<ModelNode> &nodeList, const QList<QVariant> &data)
|
||||||
|
{
|
||||||
|
if (view == this)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (identifier == "selected_material_changed") {
|
||||||
|
int idx = m_widget->materialBrowserModel()->materialIndex(nodeList.first());
|
||||||
|
if (idx != -1)
|
||||||
|
m_widget->materialBrowserModel()->selectMaterial(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::handleSelectedMaterialChanged(int idx)
|
||||||
|
{
|
||||||
|
ModelNode matNode = m_widget->materialBrowserModel()->materialAt(idx);
|
||||||
|
// to MaterialEditor...
|
||||||
|
emitCustomNotification("selected_material_changed", {matNode}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::handleApplyToSelectedTriggered(const ModelNode &material, bool add)
|
||||||
|
{
|
||||||
|
// to MaterialEditor...
|
||||||
|
emitCustomNotification("apply_to_selected_triggered", {material}, {add});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::handleRenameMaterial(const ModelNode &material, const QString &newName)
|
||||||
|
{
|
||||||
|
// to MaterialEditor...
|
||||||
|
emitCustomNotification("rename_material", {material}, {newName});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserView::handleAddNewMaterial()
|
||||||
|
{
|
||||||
|
// to MaterialEditor...
|
||||||
|
emitCustomNotification("add_new_material");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,79 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <abstractview.h>
|
||||||
|
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class MaterialBrowserWidget;
|
||||||
|
|
||||||
|
class MaterialBrowserView : public AbstractView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialBrowserView(QObject *parent = nullptr);
|
||||||
|
~MaterialBrowserView() override;
|
||||||
|
|
||||||
|
bool hasWidget() const override;
|
||||||
|
WidgetInfo widgetInfo() override;
|
||||||
|
|
||||||
|
// AbstractView
|
||||||
|
void modelAttached(Model *model) override;
|
||||||
|
void modelAboutToBeDetached(Model *model) override;
|
||||||
|
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
||||||
|
const QList<ModelNode> &lastSelectedNodeList) override;
|
||||||
|
void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) override;
|
||||||
|
void variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags propertyChange) override;
|
||||||
|
void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
AbstractView::PropertyChangeFlags propertyChange) override;
|
||||||
|
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
|
||||||
|
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty,
|
||||||
|
PropertyChangeFlags propertyChange) 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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refreshModel();
|
||||||
|
bool isMaterial(const ModelNode &node) const;
|
||||||
|
|
||||||
|
QPointer<MaterialBrowserWidget> m_widget;
|
||||||
|
bool m_hasQuick3DImport = false;
|
||||||
|
bool m_autoSelectModelMaterial = false; // TODO: wire this to some action
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleSelectedMaterialChanged(int idx);
|
||||||
|
void handleApplyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false);
|
||||||
|
void handleRenameMaterial(const QmlDesigner::ModelNode &material, const QString &newName);
|
||||||
|
void handleAddNewMaterial();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,205 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "materialbrowserwidget.h"
|
||||||
|
#include "materialbrowsermodel.h"
|
||||||
|
|
||||||
|
#include <theme.h>
|
||||||
|
|
||||||
|
#include <designeractionmanager.h>
|
||||||
|
#include <designermcumanager.h>
|
||||||
|
#include <documentmanager.h>
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/stylehelper.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QImageReader>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickImageProvider>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QTabBar>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWheelEvent>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
static QString propertyEditorResourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
class PreviewImageProvider : public QQuickImageProvider
|
||||||
|
{
|
||||||
|
QHash<qint32, QPixmap> m_pixmaps;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PreviewImageProvider()
|
||||||
|
: QQuickImageProvider(Pixmap) {}
|
||||||
|
|
||||||
|
void setPixmap(const ModelNode &node, const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
m_pixmaps.insert(node.internalId(), pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(requestedSize)
|
||||||
|
|
||||||
|
QPixmap pixmap{150, 150};
|
||||||
|
|
||||||
|
qint32 internalId = id.toInt();
|
||||||
|
if (m_pixmaps.contains(internalId))
|
||||||
|
pixmap = m_pixmaps.value(internalId);
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
*size = pixmap.size();
|
||||||
|
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool MaterialBrowserWidget::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::FocusOut) {
|
||||||
|
if (obj == m_quickWidget.data())
|
||||||
|
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "closeContextMenu");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QObject::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialBrowserWidget::MaterialBrowserWidget()
|
||||||
|
: m_materialBrowserModel(new MaterialBrowserModel(this))
|
||||||
|
, m_quickWidget(new QQuickWidget(this))
|
||||||
|
, m_previewImageProvider(new PreviewImageProvider())
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Material Browser", "Title of material browser widget"));
|
||||||
|
setMinimumWidth(120);
|
||||||
|
|
||||||
|
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
|
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||||
|
m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
|
||||||
|
|
||||||
|
m_quickWidget->rootContext()->setContextProperties({
|
||||||
|
{"rootView", QVariant::fromValue(this)},
|
||||||
|
{"materialBrowserModel", QVariant::fromValue(m_materialBrowserModel.data())},
|
||||||
|
});
|
||||||
|
|
||||||
|
m_quickWidget->engine()->addImageProvider("materialBrowser", m_previewImageProvider);
|
||||||
|
Theme::setupTheme(m_quickWidget->engine());
|
||||||
|
m_quickWidget->installEventFilter(this);
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout(this);
|
||||||
|
layout->setContentsMargins({});
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->addWidget(m_quickWidget.data());
|
||||||
|
|
||||||
|
updateSearch();
|
||||||
|
|
||||||
|
setStyleSheet(Theme::replaceCssColors(
|
||||||
|
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
|
||||||
|
|
||||||
|
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F8), this);
|
||||||
|
connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &MaterialBrowserWidget::reloadQmlSource);
|
||||||
|
|
||||||
|
reloadQmlSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserWidget::updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
m_previewImageProvider->setPixmap(node, pixmap);
|
||||||
|
int idx = m_materialBrowserModel->materialIndex(node);
|
||||||
|
if (idx != -1)
|
||||||
|
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "refreshPreview", Q_ARG(QVariant, idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QToolButton *> MaterialBrowserWidget::createToolBarWidgets()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserWidget::handleSearchfilterChanged(const QString &filterText)
|
||||||
|
{
|
||||||
|
if (filterText != m_filterText) {
|
||||||
|
m_filterText = filterText;
|
||||||
|
updateSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MaterialBrowserWidget::qmlSourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/materialBrowserQmlSource";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/materialBrowserQmlSource").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserWidget::clearSearchFilter()
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "clearSearchFilter");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserWidget::reloadQmlSource()
|
||||||
|
{
|
||||||
|
const QString materialBrowserQmlPath = qmlSourcesPath() + "/MaterialBrowser.qml";
|
||||||
|
|
||||||
|
QTC_ASSERT(QFileInfo::exists(materialBrowserQmlPath), return);
|
||||||
|
|
||||||
|
m_quickWidget->engine()->clearComponentCache();
|
||||||
|
m_quickWidget->setSource(QUrl::fromLocalFile(materialBrowserQmlPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserWidget::updateSearch()
|
||||||
|
{
|
||||||
|
m_materialBrowserModel->setSearchText(m_filterText);
|
||||||
|
m_quickWidget->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickWidget *MaterialBrowserWidget::quickWidget() const
|
||||||
|
{
|
||||||
|
return m_quickWidget.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointer<MaterialBrowserModel> MaterialBrowserWidget::materialBrowserModel() const
|
||||||
|
{
|
||||||
|
return m_materialBrowserModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,91 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "itemlibraryinfo.h"
|
||||||
|
#include "import.h"
|
||||||
|
#include "modelnode.h"
|
||||||
|
|
||||||
|
#include <utils/fancylineedit.h>
|
||||||
|
#include <utils/dropsupport.h>
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QFileIconProvider>
|
||||||
|
#include <QQuickWidget>
|
||||||
|
#include <QQmlPropertyMap>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QPointF>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QStackedWidget;
|
||||||
|
class QShortcut;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class MaterialBrowserModel;
|
||||||
|
class PreviewImageProvider;
|
||||||
|
|
||||||
|
class MaterialBrowserWidget : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialBrowserWidget();
|
||||||
|
~MaterialBrowserWidget() = default;
|
||||||
|
|
||||||
|
QList<QToolButton *> createToolBarWidgets();
|
||||||
|
|
||||||
|
static QString qmlSourcesPath();
|
||||||
|
void clearSearchFilter();
|
||||||
|
|
||||||
|
QPointer<MaterialBrowserModel> materialBrowserModel() const;
|
||||||
|
void updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap);
|
||||||
|
|
||||||
|
Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText);
|
||||||
|
|
||||||
|
QQuickWidget *quickWidget() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reloadQmlSource();
|
||||||
|
void updateSearch();
|
||||||
|
|
||||||
|
QPointer<MaterialBrowserModel> m_materialBrowserModel;
|
||||||
|
QScopedPointer<QQuickWidget> m_quickWidget;
|
||||||
|
|
||||||
|
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
|
||||||
|
PreviewImageProvider *m_previewImageProvider = nullptr;
|
||||||
|
|
||||||
|
QString m_filterText;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,378 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "materialeditorcontextobject.h"
|
||||||
|
|
||||||
|
#include <abstractview.h>
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
#include <rewritingexception.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
#include <qmlmodelnodeproxy.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 {
|
||||||
|
|
||||||
|
MaterialEditorContextObject::MaterialEditorContextObject(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
qmlRegisterUncreatableType<MaterialEditorContextObject>("ToolBarAction", 1, 0, "ToolBarAction", "Enum type");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MaterialEditorContextObject::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 MaterialEditorContextObject::colorFromString(const QString &colorString)
|
||||||
|
{
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::changeTypeName(const QString &typeName)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_model && m_model->rewriterView(), return);
|
||||||
|
QTC_ASSERT(m_selectedMaterial.isValid(), return);
|
||||||
|
|
||||||
|
if (m_selectedMaterial.simplifiedTypeName() == typeName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ideally we should not misuse the rewriterView
|
||||||
|
// If we add more code here we have to forward the material editor view
|
||||||
|
RewriterView *rewriterView = m_model->rewriterView();
|
||||||
|
|
||||||
|
rewriterView->executeInTransaction("MaterialEditorContextObject::changeTypeName", [&] {
|
||||||
|
NodeMetaInfo metaInfo = m_model->metaInfo(typeName.toLatin1());
|
||||||
|
|
||||||
|
QTC_ASSERT(metaInfo.isValid(), return);
|
||||||
|
|
||||||
|
// Create a list of properties available for the new type
|
||||||
|
PropertyNameList propertiesAndSignals(metaInfo.propertyNames());
|
||||||
|
// Add signals to the list
|
||||||
|
const PropertyNameList signalNames = metaInfo.signalNames();
|
||||||
|
for (const PropertyName &signal : signalNames) {
|
||||||
|
if (signal.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PropertyName name = signal;
|
||||||
|
QChar firstChar = QChar(signal.at(0)).toUpper().toLatin1();
|
||||||
|
name[0] = firstChar.toLatin1();
|
||||||
|
name.prepend("on");
|
||||||
|
propertiesAndSignals.append(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add dynamic properties and respective change signals
|
||||||
|
const QList<AbstractProperty> matProps = m_selectedMaterial.properties();
|
||||||
|
for (const auto &property : matProps) {
|
||||||
|
if (!property.isDynamic())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add dynamic property
|
||||||
|
propertiesAndSignals.append(property.name());
|
||||||
|
// Add its change signal
|
||||||
|
PropertyName name = property.name();
|
||||||
|
QChar firstChar = QChar(property.name().at(0)).toUpper().toLatin1();
|
||||||
|
name[0] = firstChar.toLatin1();
|
||||||
|
name.prepend("on");
|
||||||
|
name.append("Changed");
|
||||||
|
propertiesAndSignals.append(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare current properties and signals with the ones available for change type
|
||||||
|
QList<PropertyName> incompatibleProperties;
|
||||||
|
for (const auto &property : matProps) {
|
||||||
|
if (!propertiesAndSignals.contains(property.name()))
|
||||||
|
incompatibleProperties.append(property.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::sort(incompatibleProperties);
|
||||||
|
|
||||||
|
// Create a dialog showing incompatible properties and signals
|
||||||
|
if (!incompatibleProperties.empty()) {
|
||||||
|
QString detailedText = tr("<b>Incompatible properties:</b><br>");
|
||||||
|
|
||||||
|
for (const auto &p : std::as_const(incompatibleProperties))
|
||||||
|
detailedText.append("- " + QString::fromUtf8(p) + "<br>");
|
||||||
|
|
||||||
|
detailedText.chop(QString("<br>").size());
|
||||||
|
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setTextFormat(Qt::RichText);
|
||||||
|
msgBox.setIcon(QMessageBox::Question);
|
||||||
|
msgBox.setWindowTitle(tr("Change Type"));
|
||||||
|
msgBox.setText(tr("Changing the type from %1 to %2 can't be done without removing incompatible properties.<br><br>%3")
|
||||||
|
.arg(m_selectedMaterial.simplifiedTypeName(), typeName, detailedText));
|
||||||
|
msgBox.setInformativeText(tr("Do you want to continue by removing incompatible properties?"));
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||||
|
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||||
|
|
||||||
|
if (msgBox.exec() == QMessageBox::Cancel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const auto &p : std::as_const(incompatibleProperties))
|
||||||
|
m_selectedMaterial.removeProperty(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_selectedMaterial.isRootNode())
|
||||||
|
rewriterView->changeRootNodeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion());
|
||||||
|
else
|
||||||
|
m_selectedMaterial.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::insertKeyframe(const QString &propertyName)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_model && m_model->rewriterView(), return);
|
||||||
|
QTC_ASSERT(m_selectedMaterial.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("MaterialEditorContextObject::insertKeyframe", [&] {
|
||||||
|
timeline.insertKeyframe(m_selectedMaterial, propertyName.toUtf8());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int MaterialEditorContextObject::majorVersion() const
|
||||||
|
{
|
||||||
|
return m_majorVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setMajorVersion(int majorVersion)
|
||||||
|
{
|
||||||
|
if (m_majorVersion == majorVersion)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_majorVersion = majorVersion;
|
||||||
|
|
||||||
|
emit majorVersionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialEditorContextObject::hasActiveTimeline() const
|
||||||
|
{
|
||||||
|
return m_hasActiveTimeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setHasActiveTimeline(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasActiveTimeline)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasActiveTimeline = b;
|
||||||
|
emit hasActiveTimelineChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialEditorContextObject::hasQuick3DImport() const
|
||||||
|
{
|
||||||
|
return m_hasQuick3DImport;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setHasQuick3DImport(bool b)
|
||||||
|
{
|
||||||
|
if (b == m_hasQuick3DImport)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasQuick3DImport = b;
|
||||||
|
emit hasQuick3DImportChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setSelectedMaterial(const ModelNode &matNode)
|
||||||
|
{
|
||||||
|
m_selectedMaterial = matNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setSpecificsUrl(const QUrl &newSpecificsUrl)
|
||||||
|
{
|
||||||
|
if (newSpecificsUrl == m_specificsUrl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_specificsUrl = newSpecificsUrl;
|
||||||
|
emit specificsUrlChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setStateName(const QString &newStateName)
|
||||||
|
{
|
||||||
|
if (newStateName == m_stateName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_stateName = newStateName;
|
||||||
|
emit stateNameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setAllStateNames(const QStringList &allStates)
|
||||||
|
{
|
||||||
|
if (allStates == m_allStateNames)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_allStateNames = allStates;
|
||||||
|
emit allStateNamesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setIsBaseState(bool newIsBaseState)
|
||||||
|
{
|
||||||
|
if (newIsBaseState == m_isBaseState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_isBaseState = newIsBaseState;
|
||||||
|
emit isBaseStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setSelectionChanged(bool newSelectionChanged)
|
||||||
|
{
|
||||||
|
if (newSelectionChanged == m_selectionChanged)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_selectionChanged = newSelectionChanged;
|
||||||
|
emit selectionChangedChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setBackendValues(QQmlPropertyMap *newBackendValues)
|
||||||
|
{
|
||||||
|
if (newBackendValues == m_backendValues)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_backendValues = newBackendValues;
|
||||||
|
emit backendValuesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setModel(Model *model)
|
||||||
|
{
|
||||||
|
m_model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::triggerSelectionChanged()
|
||||||
|
{
|
||||||
|
setSelectionChanged(!m_selectionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::setHasAliasExport(bool hasAliasExport)
|
||||||
|
{
|
||||||
|
if (m_aliasExport == hasAliasExport)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_aliasExport = hasAliasExport;
|
||||||
|
emit hasAliasExportChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::hideCursor()
|
||||||
|
{
|
||||||
|
if (QApplication::overrideCursor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
m_lastPos = QCursor::pos(w->screen());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::restoreCursor()
|
||||||
|
{
|
||||||
|
if (!QApplication::overrideCursor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
QCursor::setPos(w->screen(), m_lastPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorContextObject::holdCursorInPlace()
|
||||||
|
{
|
||||||
|
if (!QApplication::overrideCursor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
QCursor::setPos(w->screen(), m_lastPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MaterialEditorContextObject::devicePixelRatio()
|
||||||
|
{
|
||||||
|
if (QWidget *w = QApplication::activeWindow())
|
||||||
|
return w->devicePixelRatio();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MaterialEditorContextObject::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 MaterialEditorContextObject::isBlocked(const QString &propName) const
|
||||||
|
{
|
||||||
|
if (!m_selectedMaterial.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_model || !m_model->rewriterView())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (QmlObjectNode(m_selectedMaterial).isBlocked(propName.toUtf8()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // QmlDesigner
|
@@ -0,0 +1,157 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <model.h>
|
||||||
|
#include <modelnode.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QQmlPropertyMap>
|
||||||
|
#include <QQmlComponent>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class MaterialEditorContextObject : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QUrl specificsUrl READ specificsUrl WRITE setSpecificsUrl NOTIFY specificsUrlChanged)
|
||||||
|
|
||||||
|
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(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialEditorContextObject(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QUrl specificsUrl() const { return m_specificsUrl; }
|
||||||
|
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 changeTypeName(const QString &typeName);
|
||||||
|
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;
|
||||||
|
|
||||||
|
enum ToolBarAction {
|
||||||
|
ApplyToSelected = 0,
|
||||||
|
ApplyToSelectedAdd,
|
||||||
|
AddNewMaterial,
|
||||||
|
DeleteCurrentMaterial,
|
||||||
|
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 hasAliasExport() const { return m_aliasExport; }
|
||||||
|
|
||||||
|
void setSelectedMaterial(const ModelNode &matNode);
|
||||||
|
|
||||||
|
void setSpecificsUrl(const QUrl &newSpecificsUrl);
|
||||||
|
void setStateName(const QString &newStateName);
|
||||||
|
void setAllStateNames(const QStringList &allStates);
|
||||||
|
void setIsBaseState(bool newIsBaseState);
|
||||||
|
void setSelectionChanged(bool newSelectionChanged);
|
||||||
|
void setBackendValues(QQmlPropertyMap *newBackendValues);
|
||||||
|
void setModel(QmlDesigner::Model *model);
|
||||||
|
|
||||||
|
void triggerSelectionChanged();
|
||||||
|
void setHasAliasExport(bool hasAliasExport);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void specificsUrlChanged();
|
||||||
|
void stateNameChanged();
|
||||||
|
void allStateNamesChanged();
|
||||||
|
void isBaseStateChanged();
|
||||||
|
void selectionChangedChanged();
|
||||||
|
void backendValuesChanged();
|
||||||
|
void majorVersionChanged();
|
||||||
|
void hasAliasExportChanged();
|
||||||
|
void hasActiveTimelineChanged();
|
||||||
|
void hasQuick3DImportChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUrl m_specificsUrl;
|
||||||
|
|
||||||
|
QString m_stateName;
|
||||||
|
QStringList m_allStateNames;
|
||||||
|
|
||||||
|
int m_majorVersion = 1;
|
||||||
|
|
||||||
|
QQmlPropertyMap *m_backendValues = nullptr;
|
||||||
|
QQmlComponent *m_qmlComponent = 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;
|
||||||
|
|
||||||
|
ModelNode m_selectedMaterial;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // QmlDesigner
|
@@ -0,0 +1,346 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "materialeditorqmlbackend.h"
|
||||||
|
|
||||||
|
#include "propertyeditorvalue.h"
|
||||||
|
#include "materialeditortransaction.h"
|
||||||
|
#include "materialeditorcontextobject.h"
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
|
#include <qmltimeline.h>
|
||||||
|
|
||||||
|
#include <qmlobjectnode.h>
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/fileutils.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 MaterialEditorImageProvider : public QQuickImageProvider
|
||||||
|
{
|
||||||
|
QPixmap m_previewPixmap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialEditorImageProvider()
|
||||||
|
: QQuickImageProvider(Pixmap) {}
|
||||||
|
|
||||||
|
void setPixmap(const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
m_previewPixmap = pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(requestedSize)
|
||||||
|
|
||||||
|
QPixmap pixmap{150, 150};
|
||||||
|
|
||||||
|
if (id == "preview") {
|
||||||
|
if (!m_previewPixmap.isNull())
|
||||||
|
pixmap = m_previewPixmap;
|
||||||
|
} else {
|
||||||
|
QString path = Core::ICore::resourcePath("qmldesigner/materialEditorQmlSources/images/" + id).toString();
|
||||||
|
pixmap = QPixmap{path};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
*size = pixmap.size();
|
||||||
|
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor)
|
||||||
|
: m_view(new QQuickWidget)
|
||||||
|
, m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor))
|
||||||
|
, m_contextObject(new MaterialEditorContextObject())
|
||||||
|
, m_materialEditorImageProvider(new MaterialEditorImageProvider())
|
||||||
|
{
|
||||||
|
m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
|
m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||||
|
m_view->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider);
|
||||||
|
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
|
||||||
|
m_contextObject->setModel(materialEditor->model());
|
||||||
|
context()->setContextObject(m_contextObject.data());
|
||||||
|
|
||||||
|
QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged,
|
||||||
|
materialEditor, &MaterialEditorView::changeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialEditorQmlBackend::~MaterialEditorQmlBackend()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyName MaterialEditorQmlBackend::auxNamePostFix(const PropertyName &propertyName)
|
||||||
|
{
|
||||||
|
return propertyName + "__AUX";
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MaterialEditorQmlBackend::properDefaultAuxiliaryProperties(const QmlObjectNode &qmlObjectNode,
|
||||||
|
const PropertyName &propertyName)
|
||||||
|
{
|
||||||
|
const ModelNode node = qmlObjectNode.modelNode();
|
||||||
|
const PropertyName auxName = propertyName;
|
||||||
|
|
||||||
|
if (node.hasAuxiliaryData(auxName))
|
||||||
|
return node.auxiliaryData(auxName);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
|
||||||
|
const PropertyName &name,
|
||||||
|
const QVariant &value,
|
||||||
|
MaterialEditorView *materialEditor)
|
||||||
|
{
|
||||||
|
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, materialEditor, &MaterialEditorView::changeExpression);
|
||||||
|
QObject::connect(valueObject, &PropertyEditorValue::exportPropertyAsAliasRequested, materialEditor, &MaterialEditorView::exportPropertyAsAlias);
|
||||||
|
QObject::connect(valueObject, &PropertyEditorValue::removeAliasExportRequested, materialEditor, &MaterialEditorView::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 MaterialEditorQmlBackend::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 *MaterialEditorQmlBackend::context() const
|
||||||
|
{
|
||||||
|
return m_view->rootContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const
|
||||||
|
{
|
||||||
|
return m_contextObject.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickWidget *MaterialEditorQmlBackend::widget() const
|
||||||
|
{
|
||||||
|
return m_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorQmlBackend::setSource(const QUrl &url)
|
||||||
|
{
|
||||||
|
m_view->setSource(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::QmlAnchorBindingProxy &MaterialEditorQmlBackend::backendAnchorBinding()
|
||||||
|
{
|
||||||
|
return m_backendAnchorBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorQmlBackend::updateMaterialPreview(const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
m_materialEditorImageProvider->setPixmap(pixmap);
|
||||||
|
QMetaObject::invokeMethod(m_view->rootObject(), "refreshPreview");
|
||||||
|
}
|
||||||
|
|
||||||
|
DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap()
|
||||||
|
{
|
||||||
|
return m_backendValuesPropertyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialEditorTransaction *MaterialEditorQmlBackend::materialEditorTransaction() const
|
||||||
|
{
|
||||||
|
return m_materialEditorTransaction.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyEditorValue *MaterialEditorQmlBackend::propertyValueForName(const QString &propertyName)
|
||||||
|
{
|
||||||
|
return qobject_cast<PropertyEditorValue *>(variantToQObject(backendValuesPropertyMap().value(propertyName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode, const QString &stateName,
|
||||||
|
const QUrl &qmlSpecificsFile, MaterialEditorView *materialEditor)
|
||||||
|
{
|
||||||
|
if (selectedMaterialNode.isValid()) {
|
||||||
|
m_contextObject->setModel(materialEditor->model());
|
||||||
|
|
||||||
|
const PropertyNameList propertyNames = selectedMaterialNode.modelNode().metaInfo().propertyNames();
|
||||||
|
for (const PropertyName &propertyName : propertyNames)
|
||||||
|
createPropertyEditorValue(selectedMaterialNode, propertyName, selectedMaterialNode.instanceValue(propertyName), materialEditor);
|
||||||
|
|
||||||
|
// model node
|
||||||
|
m_backendModelNode.setup(selectedMaterialNode.modelNode());
|
||||||
|
context()->setContextProperty("modelNodeBackend", &m_backendModelNode);
|
||||||
|
context()->setContextProperty("hasMaterial", 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(selectedMaterialNode.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(selectedMaterialNode.modelNode());
|
||||||
|
context()->setContextProperties(
|
||||||
|
QVector<QQmlContext::PropertyPair>{
|
||||||
|
{{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
|
||||||
|
{{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.data())}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
contextObject()->setSpecificsUrl(qmlSpecificsFile);
|
||||||
|
contextObject()->setStateName(stateName);
|
||||||
|
|
||||||
|
QStringList stateNames = selectedMaterialNode.allStateNames();
|
||||||
|
stateNames.prepend("base state");
|
||||||
|
contextObject()->setAllStateNames(stateNames);
|
||||||
|
contextObject()->setSelectedMaterial(selectedMaterialNode);
|
||||||
|
contextObject()->setIsBaseState(selectedMaterialNode.isInBaseState());
|
||||||
|
contextObject()->setHasAliasExport(selectedMaterialNode.isAliasExported());
|
||||||
|
contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(selectedMaterialNode.view()));
|
||||||
|
|
||||||
|
contextObject()->setSelectionChanged(false);
|
||||||
|
|
||||||
|
NodeMetaInfo metaInfo = selectedMaterialNode.modelNode().metaInfo();
|
||||||
|
contextObject()->setMajorVersion(metaInfo.isValid() ? metaInfo.majorVersion() : -1);
|
||||||
|
} else {
|
||||||
|
context()->setContextProperty("hasMaterial", QVariant(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MaterialEditorQmlBackend::propertyEditorResourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorQmlBackend::emitSelectionToBeChanged()
|
||||||
|
{
|
||||||
|
m_backendModelNode.emitSelectionToBeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorQmlBackend::emitSelectionChanged()
|
||||||
|
{
|
||||||
|
m_backendModelNode.emitSelectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorQmlBackend::setValueforAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, const PropertyName &name)
|
||||||
|
{
|
||||||
|
const PropertyName propertyName = auxNamePostFix(name);
|
||||||
|
setValue(qmlObjectNode, propertyName, qmlObjectNode.modelNode().auxiliaryData(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,93 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "designerpropertymap.h"
|
||||||
|
#include "qmlanchorbindingproxy.h"
|
||||||
|
#include "qmlmodelnodeproxy.h"
|
||||||
|
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
|
||||||
|
class PropertyEditorValue;
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QQuickWidget;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class MaterialEditorContextObject;
|
||||||
|
class MaterialEditorImageProvider;
|
||||||
|
class MaterialEditorTransaction;
|
||||||
|
class MaterialEditorView;
|
||||||
|
|
||||||
|
class MaterialEditorQmlBackend
|
||||||
|
{
|
||||||
|
Q_DISABLE_COPY(MaterialEditorQmlBackend)
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialEditorQmlBackend(MaterialEditorView *materialEditor);
|
||||||
|
~MaterialEditorQmlBackend();
|
||||||
|
|
||||||
|
void setup(const QmlObjectNode &selectedMaterialNode, const QString &stateName, const QUrl &qmlSpecificsFile,
|
||||||
|
MaterialEditorView *materialEditor);
|
||||||
|
void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value);
|
||||||
|
|
||||||
|
QQmlContext *context() const;
|
||||||
|
MaterialEditorContextObject *contextObject() const;
|
||||||
|
QQuickWidget *widget() const;
|
||||||
|
void setSource(const QUrl &url);
|
||||||
|
Internal::QmlAnchorBindingProxy &backendAnchorBinding();
|
||||||
|
void updateMaterialPreview(const QPixmap &pixmap);
|
||||||
|
DesignerPropertyMap &backendValuesPropertyMap();
|
||||||
|
MaterialEditorTransaction *materialEditorTransaction() const;
|
||||||
|
|
||||||
|
PropertyEditorValue *propertyValueForName(const QString &propertyName);
|
||||||
|
|
||||||
|
static QString propertyEditorResourcesPath();
|
||||||
|
|
||||||
|
void emitSelectionToBeChanged();
|
||||||
|
void emitSelectionChanged();
|
||||||
|
|
||||||
|
void setValueforAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, const PropertyName &name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
|
||||||
|
const PropertyName &name, const QVariant &value,
|
||||||
|
MaterialEditorView *materialEditor);
|
||||||
|
PropertyName auxNamePostFix(const PropertyName &propertyName);
|
||||||
|
QVariant properDefaultAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, const PropertyName &propertyName);
|
||||||
|
|
||||||
|
QQuickWidget *m_view = nullptr;
|
||||||
|
Internal::QmlAnchorBindingProxy m_backendAnchorBinding;
|
||||||
|
QmlModelNodeProxy m_backendModelNode;
|
||||||
|
DesignerPropertyMap m_backendValuesPropertyMap;
|
||||||
|
QScopedPointer<MaterialEditorTransaction> m_materialEditorTransaction;
|
||||||
|
QScopedPointer<MaterialEditorContextObject> m_contextObject;
|
||||||
|
MaterialEditorImageProvider *m_materialEditorImageProvider = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,73 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "materialeditortransaction.h"
|
||||||
|
|
||||||
|
#include <QTimerEvent>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
MaterialEditorTransaction::MaterialEditorTransaction(QmlDesigner::MaterialEditorView *materialEditor)
|
||||||
|
: QObject(materialEditor),
|
||||||
|
m_materialEditor(materialEditor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorTransaction::start()
|
||||||
|
{
|
||||||
|
if (!m_materialEditor->model())
|
||||||
|
return;
|
||||||
|
if (m_rewriterTransaction.isValid())
|
||||||
|
m_rewriterTransaction.commit();
|
||||||
|
m_rewriterTransaction = m_materialEditor->beginRewriterTransaction(QByteArrayLiteral("MaterialEditorTransaction::start"));
|
||||||
|
m_timerId = startTimer(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorTransaction::end()
|
||||||
|
{
|
||||||
|
if (m_rewriterTransaction.isValid() && m_materialEditor->model()) {
|
||||||
|
killTimer(m_timerId);
|
||||||
|
m_rewriterTransaction.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialEditorTransaction::active() const
|
||||||
|
{
|
||||||
|
return m_rewriterTransaction.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorTransaction::timerEvent(QTimerEvent *timerEvent)
|
||||||
|
{
|
||||||
|
if (timerEvent->timerId() != m_timerId)
|
||||||
|
return;
|
||||||
|
killTimer(timerEvent->timerId());
|
||||||
|
if (m_rewriterTransaction.isValid())
|
||||||
|
m_rewriterTransaction.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
|
|
@@ -0,0 +1,53 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "materialeditorview.h"
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class MaterialEditorTransaction : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialEditorTransaction(QmlDesigner::MaterialEditorView *materialEditor);
|
||||||
|
|
||||||
|
Q_INVOKABLE void start();
|
||||||
|
Q_INVOKABLE void end();
|
||||||
|
|
||||||
|
Q_INVOKABLE bool active() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QmlDesigner::MaterialEditorView *m_materialEditor = nullptr;
|
||||||
|
QmlDesigner::RewriterTransaction m_rewriterTransaction;
|
||||||
|
int m_timerId = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,817 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "materialeditorview.h"
|
||||||
|
|
||||||
|
#include "materialeditorqmlbackend.h"
|
||||||
|
#include "materialeditorcontextobject.h"
|
||||||
|
#include "propertyeditorvalue.h"
|
||||||
|
#include "materialeditortransaction.h"
|
||||||
|
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
|
#include <qmltimeline.h>
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
#include <nodeproperty.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
#include <nodeinstanceview.h>
|
||||||
|
#include <metainfo.h>
|
||||||
|
|
||||||
|
#include <rewritingexception.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
|
||||||
|
#include <theme.h>
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/messagebox.h>
|
||||||
|
#include <designmodewidget.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QQuickWidget>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
MaterialEditorView::MaterialEditorView(QWidget *parent)
|
||||||
|
: AbstractView(parent)
|
||||||
|
, m_stackedWidget(new QStackedWidget(parent))
|
||||||
|
{
|
||||||
|
m_updateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F7), m_stackedWidget);
|
||||||
|
connect(m_updateShortcut, &QShortcut::activated, this, &MaterialEditorView::reloadQml);
|
||||||
|
|
||||||
|
m_stackedWidget->setStyleSheet(Theme::replaceCssColors(
|
||||||
|
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
|
||||||
|
m_stackedWidget->setMinimumWidth(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::ensureMaterialLibraryNode()
|
||||||
|
{
|
||||||
|
if (!m_hasQuick3DImport)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_materialLibrary = modelNodeForId(Constants::MATERIAL_LIB_ID);
|
||||||
|
if (m_materialLibrary.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QList<ModelNode> materials = rootModelNode().subModelNodesOfType("QtQuick3D.Material");
|
||||||
|
if (materials.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// create material library node
|
||||||
|
TypeName nodeType = rootModelNode().isSubclassOf("QtQuick3D.Node") ? "Quick3D.Node" : "QtQuick.Item";
|
||||||
|
NodeMetaInfo metaInfo = model()->metaInfo(nodeType);
|
||||||
|
m_materialLibrary = createModelNode(nodeType, metaInfo.majorVersion(), metaInfo.minorVersion());
|
||||||
|
|
||||||
|
m_materialLibrary.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID);
|
||||||
|
rootModelNode().defaultNodeListProperty().reparentHere(m_materialLibrary);
|
||||||
|
|
||||||
|
// move all materials to under material library node
|
||||||
|
for (const ModelNode &node : materials) {
|
||||||
|
// if material has no name, set name to id
|
||||||
|
QString matName = node.variantProperty("objectName").value().toString();
|
||||||
|
if (matName.isEmpty()) {
|
||||||
|
VariantProperty objNameProp = node.variantProperty("objectName");
|
||||||
|
objNameProp.setValue(node.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_materialLibrary.defaultNodeListProperty().reparentHere(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialEditorView::~MaterialEditorView()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_qmlBackendHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from material editor to model
|
||||||
|
void MaterialEditorView::changeValue(const QString &name)
|
||||||
|
{
|
||||||
|
PropertyName propertyName = name.toUtf8();
|
||||||
|
|
||||||
|
if (propertyName.isNull() || locked() || noValidSelection() || propertyName == "id"
|
||||||
|
|| propertyName == Constants::PROPERTY_EDITOR_CLASSNAME_PROPERTY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyName == "objectName") {
|
||||||
|
renameMaterial(m_selectedMaterial, m_qmlBackEnd->propertyValueForName("objectName")->value().toString());
|
||||||
|
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_selectedMaterial.metaInfo();
|
||||||
|
|
||||||
|
QVariant castedValue;
|
||||||
|
|
||||||
|
if (metaInfo.isValid() && metaInfo.hasProperty(propertyName)) {
|
||||||
|
castedValue = metaInfo.propertyCastedValue(propertyName, 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 (metaInfo.isValid() && metaInfo.hasProperty(propertyName)) {
|
||||||
|
if (metaInfo.propertyTypeName(propertyName) == "QUrl"
|
||||||
|
|| metaInfo.propertyTypeName(propertyName) == "url") {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestPreviewRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTrueFalseLiteral(const QString &expression)
|
||||||
|
{
|
||||||
|
return (expression.compare("false", Qt::CaseInsensitive) == 0)
|
||||||
|
|| (expression.compare("true", Qt::CaseInsensitive) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::changeExpression(const QString &propertyName)
|
||||||
|
{
|
||||||
|
PropertyName name = propertyName.toUtf8();
|
||||||
|
|
||||||
|
if (name.isNull() || locked() || noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
executeInTransaction("MaterialEditorView::changeExpression", [this, name] {
|
||||||
|
PropertyName underscoreName(name);
|
||||||
|
underscoreName.replace('.', '_');
|
||||||
|
|
||||||
|
QmlObjectNode qmlObjectNode(m_selectedMaterial);
|
||||||
|
PropertyEditorValue *value = m_qmlBackEnd->propertyValueForName(QString::fromLatin1(underscoreName));
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
qWarning() << __FUNCTION__ << "no value for " << underscoreName;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_selectedMaterial.metaInfo().isValid() && m_selectedMaterial.metaInfo().hasProperty(name)) {
|
||||||
|
if (m_selectedMaterial.metaInfo().propertyTypeName(name) == "QColor") {
|
||||||
|
if (QColor(value->expression().remove('"')).isValid()) {
|
||||||
|
qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"')));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (m_selectedMaterial.metaInfo().propertyTypeName(name) == "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 (m_selectedMaterial.metaInfo().propertyTypeName(name) == "int") {
|
||||||
|
bool ok;
|
||||||
|
int intValue = value->expression().toInt(&ok);
|
||||||
|
if (ok) {
|
||||||
|
qmlObjectNode.setVariantProperty(name, intValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (m_selectedMaterial.metaInfo().propertyTypeName(name) == "qreal") {
|
||||||
|
bool ok;
|
||||||
|
qreal realValue = value->expression().toDouble(&ok);
|
||||||
|
if (ok) {
|
||||||
|
qmlObjectNode.setVariantProperty(name, realValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (m_selectedMaterial.metaInfo().propertyTypeName(name) == "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());
|
||||||
|
|
||||||
|
requestPreviewRender();
|
||||||
|
}); // end of transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::exportPropertyAsAlias(const QString &name)
|
||||||
|
{
|
||||||
|
if (name.isNull() || locked() || noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
executeInTransaction("MaterialEditorView::exportPopertyAsAlias", [this, name] {
|
||||||
|
const QString id = m_selectedMaterial.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 MaterialEditorView::removeAliasExport(const QString &name)
|
||||||
|
{
|
||||||
|
if (name.isNull() || locked() || noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
executeInTransaction("MaterialEditorView::removeAliasExport", [this, name] {
|
||||||
|
const QString id = m_selectedMaterial.validId();
|
||||||
|
|
||||||
|
const QList<BindingProperty> bindingProps = rootModelNode().bindingProperties();
|
||||||
|
for (const BindingProperty &property : bindingProps) {
|
||||||
|
if (property.expression() == (id + "." + name)) {
|
||||||
|
rootModelNode().removeProperty(property.name());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialEditorView::locked() const
|
||||||
|
{
|
||||||
|
return m_locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::currentTimelineChanged(const ModelNode &)
|
||||||
|
{
|
||||||
|
m_qmlBackEnd->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::delayedResetView()
|
||||||
|
{
|
||||||
|
// TODO: it seems the delayed reset is not needed. Leaving it commented out for now just in case it
|
||||||
|
// turned out to be needed. Otherwise will be removed after a small testing period.
|
||||||
|
// if (m_timerId)
|
||||||
|
// killTimer(m_timerId);
|
||||||
|
// m_timerId = startTimer(50);
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::timerEvent(QTimerEvent *timerEvent)
|
||||||
|
{
|
||||||
|
if (m_timerId == timerEvent->timerId())
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::resetView()
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
if (m_timerId)
|
||||||
|
killTimer(m_timerId);
|
||||||
|
|
||||||
|
setupQmlBackend();
|
||||||
|
|
||||||
|
if (m_qmlBackEnd)
|
||||||
|
m_qmlBackEnd->emitSelectionChanged();
|
||||||
|
|
||||||
|
QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender);
|
||||||
|
|
||||||
|
m_locked = false;
|
||||||
|
|
||||||
|
if (m_timerId)
|
||||||
|
m_timerId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QString MaterialEditorView::materialEditorResourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/materialEditorQmlSources";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/materialEditorQmlSources").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::applyMaterialToSelectedModels(const ModelNode &material, bool add)
|
||||||
|
{
|
||||||
|
if (m_selectedModels.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTC_ASSERT(material.isValid(), return);
|
||||||
|
|
||||||
|
auto expToList = [](const QString &exp) {
|
||||||
|
QString copy = exp;
|
||||||
|
copy = copy.remove("[").remove("]");
|
||||||
|
|
||||||
|
QStringList tmp = copy.split(',', Qt::SkipEmptyParts);
|
||||||
|
for (QString &str : tmp)
|
||||||
|
str = str.trimmed();
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto listToExp = [](QStringList &stringList) {
|
||||||
|
if (stringList.size() > 1)
|
||||||
|
return QString("[" + stringList.join(",") + "]");
|
||||||
|
|
||||||
|
if (stringList.size() == 1)
|
||||||
|
return stringList.first();
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
};
|
||||||
|
|
||||||
|
executeInTransaction("MaterialEditorView::applyMaterialToSelectedModels", [&] {
|
||||||
|
for (const ModelNode &node : std::as_const(m_selectedModels)) {
|
||||||
|
QmlObjectNode qmlObjNode(node);
|
||||||
|
if (add) {
|
||||||
|
QStringList matList = expToList(qmlObjNode.expression("materials"));
|
||||||
|
matList.append(material.id());
|
||||||
|
QString updatedExp = listToExp(matList);
|
||||||
|
qmlObjNode.setBindingProperty("materials", updatedExp);
|
||||||
|
} else {
|
||||||
|
qmlObjNode.setBindingProperty("materials", material.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::handleToolBarAction(int action)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_hasQuick3DImport, return);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case MaterialEditorContextObject::ApplyToSelected: {
|
||||||
|
applyMaterialToSelectedModels(m_selectedMaterial);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MaterialEditorContextObject::ApplyToSelectedAdd: {
|
||||||
|
applyMaterialToSelectedModels(m_selectedMaterial, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MaterialEditorContextObject::AddNewMaterial: {
|
||||||
|
ensureMaterialLibraryNode();
|
||||||
|
|
||||||
|
executeInTransaction("MaterialEditorView:handleToolBarAction", [&] {
|
||||||
|
NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.DefaultMaterial");
|
||||||
|
ModelNode newMatNode = createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(),
|
||||||
|
metaInfo.minorVersion());
|
||||||
|
renameMaterial(newMatNode, "New Material");
|
||||||
|
|
||||||
|
m_materialLibrary.defaultNodeListProperty().reparentHere(newMatNode);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MaterialEditorContextObject::DeleteCurrentMaterial: {
|
||||||
|
if (m_selectedMaterial.isValid())
|
||||||
|
m_selectedMaterial.destroy();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MaterialEditorContextObject::OpenMaterialBrowser: {
|
||||||
|
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::setupQmlBackend()
|
||||||
|
{
|
||||||
|
QUrl qmlPaneUrl;
|
||||||
|
QUrl qmlSpecificsUrl;
|
||||||
|
|
||||||
|
if (m_selectedMaterial.isValid() && m_hasQuick3DImport) {
|
||||||
|
qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/MaterialEditorPane.qml");
|
||||||
|
|
||||||
|
NodeMetaInfo metaInfo = m_selectedMaterial.metaInfo();
|
||||||
|
QDir importDir(metaInfo.importDirectoryPath() + Constants::QML_DESIGNER_SUBFOLDER);
|
||||||
|
QString typeName = QString::fromUtf8(metaInfo.typeName().split('.').constLast());
|
||||||
|
qmlSpecificsUrl = QUrl::fromLocalFile(importDir.absoluteFilePath(typeName + "Specifics.qml"));
|
||||||
|
} else {
|
||||||
|
qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/EmptyMaterialEditorPane.qml");
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialEditorQmlBackend *currentQmlBackend = m_qmlBackendHash.value(qmlPaneUrl.toString());
|
||||||
|
|
||||||
|
QString currentStateName = currentState().isBaseState() ? currentState().name() : "invalid state";
|
||||||
|
|
||||||
|
if (!currentQmlBackend) {
|
||||||
|
currentQmlBackend = new MaterialEditorQmlBackend(this);
|
||||||
|
|
||||||
|
m_stackedWidget->addWidget(currentQmlBackend->widget());
|
||||||
|
m_qmlBackendHash.insert(qmlPaneUrl.toString(), currentQmlBackend);
|
||||||
|
|
||||||
|
currentQmlBackend->setup(m_selectedMaterial, currentStateName, qmlSpecificsUrl, this);
|
||||||
|
|
||||||
|
currentQmlBackend->setSource(qmlPaneUrl);
|
||||||
|
|
||||||
|
QObject::connect(currentQmlBackend->widget()->rootObject(), SIGNAL(toolBarAction(int)),
|
||||||
|
this, SLOT(handleToolBarAction(int)));
|
||||||
|
} else {
|
||||||
|
currentQmlBackend->setup(m_selectedMaterial, currentStateName, qmlSpecificsUrl, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
|
||||||
|
|
||||||
|
m_stackedWidget->setCurrentWidget(currentQmlBackend->widget());
|
||||||
|
|
||||||
|
m_qmlBackEnd = currentQmlBackend;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
executeInTransaction("MaterialEditorView:commitVariantValueToModel", [&] {
|
||||||
|
QmlObjectNode(m_selectedMaterial).setVariantProperty(propertyName, value);
|
||||||
|
});
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::commitAuxValueToModel(const PropertyName &propertyName, const QVariant &value)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
PropertyName name = propertyName;
|
||||||
|
name.chop(5);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (value.isValid())
|
||||||
|
m_selectedMaterial.setAuxiliaryData(name, value);
|
||||||
|
else
|
||||||
|
m_selectedMaterial.removeAuxiliaryData(name);
|
||||||
|
}
|
||||||
|
catch (const Exception &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::removePropertyFromModel(const PropertyName &propertyName)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
executeInTransaction("MaterialEditorView:removePropertyFromModel", [&] {
|
||||||
|
QmlObjectNode(m_selectedMaterial).removeProperty(propertyName);
|
||||||
|
});
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialEditorView::noValidSelection() const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_qmlBackEnd, return true);
|
||||||
|
return !QmlObjectNode::isValidQmlObjectNode(m_selectedMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::modelAttached(Model *model)
|
||||||
|
{
|
||||||
|
AbstractView::modelAttached(model);
|
||||||
|
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
m_hasQuick3DImport = model->hasImport("QtQuick3D");
|
||||||
|
|
||||||
|
ensureMaterialLibraryNode();
|
||||||
|
|
||||||
|
if (!m_setupCompleted) {
|
||||||
|
reloadQml();
|
||||||
|
m_setupCompleted = true;
|
||||||
|
}
|
||||||
|
resetView();
|
||||||
|
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::modelAboutToBeDetached(Model *model)
|
||||||
|
{
|
||||||
|
AbstractView::modelAboutToBeDetached(model);
|
||||||
|
m_qmlBackEnd->materialEditorTransaction()->end();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::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_selectedMaterial).isAliasExported());
|
||||||
|
|
||||||
|
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
|
||||||
|
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
for (const VariantProperty &property : propertyList) {
|
||||||
|
ModelNode node(property.parentModelNode());
|
||||||
|
|
||||||
|
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
|
||||||
|
if (m_selectedMaterial.property(property.name()).isBindingProperty())
|
||||||
|
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
|
||||||
|
else
|
||||||
|
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).modelValue(property.name()));
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed)
|
||||||
|
requestPreviewRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (noValidSelection())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
for (const BindingProperty &property : propertyList) {
|
||||||
|
ModelNode node(property.parentModelNode());
|
||||||
|
|
||||||
|
if (property.isAliasExport())
|
||||||
|
m_qmlBackEnd->contextObject()->setHasAliasExport(QmlObjectNode(m_selectedMaterial).isAliasExported());
|
||||||
|
|
||||||
|
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
|
||||||
|
if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty())
|
||||||
|
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
|
||||||
|
else
|
||||||
|
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).modelValue(property.name()));
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed)
|
||||||
|
requestPreviewRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (noValidSelection() || !node.isSelected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_qmlBackEnd->setValueforAuxiliaryProperties(m_selectedMaterial, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// request render image for the selected material node
|
||||||
|
void MaterialEditorView::requestPreviewRender()
|
||||||
|
{
|
||||||
|
if (m_selectedMaterial.isValid())
|
||||||
|
model()->nodeInstanceView()->previewImageDataForGenericNode(m_selectedMaterial, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialEditorView::hasWidget() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetInfo MaterialEditorView::widgetInfo()
|
||||||
|
{
|
||||||
|
return createWidgetInfo(m_stackedWidget, nullptr, "MaterialEditor", WidgetInfo::RightPane, 0, tr("Material Editor"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
||||||
|
const QList<ModelNode> &lastSelectedNodeList)
|
||||||
|
{
|
||||||
|
m_selectedModels.clear();
|
||||||
|
|
||||||
|
for (const ModelNode &node : selectedNodeList) {
|
||||||
|
if (node.isSubclassOf("QtQuick3D.Model"))
|
||||||
|
m_selectedModels.append(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::currentStateChanged(const ModelNode &node)
|
||||||
|
{
|
||||||
|
QmlModelState newQmlModelState(node);
|
||||||
|
Q_ASSERT(newQmlModelState.isValid());
|
||||||
|
delayedResetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList)
|
||||||
|
{
|
||||||
|
if (!m_selectedMaterial.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_selectedMaterial && 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 MaterialEditorView::nodeTypeChanged(const ModelNode &node, const TypeName &, int, int)
|
||||||
|
{
|
||||||
|
if (node == m_selectedMaterial)
|
||||||
|
delayedResetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
if (node == m_selectedMaterial)
|
||||||
|
m_qmlBackEnd->updateMaterialPreview(pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports)
|
||||||
|
{
|
||||||
|
m_hasQuick3DImport = model()->hasImport("QtQuick3D");
|
||||||
|
m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
|
||||||
|
|
||||||
|
ensureMaterialLibraryNode(); // create the material lib if Quick3D import is added
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newName)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(material.isValid(), return);
|
||||||
|
|
||||||
|
executeInTransaction("MaterialEditorView:renameMaterial", [&] {
|
||||||
|
material.setIdWithRefactoring(generateIdFromName(newName));
|
||||||
|
|
||||||
|
VariantProperty objNameProp = material.variantProperty("objectName");
|
||||||
|
objNameProp.setValue(newName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::customNotification(const AbstractView *view, const QString &identifier,
|
||||||
|
const QList<ModelNode> &nodeList, const QList<QVariant> &data)
|
||||||
|
{
|
||||||
|
if (identifier == "selected_material_changed") {
|
||||||
|
m_selectedMaterial = nodeList.first();
|
||||||
|
QTimer::singleShot(0, this, &MaterialEditorView::resetView);
|
||||||
|
} else if (identifier == "apply_to_selected_triggered") {
|
||||||
|
applyMaterialToSelectedModels(nodeList.first(), data.first().toBool());
|
||||||
|
} else if (identifier == "rename_material") {
|
||||||
|
if (m_selectedMaterial == nodeList.first())
|
||||||
|
renameMaterial(m_selectedMaterial, data.first().toString());
|
||||||
|
} else if (identifier == "add_new_material") {
|
||||||
|
handleToolBarAction(MaterialEditorContextObject::AddNewMaterial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from model to material editor
|
||||||
|
void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value)
|
||||||
|
{
|
||||||
|
m_locked = true;
|
||||||
|
m_qmlBackEnd->setValue(qmlObjectNode, name, value);
|
||||||
|
requestPreviewRender();
|
||||||
|
m_locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialEditorView::reloadQml()
|
||||||
|
{
|
||||||
|
m_qmlBackendHash.clear();
|
||||||
|
while (QWidget *widget = m_stackedWidget->widget(0)) {
|
||||||
|
m_stackedWidget->removeWidget(widget);
|
||||||
|
delete widget;
|
||||||
|
}
|
||||||
|
m_qmlBackEnd = nullptr;
|
||||||
|
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a unique camelCase id from a name
|
||||||
|
QString MaterialEditorView::generateIdFromName(const QString &name)
|
||||||
|
{
|
||||||
|
QString newId;
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
newId = "material";
|
||||||
|
} else {
|
||||||
|
// convert to camel case
|
||||||
|
QStringList nameWords = name.split(" ");
|
||||||
|
nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1);
|
||||||
|
for (int i = 1; i < nameWords.size(); ++i)
|
||||||
|
nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1);
|
||||||
|
newId = nameWords.join("");
|
||||||
|
|
||||||
|
// if id starts with a number prepend an underscore
|
||||||
|
if (newId.at(0).isDigit())
|
||||||
|
newId.prepend('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
|
||||||
|
while (hasId(newId)) { // id exists
|
||||||
|
QRegularExpressionMatch match = rgx.match(newId);
|
||||||
|
if (match.hasMatch()) { // ends with a number, increment it
|
||||||
|
QString numStr = match.captured();
|
||||||
|
int num = numStr.toInt() + 1;
|
||||||
|
newId = newId.mid(0, match.capturedStart()) + QString::number(num);
|
||||||
|
} else {
|
||||||
|
newId.append('1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,125 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <abstractview.h>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QShortcut;
|
||||||
|
class QStackedWidget;
|
||||||
|
class QTimer;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class ModelNode;
|
||||||
|
class MaterialEditorQmlBackend;
|
||||||
|
|
||||||
|
class MaterialEditorView : public AbstractView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MaterialEditorView(QWidget *parent = nullptr);
|
||||||
|
~MaterialEditorView() 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, const PropertyName &name, const QVariant &data) override;
|
||||||
|
|
||||||
|
void resetView();
|
||||||
|
void currentStateChanged(const ModelNode &node) override;
|
||||||
|
void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
|
||||||
|
|
||||||
|
void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override;
|
||||||
|
void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) 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 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;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void handleToolBarAction(int action);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QString materialEditorResourcesPath();
|
||||||
|
|
||||||
|
void reloadQml();
|
||||||
|
QString generateIdFromName(const QString &name);
|
||||||
|
|
||||||
|
void ensureMaterialLibraryNode();
|
||||||
|
void requestPreviewRender();
|
||||||
|
void applyMaterialToSelectedModels(const ModelNode &material, bool add = false);
|
||||||
|
|
||||||
|
void delayedResetView();
|
||||||
|
void setupQmlBackend();
|
||||||
|
|
||||||
|
void commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value);
|
||||||
|
void commitAuxValueToModel(const PropertyName &propertyName, const QVariant &value);
|
||||||
|
void removePropertyFromModel(const PropertyName &propertyName);
|
||||||
|
void renameMaterial(ModelNode &material, const QString &newName);
|
||||||
|
|
||||||
|
bool noValidSelection() const;
|
||||||
|
|
||||||
|
ModelNode m_selectedMaterial;
|
||||||
|
ModelNode m_materialLibrary;
|
||||||
|
QShortcut *m_updateShortcut = nullptr;
|
||||||
|
int m_timerId = 0;
|
||||||
|
QStackedWidget *m_stackedWidget = nullptr;
|
||||||
|
QList<ModelNode> m_selectedModels;
|
||||||
|
QHash<QString, MaterialEditorQmlBackend *> m_qmlBackendHash;
|
||||||
|
MaterialEditorQmlBackend *m_qmlBackEnd = nullptr;
|
||||||
|
bool m_locked = false;
|
||||||
|
bool m_setupCompleted = false;
|
||||||
|
bool m_hasQuick3DImport = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -42,10 +42,10 @@
|
|||||||
#include <abstractview.h>
|
#include <abstractview.h>
|
||||||
#include <invalididexception.h>
|
#include <invalididexception.h>
|
||||||
#include <rewritingexception.h>
|
#include <rewritingexception.h>
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
#include <qmlitemnode.h>
|
#include <qmlitemnode.h>
|
||||||
#include <designeractionmanager.h>
|
#include <designeractionmanager.h>
|
||||||
#include <import.h>
|
#include <import.h>
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <qmlprojectmanager/qmlproject.h>
|
#include <qmlprojectmanager/qmlproject.h>
|
||||||
@@ -339,7 +339,8 @@ QList<ModelNode> NavigatorTreeModel::filteredList(const NodeListProperty &proper
|
|||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
list.append(Utils::filtered(nameFilteredList, [] (const ModelNode &arg) {
|
list.append(Utils::filtered(nameFilteredList, [] (const ModelNode &arg) {
|
||||||
const bool value = QmlItemNode::isValidQmlItemNode(arg) || NodeHints::fromModelNode(arg).visibleInNavigator();
|
const bool value = (QmlItemNode::isValidQmlItemNode(arg) || NodeHints::fromModelNode(arg).visibleInNavigator())
|
||||||
|
&& arg.id() != Constants::MATERIAL_LIB_ID;
|
||||||
return value;
|
return value;
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
@@ -688,13 +689,20 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
bool validContainer = false;
|
bool validContainer = false;
|
||||||
bool showMatToCompInfo = false;
|
ModelNode targetNode = targetProperty.parentModelNode();
|
||||||
|
|
||||||
|
// don't allow dropping materials on any node but Models
|
||||||
|
QString itemType = QString::fromLatin1(itemLibraryEntry.typeName());
|
||||||
|
if (itemType.startsWith("QtQuick3D.") && itemType.endsWith("Material")
|
||||||
|
&& !targetNode.isSubclassOf("QtQuick3D.Model")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QmlObjectNode newQmlObjectNode;
|
QmlObjectNode newQmlObjectNode;
|
||||||
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryItemDrop", [&] {
|
m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryItemDrop", [&] {
|
||||||
newQmlObjectNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, QPointF(), targetProperty, false);
|
newQmlObjectNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, QPointF(), targetProperty, false);
|
||||||
ModelNode newModelNode = newQmlObjectNode.modelNode();
|
ModelNode newModelNode = newQmlObjectNode.modelNode();
|
||||||
if (newModelNode.isValid()) {
|
if (newModelNode.isValid()) {
|
||||||
ModelNode targetNode = targetProperty.parentModelNode();
|
|
||||||
ChooseFromPropertyListDialog *dialog = ChooseFromPropertyListDialog::createIfNeeded(
|
ChooseFromPropertyListDialog *dialog = ChooseFromPropertyListDialog::createIfNeeded(
|
||||||
targetNode, newModelNode, Core::ICore::dialogParent());
|
targetNode, newModelNode, Core::ICore::dialogParent());
|
||||||
if (dialog) {
|
if (dialog) {
|
||||||
@@ -728,17 +736,35 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
|
|||||||
}
|
}
|
||||||
delete dialog;
|
delete dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newModelNode.isSubclassOf("QtQuick3D.View3D")) {
|
||||||
|
const QList<ModelNode> models = newModelNode.subModelNodesOfType("QtQuick3D.Model");
|
||||||
|
|
||||||
|
QTC_ASSERT(models.size() == 1, return);
|
||||||
|
|
||||||
|
assignMaterialToModel(models.at(0));
|
||||||
|
} else if (newModelNode.isSubclassOf("QtQuick3D.Model")) {
|
||||||
|
assignMaterialToModel(newModelNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropping a material on a model
|
||||||
if (newModelNode.isSubclassOf("QtQuick3D.Material")
|
if (newModelNode.isSubclassOf("QtQuick3D.Material")
|
||||||
&& targetProperty.parentModelNode().isSubclassOf("QtQuick3D.Node")
|
&& targetNode.isSubclassOf("QtQuick3D.Model")) {
|
||||||
&& targetProperty.parentModelNode().isComponent()) {
|
// parent material to material library and assign it to target model
|
||||||
// Inserting materials under imported components is likely a mistake, so
|
ModelNode matLib = m_view->modelNodeForId(Constants::MATERIAL_LIB_ID);
|
||||||
// notify user with a helpful messagebox that suggests the correct action.
|
|
||||||
showMatToCompInfo = true;
|
QTC_ASSERT(matLib.isValid(), return);
|
||||||
|
|
||||||
|
VariantProperty objName = newModelNode.variantProperty("objectName");
|
||||||
|
objName.setValue("New Material");
|
||||||
|
BindingProperty matsProp = targetNode.bindingProperty("materials");
|
||||||
|
matsProp.setExpression(newModelNode.id());
|
||||||
|
matLib.defaultNodeListProperty().reparentHere(newModelNode);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validContainer) {
|
if (!validContainer) {
|
||||||
if (!showMatToCompInfo)
|
validContainer = NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newModelNode);
|
||||||
validContainer = NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newModelNode);
|
|
||||||
if (!validContainer)
|
if (!validContainer)
|
||||||
newQmlObjectNode.destroy();
|
newQmlObjectNode.destroy();
|
||||||
}
|
}
|
||||||
@@ -771,30 +797,6 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showMatToCompInfo) {
|
|
||||||
QMessageBox::StandardButton selectedButton = QMessageBox::information(
|
|
||||||
Core::ICore::dialogParent(),
|
|
||||||
QCoreApplication::translate("NavigatorTreeModel", "Warning"),
|
|
||||||
QCoreApplication::translate(
|
|
||||||
"NavigatorTreeModel",
|
|
||||||
"Inserting materials under imported 3D component nodes is not supported. "
|
|
||||||
"Materials used in imported 3D components have to be modified inside the component itself.\n\n"
|
|
||||||
"Would you like to go into component \"%1\"?")
|
|
||||||
.arg(targetProperty.parentModelNode().id()),
|
|
||||||
QMessageBox::Yes | QMessageBox::No,
|
|
||||||
QMessageBox::No);
|
|
||||||
if (selectedButton == QMessageBox::Yes) {
|
|
||||||
qint32 internalId = targetProperty.parentModelNode().internalId();
|
|
||||||
QTimer::singleShot(0, this, [internalId, this]() {
|
|
||||||
if (!m_view.isNull() && m_view->model()) {
|
|
||||||
ModelNode node = m_view->modelNodeForInternalId(internalId);
|
|
||||||
if (node.isValid() && node.isComponent())
|
|
||||||
DocumentManager::goIntoComponent(node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1092,6 +1094,40 @@ ModelNode NavigatorTreeModel::createTextureNode(const NodeAbstractProperty &targ
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a material to a Quick3D.Model node
|
||||||
|
void NavigatorTreeModel::assignMaterialToModel(const ModelNode &node)
|
||||||
|
{
|
||||||
|
ModelNode matLib = m_view->modelNodeForId(Constants::MATERIAL_LIB_ID);
|
||||||
|
|
||||||
|
QTC_ASSERT(matLib.isValid(), return);
|
||||||
|
QTC_ASSERT(node.isSubclassOf("QtQuick3D.Model"), return);
|
||||||
|
|
||||||
|
const QList<ModelNode> materials = matLib.directSubModelNodes();
|
||||||
|
ModelNode material;
|
||||||
|
if (materials.size() > 0) {
|
||||||
|
for (const ModelNode &mat : materials) {
|
||||||
|
if (mat.isSubclassOf("QtQuick3D.Material")) {
|
||||||
|
material = mat;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no valid material, create a new default material
|
||||||
|
if (!material.isValid()) {
|
||||||
|
NodeMetaInfo metaInfo = m_view->model()->metaInfo("QtQuick3D.DefaultMaterial");
|
||||||
|
material = m_view->createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(),
|
||||||
|
metaInfo.minorVersion());
|
||||||
|
VariantProperty matNameProp = material.variantProperty("objectName");
|
||||||
|
matNameProp.setValue("New Material");
|
||||||
|
material.validId();
|
||||||
|
matLib.defaultNodeListProperty().reparentHere(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingProperty modelMatsProp = node.bindingProperty("materials");
|
||||||
|
modelMatsProp.setExpression(material.id());
|
||||||
|
}
|
||||||
|
|
||||||
TypeName propertyType(const NodeAbstractProperty &property)
|
TypeName propertyType(const NodeAbstractProperty &property)
|
||||||
{
|
{
|
||||||
return property.parentModelNode().metaInfo().propertyTypeName(property.name());
|
return property.parentModelNode().metaInfo().propertyTypeName(property.name());
|
||||||
|
@@ -130,6 +130,7 @@ private:
|
|||||||
bool dropAsImage3dTexture(const ModelNode &targetNode, const NodeAbstractProperty &targetProp,
|
bool dropAsImage3dTexture(const ModelNode &targetNode, const NodeAbstractProperty &targetProp,
|
||||||
const QString &imagePath, ModelNode &newNode, bool &outMoveNodesAfter);
|
const QString &imagePath, ModelNode &newNode, bool &outMoveNodesAfter);
|
||||||
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath);
|
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath);
|
||||||
|
void assignMaterialToModel(const ModelNode &node);
|
||||||
QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes);
|
QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes);
|
||||||
void addImport(const QString &importName);
|
void addImport(const QString &importName);
|
||||||
QList<ModelNode> filteredList(const NodeListProperty &property, bool filter, bool reverseOrder) const;
|
QList<ModelNode> filteredList(const NodeListProperty &property, bool filter, bool reverseOrder) const;
|
||||||
|
@@ -24,12 +24,15 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "propertyeditorvalue.h"
|
#include "propertyeditorvalue.h"
|
||||||
|
#include "variantproperty.h"
|
||||||
|
#include "documentmanager.h"
|
||||||
|
|
||||||
#include <abstractview.h>
|
#include <abstractview.h>
|
||||||
#include <bindingproperty.h>
|
#include <bindingproperty.h>
|
||||||
#include <designdocument.h>
|
#include <designdocument.h>
|
||||||
#include <nodeproperty.h>
|
#include <nodelistproperty.h>
|
||||||
#include <nodemetainfo.h>
|
#include <nodemetainfo.h>
|
||||||
|
#include <nodeproperty.h>
|
||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
#include <qmlobjectnode.h>
|
#include <qmlobjectnode.h>
|
||||||
#include <designermcumanager.h>
|
#include <designermcumanager.h>
|
||||||
@@ -40,6 +43,7 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
|
#include <assetslibrarymodel.h>
|
||||||
|
|
||||||
//using namespace QmlDesigner;
|
//using namespace QmlDesigner;
|
||||||
|
|
||||||
@@ -367,6 +371,18 @@ void PropertyEditorValue::setEnumeration(const QString &scope, const QString &na
|
|||||||
setValueWithEmit(QVariant::fromValue(newEnumeration));
|
setValueWithEmit(QVariant::fromValue(newEnumeration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PropertyEditorValue::isSupportedDrop(const QString &path)
|
||||||
|
{
|
||||||
|
QString suffix = "*." + QFileInfo(path).suffix().toLower();
|
||||||
|
|
||||||
|
if (m_modelNode.isSubclassOf("QtQuick3D.Material") && nameAsQString().endsWith("Map"))
|
||||||
|
return QmlDesigner::AssetsLibraryModel::supportedImageSuffixes().contains(suffix);
|
||||||
|
|
||||||
|
// TODO: handle support for other object properties dnd here (like image source)
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void PropertyEditorValue::exportPropertyAsAlias()
|
void PropertyEditorValue::exportPropertyAsAlias()
|
||||||
{
|
{
|
||||||
emit exportPropertyAsAliasRequested(nameAsQString());
|
emit exportPropertyAsAliasRequested(nameAsQString());
|
||||||
@@ -499,6 +515,29 @@ bool PropertyEditorValue::idListReplace(int idx, const QString &value)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PropertyEditorValue::commitDrop(const QString &path)
|
||||||
|
{
|
||||||
|
if (m_modelNode.isSubclassOf("QtQuick3D.Material") && nameAsQString().endsWith("Map")) {
|
||||||
|
// create a texture node
|
||||||
|
QmlDesigner::NodeMetaInfo metaInfo = m_modelNode.view()->model()->metaInfo("QtQuick3D.Texture");
|
||||||
|
QmlDesigner::ModelNode texture = m_modelNode.view()->createModelNode("QtQuick3D.Texture",
|
||||||
|
metaInfo.majorVersion(),
|
||||||
|
metaInfo.minorVersion());
|
||||||
|
texture.validId();
|
||||||
|
modelNode().view()->rootModelNode().defaultNodeListProperty().reparentHere(texture);
|
||||||
|
// TODO: group textures under 1 node (just like materials)
|
||||||
|
|
||||||
|
// set texture source
|
||||||
|
Utils::FilePath imagePath = Utils::FilePath::fromString(path);
|
||||||
|
Utils::FilePath currFilePath = QmlDesigner::DocumentManager::currentFilePath();
|
||||||
|
QmlDesigner::VariantProperty srcProp = texture.variantProperty("source");
|
||||||
|
srcProp.setValue(imagePath.relativePath(currFilePath).toUrl());
|
||||||
|
|
||||||
|
// assign the texture to the property
|
||||||
|
setExpressionWithEmit(texture.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QStringList PropertyEditorValue::generateStringList(const QString &string) const
|
QStringList PropertyEditorValue::generateStringList(const QString &string) const
|
||||||
{
|
{
|
||||||
QString copy = string;
|
QString copy = string;
|
||||||
|
@@ -143,10 +143,12 @@ public:
|
|||||||
Q_INVOKABLE bool idListAdd(const QString &value);
|
Q_INVOKABLE bool idListAdd(const QString &value);
|
||||||
Q_INVOKABLE bool idListRemove(int idx);
|
Q_INVOKABLE bool idListRemove(int idx);
|
||||||
Q_INVOKABLE bool idListReplace(int idx, const QString &value);
|
Q_INVOKABLE bool idListReplace(int idx, const QString &value);
|
||||||
|
Q_INVOKABLE void commitDrop(const QString &path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void resetValue();
|
void resetValue();
|
||||||
void setEnumeration(const QString &scope, const QString &name);
|
void setEnumeration(const QString &scope, const QString &name);
|
||||||
|
bool isSupportedDrop(const QString &path);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(const QString &name, const QVariant&);
|
void valueChanged(const QString &name, const QVariant&);
|
||||||
|
@@ -125,6 +125,7 @@ public:
|
|||||||
void clearMetaInfoCache();
|
void clearMetaInfoCache();
|
||||||
|
|
||||||
bool hasId(const QString &id) const;
|
bool hasId(const QString &id) const;
|
||||||
|
bool hasImport(const QString &importUrl) const;
|
||||||
|
|
||||||
QString generateNewId(const QString &prefixName) const;
|
QString generateNewId(const QString &prefixName) const;
|
||||||
QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const;
|
QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const;
|
||||||
|
@@ -1462,6 +1462,13 @@ bool Model::hasId(const QString &id) const
|
|||||||
return d->hasId(id);
|
return d->hasId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Model::hasImport(const QString &importUrl) const
|
||||||
|
{
|
||||||
|
return Utils::anyOf(imports(), [&](const Import &import) {
|
||||||
|
return import.url() == importUrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static QString firstCharToLower(const QString &string)
|
static QString firstCharToLower(const QString &string)
|
||||||
{
|
{
|
||||||
QString resultString = string;
|
QString resultString = string;
|
||||||
|
@@ -42,6 +42,8 @@
|
|||||||
#include <navigatorview.h>
|
#include <navigatorview.h>
|
||||||
#include <nodeinstanceview.h>
|
#include <nodeinstanceview.h>
|
||||||
#include <propertyeditorview.h>
|
#include <propertyeditorview.h>
|
||||||
|
#include <materialeditorview.h>
|
||||||
|
#include <materialbrowserview.h>
|
||||||
#include <rewriterview.h>
|
#include <rewriterview.h>
|
||||||
#include <stateseditorview.h>
|
#include <stateseditorview.h>
|
||||||
#include <texteditorview.h>
|
#include <texteditorview.h>
|
||||||
@@ -76,6 +78,8 @@ public:
|
|||||||
ItemLibraryView itemLibraryView;
|
ItemLibraryView itemLibraryView;
|
||||||
NavigatorView navigatorView;
|
NavigatorView navigatorView;
|
||||||
PropertyEditorView propertyEditorView;
|
PropertyEditorView propertyEditorView;
|
||||||
|
MaterialEditorView materialEditorView;
|
||||||
|
MaterialBrowserView materialBrowserView;
|
||||||
StatesEditorView statesEditorView;
|
StatesEditorView statesEditorView;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<AbstractView>> additionalViews;
|
std::vector<std::unique_ptr<AbstractView>> additionalViews;
|
||||||
@@ -92,7 +96,7 @@ ViewManager::ViewManager()
|
|||||||
d->formEditorView.setGotoErrorCallback([this](int line, int column) {
|
d->formEditorView.setGotoErrorCallback([this](int line, int column) {
|
||||||
d->textEditorView.gotoCursorPosition(line, column);
|
d->textEditorView.gotoCursorPosition(line, column);
|
||||||
if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance()->mainWidget())
|
if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance()->mainWidget())
|
||||||
designModeWidget->showInternalTextEditor();
|
designModeWidget->showDockWidget("TextEditor");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +187,8 @@ QList<AbstractView *> ViewManager::standardViews() const
|
|||||||
&d->itemLibraryView,
|
&d->itemLibraryView,
|
||||||
&d->navigatorView,
|
&d->navigatorView,
|
||||||
&d->propertyEditorView,
|
&d->propertyEditorView,
|
||||||
|
&d->materialEditorView,
|
||||||
|
&d->materialBrowserView,
|
||||||
&d->statesEditorView,
|
&d->statesEditorView,
|
||||||
&d->designerActionManagerView};
|
&d->designerActionManagerView};
|
||||||
|
|
||||||
@@ -316,6 +322,8 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
|
|||||||
widgetInfoList.append(d->itemLibraryView.widgetInfo());
|
widgetInfoList.append(d->itemLibraryView.widgetInfo());
|
||||||
widgetInfoList.append(d->navigatorView.widgetInfo());
|
widgetInfoList.append(d->navigatorView.widgetInfo());
|
||||||
widgetInfoList.append(d->propertyEditorView.widgetInfo());
|
widgetInfoList.append(d->propertyEditorView.widgetInfo());
|
||||||
|
widgetInfoList.append(d->materialEditorView.widgetInfo());
|
||||||
|
widgetInfoList.append(d->materialBrowserView.widgetInfo());
|
||||||
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
||||||
if (d->debugView.hasWidget())
|
if (d->debugView.hasWidget())
|
||||||
widgetInfoList.append(d->debugView.widgetInfo());
|
widgetInfoList.append(d->debugView.widgetInfo());
|
||||||
|
@@ -583,9 +583,9 @@ CrumbleBar *DesignModeWidget::crumbleBar() const
|
|||||||
return m_crumbleBar;
|
return m_crumbleBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DesignModeWidget::showInternalTextEditor()
|
void DesignModeWidget::showDockWidget(const QString &objectName)
|
||||||
{
|
{
|
||||||
auto dockWidget = m_dockManager->findDockWidget("TextEditor");
|
auto dockWidget = m_dockManager->findDockWidget(objectName);
|
||||||
if (dockWidget)
|
if (dockWidget)
|
||||||
dockWidget->toggleView(true);
|
dockWidget->toggleView(true);
|
||||||
}
|
}
|
||||||
|
@@ -79,14 +79,14 @@ public:
|
|||||||
void enableWidgets();
|
void enableWidgets();
|
||||||
void disableWidgets();
|
void disableWidgets();
|
||||||
|
|
||||||
CrumbleBar* crumbleBar() const;
|
CrumbleBar *crumbleBar() const;
|
||||||
void showInternalTextEditor();
|
void showDockWidget(const QString &objectName);
|
||||||
|
|
||||||
void determineWorkspaceToRestoreAtStartup();
|
void determineWorkspaceToRestoreAtStartup();
|
||||||
|
|
||||||
static QWidget *createProjectExplorerWidget(QWidget *parent);
|
static QWidget *createProjectExplorerWidget(QWidget *parent);
|
||||||
|
|
||||||
private: // functions
|
private:
|
||||||
enum InitializeStatus { NotInitialized, Initializing, Initialized };
|
enum InitializeStatus { NotInitialized, Initializing, Initialized };
|
||||||
|
|
||||||
void toolBarOnGoBackClicked();
|
void toolBarOnGoBackClicked();
|
||||||
@@ -101,7 +101,6 @@ private: // functions
|
|||||||
|
|
||||||
void aboutToShowWorkspaces();
|
void aboutToShowWorkspaces();
|
||||||
|
|
||||||
private: // variables
|
|
||||||
QPointer<QWidget> m_bottomSideBar;
|
QPointer<QWidget> m_bottomSideBar;
|
||||||
Core::EditorToolBar *m_toolBar;
|
Core::EditorToolBar *m_toolBar;
|
||||||
CrumbleBar *m_crumbleBar;
|
CrumbleBar *m_crumbleBar;
|
||||||
|
@@ -84,6 +84,7 @@ const char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json";
|
|||||||
const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options";
|
const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options";
|
||||||
const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene";
|
const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene";
|
||||||
const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports";
|
const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports";
|
||||||
|
const char MATERIAL_LIB_ID[] = "__materialLibrary__";
|
||||||
|
|
||||||
const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo";
|
const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo";
|
||||||
const char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets";
|
const char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets";
|
||||||
|
@@ -488,7 +488,7 @@ Project {
|
|||||||
"componentcore/formatoperation.h",
|
"componentcore/formatoperation.h",
|
||||||
"componentcore/layoutingridlayout.cpp",
|
"componentcore/layoutingridlayout.cpp",
|
||||||
"componentcore/layoutingridlayout.h",
|
"componentcore/layoutingridlayout.h",
|
||||||
"componentcore/theme.cpp",
|
"componentcore/theme.cpp",
|
||||||
"componentcore/theme.h",
|
"componentcore/theme.h",
|
||||||
"componentcore/modelnodecontextmenu.cpp",
|
"componentcore/modelnodecontextmenu.cpp",
|
||||||
"componentcore/modelnodecontextmenu.h",
|
"componentcore/modelnodecontextmenu.h",
|
||||||
@@ -675,6 +675,20 @@ Project {
|
|||||||
"itemlibrary/itemlibrarywidget.h",
|
"itemlibrary/itemlibrarywidget.h",
|
||||||
"itemlibrary/itemlibraryiconimageprovider.cpp",
|
"itemlibrary/itemlibraryiconimageprovider.cpp",
|
||||||
"itemlibrary/itemlibraryiconimageprovider.h",
|
"itemlibrary/itemlibraryiconimageprovider.h",
|
||||||
|
"materialbrowser/materialbrowsermodel.cpp",
|
||||||
|
"materialbrowser/materialbrowsermodel.h",
|
||||||
|
"materialbrowser/materialbrowserview.cpp",
|
||||||
|
"materialbrowser/materialbrowserview.h",
|
||||||
|
"materialbrowser/materialbrowserwidget.cpp",
|
||||||
|
"materialbrowser/materialbrowserwidget.h",
|
||||||
|
"materialeditor/materialeditorcontextobject.cpp",
|
||||||
|
"materialeditor/materialeditorcontextobject.h",
|
||||||
|
"materialeditor/materialeditorqmlbackend.cpp",
|
||||||
|
"materialeditor/materialeditorqmlbackend.h",
|
||||||
|
"materialeditor/materialeditortransaction.cpp",
|
||||||
|
"materialeditor/materialeditortransaction.h",
|
||||||
|
"materialeditor/materialeditorview.cpp",
|
||||||
|
"materialeditor/materialeditorview.h",
|
||||||
"navigator/iconcheckboxitemdelegate.cpp",
|
"navigator/iconcheckboxitemdelegate.cpp",
|
||||||
"navigator/iconcheckboxitemdelegate.h",
|
"navigator/iconcheckboxitemdelegate.h",
|
||||||
"navigator/nameitemdelegate.cpp",
|
"navigator/nameitemdelegate.cpp",
|
||||||
|
Reference in New Issue
Block a user