Merge pull request #3081 from aws-lumberyard-dev/cgalvan/StyledTreeView

Moved custom tree view dragging logic from Entity Outliner to common class so it could be re-used for the UI Editor tree view.
This commit is contained in:
Chris Galvan
2021-08-12 15:21:49 -05:00
committed by GitHub
8 changed files with 177 additions and 168 deletions
@@ -20,7 +20,7 @@
#include <QMouseEvent>
OutlinerTreeView::OutlinerTreeView(QWidget* pParent)
: QTreeView(pParent)
: AzQtComponents::StyledTreeView(pParent)
, m_queuedMouseEvent(nullptr)
, m_draggingUnselectedItem(false)
{
@@ -135,16 +135,12 @@ void OutlinerTreeView::startDrag(Qt::DropActions supportedActions)
if (!selectionModel()->isSelected(index))
{
startCustomDrag({ index }, supportedActions);
StartCustomDrag({ index }, supportedActions);
return;
}
}
if (!selectionModel()->selectedIndexes().empty())
{
startCustomDrag(selectionModel()->selectedIndexes(), supportedActions);
return;
}
StyledTreeView::startDrag(supportedActions);
}
void OutlinerTreeView::dragMoveEvent(QDragMoveEvent* event)
@@ -336,14 +332,14 @@ void OutlinerTreeView::processQueuedMousePressedEvent(QMouseEvent* event)
QTreeView::mousePressEvent(&mousePressedEvent);
}
void OutlinerTreeView::startCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions)
void OutlinerTreeView::StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions)
{
m_draggingUnselectedItem = true;
//sort by container entity depth and order in hierarchy for proper drag image and drop order
QModelIndexList indexListSorted = indexList;
AZStd::unordered_map<AZ::EntityId, AZStd::list<AZ::u64>> locations;
for (auto index : indexListSorted)
for (const auto& index : indexListSorted)
{
AZ::EntityId entityId(index.data(OutlinerListModel::EntityIdRole).value<AZ::u64>());
AzToolsFramework::GetEntityLocationInHierarchy(entityId, locations[entityId]);
@@ -356,74 +352,7 @@ void OutlinerTreeView::startCustomDrag(const QModelIndexList& indexList, Qt::Dro
return AZStd::lexicographical_compare(locationsE1.begin(), locationsE1.end(), locationsE2.begin(), locationsE2.end());
});
//get the data for the unselected item(s)
QMimeData* mimeData = model()->mimeData(indexListSorted);
if (mimeData)
{
//initiate drag/drop for the item
QDrag* drag = new QDrag(this);
drag->setPixmap(QPixmap::fromImage(createDragImage(indexListSorted)));
drag->setMimeData(mimeData);
Qt::DropAction defDropAction = Qt::IgnoreAction;
if (defaultDropAction() != Qt::IgnoreAction && (supportedActions & defaultDropAction()))
{
defDropAction = defaultDropAction();
}
else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove)
{
defDropAction = Qt::CopyAction;
}
drag->exec(supportedActions, defDropAction);
}
}
QImage OutlinerTreeView::createDragImage(const QModelIndexList& indexList)
{
//generate a drag image of the item icon and text, normally done internally, and inaccessible
QRect rect(0, 0, 0, 0);
for (auto index : indexList)
{
if (index.column() != 0)
{
continue;
}
QRect itemRect = visualRect(index);
rect.setHeight(rect.height() + itemRect.height());
rect.setWidth(AZStd::GetMax(rect.width(), itemRect.width()));
}
QImage dragImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
QPainter dragPainter(&dragImage);
dragPainter.setCompositionMode(QPainter::CompositionMode_Source);
dragPainter.fillRect(dragImage.rect(), Qt::transparent);
dragPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
dragPainter.setOpacity(0.35f);
dragPainter.fillRect(rect, QColor("#222222"));
dragPainter.setOpacity(1.0f);
int imageY = 0;
for (auto index : indexList)
{
if (index.column() != 0)
{
continue;
}
QRect itemRect = visualRect(index);
dragPainter.drawPixmap(QPoint(0, imageY),
model()->data(index, Qt::DecorationRole).value<QIcon>().pixmap(QSize(16, 16)));
dragPainter.setPen(
model()->data(index, Qt::ForegroundRole).value<QBrush>().color());
dragPainter.setFont(
font());
dragPainter.drawText(QRect(20, imageY, rect.width() - 20, rect.height()),
model()->data(index, Qt::DisplayRole).value<QString>());
imageY += itemRect.height();
}
dragPainter.end();
return dragImage;
StyledTreeView::StartCustomDrag(indexListSorted, supportedActions);
}
#include <UI/Outliner/moc_OutlinerTreeView.cpp>
@@ -15,7 +15,8 @@
#include <QBasicTimer>
#include <QEvent>
#include <QTreeView>
#include <AzQtComponents/Components/Widgets/TreeView.h>
#endif
#pragma once
@@ -31,7 +32,7 @@ class OutlinerTreeViewModel;
//! allow for dragging and dropping of entities from the outliner into the property editor
//! of other entities. If the selection updates instantly, this would never be possible.
class OutlinerTreeView
: public QTreeView
: public AzQtComponents::StyledTreeView
{
Q_OBJECT;
public:
@@ -66,9 +67,7 @@ private:
void processQueuedMousePressedEvent(QMouseEvent* event);
void startCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions);
QImage createDragImage(const QModelIndexList& indexList);
void StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions) override;
void DrawLayerUI(QPainter* painter, const QRect& rect, const QModelIndex& index) const;
@@ -8,10 +8,13 @@
#include <AzQtComponents/Components/Widgets/TreeView.h>
#include <QDrag>
#include <QEvent>
#include <QSettings>
#include <QPainter>
#include <AzCore/std/algorithm.h>
#include <AzQtComponents/Components/Style.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/ConfigHelpers.h>
@@ -252,5 +255,109 @@ namespace AzQtComponents
return qobject_cast<QTreeView*>(widget) && !qobject_cast<TableView*>(widget);
}
StyledTreeView::StyledTreeView(QWidget* parent)
: QTreeView(parent)
{
}
void StyledTreeView::startDrag(Qt::DropActions supportedActions)
{
if (!selectionModel()->selectedIndexes().empty())
{
StartCustomDrag(selectionModel()->selectedIndexes(), supportedActions);
}
}
void StyledTreeView::StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions)
{
StartCustomDragInternal(this, indexList, supportedActions);
}
void StyledTreeView::StartCustomDragInternal(QAbstractItemView* itemView, const QModelIndexList& indexList, Qt::DropActions supportedActions)
{
QMimeData* mimeData = itemView->model()->mimeData(indexList);
if (mimeData)
{
QDrag* drag = new QDrag(itemView);
drag->setPixmap(QPixmap::fromImage(CreateDragImage(itemView, indexList)));
drag->setMimeData(mimeData);
Qt::DropAction defDropAction = Qt::IgnoreAction;
if (itemView->defaultDropAction() != Qt::IgnoreAction && (supportedActions & itemView->defaultDropAction()))
{
defDropAction = itemView->defaultDropAction();
}
else if (supportedActions & Qt::CopyAction && itemView->dragDropMode() != QAbstractItemView::InternalMove)
{
defDropAction = Qt::CopyAction;
}
drag->exec(supportedActions, defDropAction);
}
}
QImage StyledTreeView::CreateDragImage(QAbstractItemView* itemView, const QModelIndexList& indexList)
{
// Generate a drag image of the item icon and text, normally done internally, and inaccessible
QRect rect(0, 0, 0, 0);
for (const auto& index : indexList)
{
if (index.column() != 0)
{
continue;
}
QRect itemRect = itemView->visualRect(index);
rect.setHeight(rect.height() + itemRect.height());
rect.setWidth(AZStd::GetMax(rect.width(), itemRect.width()));
}
QImage dragImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
QPainter dragPainter(&dragImage);
dragPainter.setCompositionMode(QPainter::CompositionMode_Source);
dragPainter.fillRect(dragImage.rect(), Qt::transparent);
dragPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
dragPainter.setOpacity(0.35f);
dragPainter.fillRect(rect, QColor("#222222"));
dragPainter.setOpacity(1.0f);
int imageY = 0;
for (const auto& index : indexList)
{
if (index.column() != 0)
{
continue;
}
QRect itemRect = itemView->visualRect(index);
dragPainter.drawPixmap(QPoint(0, imageY),
itemView->model()->data(index, Qt::DecorationRole).value<QIcon>().pixmap(QSize(16, 16)));
dragPainter.setPen(
itemView->model()->data(index, Qt::ForegroundRole).value<QBrush>().color());
dragPainter.setFont(
itemView->font());
dragPainter.drawText(QRect(20, imageY, rect.width() - 20, rect.height()),
itemView->model()->data(index, Qt::DisplayRole).value<QString>());
imageY += itemRect.height();
}
dragPainter.end();
return dragImage;
}
StyledTreeWidget::StyledTreeWidget(QWidget* parent)
: QTreeWidget(parent)
{
}
void StyledTreeWidget::startDrag(Qt::DropActions supportedActions)
{
if (!selectionModel()->selectedIndexes().empty())
{
StyledTreeView::StartCustomDragInternal(this, selectionModel()->selectedIndexes(), supportedActions);
}
}
} // namespace AzQtComponents
#include <Components/Widgets/moc_TreeView.cpp>
@@ -9,8 +9,11 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <AzQtComponents/AzQtComponentsAPI.h>
#include <AzQtComponents/Components/Widgets/TableView.h>
#include <QTreeWidget>
#endif
namespace AzQtComponents
@@ -68,4 +71,46 @@ namespace AzQtComponents
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};
//! For most of the custom QTreeView styling, we override in AzQtComponents::Style class,
//! but there are some cases (e.g. drag/drop) that can only be overriden by an actual
//! subclass of the QTreeView
class AZ_QT_COMPONENTS_API StyledTreeView
: public QTreeView
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(StyledTreeView, AZ::SystemAllocator, 0);
explicit StyledTreeView(QWidget* parent = nullptr);
//! NOTE: QTreeWidget derives from QTreeView, but because we need a custom dervied class
//! of QTreeView, then we can't inherit our custom drag methods in our custom derived
//! class of QTreeWidget, so these functions are made static so they can be shared
static void StartCustomDragInternal(QAbstractItemView* itemView, const QModelIndexList& indexList, Qt::DropActions supportedActions);
static QImage CreateDragImage(QAbstractItemView* itemView, const QModelIndexList& indexList);
protected:
void startDrag(Qt::DropActions supportedActions) override;
virtual void StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions);
};
//! For most of the custom QTreeWidget styling, we override in AzQtComponents::Style class,
//! but there are some cases (e.g. drag/drop) that can only be overriden by an actual
//! subclass of the QTreeWidget.
class AZ_QT_COMPONENTS_API StyledTreeWidget
: public QTreeWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(StyledTreeWidget, AZ::SystemAllocator, 0);
explicit StyledTreeWidget(QWidget* parent = nullptr);
protected:
void startDrag(Qt::DropActions supportedActions) override;
};
} // namespace AzQtComponents
@@ -27,7 +27,7 @@
namespace AzToolsFramework
{
EntityOutlinerTreeView::EntityOutlinerTreeView(QWidget* pParent)
: QTreeView(pParent)
: AzQtComponents::StyledTreeView(pParent)
, m_queuedMouseEvent(nullptr)
, m_draggingUnselectedItem(false)
{
@@ -144,16 +144,12 @@ namespace AzToolsFramework
if (!selectionModel()->isSelected(index))
{
startCustomDrag({ index }, supportedActions);
StartCustomDrag({ index }, supportedActions);
return;
}
}
if (!selectionModel()->selectedIndexes().empty())
{
startCustomDrag(selectionModel()->selectedIndexes(), supportedActions);
return;
}
StyledTreeView::startDrag(supportedActions);
}
void EntityOutlinerTreeView::dragMoveEvent(QDragMoveEvent* event)
@@ -243,14 +239,14 @@ namespace AzToolsFramework
QTreeView::mousePressEvent(&mousePressedEvent);
}
void EntityOutlinerTreeView::startCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions)
void EntityOutlinerTreeView::StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions)
{
m_draggingUnselectedItem = true;
//sort by container entity depth and order in hierarchy for proper drag image and drop order
QModelIndexList indexListSorted = indexList;
AZStd::unordered_map<AZ::EntityId, AZStd::list<AZ::u64>> locations;
for (auto index : indexListSorted)
for (const auto& index : indexListSorted)
{
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
AzToolsFramework::GetEntityLocationInHierarchy(entityId, locations[entityId]);
@@ -263,76 +259,8 @@ namespace AzToolsFramework
return AZStd::lexicographical_compare(locationsE1.begin(), locationsE1.end(), locationsE2.begin(), locationsE2.end());
});
//get the data for the unselected item(s)
QMimeData* mimeData = model()->mimeData(indexListSorted);
if (mimeData)
{
//initiate drag/drop for the item
QDrag* drag = new QDrag(this);
drag->setPixmap(QPixmap::fromImage(createDragImage(indexListSorted)));
drag->setMimeData(mimeData);
Qt::DropAction defDropAction = Qt::IgnoreAction;
if (defaultDropAction() != Qt::IgnoreAction && (supportedActions & defaultDropAction()))
{
defDropAction = defaultDropAction();
}
else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove)
{
defDropAction = Qt::CopyAction;
}
drag->exec(supportedActions, defDropAction);
}
StyledTreeView::StartCustomDrag(indexListSorted, supportedActions);
}
QImage EntityOutlinerTreeView::createDragImage(const QModelIndexList& indexList)
{
//generate a drag image of the item icon and text, normally done internally, and inaccessible
QRect rect(0, 0, 0, 0);
for (auto index : indexList)
{
if (index.column() != 0)
{
continue;
}
QRect itemRect = visualRect(index);
rect.setHeight(rect.height() + itemRect.height());
rect.setWidth(AZStd::GetMax(rect.width(), itemRect.width()));
}
QImage dragImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
QPainter dragPainter(&dragImage);
dragPainter.setCompositionMode(QPainter::CompositionMode_Source);
dragPainter.fillRect(dragImage.rect(), Qt::transparent);
dragPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
dragPainter.setOpacity(0.35f);
dragPainter.fillRect(rect, QColor("#222222"));
dragPainter.setOpacity(1.0f);
int imageY = 0;
for (auto index : indexList)
{
if (index.column() != 0)
{
continue;
}
QRect itemRect = visualRect(index);
dragPainter.drawPixmap(QPoint(0, imageY),
model()->data(index, Qt::DecorationRole).value<QIcon>().pixmap(QSize(16, 16)));
dragPainter.setPen(
model()->data(index, Qt::ForegroundRole).value<QBrush>().color());
dragPainter.setFont(
font());
dragPainter.drawText(QRect(20, imageY, rect.width() - 20, rect.height()),
model()->data(index, Qt::DisplayRole).value<QString>());
imageY += itemRect.height();
}
dragPainter.end();
return dragImage;
}
}
#include <UI/Outliner/moc_EntityOutlinerTreeView.cpp>
@@ -14,7 +14,8 @@
#include <QBasicTimer>
#include <QEvent>
#include <QTreeView>
#include <AzQtComponents/Components/Widgets/TreeView.h>
#endif
#pragma once
@@ -33,7 +34,7 @@ namespace AzToolsFramework
//! allow for dragging and dropping of entities from the outliner into the property editor
//! of other entities. If the selection updates instantly, this would never be possible.
class EntityOutlinerTreeView
: public QTreeView
: public AzQtComponents::StyledTreeView
{
Q_OBJECT;
public:
@@ -68,9 +69,7 @@ namespace AzToolsFramework
void processQueuedMousePressedEvent(QMouseEvent* event);
void startCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions);
QImage createDragImage(const QModelIndexList& indexList);
void StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions) override;
void PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const;
+2 -2
View File
@@ -21,7 +21,7 @@
#include <QDragEnterEvent>
HierarchyWidget::HierarchyWidget(EditorWindow* editorWindow)
: QTreeWidget()
: AzQtComponents::StyledTreeWidget()
, m_isDeleting(false)
, m_editorWindow(editorWindow)
, m_entityItemMap()
@@ -391,7 +391,7 @@ void HierarchyWidget::startDrag(Qt::DropActions supportedActions)
// Remember the current selection so that we can revert back to it when the items are dragged back into the hierarchy
m_dragSelection = selectedItems();
QTreeView::startDrag(supportedActions);
AzQtComponents::StyledTreeWidget::startDrag(supportedActions);
}
void HierarchyWidget::dragEnterEvent(QDragEnterEvent* event)
+3 -1
View File
@@ -10,6 +10,8 @@
#if !defined(Q_MOC_RUN)
#include "EditorCommon.h"
#include <AzQtComponents/Components/Widgets/TreeView.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ToolsMessaging/EntityHighlightBus.h>
@@ -19,7 +21,7 @@
class QMimeData;
class HierarchyWidget
: public QTreeWidget
: public AzQtComponents::StyledTreeWidget
, private AzToolsFramework::EditorPickModeNotificationBus::Handler
, private AzToolsFramework::EntityHighlightMessages::Bus::Handler
{