forked from qt-creator/qt-creator
QmlDesigner: Block changing eulerRotation for 3D Node that has rotation
QtQuick3D doesn't support both rotation and eulerRotation set on same node, so we delete existing quaternion rotation if user sets an euler rotation to the node. If node has quaternion rotation animation, we block any change to eulerRotation in the node. 3D editor also disables the rotation gizmo for blocked nodes. Task-number: QDS-4335 Change-Id: I4ce7b022c732a9547751f101548ecea948279db8 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -40,16 +40,28 @@ Node {
|
||||
property real currentAngle
|
||||
property point currentMousePos
|
||||
property alias freeDraggerArea: mouseAreaFree
|
||||
property bool blocked: false
|
||||
|
||||
position: dragHelper.pivotScenePosition(targetNode)
|
||||
|
||||
onTargetNodeChanged: position = dragHelper.pivotScenePosition(targetNode)
|
||||
onTargetNodeChanged: {
|
||||
position = dragHelper.pivotScenePosition(targetNode);
|
||||
blocked = _generalHelper.isRotationBlocked(targetNode);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: rotateGizmo.targetNode
|
||||
function onSceneTransformChanged()
|
||||
{
|
||||
rotateGizmo.position = rotateGizmo.dragHelper.pivotScenePosition(rotateGizmo.targetNode);
|
||||
rotateGizmo.position = dragHelper.pivotScenePosition(targetNode);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: _generalHelper
|
||||
function onRotationBlocksChanged()
|
||||
{
|
||||
blocked = _generalHelper.isRotationBlocked(targetNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,11 +94,12 @@ Node {
|
||||
objectName: "Rotate Ring X"
|
||||
eulerRotation: Qt.vector3d(0, 90, 0)
|
||||
targetNode: rotateGizmo.targetNode
|
||||
color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(1, 0, 0, 1))
|
||||
: Qt.rgba(1, 0, 0, 1)
|
||||
color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1)
|
||||
: highlightOnHover && (hovering || dragging)
|
||||
? Qt.lighter(Qt.rgba(1, 0, 0, 1)) : Qt.rgba(1, 0, 0, 1)
|
||||
priority: 40
|
||||
view3D: rotateGizmo.view3D
|
||||
active: rotateGizmo.visible
|
||||
active: rotateGizmo.visible && !rotateGizmo.blocked
|
||||
dragHelper: rotateGizmo.dragHelper
|
||||
|
||||
onRotateCommit: rotateGizmo.rotateCommit()
|
||||
@@ -100,13 +113,14 @@ Node {
|
||||
objectName: "Rotate Ring Y"
|
||||
eulerRotation: Qt.vector3d(90, 0, 0)
|
||||
targetNode: rotateGizmo.targetNode
|
||||
color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1))
|
||||
: Qt.rgba(0, 0.6, 0, 1)
|
||||
color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1)
|
||||
: highlightOnHover && (hovering || dragging)
|
||||
? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) : Qt.rgba(0, 0.6, 0, 1)
|
||||
// Just a smidge smaller than higher priority rings so that it doesn't obscure them
|
||||
scale: Qt.vector3d(0.998, 0.998, 0.998)
|
||||
priority: 30
|
||||
view3D: rotateGizmo.view3D
|
||||
active: rotateGizmo.visible
|
||||
active: rotateGizmo.visible && !rotateGizmo.blocked
|
||||
dragHelper: rotateGizmo.dragHelper
|
||||
|
||||
onRotateCommit: rotateGizmo.rotateCommit()
|
||||
@@ -120,13 +134,14 @@ Node {
|
||||
objectName: "Rotate Ring Z"
|
||||
eulerRotation: Qt.vector3d(0, 0, 0)
|
||||
targetNode: rotateGizmo.targetNode
|
||||
color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1))
|
||||
: Qt.rgba(0, 0, 1, 1)
|
||||
color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1)
|
||||
: highlightOnHover && (hovering || dragging)
|
||||
? Qt.lighter(Qt.rgba(0, 0, 1, 1)) : Qt.rgba(0, 0, 1, 1)
|
||||
// Just a smidge smaller than higher priority rings so that it doesn't obscure them
|
||||
scale: Qt.vector3d(0.996, 0.996, 0.996)
|
||||
priority: 20
|
||||
view3D: rotateGizmo.view3D
|
||||
active: rotateGizmo.visible
|
||||
active: rotateGizmo.visible && !rotateGizmo.blocked
|
||||
dragHelper: rotateGizmo.dragHelper
|
||||
|
||||
onRotateCommit: rotateGizmo.rotateCommit()
|
||||
@@ -154,12 +169,14 @@ Node {
|
||||
objectName: "cameraRing"
|
||||
rotation: rotateGizmo.view3D.camera.rotation
|
||||
targetNode: rotateGizmo.targetNode
|
||||
color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1))
|
||||
: Qt.rgba(0.5, 0.5, 0.5, 1)
|
||||
color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1)
|
||||
: highlightOnHover && (hovering || dragging)
|
||||
? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1))
|
||||
: Qt.rgba(0.5, 0.5, 0.5, 1)
|
||||
scale: Qt.vector3d(1.1, 1.1, 1.1)
|
||||
priority: 10
|
||||
view3D: rotateGizmo.view3D
|
||||
active: rotateGizmo.visible
|
||||
active: rotateGizmo.visible && !rotateGizmo.blocked
|
||||
dragHelper: rotateGizmo.dragHelper
|
||||
visible: !rotRingX.dragging && !rotRingY.dragging && !rotRingZ.dragging && !freeRotator.dragging
|
||||
|
||||
@@ -176,7 +193,7 @@ Node {
|
||||
materials: DefaultMaterial {
|
||||
id: material
|
||||
diffuseColor: "black"
|
||||
opacity: mouseAreaFree.hovering ? 0.15 : 0
|
||||
opacity: mouseAreaFree.hovering && !rotateGizmo.blocked ? 0.15 : 0
|
||||
lighting: DefaultMaterial.NoLighting
|
||||
}
|
||||
scale: Qt.vector3d(0.15, 0.15, 0.15)
|
||||
@@ -200,6 +217,14 @@ Node {
|
||||
_startRotation = Qt.vector3d(rotateGizmo.targetNode.eulerRotation.x,
|
||||
rotateGizmo.targetNode.eulerRotation.y,
|
||||
rotateGizmo.targetNode.eulerRotation.z);
|
||||
// Ensure we never set NaN values for rotation, even if target node originally has them
|
||||
if (isNaN(_startRotation.x))
|
||||
_startRotation.x = 0;
|
||||
if (isNaN(_startRotation.y))
|
||||
_startRotation.y = 0;
|
||||
if (isNaN(_startRotation.z))
|
||||
_startRotation.z = 0;
|
||||
|
||||
dragging = true;
|
||||
}
|
||||
|
||||
@@ -239,7 +264,7 @@ Node {
|
||||
height: 100
|
||||
circlePickArea: Qt.point(25, 50)
|
||||
grabsMouse: rotateGizmo.targetNode
|
||||
active: rotateGizmo.visible
|
||||
active: rotateGizmo.visible && !rotateGizmo.blocked
|
||||
dragHelper: rotateGizmo.dragHelper
|
||||
|
||||
onPressed: freeRotator.handlePressed(screenPos)
|
||||
|
@@ -90,6 +90,13 @@ Model {
|
||||
_startRotation = Qt.vector3d(targetNode.eulerRotation.x,
|
||||
targetNode.eulerRotation.y,
|
||||
targetNode.eulerRotation.z);
|
||||
// Ensure we never set NaN values for rotation, even if target node originally has them
|
||||
if (isNaN(_startRotation.x))
|
||||
_startRotation.x = 0;
|
||||
if (isNaN(_startRotation.y))
|
||||
_startRotation.y = 0;
|
||||
if (isNaN(_startRotation.z))
|
||||
_startRotation.z = 0;
|
||||
currentAngle = 0;
|
||||
currentMousePos = screenPos;
|
||||
}
|
||||
|
@@ -356,6 +356,24 @@ bool GeneralHelper::isMacOS() const
|
||||
#endif
|
||||
}
|
||||
|
||||
void GeneralHelper::addRotationBlocks(const QSet<QQuick3DNode *> &nodes)
|
||||
{
|
||||
m_rotationBlockedNodes.unite(nodes);
|
||||
emit rotationBlocksChanged();
|
||||
}
|
||||
|
||||
void GeneralHelper::removeRotationBlocks(const QSet<QQuick3DNode *> &nodes)
|
||||
{
|
||||
for (auto node : nodes)
|
||||
m_rotationBlockedNodes.remove(node);
|
||||
emit rotationBlocksChanged();
|
||||
}
|
||||
|
||||
bool GeneralHelper::isRotationBlocked(QQuick3DNode *node) const
|
||||
{
|
||||
return m_rotationBlockedNodes.contains(node);
|
||||
}
|
||||
|
||||
bool GeneralHelper::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::DynamicPropertyChange) {
|
||||
|
@@ -93,11 +93,16 @@ public:
|
||||
|
||||
bool isMacOS() const;
|
||||
|
||||
void addRotationBlocks(const QSet<QQuick3DNode *> &nodes);
|
||||
void removeRotationBlocks(const QSet<QQuick3DNode *> &nodes);
|
||||
Q_INVOKABLE bool isRotationBlocked(QQuick3DNode *node) const;
|
||||
|
||||
signals:
|
||||
void overlayUpdateNeeded();
|
||||
void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState);
|
||||
void hiddenStateChanged(QQuick3DNode *node);
|
||||
void lockedStateChanged(QQuick3DNode *node);
|
||||
void rotationBlocksChanged();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) final;
|
||||
@@ -110,6 +115,7 @@ private:
|
||||
QHash<QString, QVariantMap> m_toolStates;
|
||||
QHash<QString, QVariantMap> m_toolStatesPending;
|
||||
QSet<QQuick3DNode *> m_gizmoTargets;
|
||||
QSet<QQuick3DNode *> m_rotationBlockedNodes;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -63,6 +63,7 @@
|
||||
#include "inputeventcommand.h"
|
||||
#include "view3dactioncommand.h"
|
||||
#include "requestmodelnodepreviewimagecommand.h"
|
||||
#include "changeauxiliarycommand.h"
|
||||
|
||||
#include "dummycontextobject.h"
|
||||
#include "../editor3d/generalhelper.h"
|
||||
@@ -288,6 +289,57 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport()
|
||||
#endif
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges)
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
|
||||
if (helper) {
|
||||
QSet<QQuick3DNode *> blockedNodes;
|
||||
QSet<QQuick3DNode *> unblockedNodes;
|
||||
const PropertyName propName = "rotBlocked@internal";
|
||||
for (const auto &container : valueChanges) {
|
||||
if (container.name() == propName) {
|
||||
ServerNodeInstance instance = instanceForId(container.instanceId());
|
||||
if (instance.isValid()) {
|
||||
auto node = qobject_cast<QQuick3DNode *>(instance.internalObject());
|
||||
if (node) {
|
||||
if (container.value().toBool())
|
||||
blockedNodes.insert(node);
|
||||
else
|
||||
unblockedNodes.insert(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
helper->addRotationBlocks(blockedNodes);
|
||||
helper->removeRotationBlocks(unblockedNodes);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(valueChanges)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::removeRotationBlocks(const QVector<qint32> &instanceIds)
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
|
||||
if (helper) {
|
||||
QSet<QQuick3DNode *> unblockedNodes;
|
||||
for (const auto &id : instanceIds) {
|
||||
ServerNodeInstance instance = instanceForId(id);
|
||||
if (instance.isValid()) {
|
||||
auto node = qobject_cast<QQuick3DNode *>(instance.internalObject());
|
||||
if (node)
|
||||
unblockedNodes.insert(node);
|
||||
}
|
||||
}
|
||||
helper->removeRotationBlocks(unblockedNodes);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(instanceIds)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::createEditView3D()
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
@@ -1492,8 +1544,10 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com
|
||||
sendChildrenChangedCommand(instanceList);
|
||||
nodeInstanceClient()->componentCompleted(createComponentCompletedCommand(instanceList));
|
||||
|
||||
if (isQuick3DMode())
|
||||
if (isQuick3DMode()) {
|
||||
setup3DEditView(instanceList, command.edit3dToolStates);
|
||||
updateRotationBlocks(command.auxiliaryChanges);
|
||||
}
|
||||
|
||||
QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout,
|
||||
this, &Qt5InformationNodeInstanceServer::doRenderModelNodeImageView);
|
||||
@@ -1656,6 +1710,8 @@ void Qt5InformationNodeInstanceServer::removeInstances(const RemoveInstancesComm
|
||||
{
|
||||
int nodeCount = m_3DSceneMap.size();
|
||||
|
||||
removeRotationBlocks(command.instanceIds());
|
||||
|
||||
Qt5NodeInstanceServer::removeInstances(command);
|
||||
|
||||
if (nodeCount != m_3DSceneMap.size()) {
|
||||
@@ -1741,6 +1797,7 @@ void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const Reques
|
||||
|
||||
void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
|
||||
{
|
||||
updateRotationBlocks(command.auxiliaryChanges);
|
||||
Qt5NodeInstanceServer::changeAuxiliaryValues(command);
|
||||
render3DEditView();
|
||||
}
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include "valueschangedcommand.h"
|
||||
#include "changeselectioncommand.h"
|
||||
#include "requestmodelnodepreviewimagecommand.h"
|
||||
#include "propertybindingcontainer.h"
|
||||
#include "propertyabstractcontainer.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QVariant>
|
||||
@@ -132,6 +134,8 @@ private:
|
||||
void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances);
|
||||
void handleInputEvents();
|
||||
void resolveImportSupport();
|
||||
void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges);
|
||||
void removeRotationBlocks(const QVector<qint32> &instanceIds);
|
||||
|
||||
void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData);
|
||||
|
||||
|
@@ -105,6 +105,13 @@ Item {
|
||||
extendedFunctionButton.menuVisible = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelNodeBackend
|
||||
onSelectionChanged: {
|
||||
menu.close()
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Reset")
|
||||
onTriggered: {
|
||||
|
@@ -50,6 +50,17 @@ Item {
|
||||
transaction.end();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
spinBox.enabled = !isBlocked(backendValue.name);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelNodeBackend
|
||||
function onSelectionChanged() {
|
||||
spinBox.enabled = !isBlocked(backendValue.name);
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.RealSpinBox {
|
||||
id: spinBox
|
||||
|
||||
|
@@ -303,6 +303,9 @@ void PropertyEditorContextObject::insertKeyframe(const QString &propertyName)
|
||||
{
|
||||
QTC_ASSERT(m_model && m_model->rewriterView(), return);
|
||||
|
||||
if (isBlocked(propertyName))
|
||||
return;
|
||||
|
||||
/* Ideally we should not missuse the rewriterView
|
||||
* If we add more code here we have to forward the property editor view */
|
||||
RewriterView *rewriterView = m_model->rewriterView();
|
||||
@@ -557,6 +560,20 @@ QStringList PropertyEditorContextObject::allStatesForId(const QString &id)
|
||||
return {};
|
||||
}
|
||||
|
||||
bool PropertyEditorContextObject::isBlocked(const QString &propName) const
|
||||
{
|
||||
if (m_model && m_model->rewriterView()) {
|
||||
const QList<ModelNode> nodes = m_model->rewriterView()->selectedModelNodes();
|
||||
QScopedPointer<QmlObjectNode> objNode;
|
||||
for (const auto &node : nodes) {
|
||||
objNode.reset(QmlObjectNode::getQmlObjectNodeOfCorrectType(node));
|
||||
if (objNode->isBlocked(propName.toUtf8()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EasingCurveEditor::registerDeclarativeType()
|
||||
{
|
||||
qmlRegisterType<EasingCurveEditor>("HelperWidgets", 2, 0, "EasingCurveEditor");
|
||||
|
@@ -99,6 +99,8 @@ public:
|
||||
|
||||
Q_INVOKABLE QStringList allStatesForId(const QString &id);
|
||||
|
||||
Q_INVOKABLE bool isBlocked(const QString &propName) const;
|
||||
|
||||
int majorVersion() const;
|
||||
int majorQtQuickVersion() const;
|
||||
int minorQtQuickVersion() const;
|
||||
|
@@ -39,6 +39,7 @@
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QUrl>
|
||||
#include <QScopedPointer>
|
||||
|
||||
//using namespace QmlDesigner;
|
||||
|
||||
@@ -610,14 +611,15 @@ void PropertyEditorNodeWrapper::changeValue(const QString &propertyName)
|
||||
if (name.isNull())
|
||||
return;
|
||||
if (m_modelNode.isValid()) {
|
||||
QmlDesigner::QmlObjectNode qmlObjectNode(m_modelNode);
|
||||
QScopedPointer<QmlDesigner::QmlObjectNode> qmlObjectNode{
|
||||
QmlDesigner::QmlObjectNode::getQmlObjectNodeOfCorrectType(m_modelNode)};
|
||||
|
||||
auto valueObject = qvariant_cast<PropertyEditorValue *>(m_valuesPropertyMap.value(QString::fromLatin1(name)));
|
||||
|
||||
if (valueObject->value().isValid())
|
||||
qmlObjectNode.setVariantProperty(name, valueObject->value());
|
||||
qmlObjectNode->setVariantProperty(name, valueObject->value());
|
||||
else
|
||||
qmlObjectNode.removeProperty(name);
|
||||
qmlObjectNode->removeProperty(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -56,6 +56,7 @@
|
||||
#include <QTimer>
|
||||
#include <QShortcut>
|
||||
#include <QApplication>
|
||||
#include <QScopedPointer>
|
||||
|
||||
enum {
|
||||
debug = false
|
||||
@@ -251,7 +252,7 @@ void PropertyEditorView::changeExpression(const QString &propertyName)
|
||||
PropertyName underscoreName(name);
|
||||
underscoreName.replace('.', '_');
|
||||
|
||||
QmlObjectNode qmlObjectNode(m_selectedNode);
|
||||
QScopedPointer<QmlObjectNode> qmlObjectNode {QmlObjectNode::getQmlObjectNodeOfCorrectType(m_selectedNode)};
|
||||
PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromLatin1(underscoreName));
|
||||
|
||||
if (!value) {
|
||||
@@ -259,33 +260,33 @@ void PropertyEditorView::changeExpression(const QString &propertyName)
|
||||
return;
|
||||
}
|
||||
|
||||
if (qmlObjectNode.modelNode().metaInfo().isValid() && qmlObjectNode.modelNode().metaInfo().hasProperty(name)) {
|
||||
if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "QColor") {
|
||||
if (qmlObjectNode->modelNode().metaInfo().isValid() && qmlObjectNode->modelNode().metaInfo().hasProperty(name)) {
|
||||
if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "QColor") {
|
||||
if (QColor(value->expression().remove('"')).isValid()) {
|
||||
qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"')));
|
||||
qmlObjectNode->setVariantProperty(name, QColor(value->expression().remove('"')));
|
||||
return;
|
||||
}
|
||||
} else if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "bool") {
|
||||
} else if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "bool") {
|
||||
if (value->expression().compare(QLatin1String("false"), Qt::CaseInsensitive) == 0
|
||||
|| value->expression().compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) {
|
||||
if (value->expression().compare(QLatin1String("true"), Qt::CaseInsensitive) == 0)
|
||||
qmlObjectNode.setVariantProperty(name, true);
|
||||
qmlObjectNode->setVariantProperty(name, true);
|
||||
else
|
||||
qmlObjectNode.setVariantProperty(name, false);
|
||||
qmlObjectNode->setVariantProperty(name, false);
|
||||
return;
|
||||
}
|
||||
} else if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "int") {
|
||||
} else if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "int") {
|
||||
bool ok;
|
||||
int intValue = value->expression().toInt(&ok);
|
||||
if (ok) {
|
||||
qmlObjectNode.setVariantProperty(name, intValue);
|
||||
qmlObjectNode->setVariantProperty(name, intValue);
|
||||
return;
|
||||
}
|
||||
} else if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "qreal") {
|
||||
} else if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "qreal") {
|
||||
bool ok;
|
||||
qreal realValue = value->expression().toDouble(&ok);
|
||||
if (ok) {
|
||||
qmlObjectNode.setVariantProperty(name, realValue);
|
||||
qmlObjectNode->setVariantProperty(name, realValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -296,8 +297,8 @@ void PropertyEditorView::changeExpression(const QString &propertyName)
|
||||
return;
|
||||
}
|
||||
|
||||
if (qmlObjectNode.expression(name) != value->expression() || !qmlObjectNode.propertyAffectedByCurrentState(name))
|
||||
qmlObjectNode.setBindingProperty(name, value->expression());
|
||||
if (qmlObjectNode->expression(name) != value->expression() || !qmlObjectNode->propertyAffectedByCurrentState(name))
|
||||
qmlObjectNode->setBindingProperty(name, value->expression());
|
||||
|
||||
}); /* end of transaction */
|
||||
}
|
||||
@@ -465,11 +466,13 @@ void PropertyEditorView::setupQmlBackend()
|
||||
m_stackedWidget->addWidget(currentQmlBackend->widget());
|
||||
m_qmlBackendHash.insert(qmlFile.toString(), currentQmlBackend);
|
||||
|
||||
QmlObjectNode qmlObjectNode;
|
||||
QScopedPointer<QmlObjectNode> qmlObjectNode;
|
||||
if (m_selectedNode.isValid()) {
|
||||
qmlObjectNode = QmlObjectNode(m_selectedNode);
|
||||
Q_ASSERT(qmlObjectNode.isValid());
|
||||
currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this);
|
||||
qmlObjectNode.reset(QmlObjectNode::getQmlObjectNodeOfCorrectType(m_selectedNode));
|
||||
Q_ASSERT(qmlObjectNode->isValid());
|
||||
currentQmlBackend->setup(*qmlObjectNode, currentStateName, qmlSpecificsFile, this);
|
||||
} else {
|
||||
qmlObjectNode.reset(new QmlObjectNode);
|
||||
}
|
||||
currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false));
|
||||
if (specificQmlData.isEmpty())
|
||||
@@ -480,14 +483,16 @@ void PropertyEditorView::setupQmlBackend()
|
||||
currentQmlBackend->setSource(qmlFile);
|
||||
currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(true));
|
||||
} else {
|
||||
QmlObjectNode qmlObjectNode;
|
||||
QScopedPointer<QmlObjectNode> qmlObjectNode;
|
||||
if (m_selectedNode.isValid())
|
||||
qmlObjectNode = QmlObjectNode(m_selectedNode);
|
||||
qmlObjectNode.reset(QmlObjectNode::getQmlObjectNodeOfCorrectType(m_selectedNode));
|
||||
else
|
||||
qmlObjectNode.reset(new QmlObjectNode);
|
||||
|
||||
currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false));
|
||||
if (specificQmlData.isEmpty())
|
||||
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
|
||||
currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this);
|
||||
currentQmlBackend->setup(*qmlObjectNode, currentStateName, qmlSpecificsFile, this);
|
||||
currentQmlBackend->contextObject()->setGlobalBaseUrl(qmlFile);
|
||||
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
|
||||
}
|
||||
@@ -509,8 +514,10 @@ void PropertyEditorView::commitVariantValueToModel(const PropertyName &propertyN
|
||||
RewriterTransaction transaction = beginRewriterTransaction("PropertyEditorView::commitVariantValueToMode");
|
||||
|
||||
for (const ModelNode &node : m_selectedNode.view()->selectedModelNodes()) {
|
||||
if (QmlObjectNode::isValidQmlObjectNode(node))
|
||||
QmlObjectNode(node).setVariantProperty(propertyName, value);
|
||||
if (QmlObjectNode::isValidQmlObjectNode(node)) {
|
||||
QScopedPointer<QmlObjectNode>{QmlObjectNode::getQmlObjectNodeOfCorrectType(node)}
|
||||
->setVariantProperty(propertyName, value);
|
||||
}
|
||||
}
|
||||
transaction.commit();
|
||||
}
|
||||
|
@@ -56,6 +56,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -354,8 +355,9 @@ void TimelinePropertyItem::changePropertyValue(const QVariant &value)
|
||||
QTimer::singleShot(0, deferredFunc);
|
||||
|
||||
} else {
|
||||
QmlObjectNode objectNode(m_frames.target());
|
||||
objectNode.setVariantProperty(m_frames.propertyName(), value);
|
||||
QScopedPointer<QmlObjectNode> objectNode {
|
||||
QmlObjectNode::getQmlObjectNodeOfCorrectType(m_frames.target())};
|
||||
objectNode->setVariantProperty(m_frames.propertyName(), value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -224,6 +224,8 @@ private: // functions
|
||||
|
||||
void updateWatcher(const QString &path);
|
||||
|
||||
void updateRotationBlocks();
|
||||
|
||||
private:
|
||||
QHash<QString, ModelNodePreviewImageData> m_imageDataMap;
|
||||
|
||||
@@ -251,6 +253,7 @@ private:
|
||||
QTimer m_resetTimer;
|
||||
QTimer m_updateWatcherTimer;
|
||||
QSet<QString> m_pendingUpdateDirs;
|
||||
QTimer m_rotBlockTimer;
|
||||
};
|
||||
|
||||
} // namespace ProxyNodeInstanceView
|
||||
|
@@ -49,6 +49,14 @@ public:
|
||||
Qml3DNode(const ModelNode &modelNode) : QmlVisualNode(modelNode) {}
|
||||
bool isValid() const override;
|
||||
static bool isValidQml3DNode(const ModelNode &modelNode);
|
||||
|
||||
// From QmlObjectNode
|
||||
void setVariantProperty(const PropertyName &name, const QVariant &value) override;
|
||||
void setBindingProperty(const PropertyName &name, const QString &expression) override;
|
||||
bool isBlocked(const PropertyName &propName) const override;
|
||||
|
||||
private:
|
||||
void handleEulerRotationSet();
|
||||
};
|
||||
|
||||
QMLDESIGNERCORE_EXPORT uint qHash(const Qml3DNode &node);
|
||||
|
@@ -69,8 +69,8 @@ public:
|
||||
|
||||
QmlModelState currentState() const;
|
||||
QmlTimeline currentTimeline() const;
|
||||
void setVariantProperty(const PropertyName &name, const QVariant &value);
|
||||
void setBindingProperty(const PropertyName &name, const QString &expression);
|
||||
virtual void setVariantProperty(const PropertyName &name, const QVariant &value);
|
||||
virtual void setBindingProperty(const PropertyName &name, const QString &expression);
|
||||
NodeAbstractProperty nodeAbstractProperty(const PropertyName &name) const;
|
||||
NodeAbstractProperty defaultNodeAbstractProperty() const;
|
||||
NodeProperty nodeProperty(const PropertyName &name) const;
|
||||
@@ -123,6 +123,10 @@ public:
|
||||
|
||||
QStringList allStateNames() const;
|
||||
|
||||
static QmlObjectNode *getQmlObjectNodeOfCorrectType(const ModelNode &modelNode);
|
||||
|
||||
virtual bool isBlocked(const PropertyName &propName) const;
|
||||
|
||||
protected:
|
||||
NodeInstance nodeInstance() const;
|
||||
QmlObjectNode nodeForInstance(const NodeInstance &instance) const;
|
||||
|
@@ -54,6 +54,7 @@
|
||||
#include "nodeproperty.h"
|
||||
#include "pixmapchangedcommand.h"
|
||||
#include "puppettocreatorcommand.h"
|
||||
#include "qml3dnode.h"
|
||||
#include "qmlchangeset.h"
|
||||
#include "qmldesignerconstants.h"
|
||||
#include "qmlstate.h"
|
||||
@@ -103,6 +104,7 @@
|
||||
#include <QPainter>
|
||||
#include <QDirIterator>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QScopedPointer>
|
||||
|
||||
enum {
|
||||
debug = false
|
||||
@@ -176,6 +178,10 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
|
||||
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this] {
|
||||
m_resetTimer.start();
|
||||
});
|
||||
|
||||
m_rotBlockTimer.setSingleShot(true);
|
||||
m_rotBlockTimer.setInterval(0);
|
||||
QObject::connect(&m_rotBlockTimer, &QTimer::timeout, this, &NodeInstanceView::updateRotationBlocks);
|
||||
}
|
||||
|
||||
|
||||
@@ -594,11 +600,12 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
|
||||
const QVariant &value)
|
||||
{
|
||||
QTC_ASSERT(m_nodeInstanceServer, return);
|
||||
if (((node.isRootNode() && (name == "width" || name == "height")) || name == "invisible" || name == "locked")
|
||||
const bool forceAuxChange = name == "invisible" || name == "locked" || name == "rotBlocked@internal";
|
||||
if (((node.isRootNode() && (name == "width" || name == "height")) || forceAuxChange)
|
||||
|| name.endsWith(PropertyName("@NodeInstance"))) {
|
||||
if (hasInstanceForModelNode(node)) {
|
||||
NodeInstance instance = instanceForModelNode(node);
|
||||
if (value.isValid() || name == "invisible" || name == "locked") {
|
||||
if (value.isValid() || forceAuxChange) {
|
||||
PropertyValueContainer container{instance.instanceId(), name, value, TypeName()};
|
||||
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
|
||||
} else {
|
||||
@@ -1341,10 +1348,10 @@ void NodeInstanceView::valuesModified(const ValuesModifiedCommand &command)
|
||||
if (hasInstanceForId(container.instanceId())) {
|
||||
NodeInstance instance = instanceForId(container.instanceId());
|
||||
if (instance.isValid()) {
|
||||
// QmlVisualNode is needed so timeline and state are updated
|
||||
QmlVisualNode node = instance.modelNode();
|
||||
if (node.modelValue(container.name()) != container.value())
|
||||
node.setVariantProperty(container.name(), container.value());
|
||||
QScopedPointer<QmlObjectNode> node {
|
||||
QmlObjectNode::getQmlObjectNodeOfCorrectType(instance.modelNode())};
|
||||
if (node->modelValue(container.name()) != container.value())
|
||||
node->setVariantProperty(container.name(), container.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1593,6 +1600,7 @@ void NodeInstanceView::selectedNodesChanged(const QList<ModelNode> &selectedNode
|
||||
const QList<ModelNode> & /*lastSelectedNodeList*/)
|
||||
{
|
||||
m_nodeInstanceServer->changeSelection(createChangeSelectionCommand(selectedNodeList));
|
||||
m_rotBlockTimer.start();
|
||||
}
|
||||
|
||||
void NodeInstanceView::sendInputEvent(QInputEvent *e) const
|
||||
@@ -1850,4 +1858,46 @@ void NodeInstanceView::updateWatcher(const QString &path)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInstanceView::updateRotationBlocks()
|
||||
{
|
||||
QList<ModelNode> qml3DNodes;
|
||||
QSet<ModelNode> rotationKeyframeTargets;
|
||||
bool groupsResolved = false;
|
||||
const PropertyName targetPropName {"target"};
|
||||
const PropertyName propertyPropName {"property"};
|
||||
const PropertyName rotationPropName {"rotation"};
|
||||
const QList<ModelNode> selectedNodes = selectedModelNodes();
|
||||
for (const auto &node : selectedNodes) {
|
||||
if (Qml3DNode::isValidQml3DNode(node)) {
|
||||
if (!groupsResolved) {
|
||||
const QList<ModelNode> keyframeGroups = allModelNodesOfType("KeyframeGroup");
|
||||
for (const auto &kfgNode : keyframeGroups) {
|
||||
if (kfgNode.isValid()) {
|
||||
VariantProperty varProp = kfgNode.variantProperty(propertyPropName);
|
||||
if (varProp.isValid() && varProp.value().value<PropertyName>() == rotationPropName) {
|
||||
BindingProperty bindProp = kfgNode.bindingProperty(targetPropName);
|
||||
if (bindProp.isValid()) {
|
||||
ModelNode targetNode = bindProp.resolveToModelNode();
|
||||
if (Qml3DNode::isValidQml3DNode(targetNode))
|
||||
rotationKeyframeTargets.insert(targetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
groupsResolved = true;
|
||||
}
|
||||
qml3DNodes.append(node);
|
||||
}
|
||||
}
|
||||
if (!qml3DNodes.isEmpty()) {
|
||||
const PropertyName auxDataProp {"rotBlocked@internal"};
|
||||
for (const auto &node : qAsConst(qml3DNodes)) {
|
||||
if (rotationKeyframeTargets.contains(node))
|
||||
node.setAuxiliaryData(auxDataProp, true);
|
||||
else
|
||||
node.setAuxiliaryData(auxDataProp, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -58,6 +58,68 @@ bool Qml3DNode::isValidQml3DNode(const ModelNode &modelNode)
|
||||
&& (modelNode.metaInfo().isSubclassOf("QtQuick3D.Node"));
|
||||
}
|
||||
|
||||
void Qml3DNode::setVariantProperty(const PropertyName &name, const QVariant &value)
|
||||
{
|
||||
if (isBlocked(name))
|
||||
return;
|
||||
|
||||
if (name.startsWith("eulerRotation"))
|
||||
handleEulerRotationSet();
|
||||
|
||||
QmlObjectNode::setVariantProperty(name, value);
|
||||
}
|
||||
|
||||
void Qml3DNode::setBindingProperty(const PropertyName &name, const QString &expression)
|
||||
{
|
||||
if (isBlocked(name))
|
||||
return;
|
||||
|
||||
if (name.startsWith("eulerRotation"))
|
||||
handleEulerRotationSet();
|
||||
|
||||
QmlObjectNode::setBindingProperty(name, expression);
|
||||
}
|
||||
|
||||
bool Qml3DNode::isBlocked(const PropertyName &propName) const
|
||||
{
|
||||
if (modelNode().isValid() && propName.startsWith("eulerRotation"))
|
||||
return modelNode().auxiliaryData("rotBlocked@internal").toBool();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Qml3DNode::handleEulerRotationSet()
|
||||
{
|
||||
ModelNode node = modelNode();
|
||||
// The rotation property is quaternion, which is difficult to deal with for users, so QDS
|
||||
// only supports eulerRotation. Since having both on the same object isn't supported,
|
||||
// remove the rotation property if eulerRotation is set.
|
||||
if (node.isValid() && node.isSubclassOf("QtQuick3D.Node")) {
|
||||
if (!isInBaseState()) {
|
||||
QmlPropertyChanges changeSet(currentState().propertyChanges(node));
|
||||
Q_ASSERT(changeSet.isValid());
|
||||
node = changeSet.modelNode();
|
||||
}
|
||||
|
||||
if (node.hasProperty("rotation")) {
|
||||
// We need to reset the eulerRotation values as removing rotation will zero them,
|
||||
// which is not desirable if the change only targets one of the xyz subproperties.
|
||||
// Get the eulerRotation value from instance, as they are not available in model.
|
||||
QVector3D eulerVec = instanceValue("eulerRotation").value<QVector3D>();
|
||||
node.removeProperty("rotation");
|
||||
if (qIsNaN(eulerVec.x()))
|
||||
eulerVec.setX(0.);
|
||||
if (qIsNaN(eulerVec.y()))
|
||||
eulerVec.setY(0.);
|
||||
if (qIsNaN(eulerVec.z()))
|
||||
eulerVec.setZ(0.);
|
||||
node.variantProperty("eulerRotation.x").setValue(eulerVec.x());
|
||||
node.variantProperty("eulerRotation.y").setValue(eulerVec.y());
|
||||
node.variantProperty("eulerRotation.z").setValue(eulerVec.z());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<ModelNode> toModelNodeList(const QList<Qml3DNode> &qmlVisualNodeList)
|
||||
{
|
||||
QList<ModelNode> modelNodeList;
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "qmlstate.h"
|
||||
#include "qmltimelinekeyframegroup.h"
|
||||
#include "qmlvisualnode.h"
|
||||
#include "qml3dnode.h"
|
||||
#include "variantproperty.h"
|
||||
#include "nodeproperty.h"
|
||||
#include <invalidmodelnodeexception.h>
|
||||
@@ -738,4 +739,19 @@ QStringList QmlObjectNode::allStateNames() const
|
||||
return nodeInstance().allStateNames();
|
||||
}
|
||||
|
||||
QmlObjectNode *QmlObjectNode::getQmlObjectNodeOfCorrectType(const ModelNode &modelNode)
|
||||
{
|
||||
// Create QmlObjectNode of correct type for the modelNode
|
||||
// Note: Currently we are only interested in differentiating 3D nodes, so no check for
|
||||
// visual nodes is done for efficiency reasons
|
||||
if (modelNode.isValid() && modelNode.isSubclassOf("QtQuick3D.Node"))
|
||||
return new Qml3DNode(modelNode);
|
||||
return new QmlObjectNode(modelNode);
|
||||
}
|
||||
|
||||
bool QmlObjectNode::isBlocked(const PropertyName &propName) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} //QmlDesigner
|
||||
|
Reference in New Issue
Block a user