Asset Browser Search Entries Highlight (#6133)

* Adding hilight to search entries

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Delegate Cleanup

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* delegate cleanup

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* General cleanup

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* reformatting file

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Updated Comments

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Abstracted richText functions

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Extracting highlighting behavior

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Added highlighter to the entity outliner

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Addressed Code Review Comments

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Switched to static functions

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* changed variable name

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Removed unused variable

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Apply changes to the Entity Outliner Model

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>

* Removed duplicated line

Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com>
This commit is contained in:
Ignacio Martinez
2022-01-18 13:29:06 +01:00
committed by GitHub
parent 4cd3568d7b
commit c1bbe3806d
15 changed files with 167 additions and 53 deletions
@@ -17,6 +17,7 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
// AzQtComponents
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
@@ -83,6 +84,9 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
m_ui->m_assetBrowserTableViewWidget->setVisible(false);
m_ui->m_toggleDisplayViewBtn->setVisible(false);
m_ui->m_searchWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
m_assetBrowserModel->SetFilterModel(m_filterModel.data());
if (ed_useNewAssetBrowserTableView)
{
m_ui->m_toggleDisplayViewBtn->setVisible(true);
@@ -53,6 +53,7 @@
#include <AzToolsFramework/ToolsComponents/SelectionComponent.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/Editor/RichTextHighlighter.h>
#include "OutlinerDisplayOptionsMenu.h"
#include "OutlinerSortFilterProxyModel.hxx"
@@ -252,17 +253,7 @@ QVariant OutlinerListModel::dataForName(const QModelIndex& index, int role) cons
if (s_paintingName && !m_filterString.empty())
{
// highlight characters in filter
int highlightTextIndex = 0;
do
{
highlightTextIndex = label.lastIndexOf(QString(m_filterString.c_str()), highlightTextIndex - 1, Qt::CaseInsensitive);
if (highlightTextIndex >= 0)
{
const QString BACKGROUND_COLOR{ "#707070" };
label.insert(static_cast<int>(highlightTextIndex + m_filterString.length()), "</span>");
label.insert(highlightTextIndex, "<span style=\"background-color: " + BACKGROUND_COLOR + "\">");
}
} while(highlightTextIndex > 0);
label = AzToolsFramework::RichTextHighlighter::HighlightText(label, m_filterString.c_str());
}
return label;
}
@@ -2609,16 +2600,11 @@ void OutlinerItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem&
optionV4.widget->style()->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter);
// Now we setup a Text Document so it can draw the rich text
QTextDocument textDoc;
textDoc.setDefaultFont(optionV4.font);
textDoc.setDefaultStyleSheet("body {color: white}");
textDoc.setHtml("<body>" + entityNameRichText + "</body>");
int verticalOffset = GetEntityNameVerticalOffset(entityId);
painter->translate(textRect.topLeft() + QPoint(0, verticalOffset));
textDoc.setTextWidth(textRect.width());
textDoc.drawContents(painter, QRectF(0, 0, textRect.width(), textRect.height()));
painter->restore();
AzToolsFramework::RichTextHighlighter::PaintHighlightedRichText(entityNameRichText, painter, optionV4, textRect);
OutlinerListModel::s_paintingName = false;
}
else
@@ -68,6 +68,11 @@ namespace AzToolsFramework
}
}
QSharedPointer<const StringFilter> AssetBrowserFilterModel::GetStringFilter() const
{
return m_stringFilter;
}
bool AssetBrowserFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
//get the source idx, if invalid early out
@@ -48,7 +48,7 @@ namespace AzToolsFramework
// AssetBrowserComponentNotificationBus
//////////////////////////////////////////////////////////////////////////
void OnAssetBrowserComponentReady() override;
QSharedPointer<const StringFilter> GetStringFilter() const;
Q_SIGNALS:
void filterChanged();
//////////////////////////////////////////////////////////////////////////
@@ -70,7 +70,7 @@ namespace AzToolsFramework
//Asset source name match filter
FilterConstType m_filter;
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...'
QWeakPointer<const StringFilter> m_stringFilter;
QSharedPointer<const StringFilter> m_stringFilter;
QWeakPointer<const CompositeFilter> m_assetTypeFilter;
QCollator m_collator; // cache the collator as its somewhat expensive to constantly create and destroy one.
AZ_POP_DISABLE_WARNING
@@ -13,6 +13,7 @@
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <QMimeData>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QRegularExpression::d': class 'QExplicitlySharedDataPointer<QRegularExpressionPrivate>' needs to have dll-interface to be used by clients of class 'QRegularExpression'
@@ -268,6 +269,21 @@ namespace AzToolsFramework
m_rootEntry = rootEntry;
}
AssetBrowserFilterModel* AssetBrowserModel::GetFilterModel()
{
return m_filterModel;
}
const AssetBrowserFilterModel* AssetBrowserModel::GetFilterModel() const
{
return m_filterModel;
}
void AssetBrowser::AssetBrowserModel::SetFilterModel(AssetBrowserFilterModel* filterModel)
{
m_filterModel = filterModel;
}
QModelIndex AssetBrowserModel::parent(const QModelIndex& child) const
{
if (!child.isValid())
@@ -35,6 +35,7 @@ namespace AzToolsFramework
class AssetBrowserEntry;
class RootAssetBrowserEntry;
class AssetEntryChangeset;
class AssetBrowserFilterModel;
class AssetBrowserModel
: public QAbstractItemModel
@@ -75,7 +76,7 @@ namespace AzToolsFramework
void EndAddEntry(AssetBrowserEntry* parent) override;
void BeginRemoveEntry(AssetBrowserEntry* entry) override;
void EndRemoveEntry() override;
//////////////////////////////////////////////////////////////////////////
// TickBus
//////////////////////////////////////////////////////////////////////////
@@ -84,10 +85,16 @@ namespace AzToolsFramework
AZStd::shared_ptr<RootAssetBrowserEntry> GetRootEntry() const;
void SetRootEntry(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry);
AssetBrowserFilterModel* GetFilterModel();
const AssetBrowserFilterModel* GetFilterModel() const;
void SetFilterModel(AssetBrowserFilterModel* filterModel);
static void SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector<AZ::Data::AssetId>& assetIds);
static void SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector<AssetBrowserEntry*>& entries);
private:
//Non owning pointer
AssetBrowserFilterModel* m_filterModel = nullptr;
AZStd::shared_ptr<RootAssetBrowserEntry> m_rootEntry;
bool m_loaded;
bool m_addingEntry;
@@ -229,6 +229,11 @@ namespace AzToolsFramework
Q_EMIT updatedSignal();
}
QString StringFilter::GetFilterString() const
{
return m_filterString;
}
QString StringFilter::GetNameInternal() const
{
return m_filterString;
@@ -106,6 +106,7 @@ namespace AzToolsFramework
~StringFilter() override = default;
void SetFilterString(const QString& filterString);
QString GetFilterString() const;
protected:
QString GetNameInternal() const override;
@@ -66,6 +66,7 @@ namespace AzToolsFramework
m_tableModel = qobject_cast<AssetBrowserTableModel*>(model);
AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel");
m_sourceFilterModel = qobject_cast<AssetBrowserFilterModel*>(m_tableModel->sourceModel());
m_delegate->Init();
AzQtComponents::TableView::setModel(model);
connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot);
@@ -9,12 +9,16 @@
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/AssetBrowser/Views/EntryDelegate.h>
#include <AzCore/Utils/Utils.h>
#include <AzQtComponents/Components/StyledBusyLabel.h>
#include <AzToolsFramework/Editor/RichTextHighlighter.h>
#include <QApplication>
#include <QTextDocument>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' 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 <QAbstractItemView>
@@ -160,13 +164,20 @@ namespace AzToolsFramework
LoadBranchPixMaps();
}
void SearchEntryDelegate::Init()
{
AssetBrowserModel* assetBrowserModel;
AssetBrowserComponentRequestBus::BroadcastResult(assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel);
AZ_Assert(assetBrowserModel, "Failed to get filebrowser model");
m_assetBrowserFilerModel = assetBrowserModel->GetFilterModel();
}
void SearchEntryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto data = index.data(AssetBrowserModel::Roles::EntryRole);
if (data.canConvert<const AssetBrowserEntry*>())
{
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();
@@ -265,13 +276,21 @@ namespace AzToolsFramework
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<int>(AssetBrowserEntry::Column::Name)
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Name)))
: qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Path)));
style->drawItemText(
painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString,
isSelected ? QPalette::HighlightedText : QPalette::Text);
QStyleOptionViewItem optionV4{ option };
initStyleOption(&optionV4, index);
optionV4.state &= ~(QStyle::State_HasFocus | QStyle::State_Selected);
if (m_assetBrowserFilerModel && m_assetBrowserFilerModel->GetStringFilter()
&& !m_assetBrowserFilerModel->GetStringFilter()->GetFilterString().isEmpty())
{
displayString = RichTextHighlighter::HighlightText(displayString, m_assetBrowserFilerModel->GetStringFilter()->GetFilterString());
}
RichTextHighlighter::PaintHighlightedRichText(displayString, painter, optionV4, remainingRect);
}
}
@@ -70,7 +70,7 @@ namespace AzToolsFramework
Q_OBJECT
public:
explicit SearchEntryDelegate(QWidget* parent = nullptr);
void Init();
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
private:
@@ -78,6 +78,7 @@ namespace AzToolsFramework
void DrawBranchPixMap(EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const;
private:
AssetBrowserFilterModel* m_assetBrowserFilerModel;
QMap<EntryBranchType, QPixmap> m_branchIcons;
};
} // namespace AssetBrowser
@@ -0,0 +1,55 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "RichTextHighlighter.h"
namespace AzToolsFramework
{
QString RichTextHighlighter::HighlightText(const QString& displayString, const QString& matchingSubstring)
{
QString highlightedString = displayString;
int highlightTextIndex = 0;
do
{
highlightTextIndex = highlightedString.lastIndexOf(matchingSubstring, highlightTextIndex - 1, Qt::CaseInsensitive);
if (highlightTextIndex >= 0)
{
const QString backgroundColor{ "#707070" };
highlightedString.insert(static_cast<int>(highlightTextIndex + matchingSubstring.length()), "</span>");
highlightedString.insert(highlightTextIndex, "<span style=\"background-color: " + backgroundColor + "\">");
}
} while (highlightTextIndex > 0);
return highlightedString;
}
void RichTextHighlighter::PaintHighlightedRichText(const QString& highlightedString,QPainter* painter, QStyleOptionViewItem option, QRect availableRect)
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
// Now we setup a Text Document so it can draw the rich text
QTextDocument textDoc;
textDoc.setDefaultFont(option.font);
if (option.state & QStyle::State_Enabled)
{
textDoc.setDefaultStyleSheet("body {color: white}");
}
else
{
textDoc.setDefaultStyleSheet("body {color: #7C7C7C}");
}
textDoc.setHtml("<body>" + highlightedString + "</body>");
painter->translate(availableRect.topLeft());
textDoc.setTextWidth(availableRect.width());
textDoc.drawContents(painter, QRectF(0, 0, availableRect.width(), availableRect.height()));
painter->restore();
}
} // namespace AzToolsFramework
@@ -0,0 +1,36 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/base.h>
#include <QString>
#include <QStyleOptionViewItem>
#include <QTextDocument>
AZ_PUSH_DISABLE_WARNING(4251 4800,"-Wunknown-warning-option") // 4251: class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' 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 <QPainter>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
//! @class RichTextHighlighter
//! @brief Highlights a given string given a matching substring.
class RichTextHighlighter
{
public:
AZ_CLASS_ALLOCATOR(RichTextHighlighter, AZ::SystemAllocator, 0);
RichTextHighlighter() = delete;
static QString HighlightText(const QString& displayString, const QString& matchingSubstring);
static void PaintHighlightedRichText(const QString& highlightedString,QPainter* painter, QStyleOptionViewItem option, QRect availableRect);
};
} // namespace AzToolsFramework
@@ -66,6 +66,7 @@
#include <AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerCacheBus.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <AzToolsFramework/Editor/RichTextHighlighter.h>
////////////////////////////////////////////////////////////////////////////
// EntityOutlinerListModel
@@ -259,17 +260,7 @@ namespace AzToolsFramework
if (s_paintingName && !m_filterString.empty())
{
// highlight characters in filter
int highlightTextIndex = 0;
do
{
highlightTextIndex = label.lastIndexOf(QString(m_filterString.c_str()), highlightTextIndex - 1, Qt::CaseInsensitive);
if (highlightTextIndex >= 0)
{
const QString BACKGROUND_COLOR{ "#707070" };
label.insert(highlightTextIndex + static_cast<int>(m_filterString.length()), "</span>");
label.insert(highlightTextIndex, "<span style=\"background-color: " + BACKGROUND_COLOR + "\">");
}
} while(highlightTextIndex > 0);
label = AzToolsFramework::RichTextHighlighter::HighlightText(label, m_filterString.c_str());
}
return label;
}
@@ -2375,23 +2366,8 @@ namespace AzToolsFramework
optionV4.text.clear();
optionV4.widget->style()->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter);
// Now we setup a Text Document so it can draw the rich text
QTextDocument textDoc;
textDoc.setDefaultFont(optionV4.font);
if (option.state & QStyle::State_Enabled)
{
textDoc.setDefaultStyleSheet("body {color: white}");
}
else
{
textDoc.setDefaultStyleSheet("body {color: #7C7C7C}");
}
textDoc.setHtml("<body>" + entityNameRichText + "</body>");
painter->translate(textRect.topLeft());
textDoc.setTextWidth(textRect.width());
textDoc.drawContents(painter, QRectF(0, 0, textRect.width(), textRect.height()));
AzToolsFramework::RichTextHighlighter::PaintHighlightedRichText(entityNameRichText, painter, optionV4, textRect);
painter->restore();
EntityOutlinerListModel::s_paintingName = false;
}
@@ -123,6 +123,8 @@ set(FILES
ContainerEntity/ContainerEntitySystemComponent.h
Editor/EditorContextMenuBus.h
Editor/EditorSettingsAPIBus.h
Editor/RichTextHighlighter.h
Editor/RichTextHighlighter.cpp
Entity/EditorEntityStartStatus.h
Entity/EditorEntityAPIBus.h
Entity/EditorEntityContextComponent.cpp