Asset Bundler GUI (#427)

The AssetBundler is a new ToolsApplication that allows users to work through the entire Asset Bundling process without ever touching a command line.

* Integrating github/AssetBundler through commit 1d65018

* Asset Bundler bug fixes: platform initialization and GUI styling (#5)

* fixed enabled platform initialization

* fixed the cached engine root. This fixed some of my Seeds tab data issues. The default Engine Seed List appeared, and was able to display the contents on screen.

* updated my notes of active bugs

* changed some casing in various include lines to hopefully fix my linux build

* another include fix for linux

* AssetBundler GUI is now compiling on Mac

* removed some things off of my todo list because the mac build fix actually fixed the visuals! everything's the right color again

* removed the word Lumberyard from the bundler

* Asset bundler bug fixes - Bundles, Gems, and Tests (#9)

* Fixed the Bundle loading and generation problem. Turned out to be a FileWatcher issue.

* turns out gem loading wasn't broken, there were just no existing SeedListFiles for any of the gems loaded by the AutomatedTesting project. I added a default SeedListFile for the PrimitiveAssets Gem.

* fixed some failing AssetBundler Gem tests

* Misunderstood the need to have default seed lists for asset-only gems. removing the previously created seed list file

* Asset bundler bug fixes: Seeds Tab display issues and _dependencies.xml loading (#10)

* Fixed the Project Source column in the Seeds tab

* The AssetBundler will no longer attempt to copy a template version of the ProjectName_dependencies.xml file into your active project. However, it will throw an error if you do not have one. A follow-up ticket has been cut to address this issue.

* updated the AssetBundler icon. This one matches the current style guides

* PR feedback: pass a const ref instead of a value

* PR feedback: safer conversion from a string_view to a QString

Co-authored-by: alexpete <alexpete@amazon.com>
This commit is contained in:
Zaladane
2021-04-30 15:42:10 -07:00
committed by GitHub
parent 4e3846e679
commit a5d4eb5d44
76 changed files with 10163 additions and 63 deletions
+39 -3
View File
@@ -13,9 +13,13 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
include(${CMAKE_CURRENT_SOURCE_DIR}/Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
# AssetBundlerBatch - CLI Application
ly_add_target(
NAME AssetBundler.Static STATIC
NAME AssetBundlerBatch.Static STATIC
NAMESPACE AZ
AUTOMOC
FILES_CMAKE
assetbundlerbatch_files.cmake
INCLUDE_DIRECTORIES
@@ -23,6 +27,9 @@ ly_add_target(
.
BUILD_DEPENDENCIES
PUBLIC
3rdParty::Qt::Core
3rdParty::Qt::Gui
3rdParty::Qt::Widgets
AZ::AzToolsFramework
${additional_dependencies}
)
@@ -32,12 +39,41 @@ ly_add_target(
NAMESPACE AZ
FILES_CMAKE
assetbundlerbatch_exe_files.cmake
COMPILE_DEFINITIONS
PRIVATE
AB_BATCH_MODE
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AssetBundler.Static
AZ::AssetBundlerBatch.Static
)
# AssetBundler - Qt GUI Application
ly_add_target(
NAME AssetBundler ${PAL_TRAIT_BUILD_ASSETBUNDLER_APPLICATION_TYPE}
NAMESPACE AZ
AUTOMOC
AUTOUIC
AUTORCC
FILES_CMAKE
assetbundlergui_files.cmake
assetbundler_exe_files.cmake
Platform/${PAL_PLATFORM_NAME}/assetbundlergui_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
Platform/${PAL_PLATFORM_NAME}
PRIVATE
.
BUILD_DEPENDENCIES
PUBLIC
3rdParty::Qt::Core
3rdParty::Qt::Gui
3rdParty::Qt::Widgets
AZ::AzToolsFramework
AZ::AssetBundlerBatch.Static
${additional_dependencies}
)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
@@ -53,7 +89,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
AZ::AssetBundler.Static
AZ::AssetBundlerBatch.Static
AZ::AzFrameworkTestShared
)
@@ -0,0 +1,13 @@
#
# 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.
#
set (PAL_TRAIT_BUILD_ASSETBUNDLER_APPLICATION_TYPE APPLICATION)
@@ -0,0 +1,14 @@
#
# 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.
#
set(FILES
source/utils/GUIApplicationManager_Linux.cpp
)
@@ -0,0 +1,21 @@
/*
* 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.
*
*/
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
namespace Platform
{
AzQtComponents::WindowDecorationWrapper::Option GetWindowDecorationWrapperOption()
{
return AzQtComponents::WindowDecorationWrapper::OptionDisabled;
}
}
@@ -0,0 +1,13 @@
#
# 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.
#
set (PAL_TRAIT_BUILD_ASSETBUNDLER_APPLICATION_TYPE APPLICATION)
@@ -0,0 +1,14 @@
#
# 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.
#
set(FILES
source/utils/GUIApplicationManager_OSX.cpp
)
@@ -0,0 +1,21 @@
/*
* 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.
*
*/
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
namespace Platform
{
AzQtComponents::WindowDecorationWrapper::Option GetWindowDecorationWrapperOption()
{
return AzQtComponents::WindowDecorationWrapper::OptionDisabled;
}
}
@@ -0,0 +1,13 @@
#
# 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.
#
set (PAL_TRAIT_BUILD_ASSETBUNDLER_APPLICATION_TYPE APPLICATION)
@@ -0,0 +1,14 @@
#
# 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.
#
set(FILES
source/utils/GUIApplicationManager_Win.cpp
)
@@ -0,0 +1,21 @@
/*
* 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.
*
*/
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
namespace Platform
{
AzQtComponents::WindowDecorationWrapper::Option GetWindowDecorationWrapperOption()
{
return AzQtComponents::WindowDecorationWrapper::OptionAutoAttach;
}
}
@@ -0,0 +1,15 @@
#
# 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.
#
set(FILES
source/main.cpp
source/AssetBundler.rc
)
@@ -0,0 +1,70 @@
#
# 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.
#
set(FILES
source/models/AssetBundlerAbstractFileTableModel.h
source/models/AssetBundlerAbstractFileTableModel.cpp
source/models/AssetBundlerFileTableFilterModel.h
source/models/AssetBundlerFileTableFilterModel.cpp
source/models/AssetListFileTableModel.h
source/models/AssetListFileTableModel.cpp
source/models/AssetListTableModel.cpp
source/models/AssetListTableModel.h
source/models/BundleFileListModel.h
source/models/BundleFileListModel.cpp
source/models/RulesFileTableModel.h
source/models/RulesFileTableModel.cpp
source/models/SeedListFileTableModel.h
source/models/SeedListFileTableModel.cpp
source/models/SeedListTableModel.h
source/models/SeedListTableModel.cpp
source/ui/style/AssetBundler.qrc
source/ui/style/AssetBundler.qss
source/ui/style/AssetBundlerConfig.ini
source/ui/style/AssetBundler-Icon-256x256@x2.ico
source/ui/AddSeedDialog.h
source/ui/AddSeedDialog.cpp
source/ui/AddSeedDialog.ui
source/ui/AssetBundlerTabWidget.h
source/ui/AssetBundlerTabWidget.cpp
source/ui/AssetListTabWidget.h
source/ui/AssetListTabWidget.cpp
source/ui/AssetListTabWidget.ui
source/ui/BundleListTabWidget.h
source/ui/BundleListTabWidget.cpp
source/ui/BundleListTabWidget.ui
source/ui/ComparisonDataWidget.h
source/ui/ComparisonDataWidget.cpp
source/ui/ComparisonDataWidget.ui
source/ui/EditSeedDialog.h
source/ui/EditSeedDialog.cpp
source/ui/EditSeedDialog.ui
source/ui/GenerateBundlesModal.h
source/ui/GenerateBundlesModal.cpp
source/ui/GenerateBundlesModal.ui
source/ui/MainWindow.h
source/ui/MainWindow.cpp
source/ui/MainWindow.ui
source/ui/NewFileDialog.h
source/ui/NewFileDialog.cpp
source/ui/NewFileDialog.ui
source/ui/PlatformSelectionWidget.h
source/ui/PlatformSelectionWidget.cpp
source/ui/PlatformSelectionWidget.ui
source/ui/RulesTabWidget.h
source/ui/RulesTabWidget.cpp
source/ui/RulesTabWidget.ui
source/ui/SeedTabWidget.h
source/ui/SeedTabWidget.cpp
source/ui/SeedTabWidget.ui
source/utils/GUIApplicationManager.h
source/utils/GUIApplicationManager.cpp
)
@@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "ui\\style\\AssetBundler-Icon-256x256@x2.ico"
+34 -3
View File
@@ -10,17 +10,48 @@
*
*/
#if defined(AB_BATCH_MODE)
#include <source/utils/applicationManager.h>
#else
#include <source/utils/GUIApplicationManager.h>
#include <AzQtComponents/Utilities/QtPluginPaths.h>
#endif
#if defined(AZ_TESTS_ENABLED)
#include <AzTest/AzTest.h>
DECLARE_AZ_UNIT_TEST_MAIN();
#endif
int main(int argc, char* argv[])
{
#if defined(AZ_TESTS_ENABLED)
INVOKE_AZ_UNIT_TEST_MAIN();
#endif
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
int runSuccess = 0;
{
AssetBundler::ApplicationManager applicationManger(&argc, &argv);
applicationManger.Init();
runSuccess = applicationManger.Run() ? 0 : 1;
//This nested scope is necessary as the applicationManager needs to have its destructor called BEFORE you destroy the allocators
#if defined(AB_BATCH_MODE)
AssetBundler::ApplicationManager applicationManager(&argc, &argv);
#else
// Must be called before using any Qt, or the app won't be able to locate Qt libs
AzQtComponents::PrepareQtPaths();
AssetBundler::GUIApplicationManager applicationManager(&argc, &argv);
#endif
if (!applicationManager.Init())
{
AZ_Error("AssetBundler", false, "AssetBundler initialization failed");
runSuccess = 1;
}
else
{
runSuccess = applicationManager.Run() ? 0 : 1;
}
}
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
return runSuccess;
}
@@ -0,0 +1,233 @@
/*
* 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.
*
*/
#include <source/models/AssetBundlerAbstractFileTableModel.h>
#include <source/utils/utils.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <QSetIterator>
namespace AssetBundler
{
const char* DateTimeFormat = "hh:mm:ss MMM dd, yyyy";
AssetBundlerAbstractFileTableModel::AssetBundlerAbstractFileTableModel(QObject* parent)
: QAbstractTableModel(parent)
{
}
void AssetBundlerAbstractFileTableModel::Reload(const char* fileExtension, const QSet<QString>& watchedFolders, const QSet<QString>& watchedFiles, const AZStd::unordered_map<AZStd::string, AZStd::string>& pathToProjectNameMap)
{
AZStd::vector<AZStd::string> keysToRemove = m_fileListKeys;
// Reload all the files in the watched folders
QString filter = QString("*.%1").arg(fileExtension);
QSetIterator<QString> itr(watchedFolders);
while (itr.hasNext())
{
QDir filesDir(itr.next());
filesDir.setNameFilters({ filter });
for (const QString& fileNameAndExtension : filesDir.entryList(QDir::Files))
{
AZStd::string absolutePath = filesDir.absoluteFilePath(fileNameAndExtension).toUtf8().data();
AZStd::string projectName;
if (pathToProjectNameMap.contains(absolutePath))
{
projectName = pathToProjectNameMap.at(absolutePath);
}
// If a project name is already specified, then the associated file is a default file
LoadFile(absolutePath, projectName, !projectName.empty());
keysToRemove.erase(AZStd::remove(keysToRemove.begin(), keysToRemove.end(), AssetBundler::GenerateKeyFromAbsolutePath(absolutePath)), keysToRemove.end());
}
}
// Reload all the watched files
for (const QString& filePath : watchedFiles)
{
AZStd::string absolutePath = filePath.toUtf8().data();
if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath.c_str()))
{
AZStd::string projectName = pathToProjectNameMap.at(absolutePath);
// If a project name is already specified, then the associated file is a default file
LoadFile(absolutePath, projectName, !projectName.empty());
keysToRemove.erase(AZStd::remove(keysToRemove.begin(), keysToRemove.end(), AssetBundler::GenerateKeyFromAbsolutePath(absolutePath)), keysToRemove.end());
}
}
//Remove nonexistant files from the model
for (const AZStd::string& key : keysToRemove)
{
DeleteFileByKey(key);
}
}
void AssetBundlerAbstractFileTableModel::ReloadFiles(const AZStd::vector<AZStd::string>& absoluteFilePathList, AZStd::unordered_map<AZStd::string, AZStd::string> pathToProjectNameMap)
{
for (const AZStd::string& absoluteFilePath : absoluteFilePathList)
{
if (!AZ::IO::FileIOBase::GetInstance()->Exists(absoluteFilePath.c_str()))
{
// File is not present on-disk, make sure to remove it from the model
DeleteFileByKey(AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath));
continue;
}
// If a project name is already specified, then the associated file is a default file
AZStd::string projectName = pathToProjectNameMap[absoluteFilePath];
LoadFile(absoluteFilePath, projectName, !projectName.empty());
}
}
bool AssetBundlerAbstractFileTableModel::Save(const QModelIndex& selectedIndex)
{
if (!selectedIndex.isValid() || m_keysWithUnsavedChanges.empty())
{
// There is nothing to save
return true;
}
AZStd::string key = GetFileKey(selectedIndex);
if (key.empty())
{
// Error has already been thrown
return false;
}
// Save the file
if (!WriteToDisk(key))
{
return false;
}
// Update the display
m_keysWithUnsavedChanges.erase(key);
QModelIndex topLeftIndex = index(selectedIndex.row(), 0);
QModelIndex bottomRightIndex = index(selectedIndex.row(), columnCount() - 1);
emit dataChanged(topLeftIndex, bottomRightIndex, { Qt::DisplayRole, Qt::FontRole });
return true;
}
bool AssetBundlerAbstractFileTableModel::SaveAll()
{
if (!HasUnsavedChanges())
{
// No need to update all of the elements if we are not changing anything
return true;
}
bool hasSaveErrors = false;
// Save every file with unsaved changes
AZStd::unordered_set<AZStd::string> keysWithErrors;
for (const AZStd::string& key : m_keysWithUnsavedChanges)
{
if (!WriteToDisk(key))
{
// Error has already been thrown
hasSaveErrors = true;
keysWithErrors.emplace(key);
}
}
m_keysWithUnsavedChanges = keysWithErrors;
// Update the display of all elements
QModelIndex firstIndex = index(0, 0);
QModelIndex lastIndex = index(rowCount() - 1, columnCount() - 1);
emit dataChanged(firstIndex, lastIndex, { Qt::DisplayRole, Qt::FontRole });
return !hasSaveErrors;
}
bool AssetBundlerAbstractFileTableModel::HasUnsavedChanges() const
{
return !m_keysWithUnsavedChanges.empty();
}
int AssetBundlerAbstractFileTableModel::rowCount(const QModelIndex& /*parent*/) const
{
return static_cast<int>(m_fileListKeys.size());
}
AZStd::string AssetBundlerAbstractFileTableModel::GetFileKey(const QModelIndex& index) const
{
int row = index.row();
int col = index.column();
if (row >= rowCount() || row < 0 || col >= columnCount() || col < 0)
{
AZ_Error("AssetBundler", false, "Selected index (%i, %i) is out of range.", row, col);
return {};
}
return m_fileListKeys.at(row);
}
const AZStd::vector<AZStd::string>& AssetBundlerAbstractFileTableModel::GetAllFileKeys() const
{
return m_fileListKeys;
}
int AssetBundlerAbstractFileTableModel::GetIndexRowByKey(const AZStd::string& key) const
{
auto it = AZStd::find(m_fileListKeys.begin(), m_fileListKeys.end(), key);
return it == m_fileListKeys.end() ? -1 : static_cast<int>(AZStd::distance(m_fileListKeys.begin(), it));
}
void AssetBundlerAbstractFileTableModel::AddFileKey(const AZStd::string& key)
{
if (AZStd::find(m_fileListKeys.begin(), m_fileListKeys.end(), key) != m_fileListKeys.end())
{
// Key already exists. This could happen when we update existing entires.
return;
}
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_fileListKeys.push_back(key);
endInsertRows();
}
bool AssetBundlerAbstractFileTableModel::RemoveFileKey(const QModelIndex& index)
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return false;
}
int row = index.row();
beginRemoveRows(QModelIndex(), row, row);
m_fileListKeys.erase(m_fileListKeys.begin() + row);
m_keysWithUnsavedChanges.erase(key);
endRemoveRows();
return true;
}
bool AssetBundlerAbstractFileTableModel::DeleteFileByKey(const AZStd::string& key)
{
int indexRow = GetIndexRowByKey(key);
if (indexRow >= 0)
{
return DeleteFile(index(indexRow, 0));
}
return false;
}
} // namespace AssetBundler
@@ -0,0 +1,111 @@
/*
* 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/std/containers/unordered_set.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QAbstractTableModel>
#include <QModelIndex>
#include <QSet>
#include <QString>
namespace AssetBundler
{
extern const char* DateTimeFormat;
//! Provides an abstract model that can be subclassed to create table models used to store information about files found on-disk.
class AssetBundlerAbstractFileTableModel
: public QAbstractTableModel
{
public:
enum DataRoles
{
SortRole = Qt::UserRole + 1,
};
explicit AssetBundlerAbstractFileTableModel(QObject* parent = nullptr);
virtual ~AssetBundlerAbstractFileTableModel() {}
//////////////////////////////////////////////////////////////////////////
// Pure virtual functions
virtual AZStd::vector<AZStd::string> CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms, const QString& project = QString()) = 0;
virtual bool DeleteFile(const QModelIndex& index) = 0;
virtual void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) = 0;
virtual bool WriteToDisk(const AZStd::string& key) = 0;
//! Returns the absolute path of the file at the given index on success, returns an empty string on failure.
virtual AZStd::string GetFileAbsolutePath(const QModelIndex& index) const = 0;
virtual int GetFileNameColumnIndex() const = 0;
virtual int GetTimeStampColumnIndex() const = 0;
//////////////////////////////////////////////////////////////////////////
//! Reload all the data based on the watched folders and files
virtual void Reload(const char* fileExtension, const QSet<QString>& watchedFolders, const QSet<QString>& watchedFiles = QSet<QString>(), const AZStd::unordered_map<AZStd::string, AZStd::string>& pathToProjectNameMap = AZStd::unordered_map<AZStd::string, AZStd::string>());
virtual void ReloadFiles(const AZStd::vector<AZStd::string>& absoluteFilePathList, AZStd::unordered_map<AZStd::string, AZStd::string> pathToProjectNameMap = AZStd::unordered_map<AZStd::string, AZStd::string>());
bool Save(const QModelIndex& selectedIndex);
bool SaveAll();
bool HasUnsavedChanges() const;
//////////////////////////////////////////////////////////////////////////
// QAbstractTableModel overrides
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
//////////////////////////////////////////////////////////////////////////
protected:
//! Verifies input index is in range and returns the associated key.
//! Throws an error and returns an empty string if the input index is out of range.
AZStd::string GetFileKey(const QModelIndex& index) const;
//! Returns an ordered list of every file key in the model.
//! If a proxy model is not used, the order of this list will also be the display order.
const AZStd::vector<AZStd::string>& GetAllFileKeys() const;
//! Get the index row if the file info is stored in the model
//! Returns -1 if the file key doesn't exist.
int GetIndexRowByKey(const AZStd::string& key) const;
//! Adds input key to the end of the list of all keys and notifies the view that a row has been added.
//! When subclassing, instantiate all data associated with this key so the view can update properly.
void AddFileKey(const AZStd::string& key);
//! Verifies input index, signals to the view that rows will be removed, and removes the key found at the input index.
//! When subclassing, be sure to remove all data associated with the key at this index before calling this function.
//! Returns true if the index is successfully removed, and false on failure
bool RemoveFileKey(const QModelIndex& index);
//! Delete a file from the disk and remove it from the model by its key
bool DeleteFileByKey(const AZStd::string& key);
AZStd::unordered_set<AZStd::string> m_keysWithUnsavedChanges;
private:
//! When subclassing: store file information in a map, and add the keys to this vector.
//! Provides a 1:1 mapping between a QModelIndex::row value and a key.
AZStd::vector<AZStd::string> m_fileListKeys;
};
} // namespace AssetBundler
@@ -0,0 +1,62 @@
/*
* 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.
*
*/
#include "AssetBundlerFileTableFilterModel.h"
#include <source/models/AssetBundlerAbstractFileTableModel.h>
#include <QDateTime>
namespace AssetBundler
{
AssetBundlerFileTableFilterModel::AssetBundlerFileTableFilterModel(QObject* parent, int displayNameCol, int dateTimeCol)
: QSortFilterProxyModel(parent)
, m_displayNameCol(displayNameCol)
, m_dateTimeCol(dateTimeCol)
{
}
void AssetBundlerFileTableFilterModel::FilterChanged(const QString& newFilter)
{
setFilterRegExp(newFilter.toLower());
invalidateFilter();
}
bool AssetBundlerFileTableFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
// Override the default implemention since we want to define custom rules here
QModelIndex index = sourceModel()->index(sourceRow, m_displayNameCol, sourceParent);
QRegExp filter(filterRegExp());
return sourceModel()->data(index).toString().toLower().contains(filter);
}
bool AssetBundlerFileTableFilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const
{
// Any column displaying a DateTime string needs to be compared as a DateTime object to ensure
// proper sorting
if (left.column() != m_dateTimeCol || right.column() != m_dateTimeCol)
{
return QSortFilterProxyModel::lessThan(left, right);
}
QVariant leftTime = sourceModel()->data(left, AssetBundlerAbstractFileTableModel::SortRole);
QVariant rightTime = sourceModel()->data(right, AssetBundlerAbstractFileTableModel::SortRole);
if (leftTime.type() != QVariant::DateTime || rightTime.type() != QVariant::DateTime)
{
return QSortFilterProxyModel::lessThan(left, right);
}
return leftTime.toDateTime() < rightTime.toDateTime();
}
}
@@ -0,0 +1,34 @@
/*
* 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 <QSortFilterProxyModel>
namespace AssetBundler
{
class AssetBundlerFileTableFilterModel
: public QSortFilterProxyModel
{
public:
AssetBundlerFileTableFilterModel(QObject* parent, int displayNameCol, int dateTimeCol = -1);
void FilterChanged(const QString& newFilter);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
bool lessThan(const QModelIndex& left, const QModelIndex& right) const override;
int m_displayNameCol = 0;
int m_dateTimeCol = -1;
};
}
@@ -0,0 +1,251 @@
/*
* 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.
*
*/
#include <source/models/AssetListFileTableModel.h>
#include <source/models/AssetListTableModel.h>
#include <source/utils/utils.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <QFileInfo>
#include <QFont>
namespace AssetBundler
{
//////////////////////////////////////////////////////////////////////////////////////////////////
// AssetListFileInfo
//////////////////////////////////////////////////////////////////////////////////////////////////
AssetListFileInfo::AssetListFileInfo(const AZStd::string& absolutePath, const QString& fileName, const AZStd::string& platform)
: m_absolutePath(absolutePath)
, m_fileName(fileName)
, m_platform(QString(platform.c_str()))
{
AzFramework::StringFunc::Path::Normalize(m_absolutePath);
// Modification time will either give us the time the file was last overwritten
// (or the time it was created if it has never been overwritten)
QFileInfo fileInfo(m_absolutePath.c_str());
m_fileCreationTime = fileInfo.fileTime(QFileDevice::FileModificationTime);
// Load the contents of the asset list file into memory
m_assetListModel.reset(new AssetListTableModel(nullptr, absolutePath, platform));
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// AssetListFileTableModel
//////////////////////////////////////////////////////////////////////////////////////////////////
AssetListFileTableModel::AssetListFileTableModel()
: AssetBundlerAbstractFileTableModel()
{
}
QSharedPointer<AssetListTableModel> AssetListFileTableModel::GetAssetListFileContents(const QModelIndex& index)
{
auto assetListFileInfoOutcome = GetAssetFileInfo(index);
if (!assetListFileInfoOutcome.IsSuccess())
{
return QSharedPointer<AssetListTableModel>();
}
return assetListFileInfoOutcome.GetValue()->m_assetListModel;
}
bool AssetListFileTableModel::DeleteFile(const QModelIndex& index)
{
auto assetFileInfoOutcome = GetAssetFileInfo(index);
if (!assetFileInfoOutcome.IsSuccess())
{
return false;
}
AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(assetFileInfoOutcome.GetValue()->m_absolutePath);
if (m_assetListFileInfoMap.find(key) == m_assetListFileInfoMap.end())
{
return false;
}
AssetListFileInfoPtr assetFileInfo = m_assetListFileInfoMap[key];
// Remove file from disk
const char* absolutePath = assetFileInfo->m_absolutePath.c_str();
if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath))
{
if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(absolutePath))
{
return false;
}
auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(absolutePath);
if (!deleteResult)
{
return false;
}
}
// Remove file from model
m_assetListFileInfoMap.erase(key);
RemoveFileKey(index);
return true;
}
void AssetListFileTableModel::LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& /*projectName*/, bool /*isDefaultFile*/)
{
AZStd::string fullFileName;
AzFramework::StringFunc::Path::GetFullFileName(absoluteFilePath.c_str(), fullFileName);
// Get the file name without the platform for display purposes
AZStd::string baseFileName;
AZStd::string platformIdentifier;
AzToolsFramework::SplitFilename(fullFileName, baseFileName, platformIdentifier);
// Read the AssetListFile into memory and store it
AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath);
m_assetListFileInfoMap[key].reset(new AssetListFileInfo(absoluteFilePath, QString(baseFileName.c_str()), platformIdentifier));
AddFileKey(key);
}
bool AssetListFileTableModel::WriteToDisk(const AZStd::string& /*key*/)
{
return true;
}
AZStd::string AssetListFileTableModel::GetFileAbsolutePath(const QModelIndex& index) const
{
auto assetFileInfoOutcome = GetAssetFileInfo(index);
if (!assetFileInfoOutcome.IsSuccess())
{
// Error has already been thrown
return AZStd::string();
}
return assetFileInfoOutcome.GetValue()->m_absolutePath;
}
int AssetListFileTableModel::GetFileNameColumnIndex() const
{
return Column::ColumnFileName;
}
int AssetListFileTableModel::GetTimeStampColumnIndex() const
{
return Column::ColumnFileCreationTime;
}
int AssetListFileTableModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : Column::Max;
}
QVariant AssetListFileTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case Column::ColumnFileName:
return QString(tr("Asset List File"));
case Column::ColumnPlatform:
return QString(tr("Platform"));
case Column::ColumnFileCreationTime:
return QString(tr("Creation Time"));
default:
break;
}
}
return QVariant();
}
QVariant AssetListFileTableModel::data(const QModelIndex& index, int role) const
{
auto assetListFileInfoOutcome = GetAssetFileInfo(index);
if (!assetListFileInfoOutcome.IsSuccess())
{
return QVariant();
}
int col = index.column();
switch (role)
{
case Qt::DisplayRole:
[[fallthrough]];
case SortRole:
{
if (col == Column::ColumnFileName)
{
return assetListFileInfoOutcome.GetValue()->m_fileName;
}
else if (col == Column::ColumnPlatform)
{
return assetListFileInfoOutcome.GetValue()->m_platform;
}
else if (col == Column::ColumnFileCreationTime)
{
if (role == SortRole)
{
return assetListFileInfoOutcome.GetValue()->m_fileCreationTime;
}
else
{
return assetListFileInfoOutcome.GetValue()->m_fileCreationTime.toString(DateTimeFormat);
}
}
else
{
// Returning an empty QString will ensure the checkboxes do not have any text displayed next to them
return QString();
}
}
case Qt::FontRole:
{
if (col == Column::ColumnFileName)
{
QFont boldFont;
boldFont.setBold(true);
return boldFont;
}
}
default:
break;
}
return QVariant();
}
AZ::Outcome<AssetListFileInfoPtr, void> AssetListFileTableModel::GetAssetFileInfo(const QModelIndex& index) const
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return AZ::Failure();
}
if (m_assetListFileInfoMap.find(key) != m_assetListFileInfoMap.end())
{
return AZ::Success(m_assetListFileInfoMap.at(key));
}
else
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot find Asset List File Info");
return AZ::Failure();
}
}
} // namespace AssetBundler
@@ -0,0 +1,88 @@
/*
* 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 <source/models/AssetBundlerAbstractFileTableModel.h>
#include <QDateTime>
#include <QSharedPointer>
namespace AssetBundler
{
class AssetListTableModel;
struct AssetListFileInfo
{
/**
* Stores information about an Asset List File on disk.
*
* @param absolutePath The absolute path of the Asset List File
* @param fileName The name of the Asset List File. This does not include the platform ID
* @param platform The platform for the Asset List File
*/
AssetListFileInfo(const AZStd::string& absolutePath, const QString& fileName, const AZStd::string& platform);
AZStd::string m_absolutePath;
QDateTime m_fileCreationTime;
/// Use QString for display purpose. This can help to avoid losts of string conversion
QString m_fileName;
QString m_platform;
QSharedPointer<AssetListTableModel> m_assetListModel;
};
using AssetListFileInfoPtr = AZStd::shared_ptr<AssetListFileInfo>;
/// Stores AssetListFileInfo, using the absolute path (without the drive letter) of the Asset List file as the key
using AssetListFileInfoMap = AZStd::unordered_map<AZStd::string, AssetListFileInfoPtr>;
class AssetListFileTableModel
: public AssetBundlerAbstractFileTableModel
{
public:
explicit AssetListFileTableModel();
virtual ~AssetListFileTableModel() {}
QSharedPointer<AssetListTableModel> GetAssetListFileContents(const QModelIndex& index);
//////////////////////////////////////////////////////////////////////////
// AssetBundlerAbstractFileTableModel overrides
AZStd::vector<AZStd::string> CreateNewFiles(const AZStd::string& /*absoluteFilePath*/, const AzFramework::PlatformFlags& /*platforms*/, const QString& /*project*/) override { return {}; }
bool DeleteFile(const QModelIndex& index) override;
void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) override;
bool WriteToDisk(const AZStd::string& key) override;
AZStd::string GetFileAbsolutePath(const QModelIndex& index) const override;
int GetFileNameColumnIndex() const override;
int GetTimeStampColumnIndex() const override;
//////////////////////////////////////////////////////////////////////////
// QAbstractListModel overrides
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex& index, int role) const override;
//////////////////////////////////////////////////////////////////////////
enum Column
{
ColumnFileName,
ColumnPlatform,
ColumnFileCreationTime,
Max
};
private:
AZ::Outcome<AssetListFileInfoPtr, void> GetAssetFileInfo(const QModelIndex& index) const;
AssetListFileInfoMap m_assetListFileInfoMap;
};
} // namespace AssetBundler
@@ -0,0 +1,129 @@
/*
* 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.
*
*/
#include <source/models/AssetListTableModel.h>
#include <source/utils/utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace AssetBundler
{
//////////////////////////////////////////////////////////////////////////////////////////////////
// AssetListTableModel
//////////////////////////////////////////////////////////////////////////////////////////////////
AssetListTableModel::AssetListTableModel(QObject* parent, const AZStd::string& absolutePath, const AZStd::string& platform)
: QAbstractTableModel(parent)
{
m_seedListManager.reset(new AzToolsFramework::AssetSeedManager());
if (absolutePath.empty() || platform.empty())
{
return;
}
AZ::Outcome<AzToolsFramework::AssetFileInfoList, AZStd::string> outcome = m_seedListManager->LoadAssetFileInfo(absolutePath);
if (!outcome.IsSuccess())
{
AZ_Error(AssetBundler::AppWindowName, false, "Failed to load the asset file info for %s", absolutePath.c_str());
return;
}
m_assetFileInfoList = outcome.TakeValue();
m_platformId = static_cast<AzFramework::PlatformId>(AzFramework::PlatformHelper::GetPlatformIndexFromName(platform.c_str()));
}
int AssetListTableModel::rowCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : static_cast<int>(m_assetFileInfoList.m_fileInfoList.size());
}
int AssetListTableModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : Column::Max;
}
QVariant AssetListTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case Column::ColumnAssetName:
return QString("Asset Name");
case Column::ColumnRelativePath:
return QString("Relative Path");
case Column::ColumnAssetId:
return QString("Asset ID");
default:
break;
}
}
return QVariant();
}
QVariant AssetListTableModel::data(const QModelIndex& index, int role) const
{
auto assetFileInfoOutcome = GetAssetFileInfo(index);
if (!assetFileInfoOutcome.IsSuccess())
{
return QVariant();
}
switch (role)
{
case Qt::DisplayRole:
{
switch (index.column())
{
case Column::ColumnAssetName:
{
AZStd::string fileName = assetFileInfoOutcome.GetValue().m_assetRelativePath;
AzFramework::StringFunc::Path::GetFullFileName(fileName.c_str(), fileName);
return fileName.c_str();
}
case Column::ColumnRelativePath:
{
return assetFileInfoOutcome.GetValue().m_assetRelativePath.c_str();
}
case Column::ColumnAssetId:
{
AZStd::string assetIdStr;
assetFileInfoOutcome.GetValue().m_assetId.ToString(assetIdStr);
return assetIdStr.c_str();
}
default:
break;
}
}
default:
break;
}
return QVariant();
}
AZ::Outcome<AzToolsFramework::AssetFileInfo&, AZStd::string> AssetListTableModel::GetAssetFileInfo(const QModelIndex& index) const
{
int row = index.row();
int col = index.column();
if (row >= rowCount() || row < 0 || col >= columnCount() || col < 0)
{
return AZ::Failure(AZStd::string::format("Selected index (%i, %i) is out of range", row, col));
}
return AZ::Success(m_assetFileInfoList.m_fileInfoList.at(row));
}
}
@@ -0,0 +1,54 @@
/*
* 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 <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QAbstractTableModel>
namespace AssetBundler
{
class AssetListTableModel
: public QAbstractTableModel
{
public:
explicit AssetListTableModel(QObject* parent = nullptr, const AZStd::string& absolutePath = AZStd::string(), const AZStd::string& platform = "");
virtual ~AssetListTableModel() {}
AZStd::shared_ptr<AzToolsFramework::AssetSeedManager> GetSeedListManager() { return m_seedListManager; }
//////////////////////////////////////////////////////////////////////////
// QAbstractListModel overrides
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex& index, int role) const override;
//////////////////////////////////////////////////////////////////////////
enum Column
{
ColumnAssetName,
ColumnRelativePath,
ColumnAssetId,
Max
};
private:
AZ::Outcome<AzToolsFramework::AssetFileInfo&, AZStd::string> GetAssetFileInfo(const QModelIndex& index) const;
AZStd::shared_ptr<AzToolsFramework::AssetSeedManager> m_seedListManager;
AzToolsFramework::AssetFileInfoList m_assetFileInfoList;
AzFramework::PlatformId m_platformId;
};
}
@@ -0,0 +1,236 @@
/*
* 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.
*
*/
#include <source/models/BundleFileListModel.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzToolsFramework/AssetBundle/AssetBundleComponent.h>
#include <source/utils/utils.h>
#include <QFileInfo>
namespace AssetBundler
{
BundleFileInfo::BundleFileInfo(const AZStd::string& absolutePath)
: m_absolutePath(absolutePath)
, m_compressedSize(0u)
{
AzFramework::StringFunc::Path::Normalize(m_absolutePath);
AZStd::string fileNameStr;
if (AzFramework::StringFunc::Path::GetFileName(m_absolutePath.c_str(), fileNameStr))
{
m_fileName = QString(fileNameStr.c_str());
}
else
{
AZ_Error("AssetBundler", false, "Failed to get file name from %s", m_absolutePath.c_str());
}
// Modification time will either give us the time the file was last overwritten
// (or the time it was created if it has never been overwritten)
QFileInfo fileInfo(m_absolutePath.c_str());
m_fileCreationTime = fileInfo.fileTime(QFileDevice::FileModificationTime);
}
BundleFileListModel::BundleFileListModel()
: AssetBundlerAbstractFileTableModel()
{
}
bool BundleFileListModel::DeleteFile(const QModelIndex& index)
{
AZStd::string fileKey = GetFileKey(index);
if (fileKey.empty())
{
// Error has already been thrown
return false;
}
auto bundleFileInfoIt = m_bundleFileInfoMap.find(fileKey);
if (bundleFileInfoIt == m_bundleFileInfoMap.end())
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to find Bundle Info with key ( %s )", fileKey.c_str());
return false;
}
BundleFileInfoPtr bundleFileInfo = bundleFileInfoIt->second;
// Remove file from disk
const char* absolutePath = bundleFileInfo->m_absolutePath.c_str();
if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath))
{
if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(absolutePath))
{
AZ_Error(AssetBundler::AppWindowName, false, "File (%s) is Read-Only. Please check your version control and try again.", absolutePath);
return false;
}
auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(absolutePath);
if (!deleteResult)
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to delete: %s", absolutePath);
return false;
}
}
// Remove file from Model
m_bundleFileInfoMap.erase(fileKey);
RemoveFileKey(index);
return true;
}
int BundleFileListModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : Column::Max;
}
QVariant BundleFileListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case Column::ColumnFileName:
return QString(tr("Name"));
case Column::ColumnFileCreationTime:
return QString(tr("Creation Time"));
default:
break;
}
}
return QVariant();
}
QVariant BundleFileListModel::data(const QModelIndex& index, int role) const
{
auto bundleInfoOutcome = GetBundleInfo(index);
if (!bundleInfoOutcome.IsSuccess())
{
return QVariant();
}
int col = index.column();
switch (role)
{
case Qt::DisplayRole:
[[fallthrough]];
case SortRole:
{
if (col == Column::ColumnFileName)
{
return bundleInfoOutcome.GetValue()->m_fileName;
}
else if (col == Column::ColumnFileCreationTime)
{
if (role == SortRole)
{
return bundleInfoOutcome.GetValue()->m_fileCreationTime;
}
else
{
return bundleInfoOutcome.GetValue()->m_fileCreationTime.toString(DateTimeFormat);
}
}
}
default:
break;
}
return QVariant();
}
AZStd::string BundleFileListModel::GetFileAbsolutePath(const QModelIndex& index) const
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return key;
}
return m_bundleFileInfoMap.at(key)->m_absolutePath;
}
int BundleFileListModel::GetFileNameColumnIndex() const
{
return Column::ColumnFileName;
}
int BundleFileListModel::GetTimeStampColumnIndex() const
{
return Column::ColumnFileCreationTime;
}
void BundleFileListModel::LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& /*projectName*/, bool /*isDefaultFile*/)
{
AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath);
BundleFileInfo* newInfo = new BundleFileInfo(absoluteFilePath);
QFile bundleFile(absoluteFilePath.c_str());
if (bundleFile.open(QIODevice::ReadOnly))
{
newInfo->m_compressedSize = bundleFile.size();
bundleFile.close();
}
else
{
AZ_Error("AssetBundler", false, "Failed to open file at %s", absoluteFilePath.c_str());
}
auto manifest = AzToolsFramework::AssetBundleComponent::GetManifestFromBundle(absoluteFilePath);
if (manifest != nullptr)
{
for (auto& bundleName : manifest->GetDependentBundleNames())
{
newInfo->m_relatedBundles.push_back(bundleName.c_str());
}
}
else
{
AZ_Error("AssetBundler", false, "Failed to get manifest from bundle at %s", absoluteFilePath.c_str());
}
m_bundleFileInfoMap[key].reset(newInfo);
// Add it to the list that gets displayed by AssetBundlerAbstractFileTableModel. Make sure that
// AddFileKey is called after m_bundleFileInfoMap is reset since the filterAcceptsRow funtion
// of the filter model will be called when a new row is inserted and it could cause a crash
// without valid data being set
AddFileKey(key);
}
AZ::Outcome<BundleFileInfoPtr, void> BundleFileListModel::GetBundleInfo(const QModelIndex& index) const
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return AZ::Failure();
}
if (m_bundleFileInfoMap.find(key) != m_bundleFileInfoMap.end())
{
return AZ::Success(m_bundleFileInfoMap.at(key));
}
else
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot find Bundle File Info");
return AZ::Failure();
}
}
}
@@ -0,0 +1,74 @@
/*
* 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 <source/models/AssetBundlerAbstractFileTableModel.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <QDateTime>
namespace AssetBundler
{
struct BundleFileInfo
{
AZStd::string m_absolutePath;
QString m_fileName;
QDateTime m_fileCreationTime;
AZ::u64 m_compressedSize;
QStringList m_relatedBundles;
BundleFileInfo(const AZStd::string& absolutePath);
};
using BundleFileInfoPtr = AZStd::shared_ptr<BundleFileInfo>;
using BundleFileInfoMap = AZStd::unordered_map<AZStd::string, BundleFileInfoPtr>;
class BundleFileListModel
: public AssetBundlerAbstractFileTableModel
{
public:
explicit BundleFileListModel();
virtual ~BundleFileListModel() {}
AZStd::vector<AZStd::string> CreateNewFiles(const AZStd::string& /*absoluteFilePath*/, const AzFramework::PlatformFlags& /*platforms*/, const QString& /*project*/) override { return {}; }
bool DeleteFile(const QModelIndex& index) override;
void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) override;
bool WriteToDisk(const AZStd::string& /*key*/) override { return true; }
AZStd::string GetFileAbsolutePath(const QModelIndex& index) const override;
int GetFileNameColumnIndex() const override;
int GetTimeStampColumnIndex() const override;
AZ::Outcome<BundleFileInfoPtr, void> GetBundleInfo(const QModelIndex& index) const;
//////////////////////////////////////////////////////////////////////////
// QAbstractTableModel overrides
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex& index, int role) const override;
//////////////////////////////////////////////////////////////////////////
enum Column
{
ColumnFileName,
ColumnFileCreationTime,
Max
};
private:
BundleFileInfoMap m_bundleFileInfoMap;
};
}
@@ -0,0 +1,305 @@
/*
* 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.
*
*/
#include <source/models/RulesFileTableModel.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <QFileInfo>
#include <QFont>
namespace AssetBundler
{
RulesFileInfo::RulesFileInfo(const AZStd::string& absolutePath, const QString& fileName, bool loadFromFile)
: m_absolutePath(absolutePath)
, m_fileName(fileName)
{
AzFramework::StringFunc::Path::Normalize(m_absolutePath);
m_comparisonSteps = AZStd::make_shared<AzToolsFramework::AssetFileInfoListComparison>();
if (loadFromFile)
{
auto outcome = AzToolsFramework::AssetFileInfoListComparison::Load(m_absolutePath);
if (!outcome.IsSuccess())
{
AZ_Error("AssetBundler", false, outcome.GetError().c_str());
return;
}
m_comparisonSteps = AZStd::make_unique<AzToolsFramework::AssetFileInfoListComparison>(outcome.TakeValue());
QFileInfo fileInfo(m_absolutePath.c_str());
m_fileModificationTime = fileInfo.fileTime(QFileDevice::FileModificationTime);
}
else
{
m_fileModificationTime = QDateTime::currentDateTime();
}
}
bool RulesFileInfo::SaveRulesFile()
{
if (!m_comparisonSteps->Save(m_absolutePath))
{
return false;
}
m_hasUnsavedChanges = false;
m_fileModificationTime = QDateTime::currentDateTime();
return true;
}
RulesFileTableModel::RulesFileTableModel()
: AssetBundlerAbstractFileTableModel()
{
}
AZStd::vector<AZStd::string> RulesFileTableModel::CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& /*platforms*/, const QString& /*project*/)
{
if (absoluteFilePath.empty())
{
AZ_Error(AssetBundler::AppWindowName, false, "Input file path is empty");
return {};
}
// Create a platform-specific file path
if (AZ::IO::FileIOBase::GetInstance()->Exists(absoluteFilePath.c_str()))
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot Create New File: (%s) already exists", absoluteFilePath.c_str());
return {};
}
// Get the file name without the extension for display purposes
AZStd::string fileName = absoluteFilePath;
AzToolsFramework::RemovePlatformIdentifier(fileName);
AzFramework::StringFunc::Path::GetFileName(fileName.c_str(), fileName);
// Create a Rules File and save it to disk
AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath);
RulesFileInfoPtr newRulesFile = AZStd::make_shared<RulesFileInfo>(absoluteFilePath, QString(fileName.c_str()), false);
if (!newRulesFile->SaveRulesFile())
{
return {};
}
// Add the new file to the model
m_rulesFileInfoMap[key] = newRulesFile;
AddFileKey(key);
return { absoluteFilePath };
}
bool RulesFileTableModel::DeleteFile(const QModelIndex& index)
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return false;
}
RulesFileInfoPtr rulesFileInfo = m_rulesFileInfoMap[key];
// Remove file from disk
if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(rulesFileInfo->m_absolutePath.c_str()))
{
AZ_Error(AssetBundler::AppWindowName, false, "File (%s) is Read-Only. Please check your version control and try again.", rulesFileInfo->m_absolutePath.c_str());
return false;
}
auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(rulesFileInfo->m_absolutePath.c_str());
if (!deleteResult)
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to delete: %s", rulesFileInfo->m_absolutePath.c_str());
return false;
}
// Remove file from model
m_rulesFileInfoMap.erase(key);
RemoveFileKey(index);
return true;
}
void RulesFileTableModel::LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& /*projectName*/, bool /*isDefaultFile*/)
{
// Get the file name without the extension for display purposes
AZStd::string fileName(absoluteFilePath);
AzFramework::StringFunc::Path::GetFileName(fileName.c_str(), fileName);
// Read the Rules file into memory and store it
AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath);
auto fileInfoIt = m_rulesFileInfoMap.find(key);
if (fileInfoIt != m_rulesFileInfoMap.end() && fileInfoIt->second->m_hasUnsavedChanges)
{
AZ_Warning(AssetBundler::AppWindowName, false, "Rules File %s has unsaved changes and couldn't be reloaded", absoluteFilePath.c_str());
return;
}
m_rulesFileInfoMap[key] = AZStd::make_shared<RulesFileInfo>(absoluteFilePath, QString(fileName.c_str()), true);
AddFileKey(key);
}
bool RulesFileTableModel::WriteToDisk(const AZStd::string& key)
{
auto rulesFileIt = m_rulesFileInfoMap.find(key);
return rulesFileIt != m_rulesFileInfoMap.end() && rulesFileIt->second->SaveRulesFile();
}
AZStd::string RulesFileTableModel::GetFileAbsolutePath(const QModelIndex& index) const
{
RulesFileInfoPtr rulesFileInfo = GetRulesFileInfoPtr(index);
if (!rulesFileInfo)
{
return AZStd::string();
}
return rulesFileInfo->m_absolutePath;
}
int RulesFileTableModel::GetFileNameColumnIndex() const
{
return Column::ColumnFileName;
}
int RulesFileTableModel::GetTimeStampColumnIndex() const
{
return Column::ColumnFileModificationTime;
}
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> RulesFileTableModel::GetComparisonSteps(const QModelIndex& index) const
{
RulesFileInfoPtr rulesFileInfo = GetRulesFileInfoPtr(index);
if (!rulesFileInfo)
{
return nullptr;
}
return rulesFileInfo->m_comparisonSteps;
}
bool RulesFileTableModel::MarkFileChanged(const QModelIndex& index)
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return false;
}
m_rulesFileInfoMap[key]->m_hasUnsavedChanges = true;
// Update display so the user knows there are unsaved changes
m_keysWithUnsavedChanges.emplace(key);
QModelIndex changedIndex = QAbstractTableModel::index(index.row(), Column::ColumnFileName);
emit dataChanged(changedIndex, changedIndex, { Qt::DisplayRole, Qt::FontRole });
return true;
}
int RulesFileTableModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : Column::Max;
}
QVariant RulesFileTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case Column::ColumnFileName:
return QString(tr("Name"));
case Column::ColumnFileModificationTime:
return QString(tr("Modification Time"));
default:
break;
}
}
return QVariant();
}
QVariant RulesFileTableModel::data(const QModelIndex& index, int role) const
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
return QVariant();
}
RulesFileInfoPtr rulesFileInfo = m_rulesFileInfoMap.at(key);
int col = index.column();
bool hasUnsavedChanges = rulesFileInfo->m_hasUnsavedChanges;
switch (role)
{
case Qt::DisplayRole:
[[fallthrough]];
case SortRole:
{
if (col == Column::ColumnFileName)
{
QString displayName = rulesFileInfo->m_fileName;
if (hasUnsavedChanges)
{
displayName.append("*");
}
return displayName;
}
else if (col == Column::ColumnFileModificationTime)
{
if (role == SortRole)
{
return rulesFileInfo->m_fileModificationTime;
}
else
{
return rulesFileInfo->m_fileModificationTime.toString(DateTimeFormat);
}
}
break;
}
case Qt::FontRole:
{
if (col == Column::ColumnFileName && hasUnsavedChanges)
{
QFont boldFont;
boldFont.setBold(true);
return boldFont;
}
break;
}
default:
break;
}
return QVariant();
}
RulesFileInfoPtr RulesFileTableModel::GetRulesFileInfoPtr(const QModelIndex& index) const
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
return nullptr;
}
return m_rulesFileInfoMap.at(key);
}
} // namespace AssetBundler
@@ -0,0 +1,88 @@
/*
* 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 <source/models/AssetBundlerAbstractFileTableModel.h>
#include <source/utils/utils.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <QDateTime>
namespace AssetBundler
{
//! Stores information about a Rules file on disk.
struct RulesFileInfo
{
RulesFileInfo(const AZStd::string& absolutePath, const QString& fileName, bool loadFromFile);
bool SaveRulesFile();
AZStd::string m_absolutePath;
QString m_fileName;
QDateTime m_fileModificationTime;
bool m_hasUnsavedChanges = false;
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> m_comparisonSteps;
};
using RulesFileInfoPtr = AZStd::shared_ptr<RulesFileInfo>;
using RulesFileInfoMap = AZStd::unordered_map<AZStd::string, RulesFileInfoPtr>;
class RulesFileTableModel
: public AssetBundlerAbstractFileTableModel
{
public:
RulesFileTableModel();
virtual ~RulesFileTableModel() {}
AZStd::vector<AZStd::string> CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms = AzFramework::PlatformFlags::Platform_NONE, const QString& project = QString()) override;
bool DeleteFile(const QModelIndex& index) override;
void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) override;
bool WriteToDisk(const AZStd::string& key) override;
AZStd::string GetFileAbsolutePath(const QModelIndex& index) const override;
int GetFileNameColumnIndex() const override;
int GetTimeStampColumnIndex() const override;
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> GetComparisonSteps(const QModelIndex& index) const;
bool MarkFileChanged(const QModelIndex& index);
//////////////////////////////////////////////////////////////////////////
// QAbstractTableModel overrides
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex& index, int role) const override;
//////////////////////////////////////////////////////////////////////////
enum Column
{
ColumnFileName,
ColumnFileModificationTime,
Max
};
private:
RulesFileInfoMap m_rulesFileInfoMap;
RulesFileInfoPtr GetRulesFileInfoPtr(const QModelIndex& index) const;
};
} // namespace AssetBundler
@@ -0,0 +1,601 @@
/*
* 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.
*
*/
#include <source/models/SeedListFileTableModel.h>
#include <source/models/SeedListTableModel.h>
#include <source/ui/SeedTabWidget.h>
#include <source/utils/utils.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalog.h>
#include <QFileInfo>
#include <QFont>
namespace AssetBundler
{
//////////////////////////////////////////////////////////////////////////////////////////////////
// SeedListFileInfo
//////////////////////////////////////////////////////////////////////////////////////////////////
SeedListFileInfo::SeedListFileInfo(
const AZStd::string& absolutePath,
const QString& fileName,
const QString& project,
bool loadFromFile,
bool isDefaultSeedList,
const AZStd::vector<AZStd::string>& defaultSeeds,
const AzFramework::PlatformFlags& platforms)
: m_absolutePath(absolutePath)
, m_fileName(fileName)
, m_project(project)
, m_isDefaultSeedList(isDefaultSeedList)
{
AzFramework::StringFunc::Path::Normalize(m_absolutePath);
// Load the contents of the seed file into memory
if (loadFromFile)
{
m_seedListModel.reset(new SeedListTableModel(nullptr, absolutePath));
QFileInfo fileInfo(m_absolutePath.c_str());
m_fileModificationTime = fileInfo.fileTime(QFileDevice::FileModificationTime);
}
else
{
m_seedListModel.reset(new SeedListTableModel(nullptr, "", defaultSeeds, platforms));
m_fileModificationTime = QDateTime::currentDateTime();
}
}
bool SeedListFileInfo::SaveSeedFile()
{
if (m_seedListModel->Save(m_absolutePath))
{
m_fileModificationTime = QDateTime::currentDateTime();
return true;
}
return false;
}
bool SeedListFileInfo::HasUnsavedChanges()
{
return m_seedListModel->HasUnsavedChanges();
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// SeedListFileTableModel
//////////////////////////////////////////////////////////////////////////////////////////////////
SeedListFileTableModel::SeedListFileTableModel(SeedTabWidget* parentSeedTabWidget)
: AssetBundlerAbstractFileTableModel()
, m_seedTabWidget(parentSeedTabWidget)
{
m_inMemoryDefaultSeedList.reset(new SeedListFileInfo("", "", "", false));
}
SeedListFileTableModel::~SeedListFileTableModel()
{
m_seedTabWidget = nullptr;
}
void SeedListFileTableModel::AddDefaultSeedsToInMemoryList(const AZStd::vector<AZStd::string>& defaultSeeds, const char* projectName, const AzFramework::PlatformFlags& platforms)
{
m_inMemoryDefaultSeedList.reset(new SeedListFileInfo(m_inMemoryDefaultSeedListKey, tr("DefaultSeeds"), QString(projectName), false, true, defaultSeeds, platforms));
}
AZStd::vector<AZStd::string> SeedListFileTableModel::CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& /*platforms*/, const QString& project)
{
if (absoluteFilePath.empty())
{
AZ_Error(AssetBundler::AppWindowName, false, "Input file path is empty.");
return {};
}
// Get the file name without the extension for display purposes
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(absoluteFilePath.c_str(), fileName);
// Create a Seed List File and save it to disk
AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath);
AZStd::shared_ptr<SeedListFileInfo> newSeedListFile = AZStd::make_shared<SeedListFileInfo>(absoluteFilePath, QString(fileName.c_str()), project, false);
newSeedListFile->m_seedListModel->SetHasUnsavedChanges(true);
bool saveResult = newSeedListFile->SaveSeedFile();
if (!saveResult)
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to create Seed List File: %s", absoluteFilePath.c_str());
return {};
}
// Add new file to the model
m_seedListFileInfoMap[key] = newSeedListFile;
AddFileKey(key);
AZStd::vector<AZStd::string> createdFiles = { absoluteFilePath };
return createdFiles;
}
bool SeedListFileTableModel::DeleteFile(const QModelIndex& index)
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return false;
}
if (m_seedListFileInfoMap.find(key) == m_seedListFileInfoMap.end())
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to find Seed File Info with key ( %s )", key.c_str());
return false;
}
SeedListFileInfoPtr seedFileInfo = m_seedListFileInfoMap[key];
// Remove file from disk
const char* absolutePath = seedFileInfo->m_absolutePath.c_str();
if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath))
{
if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(absolutePath))
{
AZ_Error(AssetBundler::AppWindowName, false, "File (%s) is Read-Only. Please check your version control and try again.", absolutePath);
return false;
}
auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(absolutePath);
if (!deleteResult)
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to delete: %s", absolutePath);
return false;
}
}
// Remove file from model
m_seedListFileInfoMap.erase(key);
RemoveFileKey(index);
return true;
}
void SeedListFileTableModel::Reload(const char* fileExtension, const QSet<QString>& watchedFolders, const QSet<QString>& watchedFiles, const AZStd::unordered_map<AZStd::string, AZStd::string>& pathToProjectNameMap)
{
// Load in the Seed List files from disk
AssetBundlerAbstractFileTableModel::Reload(fileExtension, watchedFolders, watchedFiles, pathToProjectNameMap);
// Add the in-memory Default Seed List to the model
m_seedListFileInfoMap[m_inMemoryDefaultSeedListKey] = m_inMemoryDefaultSeedList;
AddFileKey(m_inMemoryDefaultSeedListKey);
}
void SeedListFileTableModel::LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName, bool isDefaultFile)
{
// Get the file name without the extension for display purposes
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(absoluteFilePath.c_str(), fileName);
// Read the SeedListFile into memory and store it
AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath);
auto fileInfoIt = m_seedListFileInfoMap.find(key);
if (fileInfoIt != m_seedListFileInfoMap.end() && fileInfoIt->second->HasUnsavedChanges())
{
AZ_Warning(AssetBundler::AppWindowName, false, "Seed List File %s has unsaved changes and couldn't be reloaded", absoluteFilePath.c_str());
return;
}
AZStd::string projectNameOnDisplay = projectName;
if (projectName.empty())
{
auto outcome = AssetBundler::GetCurrentProjectName();
if (!outcome.IsSuccess())
{
AZ_Error(AssetBundler::AppWindowName, false, outcome.TakeError().c_str());
return;
}
projectNameOnDisplay = outcome.TakeValue();
}
m_seedListFileInfoMap[key].reset(new SeedListFileInfo(absoluteFilePath, QString(fileName.c_str()), QString(projectNameOnDisplay.c_str()), true, isDefaultFile));
AddFileKey(key);
}
void SeedListFileTableModel::SelectDefaultSeedLists(bool setSelected)
{
for (const AZStd::string& key : GetAllFileKeys())
{
auto seedFileInfo = m_seedListFileInfoMap[key];
if (!seedFileInfo->m_isDefaultSeedList)
{
continue;
}
seedFileInfo->m_isChecked = setSelected;
if(setSelected)
{
m_checkedSeedListFiles.insert(key);
}
else
{
m_checkedSeedListFiles.erase(key);
}
}
m_seedTabWidget->SetGenerateAssetListsButtonEnabled(!m_checkedSeedListFiles.empty());
// Update the Check State display of all elements
QModelIndex firstIndex = index(0, 0);
QModelIndex lastIndex = index(rowCount() - 1, columnCount() - 1);
emit dataChanged(firstIndex, lastIndex, { Qt::CheckStateRole });
}
AZStd::vector<AZStd::string> SeedListFileTableModel::GenerateAssetLists(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms)
{
if (!m_checkedSeedListFiles.size())
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot Generate Asset List File(s): No Seed List Files are selected");
return {};
}
if (absoluteFilePath.empty())
{
AZ_Error(AssetBundler::AppWindowName, false, "File path cannot be empty");
return {};
}
if(platforms == AzFramework::PlatformFlags::Platform_NONE)
{
AZ_Error(AssetBundler::AppWindowName, false, "Input platform cannot be empty");
return {};
}
// Get all of the Seeds into one AssetSeedManager
AzToolsFramework::AssetSeedManager assetSeedManager;
if (m_checkedSeedListFiles.find(m_inMemoryDefaultSeedListKey) != m_checkedSeedListFiles.end())
{
// The In-Memory Default Seed List can't be loaded from a file on disk, it is a special case
assetSeedManager = *m_inMemoryDefaultSeedList->m_seedListModel->GetSeedListManager().get();
m_checkedSeedListFiles.erase(m_inMemoryDefaultSeedListKey);
}
for (const auto& checkedSeedFileKey : m_checkedSeedListFiles)
{
assetSeedManager.Load(m_seedListFileInfoMap[checkedSeedFileKey]->m_absolutePath);
}
// Generate an AssetList for every input platform
AZStd::vector<AZStd::string> createdFiles;
for (const auto& platformIndex : AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platforms))
{
AZStd::string platformSpecificCachePath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(platformIndex);
AzFramework::StringFunc::Path::StripFullName(platformSpecificCachePath);
FilePath platformSpecificPath(absoluteFilePath, AZStd::string(AzFramework::PlatformHelper::GetPlatformName(platformIndex)));
if (assetSeedManager.SaveAssetFileInfo(platformSpecificPath.AbsolutePath(), AzFramework::PlatformHelper::GetPlatformFlagFromPlatformIndex(platformIndex)))
{
createdFiles.emplace_back(platformSpecificPath.AbsolutePath());
}
}
return createdFiles;
}
QSharedPointer<SeedListTableModel> SeedListFileTableModel::GetSeedListFileContents(const QModelIndex& index)
{
auto seedFileInfoOutcome = GetSeedFileInfo(index);
if (!seedFileInfoOutcome.IsSuccess())
{
return QSharedPointer<SeedListTableModel>();
}
return seedFileInfoOutcome.GetValue()->m_seedListModel;
}
bool SeedListFileTableModel::SetSeedPlatforms(const QModelIndex& seedFileIndex, const QModelIndex& seedIndex, const AzFramework::PlatformFlags& platforms)
{
AZStd::string key = GetFileKey(seedFileIndex);
if (key.empty())
{
// Error has already been thrown
return false;
}
if (m_seedListFileInfoMap.find(key) == m_seedListFileInfoMap.end())
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to find Seed File Info with key ( %s )", key.c_str());
return false;
}
SeedListFileInfoPtr seedFileInfo = m_seedListFileInfoMap[key];
if (!seedFileInfo->m_seedListModel->SetSeedPlatforms(seedIndex, platforms))
{
// Error has already been thrown
return false;
}
// Update display so the user knows there are unsaved changes
m_keysWithUnsavedChanges.insert(key);
QModelIndex changedIndex = QAbstractTableModel::index(seedFileIndex.row(), Column::ColumnFileName);
emit dataChanged(changedIndex, changedIndex, { Qt::DisplayRole, Qt::FontRole });
return true;
}
bool SeedListFileTableModel::AddSeed(const QModelIndex& seedFileIndex, const AZStd::string& seedRelativePath, const AzFramework::PlatformFlags& platforms)
{
AZStd::string key = GetFileKey(seedFileIndex);
if (key.empty())
{
// Error has already been thrown
return false;
}
if (m_seedListFileInfoMap.find(key) == m_seedListFileInfoMap.end())
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to find Seed File Info with key ( %s )", key.c_str());
return false;
}
SeedListFileInfoPtr seedFileInfo = m_seedListFileInfoMap[key];
if (!seedFileInfo->m_seedListModel->AddSeed(seedRelativePath, platforms))
{
// Error has already been thrown
return false;
}
// Update display so the user knows there are unsaved changes
m_keysWithUnsavedChanges.insert(key);
QModelIndex changedIndex = QAbstractTableModel::index(seedFileIndex.row(), Column::ColumnFileName);
emit dataChanged(changedIndex, changedIndex, { Qt::DisplayRole, Qt::FontRole });
return true;
}
bool SeedListFileTableModel::RemoveSeed(const QModelIndex& seedFileIndex, const QModelIndex& seedIndex)
{
AZStd::string key = GetFileKey(seedFileIndex);
if (key.empty())
{
// Error has already been thrown
return false;
}
if (m_seedListFileInfoMap.find(key) == m_seedListFileInfoMap.end())
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to find Seed File Info with key ( %s )", key.c_str());
return false;
}
SeedListFileInfoPtr seedFileInfo = m_seedListFileInfoMap[key];
if (!seedFileInfo->m_seedListModel->RemoveSeed(seedIndex))
{
// Error has already been thrown
return false;
}
// Update display so the user knows there are unsaved changes
m_keysWithUnsavedChanges.insert(key);
QModelIndex changedIndex = QAbstractTableModel::index(seedFileIndex.row(), Column::ColumnFileName);
emit dataChanged(changedIndex, changedIndex, { Qt::DisplayRole, Qt::FontRole });
return true;
}
bool SeedListFileTableModel::WriteToDisk(const AZStd::string& key)
{
if (key == m_inMemoryDefaultSeedListKey)
{
return true;
}
auto seedListFileIt = m_seedListFileInfoMap.find(key);
return seedListFileIt != m_seedListFileInfoMap.end() && seedListFileIt->second->SaveSeedFile();
}
AZStd::string SeedListFileTableModel::GetFileAbsolutePath(const QModelIndex& index) const
{
auto seedFileInfoOutcome = GetSeedFileInfo(index);
if (!seedFileInfoOutcome.IsSuccess() || seedFileInfoOutcome.GetValue()->m_absolutePath == m_inMemoryDefaultSeedListKey)
{
// Error has already been thrown
return AZStd::string();
}
return seedFileInfoOutcome.GetValue()->m_absolutePath;
}
int SeedListFileTableModel::GetFileNameColumnIndex() const
{
return Column::ColumnFileName;
}
int SeedListFileTableModel::GetTimeStampColumnIndex() const
{
return Column::ColumnFileModificationTime;
}
int SeedListFileTableModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : Column::Max;
}
QVariant SeedListFileTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case Column::ColumnFileName:
return QString(tr("Seed List File"));
case Column::ColumnProject:
return QString(tr("Project Source"));
case Column::ColumnFileModificationTime:
return QString(tr("Modification Time"));
default:
break;
}
}
return QVariant();
}
QVariant SeedListFileTableModel::data(const QModelIndex& index, int role) const
{
auto seedFileInfoOutcome = GetSeedFileInfo(index);
if (!seedFileInfoOutcome.IsSuccess())
{
return QVariant();
}
int col = index.column();
bool hasUnsavedChanges = seedFileInfoOutcome.GetValue()->m_seedListModel->HasUnsavedChanges();
switch (role)
{
case Qt::DisplayRole:
[[fallthrough]];
case SortRole:
{
if (col == Column::ColumnFileName)
{
QString displayName = seedFileInfoOutcome.GetValue()->m_fileName;
if (hasUnsavedChanges)
{
displayName.append("*");
}
return displayName;
}
else if (col == Column::ColumnProject)
{
return seedFileInfoOutcome.GetValue()->m_project;
}
else if (col == Column::ColumnFileModificationTime)
{
if (role == SortRole)
{
return seedFileInfoOutcome.GetValue()->m_fileModificationTime;
}
else
{
return seedFileInfoOutcome.GetValue()->m_fileModificationTime.toString(DateTimeFormat);
}
}
else
{
// Returning an empty QString will ensure the checkboxes do not have any text displayed next to them
return QString();
}
}
case Qt::FontRole:
{
if (col == Column::ColumnFileName && hasUnsavedChanges)
{
QFont boldFont;
boldFont.setBold(true);
return boldFont;
}
}
case Qt::CheckStateRole:
{
if (col == Column::ColumnCheckBox)
{
if (seedFileInfoOutcome.GetValue()->m_isChecked)
{
return Qt::Checked;
}
else
{
return Qt::Unchecked;
}
}
}
default:
break;
}
return QVariant();
}
bool SeedListFileTableModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (role != Qt::CheckStateRole || index.column() != Column::ColumnCheckBox)
{
return false;
}
AZStd::string key = GetFileKey(index);
if (key.empty())
{
return false;
}
auto seedFileInfo = m_seedListFileInfoMap.find(key);
if (seedFileInfo == m_seedListFileInfoMap.end())
{
return false;
}
if (value == Qt::CheckState::Unchecked)
{
seedFileInfo->second->m_isChecked = false;
m_checkedSeedListFiles.erase(key);
if (seedFileInfo->second->m_isDefaultSeedList)
{
m_seedTabWidget->UncheckSelectDefaultSeedListsCheckBox();
}
}
else
{
seedFileInfo->second->m_isChecked = true;
m_checkedSeedListFiles.insert(key);
}
m_seedTabWidget->SetGenerateAssetListsButtonEnabled(!m_checkedSeedListFiles.empty());
return true;
}
Qt::ItemFlags SeedListFileTableModel::flags(const QModelIndex& index) const
{
if (index.column() == Column::ColumnCheckBox)
{
return Qt::ItemIsUserCheckable | QAbstractTableModel::flags(index);
}
return QAbstractTableModel::flags(index);
}
AZ::Outcome<SeedListFileInfoPtr, void> SeedListFileTableModel::GetSeedFileInfo(const QModelIndex& index) const
{
AZStd::string key = GetFileKey(index);
if (key.empty())
{
// Error has already been thrown
return AZ::Failure();
}
if (m_seedListFileInfoMap.find(key) != m_seedListFileInfoMap.end())
{
return AZ::Success(m_seedListFileInfoMap.at(key));
}
else
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot find Seed File Info");
return AZ::Failure();
}
}
} // namespace AssetBundler
@@ -0,0 +1,134 @@
/*
* 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 <source/models/AssetBundlerAbstractFileTableModel.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QDateTime>
#include <QSharedPointer>
namespace AssetBundler
{
class SeedListTableModel;
class SeedTabWidget;
struct SeedListFileInfo
{
/**
* Stores information about a Seed List File on disk.
*
* @param absolutePath The absolute path of the Seed List File
* @param fileName The name of the Seed List File. This does not include the ".seed" file extension
* @param project The area of the codebase the Seed List File is from (ex: ProjectName, Engine, Gem)
* @param loadFromFile Set to True if you wish to load an existing Seed List File into memory. Set to False if you are creating a new Seed List File.
*/
SeedListFileInfo(
const AZStd::string& absolutePath,
const QString& fileName,
const QString& project,
bool loadFromFile,
bool isDefaultSeedList = false,
const AZStd::vector<AZStd::string>& defaultSeeds = AZStd::vector<AZStd::string>(),
const AzFramework::PlatformFlags& platforms = AzFramework::PlatformFlags::Platform_NONE);
bool SaveSeedFile();
bool HasUnsavedChanges();
AZStd::string m_absolutePath;
bool m_isChecked = false;
bool m_isDefaultSeedList = false;
QString m_fileName;
QString m_project;
QDateTime m_fileModificationTime;
QSharedPointer<SeedListTableModel> m_seedListModel;
};
using SeedListFileInfoPtr = AZStd::shared_ptr<SeedListFileInfo>;
/// Stores SeedListFileInfo, using the absolute path (without the drive letter) of the Seed List file as the key
using SeedListFileInfoMap = AZStd::unordered_map<AZStd::string, SeedListFileInfoPtr>;
class SeedListFileTableModel
: public AssetBundlerAbstractFileTableModel
{
public:
explicit SeedListFileTableModel(SeedTabWidget* parentSeedTabWidget);
virtual ~SeedListFileTableModel();
void AddDefaultSeedsToInMemoryList(const AZStd::vector<AZStd::string>& defaultSeeds, const char* projectName, const AzFramework::PlatformFlags& platforms);
AZStd::vector<AZStd::string> CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms, const QString& project) override;
bool DeleteFile(const QModelIndex& index) override;
void Reload(const char* fileExtension, const QSet<QString>& watchedFolders, const QSet<QString>& watchedFiles = QSet<QString>(), const AZStd::unordered_map<AZStd::string, AZStd::string>& pathToProjectNameMap = AZStd::unordered_map<AZStd::string, AZStd::string>()) override;
void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) override;
void SelectDefaultSeedLists(bool setSelected);
AZStd::vector<AZStd::string> GenerateAssetLists(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms);
QSharedPointer<SeedListTableModel> GetSeedListFileContents(const QModelIndex& index);
bool SetSeedPlatforms(const QModelIndex& seedFileIndex, const QModelIndex& seedIndex, const AzFramework::PlatformFlags& platforms);
bool AddSeed(const QModelIndex& seedFileIndex, const AZStd::string& seedRelativePath, const AzFramework::PlatformFlags& platforms);
bool RemoveSeed(const QModelIndex& seedFileIndex, const QModelIndex& seedIndex);
bool WriteToDisk(const AZStd::string& key) override;
AZStd::string GetFileAbsolutePath(const QModelIndex& index) const override;
int GetFileNameColumnIndex() const override;
int GetTimeStampColumnIndex() const override;
//////////////////////////////////////////////////////////////////////////
// QAbstractListModel overrides
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex& index, int role) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::CheckStateRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
//////////////////////////////////////////////////////////////////////////
enum Column
{
ColumnCheckBox,
ColumnFileName,
ColumnProject,
ColumnFileModificationTime,
Max
};
private:
AZ::Outcome<SeedListFileInfoPtr, void> GetSeedFileInfo(const QModelIndex& index) const;
SeedListFileInfoMap m_seedListFileInfoMap;
AZStd::unordered_set<AZStd::string> m_checkedSeedListFiles;
AZStd::string m_inMemoryDefaultSeedListKey = "InMemoryDefaultKey";
SeedListFileInfoPtr m_inMemoryDefaultSeedList;
SeedTabWidget* m_seedTabWidget = nullptr;
};
} // namespace AssetBundler
@@ -0,0 +1,270 @@
/*
* 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.
*
*/
#include <source/models/SeedListTableModel.h>
#include <source/utils/utils.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalog.h>
#include <QFont>
namespace AssetBundler
{
//////////////////////////////////////////////////////////////////////////////////////////////////
// AdditionalSeedInfo
//////////////////////////////////////////////////////////////////////////////////////////////////
AdditionalSeedInfo::AdditionalSeedInfo(const QString& relativePath, const QString& platformList)
: m_relativePath(relativePath)
, m_platformList(platformList)
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// SeedListTableModel
//////////////////////////////////////////////////////////////////////////////////////////////////
SeedListTableModel::SeedListTableModel(QObject* parent, const AZStd::string& absolutePath, const AZStd::vector<AZStd::string>& defaultSeeds, const AzFramework::PlatformFlags& platforms)
: QAbstractTableModel(parent)
{
m_seedListManager.reset(new AzToolsFramework::AssetSeedManager());
if (absolutePath.empty() && defaultSeeds.empty())
{
return;
}
if (!defaultSeeds.empty())
{
for (const AZStd::string& seed : defaultSeeds)
{
m_seedListManager->AddSeedAssetForValidPlatforms(seed, platforms);
}
m_isFileOnDisk = false;
}
else
{
m_seedListManager->Load(absolutePath);
}
AZ::Data::AssetInfo assetInfo;
QString platformList;
for (const auto& seed : m_seedListManager->GetAssetSeedList())
{
assetInfo = AzToolsFramework::AssetSeedManager::GetAssetInfoById(seed.m_assetId, AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(seed.m_platformFlags)[0], absolutePath);
platformList = QString(m_seedListManager->GetReadablePlatformList(seed).c_str());
m_additionalSeedInfoMap[seed.m_assetId].reset(new AdditionalSeedInfo(assetInfo.m_relativePath.c_str(), platformList));
}
}
AZ::Outcome<AzFramework::PlatformFlags, void> SeedListTableModel::GetSeedPlatforms(const QModelIndex& index) const
{
auto seedOutcome = GetSeedInfo(index);
if (!seedOutcome.IsSuccess())
{
// Error has already been thrown
return AZ::Failure();
}
return AZ::Success(seedOutcome.GetValue().m_platformFlags);
}
bool SeedListTableModel::Save(const AZStd::string& absolutePath)
{
if (!HasUnsavedChanges())
{
// There are no changes, so there is nothing to save
return true;
}
SetHasUnsavedChanges(!m_seedListManager->Save(absolutePath));
return !HasUnsavedChanges();
}
bool SeedListTableModel::SetSeedPlatforms(const QModelIndex& index, const AzFramework::PlatformFlags& platforms)
{
auto seedOutcome = GetSeedInfo(index);
if (!seedOutcome.IsSuccess())
{
// Error has already been thrown
return false;
}
if (platforms == AzFramework::PlatformFlags::Platform_NONE)
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot Edit Platforms: No platforms were selected");
return false;
}
auto setPlatformOutcome = m_seedListManager->SetSeedPlatformFlags(index.row(), platforms);
if (!setPlatformOutcome.IsSuccess())
{
AZ_Error(AssetBundler::AppWindowName, false, setPlatformOutcome.GetError().c_str());
return false;
}
// Update the cached display info
auto additionalSeedInfo = m_additionalSeedInfoMap.find(seedOutcome.GetValue().m_assetId);
if (additionalSeedInfo == m_additionalSeedInfoMap.end())
{
AZ_Error(AssetBundler::AppWindowName, false, "Unable to find additional Seed info");
return false;
}
additionalSeedInfo->second->m_platformList = QString(AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platforms).c_str());
SetHasUnsavedChanges(true);
// Update the display
QModelIndex firstChangedIndex = QAbstractTableModel::index(index.row(), Column::ColumnRelativePath);
QModelIndex lastChangedIndex = QAbstractTableModel::index(index.row(), Column::Max - 1);
emit dataChanged(firstChangedIndex, lastChangedIndex, { Qt::DisplayRole });
return true;
}
bool SeedListTableModel::AddSeed(const AZStd::string& seedRelativePath, const AzFramework::PlatformFlags& platforms)
{
AZStd::pair<AZ::Data::AssetId, AzFramework::PlatformFlags> addSeedsResult = m_seedListManager->AddSeedAssetForValidPlatforms(seedRelativePath, platforms);
if (!addSeedsResult.first.IsValid() || addSeedsResult.second == AzFramework::PlatformFlags::Platform_NONE)
{
// Error has already been thrown
return false;
}
QString platformList = QString(AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(addSeedsResult.second).c_str());
int lastRowIndex = AZStd::max(rowCount() - 1, 0);
beginInsertRows(QModelIndex(), lastRowIndex, lastRowIndex);
m_additionalSeedInfoMap[addSeedsResult.first].reset(new AdditionalSeedInfo(QString(seedRelativePath.c_str()), platformList));
endInsertRows();
SetHasUnsavedChanges(true);
return true;
}
bool SeedListTableModel::RemoveSeed(const QModelIndex& seedIndex)
{
auto seedOutcome = GetSeedInfo(seedIndex);
if (!seedOutcome.IsSuccess())
{
// Error has already been thrown
return false;
}
int row = seedIndex.row();
beginRemoveRows(QModelIndex(), row, row);
m_seedListManager->RemoveSeedAsset(seedOutcome.GetValue().m_assetId, seedOutcome.GetValue().m_platformFlags);
m_additionalSeedInfoMap.erase(seedOutcome.GetValue().m_assetId);
endRemoveRows();
SetHasUnsavedChanges(true);
return true;
}
int SeedListTableModel::rowCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : static_cast<int>(m_additionalSeedInfoMap.size());
}
int SeedListTableModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : Column::Max;
}
QVariant SeedListTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case Column::ColumnRelativePath:
return QString("Seed");
case Column::ColumnPlatformList:
return QString("Platforms");
default:
break;
}
}
return QVariant();
}
QVariant SeedListTableModel::data(const QModelIndex& index, int role) const
{
auto additionalSeedInfoOutcome = GetAdditionalSeedInfo(index);
if (!additionalSeedInfoOutcome.IsSuccess())
{
return QVariant();
}
switch (role)
{
case Qt::DisplayRole:
{
if (index.column() == Column::ColumnRelativePath)
{
return additionalSeedInfoOutcome.GetValue()->m_relativePath;
}
else if (index.column() == Column::ColumnPlatformList)
{
return additionalSeedInfoOutcome.GetValue()->m_platformList;
}
}
default:
break;
}
return QVariant();
}
AZ::Outcome<AzFramework::SeedInfo&, void> SeedListTableModel::GetSeedInfo(const QModelIndex& index) const
{
int row = index.row();
int col = index.column();
if (row >= rowCount() || row < 0 || col >= columnCount() || col < 0)
{
AZ_Error(AssetBundler::AppWindowName, false, "Selected index (%i, %i) is out of range", row, col);
return AZ::Failure();
}
return AZ::Success(m_seedListManager->GetAssetSeedList().at(row));
}
AZ::Outcome<AdditionalSeedInfoPtr, void> SeedListTableModel::GetAdditionalSeedInfo(const QModelIndex& index) const
{
auto seedInfoOutcome = GetSeedInfo(index);
if (!seedInfoOutcome.IsSuccess())
{
// Error has already been thrown
return AZ::Failure();
}
auto additionalSeedInfoIt = m_additionalSeedInfoMap.find(seedInfoOutcome.GetValue().m_assetId);
if (additionalSeedInfoIt == m_additionalSeedInfoMap.end())
{
return AZ::Failure();
}
return AZ::Success(additionalSeedInfoIt->second);
}
} // namespace AssetBundler
@@ -0,0 +1,84 @@
/*
* 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 <source/models/AssetBundlerAbstractFileTableModel.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QSharedPointer>
namespace AssetBundler
{
struct AdditionalSeedInfo
{
AdditionalSeedInfo(const QString& relativePath, const QString& platformList);
QString m_relativePath;
QString m_platformList;
};
using AdditionalSeedInfoPtr = AZStd::shared_ptr<AdditionalSeedInfo>;
using AdditionalSeedInfoMap = AZStd::unordered_map<AZ::Data::AssetId, AdditionalSeedInfoPtr>;
class SeedListTableModel
: public QAbstractTableModel
{
public:
explicit SeedListTableModel(QObject* parent = nullptr, const AZStd::string& absolutePath = AZStd::string(), const AZStd::vector<AZStd::string>& defaultSeeds = AZStd::vector<AZStd::string>(), const AzFramework::PlatformFlags& platforms = AzFramework::PlatformFlags::Platform_NONE);
virtual ~SeedListTableModel() {}
AZStd::shared_ptr<AzToolsFramework::AssetSeedManager> GetSeedListManager() { return m_seedListManager; }
bool HasUnsavedChanges() { return m_hasUnsavedChanges && m_isFileOnDisk; }
void SetHasUnsavedChanges(bool hasUnsavedChanges) { m_hasUnsavedChanges = hasUnsavedChanges; }
bool Save(const AZStd::string& absolutePath);
AZ::Outcome<AzFramework::PlatformFlags, void> GetSeedPlatforms(const QModelIndex& index) const;
bool SetSeedPlatforms(const QModelIndex& index, const AzFramework::PlatformFlags& platforms);
bool AddSeed(const AZStd::string& seedRelativePath, const AzFramework::PlatformFlags& platforms);
bool RemoveSeed(const QModelIndex& seedIndex);
//////////////////////////////////////////////////////////////////////////
// QAbstractListModel overrides
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex& index, int role) const override;
//////////////////////////////////////////////////////////////////////////
enum Column
{
ColumnRelativePath,
ColumnPlatformList,
Max
};
private:
AZ::Outcome<AzFramework::SeedInfo&, void> GetSeedInfo(const QModelIndex& index) const;
AZ::Outcome<AdditionalSeedInfoPtr, void> GetAdditionalSeedInfo(const QModelIndex& index) const;
AZStd::shared_ptr<AzToolsFramework::AssetSeedManager> m_seedListManager;
AdditionalSeedInfoMap m_additionalSeedInfoMap;
bool m_hasUnsavedChanges = false;
bool m_isFileOnDisk = true;
};
} // namespace AssetBundler
@@ -0,0 +1,96 @@
/*
* 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.
*
*/
#include <source/ui/AddSeedDialog.h>
#include <source/ui/ui_AddSeedDialog.h>
#include <source/ui/PlatformSelectionWidget.h>
#include <QFileDialog>
#include <QPushButton>
const char QtRelativePathPrefix[] = "../";
namespace AssetBundler
{
AddSeedDialog::AddSeedDialog(QWidget* parent, const AzFramework::PlatformFlags& enabledPlatforms, const AZStd::string& platformSpecificCachePath)
: QDialog(parent)
, m_platformSpecificCachePath(platformSpecificCachePath.c_str())
{
m_ui.reset(new Ui::AddSeedDialog);
m_ui->setupUi(this);
// Set up Browse File button
m_ui->fileNameLineEdit->setReadOnly(true);
connect(m_ui->browseFileButton, &QPushButton::clicked, this, &AddSeedDialog::OnBrowseFileButtonPressed);
// Set up Platform selection
m_ui->platformSelectionWidget->Init(enabledPlatforms);
connect(m_ui->platformSelectionWidget, &PlatformSelectionWidget::PlatformsSelected, this, &AddSeedDialog::OnPlatformSelectionChanged);
// Set up Cancel and Create New File buttons
m_ui->addSeedButton->setEnabled(false);
connect(m_ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject);
connect(m_ui->addSeedButton, &QPushButton::clicked, this, &QDialog::accept);
}
AZStd::string AddSeedDialog::GetFileName()
{
return m_fileName;
}
AzFramework::PlatformFlags AddSeedDialog::GetPlatformFlags()
{
return m_ui->platformSelectionWidget->GetSelectedPlatforms();
}
void AddSeedDialog::OnBrowseFileButtonPressed()
{
QString seedAbsolutePath = QFileDialog::getOpenFileName(this, tr("Add New Seed"), m_platformSpecificCachePath);
m_fileNameIsValid = !seedAbsolutePath.isEmpty();
if (!m_fileNameIsValid)
{
// The user canceled out of the window, do not update anything
return;
}
// Make sure the path is relative to the platform-specific cache
QDir platformSpecificCacheDir(m_platformSpecificCachePath);
QString seedRelativePath = platformSpecificCacheDir.relativeFilePath(seedAbsolutePath);
// trim off the "../" from the relative path
FormatRelativePathString(seedRelativePath);
m_fileName = seedRelativePath.toUtf8().data();
// Update the ui
m_ui->fileNameLineEdit->setText(seedRelativePath);
m_ui->addSeedButton->setEnabled(m_platformIsValid && m_fileNameIsValid);
}
void AddSeedDialog::OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms)
{
// Hide the "Create File" button if no platforms are selected
m_platformIsValid = selectedPlatforms != AzFramework::PlatformFlags::Platform_NONE;
m_ui->addSeedButton->setEnabled(m_platformIsValid && m_fileNameIsValid);
}
void AddSeedDialog::FormatRelativePathString(QString& relativePath)
{
if (relativePath.startsWith(QtRelativePathPrefix))
{
relativePath.remove(0, static_cast<int>(strlen(QtRelativePathPrefix)));
}
}
} //namespace AssetBundler
#include <source/ui/moc_AddSeedDialog.cpp>
@@ -0,0 +1,58 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/std/string/string.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QDialog>
#include <QSharedPointer>
#endif
namespace Ui
{
class AddSeedDialog;
}
namespace AssetBundler
{
class GUIApplicationManager;
class AddSeedDialog
: public QDialog
{
Q_OBJECT
public:
explicit AddSeedDialog(QWidget* parent, const AzFramework::PlatformFlags& enabledPlatforms, const AZStd::string& platformSpecificCachePath);
virtual ~AddSeedDialog() {}
AZStd::string GetFileName();
AzFramework::PlatformFlags GetPlatformFlags();
private:
void OnBrowseFileButtonPressed();
void OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms);
void FormatRelativePathString(QString& relativePath);
QSharedPointer<Ui::AddSeedDialog> m_ui;
QString m_platformSpecificCachePath;
bool m_isAddSeedDialog = false;
AZStd::string m_fileName;
bool m_fileNameIsValid = false;
bool m_platformIsValid = false;
};
} // namespace AssetBundler
@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddSeedDialog</class>
<widget class="QDialog" name="AddSeedDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>108</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>450</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Add Seed Asset</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="inputValuesFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="fileNameLabel">
<property name="text">
<string>Select Seed Asset:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="fileNameLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browseFileButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="lineFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="AssetBundler::PlatformSelectionWidget" name="platformSelectionWidget" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
<property name="default">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addSeedButton">
<property name="text">
<string>Add Seed</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AssetBundler::PlatformSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">source/ui/PlatformSelectionWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,370 @@
/*
* 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.
*
*/
#include <source/ui/AssetBundlerTabWidget.h>
#include <source/utils/GUIApplicationManager.h>
#include <source/utils/utils.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <QApplication>
#include <QClipboard>
#include <QHeaderView>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QMenu>
#include <QMessageBox>
namespace AssetBundler
{
const char AssetBundlerCommonSettingsFile[] = "AssetBundlerCommonSettings.json";
const char AssetBundlerUserSettingsFile[] = "AssetBundlerUserSettings.json";
const char ScanPathsKey[] = "ScanPaths";
const QString AssetBundlingFileTypes[] =
{
"SeedLists",
"AssetLists",
"BundleSettings",
"Bundles",
"Rules"
};
const int MarginSize = 10;
AssetBundlerTabWidget::AssetBundlerTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager)
: QWidget(parent)
, m_guiApplicationManager(guiApplicationManager)
{
connect(m_guiApplicationManager, &GUIApplicationManager::UpdateTab, this, &AssetBundlerTabWidget::OnUpdateTab);
connect(m_guiApplicationManager, &GUIApplicationManager::UpdateFiles, this, &AssetBundlerTabWidget::OnUpdateFiles);
QAction* deleteFileAction = new QAction(tr("Delete"), this);
addAction(deleteFileAction);
deleteFileAction->setShortcut(QKeySequence::Delete);
connect(deleteFileAction, &QAction::triggered, this, &AssetBundlerTabWidget::OnDeleteSelectedFileRequested);
}
AssetBundlerTabWidget::~AssetBundlerTabWidget()
{
m_guiApplicationManager = nullptr;
}
void AssetBundlerTabWidget::Activate()
{
SetActiveProjectLabel(tr("Active Project: %1").arg(m_guiApplicationManager->GetCurrentProjectName().c_str()));
SetupContextMenu();
Reload();
connect(GetFileTableView()->header(), &QHeaderView::sortIndicatorChanged, m_fileTableFilterModel.get(), &AssetBundlerFileTableFilterModel::sort);
GetFileTableView()->header()->setSortIndicatorShown(true);
// Setting this in descending order will ensure the most recent files are at the top
GetFileTableView()->header()->setSortIndicator(GetFileTableModel()->GetTimeStampColumnIndex(), Qt::DescendingOrder);
GetFileTableView()->setSortingEnabled(true);
}
void AssetBundlerTabWidget::InitAssetBundlerSettings(const char* currentProjectFolderPath)
{
AZStd::string commonSettingsPath = GetAssetBundlerCommonSettingsFile(currentProjectFolderPath);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(commonSettingsPath.c_str()))
{
CreateEmptyAssetBundlerSettings(commonSettingsPath);
}
AZStd::string userSettingsPath = GetAssetBundlerUserSettingsFile(currentProjectFolderPath);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(userSettingsPath.c_str()))
{
CreateEmptyAssetBundlerSettings(userSettingsPath);
}
}
void AssetBundlerTabWidget::OnFileTableContextMenuRequested(const QPoint& pos)
{
AZStd::string selectedFileAbsolutePath(GetFileTableModel()->GetFileAbsolutePath(GetSelectedFileTableIndex()));
QMenu* contextMenu = new QMenu(this);
contextMenu->setToolTipsVisible(true);
bool arePathOperationsEnabled = !selectedFileAbsolutePath.empty();
QString emptyPathToolTip(tr("This file is not present on-disk."));
QAction* action = contextMenu->addAction(AzQtComponents::fileBrowserActionName(), [=]()
{
AzQtComponents::ShowFileOnDesktop(selectedFileAbsolutePath.c_str());
});
if (arePathOperationsEnabled)
{
action->setToolTip(tr("Shows the location of this file on your computer"));
}
else
{
action->setToolTip(emptyPathToolTip);
action->setEnabled(false);
}
action = contextMenu->addAction(tr("Copy Path to Clipboard"), [=]()
{
QApplication::clipboard()->setText(QString(selectedFileAbsolutePath.c_str()));
});
if (arePathOperationsEnabled)
{
action->setToolTip(tr("Copies the absolute path of this file to your Clipboard"));
}
else
{
action->setToolTip(emptyPathToolTip);
action->setEnabled(false);
}
contextMenu->addSeparator();
// We can't use the same Delete action as the constructor because we need to modify a lot of values
action = new QAction(tr("Delete"), this);
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
action->setShortcut(QKeySequence::Delete);
connect(action, &QAction::triggered, this, &AssetBundlerTabWidget::OnDeleteSelectedFileRequested);
if (arePathOperationsEnabled)
{
action->setToolTip(tr("Deletes the selected file from disk."));
}
else
{
action->setToolTip(emptyPathToolTip);
action->setEnabled(false);
}
contextMenu->addAction(action);
contextMenu->exec(GetFileTableView()->mapToGlobal(pos));
delete contextMenu;
}
void AssetBundlerTabWidget::OnDeleteSelectedFileRequested()
{
AZStd::string selectedFileAbsolutePath(GetFileTableModel()->GetFileAbsolutePath(GetSelectedFileTableIndex()));
if (selectedFileAbsolutePath.empty())
{
return;
}
QString messageBoxText = QString(tr("Are you sure you would like to delete %1? \n\nThis will permanently delete the file.")).arg(QString(selectedFileAbsolutePath.c_str()));
QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Delete %1")).arg(GetFileTypeDisplayName()), messageBoxText);
if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes)
{
// User canceled out of the confirmation dialog
return;
}
if (GetFileTableModel()->DeleteFile(GetSelectedFileTableIndex()))
{
RemoveScanPathFromAssetBundlerSettings(GetFileType(), selectedFileAbsolutePath.c_str());
}
}
void AssetBundlerTabWidget::ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType fileType)
{
AZStd::string currentProjectFolderPath = m_guiApplicationManager->GetCurrentProjectFolder();
ReadAssetBundlerSettings(GetAssetBundlerUserSettingsFile(currentProjectFolderPath.c_str()), fileType);
ReadAssetBundlerSettings(GetAssetBundlerCommonSettingsFile(currentProjectFolderPath.c_str()), fileType);
}
void AssetBundlerTabWidget::AddScanPathToAssetBundlerSettings(AssetBundlingFileType fileType, AZStd::string filePath)
{
AZStd::string defaultFolderPath;
switch (fileType)
{
case AssetBundlingFileType::SeedListFileType:
defaultFolderPath = m_guiApplicationManager->GetSeedListsFolder();
break;
case AssetBundlingFileType::AssetListFileType:
defaultFolderPath = m_guiApplicationManager->GetAssetListsFolder();
break;
case AssetBundlingFileType::RulesFileType:
defaultFolderPath = m_guiApplicationManager->GetRulesFolder();
break;
case AssetBundlingFileType::BundleSettingsFileType:
defaultFolderPath = m_guiApplicationManager->GetBundleSettingsFolder();
break;
case AssetBundlingFileType::BundleFileType:
defaultFolderPath = m_guiApplicationManager->GetBundlesFolder();
break;
default:
AZ_Warning(AssetBundler::AppWindowName, false, "No default folder is defined for AssetBundlingFileType ( %i ).", static_cast<int>(fileType));
break;
}
AzFramework::StringFunc::Path::Normalize(filePath);
AzFramework::StringFunc::Path::Normalize(defaultFolderPath);
if (AzFramework::StringFunc::StartsWith(filePath, defaultFolderPath))
{
// file is already in a watched folder, no need to add it to the settings file
return;
}
AddScanPathToAssetBundlerSettings(fileType, QString(filePath.c_str()));
}
void AssetBundlerTabWidget::AddScanPathToAssetBundlerSettings(AssetBundlingFileType fileType, const QString& filePath)
{
AZStd::string assetBundlerSettingsFileAbsolutePath = GetAssetBundlerUserSettingsFile(m_guiApplicationManager->GetCurrentProjectFolder().c_str());
QJsonObject assetBundlerSettings = AssetBundler::ReadJson(assetBundlerSettingsFileAbsolutePath);
QJsonObject scanPathsSettings = assetBundlerSettings[ScanPathsKey].toObject();
QJsonArray scanPaths = scanPathsSettings[AssetBundlingFileTypes[fileType]].toArray();
QFileInfo inputFileInfo(filePath);
for (const QJsonValue scanPath : m_watchedFiles + m_watchedFolders)
{
AZStd::string scanFilePathStr = scanPath.toString().toUtf8().data();
AzFramework::StringFunc::Path::ConstructFull(GetCachedEngineRoot().c_str(), scanFilePathStr.c_str(), scanFilePathStr);
// Check whether the file has already been watched
// Get absolute file paths via QFileInfo to keep consistency in the letter case
if (inputFileInfo.absoluteFilePath().startsWith(
QFileInfo(scanFilePathStr.c_str()).absoluteFilePath()))
{
return;
}
}
scanPaths.push_back(filePath);
scanPathsSettings[AssetBundlingFileTypes[fileType]] = scanPaths;
assetBundlerSettings[ScanPathsKey] = scanPathsSettings;
AssetBundler::SaveJson(assetBundlerSettingsFileAbsolutePath, assetBundlerSettings);
m_watchedFiles.insert(filePath);
m_guiApplicationManager->AddWatchedPath(filePath);
}
void AssetBundlerTabWidget::RemoveScanPathFromAssetBundlerSettings(AssetBundlingFileType fileType, const QString& filePath)
{
AZStd::string assetBundlerSettingsFileAbsolutePath = GetAssetBundlerUserSettingsFile(m_guiApplicationManager->GetCurrentProjectFolder().c_str());
QJsonObject assetBundlerSettings = AssetBundler::ReadJson(assetBundlerSettingsFileAbsolutePath);
QJsonObject scanPathsSettings = assetBundlerSettings[ScanPathsKey].toObject();
QJsonArray scanPaths = scanPathsSettings[AssetBundlingFileTypes[fileType]].toArray();
for (auto itr = scanPaths.begin(); itr != scanPaths.end(); ++itr)
{
QJsonValueRef scanPathValueRef = *itr;
AZStd::string scanPath = scanPathValueRef.toString().toUtf8().data();
AzFramework::StringFunc::Path::ConstructFull(GetCachedEngineRoot().c_str(), scanPath.c_str(), scanPath);
// Check whether the file is being watched
// Get absolute file paths via QFileInfo to keep consistency in the letter case
if (QFileInfo(filePath).absoluteFilePath() == QFileInfo(scanPath.c_str()).absoluteFilePath())
{
itr = scanPaths.erase(itr);
break;
}
}
scanPathsSettings[AssetBundlingFileTypes[fileType]] = scanPaths;
assetBundlerSettings[ScanPathsKey] = scanPathsSettings;
AssetBundler::SaveJson(assetBundlerSettingsFileAbsolutePath, assetBundlerSettings);
m_guiApplicationManager->RemoveWatchedPath(filePath);
}
void AssetBundlerTabWidget::OnUpdateTab(const AZStd::string& path)
{
if (m_watchedFolders.contains(path.c_str()) ||
m_watchedFiles.contains(path.c_str()))
{
Reload();
}
}
void AssetBundlerTabWidget::OnUpdateFiles(AssetBundlingFileType fileType, const AZStd::vector<AZStd::string>& absoluteFilePaths)
{
if (fileType == GetFileType())
{
GetFileTableModel()->ReloadFiles(absoluteFilePaths, m_filePathToGemNameMap);
m_fileTableFilterModel->sort(m_fileTableFilterModel->sortColumn(), m_fileTableFilterModel->sortOrder());
FileSelectionChanged();
}
}
void AssetBundlerTabWidget::SetupContextMenu()
{
GetFileTableView()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(GetFileTableView(), &QTreeView::customContextMenuRequested, this, &AssetBundlerTabWidget::OnFileTableContextMenuRequested);
}
void AssetBundlerTabWidget::ReadAssetBundlerSettings(const AZStd::string& filePath, AssetBundlingFileType fileType)
{
// Read the config file which contains the customized scan paths information
AZStd::vector<AZStd::string> assetBundlerSettingsFileList;
QJsonObject assetBundlerSettings = AssetBundler::ReadJson(filePath);
QJsonObject scanPaths = assetBundlerSettings.find(ScanPathsKey).value().toObject();
for (const QJsonValue scanPath : scanPaths[AssetBundlingFileTypes[fileType]].toArray())
{
AZStd::string absoluteScanPath = scanPath.toString().toUtf8().data();
AZStd::replace(absoluteScanPath.begin(), absoluteScanPath.end(), AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (AzFramework::StringFunc::Path::IsRelative(absoluteScanPath.c_str()))
{
AzFramework::StringFunc::Path::ConstructFull(GetCachedEngineRoot().c_str(), absoluteScanPath.c_str(), absoluteScanPath);
}
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(absoluteScanPath.c_str()))
{
// The path specified in the config file is a directory
m_watchedFolders.insert(absoluteScanPath.c_str());
}
else if (AZ::IO::FileIOBase::GetInstance()->Exists(absoluteScanPath.c_str()))
{
// The path specified in the config file is a file
m_watchedFiles.insert(absoluteScanPath.c_str());
}
}
}
AZStd::string AssetBundlerTabWidget::GetAssetBundlerUserSettingsFile(const char* currentProjectFolderPath)
{
AZStd::string absoluteFilePath;
AzFramework::StringFunc::Path::ConstructFull(currentProjectFolderPath, AssetBundlerUserSettingsFile, absoluteFilePath);
return absoluteFilePath;
}
AZStd::string AssetBundlerTabWidget::GetAssetBundlerCommonSettingsFile(const char* currentProjectFolderPath)
{
AZStd::string absoluteFilePath;
AzFramework::StringFunc::Path::ConstructFull(currentProjectFolderPath, AssetBundlerCommonSettingsFile, absoluteFilePath);
return absoluteFilePath;
}
void AssetBundlerTabWidget::CreateEmptyAssetBundlerSettings(const AZStd::string& filePath)
{
QJsonObject assetBundlerSettings;
QJsonObject scanPathSettings;
for (const auto& fileType : AssetBundlingFileTypes)
{
scanPathSettings.insert(fileType, QJsonArray());
}
assetBundlerSettings.insert(ScanPathsKey, scanPathSettings);
AssetBundler::SaveJson(filePath, assetBundlerSettings);
}
} // namespace AssetBundler
#include <source/ui/moc_AssetBundlerTabWidget.cpp>
@@ -0,0 +1,118 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <source/models/AssetBundlerAbstractFileTableModel.h>
#include <source/models/AssetBundlerFileTableFilterModel.h>
#include <source/utils/GUIApplicationManager.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzQtComponents/Components/Widgets/TableView.h>
#include <QLabel>
#include <QWidget>
#include <QItemSelection>
#endif
namespace AssetBundler
{
// Keys for the corresponding scan path lists in the asset bundling settings file
extern const QString AssetBundlingFileTypes[];
extern const int MarginSize;
class AssetBundlerTabWidget
: public QWidget
{
Q_OBJECT
public:
explicit AssetBundlerTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager);
virtual ~AssetBundlerTabWidget();
void Activate();
virtual QString GetTabTitle() = 0;
virtual QString GetFileTypeDisplayName() = 0;
virtual AssetBundlingFileType GetFileType() = 0;
virtual bool HasUnsavedChanges() = 0;
//! Reload all the files on display.
virtual void Reload() = 0;
//! Saves the selected file to disk.
//! @return true on successful save, or throws an error message and returns false on a failed save.
virtual bool SaveCurrentSelection() = 0;
//! Saves all modified files to disk.
//! @return true on successful save, or throws an error message and returns false on a failed save.
virtual bool SaveAll() = 0;
//! Set watched folders and files for the model
virtual void SetModelDataSource() = 0;
virtual AzQtComponents::TableView* GetFileTableView() = 0;
virtual QModelIndex GetSelectedFileTableIndex() = 0;
virtual AssetBundlerAbstractFileTableModel* GetFileTableModel() = 0;
virtual void SetActiveProjectLabel(const QString& labelText) = 0;
virtual void ApplyConfig() = 0;
virtual void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) = 0;
static void InitAssetBundlerSettings(const char* currentProjectFolderPath);
void AddScanPathToAssetBundlerSettings(AssetBundlingFileType fileType, AZStd::string filePath);
GUIApplicationManager* GetGUIApplicationManager() { return m_guiApplicationManager; }
protected:
void OnFileTableContextMenuRequested(const QPoint& pos);
//! Asks the user to confirm they wish to permanently delete the selected file, then removes it from disk and memory.
void OnDeleteSelectedFileRequested();
bool ConfirmFileDeleteDialog(const QString& absoluteFilePath);
void ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType fileType);
void RemoveScanPathFromAssetBundlerSettings(AssetBundlingFileType fileType, const QString& filePath);
GUIApplicationManager* m_guiApplicationManager = nullptr;
QScopedPointer<AssetBundler::AssetBundlerFileTableFilterModel> m_fileTableFilterModel;
QSet<QString> m_watchedFolders;
QSet<QString> m_watchedFiles;
AZStd::unordered_map<AZStd::string, AZStd::string> m_filePathToGemNameMap;
private slots:
void OnUpdateTab(const AZStd::string& path);
void OnUpdateFiles(AssetBundlingFileType fileType, const AZStd::vector<AZStd::string>& absoluteFilePaths);
private:
void SetupContextMenu();
void AddScanPathToAssetBundlerSettings(AssetBundlingFileType fileType, const QString& filePath);
void ReadAssetBundlerSettings(const AZStd::string& filePath, AssetBundlingFileType fileType);
static AZStd::string GetAssetBundlerUserSettingsFile(const char* currentProjectFolderPath);
static AZStd::string GetAssetBundlerCommonSettingsFile(const char* currentProjectFolderPath);
static void CreateEmptyAssetBundlerSettings(const AZStd::string& filePath);
};
} // namespace AssetBundler
@@ -0,0 +1,182 @@
/*
* 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.
*
*/
#include <source/ui/AssetListTabWidget.h>
#include <source/ui/ui_AssetListTabWidget.h>
#include <source/utils/GUIApplicationManager.h>
#include <source/ui/GenerateBundlesModal.h>
#include <source/ui/NewFileDialog.h>
#include <source/models/AssetListFileTableModel.h>
#include <source/models/AssetListTableModel.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzQtComponents/Components/FilteredSearchWidget.h>
#include <AzQtComponents/Components/Widgets/TableView.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalog.h>
#include <QItemSelection>
#include <QItemSelectionRange>
#include <QMessageBox>
#include <QPushButton>
namespace AssetBundler
{
AssetListTabWidget::AssetListTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager)
: AssetBundlerTabWidget(parent, guiApplicationManager)
, m_fileTableModel(new AssetListFileTableModel())
, m_assetListContentsModel(new AssetListTableModel())
{
m_ui.reset(new Ui::AssetListTabWidget);
m_ui->setupUi(this);
m_ui->mainVerticalLayout->setContentsMargins(10, 10, 10, 10);
// File view of all Asset List Files
m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex()));
m_fileTableFilterModel->setSourceModel(m_fileTableModel.data());
m_ui->assetListsTable->setModel(m_fileTableFilterModel.data());
connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
m_fileTableFilterModel.data(), static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
connect(m_ui->assetListsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AssetListTabWidget::FileSelectionChanged);
m_ui->fileTableHeaderLayout->setContentsMargins(0, 0, 0, 0);
m_ui->fileTableVerticalLayout->setContentsMargins(0, 0, 0, 0);
m_ui->assetListsTable->setIndentation(0);
// Generate Bundle Button
m_ui->generateBundleButton->setDefault(true);
m_ui->generateBundleButton->setEnabled(false);
connect(m_ui->generateBundleButton, &QPushButton::clicked, this, &AssetListTabWidget::OnGenerateBundleButtonPressed);
// Absolute path of selected Asset List file
m_ui->assetListFileAbsolutePathLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
// Table that displays the contents of an Asset List File
m_assetListContentsFilterModel.reset(new AssetBundlerFileTableFilterModel(this, AssetListTableModel::Column::ColumnAssetName));
m_assetListContentsFilterModel->setSourceModel(m_assetListContentsModel.data());
m_ui->assetListContentsTable->setModel(m_assetListContentsFilterModel.data());
connect(m_ui->assetListContentsFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
m_assetListContentsFilterModel.data(), static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
m_ui->fileContentsHeaderLayout->setContentsMargins(0, 0, 0, 0);
m_ui->fileContentsVerticalLayout->setContentsMargins(0, 0, 0, 0);
m_ui->assetListContentsTable->setIndentation(0);
SetModelDataSource();
}
void AssetListTabWidget::SetModelDataSource()
{
// Remove the current watched folders and files
m_guiApplicationManager->RemoveWatchedPaths(m_watchedFolders + m_watchedFiles);
// Set the new watched folder for the model
m_watchedFolders.clear();
m_watchedFiles.clear();
m_watchedFolders.insert(m_guiApplicationManager->GetAssetListsFolder().c_str());
ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType::AssetListFileType);
m_guiApplicationManager->AddWatchedPaths(m_watchedFolders + m_watchedFiles);
}
AzQtComponents::TableView* AssetListTabWidget::GetFileTableView()
{
return m_ui->assetListsTable;
}
QModelIndex AssetListTabWidget::GetSelectedFileTableIndex()
{
return m_selectedFileTableIndex;
}
AssetBundlerAbstractFileTableModel* AssetListTabWidget::GetFileTableModel()
{
return m_fileTableModel.get();
}
void AssetListTabWidget::SetActiveProjectLabel(const QString& labelText)
{
m_ui->activeProjectLabel->setText(labelText);
}
void AssetListTabWidget::ApplyConfig()
{
const GUIApplicationManager::Config& config = m_guiApplicationManager->GetConfig();
m_ui->fileTableFrame->setFixedWidth(config.fileTableWidth);
m_ui->assetListsTable->header()->resizeSection(AssetListFileTableModel::Column::ColumnFileName, config.assetListFileNameColumnWidth);
m_ui->assetListsTable->header()->resizeSection(AssetListFileTableModel::Column::ColumnPlatform, config.assetListPlatformColumnWidth);
m_ui->assetListContentsFilteredSearchWidget->setFixedWidth(config.fileTableWidth);
m_ui->assetListContentsTable->header()->resizeSection(AssetListTableModel::Column::ColumnAssetName, config.productAssetNameColumnWidth);
m_ui->assetListContentsTable->header()->resizeSection(AssetListTableModel::Column::ColumnRelativePath, config.productAssetRelativePathColumnWidth);
}
void AssetListTabWidget::Reload()
{
// Reload all the asset list files
m_fileTableModel->Reload(AzToolsFramework::AssetSeedManager::GetAssetListFileExtension(), m_watchedFolders, m_watchedFiles);
// Update the selected row
FileSelectionChanged();
}
void AssetListTabWidget::FileSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
AZ_UNUSED(selected);
AZ_UNUSED(deselected);
if (m_ui->assetListsTable->selectionModel()->selectedRows().size() == 0)
{
// Set selected index to an invalid value
m_selectedFileTableIndex = QModelIndex();
m_ui->assetListFileAbsolutePathLabel->setText("");
m_assetListContentsModel.reset(new AssetListTableModel());
m_assetListContentsFilterModel->setSourceModel(m_assetListContentsModel.data());
m_ui->generateBundleButton->setEnabled(false);
return;
}
m_selectedFileTableIndex = m_fileTableFilterModel->mapToSource(m_ui->assetListsTable->selectionModel()->currentIndex());
m_ui->assetListFileAbsolutePathLabel->setText(QString(m_fileTableModel->GetFileAbsolutePath(m_selectedFileTableIndex).c_str()));
m_assetListContentsModel = m_fileTableModel->GetAssetListFileContents(m_selectedFileTableIndex);
m_assetListContentsFilterModel->setSourceModel(m_assetListContentsModel.data());
m_ui->generateBundleButton->setEnabled(true);
}
void AssetListTabWidget::OnGenerateBundleButtonPressed()
{
GenerateBundlesModal generateBundlesModal(
this,
m_fileTableModel->GetFileAbsolutePath(m_selectedFileTableIndex),
m_guiApplicationManager->GetBundlesFolder(),
m_guiApplicationManager->GetBundleSettingsFolder(),
this);
generateBundlesModal.exec();
}
} //namespace AssetBundler
#include <source/ui/moc_AssetListTabWidget.cpp>
@@ -0,0 +1,82 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <source/ui/AssetBundlerTabWidget.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QDir>
#include <QItemSelectionModel>
#include <QModelIndex>
#include <QSharedPointer>
#include <QString>
#include <QWidget>
#endif
namespace Ui
{
class AssetListTabWidget;
}
class QFileSystemModel;
class QStringListModel;
namespace AssetBundler
{
class GUIApplicationManager;
class AssetListFileTableModel;
class AssetListTableModel;
class AssetListTabWidget
: public AssetBundlerTabWidget
{
Q_OBJECT
public:
explicit AssetListTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager);
virtual ~AssetListTabWidget() {}
//////////////////////////////////////////////////////////////////////////
// AssetBundlerTabWidget overrides
QString GetTabTitle() override { return tr("Asset Lists"); }
QString GetFileTypeDisplayName() override { return tr("Asset List file"); }
AssetBundlingFileType GetFileType() override { return AssetBundlingFileType::AssetListFileType; }
bool HasUnsavedChanges() override { return false; };
void Reload() override;
bool SaveCurrentSelection() override { return true; };
bool SaveAll() override { return true; };
void SetModelDataSource() override;
AzQtComponents::TableView* GetFileTableView() override;
QModelIndex GetSelectedFileTableIndex() override;
AssetBundlerAbstractFileTableModel* GetFileTableModel() override;
void SetActiveProjectLabel(const QString& labelText) override;
void ApplyConfig() override;
void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override;
//////////////////////////////////////////////////////////////////////////
private:
void OnGenerateBundleButtonPressed();
QSharedPointer<Ui::AssetListTabWidget> m_ui;
QSharedPointer<AssetListFileTableModel> m_fileTableModel;
QModelIndex m_selectedFileTableIndex;
QSharedPointer<AssetBundler::AssetBundlerFileTableFilterModel> m_assetListContentsFilterModel;
QSharedPointer<AssetListTableModel> m_assetListContentsModel;
};
} // namespace AssetBundler
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AssetListTabWidget</class>
<widget class="QWidget" name="AssetListTabWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>813</width>
<height>673</height>
</rect>
</property>
<property name="windowTitle">
<string>Asset List Tab Widget</string>
</property>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<item>
<layout class="QHBoxLayout" name="projectLabelHorizontalLayout">
<item>
<widget class="QLabel" name="activeProjectLabel"/>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="generateBundleButton">
<property name="text">
<string>Generate Bundle</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0">
<item>
<widget class="QFrame" name="fileTableFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="fileTableVerticalLayout">
<item>
<widget class="QFrame" name="fileTableHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="fileTableHeaderLayout">
<item>
<widget class="QLabel" name="assetListFilesLabel">
<property name="text">
<string>Asset List Files</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="fileFilteredSearchWidget">
<property name="placeholderText" stdset="0">
<string>Filter by file name</string>
</property>
</widget>
</item>
<item>
<widget class="AzQtComponents::TableView" name="assetListsTable"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="fileContentsFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="fileContentsVerticalLayout">
<item>
<widget class="QFrame" name="fileContentsHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="fileContentsHeaderLayout">
<item>
<widget class="QLabel" name="assetListContentsLabel">
<property name="text">
<string>Asset List</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="assetListFileAbsolutePathLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="assetListContentsFilteredSearchWidget">
<property name="placeholderText" stdset="0">
<string>Filter by file name</string>
</property>
</widget>
</item>
<item>
<widget class="AzQtComponents::TableView" name="assetListContentsTable"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::TableView</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/Widgets/TableView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,159 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc. or its affiliates or
*its licensors.
*
* For complete copyrightand 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.
*
*/
#include <source/ui/BundleListTabWidget.h>
#include <source/ui/ui_BundleListTabWidget.h>
#include <source/models/BundleFileListModel.h>
#include <source/utils/GUIApplicationManager.h>
#include <AzQtComponents/Components/FilteredSearchWidget.h>
#include <QFileDialog>
#include <QItemSelectionModel>
const double BytesToMegabytes = 1024.0 * 1024.0;
namespace AssetBundler
{
BundleListTabWidget::BundleListTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager)
: AssetBundlerTabWidget(parent, guiApplicationManager)
{
m_ui.reset(new Ui::BundleListTabWidget);
m_ui->setupUi(this);
m_ui->mainVerticalLayout->setContentsMargins(MarginSize, MarginSize, MarginSize, MarginSize);
m_fileTableModel.reset(new BundleFileListModel);
m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex()));
m_fileTableFilterModel->setSourceModel(m_fileTableModel.data());
m_ui->fileTableView->setModel(m_fileTableFilterModel.data());
connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
m_fileTableFilterModel.data(), static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
connect(m_ui->fileTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BundleListTabWidget::FileSelectionChanged);
m_ui->fileTableView->setIndentation(0);
m_relatedBundlesListModel.reset(new QStringListModel);
m_ui->relatedBundlesListView->setModel(m_relatedBundlesListModel.data());
m_ui->bundleFileContentsVerticalLayout->setContentsMargins(MarginSize, MarginSize, MarginSize, MarginSize);
SetModelDataSource();
}
void BundleListTabWidget::Reload()
{
// The act of cracking open paks kicks off a DirectoryChanged event. We need to temporarily remove the Bundles
// directory from our watched paths to prevent an infinite loop of events.
m_guiApplicationManager->RemoveWatchedPaths(m_watchedFolders);
// Reload all the bundle files
m_fileTableModel->Reload(AzToolsFramework::AssetBundleSettings::GetBundleFileExtension(), m_watchedFolders, m_watchedFiles);
// Update the selected row
FileSelectionChanged();
// Start recieving DirectoryChanged events for these folders again
m_guiApplicationManager->AddWatchedPaths(m_watchedFolders);
}
void BundleListTabWidget::SetModelDataSource()
{
// Remove the current watched folders and files
m_guiApplicationManager->RemoveWatchedPaths(m_watchedFolders + m_watchedFiles);
// Set the new watched folder for the model
m_watchedFolders.clear();
m_watchedFiles.clear();
m_watchedFolders.insert(m_guiApplicationManager->GetBundlesFolder().c_str());
ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType::BundleFileType);
m_guiApplicationManager->AddWatchedPaths(m_watchedFolders + m_watchedFiles);
}
AzQtComponents::TableView* BundleListTabWidget::GetFileTableView()
{
return m_ui->fileTableView;
}
QModelIndex BundleListTabWidget::GetSelectedFileTableIndex()
{
return m_selectedFileTableIndex;
}
AssetBundlerAbstractFileTableModel* BundleListTabWidget::GetFileTableModel()
{
return m_fileTableModel.get();
}
void BundleListTabWidget::SetActiveProjectLabel(const QString& labelText)
{
m_ui->activeProjectLabel->setText(labelText);
}
void BundleListTabWidget::ApplyConfig()
{
const GUIApplicationManager::Config& config = m_guiApplicationManager->GetConfig();
m_ui->fileTableFrame->setFixedWidth(config.fileTableWidth);
m_ui->fileTableView->header()->resizeSection(BundleFileListModel::Column::ColumnFileName, config.fileNameColumnWidth);
}
void BundleListTabWidget::FileSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
{
if (m_ui->fileTableView->selectionModel()->selectedRows().size() == 0)
{
m_selectedFileTableIndex = QModelIndex();
ClearDisplayedBundleValues();
return;
}
m_selectedFileTableIndex = m_fileTableFilterModel->mapToSource(m_ui->fileTableView->selectionModel()->selectedRows()[0]);
AZ::Outcome< BundleFileInfoPtr, void> fileInfoOutcome = m_fileTableModel->GetBundleInfo(m_selectedFileTableIndex);
if (!fileInfoOutcome.IsSuccess())
{
ClearDisplayedBundleValues();
return;
}
BundleFileInfoPtr fileInfoPtr = fileInfoOutcome.GetValue();
m_ui->absolutePathLabel->setText(fileInfoPtr->m_absolutePath.c_str());
QString sizeFormat("%1 MB");
double compressedSize = (double)fileInfoPtr->m_compressedSize / BytesToMegabytes;
m_ui->compressedSizeValueLabel->setText(sizeFormat.arg(compressedSize, 6, 'f', 3));
bool hasRelatedBundles = !fileInfoPtr->m_relatedBundles.isEmpty();
if (hasRelatedBundles)
{
m_relatedBundlesListModel->setStringList(fileInfoPtr->m_relatedBundles);
}
m_ui->relatedBundlesLabel->setVisible(hasRelatedBundles);
m_ui->relatedBundlesListView->setVisible(hasRelatedBundles);
}
void BundleListTabWidget::ClearDisplayedBundleValues()
{
m_ui->absolutePathLabel->clear();
m_ui->compressedSizeValueLabel->clear();
m_ui->relatedBundlesLabel->setVisible(false);
m_ui->relatedBundlesListView->setVisible(false);
m_relatedBundlesListModel->setStringList({});
}
}
#include <source/ui/moc_BundleListTabWidget.cpp>
@@ -0,0 +1,67 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <source/ui/AssetBundlerTabWidget.h>
#include <QItemSelection>
#include <QModelIndex>
#include <QSharedPointer>
#include <QString>
#include <QStringListModel>
#include <QWidget>
#endif
namespace Ui
{
class BundleListTabWidget;
}
namespace AssetBundler
{
class BundleFileListModel;
class BundleListTabWidget
: public AssetBundlerTabWidget
{
Q_OBJECT
public:
explicit BundleListTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager);
virtual ~BundleListTabWidget() {}
QString GetTabTitle() override { return tr("Completed Bundles"); }
QString GetFileTypeDisplayName() override { return tr("Bundle"); }
AssetBundlingFileType GetFileType() override { return AssetBundlingFileType::BundleFileType; }
bool HasUnsavedChanges() override { return false; }
void Reload() override;
bool SaveCurrentSelection() override { return true; }
bool SaveAll() override { return true; }
void SetModelDataSource() override;
AzQtComponents::TableView* GetFileTableView() override;
QModelIndex GetSelectedFileTableIndex() override;
AssetBundlerAbstractFileTableModel* GetFileTableModel() override;
void SetActiveProjectLabel(const QString& labelText) override;
void ApplyConfig() override;
void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override;
private:
void ClearDisplayedBundleValues();
QSharedPointer<Ui::BundleListTabWidget> m_ui;
QSharedPointer<BundleFileListModel> m_fileTableModel;
QModelIndex m_selectedFileTableIndex;
QSharedPointer<QStringListModel> m_relatedBundlesListModel;
};
}
@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BundleListTabWidget</class>
<widget class="QWidget" name="BundleListTabWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>888</width>
<height>562</height>
</rect>
</property>
<property name="windowTitle">
<string>Bundle List Tab Widget</string>
</property>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="activeProjectLabel"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0">
<item>
<widget class="QFrame" name="fileTableFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="fileTableHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="bundleFilesLabel">
<property name="text">
<string>Bundles</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="fileFilteredSearchWidget">
<property name="placeholderText" stdset="0">
<string>Filter by file name</string>
</property>
</widget>
</item>
<item>
<widget class="AzQtComponents::TableView" name="fileTableView"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="fileContentsFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="fileContentsHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="bundleInfoLabel">
<property name="text">
<string>Bundle Info</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="absolutePathLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bundleFileContentsWidget" native="true">
<layout class="QVBoxLayout" name="bundleFileContentsVerticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QFormLayout" name="bundleInfoLayout">
<item row="0" column="0">
<widget class="QLabel" name="compressedSizeLabel">
<property name="text">
<string>Compressed Size</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="compressedSizeValueLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="relatedBundlesLabel">
<property name="text">
<string>Related Bundles</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QListView" name="relatedBundlesListView"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::TableView</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/Widgets/TableView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,451 @@
/*
* 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.
*
*/
#include <source/ui/ComparisonDataWidget.h>
#include <source/ui/ui_ComparisonDataWidget.h>
#include <source/ui/NewFileDialog.h>
#include <QComboBox>
#include <QStringList>
namespace AssetBundler
{
//////////////////////////////////////////////////////////////////////////////////////////////////
// ComparisonDataWidget
//////////////////////////////////////////////////////////////////////////////////////////////////
const QStringList ComparisonTypeStringList = { "Default", "Delta", "Union", "Intersection", "Complement", "Wildcard", "Regex" };
ComparisonDataWidget::ComparisonDataWidget(
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> comparisonList,
size_t comparisonDataIndex,
const AZStd::string& defaultAssetListFileDirectory,
QWidget* parent)
: QWidget(parent)
, m_comparisonDataIndex(comparisonDataIndex)
, m_defaultAssetListFileDirectory(defaultAssetListFileDirectory)
{
m_ui.reset(new Ui::ComparisonDataWidget);
m_ui->setupUi(this);
m_comparisonList = comparisonList;
if (!IsComparisonDataIndexValid())
{
AZ_Error("AssetBundler", false, "ComparisonData index ( %u ) is out of bounds. ComparisonData cannot be displayed.", m_comparisonDataIndex);
return;
}
// Due to initialization order, we need to hard-code this or else the value will be overwritten once this
// ComparisonDataWidget is added to the ComparisonDataCard, and the Card.qss file is applied
QString lineEditStyle("background-color: #CCCCCC;");
m_ui->nameLineEdit->setStyleSheet(lineEditStyle);
m_ui->firstInputLineEdit->setStyleSheet(lineEditStyle);
m_ui->secondInputLineEdit->setStyleSheet(lineEditStyle);
m_ui->filePatternLineEdit->setStyleSheet(lineEditStyle);
m_ui->firstInputLineEdit->setReadOnly(true);
m_ui->secondInputLineEdit->setReadOnly(true);
SetAllDisplayValues(m_comparisonList->GetComparisonList()[m_comparisonDataIndex]);
MouseWheelEventFilter* mouseWheelEventFilter = new MouseWheelEventFilter(this);
connect(m_ui->nameLineEdit, &QLineEdit::textEdited, this, &ComparisonDataWidget::OnNameLineEditChanged);
m_ui->comparisonTypeComboBox->installEventFilter(mouseWheelEventFilter);
connect(m_ui->comparisonTypeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ComparisonDataWidget::OnComparisonTypeComboBoxChanged);
m_ui->firstInputComboBox->installEventFilter(mouseWheelEventFilter);
connect(m_ui->firstInputComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ComparisonDataWidget::OnFirstInputComboBoxChanged);
connect(m_ui->firstInputBrowseButton, &QPushButton::pressed, this, &ComparisonDataWidget::OnFirstInputBrowseButtonPressed);
m_ui->secondInputComboBox->installEventFilter(mouseWheelEventFilter);
connect(m_ui->secondInputComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ComparisonDataWidget::OnSecondInputComboBoxChanged);
connect(m_ui->secondInputBrowseButton, &QPushButton::pressed, this, &ComparisonDataWidget::OnSecondInputBrowseButtonPressed);
connect(m_ui->filePatternLineEdit, &QLineEdit::textEdited, this, &ComparisonDataWidget::OnFilePatternLineEditChanged);
}
void ComparisonDataWidget::UpdateListOfTokenNames()
{
using namespace AzToolsFramework;
if (!IsComparisonDataIndexValid())
{
return;
}
m_inputTokenNameList.clear();
m_ui->firstInputComboBox->clear();
m_ui->secondInputComboBox->clear();
// Store info about the current FirstInput and SecondInput values so we may auto-select them later
auto allComparisonDataSteps = m_comparisonList->GetComparisonList();
AZStd::string selectedFirstInput = allComparisonDataSteps.at(m_comparisonDataIndex).m_firstInput;
int selectedFirstInputIndex = 0;
AZStd::string selectedSecondInput = allComparisonDataSteps.at(m_comparisonDataIndex).m_secondInput;
int selectedSecondInputIndex = 0;
// Build list of all Token Names that have come before the current Comparison Step
m_inputTokenNameList.append(tr("Choose Asset List..."));
int currentComboBoxIndex = 1;
AZStd::string tokenName;
for (size_t i = 0; i < m_comparisonDataIndex; ++i)
{
tokenName = allComparisonDataSteps.at(i).m_output;
if (!AssetFileInfoListComparison::IsTokenFile(tokenName))
{
continue;
}
if (tokenName == selectedFirstInput)
{
selectedFirstInputIndex = currentComboBoxIndex;
}
if (tokenName == selectedSecondInput)
{
selectedSecondInputIndex = currentComboBoxIndex;
}
m_inputTokenNameList.append(RemoveTokenCharFromString(tokenName));
++currentComboBoxIndex;
}
// Update display with list of Token Names, and select current Token Name in both input combo boxes
m_ui->firstInputComboBox->insertItems(0, m_inputTokenNameList);
m_ui->firstInputComboBox->setCurrentIndex(selectedFirstInputIndex);
m_isFirstInputFileNameVisible = selectedFirstInputIndex == 0;
SetFirstInputFileVisibility(m_isFirstInputFileNameVisible);
m_ui->secondInputComboBox->insertItems(0, m_inputTokenNameList);
m_ui->secondInputComboBox->setCurrentIndex(selectedSecondInputIndex);
m_isSecondInputFileNameVisible = selectedSecondInputIndex == 0;
SetSecondInputFileVisibility(m_isSecondInputFileNameVisible);
}
void ComparisonDataWidget::SetAllDisplayValues(const AzToolsFramework::AssetFileInfoListComparison::ComparisonData& comparisonData)
{
using namespace AzToolsFramework;
// Name (Token value)
m_ui->nameLineEdit->setText(RemoveTokenCharFromString(comparisonData.m_output));
// Comparison Type
InitComparisonTypeComboBox(comparisonData);
// Inputs
UpdateListOfTokenNames();
m_ui->firstInputLineEdit->setText(QString(comparisonData.m_cachedFirstInputPath.c_str()));
m_ui->secondInputLineEdit->setText(QString(comparisonData.m_cachedSecondInputPath.c_str()));
// Update fields that are not always visible
UpdateOnComparisonTypeChanged(comparisonData.m_filePatternType != AssetFileInfoListComparison::FilePatternType::Default);
}
void ComparisonDataWidget::OnNameLineEditChanged()
{
if (!IsComparisonDataIndexValid())
{
return;
}
AZStd::string tokenName = m_ui->nameLineEdit->text().toUtf8().data();
AzToolsFramework::AssetFileInfoListComparison::FormatOutputToken(tokenName);
m_comparisonList->SetOutput(m_comparisonDataIndex, tokenName);
emit comparisonDataChanged();
emit comparisonDataTokenNameChanged(m_comparisonDataIndex);
}
void ComparisonDataWidget::UpdateOnComparisonTypeChanged(bool isFilePatternOperation)
{
using namespace AzToolsFramework;
if (!IsComparisonDataIndexValid())
{
return;
}
AssetFileInfoListComparison::ComparisonData comparisonData = m_comparisonList->GetComparisonList()[m_comparisonDataIndex];
m_ui->inputBLabel->setVisible(!isFilePatternOperation);
m_ui->secondInputComboBox->setVisible(!isFilePatternOperation);
SetSecondInputFileVisibility(!isFilePatternOperation && m_isSecondInputFileNameVisible);
m_ui->filePatternLabel->setVisible(isFilePatternOperation);
m_ui->filePatternLineEdit->setVisible(isFilePatternOperation);
m_ui->filePatternLineEdit->setText(comparisonData.m_filePattern.c_str());
}
void ComparisonDataWidget::InitComparisonTypeComboBox(const AzToolsFramework::AssetFileInfoListComparison::ComparisonData& comparisonData)
{
using namespace AzToolsFramework;
m_ui->comparisonTypeComboBox->insertItems(0, ComparisonTypeStringList);
int initialSelectionIndex = ComparisonTypeIndex::Default;
if (comparisonData.m_filePatternType != AssetFileInfoListComparison::FilePatternType::Default
&& comparisonData.m_comparisonType == AssetFileInfoListComparison::ComparisonType::FilePattern)
{
if (comparisonData.m_filePatternType == AssetFileInfoListComparison::FilePatternType::Wildcard)
{
initialSelectionIndex = ComparisonTypeIndex::Wildcard;
}
else
{
initialSelectionIndex = ComparisonTypeIndex::Regex;
}
}
else
{
switch (comparisonData.m_comparisonType)
{
case AssetFileInfoListComparison::ComparisonType::Default:
break;
case AssetFileInfoListComparison::ComparisonType::Delta:
initialSelectionIndex = ComparisonTypeIndex::Delta;
break;
case AssetFileInfoListComparison::ComparisonType::Union:
initialSelectionIndex = ComparisonTypeIndex::Union;
break;
case AssetFileInfoListComparison::ComparisonType::Intersection:
initialSelectionIndex = ComparisonTypeIndex::Intersection;
break;
case AssetFileInfoListComparison::ComparisonType::Complement:
initialSelectionIndex = ComparisonTypeIndex::Complement;
break;
default:
AZ_Warning("AssetBundler", false, "ComparisonType ( %u ) is not supported in the Asset Bundler", comparisonData.m_comparisonType);
}
}
m_ui->comparisonTypeComboBox->setCurrentIndex(initialSelectionIndex);
}
void ComparisonDataWidget::OnComparisonTypeComboBoxChanged(int index)
{
using namespace AzToolsFramework;
if (!IsComparisonDataIndexValid())
{
return;
}
bool isFilePattern = false;
switch (index)
{
case ComparisonTypeIndex::Default:
m_comparisonList->SetComparisonType(m_comparisonDataIndex, AssetFileInfoListComparison::ComparisonType::Default);
break;
case ComparisonTypeIndex::Delta:
m_comparisonList->SetComparisonType(m_comparisonDataIndex, AssetFileInfoListComparison::ComparisonType::Delta);
break;
case ComparisonTypeIndex::Union:
m_comparisonList->SetComparisonType(m_comparisonDataIndex, AssetFileInfoListComparison::ComparisonType::Union);
break;
case ComparisonTypeIndex::Intersection:
m_comparisonList->SetComparisonType(m_comparisonDataIndex, AssetFileInfoListComparison::ComparisonType::Intersection);
break;
case ComparisonTypeIndex::Complement:
m_comparisonList->SetComparisonType(m_comparisonDataIndex, AssetFileInfoListComparison::ComparisonType::Complement);
break;
case ComparisonTypeIndex::Wildcard:
m_comparisonList->SetComparisonType(m_comparisonDataIndex, AssetFileInfoListComparison::ComparisonType::FilePattern);
m_comparisonList->SetFilePatternType(m_comparisonDataIndex, AssetFileInfoListComparison::FilePatternType::Wildcard);
isFilePattern = true;
break;
case ComparisonTypeIndex::Regex:
m_comparisonList->SetComparisonType(m_comparisonDataIndex, AssetFileInfoListComparison::ComparisonType::FilePattern);
m_comparisonList->SetFilePatternType(m_comparisonDataIndex, AssetFileInfoListComparison::FilePatternType::Regex);
isFilePattern = true;
break;
}
UpdateOnComparisonTypeChanged(isFilePattern);
emit comparisonDataChanged();
}
void ComparisonDataWidget::OnFilePatternLineEditChanged()
{
if (!IsComparisonDataIndexValid())
{
return;
}
m_comparisonList->SetFilePattern(m_comparisonDataIndex, m_ui->filePatternLineEdit->text().toUtf8().data());
emit comparisonDataChanged();
}
void ComparisonDataWidget::OnFirstInputComboBoxChanged(int index)
{
AZStd::string firstInputValue;
if (!IsComparisonDataIndexValid())
{
return;
}
m_isFirstInputFileNameVisible = index == 0;
if (!m_isFirstInputFileNameVisible)
{
// The 0th index is the default value, which translates to an empty token string.
firstInputValue = m_ui->firstInputComboBox->currentText().toUtf8().data();
AzToolsFramework::AssetFileInfoListComparison::FormatOutputToken(firstInputValue);
}
SetFirstInputFileVisibility(m_isFirstInputFileNameVisible);
m_comparisonList->SetFirstInput(m_comparisonDataIndex, firstInputValue);
emit comparisonDataChanged();
}
void ComparisonDataWidget::SetFirstInputFileVisibility(bool isVisible)
{
m_ui->firstInputLineEdit->setVisible(isVisible);
m_ui->firstInputBrowseButton->setVisible(isVisible);
}
void ComparisonDataWidget::OnFirstInputBrowseButtonPressed()
{
if (!IsComparisonDataIndexValid())
{
return;
}
QString absoluteFilePath = BrowseButtonPressed();
if (absoluteFilePath.isEmpty())
{
// User canceled out of the dialog
return;
}
m_ui->firstInputLineEdit->setText(absoluteFilePath);
m_comparisonList->SetCachedFirstInputPath(m_comparisonDataIndex, absoluteFilePath.toUtf8().data());
}
void ComparisonDataWidget::OnSecondInputComboBoxChanged(int index)
{
if (!IsComparisonDataIndexValid())
{
return;
}
AZStd::string secondInputValue;
m_isSecondInputFileNameVisible = index == 0;
if (!m_isSecondInputFileNameVisible)
{
// The 0th index is the default value, which translates to an empty token string.
secondInputValue = m_ui->secondInputComboBox->currentText().toUtf8().data();
AzToolsFramework::AssetFileInfoListComparison::FormatOutputToken(secondInputValue);
}
SetSecondInputFileVisibility(m_isSecondInputFileNameVisible);
m_comparisonList->SetSecondInput(m_comparisonDataIndex, secondInputValue);
emit comparisonDataChanged();
}
void ComparisonDataWidget::SetSecondInputFileVisibility(bool isVisible)
{
m_ui->secondInputLineEdit->setVisible(isVisible);
m_ui->secondInputBrowseButton->setVisible(isVisible);
}
void ComparisonDataWidget::OnSecondInputBrowseButtonPressed()
{
if (!IsComparisonDataIndexValid())
{
return;
}
QString absoluteFilePath = BrowseButtonPressed();
if (absoluteFilePath.isEmpty())
{
// User canceled out of the dialog
return;
}
m_ui->secondInputLineEdit->setText(absoluteFilePath);
m_comparisonList->SetCachedSecondInputPath(m_comparisonDataIndex, absoluteFilePath.toUtf8().data());
}
QString ComparisonDataWidget::BrowseButtonPressed()
{
AZStd::string selectedPath = NewFileDialog::OSNewFileDialog(
this,
AzToolsFramework::AssetSeedManager::GetAssetListFileExtension(),
"Asset List",
m_defaultAssetListFileDirectory);
AzToolsFramework::RemovePlatformIdentifier(selectedPath);
return selectedPath.c_str();
}
bool ComparisonDataWidget::IsComparisonDataIndexValid()
{
return m_comparisonList->GetComparisonList().size() > m_comparisonDataIndex;
}
QString ComparisonDataWidget::RemoveTokenCharFromString(const AZStd::string& tokenName)
{
QString displayName = tokenName.c_str();
if (displayName.startsWith(AzToolsFramework::AssetFileInfoListComparison::GetTokenIdentifier()))
{
displayName.remove(0, 1);
}
return displayName;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// MouseWheelEventFilter
//////////////////////////////////////////////////////////////////////////////////////////////////
bool MouseWheelEventFilter::eventFilter(QObject* obj, QEvent* ev)
{
if (ev->type() == QEvent::Wheel)
{
return true;
}
return QObject::eventFilter(obj, ev);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// ComparisonDataCard
//////////////////////////////////////////////////////////////////////////////////////////////////
ComparisonDataCard::ComparisonDataCard(
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> comparisonList,
size_t comparisonDataIndex,
const AZStd::string& defaultAssetListFileDirectory,
QWidget* parent)
: AzQtComponents::Card(parent)
{
m_comparisonDataWidget = new ComparisonDataWidget(comparisonList, comparisonDataIndex, defaultAssetListFileDirectory, this);
setContentWidget(m_comparisonDataWidget);
connect(this, &AzQtComponents::Card::contextMenuRequested, this, &ComparisonDataCard::OnContextMenuRequested);
}
void ComparisonDataCard::OnContextMenuRequested(const QPoint& position)
{
emit comparisonDataCardContextMenuRequested(m_comparisonDataWidget->GetComparisonDataIndex(), position);
}
} // namespace AssetBundler
#include <source/ui/moc_ComparisonDataWidget.cpp>
@@ -0,0 +1,151 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzQtComponents/Components/Widgets/Card.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <QWidget>
#endif
namespace Ui
{
class ComparisonDataWidget;
}
namespace AssetBundler
{
//! Widget for displaying and editing a single Comparison Step inside a Comparison Rules file.
class ComparisonDataWidget
: public QWidget
{
Q_OBJECT
public:
explicit ComparisonDataWidget(
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> comparisonList,
size_t comparisonDataIndex,
const AZStd::string& defaultAssetListFileDirectory,
QWidget* parent = nullptr);
virtual ~ComparisonDataWidget() {}
enum ComparisonTypeIndex : int
{
Default = 0,
Delta,
Union,
Intersection,
Complement,
Wildcard,
Regex,
MAX
};
size_t GetComparisonDataIndex() { return m_comparisonDataIndex; }
void UpdateListOfTokenNames();
Q_SIGNALS:
void comparisonDataChanged();
void comparisonDataTokenNameChanged(size_t comparisonDataIndex);
private:
void SetAllDisplayValues(const AzToolsFramework::AssetFileInfoListComparison::ComparisonData& comparisonData);
void OnNameLineEditChanged();
void UpdateOnComparisonTypeChanged(bool isFilePatternOperation);
void InitComparisonTypeComboBox(const AzToolsFramework::AssetFileInfoListComparison::ComparisonData& comparisonData);
void OnComparisonTypeComboBoxChanged(int index);
void OnFilePatternLineEditChanged();
void OnFirstInputComboBoxChanged(int index);
void SetFirstInputFileVisibility(bool isVisible);
void OnFirstInputBrowseButtonPressed();
void OnSecondInputComboBoxChanged(int index);
void SetSecondInputFileVisibility(bool isVisible);
void OnSecondInputBrowseButtonPressed();
QString BrowseButtonPressed();
bool IsComparisonDataIndexValid();
QString RemoveTokenCharFromString(const AZStd::string& tokenName);
QSharedPointer<Ui::ComparisonDataWidget> m_ui;
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> m_comparisonList;
size_t m_comparisonDataIndex;
AZStd::string m_defaultAssetListFileDirectory;
QStringList m_inputTokenNameList;
QString m_outputAssetListFileAbsolutePath;
bool m_isFirstInputFileNameVisible = false;
bool m_isSecondInputFileNameVisible = false;
};
class MouseWheelEventFilter
: public QObject
{
Q_OBJECT
public:
explicit MouseWheelEventFilter(QObject* parent) : QObject(parent) {}
protected:
bool eventFilter(QObject* obj, QEvent* ev) override;
};
//! Wrapper widget that controls the expansion state and signals that trigger a context menu of a ComparisonDataWidget.
class ComparisonDataCard
: public AzQtComponents::Card
{
Q_OBJECT
public:
explicit ComparisonDataCard(
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> comparisonList,
size_t comparisonDataIndex,
const AZStd::string& defaultAssetListFileDirectory,
QWidget* parent = nullptr);
virtual ~ComparisonDataCard() {}
ComparisonDataWidget* GetComparisonDataWidget() { return m_comparisonDataWidget; }
Q_SIGNALS:
void comparisonDataCardContextMenuRequested(size_t comparisonDataIndex, const QPoint& position);
private:
ComparisonDataWidget* m_comparisonDataWidget = nullptr;
private slots:
void OnContextMenuRequested(const QPoint& position);
};
} // namespace AssetBundler
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ComparisonDataWidget</class>
<widget class="QWidget" name="ComparisonDataWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="comparisonDataFrame">
<layout class="QFormLayout" name="formLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="comparisonTypeLabel">
<property name="text">
<string>Comparison Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comparisonTypeComboBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="inputALabel">
<property name="text">
<string>Input A</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="firstInputComboBox"/>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLineEdit" name="firstInputLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="firstInputBrowseButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="inputBLabel">
<property name="text">
<string>Input B</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="secondInputComboBox"/>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLineEdit" name="secondInputLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="secondInputBrowseButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QLabel" name="filePatternLabel">
<property name="text">
<string>File Pattern</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="filePatternLineEdit"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,62 @@
/*
* 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.
*
*/
#include <source/ui/EditSeedDialog.h>
#include <source/ui/ui_EditSeedDialog.h>
#include <source/ui/PlatformSelectionWidget.h>
#include <QPushButton>
namespace AssetBundler
{
EditSeedDialog::EditSeedDialog(
QWidget* parent,
const AzFramework::PlatformFlags& enabledPlatforms,
const AzFramework::PlatformFlags& selectedPlatforms,
const AzFramework::PlatformFlags& partiallySelectedPlatforms)
: QDialog(parent)
{
m_ui.reset(new Ui::EditSeedDialog);
m_ui->setupUi(this);
m_ui->platformSelectionWidget->Init(enabledPlatforms);
m_ui->platformSelectionWidget->SetSelectedPlatforms(selectedPlatforms, partiallySelectedPlatforms);
connect(m_ui->platformSelectionWidget, &PlatformSelectionWidget::PlatformsSelected, this, &EditSeedDialog::OnPlatformSelectionChanged);
// Set up confirm and cancel buttons
connect(m_ui->applyChangesButton, &QPushButton::clicked, this, &QDialog::accept);
connect(m_ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject);
}
AzFramework::PlatformFlags EditSeedDialog::GetPlatformFlags()
{
return m_ui->platformSelectionWidget->GetSelectedPlatforms();
}
AzFramework::PlatformFlags EditSeedDialog::GetPartiallySelectedPlatformFlags()
{
return m_ui->platformSelectionWidget->GetPartiallySelectedPlatforms();
}
void EditSeedDialog::OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms)
{
// Disable the "Apply Changes" button if no platforms are selected
bool areAnyPlatformsSelected = selectedPlatforms != AzFramework::PlatformFlags::Platform_NONE ||
partiallySelectedPlatforms != AzFramework::PlatformFlags::Platform_NONE;
m_ui->applyChangesButton->setEnabled(areAnyPlatformsSelected);
}
} // namespace AssetBundler
#include <source/ui/moc_EditSeedDialog.cpp>
@@ -0,0 +1,52 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <QDialog>
#include <QSharedPointer>
#endif
namespace Ui
{
class EditSeedDialog;
}
namespace AssetBundler
{
class EditSeedDialog
: public QDialog
{
Q_OBJECT
public:
explicit EditSeedDialog(
QWidget* parent,
const AzFramework::PlatformFlags& enabledPlatforms,
const AzFramework::PlatformFlags& selectedPlatforms,
const AzFramework::PlatformFlags& partiallySelectedPlatforms = AzFramework::PlatformFlags::Platform_NONE);
virtual ~EditSeedDialog() {}
AzFramework::PlatformFlags GetPlatformFlags();
AzFramework::PlatformFlags GetPartiallySelectedPlatformFlags();
private:
void OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms);
QSharedPointer<Ui::EditSeedDialog> m_ui;
};
} // namespace AssetBundler
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditSeedDialog</class>
<widget class="QDialog" name="EditSeedDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>92</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Edit Selected Platforms</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="inputValuesFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AssetBundler::PlatformSelectionWidget" name="platformSelectionWidget" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="applyChangesButton">
<property name="text">
<string>Apply Changes</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AssetBundler::PlatformSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">source/ui/PlatformSelectionWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,276 @@
/*
* 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.
*
*/
#include <source/ui/GenerateBundlesModal.h>
#include <source/ui/ui_GenerateBundlesModal.h>
#include <source/ui/AssetListTabWidget.h>
#include <source/ui/NewFileDialog.h>
#include <source/utils/utils.h>
#include <AzCore/IO/FileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBundle/AssetBundleAPI.h>
#include <QMessageBox>
const char CustomBundleSettingsText[] = "Custom";
namespace AssetBundler
{
GenerateBundlesModal::GenerateBundlesModal(
QWidget* parent,
const AZStd::string& assetListFileAbsolutePath,
const AZStd::string& defaultBundleDirectory,
const AZStd::string& defaultBundleSettingsDirectory,
AssetListTabWidget* assetListTabWidget)
: QDialog(parent)
, m_assetListFileAbsolutePath(assetListFileAbsolutePath)
, m_defaultBundleDirectory(defaultBundleDirectory)
, m_defaultBundleSettingsDirectory(defaultBundleSettingsDirectory)
, m_assetListTabWidget(assetListTabWidget)
{
m_ui.reset(new Ui::GenerateBundlesModal);
m_ui->setupUi(this);
m_platformName = AzToolsFramework::GetPlatformIdentifier(m_assetListFileAbsolutePath);
// Selected Asset List
m_ui->selectedAssetListPathLabel->setText(QString(m_assetListFileAbsolutePath.c_str()));
// Platform
m_ui->platformNameLabel->setText(QString(m_platformName.c_str()));
// Bundle Output
m_ui->outputBundlePathLineEdit->setReadOnly(true);
connect(m_ui->outputBundlePathBrowseButton, &QPushButton::clicked, this, &GenerateBundlesModal::OnOutputBundleLocationBrowseButtonPressed);
// Bundle Settings files
m_ui->bundleSettingsFileLineEdit->setReadOnly(true);
m_ui->bundleSettingsFileLineEdit->setText(tr(CustomBundleSettingsText));
connect(m_ui->bundleSettingsFileBrowseButton, &QPushButton::clicked, this, &GenerateBundlesModal::OnBundleSettingsBrowseButtonPressed);
connect(m_ui->bundleSettingsFileSaveButton, &QPushButton::clicked, this, &GenerateBundlesModal::OnBundleSettingsSaveButtonPressed);
// Max Bundle Size
m_ui->maxBundleSizeSpinBox->setRange(1, AzToolsFramework::MaxBundleSizeInMB);
m_ui->maxBundleSizeSpinBox->setValue(AzToolsFramework::MaxBundleSizeInMB);
m_ui->maxBundleSizeSpinBox->setButtonSymbols(QAbstractSpinBox::ButtonSymbols::NoButtons);
m_ui->maxBundleSizeSpinBox->setSuffix(" MB");
connect(m_ui->maxBundleSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &GenerateBundlesModal::OnMaxBundleSizeChanged);
// Bundle Version
m_ui->bundleVersionSpinBox->setRange(1, AzFramework::AssetBundleManifest::CurrentBundleVersion);
m_ui->bundleVersionSpinBox->setValue(AzFramework::AssetBundleManifest::CurrentBundleVersion);
m_ui->bundleVersionSpinBox->setButtonSymbols(QAbstractSpinBox::ButtonSymbols::NoButtons);
connect(m_ui->bundleVersionSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &GenerateBundlesModal::OnBundleVersionChanged);
// Cancel and Generate Bundles buttons
m_ui->generateBundlesButton->setEnabled(false);
connect(m_ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject);
connect(m_ui->generateBundlesButton, &QPushButton::clicked, this, &GenerateBundlesModal::OnGenerateBundlesButtonPressed);
// In-memory Bundle Settings
m_bundleSettings.m_assetFileInfoListPath = m_assetListFileAbsolutePath;
m_bundleSettings.m_platform = m_platformName;
m_bundleSettings.m_maxBundleSizeInMB = static_cast<AZ::u64>(m_ui->maxBundleSizeSpinBox->value());
m_bundleSettings.m_bundleVersion = m_ui->bundleVersionSpinBox->value();
}
GenerateBundlesModal::~GenerateBundlesModal()
{
m_assetListTabWidget = nullptr;
}
void GenerateBundlesModal::OnOutputBundleLocationBrowseButtonPressed()
{
AZStd::string outputBundleAbsolutePath = NewFileDialog::OSNewFileDialog(
this,
AzToolsFramework::AssetBundleSettings::GetBundleFileExtension(),
"Bundle",
m_defaultBundleDirectory);
if (outputBundleAbsolutePath.empty())
{
// User canceled out of the dialog
return;
}
AzToolsFramework::RemovePlatformIdentifier(outputBundleAbsolutePath);
AddPlatformIdentifier(outputBundleAbsolutePath, m_platformName);
m_bundleSettings.m_bundleFilePath = outputBundleAbsolutePath;
m_ui->outputBundlePathLineEdit->setText(outputBundleAbsolutePath.c_str());
m_ui->generateBundlesButton->setEnabled(true);
}
void GenerateBundlesModal::OnBundleSettingsBrowseButtonPressed()
{
AZStd::string bundleSettingsAbsolutePath = NewFileDialog::OSNewFileDialog(
this,
AzToolsFramework::AssetBundleSettings::GetBundleSettingsFileExtension(),
"Bundle Settings",
m_defaultBundleSettingsDirectory);
if (bundleSettingsAbsolutePath.empty())
{
// User canceled out of the dialog
return;
}
// Read in the values from the Bundle Settings file
bool loadResult = LoadBundleSettingsValues(bundleSettingsAbsolutePath);
if (!loadResult)
{
return;
}
// Update the display name for our settings
UpdateBundleSettingsDisplayName(bundleSettingsAbsolutePath);
}
bool GenerateBundlesModal::LoadBundleSettingsValues(const AZStd::string& absoluteBundleSettingsFilePath)
{
using namespace AzToolsFramework;
auto outcome = AssetBundleSettings::Load(absoluteBundleSettingsFilePath);
if (!outcome.IsSuccess())
{
AZ_Error("AssetBundler", false, outcome.GetError().c_str());
return false;
}
// We don't want to overwrite all of the in-memory bundle settings, so we will just update a few
AssetBundleSettings loadedSettings = outcome.TakeValue();
m_bundleSettings.m_maxBundleSizeInMB = loadedSettings.m_maxBundleSizeInMB;
m_ui->maxBundleSizeSpinBox->setValue(static_cast<int>(m_bundleSettings.m_maxBundleSizeInMB));
m_bundleSettings.m_bundleVersion = loadedSettings.m_bundleVersion;
m_ui->bundleVersionSpinBox->setValue(m_bundleSettings.m_bundleVersion);
return true;
}
void GenerateBundlesModal::UpdateBundleSettingsDisplayName(const AZStd::string& absoluteBundleSettingsFilePath)
{
if (absoluteBundleSettingsFilePath.empty())
{
m_ui->bundleSettingsFileLineEdit->setText(tr(CustomBundleSettingsText));
return;
}
AZStd::string bundleSettingsFileName = absoluteBundleSettingsFilePath;
AzToolsFramework::RemovePlatformIdentifier(bundleSettingsFileName);
AzFramework::StringFunc::Path::GetFileName(bundleSettingsFileName.c_str(), bundleSettingsFileName);
m_ui->bundleSettingsFileLineEdit->setText(bundleSettingsFileName.c_str());
}
void GenerateBundlesModal::OnBundleSettingsSaveButtonPressed()
{
using namespace AzToolsFramework;
// Ask the user where they want to save the Bundle Settings file
AZStd::string bundleSettingsAbsolutePath = NewFileDialog::OSNewFileDialog(
this,
AssetBundleSettings::GetBundleSettingsFileExtension(),
"Bundle Settings",
m_defaultBundleSettingsDirectory);
if (bundleSettingsAbsolutePath.empty())
{
// User canceled out of the operation
return;
}
AzToolsFramework::RemovePlatformIdentifier(bundleSettingsAbsolutePath);
AddPlatformIdentifier(bundleSettingsAbsolutePath, m_platformName);
if (AZ::IO::FileIOBase::GetInstance()->Exists(bundleSettingsAbsolutePath.c_str()))
{
QString messageBoxText = QString(tr("Bundle Settings ( %1 ) already exists on-disk. Saving the current settings will override the existing settings. \n\nDo you wish to continue?")).arg(bundleSettingsAbsolutePath.c_str());
QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Replace Existing Settings")), messageBoxText);
if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes)
{
// User canceled out of the operation
return;
}
}
bool saveResult = AssetBundleSettings::Save(m_bundleSettings, bundleSettingsAbsolutePath);
if (!saveResult)
{
// Error has already been thrown
return;
}
m_assetListTabWidget->AddScanPathToAssetBundlerSettings(AssetBundlingFileType::BundleSettingsFileType, bundleSettingsAbsolutePath);
UpdateBundleSettingsDisplayName(bundleSettingsAbsolutePath);
}
void GenerateBundlesModal::OnMaxBundleSizeChanged()
{
m_bundleSettings.m_maxBundleSizeInMB = static_cast<AZ::u64>(m_ui->maxBundleSizeSpinBox->value());
UpdateBundleSettingsDisplayName("");
}
void GenerateBundlesModal::OnBundleVersionChanged()
{
m_bundleSettings.m_bundleVersion = m_ui->bundleVersionSpinBox->value();
UpdateBundleSettingsDisplayName("");
}
void GenerateBundlesModal::OnGenerateBundlesButtonPressed()
{
using namespace AzToolsFramework;
if (AZ::IO::FileIOBase::GetInstance()->Exists(m_bundleSettings.m_bundleFilePath.c_str()))
{
QString messageBoxText = QString(tr("Asset Bundle ( %1 ) already exists on-disk. Generating a new Bundle will override the existing Bundle. \n\nDo you wish to permanently delete the existing Bundle?")).arg(m_bundleSettings.m_bundleFilePath.c_str());
QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Replace Existing Bundle")), messageBoxText);
if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes)
{
// User canceled out of the operation
return;
}
}
bool result = false;
AssetBundleCommandsBus::BroadcastResult(result, &AssetBundleCommandsBus::Events::CreateAssetBundle, m_bundleSettings);
// This operation will take long enough that the OS will throw up its own wait cursor,
// but there is a slight delay where the UI is unresponsive but the cursor hasn't changed.
// We are changing the cursor manually to avoid that.
setCursor(Qt::WaitCursor);
emit QDialog::accept();
if (result)
{
m_assetListTabWidget->AddScanPathToAssetBundlerSettings(AssetBundlingFileType::BundleFileType, m_bundleSettings.m_bundleFilePath);
// The watched files list was updated after the files were created, so we need to force-reload them
m_assetListTabWidget->GetGUIApplicationManager()->UpdateFiles(AssetBundlingFileType::BundleFileType, { m_bundleSettings.m_bundleFilePath });
}
AZStd::vector<AZStd::string> generatedFilePaths = { m_bundleSettings.m_bundleFilePath };
NewFileDialog::FileGenerationResultMessageBox(this, generatedFilePaths, !result);
unsetCursor();
}
} // namespace AssetBundler
#include <source/ui/moc_GenerateBundlesModal.cpp>
@@ -0,0 +1,75 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/std/string/string.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <QDialog>
#include <QSharedPointer>
#endif
namespace Ui
{
class GenerateBundlesModal;
}
namespace AssetBundler
{
class AssetListTabWidget;
class GenerateBundlesModal
: public QDialog
{
Q_OBJECT
public:
explicit GenerateBundlesModal(
QWidget* parent,
const AZStd::string& assetListFileAbsolutePath,
const AZStd::string& defaultBundleDirectory,
const AZStd::string& defaultBundleSettingsDirectory,
AssetListTabWidget* assetListTabWidget);
virtual ~GenerateBundlesModal();
private:
void OnOutputBundleLocationBrowseButtonPressed();
void OnBundleSettingsBrowseButtonPressed();
bool LoadBundleSettingsValues(const AZStd::string& absoluteBundleSettingsFilePath);
void UpdateBundleSettingsDisplayName(const AZStd::string& absoluteBundleSettingsFilePath);
void OnBundleSettingsSaveButtonPressed();
void OnMaxBundleSizeChanged();
void OnBundleVersionChanged();
void OnGenerateBundlesButtonPressed();
QSharedPointer<Ui::GenerateBundlesModal> m_ui;
AssetListTabWidget* m_assetListTabWidget = nullptr;
AZStd::string m_assetListFileAbsolutePath;
AZStd::string m_defaultBundleDirectory;
AZStd::string m_defaultBundleSettingsDirectory;
AZStd::string m_platformName;
AzToolsFramework::AssetBundleSettings m_bundleSettings;
};
} // namespace AssetBundler
@@ -0,0 +1,292 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GenerateBundlesModal</class>
<widget class="QDialog" name="GenerateBundlesModal">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>750</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Generate Bundles</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QFrame" name="bundleSettingsFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="bundleSettingsFrameLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="outputBundleLocationFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="selectedAssetListLabel">
<property name="text">
<string>Asset List</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="selectedAssetListPathLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="platformLabel">
<property name="text">
<string>Platform</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="platformNameLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="outputBundleNameLabel">
<property name="text">
<string>Output Bundle Name</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="outputBundlePathLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="outputBundlePathBrowseButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="bundleSettingsFileFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Settings</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="bundleSettingsFileLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="bundleSettingsFileBrowseButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bundleSettingsFileSaveButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Max Bundle Size</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSpinBox" name="maxBundleSizeSpinBox"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Bundle Version</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSpinBox" name="bundleVersionSpinBox"/>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="generateBundlesButton">
<property name="text">
<string>Generate Bundles</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,249 @@
/*
* 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.
*
*/
#include <source/ui/MainWindow.h>
#include <source/ui/ui_MainWindow.h>
#include <source/utils/GUIApplicationManager.h>
#include <AzCore/std/utils.h>
#include <AzToolsFramework/UI/Logging/LogTableItemDelegate.h>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QCloseEvent>
#include <QDateTime>
#include <QDesktopServices>
namespace AssetBundler
{
MainWindow::MainWindow(AssetBundler::GUIApplicationManager* guiApplicationManager, QWidget* parent)
: QMainWindow(parent)
, m_guiApplicationManager(guiApplicationManager)
{
m_ui.reset(new Ui::MainWindow);
m_ui->setupUi(this);
m_ui->verticalLayout->setContentsMargins(0, 0, 0, 0);
// Set up Log
m_logModel.reset(new AzToolsFramework::Logging::LogTableModel(this));
m_ui->logTableView->setModel(m_logModel.data());
m_ui->logTableView->setIndentation(0);
m_ui->logTableView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui->logTableView, &QWidget::customContextMenuRequested, this, &MainWindow::ShowLogContextMenu);
AZ::Debug::TraceMessageBus::Handler::BusConnect();
// Create the Unsaved Changes Popup
m_unsavedChangesMsgBox.reset(new QMessageBox(this));
m_unsavedChangesMsgBox->setText(tr("There are unsaved changes."));
m_unsavedChangesMsgBox->setInformativeText(tr("Would you like to save all changes before quitting?"));
m_unsavedChangesMsgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
m_unsavedChangesMsgBox->setDefaultButton(QMessageBox::Save);
// Set up Quit functionality
m_ui->actionClose->setShortcut(Qt::Key_Q | Qt::CTRL);
m_ui->actionClose->setMenuRole(QAction::QuitRole);
connect(m_ui->actionClose, SIGNAL(triggered()), this, SLOT(close()));
this->addAction(m_ui->actionClose);
// Set up Tabs
AssetBundlerTabWidget::InitAssetBundlerSettings(m_guiApplicationManager->GetCurrentProjectFolder().c_str());
m_seedListTab.reset(new SeedTabWidget(this, m_guiApplicationManager, QString(m_guiApplicationManager->GetAssetBundlingFolder().c_str())));
m_ui->tabWidget->addTab(m_seedListTab.data(), m_seedListTab->GetTabTitle());
m_assetListTab.reset(new AssetListTabWidget(this, m_guiApplicationManager));
m_ui->tabWidget->addTab(m_assetListTab.data(), m_assetListTab->GetTabTitle());
m_rulesTab.reset(new RulesTabWidget(this, m_guiApplicationManager));
m_ui->tabWidget->addTab(m_rulesTab.data(), m_rulesTab->GetTabTitle());
m_bundleListTab.reset(new BundleListTabWidget(this, m_guiApplicationManager));
m_ui->tabWidget->addTab(m_bundleListTab.data(), m_bundleListTab->GetTabTitle());
// Set up link to documentation
QAction* supportAction = new QAction(QIcon(":/stylesheet/img/help.svg"), "", this);
connect(supportAction, &QAction::triggered, this, &MainWindow::OnSupportClicked);
connect(m_ui->actionDocumentation, &QAction::triggered, this, &MainWindow::OnSupportClicked);
m_ui->tabWidget->setActionToolBarVisible();
m_ui->tabWidget->addAction(supportAction);
// Set up Save functionality
m_ui->actionSave->setShortcut(Qt::Key_S | Qt::CTRL);
connect(m_ui->actionSave, &QAction::triggered, this, &MainWindow::SaveCurrentSelection);
m_ui->actionSaveAll->setShortcut(Qt::Key_S | Qt::CTRL | Qt::SHIFT);
connect(m_ui->actionSaveAll, &QAction::triggered, this, &MainWindow::SaveAll);
}
MainWindow::~MainWindow()
{
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
m_guiApplicationManager = nullptr;
}
void MainWindow::Activate()
{
m_seedListTab->Activate();
m_rulesTab->Activate();
m_assetListTab->Activate();
m_bundleListTab->Activate();
}
void MainWindow::ApplyConfig()
{
const GUIApplicationManager::Config& config = m_guiApplicationManager->GetConfig();
// Event Log Details
m_ui->logTableView->header()->resizeSection(AzToolsFramework::Logging::LogTableModel::ColumnType, config.logTypeColumnWidth);
m_ui->logTableView->header()->resizeSection(AzToolsFramework::Logging::LogTableModel::ColumnWindow, config.logSourceColumnWidth);
m_seedListTab->ApplyConfig();
m_assetListTab->ApplyConfig();
m_rulesTab->ApplyConfig();
m_bundleListTab->ApplyConfig();
}
void MainWindow::WriteToLog(const AZStd::string& message, AzToolsFramework::Logging::LogLine::LogType logType)
{
WriteToLog(message.c_str(), logType);
}
void MainWindow::WriteToLog(const QString& message, AzToolsFramework::Logging::LogLine::LogType logType)
{
WriteToLog(message.toUtf8().data(), logType);
}
void MainWindow::WriteToLog(const char* message, AzToolsFramework::Logging::LogLine::LogType logType)
{
WriteToLog(message, "AssetBundler", logType);
}
void MainWindow::WriteToLog(const char* message, const char* window, AzToolsFramework::Logging::LogLine::LogType logType)
{
m_logModel->AppendLine(
AzToolsFramework::Logging::LogLine(
message,
window,
logType,
QDateTime::currentMSecsSinceEpoch()));
m_ui->logTableView->scrollToBottom();
}
bool MainWindow::OnPreError(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message)
{
WriteToLog(message, window, AzToolsFramework::Logging::LogLine::LogType::TYPE_ERROR);
return true;
}
bool MainWindow::OnPreWarning(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message)
{
WriteToLog(message, window, AzToolsFramework::Logging::LogLine::LogType::TYPE_WARNING);
return true;
}
bool MainWindow::OnPrintf(const char* /*window*/, const char* /*message*/)
{
return true;
}
void MainWindow::ShowWindow()
{
AzQtComponents::bringWindowToTop(this);
}
void MainWindow::closeEvent(QCloseEvent* event)
{
if (!HasUnsavedChanges())
{
// No need to ask the user if they want to quit when there are no unsaved changes
event->accept();
return;
}
int unsavedChangesResult = m_unsavedChangesMsgBox->exec();
switch (unsavedChangesResult)
{
case QMessageBox::Save:
// Save All was clicked
SaveAll();
event->accept();
break;
case QMessageBox::Discard:
// Don't Save was clicked
event->accept();
break;
case QMessageBox::Cancel:
// Cancel was clicked
event->ignore();
break;
default:
// should never be reached
AZ_Assert(false, "No result was returned by the Unsaved Changes Message Box!");
break;
}
}
void MainWindow::OnSupportClicked()
{
QDesktopServices::openUrl(
QStringLiteral("https://docs.aws.amazon.com/lumberyard/latest/userguide/asset-bundler-overview.html"));
}
void MainWindow::ShowLogContextMenu(const QPoint& pos)
{
QModelIndex index = m_ui->logTableView->indexAt(pos);
QMenu menu;
QAction* action = menu.addAction(tr("Copy line"), this, [&]()
{
QApplication::clipboard()->setText(index.data(AzToolsFramework::Logging::LogTableModel::LogLineTextRole).toString());
});
action->setEnabled(index.isValid());
menu.exec(m_ui->logTableView->mapToGlobal(pos));
}
bool MainWindow::HasUnsavedChanges()
{
return m_seedListTab->HasUnsavedChanges() || m_rulesTab->HasUnsavedChanges();
}
void MainWindow::SaveCurrentSelection()
{
switch (m_ui->tabWidget->currentIndex())
{
case TabIndex::Seeds:
m_seedListTab->SaveCurrentSelection();
break;
case TabIndex::Rules:
m_rulesTab->SaveCurrentSelection();
break;
default:
break;
}
}
void MainWindow::SaveAll()
{
m_seedListTab->SaveAll();
m_rulesTab->SaveAll();
}
} // namespace AssetBundler
#include <source/ui/moc_MainWindow.cpp>
@@ -0,0 +1,105 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <source/ui/AssetListTabWidget.h>
#include <source/ui/SeedTabWidget.h>
#include <source/ui/BundleListTabWidget.h>
#include <source/ui/RulesTabWidget.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzToolsFramework/UI/Logging/LogLine.h>
#include <AzToolsFramework/UI/Logging/LogTableModel.h>
#include <QMainWindow>
#include <QMessageBox>
#include <QSharedPointer>
#endif
namespace Ui
{
class MainWindow;
}
namespace AssetBundler
{
class GUIApplicationManager;
class MainWindow
: public QMainWindow
, public AZ::Debug::TraceMessageBus::Handler
{
Q_OBJECT
public:
explicit MainWindow(AssetBundler::GUIApplicationManager* guiApplicationManager, QWidget* parent = 0);
virtual ~MainWindow();
void Activate();
void ApplyConfig();
void WriteToLog(const AZStd::string& message, AzToolsFramework::Logging::LogLine::LogType logType);
void WriteToLog(const QString& message, AzToolsFramework::Logging::LogLine::LogType logType);
void WriteToLog(const char* message, AzToolsFramework::Logging::LogLine::LogType logType);
void WriteToLog(const char* message, const char* window, AzToolsFramework::Logging::LogLine::LogType logType);
/////////////////////////////////////////////////////////
// TraceMessageBus overrides
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
bool OnPrintf(const char* window, const char* message) override;
/////////////////////////////////////////////////////////
public Q_SLOTS:
void ShowWindow();
protected:
void closeEvent(QCloseEvent* event) override;
private:
QSharedPointer<Ui::MainWindow> m_ui;
// Tabs
QSharedPointer<AssetListTabWidget> m_assetListTab;
QSharedPointer<SeedTabWidget> m_seedListTab;
QSharedPointer<RulesTabWidget> m_rulesTab;
QSharedPointer<BundleListTabWidget> m_bundleListTab;
enum TabIndex
{
Seeds,
AssetLists,
Rules,
Bundles,
MAX
};
void OnSupportClicked();
// Log
QSharedPointer<AzToolsFramework::Logging::LogTableModel> m_logModel;
void ShowLogContextMenu(const QPoint& pos);
// Detecting Unsaved Changes
QSharedPointer<QMessageBox> m_unsavedChangesMsgBox;
bool HasUnsavedChanges();
void SaveCurrentSelection();
void SaveAll();
AssetBundler::GUIApplicationManager* m_guiApplicationManager = nullptr;
};
} // namespace AssetBundler
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>1000</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="windowTitle">
<string>Asset Bundler</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="AzQtComponents::TabWidget" name="tabWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item>
<widget class="AzQtComponents::TableView" name="logTableView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionSave"/>
<addaction name="actionSaveAll"/>
<addaction name="separator"/>
<addaction name="actionClose"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionDocumentation"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<action name="actionSave">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionClose">
<property name="text">
<string>Close</string>
</property>
</action>
<action name="actionDocumentation">
<property name="text">
<string>Documentation</string>
</property>
</action>
<action name="actionAbout_Asset_Bundler">
<property name="text">
<string>About Asset Bundler</string>
</property>
</action>
<action name="actionSaveAll">
<property name="text">
<string>Save All</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::TableView</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/Widgets/TableView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header location="global">AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,224 @@
/*
* 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.
*
*/
#include <source/ui/NewFileDialog.h>
#include <source/ui/ui_NewFileDialog.h>
#include <source/ui/PlatformSelectionWidget.h>
#include <source/utils/utils.h>
#include <AzCore/IO/FileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
namespace AssetBundler
{
NewFileDialog::NewFileDialog(
QWidget* parent,
const QString& dialogTitle,
const QString& startingPath,
const char* fileExtension,
const QString& fileNameFilter,
const AzFramework::PlatformFlags& enabledPlatforms,
bool isRunningRule)
: QDialog(parent)
, m_startingPath(startingPath)
, m_fileExtension(fileExtension)
{
m_ui.reset(new Ui::NewFileDialog);
m_ui->setupUi(this);
setWindowTitle(dialogTitle);
// Set up File Name section
m_ui->fileNameLineEdit->setEnabled(false);
connect(m_ui->browseButton, &QPushButton::clicked, this, &NewFileDialog::OnBrowseButtonPressed);
m_newFileDialog.setFileMode(QFileDialog::AnyFile);
m_newFileDialog.setNameFilter(fileNameFilter);
m_newFileDialog.setViewMode(QFileDialog::Detail);
m_newFileDialog.setDirectory(m_startingPath);
// We are not creating a new file when Qt thinks we are, so we need to block signals or else the file watcher will be triggered too soon
m_newFileDialog.blockSignals(true);
// Set up Platform selection
QString disabledPatformMessageOverride;
if (isRunningRule)
{
disabledPatformMessageOverride = tr("This platform is not valid for all input Asset Lists.");
}
m_ui->platformSelectionWidget->Init(enabledPlatforms, disabledPatformMessageOverride);
connect(m_ui->platformSelectionWidget, &PlatformSelectionWidget::PlatformsSelected, this, &NewFileDialog::OnPlatformSelectionChanged);
// Set up Cancel and Create New File buttons
m_ui->createFileButton->setEnabled(false);
connect(m_ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject);
connect(m_ui->createFileButton, &QPushButton::clicked, this, &NewFileDialog::OnCreateFileButtonPressed);
}
AZStd::string NewFileDialog::GetAbsoluteFilePath()
{
return m_absoluteFilePath;
}
AzFramework::PlatformFlags NewFileDialog::GetPlatformFlags()
{
return m_ui->platformSelectionWidget->GetSelectedPlatforms();
}
void NewFileDialog::OnBrowseButtonPressed()
{
int result = m_newFileDialog.exec();
if (result == QDialog::DialogCode::Accepted)
{
m_absoluteFilePath = m_newFileDialog.selectedFiles()[0].toUtf8().data();
AzToolsFramework::RemovePlatformIdentifier(m_absoluteFilePath);
if (m_fileExtension && !AzFramework::StringFunc::Path::HasExtension(m_absoluteFilePath.c_str()))
{
m_absoluteFilePath.append(".");
m_absoluteFilePath.append(m_fileExtension);
}
m_ui->fileNameLineEdit->setEnabled(true);
m_ui->fileNameLineEdit->setText(m_absoluteFilePath.c_str());
}
m_fileNameIsValid = !m_absoluteFilePath.empty();
m_ui->createFileButton->setEnabled(m_platformIsValid && m_fileNameIsValid);
}
void NewFileDialog::OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms)
{
// Hide the "Create File" button if no platforms are selected
m_platformIsValid = selectedPlatforms != AzFramework::PlatformFlags::Platform_NONE;
m_ui->createFileButton->setEnabled(m_platformIsValid && m_fileNameIsValid);
}
void NewFileDialog::OnCreateFileButtonPressed()
{
// Check to see if any of the selected platform-specific files already exist on-disk
QString overwriteExistingFilesList;
AZStd::fixed_vector<AZStd::string, AzFramework::NumPlatforms> selectedPlatformNames = AzFramework::PlatformHelper::GetPlatforms(GetPlatformFlags());
for (const AZStd::string& platformName : selectedPlatformNames)
{
FilePath platformSpecificFilePath(GetAbsoluteFilePath(), platformName);
const char* platformSpecificFileAbsolutePath = platformSpecificFilePath.AbsolutePath().c_str();
if (AZ::IO::FileIOBase::GetInstance()->Exists(platformSpecificFileAbsolutePath))
{
overwriteExistingFilesList.append(QString("%1\n").arg(platformSpecificFileAbsolutePath));
}
}
// Ask the user if they are sure they want to overwrite existing files
if (!overwriteExistingFilesList.isEmpty())
{
QString messageBoxText = QString(tr("The following files already exist on-disk. Generating new files will overwrite the existing ones.\n\n%1\n\nDo you wish to permanently delete the existing files?")).arg(overwriteExistingFilesList);
QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Replace Existing Files")), messageBoxText);
if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes)
{
// User canceled out of the operation
return;
}
}
emit QDialog::accept();
}
AZStd::string NewFileDialog::OSNewFileDialog(QWidget* parent, const char* fileExtension, const char* fileTypeDisplayName, const AZStd::string& startingDirectory)
{
QFileDialog filePathDialog(parent);
filePathDialog.setFileMode(QFileDialog::AnyFile);
filePathDialog.setNameFilter(QString(tr("%1 (*.%2)")).arg(fileTypeDisplayName).arg(fileExtension));
filePathDialog.setViewMode(QFileDialog::Detail);
filePathDialog.setDirectory(startingDirectory.c_str());
// Since we handle file creation instead of the OS, we have to block signals or else the model will be reloaded,
// and our in-memory changes will be lost.
filePathDialog.blockSignals(true);
int result = filePathDialog.exec();
if (result == QDialog::DialogCode::Rejected || filePathDialog.selectedFiles().empty())
{
// User canceled out of the file dialog, cancel operation
return "";
}
AZStd::string absoluteFilePath(filePathDialog.selectedFiles()[0].toUtf8().data());
if (!AzFramework::StringFunc::Path::HasExtension(absoluteFilePath.c_str()))
{
absoluteFilePath = AZStd::string::format("%s%c%s", absoluteFilePath.c_str(), AZ_FILESYSTEM_EXTENSION_SEPARATOR, fileExtension);
}
return absoluteFilePath;
}
int NewFileDialog::FileGenerationResultMessageBox(QWidget* parent, const AZStd::vector<AZStd::string>& generatedFiles, bool generatedWithErrors)
{
QMessageBox messageBox(parent);
messageBox.setStandardButtons(QMessageBox::Ok);
messageBox.setDefaultButton(QMessageBox::Ok);
if (generatedFiles.empty())
{
messageBox.setText(QString("No files were generated. Please refer to the console for more information."));
messageBox.setIcon(QMessageBox::Critical);
return messageBox.exec();
}
QString messageText;
if (generatedWithErrors)
{
messageText = QString("The following files were generated with errors. Please refer to the console for more information.\n\n");
messageBox.setIcon(QMessageBox::Warning);
}
else
{
messageText = QString("You have successfully generated:\n\n");
messageBox.setIcon(QMessageBox::NoIcon);
}
AZStd::string fileName;
for (const AZStd::string& filePath : generatedFiles)
{
AzFramework::StringFunc::Path::GetFullFileName(filePath.c_str(), fileName);
messageText.append(QString("%1\n").arg(fileName.c_str()));
}
AZStd::string extension;
AzFramework::StringFunc::Path::GetExtension(fileName.c_str(), extension, false);
if (extension == AzToolsFramework::AssetSeedManager::GetAssetListFileExtension())
{
messageText.append("\nVisit the Asset Lists tab to see the lists.");
}
else if (extension == AzToolsFramework::AssetBundleSettings::GetBundleFileExtension())
{
messageText.append("\nVisit the Completed Bundles tab to see the bundles.");
}
messageBox.setText(messageText);
// QMessageBoxes try to shrink to the smallest size possible, so we need to make a spacer
QSpacerItem* horizontalSpacer = new QSpacerItem(550, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
QGridLayout* layout = static_cast<QGridLayout*>(messageBox.layout());
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
return messageBox.exec();
}
} //namespace AssetBundler
#include <source/ui/moc_NewFileDialog.cpp>
@@ -0,0 +1,73 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/std/string/string.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QDialog>
#include <QFileDialog>
#include <QSharedPointer>
#endif
namespace Ui
{
class NewFileDialog;
}
namespace AssetBundler
{
class GUIApplicationManager;
class NewFileDialog
: public QDialog
{
Q_OBJECT
public:
explicit NewFileDialog(
QWidget* parent,
const QString& dialogTitle,
const QString& startingPath,
const char* fileExtension,
const QString& fileNameFilter,
const AzFramework::PlatformFlags& enabledPlatforms,
bool isRunningRule = false);
virtual ~NewFileDialog() {}
AZStd::string GetAbsoluteFilePath();
AzFramework::PlatformFlags GetPlatformFlags();
//! A standard OS-specific New File Dialog, but blocks all Qt signals from the dialog and does NOT create a new file.
//! Use in place of the static QFileDialog functions to avoid unexpected file watcher updates.
//! Returns the absolute path of the file the user either selected or attempted to create, or an empty string if the user canceled out of the dialog.
static AZStd::string OSNewFileDialog(QWidget* parent, const char* fileExtension, const char* fileTypeDisplayName, const AZStd::string& startingDirectory);
static int FileGenerationResultMessageBox(QWidget* parent, const AZStd::vector<AZStd::string>& generatedFiles, bool generatedWithErrors);
private:
void OnBrowseButtonPressed();
void OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms);
void OnCreateFileButtonPressed();
QSharedPointer<Ui::NewFileDialog> m_ui;
QString m_startingPath;
const char* m_fileExtension;
QFileDialog m_newFileDialog;
AZStd::string m_absoluteFilePath;
bool m_fileNameIsValid = false;
bool m_platformIsValid = false;
};
} // namespace AssetBundler
@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewFileDialog</class>
<widget class="QDialog" name="NewFileDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>90</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>450</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="inputValuesFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="fileNameLabel">
<property name="text">
<string>Save As:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="fileNameLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browseButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="AssetBundler::PlatformSelectionWidget" name="platformSelectionWidget" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
<property name="default">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="createFileButton">
<property name="text">
<string>Create New File</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AssetBundler::PlatformSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">source/ui/PlatformSelectionWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,126 @@
/*
* 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.
*
*/
#include <source/ui/PlatformSelectionWidget.h>
#include <source/ui/ui_PlatformSelectionWidget.h>
#include <AzCore/std/string/string.h>
namespace AssetBundler
{
PlatformSelectionWidget::PlatformSelectionWidget(QWidget* parent)
: QWidget(parent)
{
m_ui.reset(new Ui::PlatformSelectionWidget);
m_ui->setupUi(this);
m_platformHelper.reset(new AzFramework::PlatformHelper);
m_selectedPlatforms = AzFramework::PlatformFlags::Platform_NONE;
m_partiallySelectedPlatforms = AzFramework::PlatformFlags::Platform_NONE;
}
void PlatformSelectionWidget::Init(const AzFramework::PlatformFlags& enabledPlatforms, const QString& disabledPatformMessageOverride)
{
using namespace AzFramework;
// Set up Platform Checkboxes
for (const AZStd::string_view& platformString : m_platformHelper->GetPlatforms(PlatformFlags::AllNamedPlatforms))
{
// Create the CheckBox and store what platform it maps to
QSharedPointer<QCheckBox> platformCheckBox(new QCheckBox(QString::fromUtf8(platformString.data(), platformString.size())));
m_platformCheckBoxes.push_back(platformCheckBox);
PlatformFlags currentPlatformFlag = m_platformHelper->GetPlatformFlag(platformString);
m_platformList.push_back(currentPlatformFlag);
// Add the CheckBox to the view
m_ui->platformCheckBoxLayout->addWidget(platformCheckBox.data());
// If the platform is not enabled for the current project, disable it and tell the user
if ((enabledPlatforms & currentPlatformFlag) == PlatformFlags::Platform_NONE)
{
platformCheckBox->setEnabled(false);
if (disabledPatformMessageOverride.isEmpty())
{
platformCheckBox->setToolTip(tr("This platform is not enabled for the current project."));
}
else
{
platformCheckBox->setToolTip(disabledPatformMessageOverride);
}
}
else
{
connect(platformCheckBox.data(), &QCheckBox::stateChanged, this, &PlatformSelectionWidget::OnPlatformSelectionChanged);
}
}
}
void PlatformSelectionWidget::SetSelectedPlatforms(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms)
{
m_selectedPlatforms = AzFramework::PlatformFlags::Platform_NONE;
m_partiallySelectedPlatforms = AzFramework::PlatformFlags::Platform_NONE;
for (int checkBoxIndex = 0; checkBoxIndex < m_platformCheckBoxes.size(); ++checkBoxIndex)
{
AzFramework::PlatformFlags checkBoxPlatform = m_platformList[checkBoxIndex];
bool isSelected = (checkBoxPlatform & selectedPlatforms) != AzFramework::PlatformFlags::Platform_NONE;
m_platformCheckBoxes[checkBoxIndex]->setChecked(isSelected);
if (isSelected)
{
m_selectedPlatforms |= checkBoxPlatform;
}
// Set the check status to Qt::PartiallyChecked when some of the selected items support the current platform
bool isPartiallySelected = (checkBoxPlatform & partiallySelectedPlatforms) != AzFramework::PlatformFlags::Platform_NONE;
if (isPartiallySelected)
{
m_platformCheckBoxes[checkBoxIndex]->setCheckState(Qt::PartiallyChecked);
m_partiallySelectedPlatforms |= checkBoxPlatform;
}
}
emit PlatformsSelected(m_selectedPlatforms, m_partiallySelectedPlatforms);
}
AzFramework::PlatformFlags PlatformSelectionWidget::GetSelectedPlatforms()
{
return m_selectedPlatforms;
}
AzFramework::PlatformFlags PlatformSelectionWidget::GetPartiallySelectedPlatforms()
{
return m_partiallySelectedPlatforms;
}
void PlatformSelectionWidget::OnPlatformSelectionChanged()
{
m_selectedPlatforms = AzFramework::PlatformFlags::Platform_NONE;
m_partiallySelectedPlatforms = AzFramework::PlatformFlags::Platform_NONE;
for (int checkBoxIndex = 0; checkBoxIndex < m_platformCheckBoxes.size(); ++checkBoxIndex)
{
if (m_platformCheckBoxes[checkBoxIndex]->checkState() == Qt::Checked)
{
m_selectedPlatforms |= m_platformList[checkBoxIndex];
}
else if (m_platformCheckBoxes[checkBoxIndex]->checkState() == Qt::PartiallyChecked)
{
m_partiallySelectedPlatforms |= m_platformList[checkBoxIndex];
}
}
emit PlatformsSelected(m_selectedPlatforms, m_partiallySelectedPlatforms);
}
} // namespace AssetBundler
#include <source/ui/moc_PlatformSelectionWidget.cpp>
@@ -0,0 +1,62 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QCheckBox>
#include <QSharedPointer>
#include <QWidget>
#endif
namespace Ui
{
class PlatformSelectionWidget;
}
namespace AssetBundler
{
class PlatformSelectionWidget
: public QWidget
{
Q_OBJECT
public:
explicit PlatformSelectionWidget(QWidget* parent);
virtual ~PlatformSelectionWidget() {}
void Init(const AzFramework::PlatformFlags& enabledPlatforms, const QString& disabledPatformMessageOverride = "");
void SetSelectedPlatforms(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms);
AzFramework::PlatformFlags GetSelectedPlatforms();
AzFramework::PlatformFlags GetPartiallySelectedPlatforms();
Q_SIGNALS:
void PlatformsSelected(AzFramework::PlatformFlags selectedPlatforms, AzFramework::PlatformFlags partiallySelectedPlatforms);
private:
void OnPlatformSelectionChanged();
QSharedPointer<Ui::PlatformSelectionWidget> m_ui;
AZStd::unique_ptr<AzFramework::PlatformHelper> m_platformHelper;
QVector<QSharedPointer<QCheckBox>> m_platformCheckBoxes;
AZStd::vector<AzFramework::PlatformFlags> m_platformList;
AzFramework::PlatformFlags m_selectedPlatforms;
AzFramework::PlatformFlags m_partiallySelectedPlatforms;
};
} // namespace AssetBundler
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PlatformSelectionWidget</class>
<widget class="QWidget" name="PlatformSelectionWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="platformsLabel">
<property name="text">
<string>Platforms:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="platformCheckBoxLayout"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,432 @@
/*
* 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.
*
*/
#include <source/ui/RulesTabWidget.h>
#include <source/ui/ui_RulesTabWidget.h>
#include <source/models/RulesFileTableModel.h>
#include <source/ui/ComparisonDataWidget.h>
#include <source/ui/NewFileDialog.h>
#include <source/utils/GUIApplicationManager.h>
#include <source/utils/utils.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <AzToolsFramework/AssetBundle/AssetBundleAPI.h>
#include <AzQtComponents/Components/FilteredSearchWidget.h>
#include <QAbstractTableModel>
#include <QItemSelectionModel>
#include <QMenu>
#include <QPushButton>
namespace AssetBundler
{
RulesTabWidget::RulesTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager)
: AssetBundlerTabWidget(parent, guiApplicationManager)
{
m_ui.reset(new Ui::RulesTabWidget);
m_ui->setupUi(this);
m_ui->mainVerticalLayout->setContentsMargins(MarginSize, MarginSize, MarginSize, MarginSize);
m_fileTableModel.reset(new RulesFileTableModel);
m_ui->fileTableView->setModel(m_fileTableModel.data());
// Table View of all Rules files
m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex()));
m_fileTableFilterModel->setSourceModel(m_fileTableModel.data());
m_ui->fileTableView->setModel(m_fileTableFilterModel.data());
connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
m_fileTableFilterModel.data(), static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
connect(m_ui->fileTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RulesTabWidget::FileSelectionChanged);
m_ui->fileTableView->setIndentation(0);
// New File Button
connect(m_ui->createNewFileButton, &QPushButton::clicked, this, &RulesTabWidget::OnNewFileButtonPressed);
// Run Rule Button
m_ui->runRuleButton->setEnabled(false);
connect(m_ui->runRuleButton, &QPushButton::clicked, this, &RulesTabWidget::OnRunRuleButtonPressed);
// Add Comparison Step button
connect(m_ui->addComparisonStepButton, &QPushButton::clicked, this, &RulesTabWidget::AddNewComparisonStep);
SetModelDataSource();
}
bool RulesTabWidget::HasUnsavedChanges()
{
return m_fileTableModel->HasUnsavedChanges();
}
void RulesTabWidget::Reload()
{
m_fileTableModel->Reload(AzToolsFramework::AssetFileInfoListComparison::GetComparisonRulesFileExtension(), m_watchedFolders, m_watchedFiles);
FileSelectionChanged();
}
bool RulesTabWidget::SaveCurrentSelection()
{
return m_fileTableModel->Save(m_selectedFileTableIndex);
}
bool RulesTabWidget::SaveAll()
{
return m_fileTableModel->SaveAll();
}
void RulesTabWidget::SetModelDataSource()
{
// Remove the current watched folders and files
m_guiApplicationManager->RemoveWatchedPaths(m_watchedFolders + m_watchedFiles);
// Set the new watched folder for the model
m_watchedFolders.clear();
m_watchedFiles.clear();
m_watchedFolders.insert(m_guiApplicationManager->GetRulesFolder().c_str());
ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType::RulesFileType);
m_guiApplicationManager->AddWatchedPaths(m_watchedFolders + m_watchedFiles);
}
AzQtComponents::TableView* RulesTabWidget::GetFileTableView()
{
return m_ui->fileTableView;
}
QModelIndex RulesTabWidget::GetSelectedFileTableIndex()
{
return m_selectedFileTableIndex;
}
AssetBundlerAbstractFileTableModel* RulesTabWidget::GetFileTableModel()
{
return m_fileTableModel.get();
}
void RulesTabWidget::SetActiveProjectLabel(const QString& labelText)
{
m_ui->activeProjectLabel->setText(labelText);
}
void RulesTabWidget::ApplyConfig()
{
const GUIApplicationManager::Config& config = m_guiApplicationManager->GetConfig();
m_ui->fileTableFrame->setFixedWidth(config.fileTableWidth);
m_ui->fileTableView->header()->resizeSection(RulesFileTableModel::Column::ColumnFileName, config.fileNameColumnWidth);
}
void RulesTabWidget::FileSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
{
if (m_ui->fileTableView->selectionModel()->selectedRows().size() == 0)
{
m_selectedFileTableIndex = QModelIndex();
m_selectedComparisonRules = nullptr;
m_ui->runRuleButton->setEnabled(false);
m_ui->addComparisonStepButton->setEnabled(false);
m_ui->rulesFileAbsolutePathLabel->clear();
RemoveAllComparisonDataCards();
return;
}
m_ui->runRuleButton->setEnabled(true);
m_ui->addComparisonStepButton->setEnabled(true);
m_selectedFileTableIndex = m_fileTableFilterModel->mapToSource(m_ui->fileTableView->selectionModel()->selectedRows()[0]);
m_selectedComparisonRules = m_fileTableModel->GetComparisonSteps(m_selectedFileTableIndex);
m_ui->rulesFileAbsolutePathLabel->setText(m_fileTableModel->GetFileAbsolutePath(m_selectedFileTableIndex).c_str());
RebuildComparisonDataCardList();
}
void RulesTabWidget::OnNewFileButtonPressed()
{
AZStd::string absoluteFilePath = NewFileDialog::OSNewFileDialog(
this,
AzToolsFramework::AssetFileInfoListComparison::GetComparisonRulesFileExtension(),
"Comparison Rules",
m_guiApplicationManager->GetRulesFolder());
if (absoluteFilePath.empty())
{
// User canceled out of the dialog
return;
}
AZStd::vector<AZStd::string> createdFile = m_fileTableModel->CreateNewFiles(absoluteFilePath);
if (!createdFile.empty())
{
AddScanPathToAssetBundlerSettings(AssetBundlingFileType::RulesFileType, createdFile.at(0));
}
}
void RulesTabWidget::OnRunRuleButtonPressed()
{
using namespace AzToolsFramework;
// Determine which platforms all of the input Asset List files have in common
AzFramework::PlatformFlags commonPlatforms = AzFramework::PlatformFlags::AllNamedPlatforms;
for (const AssetFileInfoListComparison::ComparisonData& comparisonStep : m_selectedComparisonRules->GetComparisonList())
{
if (!comparisonStep.m_cachedFirstInputPath.empty())
{
commonPlatforms = commonPlatforms & GetPlatformsOnDiskForPlatformSpecificFile(comparisonStep.m_cachedFirstInputPath);
}
if (!comparisonStep.m_cachedSecondInputPath.empty())
{
commonPlatforms = commonPlatforms & GetPlatformsOnDiskForPlatformSpecificFile(comparisonStep.m_cachedSecondInputPath);
}
}
if (commonPlatforms == AzFramework::PlatformFlags::Platform_NONE)
{
AZ_Error("AssetBundler", false, "Unable to run Rule: Input Asset List files have no platforms in common.");
return;
}
// Prompt the user to select an output path and the platforms to run the rule on
NewFileDialog runRuleDialog = NewFileDialog(
this,
"Run Rule",
QString(m_guiApplicationManager->GetAssetListsFolder().c_str()),
AzToolsFramework::AssetSeedManager::GetAssetListFileExtension(),
QString(tr("Asset List (*.%1)")).arg(AzToolsFramework::AssetSeedManager::GetAssetListFileExtension()),
commonPlatforms,
true);
auto dialogResponse = runRuleDialog.exec();
if (dialogResponse == QDialog::DialogCode::Rejected)
{
// User canceled the operation
return;
}
AZStd::vector<AZStd::string> outputFilePaths;
bool hasFileGenerationErrors = false;
AZStd::fixed_vector<AZStd::string, AzFramework::NumPlatforms> selectedPlatformNames = AzFramework::PlatformHelper::GetPlatforms(runRuleDialog.GetPlatformFlags());
for (const AZStd::string& platformName : selectedPlatformNames)
{
// We do not want to modify the original Rules file, as we do not save Asset List file paths to disk
AssetFileInfoListComparison currentFileCopy = *m_selectedComparisonRules.get();
//Update the first and second input values with any non-token Asset List file paths that have been set,
size_t numComparisonSteps = currentFileCopy.GetNumComparisonSteps();
for (size_t comparisonStepIndex = 0; comparisonStepIndex < numComparisonSteps; ++comparisonStepIndex)
{
AssetFileInfoListComparison::ComparisonData comparisonStep = currentFileCopy.GetComparisonList()[comparisonStepIndex];
if (comparisonStep.m_firstInput.empty())
{
if (comparisonStep.m_cachedFirstInputPath.empty())
{
AZ_Error("AssetBundler", false, "Unable to run Rule: Comparison Step #%u has no specified first input.", comparisonStepIndex);
return;
}
FilePath firstInput = FilePath(comparisonStep.m_cachedFirstInputPath, platformName);
currentFileCopy.SetFirstInput(comparisonStepIndex, firstInput.AbsolutePath());
}
if (comparisonStep.m_comparisonType != AssetFileInfoListComparison::ComparisonType::FilePattern
&& comparisonStep.m_secondInput.empty())
{
if (comparisonStep.m_cachedSecondInputPath.empty())
{
AZ_Error("AssetBundler", false, "Unable to run Rule: Comparison Step #%u has no specified second input.", comparisonStepIndex);
return;
}
FilePath secondInput = FilePath(comparisonStep.m_cachedSecondInputPath, platformName);
currentFileCopy.SetSecondInput(comparisonStepIndex, secondInput.AbsolutePath());
}
}
// Set the output location of the Asset List file that will be generated
FilePath finalOutputPath = FilePath(runRuleDialog.GetAbsoluteFilePath(), platformName);
currentFileCopy.SetOutput(numComparisonSteps - 1, finalOutputPath.AbsolutePath());
//Run the Rule
auto compareOutcome = currentFileCopy.CompareAndSaveResults();
if (compareOutcome.IsSuccess())
{
outputFilePaths.emplace_back(finalOutputPath.AbsolutePath());
}
else
{
hasFileGenerationErrors = true;
AZ_Error("AssetBundler", false, compareOutcome.GetError().c_str());
}
}
// Add created files to the file watcher
for (const AZStd::string& absolutePath : outputFilePaths)
{
AddScanPathToAssetBundlerSettings(AssetBundlingFileType::AssetListFileType, absolutePath);
}
// The watched files list was updated after the files were created, so we need to force-reload them
m_guiApplicationManager->UpdateFiles(AssetBundlingFileType::AssetListFileType, outputFilePaths);
NewFileDialog::FileGenerationResultMessageBox(this, outputFilePaths, hasFileGenerationErrors);
}
void RulesTabWidget::MarkFileChanged()
{
m_fileTableModel->MarkFileChanged(m_selectedFileTableIndex);
}
void RulesTabWidget::RebuildComparisonDataCardList()
{
RemoveAllComparisonDataCards();
PopulateComparisonDataCardList();
}
void RulesTabWidget::PopulateComparisonDataCardList()
{
if (!m_selectedComparisonRules)
{
return;
}
for (int index = 0; index < m_selectedComparisonRules->GetComparisonList().size(); ++index)
{
CreateComparisonDataCard(m_selectedComparisonRules, static_cast<size_t>(index));
}
}
void RulesTabWidget::CreateComparisonDataCard(AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> comparisonList, size_t comparisonDataIndex)
{
ComparisonDataCard* comparisonDataCard = new ComparisonDataCard(comparisonList, comparisonDataIndex, m_guiApplicationManager->GetAssetListsFolder());
comparisonDataCard->setTitle(tr("Step %1").arg(static_cast<int>(comparisonDataIndex) + 1));
m_ui->comparisonDataListLayout->addWidget(comparisonDataCard);
m_comparisonDataCardList.push_back(comparisonDataCard);
ComparisonDataWidget* comparisonDataWidget = comparisonDataCard->GetComparisonDataWidget();
connect(comparisonDataCard, &ComparisonDataCard::comparisonDataCardContextMenuRequested, this, &RulesTabWidget::OnComparisonDataCardContextMenuRequested);
connect(comparisonDataWidget, &ComparisonDataWidget::comparisonDataChanged, this, &RulesTabWidget::MarkFileChanged);
connect(comparisonDataWidget, &ComparisonDataWidget::comparisonDataTokenNameChanged, this, &RulesTabWidget::OnAnyTokenNameChanged);
comparisonDataCard->show();
}
void RulesTabWidget::RemoveAllComparisonDataCards()
{
m_comparisonDataCardList.clear();
while (!m_ui->comparisonDataListLayout->isEmpty())
{
QLayoutItem* item = m_ui->comparisonDataListLayout->takeAt(0);
item->widget()->hide();
delete item;
}
}
void RulesTabWidget::AddNewComparisonStep()
{
if (!m_selectedComparisonRules)
{
return;
}
if (!m_selectedComparisonRules->AddComparisonStep(AzToolsFramework::AssetFileInfoListComparison::ComparisonData()))
{
return;
}
CreateComparisonDataCard(m_selectedComparisonRules, m_selectedComparisonRules->GetComparisonList().size() - 1);
MarkFileChanged();
}
void RulesTabWidget::RemoveComparisonStep(size_t comparisonDataIndex)
{
if (!m_selectedComparisonRules)
{
return;
}
if (m_selectedComparisonRules->RemoveComparisonStep(comparisonDataIndex))
{
MarkFileChanged();
RebuildComparisonDataCardList();
}
}
void RulesTabWidget::MoveComparisonStep(size_t startingIndex, size_t destinationIndex)
{
if (!m_selectedComparisonRules)
{
return;
}
if (m_selectedComparisonRules->MoveComparisonStep(startingIndex, destinationIndex))
{
MarkFileChanged();
RebuildComparisonDataCardList();
}
}
void RulesTabWidget::OnAnyTokenNameChanged(size_t comparisonDataIndex)
{
if (comparisonDataIndex >= m_comparisonDataCardList.size() - 1)
{
return;
}
for (size_t i = comparisonDataIndex + 1; i < m_comparisonDataCardList.size(); ++i)
{
m_comparisonDataCardList[i]->GetComparisonDataWidget()->UpdateListOfTokenNames();
}
}
void RulesTabWidget::OnComparisonDataCardContextMenuRequested(size_t comparisonDataIndex, const QPoint& position)
{
if (!m_selectedComparisonRules)
{
return;
}
QMenu menu;
QAction* moveUpAction = new QAction(tr("Move Up"), this);
moveUpAction->setEnabled(comparisonDataIndex > 0);
connect(moveUpAction, &QAction::triggered, this, [=]() { MoveComparisonStep(comparisonDataIndex, comparisonDataIndex - 1); });
menu.addAction(moveUpAction);
QAction* moveDownAction = new QAction(tr("Move Down"), this);
moveDownAction->setEnabled(comparisonDataIndex < m_selectedComparisonRules->GetNumComparisonSteps() - 1);
connect(moveDownAction, &QAction::triggered, this, [=]() { MoveComparisonStep(comparisonDataIndex, comparisonDataIndex + 2); });
menu.addAction(moveDownAction);
QAction* separator = new QAction(this);
separator->setSeparator(true);
menu.addAction(separator);
QAction* deleteAction = new QAction(tr("Remove Comparison Step"), this);
connect(deleteAction, &QAction::triggered, this, [=]() { RemoveComparisonStep(comparisonDataIndex); });
menu.addAction(deleteAction);
menu.exec(position);
}
} // namespace AssetBundler
#include <source/ui/moc_RulesTabWidget.cpp>
@@ -0,0 +1,120 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <source/ui/AssetBundlerTabWidget.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <QAction>
#include <QModelIndex>
#include <QSharedPointer>
#include <QString>
#include <QWidget>
#endif
namespace Ui
{
class RulesTabWidget;
}
class QItemSelection;
namespace AzToolsFramework
{
class AssetFileInfoListComparison;
}
namespace AssetBundler
{
class GUIApplicationManager;
class RulesFileTableModel;
class ComparisonDataCard;
class ComparisonDataWidget;
class RulesTabWidget
: public AssetBundlerTabWidget
{
Q_OBJECT
public:
explicit RulesTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager);
virtual ~RulesTabWidget() {}
QString GetTabTitle() override { return tr("Rules"); }
QString GetFileTypeDisplayName() override { return tr("Rules file"); }
AssetBundlingFileType GetFileType() override { return AssetBundlingFileType::RulesFileType; }
bool HasUnsavedChanges() override;
void Reload() override;
bool SaveCurrentSelection() override;
bool SaveAll() override;
void SetModelDataSource() override;
AzQtComponents::TableView* GetFileTableView() override;
QModelIndex GetSelectedFileTableIndex() override;
AssetBundlerAbstractFileTableModel* GetFileTableModel() override;
void SetActiveProjectLabel(const QString& labelText) override;
void ApplyConfig() override;
void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override;
private:
void OnNewFileButtonPressed();
void OnRunRuleButtonPressed();
void MarkFileChanged();
void RebuildComparisonDataCardList();
void PopulateComparisonDataCardList();
void CreateComparisonDataCard(AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> comparisonList, size_t comparisonDataIndex);
void RemoveAllComparisonDataCards();
void AddNewComparisonStep();
void RemoveComparisonStep(size_t comparisonDataIndex);
void MoveComparisonStep(size_t startingIndex, size_t destinationIndex);
void OnAnyTokenNameChanged(size_t comparisonDataIndex);
QSharedPointer<Ui::RulesTabWidget> m_ui;
QSharedPointer<RulesFileTableModel> m_fileTableModel;
QModelIndex m_selectedFileTableIndex;
AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> m_selectedComparisonRules;
AZStd::vector<ComparisonDataCard*> m_comparisonDataCardList;
private slots:
void OnComparisonDataCardContextMenuRequested(size_t comparisonDataIndex, const QPoint& position);
};
} // namespace AssetBundler
@@ -0,0 +1,312 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RulesTabWidget</class>
<widget class="QWidget" name="RulesTabWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1180</width>
<height>728</height>
</rect>
</property>
<property name="windowTitle">
<string>Rules Tab Widget</string>
</property>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<item>
<layout class="QHBoxLayout" name="projectLabelHorizontalLayout">
<item>
<widget class="QLabel" name="activeProjectLabel"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="runRuleButton">
<property name="text">
<string>Run Selected Rule</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0">
<item>
<widget class="QFrame" name="fileTableFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="fileTableHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="rulesFilesLabel">
<property name="text">
<string>Rules</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="fileFilteredSearchWidget">
<property name="placeholderText" stdset="0">
<string>Filter by file name</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="createNewFileButton">
<property name="text">
<string>+ Create new Rules file</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="AzQtComponents::TableView" name="fileTableView"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="fileContentsFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="fileContentsHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="comparisonRulesFileNameLabel">
<property name="text">
<string>Comparison Steps</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rulesFileAbsolutePathLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="rightMargin">
<number>5</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="addComparisonStepButton">
<property name="text">
<string>+ Add Step</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="comparisonDataList">
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<widget class="QWidget" name="comparisonDataListContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>573</width>
<height>614</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>12</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="comparisonDataListLayout"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::TableView</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/Widgets/TableView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,453 @@
/*
* 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.
*
*/
#include <source/ui/SeedTabWidget.h>
#include <source/ui/ui_SeedTabWidget.h>
#include <source/utils/GUIApplicationManager.h>
#include <source/ui/AddSeedDialog.h>
#include <source/ui/EditSeedDialog.h>
#include <source/ui/NewFileDialog.h>
#include <source/models/SeedListFileTableModel.h>
#include <source/models/SeedListTableModel.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzQtComponents/Components/FilteredSearchWidget.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalog.h>
#include <QCheckBox>
#include <QFileDialog>
#include <QItemSelection>
#include <QItemSelectionRange>
#include <QMessageBox>
#include <QPushButton>
const char GenerateAssetListFilesDialogName[] = "Generate Asset List Files";
const int CheckBoxTableIndentationSize = 2; // when the indentation is 0, the checkboxes are too close to the edge
namespace AssetBundler
{
SeedTabWidget::SeedTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager, const QString& assetBundlingDirectory)
: AssetBundlerTabWidget(parent, guiApplicationManager)
, m_fileTableModel(new SeedListFileTableModel(this))
, m_seedListContentsModel(new SeedListTableModel())
{
AZ_UNUSED(assetBundlingDirectory);
m_ui.reset(new Ui::SeedTabWidget);
m_ui->setupUi(this);
m_ui->mainVerticalLayout->setContentsMargins(MarginSize, MarginSize, MarginSize, MarginSize);
AZ::Debug::TraceMessageBus::Handler::BusConnect();
// File view of all Seed List Files
m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex()));
m_fileTableFilterModel->setSourceModel(m_fileTableModel.data());
m_ui->fileTableView->setModel(m_fileTableFilterModel.data());
connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
m_fileTableFilterModel.data(), static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
connect(m_ui->fileTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SeedTabWidget::FileSelectionChanged);
m_ui->fileTableView->setIndentation(CheckBoxTableIndentationSize);
// New File Button
connect(m_ui->createNewSeedListButton, &QPushButton::clicked, this, &SeedTabWidget::OnNewFileButtonPressed);
// Select Default Seed Lists
connect(m_ui->selectDefaultSeedListsCheckBox, &QCheckBox::clicked, this, &SeedTabWidget::OnSelectDefaultSeedListsCheckBoxChanged);
// Generate Asset Lists Button
SetGenerateAssetListsButtonEnabled(false);
connect(m_ui->generateAssetListsButton, &QPushButton::clicked, this, &SeedTabWidget::OnGenerateAssetListsButtonPressed);
// Table that displays the contents of a Seed List File
m_seedListContentsFilterModel.reset(new AssetBundlerFileTableFilterModel(this, SeedListTableModel::Column::ColumnRelativePath));
m_seedListContentsFilterModel->setSourceModel(m_seedListContentsModel.data());
m_ui->seedFileContentsTable->setModel(m_seedListContentsFilterModel.data());
connect(m_ui->seedListContentsFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
m_seedListContentsFilterModel.data(), static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
m_ui->seedFileContentsTable->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui->seedFileContentsTable, &QTreeView::customContextMenuRequested, this, &SeedTabWidget::OnSeedListContentsTableContextMenuRequested);
m_ui->seedFileContentsTable->setIndentation(0);
// Edit All Platforms
connect(m_ui->editAllSeedsButton, &QPushButton::clicked, this, &SeedTabWidget::OnEditAllButtonPressed);
// Add Seed
connect(m_ui->addSeedButton, &QPushButton::clicked, this, &SeedTabWidget::OnAddSeedButtonPressed);
SetModelDataSource();
}
SeedTabWidget::~SeedTabWidget()
{
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
}
bool SeedTabWidget::HasUnsavedChanges()
{
return m_fileTableModel->HasUnsavedChanges();
}
void SeedTabWidget::Reload()
{
// Reload all the seed list files
m_fileTableModel->Reload(AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), m_watchedFolders, m_watchedFiles, m_filePathToGemNameMap);
// Update the selected row
FileSelectionChanged();
}
bool SeedTabWidget::SaveCurrentSelection()
{
return m_fileTableModel->Save(m_selectedFileTableIndex);
}
bool SeedTabWidget::SaveAll()
{
return m_fileTableModel->SaveAll();
}
void SeedTabWidget::SetModelDataSource()
{
// Remove the current watched folders and files
m_guiApplicationManager->RemoveWatchedPaths(m_watchedFolders + m_watchedFiles);
// Set the new watched folders for the model
m_watchedFolders.clear();
m_watchedFolders.insert(m_guiApplicationManager->GetSeedListsFolder().c_str());
// Get the list of default Seed List files
m_filePathToGemNameMap = AssetBundler::GetDefaultSeedListFiles(GetCachedEngineRoot().c_str(), m_guiApplicationManager->GetCurrentProjectName(),
m_guiApplicationManager->GetGemInfoList(), m_guiApplicationManager->GetEnabledPlatforms());
// Get the list of default Seeds that are not stored in a Seed List file on-disk
AZStd::vector<AZStd::string> defaultSeeds = GetDefaultSeeds(AZ::Utils::GetProjectPath(), m_guiApplicationManager->GetCurrentProjectName());
m_fileTableModel->AddDefaultSeedsToInMemoryList(defaultSeeds, m_guiApplicationManager->GetCurrentProjectName().c_str(), m_guiApplicationManager->GetEnabledPlatforms());
// Set the new watched filess for the model
m_watchedFiles.clear();
for (const auto& it : m_filePathToGemNameMap)
{
m_watchedFiles.insert(it.first.c_str());
}
ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType::SeedListFileType);
m_guiApplicationManager->AddWatchedPaths(m_watchedFolders + m_watchedFiles);
}
AzQtComponents::TableView* SeedTabWidget::GetFileTableView()
{
return m_ui->fileTableView;
}
QModelIndex SeedTabWidget::GetSelectedFileTableIndex()
{
return m_selectedFileTableIndex;
}
AssetBundlerAbstractFileTableModel* SeedTabWidget::GetFileTableModel()
{
return m_fileTableModel.get();
}
void SeedTabWidget::SetActiveProjectLabel(const QString& labelText)
{
m_ui->activeProjectLabel->setText(labelText);
}
void SeedTabWidget::ApplyConfig()
{
const GUIApplicationManager::Config& config = m_guiApplicationManager->GetConfig();
m_ui->fileTableFrame->setFixedWidth(config.fileTableWidth);
m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnFileName, config.seedListFileNameColumnWidth);
m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnCheckBox, config.checkBoxColumnWidth);
m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnProject, config.projectNameColumnWidth);
m_ui->seedFileContentsTable->header()->resizeSection(SeedListTableModel::Column::ColumnRelativePath, config.seedListContentsNameColumnWidth);
}
void SeedTabWidget::UncheckSelectDefaultSeedListsCheckBox()
{
m_ui->selectDefaultSeedListsCheckBox->setChecked(false);
}
void SeedTabWidget::SetGenerateAssetListsButtonEnabled(bool isEnabled)
{
m_ui->generateAssetListsButton->setEnabled(isEnabled);
}
bool SeedTabWidget::OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/)
{
m_hasWarnings = true;
return false;
}
bool SeedTabWidget::OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/)
{
m_hasWarnings = true;
return false;
}
void SeedTabWidget::FileSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
{
if (m_ui->fileTableView->selectionModel()->selectedRows().size() == 0)
{
// Set selected index to an invalid value
m_selectedFileTableIndex = QModelIndex();
m_ui->seedListFileAbsolutePathLabel->clear();
return;
}
m_selectedFileTableIndex = m_fileTableFilterModel->mapToSource(m_ui->fileTableView->selectionModel()->currentIndex());
m_seedListContentsModel = m_fileTableModel->GetSeedListFileContents(m_selectedFileTableIndex);
m_seedListContentsFilterModel->setSourceModel(m_seedListContentsModel.data());
m_ui->seedListFileAbsolutePathLabel->setText(m_fileTableModel->GetFileAbsolutePath(m_selectedFileTableIndex).c_str());
}
void SeedTabWidget::OnNewFileButtonPressed()
{
AZStd::string absoluteFilePath = NewFileDialog::OSNewFileDialog(
this,
AzToolsFramework::AssetSeedManager::GetSeedFileExtension(),
"Seed List",
m_guiApplicationManager->GetSeedListsFolder());
if (absoluteFilePath.empty())
{
// User canceled out of the file dialog
return;
}
AZStd::vector<AZStd::string> createdFiles = m_fileTableModel->CreateNewFiles(
absoluteFilePath,
AzFramework::PlatformFlags::Platform_NONE,
QString(m_guiApplicationManager->GetCurrentProjectName().c_str()));
if (!createdFiles.empty())
{
AddScanPathToAssetBundlerSettings(AssetBundlingFileType::SeedListFileType, createdFiles.at(0));
}
}
void SeedTabWidget::OnSelectDefaultSeedListsCheckBoxChanged()
{
m_fileTableModel->SelectDefaultSeedLists(m_ui->selectDefaultSeedListsCheckBox->isChecked());
}
void SeedTabWidget::OnGenerateAssetListsButtonPressed()
{
m_generateAssetListsDialog.reset(
new NewFileDialog(
this,
GenerateAssetListFilesDialogName,
QString(m_guiApplicationManager->GetAssetListsFolder().c_str()),
AzToolsFramework::AssetSeedManager::GetAssetListFileExtension(),
QString(tr("Asset List (*.%1)")).arg(AzToolsFramework::AssetSeedManager::GetAssetListFileExtension()),
m_guiApplicationManager->GetEnabledPlatforms()));
auto dialogResponse = m_generateAssetListsDialog->exec();
if (dialogResponse == QDialog::DialogCode::Rejected)
{
// User canceled the operation
return;
}
m_hasWarnings = false;
auto createdFiles = m_fileTableModel->GenerateAssetLists(m_generateAssetListsDialog->GetAbsoluteFilePath(), m_generateAssetListsDialog->GetPlatformFlags());
// Warnings will not prevent the generation of Asset List files, we must track them separately
NewFileDialog::FileGenerationResultMessageBox(this, createdFiles, m_hasWarnings);
if (createdFiles.empty())
{
// Error has already been thrown
return;
}
// Add created files to the file watcher
for (const AZStd::string& absolutePath : createdFiles)
{
AddScanPathToAssetBundlerSettings(AssetBundlingFileType::AssetListFileType, absolutePath);
}
// The watched files list was updated after the files were created, so we need to force-reload them
m_guiApplicationManager->UpdateFiles(AssetBundlingFileType::AssetListFileType, createdFiles);
}
void SeedTabWidget::OnEditSeedButtonPressed()
{
if (!m_selectedFileTableIndex.isValid())
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Edit Seed operation: No Seed List File is selected");
return;
}
// Get the current platforms of the selected Seed so we can display them as already checked
QModelIndex currentSeedIndex = m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex());
auto getPlatformOutcome = m_seedListContentsModel->GetSeedPlatforms(currentSeedIndex);
if (!getPlatformOutcome.IsSuccess())
{
// Error has already been thrown
return;
}
// Create and display the Edit Seed dialog
m_editSeedDialog.reset(new EditSeedDialog(this, m_guiApplicationManager->GetEnabledPlatforms(), getPlatformOutcome.GetValue()));
auto dialogResponse = m_editSeedDialog->exec();
if (dialogResponse == QDialog::DialogCode::Rejected)
{
// User canceled the operation
return;
}
// Set the data in the model
m_fileTableModel->SetSeedPlatforms(m_selectedFileTableIndex, currentSeedIndex, m_editSeedDialog->GetPlatformFlags());
}
void SeedTabWidget::OnEditAllButtonPressed()
{
if (!m_selectedFileTableIndex.isValid())
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Edit All operation: No Seed List File is selected");
return;
}
// Get the platforms of all the seeds so we can display them as already checked or partially checked
AzFramework::PlatformFlags selectedPlatformFlags = AzFramework::PlatformFlags::AllNamedPlatforms;
AzFramework::PlatformFlags partiallySelectedPlatformFlags = AzFramework::PlatformFlags::Platform_NONE;
QMap<QModelIndex, AzFramework::PlatformFlags> indexToPlatformFlagsMap;
for (int row = 0; row < m_seedListContentsModel->rowCount(); ++row)
{
QModelIndex currentSeedIndex = m_seedListContentsModel->index(row, 0);
auto getPlatformOutcome = m_seedListContentsModel->GetSeedPlatforms(currentSeedIndex);
if (!getPlatformOutcome.IsSuccess())
{
// Error has already been thrown
return;
}
indexToPlatformFlagsMap[currentSeedIndex] = getPlatformOutcome.TakeValue();
selectedPlatformFlags &= indexToPlatformFlagsMap[currentSeedIndex];
partiallySelectedPlatformFlags |= selectedPlatformFlags ^ indexToPlatformFlagsMap[currentSeedIndex];
}
// Create and display the Edit Seed dialog
m_editSeedDialog.reset(new EditSeedDialog(
this,
m_guiApplicationManager->GetEnabledPlatforms(),
selectedPlatformFlags,
partiallySelectedPlatformFlags));
auto dialogResponse = m_editSeedDialog->exec();
if (dialogResponse == QDialog::DialogCode::Rejected)
{
// User canceled the operation
return;
}
// Set the data in the model
for (int row = 0; row < m_seedListContentsModel->rowCount(); ++row)
{
QModelIndex currentSeedIndex = m_seedListContentsModel->index(row, 0);
AzFramework::PlatformFlags checkedPlatforms = m_editSeedDialog->GetPlatformFlags();
AzFramework::PlatformFlags partiallyCheckedPlatforms = m_editSeedDialog->GetPartiallySelectedPlatformFlags();
// If the platform is partially checked, we want to keep its original status when saving the changes
AzFramework::PlatformFlags platformFlags = indexToPlatformFlagsMap[currentSeedIndex] & partiallyCheckedPlatforms | checkedPlatforms;
m_fileTableModel->SetSeedPlatforms(m_selectedFileTableIndex, currentSeedIndex, platformFlags);
}
}
void SeedTabWidget::OnAddSeedButtonPressed()
{
if (!m_selectedFileTableIndex.isValid())
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Add Seed operation: No Seed List File is selected");
return;
}
// Get path to the platform-specific cache folder of one of the enabled platforms
AzFramework::PlatformFlags enabledPlatforms = m_guiApplicationManager->GetEnabledPlatforms();
AZStd::fixed_vector<AzFramework::PlatformId, AzFramework::PlatformId::NumPlatformIds> enabledPlatformIndices = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(enabledPlatforms);
AZStd::string platformSpecificCachePath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(enabledPlatformIndices[0]);
// Create and display the Add Seed Dialog
m_addSeedDialog.reset(new AddSeedDialog(this, enabledPlatforms, platformSpecificCachePath));
auto dialogResponse = m_addSeedDialog->exec();
if (dialogResponse == QDialog::DialogCode::Rejected)
{
// User canceled the operation
return;
}
// Set the data in the model
m_fileTableModel->AddSeed(m_selectedFileTableIndex, m_addSeedDialog->GetFileName(), m_addSeedDialog->GetPlatformFlags());
}
void SeedTabWidget::OnRemoveSeedButtonPressed()
{
if (!m_selectedFileTableIndex.isValid())
{
AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Remove Seed operation: No Seed List File is selected");
return;
}
// Set the data in the model
QModelIndex currentSeedIndex = m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex());
m_fileTableModel->RemoveSeed(m_selectedFileTableIndex, currentSeedIndex);
}
void SeedTabWidget::OnSeedListContentsTableContextMenuRequested(const QPoint& pos)
{
if (!m_selectedFileTableIndex.isValid())
{
return;
}
QMenu* contextMenu = new QMenu(this);
contextMenu->setToolTipsVisible(true);
QAction* action = contextMenu->addAction(tr("Edit Platforms"));
connect(action, &QAction::triggered, this, &SeedTabWidget::OnEditSeedButtonPressed);
action->setToolTip(tr("Change what platforms are referenced when generating an Asset List file."));
contextMenu->addSeparator();
action = contextMenu->addAction(tr("Add Seed"));
connect(action, &QAction::triggered, this, &SeedTabWidget::OnAddSeedButtonPressed);
action->setToolTip(tr("Add a new Seed to the Seed List file."));
action = contextMenu->addAction(tr("Remove Seed"));
connect(action, &QAction::triggered, this, &SeedTabWidget::OnRemoveSeedButtonPressed);
action->setToolTip(tr("Removes the Seed from the Seed List file."));
contextMenu->exec(m_ui->seedFileContentsTable->mapToGlobal(pos));
delete contextMenu;
}
} //namespace AssetBundler
#include <source/ui/moc_SeedTabWidget.cpp>
@@ -0,0 +1,129 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <source/ui/AssetBundlerTabWidget.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QDir>
#include <QItemSelectionModel>
#include <QModelIndex>
#include <QSharedPointer>
#include <QString>
#include <QWidget>
#endif
namespace Ui
{
class SeedTabWidget;
}
class QFileSystemModel;
class QStringListModel;
namespace AssetBundler
{
class GUIApplicationManager;
class SeedListFileTableModel;
class SeedListTableModel;
class NewFileDialog;
class EditSeedDialog;
class AddSeedDialog;
class SeedTabWidget
: public AssetBundlerTabWidget
, public AZ::Debug::TraceMessageBus::Handler
{
Q_OBJECT
public:
explicit SeedTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager, const QString& assetBundlingDirectory);
virtual ~SeedTabWidget();
QString GetTabTitle() override { return tr("Seeds"); }
QString GetFileTypeDisplayName() override { return tr("Seed List file"); }
AssetBundlingFileType GetFileType() override { return AssetBundlingFileType::SeedListFileType; }
bool HasUnsavedChanges() override;
void Reload() override;
bool SaveCurrentSelection() override;
bool SaveAll() override;
void SetModelDataSource() override;
AzQtComponents::TableView* GetFileTableView() override;
QModelIndex GetSelectedFileTableIndex() override;
AssetBundlerAbstractFileTableModel* GetFileTableModel() override;
void SetActiveProjectLabel(const QString& labelText) override;
void ApplyConfig() override;
void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override;
void UncheckSelectDefaultSeedListsCheckBox();
void SetGenerateAssetListsButtonEnabled(bool isEnabled);
/////////////////////////////////////////////////////////
// TraceMessageBus overrides
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
/////////////////////////////////////////////////////////
private:
void OnNewFileButtonPressed();
void OnSelectDefaultSeedListsCheckBoxChanged();
void OnGenerateAssetListsButtonPressed();
void OnEditSeedButtonPressed();
void OnEditAllButtonPressed();
void OnAddSeedButtonPressed();
void OnRemoveSeedButtonPressed();
void OnSeedListContentsTableContextMenuRequested(const QPoint& pos);
QSharedPointer<Ui::SeedTabWidget> m_ui;
QDir m_assetBundlingFolder;
QSharedPointer<SeedListFileTableModel> m_fileTableModel;
QModelIndex m_selectedFileTableIndex;
QSharedPointer<NewFileDialog> m_generateAssetListsDialog;
QSharedPointer<AssetBundler::AssetBundlerFileTableFilterModel> m_seedListContentsFilterModel;
QSharedPointer<SeedListTableModel> m_seedListContentsModel;
QSharedPointer<EditSeedDialog> m_editSeedDialog;
QSharedPointer<AddSeedDialog> m_addSeedDialog;
bool m_hasWarnings = false;
};
} // namespace AssetBundler
@@ -0,0 +1,297 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SeedTabWidget</class>
<widget class="QWidget" name="SeedTabWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>813</width>
<height>673</height>
</rect>
</property>
<property name="windowTitle">
<string>Seed Tab Widget</string>
</property>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<item>
<layout class="QHBoxLayout" name="projectLabelHorizontalLayout">
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="activeProjectLabel"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="generateAssetListsButton">
<property name="text">
<string>Generate Asset Lists</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0">
<item>
<widget class="QFrame" name="fileTableFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="fileTableHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="seedListsLabel">
<property name="text">
<string>Seed List files</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="fileFilteredSearchWidget">
<property name="placeholderText" stdset="0">
<string>Filter by file name</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="leftMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="createNewSeedListButton">
<property name="text">
<string>+ Create new Seed List file</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="AzQtComponents::TableView" name="fileTableView"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="selectDefaultSeedListsCheckBox">
<property name="text">
<string>Default Seed Lists</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="fileContentsFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="fileContentsHeaderFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="seedListContentsLabel">
<property name="text">
<string>Product Assets</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedListFileAbsolutePathLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="seedListContentsFilteredSearchWidget">
<property name="placeholderText" stdset="0">
<string>Filter by file name</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="editAllSeedsButton">
<property name="text">
<string>Edit All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addSeedButton">
<property name="text">
<string>+ Add Asset</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="AzQtComponents::TableView" name="seedFileContentsTable"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::TableView</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/Widgets/TableView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QTreeView</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:690a47a0e2ff4c41ce23ad0bc5dd5bd525e03676a26d3367726beaa96babf207
size 138199
@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/AssetBundler/style">
<file>AssetBundler.qss</file>
<file>AssetBundlerConfig.ini</file>
<file>AssetBundler-Icon-256x256@x2.ico</file>
</qresource>
</RCC>
@@ -0,0 +1,173 @@
/*
* 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.
*
*/
/************** General (MainWindow) **************/
QTabWidget:pane {
border: none;
}
QLabel#activeProjectLabel {
font-size: 18px;
font-family: "Amazon Ember Light";
margin-bottom: 5px;
}
QTreeView#logTableView {
border: 1px solid #1B1B1B;
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
}
QTreeView#logTableView QHeaderView::section {
background-color: #1B1B1B;
}
/************** General (Tabs) **************/
QFrame#fileTableFrame,
QFrame#fileContentsFrame {
border: 1px solid #1B1B1B;
}
QFrame#fileTableFrame {
margin-right: 10px;
}
QFrame#fileTableHeaderFrame,
QFrame#fileContentsHeaderFrame,
QFrame#fileTableHeaderFrame QLabel,
QFrame#fileContentsHeaderFrame QLabel{
background-color: #1B1B1B;
}
QFrame#fileTableHeaderFrame QLabel,
QFrame#fileContentsHeaderFrame QLabel{
background-color: #1B1B1B;
margin-left: 5px;
}
QTabWidget QTreeView QHeaderView::section {
background-color: rgb(77, 77, 77);
}
QWidget#PlatformSelectionWidget QLabel#platformSelectionLabel {
margin-right: 10px;
}
QDialog#NewFileDialog QPushButton#browseFileButton,
QDialog#NewFileDialog QPushButton#createFileButton,
QDialog#NewFileDialog QPushButton#cancelButton {
margin-left: 10px;
margin-right: 10px;
}
/************** Seeds Tab **************/
QWidget#SeedTabWidget QPushButton#generateAssetListsButton,
QWidget#SeedTabWidget QPushButton#browseFileButton,
QWidget#SeedTabWidget QPushButton#cancelButton,
QWidget#SeedTabWidget QPushButton#addSeedButton,
QWidget#SeedTabWidget QPushButton#applyChangesButton {
margin-left: 10px;
margin-right: 10px;
}
QCheckBox#selectDefaultSeedListsCheckBox {
margin: 5px;
}
QWidget#seedListContentsFilteredSearchWidget {
margin-left: 5px;
}
QDialog QFrame#inputValuesFrame {
border: none;
margin: 10px;
padding: 10px;
}
QDialog QFrame#lineFrame {
border: none;
margin-top: 10px;
margin-bottom: 10px;
}
QDialog QFrame#inputValuesFrame,
QDialog QFrame#lineFrame,
QDialog#AddSeedDialog QLabel,
QDialog#AddSeedDialog QCheckBox,
QDialog#EditSeedDialog QLabel,
QDialog#EditSeedDialog QCheckBox,
QDialog#NewFileDialog QLabel,
QDialog#NewFileDialog QCheckBox {
background-color: #555555;
}
/************** Asset List Tab **************/
QLabel#assetListContentsLabel {
margin-right: 10px;
}
QWidget#fileFilteredSearchWidget,
QWidget#assetListContentsFilteredSearchWidget {
margin-left: 5px;
margin-right: 5px;
}
QPushButton#generateBundleButton {
margin-left: 10px;
margin-right: 10px;
}
/************** Generate Bundles Modal **************/
QFrame#bundleSettingsFrame {
border: none;
margin-bottom: 10px;
}
QFrame#outputBundleLocationFrame,
QFrame#bundleSettingsFileFrame {
border: none;
margin: 10px;
}
QFrame#bundleSettingsFrame,
QFrame#outputBundleLocationFrame,
QFrame#bundleSettingsFileFrame,
QFrame#bundleSettingsFrame QLabel {
background-color: #555555;
}
QWidget#GenerateBundlesModal QPushButton {
margin-left: 10px;
margin-right: 10px;
}
/************** Rules Tab **************/
QPushButton#runRuleButton,
QPushButton#addComparisonStepButton {
margin-left: 10px;
margin-right: 10px;
}
QScrollArea#comparisonDataList {
border: none;
}
QFrame#comparisonDataFrame {
padding: 10px;
}
@@ -0,0 +1,19 @@
[ErrorLogDetails]
LogTypeColumnWidth=180
LogSourceColumnWidth=130
[GeneralFileTableDetails]
FileTableWidth=450
FileNameColumnWidth=250
[SeedsTabDetails]
CheckBoxColumnWidth=22
SeedListFileNameColumnWidth=150
ProjectNameColumnWidth=120
SeedListContentsNameColumnWidth=400
[AssetListsTabDetails]
AssetListFileNameColumnWidth=210
AssetListPlatformColumnWidth=70
ProductAssetNameColumnWidth=220
ProductAssetRelativePathColumnWidth=300
@@ -0,0 +1,386 @@
/*
* 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.
*
*/
#include <source/utils/GUIApplicationManager.h>
#include <source/ui/MainWindow.h>
#include <source/utils/utils.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Asset/AssetCatalog.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogBus.h>
#include <AzQtComponents/Components/ConfigHelpers.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <QApplication>
#include <QLocale>
#include <QStringList>
// Forward declare platform-specific functions
namespace Platform
{
/// On Windows this will return the setting for our custom title bar
/// On other platforms this will return the setting for using platform default
/// This ensures that functions like Exit, Maximize, and Minimize appear in the right platform-specific style
AzQtComponents::WindowDecorationWrapper::Option GetWindowDecorationWrapperOption();
} // namespace Platform
const char AssetBundlingFolderName [] = "AssetBundling";
const char SeedListsFolderName [] = "SeedLists";
const char AssetListsFolderName [] = "AssetLists";
const char RulesFolderName[] = "Rules";
const char BundleSettingsFolderName [] = "BundleSettings";
const char BundlesFolderName [] = "Bundles";
namespace AssetBundler
{
GUIApplicationManager::Config GUIApplicationManager::loadConfig(QSettings& settings)
{
using namespace AzQtComponents;
Config config = defaultConfig();
// Error Log
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("ErrorLogDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("LogTypeColumnWidth"), config.logTypeColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("LogSourceColumnWidth"), config.logSourceColumnWidth);
}
// General File Tables
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("GeneralFileTableDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("FileTableWidth"), config.fileTableWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("FileNameColumnWidth"), config.fileNameColumnWidth);
}
// Seeds Tab
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("SeedsTabDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("CheckBoxColumnWidth"), config.checkBoxColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("SeedListFileNameColumnWidth"), config.seedListFileNameColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("ProjectNameColumnWidth"), config.projectNameColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("SeedListContentsNameColumnWidth"), config.seedListContentsNameColumnWidth);
}
// Asset Lists Tab
{
ConfigHelpers::GroupGuard details(&settings, QStringLiteral("AssetListsTabDetails"));
ConfigHelpers::read<int>(settings, QStringLiteral("AssetListFileNameColumnWidth"), config.assetListFileNameColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("AssetListPlatformColumnWidth"), config.assetListPlatformColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("ProductAssetNameColumnWidth"), config.productAssetNameColumnWidth);
ConfigHelpers::read<int>(settings, QStringLiteral("ProductAssetRelativePathColumnWidth"), config.productAssetRelativePathColumnWidth);
}
return config;
}
GUIApplicationManager::Config GUIApplicationManager::defaultConfig()
{
// These are used if the values can't be read from AssetBundlerConfig.ini.
Config config;
config.logTypeColumnWidth = 150;
config.logSourceColumnWidth = 150;
config.fileTableWidth = 250;
config.fileNameColumnWidth = 150;
config.checkBoxColumnWidth = 150;
config.seedListFileNameColumnWidth = 150;
config.projectNameColumnWidth = 150;
config.seedListContentsNameColumnWidth = 150;
config.assetListFileNameColumnWidth = 150;
config.assetListPlatformColumnWidth = 150;
config.productAssetNameColumnWidth = 150;
config.productAssetRelativePathColumnWidth = 150;
return config;
}
GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, QObject* parent)
: ApplicationManager(argc, argv, parent)
{
}
GUIApplicationManager::~GUIApplicationManager()
{
// Reset this before DestroyApplication, BusDisconnect needs to happen before Application::Stop() destroys the allocators.
m_platformCatalogManager.reset();
}
bool GUIApplicationManager::Init()
{
m_isInitializing = true;
// Initialize Asset Bundler Batch
ApplicationManager::Init();
AZ::IO::FixedMaxPath engineRoot = GetEngineRoot();
if (engineRoot.empty())
{
// Error has already been thrown
return false;
}
// Determine the name of the current project
auto projectOutcome = AssetBundler::GetCurrentProjectName();
if (!projectOutcome.IsSuccess())
{
AZ_Error("AssetBundler", false, projectOutcome.GetError().c_str());
return false;
}
m_currentProjectName = projectOutcome.GetValue();
// Set up paths to the Project folder, Project Cache folder, and determine enabled platforms
auto pathOutcome = InitializePaths();
if (!pathOutcome.IsSuccess())
{
AZ_Error("AssetBundler", false, pathOutcome.GetError().c_str());
return false;
}
// Set up platform-specific Asset Catalogs
m_platformCatalogManager = AZStd::make_unique<AzToolsFramework::PlatformAddressedAssetCatalogManager>();
// Define some application-level settings
QApplication::setOrganizationName("Amazon");
QApplication::setOrganizationDomain("amazon.com");
QApplication::setApplicationName("Asset Bundler");
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
m_isInitializing = false;
// Create the actual Qt Application
m_qApp.reset(new QApplication(*GetArgC(), *GetArgV()));
// Create the Main Window
m_mainWindow.reset(new MainWindow(this));
return true;
}
bool GUIApplicationManager::Run()
{
// Set up the Style Manager
AzQtComponents::StyleManager styleManager(qApp);
styleManager.initialize(qApp, GetEngineRoot());
AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
QDir engineRootDir(engineRoot.c_str());
AzQtComponents::StyleManager::addSearchPaths(
QStringLiteral("style"),
engineRootDir.filePath(QStringLiteral("Code/Tools/AssetBundler/source/ui/style")),
QStringLiteral(":/AssetBundler/style"),
engineRoot);
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:AssetBundler.qss"));
AzQtComponents::ConfigHelpers::loadConfig<Config, GUIApplicationManager>(&m_fileWatcher, &m_config, QStringLiteral("style:AssetBundlerConfig.ini"), this, std::bind(&GUIApplicationManager::ApplyConfig, this));
ApplyConfig();
qApp->setWindowIcon(QIcon("style:AssetBundler-Icon-256x256@x2.ico"));
// Set up the Main Window
auto wrapper = new AzQtComponents::WindowDecorationWrapper(Platform::GetWindowDecorationWrapperOption());
wrapper->setGuest(m_mainWindow.data());
m_mainWindow->Activate();
wrapper->show();
m_mainWindow->show();
qApp->setQuitOnLastWindowClosed(true);
// Run the application
return qApp->exec();
}
void GUIApplicationManager::AddWatchedPath(const QString& folderPath)
{
m_fileWatcher.addPath(folderPath);
}
void GUIApplicationManager::AddWatchedPaths(const QSet<QString>& folderPaths)
{
m_fileWatcher.addPaths(folderPaths.values());
}
void GUIApplicationManager::RemoveWatchedPath(const QString& path)
{
m_fileWatcher.removePath(path);
}
void GUIApplicationManager::RemoveWatchedPaths(const QSet<QString>& paths)
{
// Check whether the list is empty to get rid of the warning from Qt
if (paths.isEmpty())
{
return;
}
m_fileWatcher.removePaths(paths.values());
}
bool GUIApplicationManager::OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message)
{
// We want to display errors during initialization, then let the MainWindow handle errors during runtime
if (m_isInitializing)
{
// These are fatal initialization errors, and the application will shut down after the user closes the message box
m_qApp.reset(new QApplication(*GetArgC(), *GetArgV()));
QMessageBox errorMessageBox;
errorMessageBox.setWindowTitle("Asset Bundler");
errorMessageBox.setText(message);
errorMessageBox.setStandardButtons(QMessageBox::Ok);
errorMessageBox.setDefaultButton(QMessageBox::Ok);
errorMessageBox.exec();
return true;
}
return false;
}
bool GUIApplicationManager::OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/)
{
// Don't handle warnings, let the MainWindow print them
return false;
}
bool GUIApplicationManager::OnPrintf(const char* /*window*/, const char* /*message*/)
{
// This is disabled during initialization to prevent a lot of message spam printed to the CLI that gets generated on setup
return m_isInitializing;
}
AZ::Outcome<void, AZStd::string> GUIApplicationManager::InitializePaths()
{
// Calculate the path to the Cache for the current project
auto pathOutcome = AssetBundler::GetProjectCacheFolderPath();
if (!pathOutcome.IsSuccess())
{
return AZ::Failure(pathOutcome.GetError());
}
m_currentProjectCacheFolder = pathOutcome.GetValue().String();
// Calculate the path to the current project folder
pathOutcome = AssetBundler::GetProjectFolderPath();
if (!pathOutcome.IsSuccess())
{
return AZ::Failure(pathOutcome.GetError());
}
m_currentProjectFolder = pathOutcome.GetValue().String();
// Generate the AssetBundling folder inside the current project
AzFramework::StringFunc::Path::ConstructFull(m_currentProjectFolder.c_str(), AssetBundlingFolderName, m_assetBundlingFolder);
// Seed Lists folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), SeedListsFolderName, m_seedListsFolder);
AZ::Outcome<void, AZStd::string> createPathOutcome = AssetBundler::MakePath(m_seedListsFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Asset Lists folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), AssetListsFolderName, m_assetListsFolder);
createPathOutcome = AssetBundler::MakePath(m_assetListsFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Rules folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), RulesFolderName, m_rulesFolder);
createPathOutcome = AssetBundler::MakePath(m_rulesFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Bundle Settings Folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), BundleSettingsFolderName, m_bundleSettingsFolder);
createPathOutcome = AssetBundler::MakePath(m_bundleSettingsFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Bundles Folder
AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), BundlesFolderName, m_bundlesFolder);
createPathOutcome = AssetBundler::MakePath(m_bundlesFolder);
if (!createPathOutcome.IsSuccess())
{
return createPathOutcome;
}
// Determine the enabled platforms
const char* appRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
m_enabledPlatforms = GetEnabledPlatformFlags(GetEngineRoot(), appRoot, AZ::Utils::GetProjectPath().c_str());
// Determine which Gems are enabled for the current project
if (!AzFramework::GetGemsInfo(m_gemInfoList, *m_settingsRegistry))
{
return AZ::Failure(AZStd::string::format("Failed to read Gems for project: %s\n", m_currentProjectName.c_str()));
}
QObject::connect(&m_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &GUIApplicationManager::DirectoryChanged);
QObject::connect(&m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &GUIApplicationManager::FileChanged);
return AZ::Success();
}
void GUIApplicationManager::DirectoryChanged(const QString& directory)
{
UpdateTab(directory.toUtf8().data());
}
void GUIApplicationManager::FileChanged(const QString& path)
{
// FileChanged will only be called when engine or gem seed files are updated
// Otherwilse DirectoryChanged should be triggered
AZStd::string extension;
AzFramework::StringFunc::Path::GetExtension(path.toUtf8().data(), extension);
extension = extension.starts_with(".") ? extension.substr(1) : extension;
if (extension == AzToolsFramework::AssetSeedManager::GetSeedFileExtension())
{
UpdateTab(GetSeedListsFolder());
}
// Many applications save an open file by writing a new file and then deleting the old one
// Add the file path back if it has been removed from the watcher file list
if (!m_fileWatcher.files().contains(path))
{
m_fileWatcher.addPath(path);
}
}
void GUIApplicationManager::ApplyConfig()
{
m_mainWindow->ApplyConfig();
}
} // namespace AssetBundler
#include <source/utils/moc_GUIApplicationManager.cpp>
@@ -0,0 +1,164 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <source/utils/applicationManager.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Asset/AssetCatalog.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <QCoreApplication>
#include <QDir>
#include <QMap>
#include <QSettings>
#include <QSharedPointer>
#include <QString>
#include <QFileSystemWatcher>
#endif
namespace AssetBundler
{
enum AssetBundlingFileType : int
{
SeedListFileType = 0,
AssetListFileType,
BundleSettingsFileType,
BundleFileType,
RulesFileType,
NumBundlingFileTypes
};
class MainWindow;
class GUIApplicationManager
: public ApplicationManager
{
Q_OBJECT
public:
struct Config
{
// These default values are used if the values can't be read from AssetBundlerConfig.ini,
// and the call to defaultConfig fails.
// Error Log
int logTypeColumnWidth = -1;
int logSourceColumnWidth = -1;
// General File Tables
int fileTableWidth = -1;
int fileNameColumnWidth = -1;
// Seeds Tab
int checkBoxColumnWidth = -1;
int seedListFileNameColumnWidth = -1;
int projectNameColumnWidth = -1;
int seedListContentsNameColumnWidth = -1;
// Asset Lists Tab
int assetListFileNameColumnWidth = -1;
int assetListPlatformColumnWidth = -1;
int productAssetNameColumnWidth = -1;
int productAssetRelativePathColumnWidth = -1;
};
/*!
* Loads the button config data from a settings object.
*/
static Config loadConfig(QSettings& settings);
/*!
* Returns default button config data.
*/
static Config defaultConfig();
explicit GUIApplicationManager(int* argc, char*** argv, QObject* parent = 0);
virtual ~GUIApplicationManager();
bool Init() override;
bool Run() override;
AZStd::string GetCurrentProjectFolder() { return m_currentProjectFolder; }
AZStd::string GetAssetBundlingFolder() { return m_assetBundlingFolder; }
AZStd::string GetSeedListsFolder() { return m_seedListsFolder; }
AZStd::string GetAssetListsFolder() { return m_assetListsFolder; }
AZStd::string GetRulesFolder() { return m_rulesFolder; }
AZStd::string GetBundleSettingsFolder() { return m_bundleSettingsFolder; }
AZStd::string GetBundlesFolder() { return m_bundlesFolder; }
AZStd::string GetCurrentProjectCacheFolder() { return m_currentProjectCacheFolder; }
AzFramework::PlatformFlags GetEnabledPlatforms() { return m_enabledPlatforms; }
void AddWatchedPath(const QString& path);
void AddWatchedPaths(const QSet<QString>& paths);
void RemoveWatchedPath(const QString& path);
void RemoveWatchedPaths(const QSet<QString>& paths);
////////////////////////////////////////////////////////////////////////////////////////////
// Override the ApplicationManager TraceMessageBus methods so that messages go through MainWindow and not the CLI
bool OnPreError(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) override;
bool OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override;
bool OnPrintf(const char* /*window*/, const char* /*message*/) override;
////////////////////////////////////////////////////////////////////////////////////////////
const Config& GetConfig() { return m_config; }
Q_SIGNALS:
void ShowWindow();
void UpdateTab(const AZStd::string& directory);
void UpdateFiles(AssetBundlingFileType fileType, const AZStd::vector<AZStd::string>& absoluteFilePaths);
protected Q_SLOTS:
void DirectoryChanged(const QString& directory);
void FileChanged(const QString& path);
void ApplyConfig();
private:
/**
* Generates directory information for all paths used in this tool
* @return void on success, error message on failure
*/
AZ::Outcome<void, AZStd::string> InitializePaths();
QSharedPointer<QCoreApplication> m_qApp;
Config m_config;
QSharedPointer<MainWindow> m_mainWindow;
AZStd::string m_currentProjectFolder;
AZStd::string m_assetBundlingFolder;
AZStd::string m_seedListsFolder;
AZStd::string m_assetListsFolder;
AZStd::string m_rulesFolder;
AZStd::string m_bundleSettingsFolder;
AZStd::string m_bundlesFolder;
AZStd::string m_currentProjectCacheFolder;
AzFramework::PlatformFlags m_enabledPlatforms = AzFramework::PlatformFlags::Platform_NONE;
AZStd::unique_ptr<AzToolsFramework::PlatformAddressedAssetCatalogManager> m_platformCatalogManager;
bool m_isInitializing = false;
QFileSystemWatcher m_fileWatcher;
};
} // namespace AssetBundler
@@ -45,8 +45,16 @@ namespace AssetBundler
{
const char compareVariablePrefix = '$';
ApplicationManager::ApplicationManager(int* argc, char*** argv)
: AzToolsFramework::ToolsApplication(argc, argv)
GemInfo::GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath)
: m_gemName(name)
, m_relativeFilePath(relativeFilePath)
, m_absoluteFilePath(absoluteFilePath)
{
}
ApplicationManager::ApplicationManager(int* argc, char*** argv, QObject* parent)
: QObject(parent)
, AzToolsFramework::ToolsApplication(argc, argv)
{
}
@@ -55,7 +63,7 @@ namespace AssetBundler
DestroyApplication();
}
void ApplicationManager::Init()
bool ApplicationManager::Init()
{
AZ::Debug::TraceMessageBus::Handler::BusConnect();
Start(AzFramework::Application::Descriptor());
@@ -72,8 +80,11 @@ namespace AssetBundler
m_assetSeedManager = AZStd::make_unique<AzToolsFramework::AssetSeedManager>();
AZ_TracePrintf(AssetBundler::AppWindowName, "\n");
g_cachedEngineRoot = AZ::IO::FixedMaxPath(GetEngineRoot());
// There is no need to update the UserSettings file, so we can avoid a race condition by disabling save on shutdown
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
return true;
}
void ApplicationManager::DestroyApplication()
@@ -1522,7 +1533,7 @@ namespace AssetBundler
}
}
AZStd::vector<AZStd::string> defaultSeeds = GetDefaultSeeds(GetEngineRoot(), AZ::Utils::GetProjectPath(), m_currentProjectName);
AZStd::vector<AZStd::string> defaultSeeds = GetDefaultSeeds(AZ::Utils::GetProjectPath(), m_currentProjectName);
if (defaultSeeds.empty())
{
// Error has already been thrown
@@ -2847,3 +2858,4 @@ namespace AssetBundler
return !m_showVerboseOutput;
}
} // namespace AssetBundler
#include <source/utils/moc_applicationManager.cpp>
@@ -11,6 +11,7 @@
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
@@ -22,7 +23,7 @@
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <source/utils/utils.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogManager.h>
#endif
namespace AssetBundler
{
struct SeedsParams
@@ -161,16 +162,18 @@ namespace AssetBundler
};
class ApplicationManager
: public AZ::Debug::TraceMessageBus::Handler
: public QObject
, public AZ::Debug::TraceMessageBus::Handler
, public AzToolsFramework::ToolsApplication
{
Q_OBJECT
public:
explicit ApplicationManager(int* argc, char*** argv);
~ApplicationManager();
explicit ApplicationManager(int* argc, char*** argv, QObject* parent = 0);
virtual ~ApplicationManager();
void Init();
virtual bool Init();
void DestroyApplication();
bool Run();
virtual bool Run();
////////////////////////////////////////////////////////////////////////////////////////////
// AzFramework::Application overrides
+41 -35
View File
@@ -31,6 +31,7 @@ AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
#include <QDir>
#include <QString>
#include <QStringList>
#include <QJsonDocument>
AZ_POP_DISABLE_WARNING
namespace AssetBundler
@@ -108,6 +109,8 @@ namespace AssetBundler
const char* AssetCatalogFilename = "assetcatalog.xml";
AZ::IO::FixedMaxPath g_cachedEngineRoot;
const char EngineDirectoryName[] = "Engine";
const char RestrictedDirectoryName[] = "restricted";
@@ -128,7 +131,7 @@ namespace AssetBundler
AZStd::unordered_map<AZStd::string, AZStd::string>& defaultSeedLists,
AzFramework::PlatformFlags platformFlags)
{
AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
AZ::IO::FixedMaxPath engineRoot(GetCachedEngineRoot());
AZ::IO::FixedMaxPath engineRestrictedRoot = engineRoot / RestrictedDirectoryName;
AZ::IO::FixedMaxPath engineLocalPath = AZ::IO::PathView(engineDirectory.LexicallyRelative(engineRoot));
@@ -201,15 +204,10 @@ namespace AssetBundler
}
}
AZ::IO::FixedMaxPath GetEngineRoot()
AZ::IO::FixedMaxPath GetCachedEngineRoot()
{
AZ::IO::FixedMaxPath engineRootPath;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
}
return engineRootPath;
AZ_Error(AppWindowName, !g_cachedEngineRoot.empty(), "Cached Engine Root has not been initialized by the Bundler.");
return g_cachedEngineRoot;
}
void AddPlatformIdentifier(AZStd::string& filePath, const AZStd::string& platformIdentifier)
@@ -276,11 +274,11 @@ namespace AssetBundler
return defaultSeedLists;
}
AZStd::vector<AZStd::string> GetDefaultSeeds(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName)
AZStd::vector<AZStd::string> GetDefaultSeeds(AZStd::string_view projectPath, AZStd::string_view projectName)
{
AZStd::vector<AZStd::string> defaultSeeds;
defaultSeeds.emplace_back(GetProjectDependenciesAssetPath(enginePath, projectPath, projectName));
defaultSeeds.emplace_back(GetProjectDependenciesAssetPath(projectPath, projectName));
return defaultSeeds;
}
@@ -294,34 +292,12 @@ namespace AssetBundler
return projectDependenciesFilePath.LexicallyNormal();
}
AZ::IO::Path GetProjectDependenciesFileTemplate(AZStd::string_view engineRoot)
{
AZ::IO::Path projectDependenciesFileTemplate = engineRoot;
projectDependenciesFileTemplate /= DefaultProjectTemplatePath;
projectDependenciesFileTemplate /= AZStd::string::format("%s%s", ProjectName, DependenciesFileSuffix);
projectDependenciesFileTemplate.ReplaceExtension(DependenciesFileExtension);
return projectDependenciesFileTemplate.LexicallyNormal();
}
AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName)
AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view projectPath, AZStd::string_view projectName)
{
AZ::IO::Path projectDependenciesFile = GetProjectDependenciesFile(projectPath, projectName);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(projectDependenciesFile.c_str()))
{
AZ_TracePrintf(AssetBundler::AppWindowName, "Project dependencies file %s doesn't exist.\n", projectDependenciesFile.c_str());
AZ::IO::Path projectDependenciesFileTemplate = GetProjectDependenciesFileTemplate(enginePath);
if (AZ::IO::FileIOBase::GetInstance()->Copy(projectDependenciesFileTemplate.c_str(), projectDependenciesFile.c_str()))
{
AZ_TracePrintf(AssetBundler::AppWindowName, "Copied project dependencies file template %s to the current project.\n",
projectDependenciesFile.c_str());
}
else
{
AZ_Error(AppWindowName, false, "Failed to copy project dependencies file template %s from default project"
" template to the current project.\n", projectDependenciesFileTemplate.c_str());
return {};
}
AZ_Error(AssetBundler::AppWindowName, false, "Project dependencies file %s doesn't exist.\n", projectDependenciesFile.c_str());
}
// Turn the absolute path into a cache-relative path
@@ -538,6 +514,14 @@ namespace AssetBundler
return platformSpecificCacheFolderPath;
}
AZStd::string GenerateKeyFromAbsolutePath(const AZStd::string& absoluteFilePath)
{
AZStd::string key(absoluteFilePath);
AzFramework::StringFunc::Path::Normalize(key);
AzFramework::StringFunc::Path::StripDrive(key);
return key;
}
void ConvertToRelativePath(AZStd::string_view parentFolderPath, AZStd::string& absoluteFilePath)
{
absoluteFilePath = AZ::IO::PathView(absoluteFilePath).LexicallyRelative(parentFolderPath).String();
@@ -822,4 +806,26 @@ namespace AssetBundler
}
return false;
}
QJsonObject ReadJson(const AZStd::string& filePath)
{
QByteArray byteArray;
QFile jsonFile;
jsonFile.setFileName(filePath.c_str());
jsonFile.open(QIODevice::ReadOnly | QIODevice::Text);
byteArray = jsonFile.readAll();
jsonFile.close();
return QJsonDocument::fromJson(byteArray).object();
}
void SaveJson(const AZStd::string& filePath, const QJsonObject& jsonObject)
{
QFile jsonFile(filePath.c_str());
QJsonDocument JsonDocument;
JsonDocument.setObject(jsonObject);
jsonFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
jsonFile.write(JsonDocument.toJson());
jsonFile.close();
}
}
+24 -8
View File
@@ -19,7 +19,9 @@
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzToolsFramework/Asset/AssetBundler.h>
#include <AzToolsFramework/Asset/AssetUtils.h>
#include <QDir>
#include <QStringList>
#include <QJsonObject>
namespace AssetBundler
{
@@ -120,8 +122,20 @@ namespace AssetBundler
////////////////////////////////////////////////////////////////////////////////////////////
extern const char* AssetCatalogFilename;
extern AZ::IO::FixedMaxPath g_cachedEngineRoot;
static const size_t MaxErrorMessageLength = 4096;
//! This struct stores gem related information
struct GemInfo
{
AZ_CLASS_ALLOCATOR(GemInfo, AZ::SystemAllocator, 0);
GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath);
GemInfo() = default;
AZStd::string m_gemName;
AZStd::string m_relativeFilePath;
AZStd::string m_absoluteFilePath;
};
// The Warning Absorber is used to absorb warnings
// One case that this is being used is during loading of the asset catalog.
@@ -137,8 +151,8 @@ namespace AssetBundler
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
};
// Returns the engine root
AZ::IO::FixedMaxPath GetEngineRoot();
// Returns the cached engine root. Throws an error if the cached path has not been initialized by the Bundler Application.
AZ::IO::FixedMaxPath GetCachedEngineRoot();
/**
* Determines the name of the currently enabled game project
@@ -192,6 +206,8 @@ namespace AssetBundler
*/
AZ::IO::Path GetPlatformSpecificCacheFolderPath();
AZStd::string GenerateKeyFromAbsolutePath(const AZStd::string& absoluteFilePath);
void ConvertToRelativePath(AZStd::string_view parentFolderPath, AZStd::string& absoluteFilePath);
AZ::Outcome<void, AZStd::string> MakePath(const AZStd::string& path);
@@ -207,16 +223,13 @@ namespace AssetBundler
const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
//! Returns a vector of relative paths to Assets that should be included as default Seeds, but are not already in a Seed List file.
AZStd::vector<AZStd::string> GetDefaultSeeds(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName);
AZStd::vector<AZStd::string> GetDefaultSeeds(AZStd::string_view projectPath, AZStd::string_view projectName);
//! Returns the absolute path of {ProjectName}_Dependencies.xml
AZ::IO::Path GetProjectDependenciesFile(AZStd::string_view productPath, AZStd::string_view projectName);
//! Returns the absolute path of the project dependencies file in the default project template
AZ::IO::Path GetProjectDependenciesFileTemplate(AZStd::string_view enginePath);
//! Creates the ProjectName_Dependencies.xml file if it does not exist, and adds returns the relative path to the asset in the Cache.
AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view enginePath, AZStd::string_view projectPath, AZStd::string_view projectName);
AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view projectPath, AZStd::string_view projectName);
//! Returns the map from gem seed list file path to gem name
AZStd::unordered_map<AZStd::string, AZStd::string> GetGemSeedListFilePathToGemNameMap(const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AzFramework::PlatformFlags platformFlags);
@@ -229,6 +242,9 @@ namespace AssetBundler
//! Please note that the game project could be in a different location to the engine therefore we need the assetRoot param.
AzFramework::PlatformFlags GetEnabledPlatformFlags(AZStd::string_view enginePath, AZStd::string_view assetRoot, AZStd::string_view projectPath);
QJsonObject ReadJson(const AZStd::string& filePath);
void SaveJson(const AZStd::string& filePath, const QJsonObject& jsonObject);
//! Filepath is a helper class that is used to find the absolute path of a file
//! if the inputted file path is an absolute path than it does nothing
//! if the inputted file path is a relative path than based on whether the user
+3 -4
View File
@@ -113,14 +113,13 @@ namespace AssetBundler
AZ::SettingsRegistry::Register(&m_registry);
}
const char* engineRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
if (!engineRoot)
AssetBundler::g_cachedEngineRoot = m_data->m_application.get()->GetEngineRoot();
if (AssetBundler::g_cachedEngineRoot.empty())
{
GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to locate engine root.\n").c_str());
}
AzFramework::StringFunc::Path::Join(engineRoot, RelativeTestFolder, m_data->m_testEngineRoot);
AzFramework::StringFunc::Path::Join(AssetBundler::g_cachedEngineRoot.c_str(), RelativeTestFolder, m_data->m_testEngineRoot);
m_data->m_localFileIO = aznew AZ::IO::LocalFileIO();
m_data->m_priorFileIO = AZ::IO::FileIOBase::GetInstance();