QmlDesigner: Add delete action to 3D Editor's context menu

This entails selecting a model upon right-clicking if it is not selected.
Also fixed a memory leak and small tweaks.

Fixes: QDS-7401
Change-Id: I592acb3fff30ecc3236f3cf2fbe126de4fb389dc
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-23 15:19:56 +03:00
parent f6e8b5f753
commit 8656bafbd4
6 changed files with 51 additions and 25 deletions

View File

@@ -363,7 +363,7 @@ Item {
}
}
function handleObjectClicked(object, multi)
function handleObjectClicked(object, button, multi)
{
if (object instanceof View3D) {
// View3D can be the resolved pick target in case the 3D editor is showing content
@@ -393,7 +393,14 @@ Item {
// Null object always clears entire selection
var newSelection = [];
if (clickedObject) {
if (multi && selectedNodes.length > 0) {
if (button === Qt.RightButton) {
// Right-clicking does only single selection (when clickedObject is unselected)
// This is needed for selecting a target for the context menu
if (!selectedNodes.includes(clickedObject))
newSelection[0] = clickedObject;
else
newSelection = selectedNodes;
} else if (multi && selectedNodes.length > 0) {
var deselect = false;
for (var i = 0; i < selectedNodes.length; ++i) {
// Multiselecting already selected object clears that object from selection
@@ -697,10 +704,10 @@ Item {
view3D: overlayView
dragHelper: gizmoDragHelper
onPropertyValueCommit: (propName)=> {
onPropertyValueCommit: (propName) => {
viewRoot.commitObjectProperty([targetNode], [propName]);
}
onPropertyValueChange: (propName)=> {
onPropertyValueChange: (propName) => {
viewRoot.changeObjectProperty([targetNode], [propName]);
}
}
@@ -772,17 +779,17 @@ Item {
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: false
property MouseArea3D freeDraggerArea
property point pressPoint
property bool initialMoveBlock: false
onPressed: (mouse)=> {
onPressed: (mouse) => {
if (viewRoot.editView) {
var pickResult = viewRoot.editView.pick(mouse.x, mouse.y);
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), mouse.button,
mouse.modifiers & Qt.ControlModifier);
if (pickResult.objectHit && pickResult.objectHit instanceof Node) {
@@ -800,7 +807,7 @@ Item {
}
}
}
onPositionChanged: (mouse)=> {
onPositionChanged: (mouse) => {
if (freeDraggerArea) {
if (initialMoveBlock && Math.abs(pressPoint.x - mouse.x) + Math.abs(pressPoint.y - mouse.y) > 10) {
// Don't force press event at actual press, as that puts the gizmo
@@ -825,10 +832,10 @@ Item {
}
}
onReleased: (mouse)=> {
onReleased: (mouse) => {
handleRelease(mouse);
}
onCanceled: (mouse)=> {
onCanceled: (mouse) => {
handleRelease(mouse);
}
}

View File

@@ -363,7 +363,7 @@ Item {
}
}
function handleObjectClicked(object, multi)
function handleObjectClicked(object, button, multi)
{
if (object instanceof View3D) {
// View3D can be the resolved pick target in case the 3D editor is showing content
@@ -393,7 +393,14 @@ Item {
// Null object always clears entire selection
var newSelection = [];
if (clickedObject) {
if (multi && selectedNodes.length > 0) {
if (button === Qt.RightButton) {
// Right-clicking does only single selection (when clickedObject is unselected)
// This is needed for selecting a target for the context menu
if (!selectedNodes.includes(clickedObject))
newSelection[0] = clickedObject;
else
newSelection = selectedNodes;
} else if (multi && selectedNodes.length > 0) {
var deselect = false;
for (var i = 0; i < selectedNodes.length; ++i) {
// Multiselecting already selected object clears that object from selection
@@ -841,10 +848,10 @@ Item {
view3D: overlayView
dragHelper: gizmoDragHelper
onPropertyValueCommit: (propName)=> {
onPropertyValueCommit: (propName) => {
viewRoot.commitObjectProperty([targetNode], [propName]);
}
onPropertyValueChange: (propName)=> {
onPropertyValueChange: (propName) => {
viewRoot.changeObjectProperty([targetNode], [propName]);
}
}
@@ -917,14 +924,14 @@ Item {
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: false
property MouseArea3D freeDraggerArea
property point pressPoint
property bool initialMoveBlock: false
onPressed: (mouse)=> {
onPressed: (mouse) => {
if (viewRoot.editView) {
// First pick overlay to check for hits there
var pickResult = _generalHelper.pickViewAt(overlayView, mouse.x, mouse.y);
@@ -935,7 +942,7 @@ Item {
resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
}
handleObjectClicked(resolvedResult, mouse.modifiers & Qt.ControlModifier);
handleObjectClicked(resolvedResult, mouse.button, mouse.modifiers & Qt.ControlModifier);
if (pickResult.objectHit && pickResult.objectHit instanceof Node) {
if (transformMode === EditView3D.TransformMode.Move)
@@ -952,7 +959,7 @@ Item {
}
}
}
onPositionChanged: (mouse)=> {
onPositionChanged: (mouse) => {
if (freeDraggerArea) {
if (initialMoveBlock && Math.abs(pressPoint.x - mouse.x) + Math.abs(pressPoint.y - mouse.y) > 10) {
// Don't force press event at actual press, as that puts the gizmo
@@ -977,10 +984,10 @@ Item {
}
}
onReleased: (mouse)=> {
onReleased: (mouse) => {
handleRelease(mouse);
}
onCanceled: (mouse)=> {
onCanceled: (mouse) => {
handleRelease(mouse);
}
}

View File

@@ -285,12 +285,12 @@ void Qt5InformationNodeInstanceServer::handleInputEvents()
continue;
}
}
auto me = new QMouseEvent(command.type(), command.pos(), command.button(),
command.buttons(), command.modifiers());
QMouseEvent me(command.type(), command.pos(), command.button(), command.buttons(),
command.modifiers());
// We must use sendEvent in Qt 6, as using postEvent allows the associated position
// data stored internally in QMutableEventPoint to potentially be updated by system
// 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)
@@ -428,7 +428,8 @@ void Qt5InformationNodeInstanceServer::getModelAtPos(const QPointF &pos)
QVariant instance = resolvedPick ? instanceForObject(resolvedPick).instanceId() : -1;
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::ModelAtPos, instance});
return;
#else
Q_UNUSED(pos)
#endif
}

View File

@@ -249,6 +249,10 @@ void Edit3DView::customNotification(const AbstractView *view, const QString &ide
void Edit3DView::modelAtPosReady(const ModelNode &modelNode)
{
if (m_modelAtPosReqType == ModelAtPosReqType::ContextMenu) {
// Make sure right-clicked item is selected. Due to a bug in puppet side right-clicking an item
// while the context-menu is shown doesn't select the item.
if (modelNode.isValid() && !modelNode.isSelected())
setSelectedModelNode(modelNode);
m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode);
} else if (m_modelAtPosReqType == ModelAtPosReqType::MaterialDrop) {
if (m_droppedMaterial.isValid() && modelNode.isValid()) {

View File

@@ -185,7 +185,12 @@ void Edit3DWidget::createContextMenu()
ModelNodeOperations::editMaterial(selCtx);
});
// TODO: add more actions: delete, create, etc
m_deleteAction = m_contextMenu->addAction(tr("Delete"), [&] {
view()->executeInTransaction("Edit3DWidget::createContextMenu", [&] {
for (ModelNode &node : m_view->selectedModelNodes())
node.destroy();
});
});
}
void Edit3DWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
@@ -241,6 +246,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
m_contextMenuTarget = modelNode;
m_editMaterialAction->setEnabled(modelNode.isValid());
m_deleteAction->setEnabled(modelNode.isValid());
m_contextMenu->popup(mapToGlobal(pos));
}

View File

@@ -76,6 +76,7 @@ private:
QPointer<QMenu> m_backgroundColorMenu;
QPointer<QMenu> m_contextMenu;
QPointer<QAction> m_editMaterialAction;
QPointer<QAction> m_deleteAction;
ModelNode m_contextMenuTarget;
};