diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg new file mode 100644 index 0000000000..f1d36d3e41 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg new file mode 100644 index 0000000000..9fc9fe52c2 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg new file mode 100644 index 0000000000..7a61db38e0 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg new file mode 100644 index 0000000000..c6fb977c52 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 469a235b9f..ec709aef8a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -138,7 +138,13 @@ namespace AzToolsFramework m_indexMap[row] = index; m_rowMap[index] = row; ++row; - ++m_displayedItemsCounter; + + // We only want to increase the displayed counter if it is a parent (Source) + // so we don't cut children entries. + if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source) + { + ++m_displayedItemsCounter; + } } if (model->hasChildren(index)) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index 37091c11e2..217e282a37 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -29,20 +29,20 @@ namespace AzToolsFramework { AssetBrowserTableView::AssetBrowserTableView(QWidget* parent) : AzQtComponents::TableView(parent) - , m_delegate(new EntryDelegate(this)) + , m_delegate(new SearchEntryDelegate(this)) { - setSortingEnabled(true); + setSortingEnabled(false); setItemDelegate(m_delegate); setRootIsDecorated(false); //Styling the header aligning text to the left and using a bold font. header()->setDefaultAlignment(Qt::AlignLeft); - header()->setStyleSheet("QHeaderView { font-weight: bold; }"); + header()->setStyleSheet("QHeaderView { font-weight: bold; };"); + setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); - setSortingEnabled(false); setSelectionMode(QAbstractItemView::SingleSelection); connect(this, &AzQtComponents::TableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); @@ -67,6 +67,8 @@ namespace AzToolsFramework header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); + header()->setSortIndicatorShown(false); + header()->setSectionsClickable(false); } void AssetBrowserTableView::SetName(const QString& name) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index 94e9bdc4e4..b4eca59cef 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -26,7 +26,7 @@ namespace AzToolsFramework class AssetBrowserEntry; class AssetBrowserTableModel; class AssetBrowserFilterModel; - class EntryDelegate; + class SearchEntryDelegate; class AssetBrowserTableView //! Table view that displays the asset browser entries in a list. : public AzQtComponents::TableView @@ -67,9 +67,9 @@ namespace AzToolsFramework private: QString m_name; - QPointer m_tableModel = nullptr; - QPointer m_sourceFilterModel = nullptr; - EntryDelegate* m_delegate = nullptr; + QPointer m_tableModel; + QPointer m_sourceFilterModel; + SearchEntryDelegate* m_delegate = nullptr; private Q_SLOTS: void OnContextMenu(const QPoint& point); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index 29324c612c..755c59b55b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -11,12 +11,13 @@ #include #include #include - +#include #include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer' needs to have dll-interface to be used by clients of class 'QBrush' // 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning) +#include #include AZ_POP_DISABLE_WARNING @@ -24,8 +25,13 @@ namespace AzToolsFramework { namespace AssetBrowser { - const int ENTRY_SPACING_LEFT_PIXELS = 8; - const int ENTRY_ICON_MARGIN_LEFT_PIXELS = 2; + static constexpr const char* TreeIconPathFirst = "Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg"; + static constexpr const char* TreeIconPathMiddle = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg"; + static constexpr const char* TreeIconPathLast = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg"; + static constexpr const char* TreeIconPathOneChild = "Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg"; + + const int EntrySpacingLeftPixels = 8; + const int EntryIconMarginLeftPixels = 2; EntryDelegate::EntryDelegate(QWidget* parent) : QStyledItemDelegate(parent) @@ -62,7 +68,7 @@ namespace AzToolsFramework // Draw main entry thumbnail. QRect remainingRect(option.rect); - remainingRect.adjust(ENTRY_ICON_MARGIN_LEFT_PIXELS, 0, 0, 0); // bump it rightwards to give some margin to the icon. + remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon. QSize iconSize(m_iconSize, m_iconSize); // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size @@ -89,7 +95,7 @@ namespace AzToolsFramework } remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail - remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. + remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing. } QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) @@ -148,7 +154,162 @@ namespace AzToolsFramework return m_iconSize; } - } // namespace Thumbnailer + SearchEntryDelegate::SearchEntryDelegate(QWidget* parent) + : EntryDelegate(parent) + { + LoadBranchPixMaps(); + } + + void SearchEntryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const + { + auto data = index.data(AssetBrowserModel::Roles::EntryRole); + if (data.canConvert()) + { + bool isEnabled = (option.state & QStyle::State_Enabled) != 0; + bool isSelected = (option.state & QStyle::State_Selected) != 0; + + QStyle* style = option.widget ? option.widget->style() : QApplication::style(); + + // draw the background + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); + + // Draw main entry thumbnail. + QRect remainingRect(option.rect); + + QSize iconSize(m_iconSize, m_iconSize); + // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size + // so it needs to center vertically with padding in that case: + QPoint iconTopLeft; + QPoint branchIconTopLeft = QPoint(); + + auto entry = qvariant_cast(data); + auto sourceEntry = azrtti_cast(entry); + + //If it is a SourceEntry or it is not the column name we don't want to add space for the branch Icon + if (sourceEntry || index.column() != aznumeric_cast(AssetBrowserEntry::Column::Name)) + { + remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon. + iconTopLeft = QPoint(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + } + else + { + remainingRect.adjust(EntryIconMarginLeftPixels + m_iconSize, 0, 0, 0); // bump it rightwards to give some margin to the icon. + iconTopLeft = QPoint(remainingRect.x() / 2 + m_iconSize, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + branchIconTopLeft = QPoint((remainingRect.x() / 2) - 2, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + } + + QPalette actualPalette(option.palette); + + if (index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)) + { + int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); + if (sourceEntry) + { + if (m_showSourceControl) + { + DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey()); + } + // sources with no children should be greyed out. + if (sourceEntry->GetChildCount() == 0) + { + isEnabled = false; // draw in disabled style. + actualPalette.setCurrentColorGroup(QPalette::Disabled); + } + } + else + { + //Get the indexes above and below our entry to see what type are they. + QAbstractItemView* view = qobject_cast(option.styleObject); + const QAbstractItemModel* viewModel = view->model(); + + const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column()); + const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column()); + + auto aboveEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole)); + auto belowEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole)); + + auto aboveSourceEntry = azrtti_cast(aboveEntry); + auto belowSourceEntry = azrtti_cast(belowEntry); + + // if current index is the last entry in the view + // or the index above it is a Source Entry and + // the index below is invalid or is valid but it is also a source entry + // then the current index is the only child. + if (index.row() == viewModel->rowCount() - 1 || + (indexBelow.isValid() && aboveSourceEntry && + (!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry)))) + { + DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon + } + else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry + { + DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon + } + else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry + { + DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon + } + else //the index above and below are also child entries + { + DrawBranchPixMap(EntryBranchType::Middle, painter, branchIconTopLeft, iconSize); // Draw Default child Icon. + } + } + + remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail + remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing. + } + QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) + ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) + : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path))); + + style->drawItemText( + painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString, + isSelected ? QPalette::HighlightedText : QPalette::Text); + } + } + + void SearchEntryDelegate::LoadBranchPixMaps() + { + AZ::IO::BasicPath absoluteIconPath; + for (int branchType = EntryBranchType::First; branchType != EntryBranchType::Count; ++branchType) + { + QPixmap pixmap; + switch (branchType) + { + case AzToolsFramework::AssetBrowser::EntryBranchType::First: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathFirst; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::Middle: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathMiddle; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::Last: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild: + default: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild; + break; + } + bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str()); + AZ_Assert(pixmapLoadedSuccess, "Error loading Branch Icons in SearchEntryDelegate"); + + m_branchIcons[static_cast(branchType)] = pixmap; + + } + } + + void SearchEntryDelegate::DrawBranchPixMap( + EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const + { + const QPixmap& pixmap = m_branchIcons[branchType]; + + pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + const QSize sizeDelta = size - pixmap.size(); + const QPoint pointDelta = QPoint(sizeDelta.width() / 2, sizeDelta.height() / 2); + painter->drawPixmap(point + pointDelta, pixmap); + } + + } // namespace AssetBrowser } // namespace AzToolsFramework #include "AssetBrowser/Views/moc_EntryDelegate.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h index 643bedec7c..ac68c19248 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h @@ -27,9 +27,19 @@ namespace AzToolsFramework { namespace AssetBrowser { + //! Type of branch icon the delegate should paint. + enum EntryBranchType + { + First, + Middle, + Last, + OneChild, + Count + }; + class AssetBrowserFilterModel; - //! EntryDelegate draws a single item in AssetBrowser + //! EntryDelegate draws a single item in AssetBrowser. class EntryDelegate : public QStyledItemDelegate { @@ -52,5 +62,23 @@ namespace AzToolsFramework //! Draw a thumbnail and return its width int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const; }; + + //! SearchEntryDelegate draws a single item in AssetBrowserTableView. + class SearchEntryDelegate + : public EntryDelegate + { + Q_OBJECT + public: + explicit SearchEntryDelegate(QWidget* parent = nullptr); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + private: + void LoadBranchPixMaps(); + void DrawBranchPixMap(EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const; + + private: + QMap m_branchIcons; + }; } // namespace AssetBrowser } // namespace AzToolsFramework