|
@@ -0,0 +1,461 @@
|
|
|
+/*
|
|
|
+ * 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 "SceneSettingsCard.h"
|
|
|
+
|
|
|
+#include <QDateTime>
|
|
|
+#include <QHeaderView>
|
|
|
+#include <QHBoxLayout>
|
|
|
+#include <QListWidget>
|
|
|
+#include <QMenu>
|
|
|
+#include <QPushButton>
|
|
|
+#include <QSvgWidget>
|
|
|
+#include <QSvgRenderer>
|
|
|
+#include <QWidgetAction>
|
|
|
+#include <AzFramework/StringFunc/StringFunc.h>
|
|
|
+#include <AzToolsFramework/Debug/TraceContextLogFormatter.h>
|
|
|
+#include <AzToolsFramework/Debug/TraceContextStack.h>
|
|
|
+#include <AzToolsFramework/UI/Logging/LogEntry.h>
|
|
|
+#include <AzQtComponents/Components/StyledDetailsTableView.h>
|
|
|
+#include <AzQtComponents/Components/Widgets/TableView.h>
|
|
|
+#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/ProcessingHandler.h>
|
|
|
+
|
|
|
+SceneSettingsCardHeader::SceneSettingsCardHeader(QWidget* parent /* = nullptr */)
|
|
|
+ : AzQtComponents::CardHeader(parent)
|
|
|
+{
|
|
|
+ m_busySpinner = new QSvgWidget(":/stylesheet/img/loading.svg", this);
|
|
|
+ m_busySpinner->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
|
+ m_busySpinner->setMinimumSize(20, 20);
|
|
|
+ m_busySpinner->setMaximumSize(20, 20);
|
|
|
+ m_busySpinner->setBaseSize(20, 20);
|
|
|
+ m_backgroundLayout->insertWidget(1, m_busySpinner);
|
|
|
+ m_busySpinner->setStyleSheet("background-color: rgba(0,0,0,0)");
|
|
|
+
|
|
|
+ m_closeButton = new QPushButton(this);
|
|
|
+ m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
|
+ m_closeButton->setMinimumSize(24, 24);
|
|
|
+ m_closeButton->setMaximumSize(24, 24);
|
|
|
+ m_closeButton->setBaseSize(24, 24);
|
|
|
+
|
|
|
+ QIcon closeButtonIcon;
|
|
|
+ closeButtonIcon.addPixmap(QPixmap(":/SceneUI/Common/CloseIcon.svg"));
|
|
|
+ m_closeButton->setIcon(closeButtonIcon);
|
|
|
+ m_closeButton->setFlat(true);
|
|
|
+
|
|
|
+ m_backgroundLayout->addWidget(m_closeButton);
|
|
|
+
|
|
|
+ connect(m_closeButton, &QPushButton::clicked, this, &SceneSettingsCardHeader::triggerCloseButton);
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCardHeader::triggerCloseButton()
|
|
|
+{
|
|
|
+ parent()->deleteLater();
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCardHeader::SetCanClose(bool canClose)
|
|
|
+{
|
|
|
+ m_closeButton->setEnabled(canClose);
|
|
|
+ // If this card can be closed, it's not busy
|
|
|
+ m_busySpinner->setHidden(canClose);
|
|
|
+}
|
|
|
+
|
|
|
+SceneSettingsCard::SceneSettingsCard(AZ::Uuid traceTag, QString fileTracked, Layout layout, QWidget* parent /* = nullptr */)
|
|
|
+ :
|
|
|
+ AzQtComponents::Card(new SceneSettingsCardHeader(), parent),
|
|
|
+ m_traceTag(traceTag),
|
|
|
+ m_fileTracked(fileTracked)
|
|
|
+{
|
|
|
+ m_settingsHeader = qobject_cast<SceneSettingsCardHeader*>(header());
|
|
|
+ // This has to be set here, instead of in the customheader,
|
|
|
+ // because the Card constructor forces the context menu to be visible.
|
|
|
+ header()->setHasContextMenu(false);
|
|
|
+
|
|
|
+ m_reportModel = new AzQtComponents::StyledDetailsTableModel(this);
|
|
|
+ int statusColumn = m_reportModel->AddColumn("Status", AzQtComponents::StyledDetailsTableModel::StatusIcon);
|
|
|
+ int platformColumn = -1;
|
|
|
+ int windowColumn = -1;
|
|
|
+ if (layout == Layout::Exporting)
|
|
|
+ {
|
|
|
+ platformColumn = m_reportModel->AddColumn("Platform");
|
|
|
+ windowColumn = m_reportModel->AddColumn("Window");
|
|
|
+ m_reportModel->AddColumnAlias("window", "Window");
|
|
|
+ }
|
|
|
+ int timeColumn = m_reportModel->AddColumn("Time");
|
|
|
+ m_reportModel->AddColumn("Message");
|
|
|
+ m_reportModel->AddColumnAlias("message", "Message");
|
|
|
+ m_reportView = new AzQtComponents::TableView(this);
|
|
|
+ m_reportView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
|
+ m_reportView->setModel(m_reportModel);
|
|
|
+
|
|
|
+ if (platformColumn > 0)
|
|
|
+ {
|
|
|
+ m_reportView->header()->setSectionResizeMode(platformColumn, QHeaderView::ResizeToContents);
|
|
|
+ }
|
|
|
+ if (windowColumn > 0)
|
|
|
+ {
|
|
|
+ m_reportView->header()->setSectionResizeMode(windowColumn, QHeaderView::ResizeToContents);
|
|
|
+ }
|
|
|
+
|
|
|
+ m_reportView->header()->setSectionResizeMode(statusColumn, QHeaderView::ResizeToContents);
|
|
|
+ m_reportView->header()->setSectionResizeMode(timeColumn, QHeaderView::ResizeToContents);
|
|
|
+ setContentWidget(m_reportView);
|
|
|
+
|
|
|
+ AZ::Debug::TraceMessageBus::Handler::BusConnect();
|
|
|
+
|
|
|
+ m_reportView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
+ connect(m_reportView, &AzQtComponents::TableView::customContextMenuRequested, this, &SceneSettingsCard::ShowLogContextMenu);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+SceneSettingsCard::~SceneSettingsCard()
|
|
|
+{
|
|
|
+ AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::SetAndStartProcessingHandler(const AZStd::shared_ptr<AZ::SceneAPI::SceneUI::ProcessingHandler>& handler)
|
|
|
+{
|
|
|
+ AZ_Assert(handler, "Processing handler was null");
|
|
|
+ AZ_Assert(!m_targetHandler, "A handler has already been assigned. Only one can be active per layer at any given time.");
|
|
|
+ if (m_targetHandler)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_targetHandler = handler;
|
|
|
+
|
|
|
+ connect(m_targetHandler.get(), &AZ::SceneAPI::SceneUI::ProcessingHandler::StatusMessageUpdated, this, &SceneSettingsCard::OnSetStatusMessage);
|
|
|
+ connect(m_targetHandler.get(), &AZ::SceneAPI::SceneUI::ProcessingHandler::AddLogEntry, this, &SceneSettingsCard::AddLogEntry);
|
|
|
+ connect(m_targetHandler.get(), &AZ::SceneAPI::SceneUI::ProcessingHandler::ProcessingComplete, this, &SceneSettingsCard::OnProcessingComplete);
|
|
|
+
|
|
|
+ handler->BeginProcessing();
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::AddLogEntry(const AzToolsFramework::Logging::LogEntry& logEntry)
|
|
|
+{
|
|
|
+ if (logEntry.GetSeverity() == AzToolsFramework::Logging::LogEntry::Severity::Message)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ AzQtComponents::StyledDetailsTableModel::TableEntry reportEntry;
|
|
|
+
|
|
|
+ QVector<QPair<QString, QString>> detailsForLogLine;
|
|
|
+
|
|
|
+ for (auto& field : logEntry.GetFields())
|
|
|
+ {
|
|
|
+ if (AzFramework::StringFunc::Equal("message", field.second.m_name.c_str()) ||
|
|
|
+ AzFramework::StringFunc::Equal("window", field.second.m_name.c_str()))
|
|
|
+ {
|
|
|
+ // Add the message and window to the direct log
|
|
|
+ reportEntry.Add(field.second.m_name.c_str(), field.second.m_value.c_str());
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // All other fields, add to the additional details view.
|
|
|
+ detailsForLogLine.push_back(QPair<QString, QString>(field.second.m_name.c_str(), field.second.m_value.c_str()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ m_additionalLogDetails.push_back(detailsForLogLine);
|
|
|
+
|
|
|
+ if (logEntry.GetSeverity() == AzToolsFramework::Logging::LogEntry::Severity::Error)
|
|
|
+ {
|
|
|
+ reportEntry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
|
|
|
+ UpdateCompletionState(CompletionState::Error);
|
|
|
+ }
|
|
|
+ else if (logEntry.GetSeverity() == AzToolsFramework::Logging::LogEntry::Severity::Warning)
|
|
|
+ {
|
|
|
+ reportEntry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusWarning);
|
|
|
+ UpdateCompletionState(CompletionState::Warning);
|
|
|
+ }
|
|
|
+ reportEntry.Add("Time", GetTimeNowAsString());
|
|
|
+ AddLogTableEntry(reportEntry);
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::OnProcessingComplete()
|
|
|
+{
|
|
|
+ AzQtComponents::StyledDetailsTableModel::TableEntry entry;
|
|
|
+ entry.Add("Message", "Asset processing completed.");
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusSuccess);
|
|
|
+ entry.Add("Time", GetTimeNowAsString());
|
|
|
+ AddLogTableEntry(entry);
|
|
|
+
|
|
|
+ SetState(SceneSettingsCard::State::Done);
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::OnSetStatusMessage(const AZStd::string& message)
|
|
|
+{
|
|
|
+ AzQtComponents::StyledDetailsTableModel::TableEntry entry;
|
|
|
+ entry.Add("Message", message.c_str());
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusSuccess);
|
|
|
+ entry.Add("Time", GetTimeNowAsString());
|
|
|
+ AddLogTableEntry(entry);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+bool SceneSettingsCard::OnPrintf(const char* window, const char* message)
|
|
|
+{
|
|
|
+ if (!ShouldProcessMessage())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ AzQtComponents::StyledDetailsTableModel::TableEntry entry;
|
|
|
+ if (AzFramework::StringFunc::Find(window, "Success") != AZStd::string::npos)
|
|
|
+ {
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusSuccess);
|
|
|
+ }
|
|
|
+ else if (AzFramework::StringFunc::Find(window, "Warning") != AZStd::string::npos)
|
|
|
+ {
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusWarning);
|
|
|
+ UpdateCompletionState(CompletionState::Warning);
|
|
|
+ }
|
|
|
+ else if (AzFramework::StringFunc::Find(window, "Error") != AZStd::string::npos)
|
|
|
+ {
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
|
|
|
+ UpdateCompletionState(CompletionState::Error);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // To reduce noise in the report widget, only show success, warning and error messages.
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ entry.Add("Message", message);
|
|
|
+ entry.Add("Time", GetTimeNowAsString());
|
|
|
+ AddLogTableEntry(entry);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool SceneSettingsCard::OnError(const char* /*window*/, const char* message)
|
|
|
+{
|
|
|
+ if (!ShouldProcessMessage())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ AzQtComponents::StyledDetailsTableModel::TableEntry entry;
|
|
|
+ entry.Add("Message", message);
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
|
|
|
+ entry.Add("Time", GetTimeNowAsString());
|
|
|
+ AddLogTableEntry(entry);
|
|
|
+
|
|
|
+ UpdateCompletionState(CompletionState::Error);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool SceneSettingsCard::OnWarning(const char* /*window*/, const char* message)
|
|
|
+{
|
|
|
+ if (!ShouldProcessMessage())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ AzQtComponents::StyledDetailsTableModel::TableEntry entry;
|
|
|
+ entry.Add("Message", message);
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusWarning);
|
|
|
+ entry.Add("Time", GetTimeNowAsString());
|
|
|
+ AddLogTableEntry(entry);
|
|
|
+ UpdateCompletionState(CompletionState::Warning);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool SceneSettingsCard::OnAssert(const char* message)
|
|
|
+{
|
|
|
+ if (!ShouldProcessMessage())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ AzQtComponents::StyledDetailsTableModel::TableEntry entry;
|
|
|
+ entry.Add("Message", message);
|
|
|
+ entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
|
|
|
+ entry.Add("Time", GetTimeNowAsString());
|
|
|
+ AddLogTableEntry(entry);
|
|
|
+ UpdateCompletionState(CompletionState::Error);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::SetState(State newState)
|
|
|
+{
|
|
|
+ switch (newState)
|
|
|
+ {
|
|
|
+ case State::Loading:
|
|
|
+ setTitle(tr("Loading scene settings"));
|
|
|
+ m_settingsHeader->SetCanClose(false);
|
|
|
+ break;
|
|
|
+ case State::Processing:
|
|
|
+ setTitle(tr("Saving scene settings, and reprocessing scene file"));
|
|
|
+ m_settingsHeader->SetCanClose(false);
|
|
|
+ break;
|
|
|
+ case State::Done:
|
|
|
+ {
|
|
|
+ QString errorsAndWarningsString;
|
|
|
+ if (m_warningCount > 0 || m_errorCount > 0)
|
|
|
+ {
|
|
|
+ errorsAndWarningsString = tr(" with %1 warning(s), %2 error(s)").arg(m_warningCount).arg(m_errorCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ QString previousStateString;
|
|
|
+ switch (m_sceneCardState)
|
|
|
+ {
|
|
|
+ case State::Loading:
|
|
|
+ previousStateString = tr("Loading ");
|
|
|
+ break;
|
|
|
+ case State::Processing:
|
|
|
+ previousStateString = tr("Processing ");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ setTitle(tr("%1%2 completed at %3%4").arg(previousStateString).arg(m_fileTracked).arg(GetTimeNowAsString()).arg(errorsAndWarningsString));
|
|
|
+ m_settingsHeader->SetCanClose(true);
|
|
|
+
|
|
|
+ switch (m_completionState)
|
|
|
+ {
|
|
|
+ case CompletionState::Error:
|
|
|
+ case CompletionState::Failure:
|
|
|
+ m_settingsHeader->setIcon(QIcon(":/SceneUI/Common/ErrorIcon.svg"));
|
|
|
+ break;
|
|
|
+ case CompletionState::Warning:
|
|
|
+ m_settingsHeader->setIcon(QIcon(":/SceneUI/Common/WarningIcon.svg"));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ m_settingsHeader->setIcon(QIcon(":/SceneUI/Common/SuccessIcon.svg"));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
|
|
+ emit ProcessingCompleted();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ m_sceneCardState = newState;
|
|
|
+}
|
|
|
+
|
|
|
+bool SceneSettingsCard::ShouldProcessMessage()
|
|
|
+{
|
|
|
+ AZStd::shared_ptr<const AzToolsFramework::Debug::TraceContextStack> stack = m_traceStackHandler.GetCurrentStack();
|
|
|
+ if (stack)
|
|
|
+ {
|
|
|
+ for (size_t i = 0; i < stack->GetStackCount(); ++i)
|
|
|
+ {
|
|
|
+ if (stack->GetType(i) == AzToolsFramework::Debug::TraceContextStackInterface::ContentType::UuidType)
|
|
|
+ {
|
|
|
+ if (stack->GetUuidValue(i) == m_traceTag)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::UpdateCompletionState(CompletionState newState)
|
|
|
+{
|
|
|
+ // Use the highest encountered state
|
|
|
+ m_completionState = AZStd::max(m_completionState, newState);
|
|
|
+ switch (newState)
|
|
|
+ {
|
|
|
+ case CompletionState::Warning:
|
|
|
+ ++m_warningCount;
|
|
|
+ break;
|
|
|
+ case CompletionState::Error:
|
|
|
+ ++m_errorCount;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::CopyTraceContext(AzQtComponents::StyledDetailsTableModel::TableEntry& entry) const
|
|
|
+{
|
|
|
+ AZStd::shared_ptr<const AzToolsFramework::Debug::TraceContextStack> stack = m_traceStackHandler.GetCurrentStack();
|
|
|
+ if (stack)
|
|
|
+ {
|
|
|
+ AZStd::string value;
|
|
|
+ for (size_t i = 0; i < stack->GetStackCount(); ++i)
|
|
|
+ {
|
|
|
+ if (stack->GetType(i) != AzToolsFramework::Debug::TraceContextStackInterface::ContentType::UuidType)
|
|
|
+ {
|
|
|
+ const char* key = stack->GetKey(i);
|
|
|
+ AzToolsFramework::Debug::TraceContextLogFormatter::PrintValue(value, *stack, i);
|
|
|
+ entry.Add(key, value.c_str());
|
|
|
+ value.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::AddLogTableEntry(AzQtComponents::StyledDetailsTableModel::TableEntry& entry)
|
|
|
+{
|
|
|
+ CopyTraceContext(entry);
|
|
|
+ m_reportModel->AddEntry(entry);
|
|
|
+ // Set the height of the view, so the cards expand more vertically.
|
|
|
+ // Clamp that max height at a point so it doesn't try to pull too much height from containing window.
|
|
|
+ int rowCount = m_reportModel->rowCount();
|
|
|
+ if(rowCount < 10)
|
|
|
+ {
|
|
|
+ m_reportView->setMinimumHeight(m_reportView->sizeHintForRow(0) * (rowCount + 1));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+QString SceneSettingsCard::GetTimeNowAsString()
|
|
|
+{
|
|
|
+ return QDateTime::currentDateTime().toString(tr("hh:mm:ss ap"));
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSettingsCard::ShowLogContextMenu(const QPoint& pos)
|
|
|
+{
|
|
|
+ const QModelIndex selectedIndex = m_reportView->indexAt(pos);
|
|
|
+ if (!selectedIndex.isValid())
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int logRow = selectedIndex.row();
|
|
|
+ if (logRow <= 0)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (logRow > m_additionalLogDetails.count())
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int additionalLogCount = m_additionalLogDetails[logRow].count();
|
|
|
+
|
|
|
+ if (additionalLogCount <= 0)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the log message for the first row is empty, skip. This happens when there was no log details at all.
|
|
|
+ if (additionalLogCount == 1 && m_additionalLogDetails[logRow][0].second.isEmpty())
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ QMenu menu;
|
|
|
+ menu.setToolTipsVisible(true);
|
|
|
+ QAction* contextMenuTitleAction = menu.addAction("Additional log context");
|
|
|
+ contextMenuTitleAction->setToolTip(tr("Additional log information for the selected line"));
|
|
|
+ menu.addSeparator();
|
|
|
+
|
|
|
+ QWidgetAction* logMenuListAction = new QWidgetAction(&menu);
|
|
|
+
|
|
|
+ QListWidget* logDetailsWidget = new QListWidget(&menu);
|
|
|
+ logDetailsWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
|
|
|
+ logDetailsWidget->setTextElideMode(Qt::ElideLeft);
|
|
|
+ logDetailsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
+ logDetailsWidget->setSelectionMode(QAbstractItemView::NoSelection);
|
|
|
+
|
|
|
+ logMenuListAction->setDefaultWidget(logDetailsWidget);
|
|
|
+
|
|
|
+ for (const auto& logDetail : m_additionalLogDetails[logRow])
|
|
|
+ {
|
|
|
+ logDetailsWidget->addItem(tr("%1 - %2").arg(logDetail.first).arg(logDetail.second));
|
|
|
+ }
|
|
|
+
|
|
|
+ logDetailsWidget->setFixedHeight(additionalLogCount * logDetailsWidget->sizeHintForRow(0));
|
|
|
+ logDetailsWidget->setFixedWidth(logDetailsWidget->sizeHintForColumn(0));
|
|
|
+ menu.addAction(logMenuListAction);
|
|
|
+
|
|
|
+ menu.exec(m_reportView->viewport()->mapToGlobal(pos));
|
|
|
+}
|