From 77d7e220dafbf8a76e26a0dbe87c7fbe9bf5b07b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 21 Nov 2017 15:59:19 +0200 Subject: Fix slide manipulation undo/redo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now slide add, delete, rearrange, duplicate, and rename can be undone and redone. Task-number: QT3DS-480 Change-Id: I1256c954b0a478e095d3431c8b61e9c41aed5895 Reviewed-by: Tomi Korpipää --- src/Authoring/Studio/Palettes/Slide/SlideModel.cpp | 174 +++++++++++++++++---- src/Authoring/Studio/Palettes/Slide/SlideModel.h | 16 +- src/Authoring/Studio/Palettes/Slide/SlideView.cpp | 41 +++-- src/Authoring/Studio/Palettes/Slide/SlideView.h | 2 + src/Authoring/Studio/Palettes/Slide/SlideView.qml | 12 +- 5 files changed, 197 insertions(+), 48 deletions(-) diff --git a/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp b/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp index f97c621c..f31ae51f 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp +++ b/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp @@ -32,7 +32,7 @@ #include "Core.h" #include "Doc.h" #include "StudioApp.h" - +#include "SlideSystem.h" #include "IDocumentEditor.h" #include "ClientDataModelBridge.h" @@ -42,7 +42,6 @@ SlideModel::SlideModel(int slideCount, QObject *parent) : QAbstractListModel(parent) , m_slides(slideCount) { - } QVariant SlideModel::data(const QModelIndex &index, int role) const @@ -77,6 +76,9 @@ bool SlideModel::setData(const QModelIndex &index, const QVariant &value, int ro } case HandleRole: { slideHandle = value.value(); + qt3dsdm::Qt3DSDMInstanceHandle instanceHandle + = GetDoc()->GetStudioSystem()->GetSlideSystem()->GetSlideInstance(slideHandle); + m_slideLookupHash.insert(instanceHandle, slideHandle); Q_EMIT dataChanged(index, index, {HandleRole, NameRole}); break; } @@ -121,7 +123,7 @@ bool SlideModel::insertRows(int row, int count, const QModelIndex &parent) return false; beginInsertRows(parent, row, row + count - 1); - for (int i = 0; i < count; i++) + for (int i = 0; i < count; ++i) m_slides.insert(row, {}); endInsertRows(); @@ -136,7 +138,7 @@ bool SlideModel::removeRows(int row, int count, const QModelIndex &parent) bool selectionRemoved = false; beginRemoveRows(parent, row, row + count - 1); - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; ++i) { if (m_selectedRow == row) selectionRemoved = true; m_slides.removeAt(row); @@ -161,15 +163,14 @@ bool SlideModel::removeRows(int row, int count, const QModelIndex &parent) void SlideModel::duplicateRow(int row) { const auto handle = m_slides[row]; + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Duplicate Slide")) + ->DuplicateSlide(handle); +} - beginInsertRows({}, row, row); - m_slides.insert(row + 1, Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), - QObject::tr("Duplicate Slide")) - ->DuplicateSlide(handle)); - endInsertRows(); - setData(index(row + 1), true, SelectedRole); - - Q_EMIT dataChanged(index(row, 0), index(row + 1, 0), {}); +void SlideModel::startRearrange(int row) +{ + m_rearrangeStartRow = row; + m_rearrangeEndRow = -1; } void SlideModel::move(int fromRow, int toRow) @@ -177,40 +178,44 @@ void SlideModel::move(int fromRow, int toRow) if (fromRow == toRow) return; - auto handle = m_slides[fromRow]; - // toRow + 1 as DocumentEditor uses 1 based indexes for slides - Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Rearrange Slide")) - ->RearrangeSlide(handle, toRow + 1); + onSlideRearranged({}, fromRow + 1, toRow + 1); +} - if (fromRow > toRow) - beginMoveRows({}, fromRow, fromRow, {}, toRow); - else - beginMoveRows({}, fromRow, fromRow, {}, toRow + 1); - m_slides.move(fromRow, toRow); - endMoveRows(); +void SlideModel::finishRearrange(bool commit) +{ + if (m_rearrangeEndRow != m_rearrangeStartRow + && m_rearrangeEndRow >= 0 && m_rearrangeStartRow >= 0) { + // Restore state before committing the actual change + // +1 added as DocumentEditor uses 1 based indexes for slides + int endRow = m_rearrangeEndRow + 1; + onSlideRearranged({}, endRow, m_rearrangeStartRow + 1); + + if (commit) { + auto handle = m_slides[m_rearrangeStartRow]; + m_rearrangeStartRow = -1; + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Rearrange Slide")) + ->RearrangeSlide(handle, endRow); + } + } + m_rearrangeEndRow = -1; + m_rearrangeStartRow = -1; } void SlideModel::clear() { beginResetModel(); m_slides.clear(); + m_slideLookupHash.clear(); endResetModel(); } void SlideModel::addNewSlide(int row) { const auto handle = (row < m_slides.size()) ? m_slides[row] : m_slides.last(); - const auto instanceHandle = GetBridge()->GetOwningComponentInstance(handle); qt3dsdm::Qt3DSDMSlideHandle theMasterSlide = GetBridge()->GetComponentSlide(instanceHandle, 0); - - beginInsertRows({}, row, row); - m_slides.insert(row, Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), - QObject::tr("Create Slide")) - ->AddSlide(theMasterSlide, row + 1)); - endInsertRows(); - - setData(index(row), true, SelectedRole); + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Create Slide")) + ->AddSlide(theMasterSlide, row + 1); } void SlideModel::removeSlide(int row) @@ -218,13 +223,94 @@ void SlideModel::removeSlide(int row) // Don't allow deleting of the last slide if (m_slides.size() > 1) { const auto handle = m_slides[row]; - Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Delete Slide"))->DeleteSlide( handle); - removeRows(row, 1); } } +void SlideModel::onNewSlide(const qt3dsdm::Qt3DSDMSlideHandle &inSlide) +{ + qt3dsdm::ISlideSystem &theSlideSystem(*GetDoc()->GetStudioSystem()->GetSlideSystem()); + + // Ignore new slides added to random different components + if (m_slides.size() && theSlideSystem.GetMasterSlide(inSlide) + != theSlideSystem.GetMasterSlide(m_slides[0])) { + return; + } + + finishRearrange(false); // Cancel any uncommitted rearrange + + // Find the slide index + int row = int(slideIndex(inSlide)); + + // Slide index zero indicates master slide. We can't add master slides + Q_ASSERT(row > 0); + + --row; + + beginInsertRows({}, row, row); + m_slides.insert(row, inSlide); + qt3dsdm::Qt3DSDMInstanceHandle instanceHandle + = GetDoc()->GetStudioSystem()->GetSlideSystem()->GetSlideInstance(inSlide); + m_slideLookupHash.insert(instanceHandle, inSlide); + endInsertRows(); + + setData(index(row), true, SelectedRole); +} + +void SlideModel::onDeleteSlide(const qt3dsdm::Qt3DSDMSlideHandle &inSlide) +{ + for (int i = 0; i < m_slides.size(); ++i) { + if (m_slides[i] == inSlide) { + if (m_rearrangeStartRow >= 0) { + finishRearrange(false); // Cancel any uncommitted rearrange + // We need to re-resolve the index after rearrange cancel + for (int j = 0; j < m_slides.size(); ++j) { + if (m_slides[j] == inSlide) { + i = j; + break; + } + } + } + QList keys = m_slideLookupHash.keys(inSlide); + for (auto key : keys) + m_slideLookupHash.remove(key); + removeRows(i, 1); + break; + } + } +} + +void SlideModel::onSlideRearranged(const qt3dsdm::Qt3DSDMSlideHandle &inMaster, int fromRow, + int toRow) +{ + if (inMaster.Valid()) { + // If imMaster is valid, this was triggered by either a rearrange commit or + // undo/redo of a rearrange, so we need to cancel any uncommitted rearrange. + finishRearrange(false); + // Check we are working on correct slide set + qt3dsdm::ISlideSystem &theSlideSystem(*GetDoc()->GetStudioSystem()->GetSlideSystem()); + if (fromRow - 1 >= m_slides.size() + || inMaster != theSlideSystem.GetMasterSlide(m_slides[fromRow - 1])) + return; + } else { + // Do not do uncommitted rearranges if we haven't started a rearrange (or more likely + // an uncommitted rearrange was canceled while in progress by undo/redo operation) + if (m_rearrangeStartRow < 0) + return; + } + + // -1 because internal slide model has 1-based indexing for non-master slides + if (fromRow > toRow) + beginMoveRows({}, fromRow - 1, fromRow - 1, {}, toRow - 1); + else + beginMoveRows({}, fromRow - 1, fromRow - 1, {}, toRow); + m_slides.move(fromRow - 1, toRow - 1); + m_rearrangeEndRow = toRow - 1; + + endMoveRows(); +} + bool SlideModel::hasSlideWithName(const QString &name) const { for (const auto &slide: m_slides) { @@ -266,6 +352,11 @@ CDoc *SlideModel::GetDoc() const return g_StudioApp.GetCore()->GetDoc(); } +long SlideModel::slideIndex(const qt3dsdm::Qt3DSDMSlideHandle &handle) +{ + return GetDoc()->GetStudioSystem()->GetSlideSystem()->GetSlideIndex(handle); +} + CClientDataModelBridge *SlideModel::GetBridge() const { auto doc = GetDoc(); @@ -274,4 +365,21 @@ CClientDataModelBridge *SlideModel::GetBridge() const return doc->GetStudioSystem()->GetClientDataModelBridge(); } +void SlideModel::refreshSlideLabel(qt3dsdm::Qt3DSDMInstanceHandle instanceHandle, + qt3dsdm::Qt3DSDMPropertyHandle propertyHandle) +{ + if (m_slideLookupHash.contains(instanceHandle) + && propertyHandle == GetBridge()->GetNameProperty()) { + qt3dsdm::Qt3DSDMSlideHandle slideHandle = m_slideLookupHash.value(instanceHandle); + for (int i = 0; i < m_slides.size(); ++i) { + if (m_slides[i] == slideHandle) { + setData(index(i, 0), GetBridge()->GetName(instanceHandle).toQString(), + SlideModel::NameRole); + } + } + } + +} + + diff --git a/src/Authoring/Studio/Palettes/Slide/SlideModel.h b/src/Authoring/Studio/Palettes/Slide/SlideModel.h index b5da9670..da44c1c7 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideModel.h +++ b/src/Authoring/Studio/Palettes/Slide/SlideModel.h @@ -29,7 +29,8 @@ #ifndef SLIDEMODEL_H #define SLIDEMODEL_H -#include +#include +#include #include "Qt3DSDMHandles.h" @@ -60,21 +61,34 @@ public: bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; void duplicateRow(int row); + void startRearrange(int row); void move(int fromRow, int toRow); + void finishRearrange(bool commit); void clear(); void addNewSlide(int row); void removeSlide(int row); + void onNewSlide(const qt3dsdm::Qt3DSDMSlideHandle &inSlide); + void onDeleteSlide(const qt3dsdm::Qt3DSDMSlideHandle &inSlide); + void onSlideRearranged(const qt3dsdm::Qt3DSDMSlideHandle &inMaster, int inOldIndex, + int toRow); + void refreshSlideLabel(qt3dsdm::Qt3DSDMInstanceHandle instanceHandle, + qt3dsdm::Qt3DSDMPropertyHandle propertyHandle); + private: bool hasSlideWithName(const QString &name) const; QString slideName(const qt3dsdm::Qt3DSDMSlideHandle &handle) const; void setSlideName(const qt3dsdm::Qt3DSDMSlideHandle &handle, const QString &name); inline CDoc *GetDoc() const; + inline long slideIndex(const qt3dsdm::Qt3DSDMSlideHandle &handle); inline CClientDataModelBridge *GetBridge() const; QVector m_slides; int m_selectedRow = -1; + int m_rearrangeStartRow = -1; + int m_rearrangeEndRow = -1; + QHash m_slideLookupHash; }; diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.cpp b/src/Authoring/Studio/Palettes/Slide/SlideView.cpp index eb803874..c6c188f3 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideView.cpp +++ b/src/Authoring/Studio/Palettes/Slide/SlideView.cpp @@ -136,11 +136,21 @@ void SlideView::duplicateSlide(int row) m_SlidesModel->duplicateRow(row); } +void SlideView::startSlideRearrange(int row) +{ + m_SlidesModel->startRearrange(row); +} + void SlideView::moveSlide(int from, int to) { m_SlidesModel->move(from, to); } +void SlideView::finishSlideRearrange(bool commit) +{ + m_SlidesModel->finishRearrange(commit); +} + void SlideView::showContextMenu(int x, int y, int row) { SlideContextMenu contextMenu(this, row, m_SlidesModel->rowCount(), @@ -159,9 +169,7 @@ void SlideView::OnNewPresentation() std::bind(&SlideView::OnActiveSlide, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))); - // KDAB_TODO We most probably don't need to listen to the below signals, - // as the functionality is done in the model already. Remove after it is confirmed - // it works as desired when the rendering works. + // Needed for undo/redo functionality to work properly m_Connections.push_back(theSignalProvider->ConnectSlideCreated( std::bind(&SlideView::OnNewSlide, this, std::placeholders::_1))); m_Connections.push_back(theSignalProvider->ConnectSlideDeleted( @@ -169,6 +177,12 @@ void SlideView::OnNewPresentation() m_Connections.push_back(theSignalProvider->ConnectSlideRearranged( std::bind(&SlideView::OnSlideRearranged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))); + + // Set up listener for the name changes to slide + m_Connections.push_back(theSignalProvider->ConnectInstancePropertyValue( + std::bind(&SlideModel::refreshSlideLabel, m_SlidesModel, + std::placeholders::_1, std::placeholders::_2))); + } void SlideView::OnClosingPresentation() @@ -180,26 +194,29 @@ void SlideView::OnClosingPresentation() void SlideView::OnActiveSlide(const qt3dsdm::Qt3DSDMSlideHandle &inMaster, int inIndex, const qt3dsdm::Qt3DSDMSlideHandle &inSlide) { - // When the active slide changes, we need to update our button and mode - if (inMaster.Valid()) { - // if inIndex is 0, it means that we are activating master slide - setShowMasterSlide(inIndex == 0); - setActiveSlide(inSlide); - } + // Don't use inIndex because inIndex might have been changed due to deletion + Q_UNUSED(inIndex); + Q_UNUSED(inMaster); + + qt3dsdm::ISlideSystem &theSlideSystem(*GetDoc()->GetStudioSystem()->GetSlideSystem()); + setShowMasterSlide(theSlideSystem.GetSlideIndex(inSlide) == 0); + setActiveSlide(inSlide); } void SlideView::OnNewSlide(const qt3dsdm::Qt3DSDMSlideHandle &inSlide) { - + m_SlidesModel->onNewSlide(inSlide); } void SlideView::OnDeleteSlide(const qt3dsdm::Qt3DSDMSlideHandle &inSlide) { - + m_SlidesModel->onDeleteSlide(inSlide); } -void SlideView::OnSlideRearranged(const qt3dsdm::Qt3DSDMSlideHandle &inMaster, int inOldIndex, int inNewIndex) +void SlideView::OnSlideRearranged(const qt3dsdm::Qt3DSDMSlideHandle &inMaster, int inOldIndex, + int inNewIndex) { + m_SlidesModel->onSlideRearranged(inMaster, inOldIndex, inNewIndex); } void SlideView::initialize() diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.h b/src/Authoring/Studio/Palettes/Slide/SlideView.h index d818c745..b8f6d01d 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideView.h +++ b/src/Authoring/Studio/Palettes/Slide/SlideView.h @@ -62,7 +62,9 @@ public: Q_INVOKABLE void addNewSlide(int row); Q_INVOKABLE void removeSlide(int row); Q_INVOKABLE void duplicateSlide(int row); + Q_INVOKABLE void startSlideRearrange(int row); Q_INVOKABLE void moveSlide(int from, int to); + Q_INVOKABLE void finishSlideRearrange(bool commit); Q_INVOKABLE void showContextMenu(int x, int y, int row); // Presentation Change Listener diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.qml b/src/Authoring/Studio/Palettes/Slide/SlideView.qml index 4fb00eb3..1d9e5cc7 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideView.qml +++ b/src/Authoring/Studio/Palettes/Slide/SlideView.qml @@ -151,14 +151,22 @@ Rectangle { drag.target: held ? delegateItem : null drag.axis: Drag.YAxis - onPressed: { dragIndex = model.index; + _slideView.startSlideRearrange(model.index); if (mouse.x > delegateItem.x && mouse.x < delegateItem.x + delegateItem.width) held = true; } - onReleased: held = false + onReleased: { + held = false; + _slideView.finishSlideRearrange(true); + } + + onCanceled: { + held = false; + _slideView.finishSlideRearrange(false); + } onClicked: { _slideView.deselectAll(); -- cgit v1.2.3