Files
o3de/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentTextGraphicsWidget.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

482 lines
16 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/PlatformDef.h>
AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option")
#include <QEvent>
#include <QGraphicsItem>
#include <QGraphicsGridLayout>
#include <QGraphicsLayoutItem>
#include <QGraphicsScene>
#include <qgraphicssceneevent.h>
#include <QGraphicsWidget>
#include <QPainter>
AZ_POP_DISABLE_WARNING
#include <Components/Nodes/Comment/CommentTextGraphicsWidget.h>
#include <GraphCanvas/Components/GeometryBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Nodes/NodeUIBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/tools.h>
namespace GraphCanvas
{
//////////////////////////////////
// CommentNodeTextGraphicsWidget
//////////////////////////////////
CommentTextGraphicsWidget::CommentTextGraphicsWidget(const AZ::EntityId& targetId)
: m_commentMode(CommentMode::Unknown)
, m_editable(false)
, m_layoutLock(false)
, m_displayLabel(nullptr)
, m_textEdit(nullptr)
, m_proxyWidget(nullptr)
, m_pressed(false)
, m_entityId(targetId)
{
setFlag(ItemIsMovable, false);
m_displayLabel = aznew GraphCanvasLabel();
m_displayLabel->SetAllowNewlines(true);
m_layout = new QGraphicsLinearLayout(Qt::Vertical);
m_layout->setSpacing(0);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setInstantInvalidatePropagation(true);
m_layout->addItem(m_displayLabel);
setLayout(m_layout);
setData(GraphicsItemName, QStringLiteral("Comment/%1").arg(static_cast<AZ::u64>(GetEntityId()), 16, 16, QChar('0')));
SetCommentMode(CommentMode::Comment);
}
void CommentTextGraphicsWidget::Activate()
{
CommentUIRequestBus::Handler::BusConnect(GetEntityId());
CommentLayoutRequestBus::Handler::BusConnect(GetEntityId());
StyleNotificationBus::Handler::BusConnect(GetEntityId());
UpdateLayout();
}
void CommentTextGraphicsWidget::Deactivate()
{
StyleNotificationBus::Handler::BusDisconnect();
CommentLayoutRequestBus::Handler::BusDisconnect();
CommentUIRequestBus::Handler::BusDisconnect();
}
void CommentTextGraphicsWidget::OnAddedToScene()
{
UpdateSizing();
}
void CommentTextGraphicsWidget::SetStyle(const AZStd::string& style)
{
if (m_style != style)
{
m_style = style;
OnStyleChanged();
}
}
void CommentTextGraphicsWidget::UpdateLayout()
{
if (m_layoutLock)
{
return;
}
QGraphicsScene* graphicsScene = nullptr;
AZ::EntityId sceneId;
SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);
SceneRequestBus::EventResult(graphicsScene, sceneId, &SceneRequests::AsQGraphicsScene);
prepareGeometryChange();
for (int i = m_layout->count() - 1; i >= 0; --i)
{
QGraphicsLayoutItem* layoutItem = m_layout->itemAt(i);
m_layout->removeAt(i);
layoutItem->setParentLayoutItem(nullptr);
if (graphicsScene)
{
graphicsScene->removeItem(layoutItem->graphicsItem());
}
}
if (m_editable)
{
// Adjust the size of the editable widget to match the label
m_layout->addItem(m_proxyWidget);
UpdateSizing();
}
else
{
m_layout->addItem(m_displayLabel);
}
RefreshDisplay();
}
void CommentTextGraphicsWidget::UpdateStyles()
{
Styling::StyleHelper overallStyle(GetEntityId());
qreal margin = overallStyle.GetAttribute(Styling::Attribute::Margin, 0.0);
m_layout->setContentsMargins(margin, margin, margin, margin);
m_displayLabel->SetStyle(GetEntityId(), m_style.c_str());
const Styling::StyleHelper& styleHelper = m_displayLabel->GetStyleHelper();
QPen border = styleHelper.GetBorder();
// We construct a stylesheet for the Qt widget based on our calculated style
QStringList fields;
fields.push_back("background-color: rgba(0,0,0,0)");
fields.push_back(QString("border-width: %1").arg(border.width()));
switch (border.style())
{
case Qt::PenStyle::SolidLine:
fields.push_back("border-style: solid");
break;
case Qt::PenStyle::DashLine:
fields.push_back("border-style: dashed");
break;
case Qt::PenStyle::DotLine:
fields.push_back("border-style: dotted");
break;
default:
fields.push_back("border-style: none");
}
fields.push_back(QString("border-color: rgba(%1,%2,%3,%4)").arg(border.color().red()).arg(border.color().green()).arg(border.color().blue()).arg(border.color().alpha()));
fields.push_back(QString("border-radius: %1").arg(styleHelper.GetAttribute(Styling::Attribute::BorderRadius, 0)));
fields.push_back("margin: 0");
fields.push_back("padding: 0");
fields.push_back(styleHelper.GetFontStyleSheet());
if (m_textEdit)
{
m_textEdit->setStyleSheet(fields.join("; ").toUtf8().data());
if (styleHelper.HasTextAlignment())
{
m_textEdit->setAlignment(styleHelper.GetTextAlignment(m_textEdit->alignment()));
}
}
UpdateSizing();
}
void CommentTextGraphicsWidget::RefreshDisplay()
{
updateGeometry();
m_layout->invalidate();
update();
}
void CommentTextGraphicsWidget::SetComment(const AZStd::string& comment)
{
m_commentText = comment;
if (!comment.empty())
{
m_displayLabel->SetLabel(comment);
if (m_textEdit)
{
m_textEdit->setPlainText(comment.c_str());
}
}
else
{
// Hack to force the minimum height based on the style's font/size
m_displayLabel->SetLabel(" ");
if (m_textEdit)
{
m_textEdit->setPlainText(" ");
}
}
UpdateSizing();
}
AZStd::string CommentTextGraphicsWidget::GetComment() const
{
return m_commentText;
}
Styling::StyleHelper& CommentTextGraphicsWidget::GetStyleHelper()
{
return m_displayLabel->GetStyleHelper();
}
const Styling::StyleHelper& CommentTextGraphicsWidget::GetStyleHelper() const
{
return m_displayLabel->GetStyleHelper();
}
void CommentTextGraphicsWidget::SetCommentMode(CommentMode commentMode)
{
if (m_commentMode != commentMode)
{
m_commentMode = commentMode;
UpdateSizePolicies();
}
}
CommentMode CommentTextGraphicsWidget::GetCommentMode() const
{
return m_commentMode;
}
void CommentTextGraphicsWidget::SetEditable(bool editable)
{
if (m_editable != editable)
{
m_editable = editable;
if (!m_editable)
{
SubmitValue();
}
(m_editable ? SetupProxyWidget() : CleanupProxyWidget());
UpdateLayout();
if (m_editable)
{
AZ::EntityId sceneId;
SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);
SceneNotificationBus::Event(sceneId, &SceneNotifications::OnNodeIsBeingEdited, true);
CommentNotificationBus::Event(GetEntityId(), &CommentNotifications::OnEditBegin);
UpdateSizing();
StyledEntityRequestBus::Event(GetEntityId(), &StyledEntityRequests::AddSelectorState, Styling::States::Editing);
m_textEdit->selectAll();
SceneMemberUIRequestBus::Event(GetEntityId(), &SceneMemberUIRequests::SetSelected, true);
}
else
{
AZ::EntityId sceneId;
SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);
SceneNotificationBus::Event(sceneId, &SceneNotifications::OnNodeIsBeingEdited, false);
CommentNotificationBus::Event(GetEntityId(), &CommentNotifications::OnEditEnd);
StyledEntityRequestBus::Event(GetEntityId(), &StyledEntityRequests::RemoveSelectorState, Styling::States::Editing);
m_layoutLock = false;
}
OnStyleChanged();
}
}
QGraphicsLayoutItem* CommentTextGraphicsWidget::GetGraphicsLayoutItem()
{
return this;
}
void CommentTextGraphicsWidget::OnStyleChanged()
{
UpdateStyles();
RefreshDisplay();
}
void CommentTextGraphicsWidget::UpdateSizing()
{
AZStd::string value = (m_textEdit ? AZStd::string(m_textEdit->toPlainText().toUtf8().data()) : m_commentText);
m_displayLabel->SetLabel(value);
prepareGeometryChange();
if (m_textEdit)
{
QSizeF oldSize = m_textEdit->minimumSize();
// As we update the label with the new contents, adjust the editable widget size to match
if (m_commentMode == CommentMode::Comment)
{
m_textEdit->setMinimumSize(m_displayLabel->preferredSize().toSize());
m_textEdit->setMaximumSize(m_displayLabel->preferredSize().toSize());
}
else if (m_commentMode == CommentMode::BlockComment)
{
QSizeF preferredSize = m_displayLabel->preferredSize();
QRectF displaySize = m_displayLabel->GetDisplayedSize();
displaySize.setHeight(preferredSize.height());
if (displaySize.width() == 0)
{
m_textEdit->setMinimumHeight(aznumeric_cast<int>(preferredSize.height()));
m_textEdit->setMaximumHeight(aznumeric_cast<int>(preferredSize.height()));
}
else
{
m_textEdit->setMinimumSize(displaySize.size().toSize());
m_textEdit->setMaximumSize(displaySize.size().toSize());
}
}
QSizeF newSize = m_textEdit->minimumSize();
if (oldSize != newSize)
{
CommentNotificationBus::Event(GetEntityId(), &CommentNotifications::OnCommentSizeChanged, oldSize, newSize);
}
}
updateGeometry();
}
void CommentTextGraphicsWidget::SubmitValue()
{
if (m_textEdit)
{
m_commentText = m_textEdit->toPlainText().toUtf8().data();
}
CommentRequestBus::Event(GetEntityId(), &CommentRequests::SetComment, m_commentText);
CommentNotificationBus::Event(GetEntityId(), &CommentNotifications::OnCommentChanged, m_commentText);
UpdateSizing();
}
bool CommentTextGraphicsWidget::sceneEventFilter(QGraphicsItem*, QEvent* event)
{
bool consumeEvent = event->isAccepted();
switch (event->type())
{
case QEvent::GraphicsSceneMouseDoubleClick:
consumeEvent = true;
// Need to queue this since if we change out display in the middle of processing input
// Things get sad.
QTimer::singleShot(0, [this]() { this->SetEditable(true); });
break;
}
return consumeEvent;
}
void CommentTextGraphicsWidget::UpdateSizePolicies()
{
prepareGeometryChange();
switch (m_commentMode)
{
case CommentMode::BlockComment:
if (m_textEdit)
{
m_textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_textEdit->setWordWrapMode(QTextOption::WrapMode::NoWrap);
m_proxyWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
}
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_layout->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_displayLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_displayLabel->SetElide(true);
m_displayLabel->SetWrap(false);
m_displayLabel->SetWrapMode(GraphCanvasLabel::WrapMode::BoundingWidth);
break;
case CommentMode::Comment:
if (m_textEdit)
{
m_textEdit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_textEdit->setWordWrapMode(QTextOption::WrapMode::WordWrap);
m_proxyWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
}
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_layout->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_displayLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_displayLabel->SetElide(false);
m_displayLabel->SetWrap(true);
m_displayLabel->SetWrapMode(GraphCanvasLabel::WrapMode::MaximumWidth);
break;
default:
AZ_Warning("Graph Canvas", false, "Unhandled Comment Mode: %i", m_commentMode);
}
updateGeometry();
}
void CommentTextGraphicsWidget::SetupProxyWidget()
{
if (!m_textEdit)
{
m_proxyWidget = new QGraphicsProxyWidget();
m_proxyWidget->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
m_textEdit = aznew Internal::FocusableTextEdit();
m_textEdit->setProperty("HasNoWindowDecorations", true);
m_textEdit->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
m_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
m_textEdit->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents);
m_textEdit->setEnabled(true);
m_proxyWidget->setWidget(m_textEdit);
UpdateSizePolicies();
m_textEdit->setPlainText(m_commentText.c_str());
QTimer::singleShot(0, [&]()
{
m_textEdit->setFocus(Qt::FocusReason::MouseFocusReason);
m_proxyWidget->setFocus(Qt::FocusReason::MouseFocusReason);
});
QObject::connect(m_textEdit, &Internal::FocusableTextEdit::textChanged, [this]() { this->UpdateSizing(); });
QObject::connect(m_textEdit, &Internal::FocusableTextEdit::OnFocusIn, [this]() { this->m_layoutLock = true; });
QObject::connect(m_textEdit, &Internal::FocusableTextEdit::OnFocusOut, [this]() { this->SubmitValue(); this->m_layoutLock = false; this->SetEditable(false); });
QObject::connect(m_textEdit, &Internal::FocusableTextEdit::EnterPressed, [this]()
{
QTimer::singleShot(0, [this]()
{
this->SubmitValue();
this->m_layoutLock = false;
this->SetEditable(false);
});
});
}
}
void CommentTextGraphicsWidget::CleanupProxyWidget()
{
if (m_textEdit)
{
delete m_textEdit; // NB: this implicitly deletes m_proxy widget
m_textEdit = nullptr;
m_proxyWidget = nullptr;
}
}
#include <Source/Components/Nodes/Comment/moc_CommentTextGraphicsWidget.cpp>
}