Merge branch 'development' of https://github.com/o3de/o3de into daimini/FocusMode/setup_tests

This commit is contained in:
Danilo Aimini
2021-09-27 08:39:36 -07:00
27 changed files with 720 additions and 147 deletions
@@ -0,0 +1,200 @@
/*
* 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 <AzCore/IO/FileReader.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
namespace AZ::IO
{
FileReader::FileReader() = default;
FileReader::FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
{
Open(fileIoBase, filePath);
}
FileReader::~FileReader()
{
Close();
}
FileReader::FileReader(FileReader&& other)
{
AZStd::swap(m_file, other.m_file);
AZStd::swap(m_fileIoBase, other.m_fileIoBase);
}
FileReader& FileReader::operator=(FileReader&& other)
{
// Close the current file and take over other file
Close();
m_file = AZStd::move(other.m_file);
m_fileIoBase = AZStd::move(other.m_fileIoBase);
other.m_file = AZStd::monostate{};
other.m_fileIoBase = {};
return *this;
}
bool FileReader::Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
{
// Close file if the FileReader has an instance open
Close();
if (fileIoBase != nullptr)
{
AZ::IO::HandleType fileHandle;
if (fileIoBase->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
{
m_file = fileHandle;
m_fileIoBase = fileIoBase;
return true;
}
}
else
{
AZ::IO::SystemFile file;
if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
{
m_file = AZStd::move(file);
return true;
}
}
return false;
}
bool FileReader::IsOpen() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return *fileHandle != AZ::IO::InvalidHandle;
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->IsOpen();
}
return false;
}
void FileReader::Close()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::IO::FileIOBase* fileIo = m_fileIoBase; fileIo != nullptr)
{
fileIo->Close(*fileHandle);
}
}
m_file = AZStd::monostate{};
m_fileIoBase = {};
}
auto FileReader::Length() const -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize))
{
return fileSize;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Length();
}
return 0;
}
auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
{
return bytesRead;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Read(byteSize, buffer);
}
return 0;
}
auto FileReader::Tell() const -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset))
{
return fileOffset;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Tell();
}
return 0;
}
bool FileReader::Seek(AZ::s64 offset, SeekType type)
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return m_fileIoBase->Seek(*fileHandle, offset, type);
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
systemFile->Seek(offset, static_cast<AZ::IO::SystemFile::SeekMode>(type));
return true;
}
return false;
}
bool FileReader::Eof() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return m_fileIoBase->Eof(*fileHandle);
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Eof();
}
return false;
}
bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
AZ::IO::FixedMaxPathString& pathStringRef = filePath.Native();
if (m_fileIoBase->GetFilename(*fileHandle, pathStringRef.data(), pathStringRef.capacity()))
{
pathStringRef.resize_no_construct(AZStd::char_traits<char>::length(pathStringRef.data()));
return true;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
filePath = systemFile->Name();
return true;
}
return false;
}
}
@@ -0,0 +1,92 @@
/*
* 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/IO/Path/Path_fwd.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/containers/variant.h>
namespace AZ::IO
{
class FileIOBase;
enum class SeekType : AZ::u32;
//! Structure which encapsulates delegates File Read operations
//! to either the FileIOBase or SystemFile classes based if a FileIOBase* instance has been supplied
//! to the FileSystemReader class
//! the SettingsRegistry option to use FileIO
class FileReader
{
using HandleType = AZ::u32;
using FileHandleType = AZStd::variant<AZStd::monostate, AZ::IO::SystemFile, HandleType>;
public:
using SizeType = AZ::u64;
//! Creates FileReader instance in the default state with no file opend
FileReader();
~FileReader();
//! Creates a new FileReader instance and attempts to open the file at the supplied path
//! Uses the FileIOBase instance if supplied
//! @param fileIOBase pointer to fileIOBase instance
//! @param null-terminated filePath to open
FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
//! Takes ownership of the supplied FileReader handle
FileReader(FileReader&& other);
//! Moves ownership of FileReader handle to this instance
FileReader& operator=(FileReader&& other);
//! Opens a File using the FileIOBase instance if non-nullptr
//! Otherwise fall back to use SystemFile
//! @param fileIOBase pointer to fileIOBase instance
//! @param null-terminated filePath to open
//! @return true if the File is opened successfully
bool Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
//! Returns true if a file is currently open
//! @return true if the file is open
bool IsOpen() const;
//! Closes the File
void Close();
//! Retrieve the length of the OpenFile
SizeType Length() const;
//! Attempts to read up to byte size bytes into the supplied buffer
//! @param byteSize - Maximum number of bytes to read
//! @param buffer - Buffer to read bytes into
//! @returns the number of bytes read if the file is open, otherwise 0
SizeType Read(SizeType byteSize, void* buffer);
//! Returns the current file offset
//! @returns file offset if the file is open, otherwise 0
SizeType Tell() const;
//! Seeks within the open file to the offset supplied
//! @param offset File offset to seek to
//! @param type parameter to indicate the reference point to start the seek from
//! @returns true if the file is open and the seek succeeded
bool Seek(AZ::s64 offset, SeekType type);
//! Returns true if the file is open and in the EOF state
bool Eof() const;
//! Store the file path of the open file into the output file path parameter
//! The filePath reference is left unmodified, if the path was not stored
//! @return true if the filePath was stored
bool GetFilePath(AZ::IO::FixedMaxPath& filePath) const;
private:
FileHandleType m_file;
AZ::IO::FileIOBase* m_fileIoBase{};
};
}
@@ -160,12 +160,12 @@ void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
Platform::Seek(m_handle, this, offset, mode);
}
SystemFile::SizeType SystemFile::Tell()
SystemFile::SizeType SystemFile::Tell() const
{
return Platform::Tell(m_handle, this);
}
bool SystemFile::Eof()
bool SystemFile::Eof() const
{
return Platform::Eof(m_handle, this);
}
+2 -2
View File
@@ -72,9 +72,9 @@ namespace AZ
/// Seek in current file.
void Seek(SeekSizeType offset, SeekMode mode);
/// Get the cursor position in the current file.
SizeType Tell();
SizeType Tell() const;
/// Is the cursor at the end of the file?
bool Eof();
bool Eof() const;
/// Get the time the file was last modified.
AZ::u64 ModificationTime();
/// Read data from a file synchronous. Return number of bytes actually read in the buffer.
@@ -10,6 +10,7 @@
#include <cerrno>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileReader.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/error/en.h>
#include <AzCore/NativeUI//NativeUIRequests.h>
@@ -1116,118 +1117,6 @@ namespace AZ
}
}
//! Structure which encapsulates Commands to either the FileIOBase or SystemFile classes based on
//! the SettingsRegistry option to use FileIO
struct SettingsRegistryFileReader
{
using FileHandleType = AZStd::variant<AZStd::monostate, AZ::IO::SystemFile, AZ::IO::HandleType>;
SettingsRegistryFileReader() = default;
SettingsRegistryFileReader(bool useFileIo, const char* filePath)
{
Open(useFileIo, filePath);
}
~SettingsRegistryFileReader()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
{
fileIo->Close(*fileHandle);
}
}
}
bool Open(bool useFileIo, const char* filePath)
{
Close();
if (AZ::IO::FileIOBase* fileIo = useFileIo ? AZ::IO::FileIOBase::GetInstance() : nullptr; fileIo != nullptr)
{
AZ::IO::HandleType fileHandle;
if (fileIo->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
{
m_file = fileHandle;
return true;
}
}
else
{
AZ::IO::SystemFile file;
if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
{
m_file = AZStd::move(file);
return true;
}
}
return false;
}
bool IsOpen() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return *fileHandle != AZ::IO::InvalidHandle;
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->IsOpen();
}
return false;
}
void Close()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
{
fileIo->Close(*fileHandle);
}
}
m_file = AZStd::monostate{};
}
u64 Length() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (u64 fileSize{}; AZ::IO::FileIOBase::GetInstance()->Size(*fileHandle, fileSize))
{
return fileSize;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Length();
}
return 0;
}
AZ::IO::SizeType Read(AZ::IO::SizeType byteSize, void* buffer)
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::u64 bytesRead{}; AZ::IO::FileIOBase::GetInstance()->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
{
return bytesRead;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Read(byteSize, buffer);
}
return 0;
}
FileHandleType m_file;
};
bool SettingsRegistryImpl::MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey,
AZStd::vector<char>& scratchBuffer)
{
@@ -1236,7 +1125,7 @@ namespace AZ
Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-");
SettingsRegistryFileReader fileReader(m_useFileIo, path);
FileReader fileReader(m_useFileIo ? AZ::IO::FileIOBase::GetInstance(): nullptr, path);
if (!fileReader.IsOpen())
{
AZ_Error("Settings Registry", false, R"(Unable to open registry file "%s".)", path);
@@ -6,6 +6,8 @@
*
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileReader.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/TextStreamWriters.h>
@@ -388,8 +390,36 @@ namespace AZ::SettingsRegistryMergeUtils
const ConfigParserSettings& configParserSettings)
{
auto configPath = FindEngineRoot(registry) / filePath;
IO::SystemFile configFile;
if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
IO::FileReader configFile;
bool configFileOpened{};
switch (configParserSettings.m_fileReaderClass)
{
case ConfigParserSettings::FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile:
{
auto fileIo = AZ::IO::FileIOBase::GetInstance();
configFileOpened = configFile.Open(fileIo, configPath.c_str());
break;
}
case ConfigParserSettings::FileReaderClass::UseSystemFileOnly:
{
configFileOpened = configFile.Open(nullptr, configPath.c_str());
break;
}
case ConfigParserSettings::FileReaderClass::UseFileIOOnly:
{
auto fileIo = AZ::IO::FileIOBase::GetInstance();
if (fileIo == nullptr)
{
return false;
}
configFileOpened = configFile.Open(fileIo, configPath.c_str());
break;
}
default:
AZ_Error("SettingsRegistryMergeUtils", false, "An Invalid FileReaderClass enum value has been supplied");
return false;
}
if (!configFileOpened)
{
AZ_Warning("SettingsRegistryMergeUtils", false, R"(Unable to open file "%s")", configPath.c_str());
return false;
@@ -480,7 +510,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZ_Error("SettingsRegistryMergeUtils", false,
R"(The config file "%s" contains a line which is longer than the max line length of %zu.)" "\n"
R"(Parsing will halt. The line content so far is:)" "\n"
R"("%.*s")" "\n", configFile.Name(), configBuffer.max_size(),
R"("%.*s")" "\n", configPath.c_str(), configBuffer.max_size(),
aznumeric_cast<int>(configBuffer.size()), configBuffer.data());
configFileParsed = false;
break;
@@ -155,6 +155,15 @@ namespace AZ::SettingsRegistryMergeUtils
//! structure which is forwarded to the SettingsRegistryInterface MergeCommandLineArgument function
//! The structure contains a functor which returns true if a character is a valid delimiter
SettingsRegistryInterface::CommandLineArgumentSettings m_commandLineSettings;
//! enumeration to indicate if AZ::IO::FileIOBase should be used to open the config file over AZ::IO::SystemFile
enum class FileReaderClass
{
UseFileIOIfAvailableFallbackToSystemFile,
UseSystemFileOnly,
UseFileIOOnly
};
FileReaderClass m_fileReaderClass = FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile;
};
//! Loads basic configuration files which have structures similar to Windows INI files
//! It is inspired by the Python configparser module: https://docs.python.org/3.10/library/configparser.html
@@ -166,6 +166,8 @@ set(FILES
IO/FileIO.cpp
IO/FileIO.h
IO/FileIOEventBus.h
IO/FileReader.cpp
IO/FileReader.h
IO/IOUtils.h
IO/IOUtils.cpp
IO/IStreamer.h
@@ -0,0 +1,72 @@
/*
* 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 <AzCore/IO/FileReader.h>
#include <FileIOBaseTestTypes.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
template <typename FileIOType>
class FileReaderTestFixture
: public ScopedAllocatorSetupFixture
{
public:
void SetUp() override
{
if constexpr (AZStd::is_same_v<FileIOType, TestFileIOBase>)
{
m_fileIo = AZStd::make_unique<TestFileIOBase>();
}
}
void TearDown() override
{
m_fileIo.reset();
}
protected:
AZStd::unique_ptr<AZ::IO::FileIOBase> m_fileIo{};
};
using FileIOTypes = ::testing::Types<void, TestFileIOBase>;
TYPED_TEST_CASE(FileReaderTestFixture, FileIOTypes);
TYPED_TEST(FileReaderTestFixture, ConstructorWithFilePath_OpensFileSuccessfully)
{
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
EXPECT_TRUE(fileReader.IsOpen());
}
TYPED_TEST(FileReaderTestFixture, Open_OpensFileSucessfully)
{
AZ::IO::FileReader fileReader;
fileReader.Open(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
EXPECT_TRUE(fileReader.IsOpen());
}
TYPED_TEST(FileReaderTestFixture, Eof_OnNULDeviceFile_Succeeds)
{
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
EXPECT_TRUE(fileReader.Eof());
}
TYPED_TEST(FileReaderTestFixture, GetFilePath_ReturnsNULDeviceFilename_Succeeds)
{
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
AZ::IO::FixedMaxPath filePath;
EXPECT_TRUE(fileReader.GetFilePath(filePath));
AZ::IO::FixedMaxPath nulFilename{ AZ::IO::SystemFile::GetNullFilename() };
if (this->m_fileIo)
{
EXPECT_TRUE(this->m_fileIo->ResolvePath(nulFilename, nulFilename));
}
EXPECT_EQ(nulFilename, filePath);
}
} // namespace UnitTest
@@ -37,6 +37,7 @@ set(FILES
FileIOBaseTestTypes.h
Geometry2DUtils.cpp
Interface.cpp
IO/FileReaderTests.cpp
IO/Path/PathTests.cpp
IPC.cpp
Jobs.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))
@@ -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)
@@ -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<AssetBrowserTableModel> m_tableModel = nullptr;
QPointer<AssetBrowserFilterModel> m_sourceFilterModel = nullptr;
EntryDelegate* m_delegate = nullptr;
QPointer<AssetBrowserTableModel> m_tableModel;
QPointer<AssetBrowserFilterModel> m_sourceFilterModel;
SearchEntryDelegate* m_delegate = nullptr;
private Q_SLOTS:
void OnContextMenu(const QPoint& point);
@@ -11,12 +11,13 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/AssetBrowser/Views/EntryDelegate.h>
#include <AzCore/Utils/Utils.h>
#include <AzQtComponents/Components/StyledBusyLabel.h>
#include <QApplication>
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>
#include <QPainter>
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<int>(AssetBrowserEntry::Column::Name)
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(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<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();
// 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<const AssetBrowserEntry*>(data);
auto sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(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<int>(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<int>(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<QAbstractItemView*>(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<const AssetBrowserEntry*>(indexBelow.data(AssetBrowserModel::Roles::EntryRole));
auto belowEntry = qvariant_cast<const AssetBrowserEntry*>(indexAbove.data(AssetBrowserModel::Roles::EntryRole));
auto aboveSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(aboveEntry);
auto belowSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(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<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);
}
}
void SearchEntryDelegate::LoadBranchPixMaps()
{
AZ::IO::BasicPath<AZ::IO::FixedMaxPathString> 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<EntryBranchType>(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"
@@ -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<EntryBranchType, QPixmap> m_branchIcons;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework