704443ac89
Several UI property handlers were incorrectly using the autoDelete feature by calling UnregisterPropertyType and deleting the pointer themselves, which caused double-delete crashes on application shutdown. PropertyManagerComponent now gracefully handles that condition but also explicitly asserts explaining how the code should be changed, and the "known offenders" have been fixed up to use autoDelete correctly.
259 lines
8.7 KiB
C++
259 lines
8.7 KiB
C++
/*
|
|
* 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 "GradientSignal_precompiled.h"
|
|
#include "GradientPreviewDataWidget.h"
|
|
#include <GradientSignal/Ebuses/GradientPreviewContextRequestBus.h>
|
|
|
|
#include <AzCore/Component/EntityId.h>
|
|
|
|
#include <QPushButton>
|
|
#include <QVBoxLayout>
|
|
|
|
namespace GradientSignal
|
|
{
|
|
//
|
|
// GradientPreviewDataWidgetHandler
|
|
//
|
|
|
|
AZ::u32 GradientPreviewDataWidgetHandler::GetHandlerName() const
|
|
{
|
|
return AZ_CRC("GradientPreviewer", 0x1dbbba45);
|
|
}
|
|
|
|
void GradientPreviewDataWidgetHandler::ConsumeAttribute(GradientPreviewDataWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
|
|
{
|
|
(void)debugName;
|
|
|
|
if (attrib == AZ_CRC("GradientFilter", 0x99bf0362))
|
|
{
|
|
GradientPreviewWidget::SampleFilterFunc filterFunc;
|
|
if (attrValue->Read<GradientPreviewWidget::SampleFilterFunc>(filterFunc))
|
|
{
|
|
GUI->SetGradientSampleFilter(filterFunc);
|
|
}
|
|
}
|
|
else if (attrib == AZ_CRC("GradientSampler", 0xaec97010))
|
|
{
|
|
GradientSampler* sampler = nullptr;
|
|
if (attrValue->Read<GradientSampler*>(sampler) && sampler)
|
|
{
|
|
GUI->SetGradientSampler(*sampler);
|
|
}
|
|
}
|
|
else if (attrib == AZ_CRC("GradientEntity", 0xe8531817))
|
|
{
|
|
AZ::EntityId id;
|
|
if (attrValue->Read<AZ::EntityId>(id))
|
|
{
|
|
GUI->SetGradientEntity(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GradientPreviewDataWidgetHandler::ReadValueIntoGUI(size_t index, GradientPreviewDataWidget* GUI, void* value, const AZ::Uuid& propertyType)
|
|
{
|
|
(void)index;
|
|
(void)value;
|
|
(void)propertyType;
|
|
|
|
GUI->Refresh();
|
|
|
|
return false;
|
|
}
|
|
|
|
QWidget* GradientPreviewDataWidgetHandler::CreateGUI(QWidget* pParent)
|
|
{
|
|
return new GradientPreviewDataWidget(pParent);
|
|
}
|
|
|
|
void GradientPreviewDataWidgetHandler::PreventRefresh(QWidget* GUI, bool preventRefresh)
|
|
{
|
|
// Notify our preview widget to disable / enable itself during refreshes. Because it uses
|
|
// delayed, threaded logic it's possible for it to query a component during a time that the component
|
|
// is deactivated / deleted. This notification tells the preview widget to immediately cancel any
|
|
// refreshes, or restart them when it's safe again.
|
|
auto previewWidget = reinterpret_cast<GradientPreviewDataWidget*>(GUI);
|
|
previewWidget->PreventRefresh(preventRefresh);
|
|
}
|
|
|
|
void GradientPreviewDataWidgetHandler::Register()
|
|
{
|
|
using namespace AzToolsFramework;
|
|
|
|
// Property handlers are set to auto-delete by default, which means that we're handing off ownership of the pointer to the
|
|
// PropertyManagerComponent, where it will get cleaned up on system shutdown.
|
|
auto propertyHandler = aznew GradientPreviewDataWidgetHandler();
|
|
AZ_Assert(propertyHandler->AutoDelete(),
|
|
"GradientPreviewDataWidgetHandler is no longer set to auto-delete, it will leak memory.");
|
|
PropertyTypeRegistrationMessages::Bus::Broadcast(
|
|
&PropertyTypeRegistrationMessages::Bus::Events::RegisterPropertyType, propertyHandler);
|
|
}
|
|
|
|
void GradientPreviewDataWidgetHandler::Unregister()
|
|
{
|
|
// We don't need to call UnregisterPropertyType here because it's an autoDelete handler.
|
|
}
|
|
|
|
//
|
|
// GradientPreviewDataWidget
|
|
//
|
|
|
|
GradientPreviewDataWidget::GradientPreviewDataWidget(QWidget * parent)
|
|
: QWidget(parent)
|
|
{
|
|
QVBoxLayout* layout = new QVBoxLayout(this);
|
|
layout->setContentsMargins(QMargins());
|
|
layout->setAlignment(Qt::AlignHCenter);
|
|
|
|
m_preview = new GradientPreviewWidget(this);
|
|
m_preview->setFixedSize(256, 256);
|
|
layout->addWidget(m_preview);
|
|
|
|
QPushButton* popout = new QPushButton("Show Larger Preview");
|
|
layout->addWidget(popout);
|
|
connect(popout, &QPushButton::clicked, this, [this]()
|
|
{
|
|
delete m_previewWindow;
|
|
m_previewWindow = new GradientPreviewWidget;
|
|
|
|
// We need to call show() once before the resize to initialize the window frame width/height,
|
|
// so that way the resize correctly takes them into account. We then call show() a second time
|
|
// afterwards to cause the resize to take effect.
|
|
m_previewWindow->show();
|
|
m_previewWindow->resize(750, 750);
|
|
m_previewWindow->show();
|
|
Refresh();
|
|
});
|
|
|
|
//dependency monitor must be connected to an owner/observer as a target for notifications.
|
|
//generating a place holder entity
|
|
m_observerEntityStub = AZ::Entity::MakeId();
|
|
LmbrCentral::DependencyNotificationBus::Handler::BusConnect(m_observerEntityStub);
|
|
}
|
|
|
|
GradientPreviewDataWidget::~GradientPreviewDataWidget()
|
|
{
|
|
GradientPreviewRequestBus::Handler::BusDisconnect();
|
|
LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect();
|
|
m_dependencyMonitor.Reset();
|
|
delete m_previewWindow;
|
|
}
|
|
|
|
void GradientPreviewDataWidget::PreventRefresh(bool preventRefresh)
|
|
{
|
|
m_preventRefresh = preventRefresh;
|
|
if (m_preventRefresh)
|
|
{
|
|
// If we're trying to prevent refreshes, cancel any existing or pending refreshes.
|
|
CancelRefresh();
|
|
}
|
|
else
|
|
{
|
|
// If we're allowing refreshes again, start one up if it has been requested during
|
|
// the time that we weren't allowing them.
|
|
if (m_refreshQueued)
|
|
{
|
|
Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
void GradientPreviewDataWidget::SetGradientSampler(const GradientSampler& sampler)
|
|
{
|
|
m_sampler = sampler;
|
|
|
|
GradientPreviewRequestBus::Handler::BusDisconnect();
|
|
GradientPreviewRequestBus::Handler::BusConnect(sampler.m_ownerEntityId);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void GradientPreviewDataWidget::SetGradientSampleFilter(GradientPreviewWidget::SampleFilterFunc sampleFunc)
|
|
{
|
|
m_sampleFilterFunc = sampleFunc;
|
|
Refresh();
|
|
}
|
|
|
|
void GradientPreviewDataWidget::SetGradientEntity(const AZ::EntityId& id)
|
|
{
|
|
m_sampler = {};
|
|
m_sampler.m_gradientId = id;
|
|
m_sampler.m_ownerEntityId = id;
|
|
|
|
GradientPreviewRequestBus::Handler::BusDisconnect();
|
|
GradientPreviewRequestBus::Handler::BusConnect(id);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void GradientPreviewDataWidget::OnCompositionChanged()
|
|
{
|
|
Refresh();
|
|
}
|
|
|
|
void GradientPreviewDataWidget::Refresh()
|
|
{
|
|
// If we currently aren't allowing refreshes, just note that it's been requested so that
|
|
// we can start it up once refreshes are allowed again.
|
|
if (m_preventRefresh)
|
|
{
|
|
m_refreshQueued = true;
|
|
return;
|
|
}
|
|
|
|
if (!m_refreshInProgress)
|
|
{
|
|
m_refreshInProgress = true;
|
|
|
|
m_dependencyMonitor.Reset();
|
|
m_dependencyMonitor.ConnectOwner(m_observerEntityStub);
|
|
m_dependencyMonitor.ConnectDependency(m_sampler.m_gradientId);
|
|
|
|
AZ::EntityId previewEntity;
|
|
GradientPreviewContextRequestBus::BroadcastResult(previewEntity, &GradientPreviewContextRequestBus::Events::GetPreviewEntity);
|
|
m_dependencyMonitor.ConnectDependency(previewEntity);
|
|
|
|
for (GradientPreviewWidget* previewer : { m_preview, m_previewWindow })
|
|
{
|
|
if (previewer)
|
|
{
|
|
previewer->SetGradientSampler(m_sampler);
|
|
previewer->SetGradientSampleFilter(m_sampleFilterFunc);
|
|
previewer->QueueUpdate();
|
|
}
|
|
}
|
|
m_refreshInProgress = false;
|
|
m_refreshQueued = false;
|
|
}
|
|
}
|
|
|
|
AZ::EntityId GradientPreviewDataWidget::CancelRefresh()
|
|
{
|
|
bool cancelled = false;
|
|
for (GradientPreviewWidget* previewer : { m_preview, m_previewWindow })
|
|
{
|
|
if (previewer)
|
|
{
|
|
cancelled |= previewer->OnCancelRefresh();
|
|
}
|
|
}
|
|
|
|
if (cancelled)
|
|
{
|
|
return m_sampler.m_gradientId;
|
|
}
|
|
|
|
return AZ::EntityId();
|
|
}
|
|
} //namespace GradientSignal
|