QmlPuppet: Support multiselection transformations in 3D edit view

Add support for moving, rotating, and scaling a multiselection of 3D
nodes in 3D edit view.

Fixes: QDS-4313
Change-Id: Icf83911ac6f87fde1cb79d6f5059ae01ed434d7f
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2021-05-07 15:41:58 +03:00
parent 14067cca86
commit f913a65405
12 changed files with 429 additions and 69 deletions

View File

@@ -42,13 +42,22 @@ DirectionalDraggable {
return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos;
}
onPressed: {
if (targetNode == multiSelectionNode)
_generalHelper.restartMultiSelection();
}
onDragged: {
targetNode.position = localPos(sceneRelativeDistance);
if (targetNode == multiSelectionNode)
_generalHelper.moveMultiSelection(false);
positionMove();
}
onReleased: {
targetNode.position = localPos(sceneRelativeDistance);
if (targetNode == multiSelectionNode)
_generalHelper.moveMultiSelection(true);
positionCommit();
}
}

View File

@@ -49,7 +49,7 @@ Item {
property int selectionMode: EditView3D.SelectionMode.Item
property int transformMode: EditView3D.TransformMode.Move
property Node selectedNode: null // This is non-null only in single selection case
property Node selectedNode: null // This is multiSelectionNode in multi-selection case
property var selectedNodes: [] // All selected nodes
property var lightIconGizmos: []
@@ -62,8 +62,8 @@ Item {
property real fps: 0
signal selectionChanged(var selectedNodes)
signal commitObjectProperty(var object, var propName)
signal changeObjectProperty(var object, var propName)
signal commitObjectProperty(var objects, var propNames)
signal changeObjectProperty(var objects, var propNames)
signal notifyActiveSceneChange()
onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
@@ -268,10 +268,14 @@ Item {
selectionBoxes[i].targetNode = null;
selectedNodes = objects;
if (objects.length === 0 || objects.length > 1)
if (objects.length === 0) {
selectedNode = null;
else
} else if (objects.length > 1) {
selectedNode = multiSelectionNode;
_generalHelper.setMultiSelectionTargets(multiSelectionNode, objects);
} else {
selectedNode = objects[0];
}
}
function handleObjectClicked(object, multi)
@@ -513,6 +517,11 @@ Item {
view3D: overlayView
}
Node {
id: multiSelectionNode
objectName: "multiSelectionNode"
}
MoveGizmo {
id: moveGizmo
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
@@ -522,9 +531,20 @@ Item {
visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Move
view3D: overlayView
dragHelper: gizmoDragHelper
property var propertyNames: ["position"]
onPositionCommit: viewRoot.commitObjectProperty(viewRoot.selectedNode, "position")
onPositionMove: viewRoot.changeObjectProperty(viewRoot.selectedNode, "position")
onPositionCommit: {
if (targetNode == multiSelectionNode)
viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames);
else
viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames);
}
onPositionMove: {
if (targetNode == multiSelectionNode)
viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames);
else
viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames);
}
}
ScaleGizmo {
@@ -535,9 +555,21 @@ Item {
visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Scale
view3D: overlayView
dragHelper: gizmoDragHelper
property var propertyNames: ["scale"]
property var propertyNamesMulti: ["position", "scale"]
onScaleCommit: viewRoot.commitObjectProperty(viewRoot.selectedNode, "scale")
onScaleChange: viewRoot.changeObjectProperty(viewRoot.selectedNode, "scale")
onScaleCommit: {
if (targetNode == multiSelectionNode)
viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti);
else
viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames);
}
onScaleChange: {
if (targetNode == multiSelectionNode)
viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti);
else
viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames);
}
}
RotateGizmo {
@@ -549,19 +581,31 @@ Item {
visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Rotate
view3D: overlayView
dragHelper: gizmoDragHelper
property var propertyNames: ["eulerRotation"]
property var propertyNamesMulti: ["position", "eulerRotation"]
onRotateCommit: viewRoot.commitObjectProperty(viewRoot.selectedNode, "eulerRotation")
onRotateChange: viewRoot.changeObjectProperty(viewRoot.selectedNode, "eulerRotation")
onRotateCommit: {
if (targetNode == multiSelectionNode)
viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti);
else
viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames);
}
onRotateChange: {
if (targetNode == multiSelectionNode)
viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti);
else
viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames);
}
}
LightGizmo {
id: lightGizmo
targetNode: viewRoot.selectedNode
targetNode: viewRoot.selectedNode != multiSelectionNode ? viewRoot.selectedNode : null
view3D: overlayView
dragHelper: gizmoDragHelper
onPropertyValueCommit: viewRoot.commitObjectProperty(targetNode, propName)
onPropertyValueChange: viewRoot.changeObjectProperty(targetNode, propName)
onPropertyValueCommit: viewRoot.commitObjectProperty([targetNode], [propName])
onPropertyValueChange: viewRoot.changeObjectProperty([targetNode], [propName])
}
AutoScaleHelper {
@@ -578,7 +622,7 @@ Item {
Line3D {
id: pivotLine
visible: viewRoot.selectedNode
visible: viewRoot.selectedNode && viewRoot.selectedNode != multiSelectionNode
name: "3D Edit View Pivot Line"
color: "#ddd600"

View File

@@ -43,13 +43,22 @@ PlanarDraggable {
return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos;
}
onPressed: {
if (targetNode == multiSelectionNode)
_generalHelper.restartMultiSelection();
}
onDragged: {
targetNode.position = localPos(sceneRelativeDistance);
if (targetNode == multiSelectionNode)
_generalHelper.moveMultiSelection(false);
positionMove();
}
onReleased: {
targetNode.position = localPos(sceneRelativeDistance);
if (targetNode == multiSelectionNode)
_generalHelper.moveMultiSelection(true);
positionCommit();
}
}

View File

@@ -41,18 +41,24 @@ PlanarDraggable {
property vector3d _startScale
onPressed: {
if (targetNode == multiSelectionNode)
_generalHelper.restartMultiSelection();
_startScale = targetNode.scale;
}
onDragged: {
targetNode.scale = mouseArea.getNewScale(_startScale, relativeDistance.times(scale.x),
axisX, axisY);
if (targetNode == multiSelectionNode)
_generalHelper.scaleMultiSelection(false);
scaleChange();
}
onReleased: {
targetNode.scale = mouseArea.getNewScale(_startScale, relativeDistance.times(scale.x),
axisX, axisY);
if (targetNode == multiSelectionNode)
_generalHelper.scaleMultiSelection(true);
scaleCommit();
}
}

View File

@@ -208,6 +208,9 @@ Node {
if (!rotateGizmo.targetNode)
return;
if (targetNode == multiSelectionNode)
_generalHelper.restartMultiSelection();
// Need to recreate vector as we need to adjust it and we can't do that on reference of
// scenePosition, which is read-only property
var scenePos = rotateGizmo.dragHelper.pivotScenePosition(rotateGizmo.targetNode);
@@ -237,6 +240,9 @@ Node {
rotateGizmo.targetNode, _startRotation, _pointerPosPressed,
Qt.vector3d(screenPos.x, screenPos.y, 0));
if (targetNode == multiSelectionNode)
_generalHelper.rotateMultiSelection(false);
rotateGizmo.rotateChange();
}
@@ -249,6 +255,9 @@ Node {
rotateGizmo.targetNode, _startRotation, _pointerPosPressed,
Qt.vector3d(screenPos.x, screenPos.y, 0));
if (targetNode == multiSelectionNode)
_generalHelper.rotateMultiSelection(true);
rotateGizmo.rotateCommit();
dragging = false;
}

View File

@@ -77,6 +77,9 @@ Model {
if (!targetNode)
return;
if (targetNode == multiSelectionNode)
_generalHelper.restartMultiSelection();
// Need to recreate vector as we need to adjust it and we can't do that on reference of
// scenePosition, which is read-only property
var scenePos = mouseAreaMain.pivotScenePosition(targetNode);
@@ -107,6 +110,10 @@ Model {
return;
applyLocalRotation(screenPos);
if (targetNode == multiSelectionNode)
_generalHelper.rotateMultiSelection(false);
currentMousePos = screenPos;
rotateChange();
}
@@ -117,6 +124,10 @@ Model {
return;
applyLocalRotation(screenPos);
if (targetNode == multiSelectionNode)
_generalHelper.rotateMultiSelection(true);
rotateCommit();
currentAngle = 0;
currentMousePos = screenPos;

View File

@@ -212,6 +212,9 @@ Node {
if (!scaleGizmo.targetNode)
return;
if (targetNode == multiSelectionNode)
_generalHelper.restartMultiSelection();
// Recreate vector so we don't follow the changes in targetNode.scale
_startScale = Qt.vector3d(scaleGizmo.targetNode.scale.x,
scaleGizmo.targetNode.scale.y,
@@ -222,6 +225,8 @@ Node {
if (!scaleGizmo.targetNode)
return;
scaleGizmo.targetNode.scale = localScale(screenPos);
if (targetNode == multiSelectionNode)
_generalHelper.scaleMultiSelection(false);
scaleGizmo.scaleChange();
}
onReleased: {
@@ -229,6 +234,8 @@ Node {
return;
scaleGizmo.targetNode.scale = localScale(screenPos);
if (targetNode == multiSelectionNode)
_generalHelper.scaleMultiSelection(true);
scaleGizmo.scaleCommit();
}
}

View File

@@ -50,18 +50,24 @@ DirectionalDraggable {
}
onPressed: {
if (targetNode == multiSelectionNode)
_generalHelper.restartMultiSelection();
_startScale = targetNode.scale;
}
onDragged: {
targetNode.scale = mouseArea.getNewScale(_startScale, Qt.vector2d(relativeDistance, 0),
axis, Qt.vector3d(0, 0, 0));
if (targetNode == multiSelectionNode)
_generalHelper.scaleMultiSelection(false);
scaleChange();
}
onReleased: {
targetNode.scale = mouseArea.getNewScale(_startScale, Qt.vector2d(relativeDistance, 0),
axis, Qt.vector3d(0, 0, 0));
if (targetNode == multiSelectionNode)
_generalHelper.scaleMultiSelection(true);
scaleCommit();
}
}

View File

@@ -347,6 +347,171 @@ double GeneralHelper::brightnessScaler() const
#endif
}
void GeneralHelper::setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode,
const QVariantList &selectedList)
{
// Filter selection to contain only topmost parent nodes in the selection
m_multiSelDataMap.clear();
m_multiSelNodes.clear();
for (auto &connection : qAsConst(m_multiSelectConnections))
disconnect(connection);
m_multiSelectConnections.clear();
m_multiSelectRootNode = multiSelectRootNode;
QSet<QQuick3DNode *> selNodes;
for (const auto &var : selectedList) {
QQuick3DNode *node = nullptr;
node = var.value<QQuick3DNode *>();
if (node)
selNodes.insert(node);
}
for (const auto selNode : qAsConst(selNodes)) {
bool found = false;
QQuick3DNode *parent = selNode->parentNode();
while (parent) {
if (selNodes.contains(parent)) {
found = true;
break;
}
parent = parent->parentNode();
}
if (!found) {
m_multiSelDataMap.insert(selNode, {});
m_multiSelNodes.append(QVariant::fromValue(selNode));
m_multiSelectConnections.append(connect(selNode, &QObject::destroyed, [this]() {
// If any multiselected node is destroyed, assume the entire selection is invalid.
// The new selection should be notified by creator immediately after anyway.
m_multiSelDataMap.clear();
m_multiSelNodes.clear();
for (auto &connection : qAsConst(m_multiSelectConnections))
disconnect(connection);
m_multiSelectConnections.clear();
}));
m_multiSelectConnections.append(connect(selNode, &QQuick3DNode::sceneTransformChanged,
[this]() {
// Reposition the multiselection root node if scene transform of any multiselected
// node changes outside of drag (i.e. changes originating from creator side)
if (!m_blockMultiSelectionNodePositioning) {
QVector3D newPos;
for (auto it = m_multiSelDataMap.begin(); it != m_multiSelDataMap.end(); ++it)
newPos += it.key()->scenePosition();
newPos /= m_multiSelDataMap.size();
m_multiSelectRootNode->setPosition(newPos);
}
}));
}
}
restartMultiSelection();
m_blockMultiSelectionNodePositioning = false;
}
void GeneralHelper::restartMultiSelection()
{
for (auto it = m_multiSelDataMap.begin(); it != m_multiSelDataMap.end(); ++it) {
it.value() = {it.key()->scenePosition(),
it.key()->scale(),
it.key()->rotation()};
}
m_multiSelNodeData = {};
if (!m_multiSelDataMap.isEmpty()) {
for (const auto &data : qAsConst(m_multiSelDataMap))
m_multiSelNodeData.startScenePos += data.startScenePos;
m_multiSelNodeData.startScenePos /= m_multiSelDataMap.size();
}
m_multiSelectRootNode->setPosition(m_multiSelNodeData.startScenePos);
m_multiSelectRootNode->setRotation({});
m_multiSelectRootNode->setScale({1.f, 1.f, 1.f});
m_blockMultiSelectionNodePositioning = true;
}
QVariantList GeneralHelper::multiSelectionTargets() const
{
return m_multiSelNodes;
}
void GeneralHelper::moveMultiSelection(bool commit)
{
// Move the multiselected nodes in global space by offset from multiselection start to scenePos
QVector3D globalOffset = m_multiSelectRootNode->scenePosition() - m_multiSelNodeData.startScenePos;
for (auto it = m_multiSelDataMap.constBegin(); it != m_multiSelDataMap.constEnd(); ++it) {
QVector3D newGlobalPos = it.value().startScenePos + globalOffset;
QMatrix4x4 m;
if (it.key()->parentNode())
m = it.key()->parentNode()->sceneTransform();
it.key()->setPosition(m.inverted() * newGlobalPos);
}
m_blockMultiSelectionNodePositioning = !commit;
}
void GeneralHelper::scaleMultiSelection(bool commit)
{
// Offset the multiselected nodes in global space according to scale factor and scale them by
// the same factor.
// TODO: The math below breaks down with negative scaling, so don't allow it for now
const QVector3D sceneScale = m_multiSelectRootNode->sceneScale();
QVector3D fixedSceneScale = sceneScale;
for (int i = 0; i < 3; ++i) {
if (sceneScale[i] < 0.f)
fixedSceneScale[i] = -sceneScale[i];
}
const QVector3D unitVector {1.f, 1.f, 1.f};
const QVector3D diffScale = (fixedSceneScale - unitVector);
for (auto it = m_multiSelDataMap.constBegin(); it != m_multiSelDataMap.constEnd(); ++it) {
const QVector3D newGlobalPos = m_multiSelNodeData.startScenePos
+ (it.value().startScenePos - m_multiSelNodeData.startScenePos) * sceneScale;
QMatrix4x4 parentMat;
if (it.key()->parentNode())
parentMat = it.key()->parentNode()->sceneTransform().inverted();
it.key()->setPosition(parentMat * newGlobalPos);
QMatrix4x4 mat;
mat.rotate(it.key()->sceneRotation());
auto scaleDim = [&](int dim) -> QVector3D {
QVector3D dimScale;
float diffScaleDim = diffScale[dim];
dimScale[dim] = diffScaleDim;
dimScale = (mat.inverted() * dimScale).normalized() * diffScaleDim;
for (int i = 0; i < 3; ++i)
dimScale[i] = qAbs(dimScale[i]);
if (fixedSceneScale[dim] < 1.0f)
dimScale = -dimScale;
return dimScale;
};
QVector3D finalScale = scaleDim(0) + scaleDim(1) + scaleDim(2) + unitVector;
it.key()->setScale(finalScale * it.value().startScale);
}
m_blockMultiSelectionNodePositioning = !commit;
}
void GeneralHelper::rotateMultiSelection(bool commit)
{
// Rotate entire selection around the multiselection node
const QQuaternion sceneRotation = m_multiSelectRootNode->sceneRotation();
QVector3D rotAxis;
float rotAngle = 0;
sceneRotation.getAxisAndAngle(&rotAxis, &rotAngle);
for (auto it = m_multiSelDataMap.constBegin(); it != m_multiSelDataMap.constEnd(); ++it) {
QVector3D globalOffset = it.value().startScenePos - m_multiSelNodeData.startScenePos;
QVector3D newGlobalPos = m_multiSelNodeData.startScenePos + sceneRotation * globalOffset;
QMatrix4x4 parentMat;
if (it.key()->parentNode())
parentMat = it.key()->parentNode()->sceneTransform().inverted();
it.key()->setPosition(parentMat * newGlobalPos);
it.key()->setRotation(it.value().startRot);
it.key()->rotate(rotAngle, rotAxis, QQuick3DNode::SceneSpace);
}
m_blockMultiSelectionNodePositioning = !commit;
}
bool GeneralHelper::isMacOS() const
{
#ifdef Q_OS_MACOS

View File

@@ -27,13 +27,14 @@
#ifdef QUICK3D_MODULE
#include <QtCore/qobject.h>
#include <QtCore/qtimer.h>
#include <QtCore/qhash.h>
#include <QtCore/qpointer.h>
#include <QtCore/qvariant.h>
#include <QtGui/qvector3d.h>
#include <QtGui/qmatrix4x4.h>
#include <QHash>
#include <QMatrix4x4>
#include <QObject>
#include <QPointer>
#include <QQuaternion>
#include <QTimer>
#include <QVariant>
#include <QVector3D>
QT_BEGIN_NAMESPACE
class QQuick3DCamera;
@@ -91,6 +92,14 @@ public:
Q_INVOKABLE double brightnessScaler() const;
Q_INVOKABLE void setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode,
const QVariantList &selectedList);
Q_INVOKABLE void restartMultiSelection();
Q_INVOKABLE QVariantList multiSelectionTargets() const;
Q_INVOKABLE void moveMultiSelection(bool commit);
Q_INVOKABLE void scaleMultiSelection(bool commit);
Q_INVOKABLE void rotateMultiSelection(bool commit);
bool isMacOS() const;
void addRotationBlocks(const QSet<QQuick3DNode *> &nodes);
@@ -116,6 +125,19 @@ private:
QHash<QString, QVariantMap> m_toolStatesPending;
QSet<QQuick3DNode *> m_gizmoTargets;
QSet<QQuick3DNode *> m_rotationBlockedNodes;
struct MultiSelData {
QVector3D startScenePos;
QVector3D startScale;
QQuaternion startRot;
};
QHash<QQuick3DNode *, MultiSelData> m_multiSelDataMap;
QVariantList m_multiSelNodes;
MultiSelData m_multiSelNodeData;
QQuick3DNode *m_multiSelectRootNode = nullptr;
QList<QMetaObject::Connection> m_multiSelectConnections;
bool m_blockMultiSelectionNodePositioning = false;
};
}

View File

@@ -152,6 +152,34 @@ static bool isQuick3DMode()
return mode3D;
}
static QObjectList toObjectList(const QVariant &variantList)
{
QObjectList objList;
if (!variantList.isNull()) {
const auto varList = variantList.value<QVariantList>();
for (const auto &var : varList) {
QObject *obj = var.value<QObject *>();
if (obj)
objList.append(obj);
}
}
return objList;
}
static QList<PropertyName> toPropertyNameList(const QVariant &variantList)
{
QList<PropertyName> propList;
if (!variantList.isNull()) {
const auto varList = variantList.value<QVariantList>();
for (const auto &var : varList) {
PropertyName prop = var.toByteArray();
if (!prop.isEmpty())
propList.append(prop);
}
}
return propList;
}
void Qt5InformationNodeInstanceServer::createAuxiliaryQuickView(const QUrl &url,
RenderViewData &viewData)
{
@@ -422,64 +450,108 @@ Qt5InformationNodeInstanceServer::propertyToPropertyValueTriples(
return result;
}
void Qt5InformationNodeInstanceServer::modifyVariantValue(
const QVariant &node,
const PropertyName &propertyName,
ValuesModifiedCommand::TransactionOption option)
void Qt5InformationNodeInstanceServer::modifyVariantValue(const QObjectList &objects,
const QList<PropertyName> &propNames, ValuesModifiedCommand::TransactionOption option)
{
PropertyName targetPropertyName;
struct PropNamePair {
PropertyName origPropName;
PropertyName targetPropName;
};
QList<PropNamePair> propNamePairs;
// Position is a special case, because the position can be 'position.x 'or simply 'x'.
// We prefer 'x'.
if (propertyName != "position")
targetPropertyName = propertyName;
for (const auto &propName : propNames) {
if (propName != "position")
propNamePairs.append({propName, propName});
else
propNamePairs.append({propName, PropertyName{}});
}
auto *obj = node.value<QObject *>();
if (!objects.isEmpty()) {
QVector<PropertyValueContainer> valueVector;
for (const auto listObj : objects) {
ServerNodeInstance instance = instanceForObject(listObj);
if (instance.isValid()) {
const qint32 instId = instance.instanceId();
if (option == ValuesModifiedCommand::TransactionOption::Start)
instance.setModifiedFlag(true);
else if (option == ValuesModifiedCommand::TransactionOption::End)
instance.setModifiedFlag(false);
for (const auto &propNamePair : propNamePairs) {
// We do have to split vector3d props into foobar.x, foobar.y, foobar.z
const QVector<InstancePropertyValueTriple> triple
= propertyToPropertyValueTriples(instance, propNamePair.targetPropName,
listObj->property(propNamePair.origPropName));
for (const auto &property : triple) {
const PropertyName propName = property.propertyName;
const QVariant propValue = property.propertyValue;
valueVector.append(PropertyValueContainer(instId, propName,
propValue, PropertyName()));
}
}
}
}
if (obj) {
ServerNodeInstance instance = instanceForObject(obj);
if (option == ValuesModifiedCommand::TransactionOption::Start)
instance.setModifiedFlag(true);
else if (option == ValuesModifiedCommand::TransactionOption::End)
instance.setModifiedFlag(false);
// We do have to split position into position.x, position.y, position.z
ValuesModifiedCommand command = createValuesModifiedCommand(propertyToPropertyValueTriples(
instance,
targetPropertyName,
obj->property(propertyName)));
command.transactionOption = option;
nodeInstanceClient()->valuesModified(command);
if (!valueVector.isEmpty()) {
ValuesModifiedCommand command(valueVector);
command.transactionOption = option;
nodeInstanceClient()->valuesModified(command);
}
}
}
void Qt5InformationNodeInstanceServer::handleObjectPropertyCommit(const QVariant &object,
const QVariant &propName)
void Qt5InformationNodeInstanceServer::handleObjectPropertyCommit(const QVariant &objects,
const QVariant &propNames)
{
modifyVariantValue(object, propName.toByteArray(),
modifyVariantValue(toObjectList(objects), toPropertyNameList(propNames),
ValuesModifiedCommand::TransactionOption::End);
m_changedNode = {};
m_changedProperty = {};
m_changedNodes.clear();
m_changedProperties.clear();
m_propertyChangeTimer.stop();
}
void Qt5InformationNodeInstanceServer::handleObjectPropertyChange(const QVariant &object,
const QVariant &propName)
void Qt5InformationNodeInstanceServer::handleObjectPropertyChange(const QVariant &objects,
const QVariant &propNames)
{
PropertyName propertyName(propName.toByteArray());
if (m_changedProperty != propertyName || m_changedNode != object) {
if (!m_changedNode.isNull())
handleObjectPropertyCommit(m_changedNode, m_changedProperty);
modifyVariantValue(object, propertyName,
ValuesModifiedCommand::TransactionOption::Start);
QObjectList objList = toObjectList(objects);
QList<PropertyName> propList = toPropertyNameList(propNames);
bool nodeChanged = true;
if (objList.size() == m_changedNodes.size()) {
nodeChanged = false;
for (int i = 0; i < objList.size(); ++i) {
if (objList[i] != m_changedNodes[i]) {
nodeChanged = true;
break;
}
}
}
if (!nodeChanged && propList.size() == m_changedProperties.size()) {
for (int i = 0; i < propList.size(); ++i) {
if (propList[i] != m_changedProperties[i]) {
nodeChanged = true;
break;
}
}
}
if (nodeChanged) {
if (!m_changedNodes.isEmpty()) {
// Nodes/properties changed, commit currently pending changes
modifyVariantValue(m_changedNodes, m_changedProperties,
ValuesModifiedCommand::TransactionOption::End);
m_changedNodes.clear();
m_changedProperties.clear();
m_propertyChangeTimer.stop();
}
modifyVariantValue(objList, propList, ValuesModifiedCommand::TransactionOption::Start);
} else if (!m_propertyChangeTimer.isActive()) {
m_propertyChangeTimer.start();
}
m_changedNode = object;
m_changedProperty = propertyName;
m_changedNodes = objList;
m_changedProperties = propList;
}
void Qt5InformationNodeInstanceServer::handleActiveSceneChange()
@@ -1125,7 +1197,7 @@ void Qt5InformationNodeInstanceServer::initializeAuxiliaryViews()
void Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout()
{
modifyVariantValue(m_changedNode, m_changedProperty,
modifyVariantValue(m_changedNodes, m_changedProperties,
ValuesModifiedCommand::TransactionOption::None);
}

View File

@@ -77,8 +77,8 @@ public:
private slots:
void handleSelectionChanged(const QVariant &objs);
void handleObjectPropertyCommit(const QVariant &object, const QVariant &propName);
void handleObjectPropertyChange(const QVariant &object, const QVariant &propName);
void handleObjectPropertyCommit(const QVariant &objects, const QVariant &propNames);
void handleObjectPropertyChange(const QVariant &objects, const QVariant &propNames);
void handleActiveSceneChange();
void handleToolStateChanged(const QString &sceneId, const QString &tool,
const QVariant &toolState);
@@ -115,8 +115,8 @@ private:
const ServerNodeInstance &instance,
const PropertyName &propertyName,
const QVariant &variant);
void modifyVariantValue(const QVariant &node,
const PropertyName &propertyName,
void modifyVariantValue(const QObjectList &objects,
const QList<PropertyName> &propNames,
ValuesModifiedCommand::TransactionOption option);
void updateView3DRect(QObject *view3D);
void updateActiveSceneToEditView3D();
@@ -159,8 +159,8 @@ private:
QTimer m_render3DEditViewTimer;
QTimer m_renderModelNodeImageViewTimer;
QTimer m_inputEventTimer;
QVariant m_changedNode;
PropertyName m_changedProperty;
QObjectList m_changedNodes;
QList<PropertyName> m_changedProperties;
ChangeSelectionCommand m_lastSelectionChangeCommand;
QList<InputEventCommand> m_pendingInputEventCommands;
QObject *m_3dHelper = nullptr;