diff options
author | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2016-06-13 10:14:04 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2016-06-14 11:37:55 +0000 |
commit | af16128e059d51bc41dccafdbe52e8461572fb6e (patch) | |
tree | dfb4f1b8d61fc9292a7c620c689ea987c46c3778 /editorlib | |
parent | 9b190770efe147d13df3eecfd1f725202d3f21c2 (diff) |
Multiselect refactoring
Now store multiselection status and list on c++ side only to
reduce problems from keeping them properly synchronized.
Change-Id: If3277bb1eca5ffc7a7749e300e0cc6de4ccf9df2
Reviewed-by: Titta Heikkala <titta.heikkala@qt.io>
Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'editorlib')
-rw-r--r-- | editorlib/qml/ComponentMenu.qml | 42 | ||||
-rw-r--r-- | editorlib/qml/EntityTree.qml | 50 | ||||
-rw-r--r-- | editorlib/qml/GeneralPropertyView.qml | 2 | ||||
-rw-r--r-- | editorlib/qml/main.qml | 44 | ||||
-rw-r--r-- | editorlib/src/editorscene.cpp | 273 | ||||
-rw-r--r-- | editorlib/src/editorscene.h | 22 | ||||
-rw-r--r-- | editorlib/src/editorsceneitemmodel.cpp | 19 | ||||
-rw-r--r-- | editorlib/src/undohandler/resetentitycommand.cpp | 5 | ||||
-rw-r--r-- | editorlib/src/undohandler/undohandler.cpp | 10 |
9 files changed, 243 insertions, 224 deletions
diff --git a/editorlib/qml/ComponentMenu.qml b/editorlib/qml/ComponentMenu.qml index 63ce969..8a00107 100644 --- a/editorlib/qml/ComponentMenu.qml +++ b/editorlib/qml/ComponentMenu.qml @@ -67,7 +67,7 @@ Menu { MenuItem { text: qsTr("Object Picker") + editorScene.emptyString iconSource: "images/picker.png" - enabled: !entityTree.multiSelect + enabled: !editorScene.multiSelection onTriggered: { componentPropertiesView.model.appendNewComponent(sceneModel.ObjectPicker) } @@ -77,7 +77,7 @@ Menu { EntityMenu { iconSource: "images/plus.png" - enabled: !entityTree.multiSelect && !entityTreeView.cameraSelected + enabled: !editorScene.multiSelection && !entityTreeView.cameraSelected } MenuItem { text: qsTr("Remove") + editorScene.emptyString @@ -85,13 +85,14 @@ Menu { iconSource: "images/minus.png" onTriggered: { entityTreeView.editing = false - if (entityTree.multiSelect) { + if (editorScene.multiSelection) { // Handle multiselection removal editorScene.undoHandler.beginMacro(text) - var removeList = editorScene.sceneModel.parentList(selectionList) + var removeList = editorScene.sceneModel.parentList(editorScene.multiSelectionList) for (var i = 0; i < removeList.length; ++i) editorScene.undoHandler.createRemoveEntityCommand(removeList[i]) editorScene.undoHandler.endMacro() + entityTree.selectSceneRoot() } else { // Doublecheck that we don't try to remove the scene root if (entityTreeView.selection.currentIndex !== editorScene.sceneModel.sceneEntityIndex()) @@ -104,13 +105,14 @@ Menu { enabled: !entityTreeView.sceneRootSelected iconSource: "images/duplicate.png" onTriggered: { - if (entityTree.multiSelect) { + if (editorScene.multiSelection) { // Handle multiselection duplication editorScene.undoHandler.beginMacro(text) - var duplicateList = editorScene.sceneModel.parentList(selectionList) + var duplicateList = editorScene.sceneModel.parentList(editorScene.multiSelectionList) for (var i = 0; i < duplicateList.length; ++i) editorScene.undoHandler.createDuplicateEntityCommand(duplicateList[i]) editorScene.undoHandler.endMacro() + editorScene.restoreMultiSelection(editorScene.multiSelectionList) } else { var currentSelection = selectedEntity.entity() editorScene.undoHandler.createDuplicateEntityCommand(selectedEntityName) @@ -120,7 +122,7 @@ Menu { } MenuItem { text: qsTr("Copy (Ctrl + c)") + editorScene.emptyString - enabled: !entityTree.multiSelect && !entityTreeView.sceneRootSelected + enabled: !editorScene.multiSelection && !entityTreeView.sceneRootSelected iconSource: "images/copy.png" onTriggered: { mainwindow.copyEntity(selectedEntityName) @@ -128,7 +130,7 @@ Menu { } MenuItem { text: qsTr("Cut (Ctrl + x)") + editorScene.emptyString - enabled: !entityTree.multiSelect && !entityTreeView.sceneRootSelected + enabled: !editorScene.multiSelection && !entityTreeView.sceneRootSelected iconSource: "images/cut.png" onTriggered: { mainwindow.cutEntity(selectedEntityName, selectedEntity) @@ -136,7 +138,7 @@ Menu { } MenuItem { text: qsTr("Paste (Ctrl + v)") + editorScene.emptyString - enabled: trackMousePosition && !entityTree.multiSelect + enabled: trackMousePosition && !editorScene.multiSelection && (!entityTree.treeviewPasting || (entityTree.treeviewPasting && editorScene.sceneModel.canReparent( editorScene.sceneModel.editorSceneItemFromIndex( @@ -153,7 +155,7 @@ Menu { MenuItem { text: qsTr("Reset") + editorScene.emptyString iconSource: "images/reset_all.png" - enabled: !entityTree.multiSelect && !entityTreeView.sceneRootSelected + enabled: !editorScene.multiSelection && !entityTreeView.sceneRootSelected onTriggered: { editorScene.undoHandler.createResetEntityCommand(selectedEntityName) } @@ -161,7 +163,7 @@ Menu { MenuItem { text: qsTr("Reset Transform") + editorScene.emptyString iconSource: "images/reset.png" - enabled: !entityTree.multiSelect && !entityTreeView.sceneRootSelected + enabled: !editorScene.multiSelection && !entityTreeView.sceneRootSelected && !entityTreeView.cameraSelected onTriggered: { editorScene.undoHandler.createResetTransformCommand(selectedEntityName) @@ -176,8 +178,8 @@ Menu { // Copy list, as the original is emptied on insertEntity var reparentList = [] var groupCenter - if (entityTree.multiSelect) { - reparentList = editorScene.sceneModel.parentList(selectionList) + if (editorScene.multiSelection) { + reparentList = editorScene.sceneModel.parentList(editorScene.multiSelectionList) groupCenter = editorScene.getMultiSelectionCenter() } else { reparentList[0] = selectedEntityName @@ -186,10 +188,6 @@ Menu { // TODO: Allow creating groups under other entities? - // Select scene root before doing reparenting to avoid having selection changes - // while removing, as indexes can be corrupt during the removal - entityTree.selectSceneRoot() - // Add new group editorScene.undoHandler.createInsertEntityCommand(1, editorScene.sceneRootName(), groupCenter) @@ -199,11 +197,11 @@ Menu { for (var i = 0; i < reparentList.length; ++i) editorScene.undoHandler.createReparentEntityCommand(groupName, reparentList[i]) editorScene.undoHandler.endMacro() - // Clear selection - entityTree.multiSelect = false - editorScene.multiSelection = [] - // Select the added group - editorScene.selectIndex(index) + + // Single-select the added group. Need to fetch group index again as reparenting + // resets the model. + editorScene.clearMultiSelection() + editorScene.selectIndex(editorScene.sceneModel.getModelIndexByName(groupName)) } } } diff --git a/editorlib/qml/EntityTree.qml b/editorlib/qml/EntityTree.qml index 4a726e2..3edb985 100644 --- a/editorlib/qml/EntityTree.qml +++ b/editorlib/qml/EntityTree.qml @@ -40,17 +40,16 @@ Item { property alias view: entityTreeView property alias menu: addComponentMenu property bool treeviewPasting: false - property bool multiSelect: false - property bool multiSelectedCamera: false Keys.onDeletePressed: { - if (multiSelect) { + if (editorScene.multiSelection) { // Handle multiselection removal editorScene.undoHandler.beginMacro("Remove selected") - var removeList = editorScene.sceneModel.parentList(selectionList) + var removeList = editorScene.sceneModel.parentList(editorScene.multiSelectionList) for (var i = 0; i < removeList.length; ++i) editorScene.undoHandler.createRemoveEntityCommand(removeList[i]) editorScene.undoHandler.endMacro() + selectSceneRoot() } else { // Doublecheck that we don't try to remove the scene root if (entityTreeView.selection.currentIndex !== editorScene.sceneModel.sceneEntityIndex()) @@ -63,6 +62,7 @@ Item { } function selectSceneRoot() { + editorScene.clearMultiSelection() entityTreeView.selection.setCurrentIndex( editorScene.sceneModel.sceneEntityIndex(), ItemSelectionModel.SelectCurrent) @@ -74,6 +74,8 @@ Item { var y = yPos ? yPos : -1 entityTreeView.editing = false + editorScene.clearMultiSelection() + // Never allow inserting to root if (entityTreeView.selection.currentIndex.row === -1) selectSceneRoot() @@ -218,21 +220,16 @@ Item { onPressed: { if (mouse.modifiers & Qt.ControlModifier) { // Handle multiselection - // Prevent multiselecting scene root - if (styleData.index !== editorScene.sceneModel.sceneEntityIndex()) { - editorScene.addToMultiSelection(editorScene.sceneModel.entityName( - styleData.index)) - } - // If empty list, select scene root - if (selectionList.length === 0) { - selectedEntityName = "" - selectSceneRoot() + // Prevent multiselecting scene root and toggling when singly selected + // entity is clicked again + if ((editorScene.multiSelection + || styleData.index !== entityTreeView.selection.currentIndex) + && styleData.index !== editorScene.sceneModel.sceneEntityIndex()) { + editorScene.toggleEntityMultiSelection(editorScene.sceneModel.entityName( + styleData.index)) } } else { - // Clear selectionList - entityTreeView.selection.clear() - selectionList.length = 0 - editorScene.multiSelection = selectionList + editorScene.clearMultiSelection() entityTreeView.selection.setCurrentIndex(styleData.index, ItemSelectionModel.SelectCurrent) } @@ -295,6 +292,7 @@ Item { onDropped: { if (isValidDropTarget(drop.source)) { dragHighlight.visible = false + editorScene.clearMultiSelection() entityTreeView.selection.setCurrentIndex(styleData.index, ItemSelectionModel.SelectCurrent) if (drop.source.drag.target.dragKey === "changeParent") { @@ -432,14 +430,12 @@ Item { target: entityTreeView.selection onCurrentIndexChanged: { entityTreeView.editing = false - // If there is no current item selected for some reason, fall back to scene root + // If there is no current item selected for some reason, fall back to scene root, + // except when dealing with multiselection, during which currentIndex can become -1 temporarily. if (entityTreeView.selection.currentIndex.row === -1) { selectedEntityName = "" - if (!selectionList.length) { - editorScene.clearSelectionBoxes() - selectedEntity.showSelectionBox = true + if (!editorScene.multiSelection) selectSceneRoot() - } } else { entityTreeView.sceneRootSelected = (editorScene.sceneModel.sceneEntityIndex() === entityTreeView.selection.currentIndex) @@ -452,17 +448,13 @@ Item { selectedEntity.itemType() === EditorSceneItem.Group selectedEntityName = editorScene.sceneModel.entityName( entityTreeView.selection.currentIndex) - // Don't clear selection boxes if there are items in multiselection list - if (!selectionList.length) { - editorScene.clearSelectionBoxes() - selectedEntity.showSelectionBox = true - } } else { // Should never get here selectedEntityName = "" - editorScene.clearSelectionBoxes() + selectSceneRoot() } - editorScene.selection = selectedEntity.entity() + if (!editorScene.multiSelection) + editorScene.selection = selectedEntity.entity() } } } diff --git a/editorlib/qml/GeneralPropertyView.qml b/editorlib/qml/GeneralPropertyView.qml index 62a470f..d6e0be7 100644 --- a/editorlib/qml/GeneralPropertyView.qml +++ b/editorlib/qml/GeneralPropertyView.qml @@ -46,7 +46,7 @@ Item { Layout.minimumHeight: componentHeight Layout.maximumHeight: Layout.minimumHeight - visible: !entityTree.multiSelect + visible: !editorScene.multiSelection Component.onCompleted: { componentHeight = columnLayout.y + columnLayout.height diff --git a/editorlib/qml/main.qml b/editorlib/qml/main.qml index 3d78a8d..85115a4 100644 --- a/editorlib/qml/main.qml +++ b/editorlib/qml/main.qml @@ -123,8 +123,6 @@ ApplicationWindow { property real qlcControlHeight: 28 - property var selectionList: [] - property string systemLanguage: editorScene.language toolBar: EditorToolbar {} @@ -250,7 +248,7 @@ ApplicationWindow { sequence: StandardKey.Copy onActivated: { // Prevent copying multiselection (for now, at least) - if (!selectionList.length) + if (!editorScene.multiSelection) mainwindow.copyEntity(selectedEntityName) } } @@ -276,7 +274,7 @@ ApplicationWindow { sequence: StandardKey.Cut onActivated: { // Prevent cutting multiselection (for now, at least) - if (!selectionList.length) + if (!editorScene.multiSelection) mainwindow.cutEntity(selectedEntityName, selectedEntity) } } @@ -329,29 +327,11 @@ ApplicationWindow { freeView: true onSelectionChanged: { - if (multiSelection.length >= 1) { - entityTree.multiSelect = true - } else { - selectionList.length = 0 - entityTree.multiSelect = false - } restoreSelection(selection) } - onMultiSelectionChanged: { - selectionList = multiSelection - entityTree.multiSelectedCamera = false - // Deselect old ones - entityTree.view.selection.clear() - // Dig indexes of all selected entities and pass the selections to entitytree - for (var i = 0; i < multiSelection.length; ++i) { - var index = editorScene.sceneModel.getModelIndexByName(multiSelection[i]) - entityTree.view.selection.select(index, ItemSelectionModel.Select) - if (editorScene.sceneModel.editorSceneItemFromIndex(index).itemType() - === EditorSceneItem.Camera) { - entityTree.multiSelectedCamera = true - } - } + onMultiSelectionListChanged: { + restoreMultiSelection(editorScene.multiSelectionList) } onErrorChanged: { @@ -374,18 +354,28 @@ ApplicationWindow { selectIndex(index) } + function restoreMultiSelection(selectionList) { + // Deselect old ones + entityTree.view.selection.clear() + // Dig indexes of all selected entities and pass the selections to entitytree + for (var i = 0; i < selectionList.length; ++i) { + var index = editorScene.sceneModel.getModelIndexByName(multiSelectionList[i]) + entityTree.view.selection.select(index, ItemSelectionModel.Select) + expandTo(index) + } + } + function selectIndex(index) { expandTo(index) entityTree.view.forceActiveFocus() - if (!entityTree.multiSelect) - entityTree.view.selection.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect) + entityTree.view.selection.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect) } function expandTo(index) { var target = index do { - target = target.parent entityTree.view.expand(target) + target = target.parent } while (target.valid) } } diff --git a/editorlib/src/editorscene.cpp b/editorlib/src/editorscene.cpp index 1eb9cdd..d4ca572 100644 --- a/editorlib/src/editorscene.cpp +++ b/editorlib/src/editorscene.cpp @@ -120,8 +120,7 @@ EditorScene::EditorScene(QObject *parent) , m_gridSize(3) , m_duplicateCount(0) , m_previousDuplicate(nullptr) - , m_multiSelect(false) - , m_previousSelectedEntity(nullptr) + , m_ctrlDownOnLastLeftPress(false) , m_clipboardOperation(ClipboardNone) { setLanguage(language()); @@ -200,13 +199,6 @@ void EditorScene::addEntity(Qt3DCore::QEntity *entity, int index, Qt3DCore::QEnt addEntity(childEntity); } } - - if (!m_selectedEntityNameList.isEmpty()) { - // Clear multiselection list, otherwise treeview gets messed up - m_selectedEntityNameList.clear(); - m_multiSelect = false; - emit multiSelectionChanged(m_selectedEntityNameList); - } } // Removed entity is deleted @@ -243,7 +235,7 @@ void EditorScene::removeEntity(Qt3DCore::QEntity *entity) m_sceneItems.remove(entity->id()); - if (m_sceneEntity && m_selectedEntity == entity) + if (!multiSelection() && m_sceneEntity && m_selectedEntity == entity) setSelection(m_sceneEntity); delete item; @@ -1744,30 +1736,33 @@ Qt3DRender::QCamera *EditorScene::frameGraphCamera() const void EditorScene::setSelection(Qt3DCore::QEntity *entity) { + // Setting selection implies end to multiSelection + if (m_selectedEntityNameList.size()) + clearMultiSelection(); EditorSceneItem *item = m_sceneItems.value(entity->id(), nullptr); if (item) { if (entity != m_selectedEntity) { - if (m_selectedEntity) - connectDragHandles(m_sceneItems.value(m_selectedEntity->id(), nullptr), false); + if (m_selectedEntity) { + EditorSceneItem *oldItem = m_sceneItems.value(m_selectedEntity->id(), nullptr); + if (oldItem) { + connectDragHandles(oldItem, false); + oldItem->setShowSelectionBox(false); + } + } m_selectedEntity = entity; if (m_selectedEntity) { connectDragHandles(item, true); + if (m_selectedEntity != m_sceneEntity) + item->setShowSelectionBox(true); m_selectedEntityTransform = EditorUtils::entityTransform(m_selectedEntity); } - if (!m_multiSelect && m_selectedEntity != m_rootEntity) - m_previousSelectedEntity = m_selectedEntity; - else - m_previousSelectedEntity = nullptr; - // Emit signal to highlight the entity from the list emit selectionChanged(m_selectedEntity); - } else if (!m_multiSelect) { - clearSelectionBoxes(m_selectedEntity); } - m_dragHandlesTransform->setEnabled(item->isSelectionBoxShowing() && !m_multiSelect); + m_dragHandlesTransform->setEnabled(item->isSelectionBoxShowing()); if (item->itemType() == EditorSceneItem::Camera) { // Disable scale handles for cameras @@ -1818,47 +1813,29 @@ void EditorScene::setSelection(Qt3DCore::QEntity *entity) handleSelectionTransformChange(); } else { m_dragHandlesTransform->setEnabled(false); + if (m_selectedEntity != m_sceneEntity) + setSelection(m_sceneEntity); } } -QString EditorScene::previousSelectedEntityName() const { - if (m_previousSelectedEntity) - return m_previousSelectedEntity->objectName(); - else - return QString(); -} - -void EditorScene::setMultiSelection(const QStringList &multiSelection) -{ - if (m_selectedEntityNameList != multiSelection) { - checkMultiSelectionHighlights(m_selectedEntityNameList, multiSelection); - m_selectedEntityNameList = multiSelection; - } -} - -QStringList EditorScene::multiSelection() -{ - return m_selectedEntityNameList; -} - -void EditorScene::addToMultiSelection(const QString &name) +void EditorScene::toggleEntityMultiSelection(const QString &name) { - QStringList oldList = m_selectedEntityNameList; - // Add previously selected one, if different than new one and other than scene root. - if (m_selectedEntityNameList.isEmpty() && m_pickedEntity != m_selectedEntity - && m_selectedEntity != m_sceneEntity) { - m_selectedEntityNameList.append(previousSelectedEntityName()); - } // If the new one is already in, remove it. Otherwise add it. - if (!m_selectedEntityNameList.contains(name)) - m_selectedEntityNameList.append(name); + if (m_selectedEntityNameList.contains(name)) + removeEntityFromMultiSelection(name); else - m_selectedEntityNameList.removeOne(name); + addEntityToMultiSelection(name); +} - // Emit multiselection list if it changed - if (oldList != m_selectedEntityNameList) { +void EditorScene::clearMultiSelection() +{ + if (m_selectedEntityNameList.size() > 0) { + QStringList oldList = m_selectedEntityNameList; + m_selectedEntityNameList.clear(); checkMultiSelectionHighlights(oldList, m_selectedEntityNameList); - emit multiSelectionChanged(m_selectedEntityNameList); + emit multiSelectionChanged(false); + emit multiSelectionListChanged(); + setSelection(m_sceneEntity); } } @@ -1875,6 +1852,69 @@ QVector3D EditorScene::getMultiSelectionCenter() return m_selectedEntityNameList.size() ? (pos / m_selectedEntityNameList.size()) : QVector3D(); } +void EditorScene::addEntityToMultiSelection(const QString &name) +{ + QStringList oldList = m_selectedEntityNameList; + + if (oldList.size() == 0) { + // Do not add if multiselecting the currently selected entity as the first entity + if (m_selectedEntity->objectName() == name) + return; + + if (m_selectedEntity != m_sceneEntity) { + m_selectedEntityNameList.append(m_selectedEntity->objectName()); + m_dragHandlesTransform->setEnabled(false); + handleSelectionTransformChange(); + m_selectedEntity = nullptr; + } else { + // Just single-select the new entity and return if the other entity was scene entity + EditorSceneItem *item = m_sceneModel->getItemByName(name); + if (item) { + setSelection(item->entity()); + return; + } + } + } + m_selectedEntityNameList.append(name); + + checkMultiSelectionHighlights(oldList, m_selectedEntityNameList); + + if (oldList.size() == 0) + emit multiSelectionChanged(true); + emit multiSelectionListChanged(); +} + +void EditorScene::removeEntityFromMultiSelection(const QString &name) +{ + QStringList oldList = m_selectedEntityNameList; + bool removed = m_selectedEntityNameList.removeOne(name); + + if (removed) { + checkMultiSelectionHighlights(oldList, m_selectedEntityNameList); + bool lastRemoved = m_selectedEntityNameList.size() == 1; + EditorSceneItem *lastItem = nullptr; + if (lastRemoved) { + lastItem = m_sceneModel->getItemByName(m_selectedEntityNameList.at(0)); + m_selectedEntityNameList.clear(); + emit multiSelectionChanged(false); + } + emit multiSelectionListChanged(); + if (lastRemoved) { + if (lastItem) + setSelection(lastItem->entity()); + else + setSelection(m_sceneEntity); + } + } +} + +void EditorScene::renameEntityInMultiSelectionList(const QString &oldName, const QString &newName) +{ + int index = m_selectedEntityNameList.indexOf(oldName); + if (index > 0) + m_selectedEntityNameList.replace(index, newName); +} + void EditorScene::setClipboardOperation(ClipboardOperation operation) { if (operation != m_clipboardOperation) { @@ -1981,34 +2021,30 @@ void EditorScene::clearSelectionBoxes(Qt3DCore::QEntity *skipEntity) void EditorScene::endSelectionHandling() { if (m_dragMode == DragNone && m_pickedEntity) { - // Multiselection handling - if (m_multiSelect) - addToMultiSelection(m_pickedEntity->objectName()); - else - m_selectedEntityNameList.clear(); - - // Selection handling - if (m_multiSelect && m_selectedEntityNameList.isEmpty()) - setSelection(m_sceneEntity); - else + if (m_ctrlDownOnLastLeftPress) { + // Multiselection handling + toggleEntityMultiSelection(m_pickedEntity->objectName()); + } else { + // Single selection handling setSelection(m_pickedEntity); - // Selecting an object also starts drag, if translate handle is enabled - Qt3DRender::QCamera *cameraEntity = qobject_cast<Qt3DRender::QCamera *>(m_pickedEntity); - bool viewCenterDrag = cameraEntity && m_cameraViewCenterSelected && !m_viewCenterLocked; - bool entityDrag = m_dragHandleTranslateTransform->isEnabled() - && m_dragHandlesTransform->isEnabled() - && (!cameraEntity || !m_cameraViewCenterSelected); - if (viewCenterDrag || entityDrag) { - m_dragMode = DragTranslate; - m_dragEntity = m_pickedEntity; - if (cameraEntity) { - if (viewCenterDrag) - m_dragInitialTranslationValue = cameraEntity->viewCenter(); - else - m_dragInitialTranslationValue = cameraEntity->position(); - } else { - m_dragInitialTranslationValue = m_dragHandlesTransform->translation(); + // Selecting an object also starts drag, if translate handle is enabled + Qt3DRender::QCamera *cameraEntity = qobject_cast<Qt3DRender::QCamera *>(m_pickedEntity); + bool viewCenterDrag = cameraEntity && m_cameraViewCenterSelected && !m_viewCenterLocked; + bool entityDrag = m_dragHandleTranslateTransform->isEnabled() + && m_dragHandlesTransform->isEnabled() + && (!cameraEntity || !m_cameraViewCenterSelected); + if (viewCenterDrag || entityDrag) { + m_dragMode = DragTranslate; + m_dragEntity = m_pickedEntity; + if (cameraEntity) { + if (viewCenterDrag) + m_dragInitialTranslationValue = cameraEntity->viewCenter(); + else + m_dragInitialTranslationValue = cameraEntity->position(); + } else { + m_dragInitialTranslationValue = m_dragHandlesTransform->translation(); + } } } m_pickedEntity = nullptr; @@ -2021,6 +2057,14 @@ void EditorScene::handleSelectionTransformChange() EditorSceneItem *item = nullptr; if (m_selectedEntity && m_selectedEntity != m_sceneEntity) item = m_sceneItems.value(m_selectedEntity->id(), nullptr); + QPoint translatePoint; + QPoint centerPoint; + QVector3D translateHandlePos; + QVector3D itemCenterHandlePos; + QVector3D rotateHandlePos; + QVector3D cornerHandlePositions[dragCornerHandleCount]; + bool showCenterHandle = false; + if (item) { Qt3DRender::QCamera *camera = frameGraphCamera(); resizeCameraViewCenterEntity(); @@ -2041,12 +2085,11 @@ void EditorScene::handleSelectionTransformChange() // Find out x/y viewport positions of drag handles - QVector3D translateHandlePos = EditorUtils::projectRay( + translateHandlePos = EditorUtils::projectRay( camera->viewMatrix(), camera->projectionMatrix(), m_viewport->width(), m_viewport->height(), m_dragHandlesTransform->matrix() * QVector3D()); - QVector3D cornerHandlePositions[dragCornerHandleCount]; for (int i = 0; i < dragCornerHandleCount; i++) { m_dragHandleScaleTransforms.at(i)->setTranslation( translation * m_dragHandleCornerAdjustments.at(i)); @@ -2060,7 +2103,6 @@ void EditorScene::handleSelectionTransformChange() // Try to position rotate handle to the upper right corner of the selection box, as // drawn on the screen. However, we don't change the corner during drag-rotation. - QVector3D rotateHandlePos; const float handleDistance = 12.0f; if (m_dragMode != DragRotate) { float maxDelta = 0.0f; @@ -2105,11 +2147,7 @@ void EditorScene::handleSelectionTransformChange() rotateHandlePos += adjustVector; } - QPoint translatePoint(translateHandlePos.x(), translateHandlePos.y()); - - QPoint centerPoint; - QVector3D itemCenterHandlePos; - bool showCenterHandle = false; + translatePoint = QPoint(translateHandlePos.x(), translateHandlePos.y()); if (item->itemType() != EditorSceneItem::Camera && item->itemType() != EditorSceneItem::Light) { itemCenterHandlePos = EditorUtils::projectRay( @@ -2117,11 +2155,9 @@ void EditorScene::handleSelectionTransformChange() m_viewport->width(), m_viewport->height(), m_dragHandlesTransform->matrix() * m_dragHandleTranslateTransform->matrix() * QVector3D()); - centerPoint = QPoint(itemCenterHandlePos.x(), itemCenterHandlePos.y()); showCenterHandle = translatePoint != centerPoint; } - m_meshCenterIndicatorLine->setEnabled(showCenterHandle); if (showCenterHandle) { QQuaternion rot = QQuaternion::rotationTo(QVector3D(0.0f, 0.0f, 1.0f), @@ -2132,34 +2168,33 @@ void EditorScene::handleSelectionTransformChange() m_dragHandlesTransform->translation()); m_meshCenterIndicatorLineTransform->setScale(meshCenter.length()); } - - // Signal UI to reposition drag handles - emit beginDragHandlesRepositioning(); - emit repositionDragHandle(DragTranslate, translatePoint, - m_dragHandlesTransform->isEnabled() - ? m_dragHandleTranslateTransform->isEnabled() - && translateHandlePos.z() > 0.0f : false, 0, - translateHandlePos.z()); - emit repositionDragHandle(DragTranslate, centerPoint, - m_dragHandlesTransform->isEnabled() - ? m_dragHandleTranslateTransform->isEnabled() - && itemCenterHandlePos.z() > 0.0f && showCenterHandle : false, - 1, itemCenterHandlePos.z()); - emit repositionDragHandle(DragRotate, QPoint(rotateHandlePos.x(), rotateHandlePos.y()), + } + // Signal UI to reposition drag handles + emit beginDragHandlesRepositioning(); + emit repositionDragHandle(DragTranslate, translatePoint, + m_dragHandlesTransform->isEnabled() + ? m_dragHandleTranslateTransform->isEnabled() + && translateHandlePos.z() > 0.0f : false, 0, + translateHandlePos.z()); + emit repositionDragHandle(DragTranslate, centerPoint, + m_dragHandlesTransform->isEnabled() + ? m_dragHandleTranslateTransform->isEnabled() + && itemCenterHandlePos.z() > 0.0f && showCenterHandle : false, + 1, itemCenterHandlePos.z()); + emit repositionDragHandle(DragRotate, QPoint(rotateHandlePos.x(), rotateHandlePos.y()), + m_dragHandlesTransform->isEnabled() + ? m_dragHandleRotateTransform->isEnabled() + && rotateHandlePos.z() > 0.0f : false, 0, rotateHandlePos.z()); + for (int i = 0; i < dragCornerHandleCount; i++) { + emit repositionDragHandle(DragScale, + QPoint(cornerHandlePositions[i].x(), + cornerHandlePositions[i].y()), m_dragHandlesTransform->isEnabled() - ? m_dragHandleRotateTransform->isEnabled() - && rotateHandlePos.z() > 0.0f : false, 0, rotateHandlePos.z()); - for (int i = 0; i < dragCornerHandleCount; i++) { - emit repositionDragHandle(DragScale, - QPoint(cornerHandlePositions[i].x(), - cornerHandlePositions[i].y()), - m_dragHandlesTransform->isEnabled() - ? m_dragHandleScaleTransforms.at(0)->isEnabled() - && cornerHandlePositions[i].z() > 0.0f : false, i, - cornerHandlePositions[i].z()); - } - emit endDragHandlesRepositioning(); + ? m_dragHandleScaleTransforms.at(0)->isEnabled() + && cornerHandlePositions[i].z() > 0.0f : false, i, + cornerHandlePositions[i].z()); } + emit endDragHandlesRepositioning(); } void EditorScene::handlePickerPress(Qt3DRender::QPickEvent *event) @@ -2235,12 +2270,8 @@ bool EditorScene::handleMousePress(QMouseEvent *event) { m_previousMousePosition = event->pos(); m_mouseButton = event->button(); - if (m_mouseButton == Qt::LeftButton) { - m_multiSelect = event->modifiers() & Qt::ControlModifier; - // Clear multiselection list if m_multiSelect is false - if (!m_multiSelect) - m_selectedEntityNameList.clear(); - } + if (m_mouseButton == Qt::LeftButton) + m_ctrlDownOnLastLeftPress = event->modifiers() & Qt::ControlModifier; cancelDrag(); return false; // Never consume press event } diff --git a/editorlib/src/editorscene.h b/editorlib/src/editorscene.h index b525c49..c1c0aa0 100644 --- a/editorlib/src/editorscene.h +++ b/editorlib/src/editorscene.h @@ -72,7 +72,8 @@ class EditorScene : public QObject Q_OBJECT Q_PROPERTY(EditorSceneItemModel *sceneModel READ sceneModel CONSTANT) Q_PROPERTY(Qt3DCore::QEntity *selection READ selection WRITE setSelection NOTIFY selectionChanged) - Q_PROPERTY(QStringList multiSelection READ multiSelection WRITE setMultiSelection NOTIFY multiSelectionChanged) + Q_PROPERTY(QStringList multiSelectionList READ multiSelectionList NOTIFY multiSelectionListChanged) + Q_PROPERTY(bool multiSelection READ multiSelection NOTIFY multiSelectionChanged) Q_PROPERTY(QString error READ error NOTIFY errorChanged) Q_PROPERTY(int activeSceneCameraIndex READ activeSceneCameraIndex WRITE setActiveSceneCameraIndex NOTIFY activeSceneCameraIndexChanged) Q_PROPERTY(EditorViewportItem *viewport READ viewport WRITE setViewport NOTIFY viewportChanged) @@ -236,10 +237,14 @@ public: Q_INVOKABLE void dragHandleMove(const QPoint &pos, bool shiftDown, bool ctrlDown, bool altDown); Q_INVOKABLE void dragHandleRelease(); Q_INVOKABLE QString sceneRootName() const { return m_sceneEntity->objectName(); } - Q_INVOKABLE QString previousSelectedEntityName() const; - Q_INVOKABLE void addToMultiSelection(const QString &name); + Q_INVOKABLE void toggleEntityMultiSelection(const QString &name); + Q_INVOKABLE void clearMultiSelection(); Q_INVOKABLE QVector3D getMultiSelectionCenter(); + void removeEntityFromMultiSelection(const QString &name); + void addEntityToMultiSelection(const QString &name); + void renameEntityInMultiSelectionList(const QString &oldName, const QString &newName); + ClipboardOperation clipboardOperation() { return m_clipboardOperation; } void setClipboardOperation(ClipboardOperation operation); @@ -256,8 +261,9 @@ public: void setSelection(Qt3DCore::QEntity *entity); Qt3DCore::QEntity *selection() const { return m_selectedEntity; } - void setMultiSelection(const QStringList &multiSelection); - QStringList multiSelection(); + QStringList multiSelectionList() { return m_selectedEntityNameList; } + + bool multiSelection() const { return m_selectedEntityNameList.size() > 0; } const QString &error() const { return m_errorString; } @@ -307,7 +313,8 @@ public slots: signals: void selectionChanged(Qt3DCore::QEntity *selection); - void multiSelectionChanged(QStringList multiSelection); + void multiSelectionListChanged(); + void multiSelectionChanged(bool multiSelection); void errorChanged(const QString &error); void freeViewChanged(bool enabled); void activeSceneCameraIndexChanged(int index); @@ -456,10 +463,9 @@ private: int m_gridSize; int m_duplicateCount; Qt3DCore::QEntity *m_previousDuplicate; - bool m_multiSelect; + bool m_ctrlDownOnLastLeftPress; QStringList m_selectedEntityNameList; Qt::MouseButton m_mouseButton; - Qt3DCore::QEntity *m_previousSelectedEntity; QString m_clipboardEntityName; ClipboardOperation m_clipboardOperation; diff --git a/editorlib/src/editorsceneitemmodel.cpp b/editorlib/src/editorsceneitemmodel.cpp index b1cb8dd..5956c44 100644 --- a/editorlib/src/editorsceneitemmodel.cpp +++ b/editorlib/src/editorsceneitemmodel.cpp @@ -260,13 +260,13 @@ QHash<int, QByteArray> EditorSceneItemModel::roleNames() const void EditorSceneItemModel::resetModel() { - beginResetModel(); + QAbstractItemModel::beginResetModel(); // Reconnect all entities disconnectEntity(m_scene->sceneEntityItem()->entity()); connectEntity(m_scene->sceneEntityItem()->entity()); - endResetModel(); + QAbstractItemModel::endResetModel(); // Restore TreeView branch expansions, since resetting the model will collapse the branches QModelIndexList expandedIndexList; @@ -274,8 +274,9 @@ void EditorSceneItemModel::resetModel() expandedIndexList.append(getModelIndexByName(entityName)); emit expandItems(expandedIndexList); - // Select scene root after reset - emit selectIndex(sceneEntityIndex()); + // Select scene root after reset, unless multiselecting + if (!m_scene->multiSelection()) + emit selectIndex(sceneEntityIndex()); } EditorSceneItem *EditorSceneItemModel::editorSceneItemFromIndex(const QModelIndex &index) const @@ -464,11 +465,7 @@ const QString EditorSceneItemModel::setEntityName(const QModelIndex &index, cons setData(index, finalName, NameRole); - QStringList multiSelection = m_scene->multiSelection(); - if (multiSelection.removeOne(oldName)) { - multiSelection.append(finalName); - m_scene->setMultiSelection(multiSelection); - } + m_scene->renameEntityInMultiSelectionList(oldName, finalName); if (oldName == m_scene->clipboardContent()) m_scene->setClipboardContent(name); @@ -537,11 +534,13 @@ bool EditorSceneItemModel::canReparent(EditorSceneItem *newParentItem, EditorSceneItem *movedItem, bool allowSameParent) { // Dropping into camera is invalid. + // Reparenting camera is invalid. // Dropping into same parent is invalid. // Dropping item into its descendant is invalid. // If allowSameParent is true, "reparenting" under the same parent is allowed. This is useful // for entity tree copy & paste functionality. - bool reparentOk = !EditorUtils::entityCameraLens(newParentItem->entity()) + bool reparentOk = newParentItem->itemType() != EditorSceneItem::Camera + && movedItem->itemType() != EditorSceneItem::Camera && movedItem != newParentItem && !EditorUtils::isDescendant(movedItem, newParentItem); if (!allowSameParent) diff --git a/editorlib/src/undohandler/resetentitycommand.cpp b/editorlib/src/undohandler/resetentitycommand.cpp index e581488..536f15e 100644 --- a/editorlib/src/undohandler/resetentitycommand.cpp +++ b/editorlib/src/undohandler/resetentitycommand.cpp @@ -62,11 +62,6 @@ void ResetEntityCommand::undo() m_sceneModel->insertExistingEntity(m_removedEntity, m_row, parentIndex); m_removedEntity = nullptr; - if (m_type == EditorUtils::InsertableEntities::LightEntity - || m_type == EditorUtils::InsertableEntities::CameraEntity) { - m_sceneModel->resetModel(); - } - emit m_sceneModel->selectIndex(m_sceneModel->getModelIndexByName(m_entityName)); } } diff --git a/editorlib/src/undohandler/undohandler.cpp b/editorlib/src/undohandler/undohandler.cpp index 59adf79..9d6a270 100644 --- a/editorlib/src/undohandler/undohandler.cpp +++ b/editorlib/src/undohandler/undohandler.cpp @@ -42,6 +42,7 @@ #include "importentitycommand.h" #include "resetentitycommand.h" #include "resettransformcommand.h" +#include "editorsceneitem.h" #include <QtWidgets/QUndoStack> @@ -235,7 +236,14 @@ void UndoHandler::createReparentEntityCommand(const QString &newParentName, if (newParentName.isEmpty() || entityName.isEmpty()) return; - m_undoStack->push(new ReparentEntityCommand(m_scene->sceneModel(), newParentName, entityName)); + // Don't create commands for illegal reparentings + EditorSceneItem *newParentItem = m_scene->sceneModel()->getItemByName(newParentName); + EditorSceneItem *entityItem = m_scene->sceneModel()->getItemByName(entityName); + + if (m_scene->sceneModel()->canReparent(newParentItem, entityItem)) { + m_undoStack->push(new ReparentEntityCommand(m_scene->sceneModel(), newParentName, + entityName)); + } } void UndoHandler::createImportEntityCommand(const QUrl &url) |