Added drag/drop support to directory tree view

This commit is contained in:
Aruki 2017-07-25 18:34:02 -06:00
parent ca40c26154
commit 18482cbae6
15 changed files with 5331 additions and 5075 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -94,7 +94,7 @@ bool CEditorApplication::OpenProject(const QString& rkProjPath)
}
else
{
UICommon::ErrorMsg(mpWorldEditor, "Failed to open project! Is it already open in another Prime World Editor instance?");
UICommon::ErrorMsg(mpWorldEditor, "Failed to open project!");
return false;
}
}

View File

@ -195,7 +195,8 @@ HEADERS += \
Undo/CRenameResourceCommand.h \
Undo/CRenameDirectoryCommand.h \
CFileNameValidator.h \
Undo/ICreateDeleteDirectoryCommand.h
Undo/ICreateDeleteDirectoryCommand.h \
ResourceBrowser/CVirtualDirectoryTreeView.h
# Source Files
SOURCES += \
@ -268,7 +269,8 @@ SOURCES += \
ResourceBrowser/CResourceTableContextMenu.cpp \
ResourceBrowser/CResourceTableModel.cpp \
ResourceBrowser/CResourceTableView.cpp \
ResourceBrowser/CVirtualDirectoryModel.cpp
ResourceBrowser/CVirtualDirectoryModel.cpp \
ResourceBrowser/CVirtualDirectoryTreeView.cpp
# UI Files
FORMS += \

View File

@ -327,19 +327,35 @@ bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources,
{
// Check for any conflicts
QList<CResourceEntry*> ConflictingResources;
QList<CResourceEntry*> ValidResources;
foreach (CResourceEntry *pEntry, rkResources)
{
if (pNewDir->FindChildResource(pEntry->Name(), pEntry->ResourceType()) != nullptr)
CResourceEntry *pConflict = pNewDir->FindChildResource(pEntry->Name(), pEntry->ResourceType());
if (pConflict != pEntry)
{
if (pConflict != nullptr)
ConflictingResources << pEntry;
else
ValidResources << pEntry;
}
}
QList<CVirtualDirectory*> ConflictingDirs;
QList<CVirtualDirectory*> ValidDirs;
foreach (CVirtualDirectory *pDir, rkDirectories)
{
if (pNewDir->FindChildDirectory(pDir->Name(), false) != nullptr)
CVirtualDirectory *pConflict = pNewDir->FindChildDirectory(pDir->Name(), false);
if (pConflict != pDir)
{
if (pConflict != nullptr)
ConflictingDirs << pDir;
else
ValidDirs << pDir;
}
}
// If there were conflicts, notify the user of them
@ -362,15 +378,19 @@ bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources,
}
// Create undo actions to actually perform the moves
if (!ValidResources.isEmpty() || !ValidDirs.isEmpty())
{
mUndoStack.beginMacro("Move Resources");
foreach (CVirtualDirectory *pDir, rkDirectories)
foreach (CVirtualDirectory *pDir, ValidDirs)
mUndoStack.push( new CMoveDirectoryCommand(mpStore, pDir, pNewDir) );
foreach (CResourceEntry *pEntry, rkResources)
foreach (CResourceEntry *pEntry, ValidResources)
mUndoStack.push( new CMoveResourceCommand(pEntry, pNewDir) );
mUndoStack.endMacro();
}
return true;
}

View File

@ -255,7 +255,7 @@
</widget>
</item>
<item>
<widget class="QTreeView" name="DirectoryTreeView">
<widget class="CVirtualDirectoryTreeView" name="DirectoryTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
@ -268,7 +268,19 @@
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
<set>QAbstractItemView::EditKeyPressed</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
@ -285,6 +297,9 @@
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="autoExpandDelay">
<number>-1</number>
</property>
<property name="indentation">
<number>12</number>
</property>
@ -311,7 +326,7 @@
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
<set>QAbstractItemView::EditKeyPressed</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
@ -362,6 +377,11 @@
<extends>QTableView</extends>
<header>Editor/ResourceBrowser/CResourceTableView.h</header>
</customwidget>
<customwidget>
<class>CVirtualDirectoryTreeView</class>
<extends>QTreeView</extends>
<header>Editor/ResourceBrowser/CVirtualDirectoryTreeView.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../Icons.qrc"/>

View File

@ -24,6 +24,12 @@ public:
mEntries << pEntry;
}
CResourceMimeData(CVirtualDirectory *pDir)
: QMimeData()
{
mDirectories << pDir;
}
const QList<CResourceEntry*>& Resources() const { return mEntries; }
const QList<CVirtualDirectory*>& Directories() const { return mDirectories; }
};

View File

@ -62,26 +62,34 @@ QVariant CResourceTableModel::data(const QModelIndex& rkIndex, int Role) const
Qt::ItemFlags CResourceTableModel::flags(const QModelIndex& rkIndex) const
{
Qt::ItemFlags Out = Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable;
Qt::ItemFlags Out = Qt::ItemIsSelectable |Qt::ItemIsEnabled;
CVirtualDirectory *pDir = IndexDirectory(rkIndex);
if (IsIndexDirectory(rkIndex))
if (pDir)
Out |= Qt::ItemIsDropEnabled;
if (!pDir || mpCurrentDir->Parent() != pDir)
Out |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
return Out;
}
bool CResourceTableModel::canDropMimeData(const QMimeData *pkData, Qt::DropAction, int Row, int Column, const QModelIndex& rkParent) const
{
// Don't allow dropping between items.
if (Row != -1 || Column != -1)
return false;
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pkData);
if (pkMimeData)
{
// Make sure we're dropping onto a directory
QModelIndex Index = (rkParent.isValid() ? rkParent : index(Row, Column, rkParent));
// Don't allow dropping onto blank space in asset list mode
if (!rkParent.isValid() && mIsAssetListMode)
return false;
if (Index.isValid())
{
CVirtualDirectory *pDir = IndexDirectory(Index);
// Make sure we're dropping onto a directory
CVirtualDirectory *pDir = (rkParent.isValid() ? IndexDirectory(rkParent) : mpCurrentDir);
if (pDir)
{
@ -96,8 +104,6 @@ bool CResourceTableModel::canDropMimeData(const QMimeData *pkData, Qt::DropActio
return true;
}
}
else return false;
}
return false;
}
@ -108,12 +114,10 @@ bool CResourceTableModel::dropMimeData(const QMimeData *pkData, Qt::DropAction A
if (canDropMimeData(pkData, Action, Row, Column, rkParent))
{
QModelIndex Index = (rkParent.isValid() ? rkParent : index(Row, Column, rkParent));
CVirtualDirectory *pDir = IndexDirectory(Index);
CVirtualDirectory *pDir = (rkParent.isValid() ? IndexDirectory(rkParent) : mpCurrentDir);
ASSERT(pDir);
gpEdApp->ResourceBrowser()->MoveResources( pkMimeData->Resources(), pkMimeData->Directories(), pDir );
return true;
return gpEdApp->ResourceBrowser()->MoveResources( pkMimeData->Resources(), pkMimeData->Directories(), pDir );
}
else return false;
}
@ -298,7 +302,7 @@ void CResourceTableModel::CheckAddDirectory(CVirtualDirectory *pDir)
void CResourceTableModel::CheckRemoveDirectory(CVirtualDirectory *pDir)
{
if (pDir->Parent() == mpCurrentDir)
if (pDir->Parent() != mpCurrentDir)
{
QModelIndex Index = GetIndexForDirectory(pDir);

View File

@ -7,17 +7,13 @@
CResourceTableView::CResourceTableView(QWidget *pParent /*= 0*/)
: QTableView(pParent)
{
// Create "rename" key shortcut
// todo - there's no QKeySequence::Rename. Is there another standard "rename" shortcut on other platforms?
mpRenameAction = new QAction(this);
mpRenameAction->setShortcut( QKeySequence(Qt::Key_F2) );
connect(mpRenameAction, SIGNAL(triggered(bool)), this, SLOT(RenameSelected()));
addAction(mpRenameAction);
// todo: removed delete shortcut because it conflicts with the World Editor delete shortcut
#if 0
mpDeleteAction = new QAction(this);
mpDeleteAction->setShortcut(QKeySequence::Delete);
connect(mpDeleteAction, SIGNAL(triggered(bool)), this, SLOT(DeleteSelected()));
addAction(mpDeleteAction);
#endif
}
void CResourceTableView::setModel(QAbstractItemModel *pModel)
@ -40,27 +36,7 @@ void CResourceTableView::dragEnterEvent(QDragEnterEvent *pEvent)
}
}
void CResourceTableView::focusInEvent(QFocusEvent*)
{
mpRenameAction->setEnabled(true);
}
void CResourceTableView::focusOutEvent(QFocusEvent*)
{
mpRenameAction->setEnabled(false);
}
// ************ SLOTS ************
void CResourceTableView::RenameSelected()
{
QModelIndexList List = selectionModel()->selectedIndexes();
if (List.size() == 1)
{
edit(List.front());
}
}
void CResourceTableView::DeleteSelected()
{
QModelIndexList List = selectionModel()->selectedIndexes();

View File

@ -11,19 +11,14 @@ class CResourceTableView : public QTableView
CResourceTableModel *mpModel;
CResourceProxyModel *mpProxy;
QAction *mpRenameAction;
QAction *mpDeleteAction;
public:
explicit CResourceTableView(QWidget *pParent = 0);
void setModel(QAbstractItemModel *pModel);
void dragEnterEvent(QDragEnterEvent *pEvent);
void focusInEvent(QFocusEvent*);
void focusOutEvent(QFocusEvent*);
public slots:
void RenameSelected();
void DeleteSelected();
};

View File

@ -1,11 +1,13 @@
#include "CVirtualDirectoryModel.h"
#include "CResourceBrowser.h"
#include "CResourceMimeData.h"
CVirtualDirectoryModel::CVirtualDirectoryModel(CResourceBrowser *pBrowser, QObject *pParent /*= 0*/)
: QAbstractItemModel(pParent)
, mpRoot(nullptr)
, mInsertingRows(false)
, mRemovingRows(false)
, mMovingRows(false)
, mChangingLayout(false)
{
connect(pBrowser, SIGNAL(DirectoryAboutToBeMoved(CVirtualDirectory*,QString)), this, SLOT(OnDirectoryAboutToBeMoved(CVirtualDirectory*,QString)));
@ -95,6 +97,102 @@ QVariant CVirtualDirectoryModel::data(const QModelIndex& rkIndex, int Role) cons
return QVariant::Invalid;
}
bool CVirtualDirectoryModel::setData(const QModelIndex& rkIndex, const QVariant& rkValue, int Role)
{
if (Role == Qt::EditRole)
{
QString NewName = rkValue.toString();
CVirtualDirectory *pDir = IndexDirectory(rkIndex);
if (pDir)
{
gpEdApp->ResourceBrowser()->RenameDirectory(pDir, TO_TSTRING(NewName));
return true;
}
}
return false;
}
Qt::ItemFlags CVirtualDirectoryModel::flags(const QModelIndex& rkIndex) const
{
Qt::ItemFlags Out = Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled;
if (rkIndex.isValid() && rkIndex.parent().isValid())
Out |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
return Out;
}
bool CVirtualDirectoryModel::canDropMimeData(const QMimeData *pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& rkParent) const
{
// Don't allow dropping between items
if (Row != -1 || Column != -1)
return false;
if (Action == Qt::MoveAction)
{
CVirtualDirectory *pDir = IndexDirectory(rkParent);
if (pDir)
{
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pkData);
if (pkMimeData)
{
// Don't allow moving a directory into one of its children
foreach (CVirtualDirectory *pMoveDir, pkMimeData->Directories())
{
if (pDir->IsDescendantOf(pMoveDir))
return false;
}
return true;
}
}
}
return false;
}
bool CVirtualDirectoryModel::dropMimeData(const QMimeData *pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& rkParent)
{
// Perform move!
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pkData);
if (canDropMimeData(pkData, Action, Row, Column, rkParent))
{
CVirtualDirectory *pDir = IndexDirectory(rkParent);
ASSERT(pDir);
return gpEdApp->ResourceBrowser()->MoveResources(pkMimeData->Resources(), pkMimeData->Directories(), pDir);
}
else return false;
}
QMimeData* CVirtualDirectoryModel::mimeData(const QModelIndexList& rkIndexes) const
{
if (rkIndexes.size() == 1)
{
QModelIndex Index = rkIndexes.front();
CVirtualDirectory *pDir = IndexDirectory(Index);
CResourceMimeData *pMimeData = new CResourceMimeData(pDir);
return pMimeData;
}
else
return nullptr;
}
Qt::DropActions CVirtualDirectoryModel::supportedDragActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::DropActions CVirtualDirectoryModel::supportedDropActions() const
{
return Qt::MoveAction;
}
QModelIndex CVirtualDirectoryModel::GetIndexForDirectory(CVirtualDirectory *pDir)
{
if (!pDir)
@ -184,11 +282,29 @@ bool CVirtualDirectoryModel::GetProposedIndex(QString Path, QModelIndex& rOutPar
return true;
}
void CVirtualDirectoryModel::OnDirectoryAboutToBeMoved(CVirtualDirectory *, QString )
void CVirtualDirectoryModel::OnDirectoryAboutToBeMoved(CVirtualDirectory *pDir, QString NewPath)
{
QModelIndex Parent;
int Row;
if (GetProposedIndex(NewPath, Parent, Row))
{
QModelIndex OldIndex = GetIndexForDirectory(pDir);
QModelIndex OldParent = OldIndex.parent();
int OldRow = OldIndex.row();
if (OldParent == Parent && (Row == OldRow || Row == OldRow + 1))
{
emit layoutAboutToBeChanged();
mChangingLayout = true;
}
else
{
beginMoveRows(OldParent, OldRow, OldRow, Parent, Row);
mMovingRows = true;
}
}
}
void CVirtualDirectoryModel::OnDirectoryAboutToBeCreated(QString DirPath)
{
@ -225,6 +341,11 @@ void CVirtualDirectoryModel::FinishModelChanges()
endRemoveRows();
mRemovingRows = false;
}
if (mMovingRows)
{
endMoveRows();
mMovingRows = false;
}
if (mChangingLayout)
{
emit layoutChanged();

View File

@ -12,6 +12,7 @@ class CVirtualDirectoryModel : public QAbstractItemModel
CVirtualDirectory *mpRoot;
bool mInsertingRows;
bool mRemovingRows;
bool mMovingRows;
bool mChangingLayout;
public:
@ -22,6 +23,14 @@ public:
int rowCount(const QModelIndex& rkParent) const;
int columnCount(const QModelIndex& /*rkParent*/) const;
QVariant data(const QModelIndex& rkIndex, int Role) const;
bool setData(const QModelIndex& rkIndex, const QVariant& rkValue, int Role);
Qt::ItemFlags flags(const QModelIndex& rkIndex) const;
bool canDropMimeData(const QMimeData *pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& rkParent) const;
bool dropMimeData(const QMimeData *pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& rkParent);
QMimeData* mimeData(const QModelIndexList& rkIndexes) const;
Qt::DropActions supportedDragActions() const;
Qt::DropActions supportedDropActions() const;
QModelIndex GetIndexForDirectory(CVirtualDirectory *pDir);
CVirtualDirectory* IndexDirectory(const QModelIndex& rkIndex) const;

View File

@ -0,0 +1,78 @@
#include "CVirtualDirectoryTreeView.h"
#include "CResourceBrowser.h"
#include "CVirtualDirectoryModel.h"
#include <QDragEnterEvent>
CVirtualDirectoryTreeView::CVirtualDirectoryTreeView(QWidget *pParent)
: QTreeView(pParent)
, mTransferSelectionPostMove(false)
{
// slightly hacky cuz there's not really a good way to pass in CResourceBrowser as a parameter
// due to the fact that we set this class in the .ui file
CResourceBrowser *pBrowser = nullptr;
QWidget *pWidget = pParent;
while (pWidget && !pBrowser)
{
pBrowser = qobject_cast<CResourceBrowser*>(pWidget);
pWidget = pWidget->parentWidget();
}
ASSERT(pBrowser);
connect(pBrowser, SIGNAL(DirectoryAboutToBeMoved(CVirtualDirectory*,QString)), this, SLOT(OnDirectoryAboutToBeMoved(CVirtualDirectory*)));
connect(pBrowser, SIGNAL(DirectoryMoved(CVirtualDirectory*,CVirtualDirectory*,TString)), this, SLOT(OnDirectoryMoved(CVirtualDirectory*)));
}
void CVirtualDirectoryTreeView::dragEnterEvent(QDragEnterEvent *pEvent)
{
// need to reimplement this to fix a bug in QAbstractItemView
if (dragDropMode() == QAbstractItemView::InternalMove &&
(pEvent->source() != this || ((pEvent->possibleActions() & Qt::MoveAction) == 0)) )
return;
if (pEvent->possibleActions() & model()->supportedDropActions())
{
pEvent->accept();
setState(QAbstractItemView::DraggingState);
}
}
void CVirtualDirectoryTreeView::setModel(QAbstractItemModel *pModel)
{
CVirtualDirectoryModel *pDirModel = qobject_cast<CVirtualDirectoryModel*>(pModel);
if (pDirModel)
{
mpModel = pDirModel;
QTreeView::setModel(pModel);
}
}
void CVirtualDirectoryTreeView::OnDirectoryAboutToBeMoved(CVirtualDirectory *pDir)
{
if (mpModel)
{
QModelIndex Index = mpModel->GetIndexForDirectory(pDir);
if (selectionModel()->currentIndex() == Index)
mTransferSelectionPostMove = true;
}
}
void CVirtualDirectoryTreeView::OnDirectoryMoved(CVirtualDirectory *pDir)
{
if (mTransferSelectionPostMove)
{
// Make sure the model has updated first
mpModel->FinishModelChanges();
QModelIndex Index = mpModel->GetIndexForDirectory(pDir);
blockSignals(true);
expand(Index.parent());
selectionModel()->setCurrentIndex(Index, QItemSelectionModel::ClearAndSelect);
blockSignals(false);
mTransferSelectionPostMove = false;
}
}

View File

@ -0,0 +1,25 @@
#ifndef CVIRTUALDIRECTORYTREEVIEW_H
#define CVIRTUALDIRECTORYTREEVIEW_H
#include <QTreeView>
#include "CVirtualDirectoryModel.h"
#include <Core/GameProject/CVirtualDirectory.h>
class CVirtualDirectoryTreeView : public QTreeView
{
Q_OBJECT
CVirtualDirectoryModel *mpModel;
bool mTransferSelectionPostMove;
public:
CVirtualDirectoryTreeView(QWidget *pParent = 0);
void dragEnterEvent(QDragEnterEvent *pEvent);
void setModel(QAbstractItemModel *pModel);
public slots:
void OnDirectoryAboutToBeMoved(CVirtualDirectory *pDir);
void OnDirectoryMoved(CVirtualDirectory *pDir);
};
#endif // CVIRTUALDIRECTORYTREEVIEW_H

View File

@ -34,7 +34,7 @@ protected:
CVirtualDirectory *pParent = mpStore->GetVirtualDirectory(rkPath, false);
ASSERT(pDir && pParent);
gpEdApp->ResourceBrowser()->DirectoryAboutToBeMoved(pDir, TO_QSTRING(rkPath));
gpEdApp->ResourceBrowser()->DirectoryAboutToBeMoved(pDir, TO_QSTRING(rkPath + pDir->Name()));
TString OldName = pDir->Name();
CVirtualDirectory *pOldParent = pDir->Parent();