QmlDesigner: Add "Select Parent" to 3d Context Menu

"Select Parent" action is added to the context menu of the
Edit3DWidget.

Task-number: QDS-8199
Change-Id: Iff6ce50eba966bea60f1b09f497632c3ff290c27
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2022-11-08 15:45:05 +02:00
parent a43f5db582
commit 63d30351a6
4 changed files with 113 additions and 5 deletions

View File

@@ -9,6 +9,7 @@
#include "edit3dvisibilitytogglesmenu.h"
#include "metainfo.h"
#include "modelnodeoperations.h"
#include "nodeabstractproperty.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include "qmlvisualnode.h"
@@ -217,6 +218,19 @@ void Edit3DWidget::createContextMenu()
});
m_contextMenu->addSeparator();
m_selectParentAction = m_contextMenu->addAction(tr("Select Parent"), [&] {
ModelNode parentNode = ModelNode::lowestCommonAncestor(view()->selectedModelNodes());
if (!parentNode.isValid())
return;
if (!parentNode.isRootNode() && view()->isSelectedModelNode(parentNode))
parentNode = parentNode.parentProperty().parentModelNode();
view()->setSelectedModelNode(parentNode);
});
m_contextMenu->addSeparator();
}
bool Edit3DWidget::isPasteAvailable() const
@@ -372,19 +386,21 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
const bool isValid = modelNode.isValid();
const bool isModel = modelNode.metaInfo().isQtQuick3DModel();
const bool isNotRoot = isValid && !modelNode.isRootNode();
const bool isCamera = isValid && modelNode.metaInfo().isQtQuick3DCamera();
const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent();
const bool anyNodeSelected = view()->hasSelectedModelNodes();
const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected();
m_editComponentAction->setEnabled(isSingleComponent);
m_editMaterialAction->setEnabled(isModel);
m_duplicateAction->setEnabled(isNotRoot);
m_copyAction->setEnabled(isNotRoot);
m_duplicateAction->setEnabled(selectionExcludingRoot);
m_copyAction->setEnabled(selectionExcludingRoot);
m_pasteAction->setEnabled(isPasteAvailable());
m_deleteAction->setEnabled(isNotRoot);
m_fitSelectedAction->setEnabled(isNotRoot);
m_deleteAction->setEnabled(selectionExcludingRoot);
m_fitSelectedAction->setEnabled(anyNodeSelected);
m_alignCameraAction->setEnabled(isCamera);
m_alignViewAction->setEnabled(isCamera);
m_selectParentAction->setEnabled(selectionExcludingRoot);
m_contextMenu->popup(mapToGlobal(pos));
}

View File

@@ -71,6 +71,7 @@ private:
QPointer<QAction> m_fitSelectedAction;
QPointer<QAction> m_alignCameraAction;
QPointer<QAction> m_alignViewAction;
QPointer<QAction> m_selectParentAction;
QPointer<QMenu> m_createSubMenu;
ModelNode m_contextMenuTarget;
QVector3D m_contextMenuPos3d;

View File

@@ -222,6 +222,7 @@ public:
void setLocked(bool value);
static bool isThisOrAncestorLocked(const ModelNode &node);
static ModelNode lowestCommonAncestor(const QList<ModelNode> &nodes);
qint32 internalId() const;

View File

@@ -1313,6 +1313,96 @@ bool ModelNode::isThisOrAncestorLocked(const ModelNode &node)
return isThisOrAncestorLocked(node.parentProperty().parentModelNode());
}
/*!
* \brief The lowest common ancestor node for node1 and node2. If one of the nodes (Node A) is
* the ancestor of the other node, the return value is Node A and not the parent of Node A.
* \param node1 First node
* \param node2 Second node
* \param depthOfLCA Depth of the return value
* \param depthOfNode1 Depth of node1. Use this parameter for optimization
* \param depthOfNode2 Depth of node2. Use this parameter for optimization
*/
static ModelNode lowestCommonAncestor(const ModelNode &node1,
const ModelNode &node2,
int &depthOfLCA,
const int &depthOfNode1 = -1,
const int &depthOfNode2 = -1)
{
Q_ASSERT(node1.isValid() && node2.isValid());
auto depthOfNode = [] (const ModelNode &node) -> int {
int depth = 0;
ModelNode parentNode = node;
while (!parentNode.isRootNode()) {
depth++;
parentNode = parentNode.parentProperty().parentModelNode();
}
return depth;
};
if (node1 == node2) {
depthOfLCA = (depthOfNode1 < 0)
? ((depthOfNode2 < 0) ? depthOfNode(node1) : depthOfNode2)
: depthOfNode1;
return node1;
}
if (node1.isRootNode()) {
depthOfLCA = 0;
return node1;
}
if (node2.isRootNode()) {
depthOfLCA = 0;
return node2;
}
ModelNode nodeLower = node1;
ModelNode nodeHigher = node2;
int depthLower = (depthOfNode1 < 0) ? depthOfNode(nodeLower) : depthOfNode1;
int depthHigher = (depthOfNode2 < 0) ? depthOfNode(nodeHigher) :depthOfNode2;
if (depthLower > depthHigher) {
std::swap(depthLower, depthHigher);
std::swap(nodeLower, nodeHigher);
}
int depthDiff = depthHigher - depthLower;
while (depthDiff--)
nodeHigher = nodeHigher.parentProperty().parentModelNode();
while (nodeLower != nodeHigher) {
nodeLower = nodeLower.parentProperty().parentModelNode();
nodeHigher = nodeHigher.parentProperty().parentModelNode();
--depthLower;
}
depthOfLCA = depthLower;
return nodeLower;
}
/*!
* \brief The lowest common node containing all nodes. If one of the nodes (Node A) is
* the ancestor of the other nodes, the return value is Node A and not the parent of Node A.
*/
ModelNode ModelNode::lowestCommonAncestor(const QList<ModelNode> &nodes)
{
if (nodes.isEmpty())
return {};
ModelNode accumulatedNode = nodes.first();
int accumulatedNodeDepth = -1;
Utils::span<const ModelNode> nodesExceptFirst(nodes.constBegin() + 1, nodes.constEnd());
for (const ModelNode &node : nodesExceptFirst) {
accumulatedNode = QmlDesigner::lowestCommonAncestor(accumulatedNode,
node,
accumulatedNodeDepth,
accumulatedNodeDepth);
}
return accumulatedNode;
}
void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList)
{
model()->d->setScriptFunctions(m_internalNode, scriptFunctionList);