diff --git a/src/Core/Resource/StringTable/CStringTable.cpp b/src/Core/Resource/StringTable/CStringTable.cpp index 4eef865b..3c4f5d2c 100644 --- a/src/Core/Resource/StringTable/CStringTable.cpp +++ b/src/Core/Resource/StringTable/CStringTable.cpp @@ -1,4 +1,5 @@ #include "CStringTable.h" +#include #include #include @@ -88,6 +89,10 @@ void CStringTable::SetStringName(uint StringIndex, const TString& kNewName) } mStringNames[StringIndex] = kNewName; + + // Strip empty string names + while (mStringNames.back().IsEmpty()) + mStringNames.pop_back(); } /** Move string to another position in the table */ @@ -100,7 +105,7 @@ void CStringTable::MoveString(uint StringIndex, uint NewIndex) return; // Update string data - for (uint LanguageIdx = 0; LanguageIdx < StringIndex; LanguageIdx++) + for (uint LanguageIdx = 0; LanguageIdx < mLanguages.size(); LanguageIdx++) { SLanguageData& Language = mLanguages[LanguageIdx]; TString String = Language.Strings[StringIndex]; @@ -120,19 +125,34 @@ void CStringTable::MoveString(uint StringIndex, uint NewIndex) } // Update string name - TString Name = mStringNames[StringIndex]; + uint MinIndex = Math::Min(StringIndex, NewIndex); + uint MaxIndex = Math::Max(StringIndex, NewIndex); - if (NewIndex > StringIndex) + if (MinIndex < mStringNames.size()) { - for (uint i=StringIndex; i= mStringNames.size()) + { + mStringNames.resize(MaxIndex + 1); + } + + TString Name = mStringNames[StringIndex]; + + if (NewIndex > StringIndex) + { + for (uint i=StringIndex; iNewIndex; i--) + mStringNames[i] = mStringNames[i-1]; + } + mStringNames[NewIndex] = Name; + + // Strip empty string names + while (mStringNames.back().IsEmpty()) + mStringNames.pop_back(); } - else - { - for (uint i=StringIndex; i>NewIndex; i--) - mStringNames[i] = mStringNames[i-1]; - } - mStringNames[NewIndex] = Name; } /** Add a new string to the table */ diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index 9dc4d407..4af9bb56 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -206,7 +206,8 @@ HEADERS += \ CCustomDelegate.h \ CTweakEditor.h \ Undo/CEditIntrinsicPropertyCommand.h \ - Undo/TSerializeUndoCommand.h + Undo/TSerializeUndoCommand.h \ + StringEditor/CStringMimeData.h # Source Files SOURCES += \ diff --git a/src/Editor/StringEditor/CStringEditor.cpp b/src/Editor/StringEditor/CStringEditor.cpp index 1172284d..ad5d3490 100644 --- a/src/Editor/StringEditor/CStringEditor.cpp +++ b/src/Editor/StringEditor/CStringEditor.cpp @@ -18,7 +18,7 @@ class CSetStringIndexCommand : public IUndoCommand int mOldIndex, mNewIndex; public: CSetStringIndexCommand(CStringEditor* pEditor, int OldIndex, int NewIndex) - : mpEditor(pEditor), mOldIndex(OldIndex), mNewIndex(NewIndex) + : IUndoCommand("Select String"), mpEditor(pEditor), mOldIndex(OldIndex), mNewIndex(NewIndex) {} virtual void undo() override { mpEditor->SetActiveString(mOldIndex); } @@ -32,7 +32,7 @@ class CSetLanguageCommand : public IUndoCommand ELanguage mOldLanguage, mNewLanguage; public: CSetLanguageCommand(CStringEditor* pEditor, ELanguage OldLanguage, ELanguage NewLanguage) - : mpEditor(pEditor), mOldLanguage(OldLanguage), mNewLanguage(NewLanguage) + : IUndoCommand("Select Language"), mpEditor(pEditor), mOldLanguage(OldLanguage), mNewLanguage(NewLanguage) {} virtual void undo() override { mpEditor->SetActiveLanguage(mOldLanguage); } @@ -85,7 +85,7 @@ bool CStringEditor::eventFilter(QObject* pWatched, QEvent* pEvent) void CStringEditor::InitUI() { mpUI->setupUi(this); - mpListModel = new CStringListModel(mpStringTable, this); + mpListModel = new CStringListModel(this); mpUI->StringNameListView->setModel(mpListModel); mpUI->StringNameListView->setItemDelegate( new CStringDelegate(this) ); mpUI->AddStringButton->setShortcut( QKeySequence("Alt+=") ); @@ -117,6 +117,9 @@ void CStringEditor::InitUI() connect( mpUI->AddStringButton, SIGNAL(pressed()), this, SLOT(OnAddString()) ); connect( mpUI->RemoveStringButton, SIGNAL(pressed()), this, SLOT(OnRemoveString()) ); + connect( mpListModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(OnRowsMoved(QModelIndex,int,int,QModelIndex,int)) ); + connect( &UndoStack(), SIGNAL(indexChanged(int)), this, SLOT(UpdateUI()) ); mpUI->ToolBar->addSeparator(); @@ -209,20 +212,22 @@ void CStringEditor::UpdateUI() } // Update selection in string list - QModelIndex OldStringIndex = mpUI->StringNameListView->selectionModel()->hasSelection() ? - mpUI->StringNameListView->currentIndex() : QModelIndex(); + QItemSelectionModel* pSelectionModel = mpUI->StringNameListView->selectionModel(); + + QModelIndex OldStringIndex = pSelectionModel->hasSelection() ? + pSelectionModel->currentIndex() : QModelIndex(); QModelIndex NewStringIndex = mpUI->StringNameListView->model()->index(mCurrentStringIndex,0); if (OldStringIndex != NewStringIndex) { - QItemSelectionModel* pSelectionModel = mpUI->StringNameListView->selectionModel(); pSelectionModel->blockSignals(true); pSelectionModel->setCurrentIndex(NewStringIndex, QItemSelectionModel::ClearAndSelect); pSelectionModel->blockSignals(false); mpUI->StringNameListView->update(OldStringIndex); } mpUI->StringNameListView->update(NewStringIndex); + mpUI->RemoveStringButton->setEnabled( pSelectionModel->hasSelection() ); // Update language tabs uint LanguageIndex = mpUI->EditLanguageTabBar->currentIndex(); @@ -352,6 +357,26 @@ void CStringEditor::OnRemoveString() } } +void CStringEditor::OnMoveString(int StringIndex, int NewIndex) +{ + if (StringIndex != NewIndex) + { + ASSERT( StringIndex >= 0 && StringIndex < (int) mpStringTable->NumStrings() ); + ASSERT( NewIndex >= 0 && NewIndex < (int) mpStringTable->NumStrings() ); + UndoStack().beginMacro("Move String"); + + // Move string + IUndoCommand* pCommand = new TSerializeUndoCommand("Move String", mpStringTable, true); + mpStringTable->MoveString(StringIndex, NewIndex); + UndoStack().push(pCommand); + + // Select new string index + pCommand = new CSetStringIndexCommand(this, StringIndex, NewIndex); + UndoStack().push(pCommand); + UndoStack().endMacro(); + } +} + void CStringEditor::IncrementStringIndex() { uint NewIndex = mCurrentStringIndex + 1; diff --git a/src/Editor/StringEditor/CStringEditor.h b/src/Editor/StringEditor/CStringEditor.h index 2e546d4a..5752b149 100644 --- a/src/Editor/StringEditor/CStringEditor.h +++ b/src/Editor/StringEditor/CStringEditor.h @@ -64,6 +64,7 @@ public slots: void OnStringTextEdited(); void OnAddString(); void OnRemoveString(); + void OnMoveString(int StringIndex, int NewIndex); void IncrementStringIndex(); void DecrementStringIndex(); diff --git a/src/Editor/StringEditor/CStringEditor.ui b/src/Editor/StringEditor/CStringEditor.ui index fee88a1a..d4e89a32 100644 --- a/src/Editor/StringEditor/CStringEditor.ui +++ b/src/Editor/StringEditor/CStringEditor.ui @@ -57,7 +57,7 @@ true - QAbstractItemView::ExtendedSelection + QAbstractItemView::SingleSelection QAbstractItemView::SelectRows diff --git a/src/Editor/StringEditor/CStringListModel.cpp b/src/Editor/StringEditor/CStringListModel.cpp index 8f0bf39d..97810d95 100644 --- a/src/Editor/StringEditor/CStringListModel.cpp +++ b/src/Editor/StringEditor/CStringListModel.cpp @@ -1,9 +1,14 @@ #include "CStringListModel.h" +#include "CStringEditor.h" +#include "CStringMimeData.h" #include "Editor/UICommon.h" -CStringListModel::CStringListModel(CStringTable* pInStrings, QObject* pParent /*= 0*/) - : QAbstractListModel(pParent) - , mpStringTable(pInStrings) +#include + +CStringListModel::CStringListModel(CStringEditor* pInEditor) + : QAbstractListModel(pInEditor) + , mpEditor(pInEditor) + , mpStringTable(pInEditor->StringTable()) , mStringPreviewLanguage(ELanguage::English) { } @@ -67,3 +72,79 @@ QVariant CStringListModel::data(const QModelIndex& kIndex, int Role) const return QVariant::Invalid; } } + +Qt::ItemFlags CStringListModel::flags(const QModelIndex& kIndex) const +{ + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +} + +/** Drag & Drop support */ +Qt::DropActions CStringListModel::supportedDragActions() const +{ + return Qt::MoveAction; +} + +Qt::DropActions CStringListModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QMimeData* CStringListModel::mimeData(const QModelIndexList& kIndexes) const +{ + // We don't support drag&drop on multiple strings at once + ASSERT( kIndexes.size() == 1 ); + QModelIndex Index = kIndexes.front(); + return new CStringMimeData(mpStringTable->ID(), Index.row()); +} + +bool CStringListModel::canDropMimeData(const QMimeData* pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& kParent) const +{ + // Only allow dropping string mime data that originated from our string table + const CStringMimeData* pkStringMimeData = qobject_cast(pkData); + return Action == Qt::MoveAction && pkStringMimeData != nullptr && pkStringMimeData->AssetID() == mpStringTable->ID(); +} + +bool CStringListModel::dropMimeData(const QMimeData* pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& kParent) +{ + debugf("Dropped onto row %d column %d", Row, Column); + + if (Action == Qt::MoveAction) + { + const CStringMimeData* pkStringMimeData = qobject_cast(pkData); + + if (pkStringMimeData && pkStringMimeData->AssetID() == mpStringTable->ID()) + { + // Determine new row index. If the string was dropped in between items, we can place + // it in between those two items. Otherwise, if it was dropped on top of an item, we + // want to bump it in place of that item (so use the item's index). + if (Row == -1) + { + Row = kParent.row(); + + // In some cases Row can still be -1 at this point if the parent is invalid + // It seems like this can happen when trying to place at the top of the list + // So to account for it, in this case, reset Row back to 0 + if (Row == -1) + { + Row = 0; + } + } + // If the user placed the string at the end of the list, then the index we receive + // will be out of range, so cap it to a valid index. + else if (Row >= (int) mpStringTable->NumStrings()) + { + Row = mpStringTable->NumStrings() - 1; + } + // If the string is being moved further down the list, then account for the fact that + // the rest of the strings below it will be bumped up. + else if (Row > (int) pkStringMimeData->StringIndex()) + { + Row--; + } + + mpEditor->OnMoveString(pkStringMimeData->StringIndex(), Row); + return true; + } + } + return false; +} diff --git a/src/Editor/StringEditor/CStringListModel.h b/src/Editor/StringEditor/CStringListModel.h index 4200c2dd..0e22508f 100644 --- a/src/Editor/StringEditor/CStringListModel.h +++ b/src/Editor/StringEditor/CStringListModel.h @@ -5,9 +5,14 @@ #include #include +class CStringEditor; + /** Model for listing available strings in a string table */ class CStringListModel : public QAbstractListModel { + /** String editor that owns the model */ + CStringEditor* mpEditor; + /** Asset to pull the strings from */ TResPtr mpStringTable; @@ -15,7 +20,7 @@ class CStringListModel : public QAbstractListModel ELanguage mStringPreviewLanguage; public: - CStringListModel(CStringTable* pInStrings, QObject* pParent = 0); + CStringListModel(CStringEditor* pInEditor); /** Change the preview language display */ void SetPreviewLanguage(ELanguage InLanguage); @@ -23,6 +28,14 @@ public: /** QAbstractListModel interface */ virtual int rowCount(const QModelIndex& kParent) const override; virtual QVariant data(const QModelIndex& kIndex, int Role) const override; + virtual Qt::ItemFlags flags(const QModelIndex& kIndex) const override; + + /** Drag & Drop support */ + virtual Qt::DropActions supportedDragActions() const override; + virtual Qt::DropActions supportedDropActions() const override; + virtual QMimeData* mimeData(const QModelIndexList& kIndexes) const override; + virtual bool canDropMimeData(const QMimeData* pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& kParent) const override; + virtual bool dropMimeData(const QMimeData* pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& kParent) override; }; #endif // CSTRINGLISTMODEL_H diff --git a/src/Editor/StringEditor/CStringMimeData.h b/src/Editor/StringEditor/CStringMimeData.h new file mode 100644 index 00000000..6d648f56 --- /dev/null +++ b/src/Editor/StringEditor/CStringMimeData.h @@ -0,0 +1,25 @@ +#ifndef CSTRINGMIMEDATA_H +#define CSTRINGMIMEDATA_H + +#include +#include + +/** Mime data encoding a reference to a string for drag&drop support in string editor */ +class CStringMimeData : public QMimeData +{ + Q_OBJECT + CAssetID mAssetID; + uint mStringIndex; + +public: + CStringMimeData(CAssetID AssetID, uint StringIndex) + : mAssetID(AssetID), mStringIndex(StringIndex) + {} + + virtual bool hasFormat(const QString& kMimeType) const override { return true; } + + inline CAssetID AssetID() const { return mAssetID; } + inline uint StringIndex() const { return mStringIndex; } +}; + +#endif // CSTRINGMIMEDATA_H