QmlDesigner: Use transactions in 3D Edit View

We send a ValuesModifiedCommand 'immediately' when the item is moved.
This means the 2D form editor and property editor update
while moving nodes in the 3D edit view.
The updates are 'compressed' by 100ms. This is to avoid performance issues.
The timer could be reduced to 50ms or even 10ms, but 100ms feel
acceptable at least for me.

The code was a bit refactored.
Qt5InformationNodeInstanceServer::modifyVariantValue() can be used later
to update other Vector3D based properties like rotation and scale.

There is one issue left. MouseArea3D emits 'onReleased' only after the
mouse is moved again. This delays the 'commit' of the transaction which
is annoying and can triggers bugs.

Change-Id: I834a1e2658278ff8dd39678f39e51735dee91b65
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Thomas Hartmann
2019-10-25 16:37:33 +02:00
parent 523352f52d
commit 6b17cd91a6
5 changed files with 69 additions and 7 deletions

View File

@@ -43,6 +43,7 @@ Model {
property var _targetStartPos property var _targetStartPos
signal positionCommit() signal positionCommit()
signal positionMove()
materials: DefaultMaterial { materials: DefaultMaterial {
id: material id: material
@@ -85,6 +86,7 @@ Model {
return; return;
targetNode.position = posInParent(mouseArea, pointerPosition); targetNode.position = posInParent(mouseArea, pointerPosition);
arrow.positionMove();
} }
function handleReleased(mouseArea, pointerPosition) function handleReleased(mouseArea, pointerPosition)

View File

@@ -46,6 +46,7 @@ Window {
signal objectClicked(var object) signal objectClicked(var object)
signal commitObjectPosition(var object) signal commitObjectPosition(var object)
signal moveObjectPosition(var object)
function selectObject(object) { function selectObject(object) {
selectedNode = object; selectedNode = object;
@@ -70,6 +71,7 @@ Window {
targetNode: viewWindow.selectedNode targetNode: viewWindow.selectedNode
position: viewWindow.selectedNode ? viewWindow.selectedNode.scenePosition position: viewWindow.selectedNode ? viewWindow.selectedNode.scenePosition
: Qt.vector3d(0, 0, 0) : Qt.vector3d(0, 0, 0)
rotation: globalControl.checked || !viewWindow.selectedNode rotation: globalControl.checked || !viewWindow.selectedNode
? Qt.vector3d(0, 0, 0) ? Qt.vector3d(0, 0, 0)
: viewWindow.selectedNode.sceneRotation : viewWindow.selectedNode.sceneRotation
@@ -78,6 +80,7 @@ Window {
view3D: overlayView view3D: overlayView
onPositionCommit: viewWindow.commitObjectPosition(selectedNode) onPositionCommit: viewWindow.commitObjectPosition(selectedNode)
onPositionMove: viewWindow.moveObjectPosition(selectedNode)
} }
AutoScaleHelper { AutoScaleHelper {

View File

@@ -41,6 +41,7 @@ Node {
property alias arrowZ: arrowZ property alias arrowZ: arrowZ
signal positionCommit() signal positionCommit()
signal positionMove()
Arrow { Arrow {
id: arrowX id: arrowX
@@ -52,6 +53,7 @@ Node {
view3D: arrows.view3D view3D: arrows.view3D
onPositionCommit: arrows.positionCommit() onPositionCommit: arrows.positionCommit()
onPositionMove: arrows.positionMove()
} }
Arrow { Arrow {
@@ -64,6 +66,7 @@ Node {
view3D: arrows.view3D view3D: arrows.view3D
onPositionCommit: arrows.positionCommit() onPositionCommit: arrows.positionCommit()
onPositionMove: arrows.positionMove()
} }
Arrow { Arrow {
@@ -76,5 +79,6 @@ Node {
view3D: arrows.view3D view3D: arrows.view3D
onPositionCommit: arrows.positionCommit() onPositionCommit: arrows.positionCommit()
onPositionMove: arrows.positionMove()
} }
} }

View File

@@ -98,6 +98,10 @@ QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine)
QObject::connect(window, SIGNAL(objectClicked(QVariant)), this, SLOT(objectClicked(QVariant))); QObject::connect(window, SIGNAL(objectClicked(QVariant)), this, SLOT(objectClicked(QVariant)));
QObject::connect(window, SIGNAL(commitObjectPosition(QVariant)), QObject::connect(window, SIGNAL(commitObjectPosition(QVariant)),
this, SLOT(handleObjectPositionCommit(QVariant))); this, SLOT(handleObjectPositionCommit(QVariant)));
QObject::connect(window, SIGNAL(moveObjectPosition(QVariant)),
this, SLOT(handleObjectPositionMove(QVariant)));
QObject::connect(&m_moveTimer, &QTimer::timeout,
this, &Qt5InformationNodeInstanceServer::handleObjectPositionMoveTimeout);
//For macOS we have to use the 4.1 core profile //For macOS we have to use the 4.1 core profile
QSurfaceFormat surfaceFormat = window->requestedFormat(); QSurfaceFormat surfaceFormat = window->requestedFormat();
@@ -149,21 +153,54 @@ Qt5InformationNodeInstanceServer::vectorToPropertyValue(
return result; return result;
} }
void Qt5InformationNodeInstanceServer::modifyVariantValue(
const QVariant &node,
const PropertyName &propertyName,
ValuesModifiedCommand::TransactionOption option)
{
PropertyName targetPopertyName;
// Position is a special case, because the position can be 'position.x 'or simply 'x'.
// We prefer 'x'.
if (propertyName != "position")
targetPopertyName = propertyName;
auto *obj = node.value<QObject *>();
if (obj) {
// We do have to split position into position.x, position.y, position.z
ValuesModifiedCommand command = createValuesModifiedCommand(vectorToPropertyValue(
instanceForObject(obj),
targetPopertyName,
obj->property(propertyName)));
command.transactionOption = option;
nodeInstanceClient()->valuesModified(command);
}
}
void Qt5InformationNodeInstanceServer::handleObjectPositionCommit(const QVariant &object) void Qt5InformationNodeInstanceServer::handleObjectPositionCommit(const QVariant &object)
{ {
auto *obj = object.value<QObject *>(); modifyVariantValue(object, "position", ValuesModifiedCommand::TransactionOption::End);
if (obj) { m_movedNode = {};
/* We do have to split position into position.x, position.y, position.z */ m_moveTimer.stop();
nodeInstanceClient()->valuesModified(createValuesModifiedCommand(vectorToPropertyValue( }
instanceForObject(obj),
"position", void Qt5InformationNodeInstanceServer::handleObjectPositionMove(const QVariant &object)
obj->property("position")))); {
if (m_movedNode.isNull()) {
modifyVariantValue(object, "position", ValuesModifiedCommand::TransactionOption::Start);
} else {
if (!m_moveTimer.isActive())
m_moveTimer.start();
} }
m_movedNode = object;
} }
Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) :
Qt5NodeInstanceServer(nodeInstanceClient) Qt5NodeInstanceServer(nodeInstanceClient)
{ {
m_moveTimer.setInterval(100);
} }
void Qt5InformationNodeInstanceServer::sendTokenBack() void Qt5InformationNodeInstanceServer::sendTokenBack()
@@ -236,6 +273,11 @@ void Qt5InformationNodeInstanceServer::modifyProperties(
nodeInstanceClient()->valuesModified(createValuesModifiedCommand(properties)); nodeInstanceClient()->valuesModified(createValuesModifiedCommand(properties));
} }
void Qt5InformationNodeInstanceServer::handleObjectPositionMoveTimeout()
{
modifyVariantValue(m_movedNode, "position", ValuesModifiedCommand::TransactionOption::None);
}
QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport( QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport(
const QList<ServerNodeInstance> &instanceList) const const QList<ServerNodeInstance> &instanceList) const
{ {

View File

@@ -27,6 +27,10 @@
#include "qt5nodeinstanceserver.h" #include "qt5nodeinstanceserver.h"
#include "tokencommand.h" #include "tokencommand.h"
#include "valueschangedcommand.h"
#include <QTimer>
#include <QVariant>
namespace QmlDesigner { namespace QmlDesigner {
@@ -47,6 +51,7 @@ public:
private slots: private slots:
void objectClicked(const QVariant &object); void objectClicked(const QVariant &object);
void handleObjectPositionCommit(const QVariant &object); void handleObjectPositionCommit(const QVariant &object);
void handleObjectPositionMove(const QVariant &object);
protected: protected:
void collectItemChangesAndSendChangeCommands() override; void collectItemChangesAndSendChangeCommands() override;
@@ -58,17 +63,23 @@ protected:
void modifyProperties(const QVector<InstancePropertyValueTriple> &properties); void modifyProperties(const QVector<InstancePropertyValueTriple> &properties);
private: private:
void handleObjectPositionMoveTimeout();
QObject *createEditView3D(QQmlEngine *engine); QObject *createEditView3D(QQmlEngine *engine);
void setup3DEditView(const QList<ServerNodeInstance> &instanceList); void setup3DEditView(const QList<ServerNodeInstance> &instanceList);
QObject *findRootNodeOf3DViewport(const QList<ServerNodeInstance> &instanceList) const; QObject *findRootNodeOf3DViewport(const QList<ServerNodeInstance> &instanceList) const;
QVector<InstancePropertyValueTriple> vectorToPropertyValue(const ServerNodeInstance &instance, QVector<InstancePropertyValueTriple> vectorToPropertyValue(const ServerNodeInstance &instance,
const PropertyName &propertyName, const PropertyName &propertyName,
const QVariant &variant); const QVariant &variant);
void modifyVariantValue(const QVariant &node,
const PropertyName &propertyName,
ValuesModifiedCommand::TransactionOption option);
QObject *m_editView3D = nullptr; QObject *m_editView3D = nullptr;
QSet<ServerNodeInstance> m_parentChangedSet; QSet<ServerNodeInstance> m_parentChangedSet;
QList<ServerNodeInstance> m_completedComponentList; QList<ServerNodeInstance> m_completedComponentList;
QList<TokenCommand> m_tokenList; QList<TokenCommand> m_tokenList;
QTimer m_moveTimer;
QVariant m_movedNode;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner