QmlDesigner: Create a context menu for the 3D Editor

For now only 1 action is implemented (edit material), more actions are
coming next.

Task-number: QDS-7414
Task-number: QDS-7398
Change-Id: Id8e36c23d9a4d35ee94d55d3d6b15df78241a05d
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Mahmoud Badri
2022-08-19 13:04:26 +03:00
parent 86d1526564
commit fc4ebb0cab
9 changed files with 111 additions and 35 deletions

View File

@@ -291,6 +291,10 @@ void Qt5InformationNodeInstanceServer::handleInputEvents()
// data stored internally in QMutableEventPoint to potentially be updated by system // data stored internally in QMutableEventPoint to potentially be updated by system
// before the event is delivered. // before the event is delivered.
QGuiApplication::sendEvent(m_editView3DData.window, me); QGuiApplication::sendEvent(m_editView3DData.window, me);
// Context menu requested
if (command.button() == Qt::RightButton && command.modifiers() == Qt::NoModifier)
getModelAtPos(command.pos());
} }
} }
@@ -405,6 +409,29 @@ void Qt5InformationNodeInstanceServer::removeRotationBlocks(const QVector<qint32
#endif #endif
} }
void Qt5InformationNodeInstanceServer::getModelAtPos(const QPointF &pos)
{
#ifdef QUICK3D_MODULE
// pick a Quick3DModel at view position
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
if (!helper)
return;
QQmlProperty editViewProp(m_editView3DData.rootItem, "editView", context());
QObject *obj = qvariant_cast<QObject *>(editViewProp.read());
QQuick3DViewport *editView = qobject_cast<QQuick3DViewport *>(obj);
QQuick3DModel *hitModel = helper->pickViewAt(editView, pos.x(), pos.y()).objectHit();
// filter out picks of models created dynamically or inside components
QQuick3DModel *resolvedPick = qobject_cast<QQuick3DModel *>(helper->resolvePick(hitModel));
QVariant instance = resolvedPick ? instanceForObject(resolvedPick).instanceId() : -1;
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::ModelAtPos, instance});
return;
#endif
}
void Qt5InformationNodeInstanceServer::createEditView3D() void Qt5InformationNodeInstanceServer::createEditView3D()
{ {
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
@@ -2399,26 +2426,7 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
#endif #endif
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
case View3DActionCommand::GetModelAtPos: { case View3DActionCommand::GetModelAtPos: {
// pick a Quick3DModel at view position getModelAtPos(command.value().toPointF());
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
if (!helper)
return;
QQmlProperty editViewProp(m_editView3DData.rootItem, "editView", context());
QObject *obj = qvariant_cast<QObject *>(editViewProp.read());
QQuick3DViewport *editView = qobject_cast<QQuick3DViewport *>(obj);
QPointF pos = command.value().toPointF();
QQuick3DModel *hitModel = helper->pickViewAt(editView, pos.x(), pos.y()).objectHit();
// filter out picks of models created dynamically or inside components
QQuick3DModel *resolvedPick = qobject_cast<QQuick3DModel *>(helper->resolvePick(hitModel));
if (resolvedPick) {
ServerNodeInstance instance = instanceForObject(resolvedPick);
nodeInstanceClient()->handlePuppetToCreatorCommand(
{PuppetToCreatorCommand::ModelAtPos, QVariant(instance.instanceId())});
}
return; return;
} }
#endif #endif

View File

@@ -148,6 +148,7 @@ private:
void updateMaterialPreviewData(const QVector<PropertyValueContainer> &valueChanges); void updateMaterialPreviewData(const QVector<PropertyValueContainer> &valueChanges);
void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges); void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges);
void removeRotationBlocks(const QVector<qint32> &instanceIds); void removeRotationBlocks(const QVector<qint32> &instanceIds);
void getModelAtPos(const QPointF &pos);
void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData); void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData);
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE

View File

@@ -796,7 +796,11 @@ void addNewSignalHandler(const SelectionContext &selectionState)
// Open a model's material in the material editor // Open a model's material in the material editor
void editMaterial(const SelectionContext &selectionContext) void editMaterial(const SelectionContext &selectionContext)
{ {
ModelNode modelNode = selectionContext.currentSingleSelectedNode(); ModelNode modelNode = selectionContext.targetNode();
if (!modelNode.isValid())
modelNode = selectionContext.currentSingleSelectedNode();
QTC_ASSERT(modelNode.isValid(), return); QTC_ASSERT(modelNode.isValid(), return);
BindingProperty prop = modelNode.bindingProperty("materials"); BindingProperty prop = modelNode.bindingProperty("materials");

View File

@@ -102,6 +102,9 @@ QWidget *Edit3DCanvas::busyIndicator() const
void Edit3DCanvas::mousePressEvent(QMouseEvent *e) void Edit3DCanvas::mousePressEvent(QMouseEvent *e)
{ {
if (e->button() == Qt::RightButton && e->modifiers() == Qt::NoModifier)
m_parent->view()->startContextMenu(e->pos());
m_parent->view()->sendInputEvent(e); m_parent->view()->sendInputEvent(e);
QWidget::mousePressEvent(e); QWidget::mousePressEvent(e);
} }

View File

@@ -239,15 +239,26 @@ void Edit3DView::customNotification(const AbstractView *view, const QString &ide
resetPuppet(); resetPuppet();
} }
/**
* @brief get model at position from puppet process
*
* Response from puppet process for the model at requested position
*
* @param modelNode 3D model picked at the requested position, invalid node if no model exists
*/
void Edit3DView::modelAtPosReady(const ModelNode &modelNode) void Edit3DView::modelAtPosReady(const ModelNode &modelNode)
{ {
if (!m_droppedMaterial.isValid() || !modelNode.isValid()) if (m_modelAtPosReqType == ModelAtPosReqType::ContextMenu) {
return; m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode);
} else if (m_modelAtPosReqType == ModelAtPosReqType::MaterialDrop) {
if (m_droppedMaterial.isValid() && modelNode.isValid()) {
executeInTransaction(__FUNCTION__, [&] { executeInTransaction(__FUNCTION__, [&] {
assignMaterialTo3dModel(modelNode, m_droppedMaterial); assignMaterialTo3dModel(modelNode, m_droppedMaterial);
}); });
} }
}
m_modelAtPosReqType = ModelAtPosReqType::None;
}
void Edit3DView::sendInputEvent(QInputEvent *e) const void Edit3DView::sendInputEvent(QInputEvent *e) const
{ {
@@ -631,8 +642,17 @@ void Edit3DView::addQuick3DImport()
tr("Could not add QtQuick3D import to project.")); tr("Could not add QtQuick3D import to project."));
} }
// This method is called upon right-clicking the view to prepare for context-menu creation. The actual
// context menu is created when modelAtPosReady() is received from puppet
void Edit3DView::startContextMenu(const QPoint &pos)
{
m_contextMenuPos = pos;
m_modelAtPosReqType = ModelAtPosReqType::ContextMenu;
}
void Edit3DView::dropMaterial(const ModelNode &matNode, const QPointF &pos) void Edit3DView::dropMaterial(const ModelNode &matNode, const QPointF &pos)
{ {
m_modelAtPosReqType = ModelAtPosReqType::MaterialDrop;
m_droppedMaterial = matNode; m_droppedMaterial = matNode;
QmlDesignerPlugin::instance()->viewManager().nodeInstanceView()->view3DAction({View3DActionCommand::GetModelAtPos, pos}); QmlDesignerPlugin::instance()->viewManager().nodeInstanceView()->view3DAction({View3DActionCommand::GetModelAtPos, pos});
} }

View File

@@ -79,9 +79,16 @@ public:
void setSeeker(SeekerSlider *slider); void setSeeker(SeekerSlider *slider);
void addQuick3DImport(); void addQuick3DImport();
void startContextMenu(const QPoint &pos);
void dropMaterial(const ModelNode &matNode, const QPointF &pos); void dropMaterial(const ModelNode &matNode, const QPointF &pos);
private: private:
enum class ModelAtPosReqType {
MaterialDrop,
ContextMenu,
None
};
void createEdit3DWidget(); void createEdit3DWidget();
void checkImports(); void checkImports();
@@ -120,6 +127,8 @@ private:
int particlemode; int particlemode;
ModelCache<QImage> m_canvasCache; ModelCache<QImage> m_canvasCache;
ModelNode m_droppedMaterial; ModelNode m_droppedMaterial;
ModelAtPosReqType m_modelAtPosReqType;
QPoint m_contextMenuPos;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -30,6 +30,7 @@
#include "edit3dwidget.h" #include "edit3dwidget.h"
#include "edit3dvisibilitytogglesmenu.h" #include "edit3dvisibilitytogglesmenu.h"
#include "metainfo.h" #include "metainfo.h"
#include "modelnodeoperations.h"
#include "qmldesignerconstants.h" #include "qmldesignerconstants.h"
#include "qmldesignerplugin.h" #include "qmldesignerplugin.h"
#include "qmlvisualnode.h" #include "qmlvisualnode.h"
@@ -49,8 +50,8 @@
namespace QmlDesigner { namespace QmlDesigner {
Edit3DWidget::Edit3DWidget(Edit3DView *view) : Edit3DWidget::Edit3DWidget(Edit3DView *view)
m_view(view) : m_view(view)
{ {
setAcceptDrops(true); setAcceptDrops(true);
@@ -146,6 +147,8 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) :
handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false); handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false);
createContextMenu();
view->setSeeker(seeker); view->setSeeker(seeker);
seeker->setToolTip(QLatin1String("Seek particle system time when paused.")); seeker->setToolTip(QLatin1String("Seek particle system time when paused."));
@@ -173,6 +176,18 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) :
showCanvas(false); showCanvas(false);
} }
void Edit3DWidget::createContextMenu()
{
m_contextMenu = new QMenu(this);
m_editMaterialAction = m_contextMenu->addAction(tr("Edit Material"), [&] {
SelectionContext selCtx(m_view);
selCtx.setTargetNode(m_contextMenuTarget);
ModelNodeOperations::editMaterial(selCtx);
});
// TODO: add more actions: delete, create, etc
}
void Edit3DWidget::contextHelp(const Core::IContext::HelpCallback &callback) const void Edit3DWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
{ {
if (m_view) if (m_view)
@@ -221,6 +236,15 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos)
m_backgroundColorMenu->close(); m_backgroundColorMenu->close();
} }
void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode)
{
m_contextMenuTarget = modelNode;
m_editMaterialAction->setEnabled(modelNode.isValid());
m_contextMenu->popup(mapToGlobal(pos));
}
void Edit3DWidget::linkActivated(const QString &link) void Edit3DWidget::linkActivated(const QString &link)
{ {
Q_UNUSED(link) Q_UNUSED(link)

View File

@@ -24,11 +24,13 @@
****************************************************************************/ ****************************************************************************/
#pragma once #pragma once
#include <QtWidgets/qwidget.h> #include <QLabel>
#include <QtWidgets/qlabel.h> #include <QMenu>
#include <QtWidgets/qmenu.h> #include <QPointer>
#include <QtCore/qpointer.h> #include <QWidget>
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
#include <modelnode.h>
namespace QmlDesigner { namespace QmlDesigner {
@@ -54,12 +56,15 @@ public:
QMenu *backgroundColorMenu() const; QMenu *backgroundColorMenu() const;
void showBackgroundColorMenu(bool show, const QPoint &pos); void showBackgroundColorMenu(bool show, const QPoint &pos);
void showContextMenu(const QPoint &pos, const ModelNode &modelNode);
protected: protected:
void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override;
void dropEvent(QDropEvent *dropEvent) override; void dropEvent(QDropEvent *dropEvent) override;
private: private:
void linkActivated(const QString &link); void linkActivated(const QString &link);
void createContextMenu();
QPointer<Edit3DView> m_edit3DView; QPointer<Edit3DView> m_edit3DView;
QPointer<Edit3DView> m_view; QPointer<Edit3DView> m_view;
@@ -69,6 +74,9 @@ private:
Core::IContext *m_context = nullptr; Core::IContext *m_context = nullptr;
QPointer<QMenu> m_visibilityTogglesMenu; QPointer<QMenu> m_visibilityTogglesMenu;
QPointer<QMenu> m_backgroundColorMenu; QPointer<QMenu> m_backgroundColorMenu;
QPointer<QMenu> m_contextMenu;
QPointer<QAction> m_editMaterialAction;
ModelNode m_contextMenuTarget;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -1707,7 +1707,6 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand
emitImport3DSupportChanged(supportMap); emitImport3DSupportChanged(supportMap);
} else if (command.type() == PuppetToCreatorCommand::ModelAtPos) { } else if (command.type() == PuppetToCreatorCommand::ModelAtPos) {
ModelNode modelNode = modelNodeForInternalId(command.data().toUInt()); ModelNode modelNode = modelNodeForInternalId(command.data().toUInt());
if (modelNode.isValid())
emitModelAtPosResult(modelNode); emitModelAtPosResult(modelNode);
} }
} }