Integrating up through commit 90f050496

This commit is contained in:
alexpete
2021-04-07 14:03:29 -07:00
parent 8f2ed080a9
commit c2cbd430fe
2694 changed files with 285622 additions and 176874 deletions
@@ -63,7 +63,12 @@ namespace AzToolsFramework
void EditorEntityUiHandlerBase::PaintDescendantBackground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/,
const QModelIndex& /*descendantIndex*/) const
{
}
}
void EditorEntityUiHandlerBase::PaintDescendantBranchBackground(QPainter* /*painter*/, const QTreeView* /*view*/, const QRect& /*rect*/,
const QModelIndex& /*index*/, const QModelIndex& /*descendantIndex*/) const
{
}
void EditorEntityUiHandlerBase::PaintItemForeground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const
{
@@ -20,6 +20,7 @@
#include <QStyleOptionViewItem>
class QPainter;
class QTreeView;
namespace AzToolsFramework
{
@@ -50,6 +51,9 @@ namespace AzToolsFramework
//! Paints the background of the descendants of the item in the Outliner.
virtual void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const;
//! Paints the background of the descendant branches of the item in the Outliner.
virtual void PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
const QModelIndex& index, const QModelIndex& descendantIndex) const;
//! Paints visual elements on the foreground of the item in the Outliner.
virtual void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
@@ -21,29 +21,17 @@
namespace AzToolsFramework
{
const int LayerUiHandler::m_layerSquareSize = 20;
const int LayerUiHandler::m_layerStripeWidth = 1;
const int LayerUiHandler::m_layerDividerLineHeight = 1;
const int LayerUiHandler::m_lastEntityInLayerDividerLineHeight = 1;
const QColor LayerUiHandler::m_layerBackgroundColor = QColor("#2F2F2F");
const QColor LayerUiHandler::m_layerDescendantBackgroundColor = QColor("#333333");
const QColor LayerUiHandler::m_layerBorderTopColor = QColor("#515151");
const QColor LayerUiHandler::m_layerBorderBottomColor = QColor("#252525");
const QString LayerUiHandler::m_layerIconPath = QString(":/Icons/layer_icon.svg");
const QString LayerUiHandler::m_layerIconPath = QString(":/Entity/layer.svg");
QString LayerUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
{
QString result;
bool isLayerEntity = false;
AZ::Outcome<AZStd::string, AZStd::string> layerBaseNameResult;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isLayerEntity,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (!isLayerEntity)
{
return result;
}
layerBaseNameResult, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerBaseFileName);
bool hasUnsavedLayerChanges = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
@@ -51,16 +39,28 @@ namespace AzToolsFramework
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasUnsavedChanges);
QString result = "<span style=\"font-style: italic; font-weight: 400;\">";
if (layerBaseNameResult.IsSuccess())
{
result += QString("(%1.layer").arg(layerBaseNameResult.GetValue().c_str());
}
if (hasUnsavedLayerChanges)
{
result = QObject::tr("*");
result += QString("*");
}
if (layerBaseNameResult.IsSuccess())
{
result += QString(")");
}
result += "</span>";
bool isLayerNameValid = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isLayerNameValid,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::IsLayerNameValid);
isLayerNameValid, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::IsLayerNameValid);
if (!isLayerNameValid)
{
@@ -75,22 +75,7 @@ namespace AzToolsFramework
return QPixmap(m_layerIconPath);
}
void LayerUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
{
QPainterPath backgroundPath;
backgroundPath.addRect(option.rect);
painter->fillPath(backgroundPath, m_layerBackgroundColor);
}
void LayerUiHandler::PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& /*index*/,
const QModelIndex& /*descendantIndex*/) const
{
QPainterPath backgroundPath;
backgroundPath.addRect(option.rect);
painter->fillPath(backgroundPath, m_layerDescendantBackgroundColor);
}
void LayerUiHandler::PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
void LayerUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
@@ -99,11 +84,18 @@ namespace AzToolsFramework
return;
}
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
// Dark Grey Background
QPainterPath backgroundPath;
backgroundPath.addRect(option.rect);
painter->fillPath(backgroundPath, m_layerBackgroundColor);
// Left rect with the layer color
QColor layerColor;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
layerColor,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerColor);
layerColor, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerColor);
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
int indentation = outlinerTreeView->indentation();
@@ -114,39 +106,31 @@ namespace AzToolsFramework
if (isFirstColumn)
{
// Layer colored box
QPainterPath layerIconPath;
QPainterPath iconBackgroundPath;
QPoint layerBoxOffset(0, (option.rect.height() - m_layerSquareSize) / 2);
QRect layerIconRect(option.rect.topLeft() + layerBoxOffset, QSize(m_layerSquareSize, m_layerSquareSize));
layerIconPath.addRect(layerIconRect);
painter->fillPath(layerIconPath, layerColor);
iconBackgroundPath.addRect(layerIconRect);
painter->fillPath(iconBackgroundPath, layerColor);
// Left border
PaintLayerStripeAndBorder(
painter,
option.rect.left(),
option.rect.top(),
option.rect.bottom(),
m_layerBorderBottomColor,
layerColor);
painter, option.rect.left() - 1, option.rect.top(), option.rect.bottom(), m_layerBorderBottomColor, layerColor);
}
QModelIndex nameColumn = index.sibling(index.row(), EntityOutlinerListModel::Column::ColumnName);
QModelIndex sibling = index.sibling(index.row() + 1, index.column());
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
QPoint lineBottomLeft(option.rect.bottomLeft());
QPoint lineTopLeft(option.rect.topLeft());
if (isFirstColumn)
{
lineTopLeft.setX(lineTopLeft.x() + m_layerStripeWidth);
lineTopLeft.setX(lineTopLeft.x() + m_layerStripeWidth - 1);
}
QPen topLinePen(m_layerBorderTopColor, m_layerDividerLineHeight);
painter->setPen(topLinePen);
painter->drawLine(lineTopLeft, option.rect.topRight());
if (isFirstColumn)
{
lineBottomLeft.setX(lineBottomLeft.x());
@@ -156,11 +140,43 @@ namespace AzToolsFramework
lineBottomLeft.setX(lineBottomLeft.x() + m_layerStripeWidth);
}
}
QPen bottomLinePen(m_layerBorderBottomColor, m_layerDividerLineHeight);
painter->setPen(bottomLinePen);
painter->drawLine(lineBottomLeft, option.rect.bottomRight());
painter->restore();
}
void LayerUiHandler::PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& /*index*/, const QModelIndex& /*descendantIndex*/) const
{
QPainterPath backgroundPath;
backgroundPath.addRect(option.rect);
painter->fillPath(backgroundPath, m_layerDescendantBackgroundColor);
}
void LayerUiHandler::PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
const QModelIndex& index, const QModelIndex& /*descendantIndex*/) const
{
if (!painter)
{
return;
}
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
QPainterPath backgroundPath;
const int ancestorLeft = view->visualRect(index).left();
// Find the rect that extends fully to the left
QRect fullRect = rect;
fullRect.setLeft(ancestorLeft);
backgroundPath.addRect(fullRect);
painter->fillPath(backgroundPath, m_layerDescendantBackgroundColor);
painter->restore();
}
@@ -182,7 +198,7 @@ namespace AzToolsFramework
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
bool isFirstColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnName;
bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
int ancestorLeft = outlinerTreeView->visualRect(index).left();
int ancestorLeft = outlinerTreeView->visualRect(index).left() - 1;
// Draw the left stripe for this layer on the descendant's rect
if (isFirstColumn)
@@ -32,7 +32,8 @@ namespace AzToolsFramework
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const override;
void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
const QModelIndex& index, const QModelIndex& descendantIndex) const override;
void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const override;
@@ -43,12 +44,16 @@ namespace AzToolsFramework
static QModelIndex GetLastVisibleChild(const QModelIndex& parent);
static QModelIndex Internal_GetLastVisibleChild(const QAbstractItemModel* model, const QModelIndex& index);
static const int m_layerSquareSize;
static const int m_layerStripeWidth;
static const int m_layerDividerLineHeight;
static const int m_lastEntityInLayerDividerLineHeight;
static constexpr int m_layerSquareSize = 22;
static constexpr int m_layerStripeWidth = 1;
static constexpr int m_layerDividerLineHeight = 1;
static constexpr int m_lastEntityInLayerDividerLineHeight = 1;
static const QColor m_layerBackgroundColor;
static const QColor m_layerBackgroundHoveredColor;
static const QColor m_layerBackgroundSelectedColor;
static const QColor m_layerDescendantBackgroundColor;
static const QColor m_layerDescendantBackgroundHoveredColor;
static const QColor m_layerDescendantBackgroundSelectedColor;
static const QColor m_layerBorderTopColor;
static const QColor m_layerBorderBottomColor;
static const QString m_layerIconPath;
@@ -44,11 +44,11 @@ AzToolsFramework--EntityOutlinerCheckBox
border: 0px solid transparent;
border-radius: 0px;
spacing: 0px;
padding: 0px;
padding: 0;
padding-right: 2px;
line-height: 0px;
font-size: 0px;
margin: 0px;
background-color: transparent;
max-height: 20px;
max-width: 18px;
}
@@ -58,14 +58,15 @@ AzToolsFramework--EntityOutlinerCheckBox::indicator
width: 16px;
height: 16px;
image-position:center;
border: 1px solid transparent;
image: none;
spacing: 0px;
padding: 0px;
line-height: 0px;
font-size: 0px;
margin: 0;
margin: 3px 0 0 0;
max-width: 18px;
width: 18px;
}
AzToolsFramework--EntityOutlinerCheckBox#VisibilityMixed::indicator:checked
@@ -83,7 +84,6 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityMixed::indicator:checked
{
background: rgba(0, 0, 0, 80);
border-radius: 5px;
padding-bottom: 1px;
}
@@ -99,6 +99,7 @@ AzToolsFramework--EntityOutlinerCheckBox#Visibility::indicator:unchecked
, AzToolsFramework--EntityOutlinerCheckBox#VisibilityMixed::indicator:unchecked
{
image: url(:/visibility_on.svg);
margin-top: 3px;
}
AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverridden::indicator:checked
@@ -109,6 +110,7 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverridden::indicator:checked
AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverridden::indicator:unchecked
{
image: url(:/visibility_on_transparent.svg);
margin-top: 3px;
}
AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:checked
@@ -116,6 +118,7 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:checked
, AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverriddenHover::indicator:checked
{
image: url(:/visibility_default_hover.svg);
margin-top: 3px;
}
AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:unchecked
@@ -123,6 +126,7 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:unchecked
, AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverriddenHover::indicator:unchecked
{
image: url(:/visibility_on_hover.svg);
margin-top: 3px;
}
@@ -132,6 +136,7 @@ AzToolsFramework--EntityOutlinerCheckBox#Lock::indicator:checked
, AzToolsFramework--EntityOutlinerCheckBox#LockMixed::indicator:checked
{
image: url(:/lock_on.svg);
margin-top: 1px;
}
AzToolsFramework--EntityOutlinerCheckBox#Lock::indicator:unchecked
@@ -148,6 +153,7 @@ AzToolsFramework--EntityOutlinerCheckBox#LockOverridden::indicator:checked
AzToolsFramework--EntityOutlinerCheckBox#LockOverridden::indicator:unchecked
{
image: url(:/lock_default_transparent.svg);
margin-top: 1px;
}
AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:checked
@@ -155,6 +161,7 @@ AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:checked
, AzToolsFramework--EntityOutlinerCheckBox#LockOverriddenHover::indicator:checked
{
image: url(:/lock_on_hover.svg);
margin-top: 1px;
}
AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:unchecked
@@ -162,4 +169,5 @@ AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:unchecked
, AzToolsFramework--EntityOutlinerCheckBox#LockOverriddenHover::indicator:unchecked
{
image: url(:/lock_default_hover.svg);
margin-top: 1px;
}
@@ -1970,6 +1970,13 @@ namespace AzToolsFramework
void EntityOutlinerItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// Customize option to prevent Qt from painting the default focus rectangle
QStyleOptionViewItem customOption{option};
if (customOption.state & QStyle::State_HasFocus)
{
customOption.state ^= QStyle::State_HasFocus;
}
// Retrieve the Entity UI Handler
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
@@ -1977,6 +1984,9 @@ namespace AzToolsFramework
const bool isSelected = (option.state & QStyle::State_Selected);
const bool isHovered = (option.state & QStyle::State_MouseOver) && (option.state & QStyle::State_Enabled);
// Paint the Selection/Hover Rect
PaintSelectionHoverRect(painter, option, index, isSelected, isHovered);
// Paint Ancestor Backgrounds
PaintAncestorBackgrounds(painter, option, index);
@@ -1986,25 +1996,6 @@ namespace AzToolsFramework
entityUiHandler->PaintItemBackground(painter, option, index);
}
// Paint the Selection/Hover Rect
PaintSelectionHoverRect(painter, option, index, isSelected, isHovered);
// Paint Ancestor Foregrounds
PaintAncestorForegrounds(painter, option, index);
// Paint the Foreground
if (entityUiHandler != nullptr)
{
entityUiHandler->PaintItemForeground(painter, option, index);
}
// Customize option to prevent Qt from painting the default focus rectangle
QStyleOptionViewItem customOption{ option };
if (customOption.state & QStyle::State_HasFocus)
{
customOption.state ^= QStyle::State_HasFocus;
}
switch (index.column())
{
case EntityOutlinerListModel::ColumnVisibilityToggle:
@@ -2033,6 +2024,15 @@ namespace AzToolsFramework
}
break;
}
// Paint Ancestor Foregrounds
PaintAncestorForegrounds(painter, option, index);
// Paint the Foreground
if (entityUiHandler != nullptr)
{
entityUiHandler->PaintItemForeground(painter, option, index);
}
}
void EntityOutlinerItemDelegate::PaintAncestorBackgrounds(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
@@ -2069,17 +2069,18 @@ namespace AzToolsFramework
if (isSelected || isHovered)
{
QPainterPath layerBGPath;
QRect layerBGRect(option.rect);
layerBGPath.addRect(layerBGRect);
QPainterPath backgroundPath;
QRect backgroundRect(option.rect);
QColor layerBG = m_hoverColor;
backgroundPath.addRect(backgroundRect);
QColor backgroundColor = m_hoverColor;
if (isSelected)
{
layerBG = m_selectedColor;
backgroundColor = m_selectedColor;
}
painter->fillPath(layerBGPath, layerBG);
painter->fillPath(backgroundPath, backgroundColor);
}
painter->restore();
@@ -2212,13 +2213,9 @@ namespace AzToolsFramework
textWidthAvailable -= fontMetrics.horizontalAdvance(QObject::tr("..."));
if (!infoString.isEmpty())
{
// The info string includes HTML markup, which can cause an issue computing the width.
// The markup on the text is light (just color, italic or bold), so an approximate width
// is computed by taking the width of the non-HTML portion of the string and padding it a bit.
QString htmlStripped = infoString;
htmlStripped.remove(htmlMarkupRegex);
const float layerInfoPadding = 1.2f;
textWidthAvailable -= fontMetrics.horizontalAdvance(htmlStripped) * layerInfoPadding;
textWidthAvailable -= fontMetrics.horizontalAdvance(htmlStripped) + 5;
}
entityNameRichText = fontMetrics.elidedText(optionV4.text, Qt::TextElideMode::ElideRight, textWidthAvailable);
@@ -2226,7 +2223,12 @@ namespace AzToolsFramework
if (!infoString.isEmpty())
{
entityNameRichText = QObject::tr("%1%2").arg(entityNameRichText).arg(infoString);
entityNameRichText = QString("<table width=\"100%\" style=\"border-collapse: collapse; border-spacing: 0;\">")
+ QString("<tr><td>")
+ QString(entityNameRichText)
+ QString("</td><td align=\"right\">")
+ QString(infoString)
+ QString("</td></tr></table>");
}
// delete the text from the item so we can use the standard painter to draw the icon
@@ -2238,7 +2240,7 @@ namespace AzToolsFramework
textDoc.setDefaultFont(optionV4.font);
textDoc.setDefaultStyleSheet("body {color: white}");
textDoc.setHtml("<body>" + entityNameRichText + "</body>");
painter->translate(textRect.topLeft() + QPoint(0, -1));
painter->translate(textRect.topLeft());
textDoc.setTextWidth(textRect.width());
textDoc.drawContents(painter, QRectF(0, 0, textRect.width(), textRect.height()));
@@ -2246,7 +2248,7 @@ namespace AzToolsFramework
EntityOutlinerListModel::s_paintingName = false;
}
QSize EntityOutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
QSize EntityOutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
{
// Get the height of a tall character...
// we do this only once per 'tick'
@@ -2262,14 +2264,7 @@ namespace AzToolsFramework
QTimer::singleShot(0, resetFunction);
}
QSize sh = QSize(0, m_cachedBoundingRectOfTallCharacter.height() + EntityOutlinerListModel::s_OutlinerSpacing);
if (index.column() == EntityOutlinerListModel::ColumnVisibilityToggle || index.column() == EntityOutlinerListModel::ColumnLockToggle)
{
sh.setWidth(m_toggleColumnWidth);
}
return sh;
return QSize(0, m_cachedBoundingRectOfTallCharacter.height() + EntityOutlinerListModel::s_OutlinerSpacing);
}
bool EntityOutlinerItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
@@ -107,7 +107,7 @@ namespace AzToolsFramework
};
// Spacing is appropriate and matches the outliner concept work from the UI team.
static const int s_OutlinerSpacing = 5;
static const int s_OutlinerSpacing = 7;
static bool s_paintingName;
@@ -358,8 +358,6 @@ namespace AzToolsFramework
mutable CheckboxGroup m_visibilityCheckBoxes;
mutable CheckboxGroup m_lockCheckBoxes;
const int m_toggleColumnWidth = 16;
// this is a cache, and is hence mutable
mutable QRect m_cachedBoundingRectOfTallCharacter;
@@ -183,8 +183,6 @@ namespace AzToolsFramework
// Paint the branch background as defined by the entity's handler, or its closes ancestor's.
PaintBranchBackground(painter, rect, index);
// Make sure the base class is called after the layer rect is drawn,
// so that the foldout arrow draws on top of the layer color.
QTreeView::drawBranches(painter, rect, index);
}
@@ -209,12 +207,7 @@ namespace AzToolsFramework
if (ancestorUiHandler != nullptr)
{
int ancestorLeft = visualRect(ancestorIndex).left();
QStyleOptionViewItem option;
option.rect = rect;
option.rect.setLeft(ancestorLeft);
ancestorUiHandler->PaintDescendantBackground(painter, option, ancestorIndex, index);
ancestorUiHandler->PaintDescendantBranchBackground(painter, this, rect, ancestorIndex, index);
}
}
}
@@ -171,7 +171,7 @@ namespace AzToolsFramework
const int autoExpandDelayMilliseconds = 2500;
m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_gui->m_objectTree->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
m_gui->m_objectTree->setEditTriggers(QAbstractItemView::EditKeyPressed);
m_gui->m_objectTree->setAutoExpandDelay(autoExpandDelayMilliseconds);
m_gui->m_objectTree->setDragEnabled(true);
m_gui->m_objectTree->setDropIndicatorShown(true);
@@ -179,9 +179,11 @@ namespace AzToolsFramework
m_gui->m_objectTree->setDragDropOverwriteMode(false);
m_gui->m_objectTree->setDragDropMode(QAbstractItemView::DragDrop);
m_gui->m_objectTree->setDefaultDropAction(Qt::CopyAction);
m_gui->m_objectTree->setExpandsOnDoubleClick(false);
m_gui->m_objectTree->setContextMenuPolicy(Qt::CustomContextMenu);
m_gui->m_objectTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_gui->m_objectTree->setAutoScrollMargin(20);
m_gui->m_objectTree->setIndentation(24);
connect(m_gui->m_objectTree, &QTreeView::customContextMenuRequested, this, &EntityOutlinerWidget::OnOpenTreeContextMenu);
// custom item delegate
@@ -193,6 +195,7 @@ namespace AzToolsFramework
// Link up signals for informing the model of tree changes using the proxy as an intermediary
connect(m_gui->m_objectTree, &QTreeView::clicked, this, &EntityOutlinerWidget::OnTreeItemClicked);
connect(m_gui->m_objectTree, &QTreeView::doubleClicked, this, &EntityOutlinerWidget::OnTreeItemDoubleClicked);
connect(m_gui->m_objectTree, &QTreeView::expanded, this, &EntityOutlinerWidget::OnTreeItemExpanded);
connect(m_gui->m_objectTree, &QTreeView::collapsed, this, &EntityOutlinerWidget::OnTreeItemCollapsed);
connect(m_gui->m_objectTree, &EntityOutlinerTreeView::ItemDropped, this, &EntityOutlinerWidget::OnDropEvent);
@@ -223,8 +226,12 @@ namespace AzToolsFramework
m_gui->m_objectTree->header()->setStretchLastSection(false);
// resize the icon columns so that the Visibility and Lock toggle icon columns stay right-justified
m_gui->m_objectTree->header()->setStretchLastSection(false);
m_gui->m_objectTree->header()->setMinimumSectionSize(0);
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnName, QHeaderView::Stretch);
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnVisibilityToggle, QHeaderView::ResizeToContents);
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnVisibilityToggle, QHeaderView::Fixed);
m_gui->m_objectTree->header()->resizeSection(EntityOutlinerListModel::ColumnVisibilityToggle, 20);
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnLockToggle, QHeaderView::Fixed);
m_gui->m_objectTree->header()->resizeSection(EntityOutlinerListModel::ColumnLockToggle, 24);
connect(m_gui->m_objectTree->selectionModel(),
@@ -271,10 +278,12 @@ namespace AzToolsFramework
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
GetEntityContextId());
EditorEntityInfoNotificationBus::Handler::BusConnect();
AZ::Interface<EntityOutlinerWidgetInterface>::Register(this);
}
EntityOutlinerWidget::~EntityOutlinerWidget()
{
AZ::Interface<EntityOutlinerWidgetInterface>::Unregister(this);
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
EditorPickModeNotificationBus::Handler::BusDisconnect();
@@ -798,6 +807,8 @@ namespace AzToolsFramework
#ifdef Q_OS_MAC
// "Alt+Return" translates to Option+Return on macOS
m_actionToRenameSelection->setShortcut(tr("Alt+Return"));
#elseif Q_OS_WIN
m_actionToRenameSelection->setShortcut(tr("F2"));
#endif
m_actionToRenameSelection->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(m_actionToRenameSelection, &QAction::triggered, this, &EntityOutlinerWidget::DoRenameSelection);
@@ -860,6 +871,10 @@ namespace AzToolsFramework
}
}
void EntityOutlinerWidget::OnTreeItemDoubleClicked(const QModelIndex& /*index*/)
{
}
void EntityOutlinerWidget::OnTreeItemExpanded(const QModelIndex& index)
{
m_listModel->OnEntityExpanded(GetEntityIdFromIndex(index));
@@ -1075,6 +1090,30 @@ namespace AzToolsFramework
SetEntityOutlinerState(m_gui, true);
}
void EntityOutlinerWidget::SetRootEntity(AZ::EntityId rootEntityId)
{
// The proxy model needs a tick to initialize, else it will return an invalid index in mapFromSource.
QTimer::singleShot(0, this, [rootEntityId, this]() {
QModelIndex rootIndex = m_listModel->GetIndexFromEntity(rootEntityId);
QModelIndex proxyIndex = m_proxyModel->mapFromSource(rootIndex);
m_gui->m_objectTree->setRootIndex(proxyIndex);
});
}
void EntityOutlinerWidget::SetUpdatesEnabled(bool enable)
{
if (enable)
{
QTimer::singleShot(1, this, [this]() {
m_gui->m_objectTree->setUpdatesEnabled(true);
});
}
else
{
m_gui->m_objectTree->setUpdatesEnabled(false);
}
}
void EntityOutlinerWidget::OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId childId)
{
QueueContentUpdateSort(childId);
@@ -21,8 +21,9 @@
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/ToolsMessaging/EntityHighlightBus.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerSearchWidget.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerCacheBus.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerSearchWidget.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerWidgetInterface.h>
#include <AzToolsFramework/UI/SearchWidget/SearchWidgetTypes.hxx>
#include <QIcon>
@@ -60,12 +61,13 @@ namespace AzToolsFramework
, private EditorEntityContextNotificationBus::Handler
, private EditorEntityInfoNotificationBus::Handler
, private ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private EntityOutlinerWidgetInterface
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(EntityOutlinerWidget, AZ::SystemAllocator, 0)
EntityOutlinerWidget(QWidget* pParent = NULL, Qt::WindowFlags flags = Qt::WindowFlags());
explicit EntityOutlinerWidget(QWidget* pParent = NULL, Qt::WindowFlags flags = Qt::WindowFlags());
virtual ~EntityOutlinerWidget();
private Q_SLOTS:
@@ -103,6 +105,10 @@ namespace AzToolsFramework
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// EntityOutlinerWidgetInterface
void SetRootEntity(AZ::EntityId rootEntityId) override;
void SetUpdatesEnabled(bool enable) override;
// Build a selection object from the given entities. Entities already in the Widget's selection buffers are ignored.
template <class EntityIdCollection>
QItemSelection BuildSelectionFromEntities(const EntityIdCollection& entityIds);
@@ -138,6 +144,7 @@ namespace AzToolsFramework
QAction* m_actionGoToEntitiesInViewport;
void OnTreeItemClicked(const QModelIndex& index);
void OnTreeItemDoubleClicked(const QModelIndex& index);
void OnTreeItemExpanded(const QModelIndex& index);
void OnTreeItemCollapsed(const QModelIndex& index);
void OnExpandEntity(const AZ::EntityId& entityId, bool expand);
@@ -0,0 +1,30 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzToolsFramework
{
class EntityOutlinerWidgetInterface
{
public:
AZ_RTTI(EntityOutlinerWidgetInterface, "{30C0F252-EC84-4196-BF59-EB9E73B8ADCB}");
virtual void SetRootEntity(AZ::EntityId rootEntityId) = 0;
virtual void SetUpdatesEnabled(bool enable) = 0;
};
} // namespace AzToolsFramework
@@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Entity.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabInstanceContainerNotifications
: public AZ::EBusTraits
{
public:
virtual ~PrefabInstanceContainerNotifications() = default;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual void OnPrefabComponentActivate(AZ::EntityId entityId) = 0;
virtual void OnPrefabComponentDeactivate(AZ::EntityId entityId) = 0;
};
using PrefabInstanceContainerNotificationBus = AZ::EBus<PrefabInstanceContainerNotifications>;
}
}
@@ -0,0 +1,39 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabIntegrationInterface
{
public:
AZ_RTTI(PrefabIntegrationInterface, "{B88AE045-7711-49BC-8336-45003D1C9116}");
/**
* Create a new entity at the position provided.
* @param position The position the new entity will be created at.
* @param parentId The id of the parent of the newly created entity.
* @return The id of the newly created entity.
*/
virtual AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) = 0;
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -16,6 +16,7 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
@@ -24,6 +25,8 @@
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <QApplication>
#include <QFileDialog>
@@ -36,6 +39,7 @@ namespace AzToolsFramework
{
namespace Prefab
{
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr;
const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab";
@@ -54,8 +58,14 @@ namespace AzToolsFramework
PrefabIntegrationManager::PrefabIntegrationManager()
{
s_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
s_editorEntityUiInterface = AZ::Interface<EditorEntityUiInterface>::Get();
if (s_editorEntityUiInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get EditorEntityUiInterface on PrefabIntegrationManager construction.");
return;
}
s_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
if (s_prefabPublicInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabPublicInterface on PrefabIntegrationManager construction.");
@@ -63,7 +73,6 @@ namespace AzToolsFramework
}
s_prefabEditInterface = AZ::Interface<PrefabEditInterface>::Get();
if (s_prefabEditInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabEditInterface on PrefabIntegrationManager construction.");
@@ -71,12 +80,32 @@ namespace AzToolsFramework
}
EditorContextMenuBus::Handler::BusConnect();
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension);
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabIntegrationInterface>::Register(this);
bool prefabWipFeaturesEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
if (prefabWipFeaturesEnabled)
{
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension);
}
}
PrefabIntegrationManager::~PrefabIntegrationManager()
{
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect();
bool prefabWipFeaturesEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
if (prefabWipFeaturesEnabled)
{
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect();
}
AZ::Interface<PrefabIntegrationInterface>::Unregister(this);
PrefabInstanceContainerNotificationBus::Handler::BusDisconnect();
EditorContextMenuBus::Handler::BusDisconnect();
}
@@ -97,57 +126,65 @@ namespace AzToolsFramework
void PrefabIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu) const
{
// Create Prefab
bool prefabWipFeaturesEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
if (prefabWipFeaturesEnabled)
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
if (!selectedEntities.empty())
// Create Prefab
{
bool layerInSelection = false;
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
for (AZ::EntityId entityId : selectedEntities)
if (!selectedEntities.empty())
{
if (!layerInSelection)
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
layerInSelection,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
bool layerInSelection = false;
if (layerInSelection)
for (AZ::EntityId entityId : selectedEntities)
{
if (!layerInSelection)
{
break;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
layerInSelection, entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (layerInSelection)
{
break;
}
}
}
}
// Layers can't be in prefabs.
if (!layerInSelection)
{
QAction* createAction = menu->addAction(QObject::tr("Create Prefab..."));
createAction->setToolTip(QObject::tr("Creates a prefab out of the currently selected entities."));
// Layers can't be in prefabs.
if (!layerInSelection)
{
QAction* createAction = menu->addAction(QObject::tr("Create Prefab..."));
createAction->setToolTip(QObject::tr("Creates a prefab out of the currently selected entities."));
QObject::connect(createAction, &QAction::triggered, createAction, [this, selectedEntities] {
ContextMenu_CreatePrefab(selectedEntities);
});
QObject::connect(createAction, &QAction::triggered, createAction, [this, selectedEntities] {
ContextMenu_CreatePrefab(selectedEntities);
});
}
}
}
// Instantiate Prefab
{
QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate Prefab..."));
instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene."));
QObject::connect(
instantiateAction, &QAction::triggered, instantiateAction, [this] { ContextMenu_InstantiatePrefab(); });
}
menu->addSeparator();
}
// Instantiate Prefab
{
QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate Prefab..."));
instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene."));
bool itemWasShown = false;
QObject::connect(instantiateAction, &QAction::triggered, instantiateAction, [this] {
ContextMenu_InstantiatePrefab();
});
}
menu->addSeparator();
// Edit Prefab
// Edit/Save Prefab
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
@@ -159,17 +196,47 @@ namespace AzToolsFramework
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
{
QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
// Edit Prefab
if (prefabWipFeaturesEnabled)
{
bool beingEdited = s_prefabEditInterface->IsOwningPrefabBeingEdited(selectedEntity);
QObject::connect(editAction, &QAction::triggered, editAction, [this, selectedEntity] {
ContextMenu_EditPrefab(selectedEntity);
});
if (!beingEdited)
{
QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
QObject::connect(editAction, &QAction::triggered, editAction, [this, selectedEntity] {
ContextMenu_EditPrefab(selectedEntity);
});
itemWasShown = true;
}
}
// Save Prefab
AZ::IO::Path prefabFilePath = s_prefabPublicInterface->GetOwningInstancePrefabPath(selectedEntity);
auto dirtyOutcome = s_prefabPublicInterface->HasUnsavedChanges(prefabFilePath);
if (dirtyOutcome.IsSuccess() && dirtyOutcome.GetValue() == true)
{
QAction* saveAction = menu->addAction(QObject::tr("Save Prefab to file"));
saveAction->setToolTip(QObject::tr("Save the changes to the prefab to disk."));
QObject::connect(saveAction, &QAction::triggered, saveAction, [this, selectedEntity] {
ContextMenu_SavePrefab(selectedEntity);
});
itemWasShown = true;
}
}
}
}
menu->addSeparator();
if (itemWasShown)
{
menu->addSeparator();
}
}
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
@@ -289,7 +356,18 @@ namespace AzToolsFramework
{
s_prefabEditInterface->EditOwningPrefab(containerEntity);
}
void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity)
{
auto prefabPath = s_prefabPublicInterface->GetOwningInstancePrefabPath(containerEntity);
auto savePrefabOutcome = s_prefabPublicInterface->SavePrefab(prefabPath);
if (!savePrefabOutcome.IsSuccess())
{
WarnUserOfError("Prefab Save Error", savePrefabOutcome.GetError());
}
}
void PrefabIntegrationManager::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName)
{
@@ -891,5 +969,30 @@ namespace AzToolsFramework
}
return true;
}
void PrefabIntegrationManager::OnPrefabComponentActivate(AZ::EntityId entityId)
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
}
void PrefabIntegrationManager::OnPrefabComponentDeactivate(AZ::EntityId entityId)
{
s_editorEntityUiInterface->UnregisterEntity(entityId);
}
AZ::EntityId PrefabIntegrationManager::CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId)
{
Prefab::PrefabPublicInterface* prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
auto createResult = prefabPublicInterface->CreateEntity(parentId, position);
if (createResult.IsSuccess())
{
return createResult.GetValue();
}
else
{
WarnUserOfError("Entity Creation Error", createResult.GetError());
return AZ::EntityId();
}
}
}
}
@@ -20,6 +20,8 @@
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
namespace AzToolsFramework
@@ -45,6 +47,8 @@ namespace AzToolsFramework
class PrefabIntegrationManager final
: public EditorContextMenuBus::Handler
, public AssetBrowser::AssetBrowserSourceDropBus::Handler
, public PrefabInstanceContainerNotificationBus::Handler
, public PrefabIntegrationInterface
{
public:
AZ_CLASS_ALLOCATOR(PrefabIntegrationManager, AZ::SystemAllocator, 0);
@@ -62,6 +66,13 @@ namespace AzToolsFramework
// EntityOutlinerSourceDropHandlingBus...
void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override;
// PrefabInstanceContainerNotificationBus...
void OnPrefabComponentActivate(AZ::EntityId entityId) override;
void OnPrefabComponentDeactivate(AZ::EntityId entityId) override;
// PrefabIntegrationInterface...
AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override;
private:
// Manages the Edit Mode UI for prefabs
PrefabEditManager m_prefabEditManager;
@@ -73,6 +84,7 @@ namespace AzToolsFramework
static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities);
static void ContextMenu_InstantiatePrefab();
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
// Prompt and resolve dialogs
static bool QueryUserForPrefabSaveLocation(
@@ -109,6 +121,7 @@ namespace AzToolsFramework
static const AZStd::string s_prefabFileExtension;
static EditorEntityUiInterface* s_editorEntityUiInterface;
static PrefabPublicInterface* s_prefabPublicInterface;
static PrefabEditInterface* s_prefabEditInterface;
};
@@ -12,6 +12,8 @@
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
#include <QAbstractItemModel>
@@ -23,6 +25,8 @@ namespace AzToolsFramework
{
const QColor PrefabUiHandler::m_prefabCapsuleColor = QColor("#1E252F");
const QColor PrefabUiHandler::m_prefabCapsuleEditColor = QColor("#4A90E2");
const QString PrefabUiHandler::m_prefabIconPath = QString(":/Entity/prefab.svg");
const QString PrefabUiHandler::m_prefabEditIconPath = QString(":/Entity/prefab_edit.svg");
PrefabUiHandler::PrefabUiHandler()
{
@@ -33,6 +37,62 @@ namespace AzToolsFramework
AZ_Assert(false, "PrefabUiHandler - could not get PrefabEditInterface on PrefabUiHandler construction.");
return;
}
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
if (m_prefabPublicInterface == nullptr)
{
AZ_Assert(false, "PrefabUiHandler - could not get PrefabPublicInterface on PrefabUiHandler construction.");
return;
}
}
QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
{
QString infoString;
AZ::IO::Path path = m_prefabPublicInterface->GetOwningInstancePrefabPath(entityId);
if (!path.empty())
{
QString saveFlag = "";
auto dirtyOutcome = m_prefabPublicInterface->HasUnsavedChanges(path);
if (dirtyOutcome.IsSuccess() && dirtyOutcome.GetValue() == true)
{
saveFlag = "*";
}
infoString = QObject::tr("<span style=\"font-style: italic; font-weight: 400;\">(%1%2)</span>")
.arg(path.Filename().Native().data())
.arg(saveFlag);
}
return infoString;
}
QString PrefabUiHandler::GenerateItemTooltip(AZ::EntityId entityId) const
{
QString tooltip;
AZ::IO::Path path = m_prefabPublicInterface->GetOwningInstancePrefabPath(entityId);
if (!path.empty())
{
tooltip = QObject::tr("%1").arg(path.Native().data());
}
return tooltip;
}
QPixmap PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const
{
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
{
return QPixmap(m_prefabEditIconPath);
}
return QPixmap(m_prefabIconPath);
}
void PrefabUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
@@ -57,40 +117,61 @@ namespace AzToolsFramework
QPainterPath backgroundPath;
backgroundPath.setFillRule(Qt::WindingFill);
QRect tempRect = option.rect;
tempRect.setTop(tempRect.top() + 1);
if (!hasVisibleChildren)
{
tempRect.setBottom(tempRect.bottom() - 1);
}
if (isFirstColumn)
{
tempRect.setLeft(tempRect.left() - 1);
}
if (isLastColumn)
{
tempRect.setWidth(tempRect.width() - 1);
}
if (isFirstColumn || isLastColumn)
{
// Rounded rect to have rounded borders on top
backgroundPath.addRoundedRect(option.rect, m_prefabCapsuleRadius, m_prefabCapsuleRadius);
backgroundPath.addRoundedRect(tempRect, m_prefabCapsuleRadius, m_prefabCapsuleRadius);
if (hasVisibleChildren)
{
// Regular rect, half height, to square the bottom borders
QRect bottomRect = option.rect;
QRect bottomRect = tempRect;
bottomRect.setTop(bottomRect.top() + (bottomRect.height() / 2));
backgroundPath.addRect(bottomRect);
}
// Regular rect, half height, to square the opposite border
QRect squareRect = option.rect;
QRect squareRect = tempRect;
if (isFirstColumn)
{
squareRect.setLeft(option.rect.left() + (option.rect.width() / 2));
squareRect.setLeft(tempRect.left() + (tempRect.width() / 2));
}
else if (isLastColumn)
{
squareRect.setWidth(option.rect.width() / 2);
squareRect.setWidth(tempRect.width() / 2);
}
backgroundPath.addRect(squareRect);
}
else
{
backgroundPath.addRect(option.rect);
backgroundPath.addRect(tempRect);
}
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->fillPath(backgroundPath.simplified(), backgroundColor);
painter->restore();
}
void PrefabUiHandler::PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
void PrefabUiHandler::PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const
{
if (!painter)
@@ -100,8 +181,15 @@ namespace AzToolsFramework
}
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
// We hide the root instance container entity from the Outliner, so avoid drawing its full container on children
if (m_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId))
{
return;
}
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2);
const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2) - 1;
const int curveRectSize = m_prefabCapsuleRadius * 2;
const bool isFirstColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnName;
const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
@@ -117,12 +205,17 @@ namespace AzToolsFramework
// Find the rect that extends fully to the left
QRect fullRect = option.rect;
fullRect.setLeft(ancestorLeft);
if (isLastColumn)
{
fullRect.setWidth(fullRect.width() - 1);
}
// Adjust option.rect to account for the border thickness
QRect rect = option.rect;
rect.setLeft(rect.left() + (m_prefabBorderThickness / 2));
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(borderLinePen);
if (IsLastVisibleChild(index, descendantIndex))
@@ -13,10 +13,15 @@
#pragma once
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabEditInterface;
class PrefabPublicInterface;
};
class PrefabUiHandler
: public EditorEntityUiHandlerBase
{
@@ -28,11 +33,17 @@ namespace AzToolsFramework
~PrefabUiHandler() override = default;
// EditorEntityUiHandler...
QString GenerateItemInfoString(AZ::EntityId entityId) const override;
QString GenerateItemTooltip(AZ::EntityId entityId) const override;
QPixmap GenerateItemIcon(AZ::EntityId entityId) const override;
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const override;
private:
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child);
static QModelIndex GetLastVisibleChild(const QModelIndex& parent);
static QModelIndex Internal_GetLastVisibleChild(const QAbstractItemModel* model, const QModelIndex& index);
@@ -41,7 +52,7 @@ namespace AzToolsFramework
static constexpr int m_prefabBorderThickness = 2;
static const QColor m_prefabCapsuleColor;
static const QColor m_prefabCapsuleEditColor;
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
static const QString m_prefabIconPath;
static const QString m_prefabEditIconPath;
};
} // namespace AzToolsFramework
@@ -31,6 +31,7 @@ AZ_POP_DISABLE_WARNING
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/sort.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzQtComponents/Components/Style.h>
@@ -47,6 +48,7 @@ AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/AssetBrowser/EBusFindAssetTypeByName.h>
#include <AzToolsFramework/ComponentMode/ComponentModeDelegate.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Slice/SliceDataFlagsCommand.h>
#include <AzToolsFramework/Slice/SliceMetadataEntityContextBus.h>
#include <AzToolsFramework/Slice/SliceUtilities.h>
@@ -331,8 +333,14 @@ namespace AzToolsFramework
m_gui->m_entitySearchBox->setContextMenuPolicy(Qt::CustomContextMenu);
AzQtComponents::LineEdit::applySearchStyle(m_gui->m_entitySearchBox);
QStandardItemModel*model = new QStandardItemModel(3, 1);
for (int row = 0; row < 3; ++row)
AzFramework::ApplicationRequests::Bus::BroadcastResult(
m_prefabsAreEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
m_itemNames =
m_prefabsAreEnabled ? QStringList{"Universal", "Editor only"} : QStringList{"Start active", "Start inactive", "Editor only"};
int itemNameCount = m_itemNames.size();
QStandardItemModel* model = new QStandardItemModel(itemNameCount, 1);
for (int row = 0; row < itemNameCount; ++row)
{
m_comboItems[row] = new QStandardItem(m_itemNames[row]);
m_comboItems[row]->setCheckable(true);
@@ -397,6 +405,8 @@ namespace AzToolsFramework
CreateActions();
UpdateContents();
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
EditorEntityContextNotificationBus::Handler::BusConnect();
//forced to register global event filter with application for selection
@@ -741,26 +751,42 @@ namespace AzToolsFramework
isLayerEntity,
selectedEntityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
bool isPrefabEntity = (m_prefabPublicInterface) ? m_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) : false;
if (isLayerEntity)
{
if (result == SelectionEntityTypeInfo::None)
{
result = SelectionEntityTypeInfo::OnlyLayerEntities;
}
else if(result == SelectionEntityTypeInfo::OnlyStandardEntities)
else if (result == SelectionEntityTypeInfo::OnlyStandardEntities)
{
result = SelectionEntityTypeInfo::Mixed;
// An entity of both layer and non-layer type have been found, so break out of the loop.
break;
}
}
else if (isPrefabEntity)
{
if (result == SelectionEntityTypeInfo::None)
{
result = SelectionEntityTypeInfo::OnlyPrefabEntities;
}
else if (result == SelectionEntityTypeInfo::OnlyStandardEntities)
{
result = SelectionEntityTypeInfo::Mixed;
// An entity of both prefab and non-prefab type have been found, so break out of the loop.
break;
}
}
else
{
if (result == SelectionEntityTypeInfo::None)
{
result = SelectionEntityTypeInfo::OnlyStandardEntities;
}
else if (result == SelectionEntityTypeInfo::OnlyLayerEntities)
else if (result == SelectionEntityTypeInfo::OnlyLayerEntities || result == SelectionEntityTypeInfo::OnlyPrefabEntities)
{
result = SelectionEntityTypeInfo::Mixed;
// An entity of both layer and non-layer type have been found, so break out of the loop.
@@ -857,7 +883,7 @@ namespace AzToolsFramework
entityDetailsVisible = true;
entityDetailsLabelText = GetEntityDetailsLabelText();
}
else if (selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyLayerEntities)
else if (selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyLayerEntities || selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyPrefabEntities)
{
// If a customer filter is not already in use, only show layer components.
if (!m_customFilterSet)
@@ -2477,9 +2503,10 @@ namespace AzToolsFramework
return;
}
for (auto& comboItem : m_comboItems)
size_t comboItemCount = StatusTypeToIndex(StatusItems);
for (size_t i=0; i<comboItemCount; ++i)
{
comboItem->setCheckState(Qt::Unchecked);
m_comboItems[i]->setCheckState(Qt::Unchecked);
}
bool allActive = true;
@@ -2519,26 +2546,41 @@ namespace AzToolsFramework
}
}
if (m_prefabsAreEnabled)
{
if (allInactive)
{
AZ_Warning("Prefab", false, "All entities found to be inactive. This is an option that's not supported with Prefabs.");
allInactive = false;
allEditorOnly = true;
}
if (someInactive)
{
AZ_Warning("Prefab", false, "Some inactive entities found. This is an option that's not supported with Prefabs.");
someInactive = false;
}
}
m_gui->m_statusComboBox->setItalic(false);
if (allActive)
{
m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[StatusType::StatusStartActive]);
m_gui->m_statusComboBox->setCurrentIndex(StatusType::StatusStartActive);
m_comboItems[StatusType::StatusStartActive]->setCheckState(Qt::Checked);
m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[StatusTypeToIndex(StatusType::StatusStartActive)]);
m_gui->m_statusComboBox->setCurrentIndex(StatusTypeToIndex(StatusType::StatusStartActive));
m_comboItems[StatusTypeToIndex(StatusType::StatusStartActive)]->setCheckState(Qt::Checked);
}
else
if (allInactive)
{
m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[StatusType::StatusStartInactive]);
m_gui->m_statusComboBox->setCurrentIndex(StatusType::StatusStartInactive);
m_comboItems[StatusType::StatusStartInactive]->setCheckState(Qt::Checked);
m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[StatusTypeToIndex(StatusType::StatusStartInactive)]);
m_gui->m_statusComboBox->setCurrentIndex(StatusTypeToIndex(StatusType::StatusStartInactive));
m_comboItems[StatusTypeToIndex(StatusType::StatusStartInactive)]->setCheckState(Qt::Checked);
}
else
if (allEditorOnly)
{
m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[StatusType::StatusEditorOnly]);
m_gui->m_statusComboBox->setCurrentIndex(StatusType::StatusEditorOnly);
m_comboItems[StatusType::StatusEditorOnly]->setCheckState(Qt::Checked);
m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[StatusTypeToIndex(StatusType::StatusEditorOnly)]);
m_gui->m_statusComboBox->setCurrentIndex(StatusTypeToIndex(StatusType::StatusEditorOnly));
m_comboItems[StatusTypeToIndex(StatusType::StatusEditorOnly)]->setCheckState(Qt::Checked);
}
else // Some marked active, some not
{
@@ -2546,15 +2588,15 @@ namespace AzToolsFramework
m_gui->m_statusComboBox->setHeaderOverride("- Multiple selected -");
if (someActive)
{
m_comboItems[StatusType::StatusStartActive]->setCheckState(Qt::PartiallyChecked);
m_comboItems[StatusTypeToIndex(StatusType::StatusStartActive)]->setCheckState(Qt::PartiallyChecked);
}
if (someInactive)
{
m_comboItems[StatusType::StatusStartInactive]->setCheckState(Qt::PartiallyChecked);
m_comboItems[StatusTypeToIndex(StatusType::StatusStartInactive)]->setCheckState(Qt::PartiallyChecked);
}
if (someEditorOnly)
{
m_comboItems[StatusType::StatusEditorOnly]->setCheckState(Qt::PartiallyChecked);
m_comboItems[StatusTypeToIndex(StatusType::StatusEditorOnly)]->setCheckState(Qt::PartiallyChecked);
}
}
@@ -2563,6 +2605,54 @@ namespace AzToolsFramework
m_gui->m_statusComboBox->style()->polish(m_gui->m_statusComboBox);
}
size_t EntityPropertyEditor::StatusTypeToIndex(StatusType statusType) const
{
if (m_prefabsAreEnabled)
{
switch (statusType)
{
case StatusStartActive:
return 0;
case StatusStartInactive:
AZ_Assert(false, "StatusStartInactive is not supported when Prefabs are enabled.");
return 0;
case StatusEditorOnly:
return 1;
case StatusItems:
return 2;
default:
AZ_Assert(false, "StatusType for EntityPropertyEditor is out of bounds.");
return 1;
}
}
else
{
return statusType;
}
}
EntityPropertyEditor::StatusType EntityPropertyEditor::IndexToStatusType(size_t index) const
{
if (m_prefabsAreEnabled)
{
switch (index)
{
case 0:
return StatusStartActive;
case 1:
return StatusEditorOnly;
default:
AZ_Assert(index < StatusType::StatusItems, "Index for EntityPropertyEditor::IndexToStatusType is out of bounds");
return StatusEditorOnly;
}
}
else
{
AZ_Assert(index < StatusType::StatusItems, "Index for EntityPropertyEditor::IndexToStatusType is out of bounds");
return aznumeric_cast<StatusType>(index);
}
}
void EntityPropertyEditor::OnDisplayComponentEditorMenu(const QPoint& position)
{
if (m_disabled)
@@ -3649,12 +3739,12 @@ namespace AzToolsFramework
{
ScopedUndoBatch undo("Change Status");
switch (index)
switch (IndexToStatusType(index))
{
case StatusType::StatusStartActive:
case StatusType::StatusStartInactive:
{
bool entityIsStartingActive = index == StatusType::StatusStartActive;
bool entityIsStartingActive = index == StatusTypeToIndex(StatusType::StatusStartActive);
for (AZ::EntityId entityId : m_selectedEntityIds)
{
@@ -68,6 +68,11 @@ namespace AzToolsFramework
class ProductAssetBrowserEntry;
}
namespace Prefab
{
class PrefabPublicInterface;
};
namespace UndoSystem
{
class URSequencePoint;
@@ -299,6 +304,7 @@ namespace AzToolsFramework
None,
OnlyStandardEntities,
OnlyLayerEntities,
OnlyPrefabEntities,
Mixed,
LevelEntity
};
@@ -480,9 +486,11 @@ namespace AzToolsFramework
StatusItems
};
QStringList m_itemNames{ "Start active","Start inactive","Editor only" };
QStringList m_itemNames;
void UpdateStatusComboBox();
size_t StatusTypeToIndex(StatusType statusType) const;
StatusType IndexToStatusType(size_t index) const;
bool m_isBuildingProperties;
@@ -541,6 +549,9 @@ namespace AzToolsFramework
QStandardItem* m_comboItems[StatusItems];
EntityIdSet m_overrideSelectedEntityIds;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
bool m_prefabsAreEnabled = false;
// When m_initiatingPropertyChangeNotification is set to true, it means this EntityPropertyEditor is
// broadcasting a change to all listeners about a property change for a given entity. This is needed
// so that we don't update the values twice for this inspector
@@ -53,7 +53,7 @@ AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/Thumbnails/ThumbnailWidget.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
@@ -1093,13 +1093,11 @@ namespace AzToolsFramework
AZ::Data::AssetType assetType;
const AZStd::string platformName = ""; // Empty for default
AZStd::string rootFilePath;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetInfoById,
assetID, assetType, platformName, assetInfo, rootFilePath);
AssetSystemRequestBus::BroadcastResult(result, &AssetSystemRequestBus::Events::GetAssetInfoById, assetID, assetType, platformName, assetInfo, rootFilePath);
if (result)
{
AZStd::string fullSourcePath = AZStd::string::format("%s/%s", rootFilePath.c_str(), assetInfo.m_relativePath.c_str());
SharedThumbnailKey thumbnailKey = MAKE_TKEY(AzToolsFramework::AssetBrowser::SourceThumbnailKey, fullSourcePath.c_str());
SharedThumbnailKey thumbnailKey = MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, assetID);
m_thumbnail->SetThumbnailKey(thumbnailKey, Thumbnailer::ThumbnailContext::DefaultContext);
return;
}