forked from qt-creator/qt-creator
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:
@@ -9,6 +9,7 @@
|
|||||||
#include "edit3dvisibilitytogglesmenu.h"
|
#include "edit3dvisibilitytogglesmenu.h"
|
||||||
#include "metainfo.h"
|
#include "metainfo.h"
|
||||||
#include "modelnodeoperations.h"
|
#include "modelnodeoperations.h"
|
||||||
|
#include "nodeabstractproperty.h"
|
||||||
#include "qmldesignerconstants.h"
|
#include "qmldesignerconstants.h"
|
||||||
#include "qmldesignerplugin.h"
|
#include "qmldesignerplugin.h"
|
||||||
#include "qmlvisualnode.h"
|
#include "qmlvisualnode.h"
|
||||||
@@ -217,6 +218,19 @@ void Edit3DWidget::createContextMenu()
|
|||||||
});
|
});
|
||||||
|
|
||||||
m_contextMenu->addSeparator();
|
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
|
bool Edit3DWidget::isPasteAvailable() const
|
||||||
@@ -372,19 +386,21 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
|
|||||||
|
|
||||||
const bool isValid = modelNode.isValid();
|
const bool isValid = modelNode.isValid();
|
||||||
const bool isModel = modelNode.metaInfo().isQtQuick3DModel();
|
const bool isModel = modelNode.metaInfo().isQtQuick3DModel();
|
||||||
const bool isNotRoot = isValid && !modelNode.isRootNode();
|
|
||||||
const bool isCamera = isValid && modelNode.metaInfo().isQtQuick3DCamera();
|
const bool isCamera = isValid && modelNode.metaInfo().isQtQuick3DCamera();
|
||||||
const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent();
|
const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent();
|
||||||
|
const bool anyNodeSelected = view()->hasSelectedModelNodes();
|
||||||
|
const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected();
|
||||||
|
|
||||||
m_editComponentAction->setEnabled(isSingleComponent);
|
m_editComponentAction->setEnabled(isSingleComponent);
|
||||||
m_editMaterialAction->setEnabled(isModel);
|
m_editMaterialAction->setEnabled(isModel);
|
||||||
m_duplicateAction->setEnabled(isNotRoot);
|
m_duplicateAction->setEnabled(selectionExcludingRoot);
|
||||||
m_copyAction->setEnabled(isNotRoot);
|
m_copyAction->setEnabled(selectionExcludingRoot);
|
||||||
m_pasteAction->setEnabled(isPasteAvailable());
|
m_pasteAction->setEnabled(isPasteAvailable());
|
||||||
m_deleteAction->setEnabled(isNotRoot);
|
m_deleteAction->setEnabled(selectionExcludingRoot);
|
||||||
m_fitSelectedAction->setEnabled(isNotRoot);
|
m_fitSelectedAction->setEnabled(anyNodeSelected);
|
||||||
m_alignCameraAction->setEnabled(isCamera);
|
m_alignCameraAction->setEnabled(isCamera);
|
||||||
m_alignViewAction->setEnabled(isCamera);
|
m_alignViewAction->setEnabled(isCamera);
|
||||||
|
m_selectParentAction->setEnabled(selectionExcludingRoot);
|
||||||
|
|
||||||
m_contextMenu->popup(mapToGlobal(pos));
|
m_contextMenu->popup(mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ private:
|
|||||||
QPointer<QAction> m_fitSelectedAction;
|
QPointer<QAction> m_fitSelectedAction;
|
||||||
QPointer<QAction> m_alignCameraAction;
|
QPointer<QAction> m_alignCameraAction;
|
||||||
QPointer<QAction> m_alignViewAction;
|
QPointer<QAction> m_alignViewAction;
|
||||||
|
QPointer<QAction> m_selectParentAction;
|
||||||
QPointer<QMenu> m_createSubMenu;
|
QPointer<QMenu> m_createSubMenu;
|
||||||
ModelNode m_contextMenuTarget;
|
ModelNode m_contextMenuTarget;
|
||||||
QVector3D m_contextMenuPos3d;
|
QVector3D m_contextMenuPos3d;
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ public:
|
|||||||
void setLocked(bool value);
|
void setLocked(bool value);
|
||||||
|
|
||||||
static bool isThisOrAncestorLocked(const ModelNode &node);
|
static bool isThisOrAncestorLocked(const ModelNode &node);
|
||||||
|
static ModelNode lowestCommonAncestor(const QList<ModelNode> &nodes);
|
||||||
|
|
||||||
qint32 internalId() const;
|
qint32 internalId() const;
|
||||||
|
|
||||||
|
|||||||
@@ -1313,6 +1313,96 @@ bool ModelNode::isThisOrAncestorLocked(const ModelNode &node)
|
|||||||
return isThisOrAncestorLocked(node.parentProperty().parentModelNode());
|
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)
|
void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList)
|
||||||
{
|
{
|
||||||
model()->d->setScriptFunctions(m_internalNode, scriptFunctionList);
|
model()->d->setScriptFunctions(m_internalNode, scriptFunctionList);
|
||||||
|
|||||||
Reference in New Issue
Block a user