Support for reordering strings with drag & drop

This commit is contained in:
Aruki 2019-01-02 19:03:41 -07:00
parent cb262504af
commit 4714c6ccf5
8 changed files with 189 additions and 23 deletions

View File

@ -1,4 +1,5 @@
#include "CStringTable.h"
#include <Common/Math/MathUtil.h>
#include <algorithm>
#include <iterator>
@ -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<NewIndex; i++)
mStringNames[i] = mStringNames[i+1];
if (MaxIndex >= mStringNames.size())
{
mStringNames.resize(MaxIndex + 1);
}
TString Name = mStringNames[StringIndex];
if (NewIndex > StringIndex)
{
for (uint i=StringIndex; i<NewIndex; i++)
mStringNames[i] = mStringNames[i+1];
}
else
{
for (uint i=StringIndex; i>NewIndex; 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 */

View File

@ -206,7 +206,8 @@ HEADERS += \
CCustomDelegate.h \
CTweakEditor.h \
Undo/CEditIntrinsicPropertyCommand.h \
Undo/TSerializeUndoCommand.h
Undo/TSerializeUndoCommand.h \
StringEditor/CStringMimeData.h
# Source Files
SOURCES += \

View File

@ -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<CStringTable>("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;

View File

@ -64,6 +64,7 @@ public slots:
void OnStringTextEdited();
void OnAddString();
void OnRemoveString();
void OnMoveString(int StringIndex, int NewIndex);
void IncrementStringIndex();
void DecrementStringIndex();

View File

@ -57,7 +57,7 @@
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>

View File

@ -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 <QMimeData>
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<const CStringMimeData*>(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<const CStringMimeData*>(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;
}

View File

@ -5,9 +5,14 @@
#include <Core/Resource/TResPtr.h>
#include <Core/Resource/StringTable/CStringTable.h>
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<CStringTable> 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

View File

@ -0,0 +1,25 @@
#ifndef CSTRINGMIMEDATA_H
#define CSTRINGMIMEDATA_H
#include <QMimeData>
#include <Common/Common.h>
/** 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