forked from qt-creator/qt-creator
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:
@@ -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)
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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",
|
|
||||||
obj->property("position"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Qt5InformationNodeInstanceServer::handleObjectPositionMove(const QVariant &object)
|
||||||
|
{
|
||||||
|
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
|
||||||
{
|
{
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user