123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
- #include <AtomToolsFramework/Util/Util.h>
- #include <AzCore/Name/Name.h>
- #include <AzQtComponents/Components/StyledSpinBox.h>
- #include <Window/ShaderManagementConsoleTableView.h>
- #include <QComboBox>
- #include <QHeaderView>
- #include <QMenu>
- #include <QKeyEvent>
- #include <QPushButton>
- #include <QFont>
- #include <functional>
- namespace ShaderManagementConsole
- {
- template<typename BaseWidget>
- struct FocusOutConfigurable : public BaseWidget
- {
- template<typename... Objects>
- FocusOutConfigurable(Objects&&... args)
- : BaseWidget(std::forward<Objects>(args)...)
- {
- }
- void hidePopup() override
- {
- BaseWidget::hidePopup();
- if (m_onExit)
- {
- m_onExit();
- }
- }
- std::function<void()> m_onExit;
- };
- ShaderManagementConsoleTableView::ShaderManagementConsoleTableView(
- const AZ::Crc32& toolId, const AZ::Uuid& documentId, QWidget* parent)
- : QTableWidget(parent)
- , m_toolId(toolId)
- , m_documentId(documentId)
- , m_emptyOptionIcon(":/Icons/emptyoption.svg")
- {
- setEditTriggers(QAbstractItemView::NoEditTriggers);
- setSelectionBehavior(QAbstractItemView::SelectItems);
- setSelectionMode(QAbstractItemView::SingleSelection);
- verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
- horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
- setAlternatingRowColors(true);
- setContextMenuPolicy(Qt::CustomContextMenu);
- connect(this, &QTableWidget::customContextMenuRequested, this, &ShaderManagementConsoleTableView::ShowContextMenu);
- RebuildTable();
- AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
- }
- void ShaderManagementConsoleTableView::mousePressEvent(QMouseEvent* e)
- {
- QTableWidget::mousePressEvent(e);
- if (e->button() == Qt::RightButton)
- {
- ShowContextMenu(e->pos());
- }
- }
- void ShaderManagementConsoleTableView::ShowContextMenu(const QPoint& pos)
- {
- QMenu contextMenu(tr("Context menu"), this);
- contextMenu.addAction(
- tr("Add Variant"),
- [this]()
- {
- AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
- m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::BeginEdit);
- ShaderManagementConsoleDocumentRequestBus::Event(
- m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::AddOneVariantRow);
- AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
- m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::EndEdit);
- });
- QMenu* scriptsMenu = contextMenu.addMenu(QObject::tr("Python Scripts"));
- const AZStd::vector<AZStd::string> arguments{ m_documentId.ToString<AZStd::string>(false, true) };
- AtomToolsFramework::AddRegisteredScriptToMenu(scriptsMenu, "/O3DE/ShaderManagementConsole/DocumentTableView/ContextMenuScripts", arguments);
- contextMenu.exec(mapToGlobal(pos));
- }
- ShaderManagementConsoleTableView::~ShaderManagementConsoleTableView()
- {
- AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
- }
- void ShaderManagementConsoleTableView::OnDocumentOpened(const AZ::Uuid& documentId)
- {
- if (m_documentId == documentId)
- {
- RebuildTable();
- }
- }
- void ShaderManagementConsoleTableView::OnDocumentModified(const AZ::Uuid& documentId)
- {
- if (m_documentId == documentId)
- {
- RebuildTable();
- }
- }
- int ShaderManagementConsoleTableView::UiColumnToOption(int uiColumnIndex) const
- {
- return uiColumnIndex - 1; // because column #0 is the "X" (deleters)
- }
- int ShaderManagementConsoleTableView::GetColumnsCount(CountQuery query) const
- {
- return query == CountQuery::ForUi ? columnCount() : UiColumnToOption(columnCount());
- }
- void ShaderManagementConsoleTableView::RebuildTable()
- {
- QSignalBlocker blocker(this);
- // Delete any active edit widget from the current selection
- if (currentColumn() != 0)
- {
- removeCellWidget(currentRow(), currentColumn());
- }
- // Disconnect data change signal while populating the table
- disconnect();
- // Get the shader variant list source data whose options will be used to populate the table
- m_shaderVariantListSourceData = {};
- ShaderManagementConsoleDocumentRequestBus::EventResult(
- m_shaderVariantListSourceData, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantListSourceData);
- // The number of variants corresponds to the number of rows in the table
- m_shaderVariantCount = m_shaderVariantListSourceData.m_shaderVariants.size();
- // The number of options corresponds to the number of columns in the table. This data is being pulled from the asset instead of the
- // shader variant list source data. The asset may contain more options that are listed in the source data. This will result in
- // several columns with no values.
- m_shaderOptionCount = {};
- ShaderManagementConsoleDocumentRequestBus::EventResult(
- m_shaderOptionCount, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptorCount);
- // Only clear the table if the number of columns or rows have changed
- if (rowCount() != m_shaderVariantCount || GetColumnsCount(CountQuery::Options) != m_shaderOptionCount)
- {
- clear();
- setRowCount(static_cast<int>(m_shaderVariantCount));
- setColumnCount(static_cast<int>(m_shaderOptionCount) + 1); // 1 for "delete row" widgets
- }
- // Get a list of all of the shader option descriptors from the shader asset that will be used for the columns in the table
- m_shaderOptionDescriptors = {};
- m_shaderOptionDescriptors.reserve(GetColumnsCount(CountQuery::Options));
- for (int column = 0; column < GetColumnsCount(CountQuery::Options); ++column)
- {
- AZ::RPI::ShaderOptionDescriptor shaderOptionDescriptor;
- ShaderManagementConsoleDocumentRequestBus::EventResult(
- shaderOptionDescriptor,
- m_documentId,
- &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor,
- column);
- m_shaderOptionDescriptors.push_back(shaderOptionDescriptor);
- }
- switch (m_columnSortMode)
- {
- case Alpha:
- {
- // Sort descriptors by name and ascending order
- AZStd::sort(
- m_shaderOptionDescriptors.begin(),
- m_shaderOptionDescriptors.end(),
- [](const auto& a, const auto& b)
- {
- return a.GetName().GetStringView() < b.GetName().GetStringView();
- });
- }
- break;
- case Rank:
- {
- // Sort descriptors by ascending declaration order
- AZStd::sort(
- m_shaderOptionDescriptors.begin(),
- m_shaderOptionDescriptors.end(),
- [](const auto& a, const auto& b)
- {
- return a.GetOrder() < b.GetOrder();
- });
- }
- break;
- case Cost:
- {
- // Sort by cost estimate score in descending order
- AZStd::sort(
- m_shaderOptionDescriptors.begin(),
- m_shaderOptionDescriptors.end(),
- [](const auto& a, const auto& b)
- {
- return a.GetCostEstimate() > b.GetCostEstimate();
- });
- }
- break;
- }
- // Fill in the header of each column with the descriptor name
- for (int column = 1; column < GetColumnsCount(CountQuery::ForUi); ++column)
- {
- const auto& shaderOptionDescriptor = m_shaderOptionDescriptors[UiColumnToOption(column)];
- auto* tableItem = new QTableWidgetItem(shaderOptionDescriptor.GetName().GetCStr());
- tableItem->setToolTip(tr("cost %1").arg(shaderOptionDescriptor.GetCostEstimate()));
- // color material options in yellow
- if (m_shaderVariantListSourceData.m_materialOptionsHint.find(shaderOptionDescriptor.GetName()) !=
- m_shaderVariantListSourceData.m_materialOptionsHint.end())
- {
- tableItem->setForeground(QColorConstants::Yellow);
- }
- setHorizontalHeaderItem(column, tableItem);
- }
- setHorizontalHeaderItem(0, new QTableWidgetItem(""));
- // Fill all the rows with values from each variant
- for (int row = 0; row < rowCount(); ++row)
- {
- const auto& shaderVariant = m_shaderVariantListSourceData.m_shaderVariants[row];
- setVerticalHeaderItem(row, new QTableWidgetItem(QString::number(shaderVariant.m_stableId)));
- for (int column = 1; column < GetColumnsCount(CountQuery::ForUi); ++column)
- {
- const auto& shaderOptionDescriptor = m_shaderOptionDescriptors[UiColumnToOption(column)];
- const auto optionIt = shaderVariant.m_options.find(shaderOptionDescriptor.GetName());
- const AZ::Name valueName = optionIt != shaderVariant.m_options.end() ? AZ::Name(optionIt->second) : AZ::Name();
- auto* newItem = new QTableWidgetItem(valueName.GetCStr());
- if (valueName.IsEmpty())
- {
- newItem->setIcon(m_emptyOptionIcon);
- newItem->setToolTip(tr("runtime variable"));
- }
- setItem(row, column, newItem);
- }
- auto* deleterButton = new QPushButton;
- deleterButton->setText(reinterpret_cast<const char*>(u8"\u274C")); // cross sign
- deleterButton->setToolTip(tr("delete row"));
- connect(deleterButton, &QPushButton::clicked, this, [this, row](){
- auto& vec = m_shaderVariantListSourceData.m_shaderVariants;
- vec.erase(vec.begin() + row);
- TransferViewModelToModel(CallOnModified);
- });
- setCellWidget(row, 0, deleterButton);
- }
- horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
- // Connect to the data changed signal to listen for and apply table edits back to the document
- connect(this, &QTableWidget::currentCellChanged, this, &ShaderManagementConsoleTableView::OnCellSelected);
- connect(this, &QTableWidget::cellChanged, this, &ShaderManagementConsoleTableView::OnCellChanged);
- }
- void ShaderManagementConsoleTableView::OnCellSelected(int row, int column, int previousRow, int previousColumn)
- {
- if (column == 0)
- {
- return;
- }
- removeCellWidget(row, column);
- removeCellWidget(previousRow, previousColumn);
- if (row < 0 || row >= m_shaderVariantListSourceData.m_shaderVariants.size())
- {
- return;
- }
- if (column < 0 || UiColumnToOption(column) >= m_shaderOptionDescriptors.size())
- {
- return;
- }
- const auto& shaderOptionDescriptor = m_shaderOptionDescriptors[UiColumnToOption(column)];
- const auto& shaderVariant = m_shaderVariantListSourceData.m_shaderVariants[row];
- const auto optionIt = shaderVariant.m_options.find(shaderOptionDescriptor.GetName());
- const AZ::Name valueName = optionIt != shaderVariant.m_options.end() ? AZ::Name(optionIt->second) : AZ::Name();
- const AZ::RPI::ShaderOptionValue value = shaderOptionDescriptor.FindValue(valueName);
- const AZ::RPI::ShaderOptionValue valueMin = shaderOptionDescriptor.GetMinValue();
- const AZ::RPI::ShaderOptionValue valueMax = shaderOptionDescriptor.GetMaxValue();
- switch (shaderOptionDescriptor.GetType())
- {
- case AZ::RPI::ShaderOptionType::Boolean:
- case AZ::RPI::ShaderOptionType::Enumeration:
- {
- auto* comboBox = new FocusOutConfigurable<QComboBox>(this);
- static auto italicFont = QFont(comboBox->fontInfo().family(), comboBox->fontInfo().pointSize(), comboBox->fontInfo().weight(), true);
- comboBox->addItem("<dynamic>");
- comboBox->setItemData(0, italicFont, Qt::FontRole);
- comboBox->setItemIcon(0, m_emptyOptionIcon);
- for (uint32_t valueIndex = valueMin.GetIndex(); valueIndex <= valueMax.GetIndex(); ++valueIndex)
- {
- comboBox->addItem(shaderOptionDescriptor.GetValueName(AZ::RPI::ShaderOptionValue{ valueIndex }).GetCStr());
- }
- comboBox->setCurrentText(valueName.GetCStr());
- setCellWidget(row, column, comboBox);
- connect(comboBox, &QComboBox::currentTextChanged, this, [this, row, column](const QString& text) {
- item(row, column)->setText(text == "<dynamic>" ? "" : text);
- });
- comboBox->m_onExit = [this, row, column]() { removeCellWidget(row, column); };
- break;
- }
- case AZ::RPI::ShaderOptionType::IntegerRange:
- {
- auto* spinBox = new AzQtComponents::StyledSpinBox(this);
- spinBox->setRange(valueMin.GetIndex(), valueMax.GetIndex());
- spinBox->setValue(value.GetIndex());
- setCellWidget(row, column, spinBox);
- connect(spinBox, &AzQtComponents::StyledSpinBox::textChanged, this, [this, row, column](const QString& text) {
- item(row, column)->setText(text);
- });
- break;
- }
- }
- }
- void ShaderManagementConsoleTableView::keyPressEvent(QKeyEvent* e)
- {
- if (e->key() == Qt::Key_Escape)
- {
- setCurrentCell(-1, -1);
- clearFocus();
- }
- else if (e->key() == Qt::Key_Menu)
- {
- ShowContextMenu(mapFromGlobal(QCursor::pos()));
- }
- }
- void ShaderManagementConsoleTableView::OnCellChanged(int row, int column)
- {
- if (row < 0 || row >= m_shaderVariantListSourceData.m_shaderVariants.size())
- {
- return;
- }
- if (column < 0 || column > m_shaderOptionDescriptors.size())
- {
- return;
- }
- // Update the shader variant list from the table data
- auto& shaderVariant = m_shaderVariantListSourceData.m_shaderVariants[row];
- const auto optionItem = horizontalHeaderItem(column);
- if (optionItem && !optionItem->text().isEmpty())
- {
- if (auto variantItem = item(row, column))
- {
- QSignalBlocker blocker(this);
- // Set or clear the option based on the item text
- if (variantItem->text().isEmpty())
- {
- shaderVariant.m_options.erase(AZ::Name{optionItem->text().toUtf8().constData()});
- variantItem->setIcon(m_emptyOptionIcon);
- variantItem->setToolTip(tr("runtime variable"));
- }
- else
- {
- shaderVariant.m_options[AZ::Name{optionItem->text().toUtf8().constData()}] = variantItem->text().toUtf8().constData();
- variantItem->setIcon({});
- variantItem->setToolTip("");
- }
- }
- }
- TransferViewModelToModel(KeepAsIs/*because we know the change is already reflected*/);
- }
- void ShaderManagementConsoleTableView::TransferViewModelToModel(RebuildMode mode)
- {
- // Temporarily disconnect the document notification bus to prevent recursive notification handling as changes are applied
- AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
- // Send the begin edit notification to signify the beginning of an undoable change
- AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
- m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::BeginEdit);
- // Set the shader variant list source data built from the table onto the document
- ShaderManagementConsoleDocumentRequestBus::Event(
- m_documentId,
- &ShaderManagementConsoleDocumentRequestBus::Events::SetShaderVariantListSourceData,
- m_shaderVariantListSourceData);
- // Signify the end of the undoable change
- AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
- m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::EndEdit);
- // Reconnect to the notification bus now that all changes have been applied
- AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
- if (mode == CallOnModified)
- {
- // manual call to the modified handler because when the bus is disconnected, events to this goes to naught.
- OnDocumentModified(m_documentId);
- }
- }
- void ShaderManagementConsoleTableView::SetColumnSortMode(ColumnSortMode m)
- {
- m_columnSortMode = m;
- RebuildTable();
- }
- ShaderManagementConsoleContainer::ShaderManagementConsoleContainer(QWidget* container, const AZ::Crc32& toolId, const AZ::Uuid& documentId, QWidget* parent)
- :
- QVBoxLayout(container)
- , m_tableView(toolId, documentId, parent)
- {
- m_sortLabel.setText(tr("Option sort mode:"));
- m_sortComboBox.addItem(tr("Alphabetical"));
- m_sortComboBox.addItem(tr("Rank (shader declaration order)"));
- m_sortComboBox.addItem(tr("Cost impact (likely-performance weight, by static-analysis)"));
- m_sortComboBox.setCurrentIndex(2);
- m_defragVariants.setIcon(QIcon(":/Icons/defrag.svg"));
- m_defragVariants.setToolTip(tr("Merge duplicated variants, and recompact stable IDs"));
- connect(&m_defragVariants,
- &QPushButton::clicked,
- this,
- [documentId]() {
- AtomToolsFramework::AtomToolsDocumentRequestBus::Event(documentId,
- &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::BeginEdit);
- ShaderManagementConsoleDocumentRequestBus::Event(documentId,
- &ShaderManagementConsoleDocumentRequestBus::Events::DefragmentVariantList);
- AtomToolsFramework::AtomToolsDocumentRequestBus::Event(documentId,
- &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::EndEdit);
- });
- m_subLayout.addWidget(&m_sortLabel);
- m_subLayout.addWidget(&m_sortComboBox);
- m_subLayout.addWidget(&m_defragVariants);
- m_subLayout.addStretch();
- addLayout(&m_subLayout);
- addWidget(&m_tableView);
- connect(&m_sortComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int index) {
- m_tableView.SetColumnSortMode(ColumnSortMode(index));
- });
- }
- } // namespace ShaderManagementConsole
- #include <Window/moc_ShaderManagementConsoleTableView.cpp>
|