Files
o3de/Code/Tools/Standalone/Source/LUA/LUAEditorBreakpointWidget.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

300 lines
9.8 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/std/string/conversions.h>
#include "LUAEditorBreakpointWidget.hxx"
#include "LUAEditorPlainTextEdit.hxx"
#include "LUAEditorStyleMessages.h"
#include <Source/LUA/moc_LUAEditorBreakpointWidget.cpp>
#include <QTextBlockFormat>
#include <QTextBlock>
#include <QTextBlockUserData>
#include <QPainter>
namespace LUAEditor
{
namespace
{
//this used to detect deleted lines, doesnt work for last line though, but that can be detected elsewhere.
class OriginalLineNumber
: public QTextBlockUserData
{
public:
AZ_CLASS_ALLOCATOR(OriginalLineNumber, AZ::SystemAllocator, 0);
OriginalLineNumber(int lineNumber, AZStd::function<void(int)> callback)
: m_Callback(callback)
, m_LineNumber(lineNumber) {}
virtual ~OriginalLineNumber()
{
if (m_Callback)
{
m_Callback(m_LineNumber);
}
}
int GetLineNumber() const { return m_LineNumber; }
void CancelLineDelete() { m_Callback.clear(); }
private:
AZStd::function<void(int)> m_Callback;
int m_LineNumber;
};
}
LUAEditorBreakpointWidget::LUAEditorBreakpointWidget(QWidget* pParent)
: QWidget(pParent)
, m_textEdit(nullptr)
, m_currentExecLine(-1)
{
setEnabled(false);
}
LUAEditorBreakpointWidget::~LUAEditorBreakpointWidget()
{
}
void LUAEditorBreakpointWidget::PreDestruction()
{
ClearBreakpoints();
m_textEdit = nullptr;
OnToggleBreakpoint.clear();
OnBreakpointLineMoved.clear();
OnBreakpointLineDeleted.clear();
}
void LUAEditorBreakpointWidget::paintEvent(QPaintEvent* paintEvent)
{
paintEvent;
QPainter p(this);
auto colors = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC("LUA Editor Text Settings", 0xb6e15565), AZ::UserSettings::CT_GLOBAL);
auto oldPen = p.pen();
auto oldBrush = p.brush();
p.setFont(m_font);
if (isEnabled())
{
p.fillRect(geometry(), colors->GetBreakpointFocusedBackgroundColor());
}
else
{
p.fillRect(geometry(), colors->GetBreakpointUnfocusedBackgroundColor());
}
QFontMetrics metrics(m_font);
int avgCharWidth = metrics.averageCharWidth();
m_textEdit->ForEachVisibleBlock([&](const QTextBlock& block, const QRectF& blockRect)
{
int lineNum = block.blockNumber() + 1; // offset by one because line number starts from 1
AZStd::string lineNumStr;
AZStd::to_string(lineNumStr, lineNum);
QRectF drawRect = blockRect;
drawRect.setLeft(c_borderSize);
drawRect.setRight(c_borderSize + m_numDigits * avgCharWidth);
p.setPen(colors->GetTextColor());
p.drawText(drawRect.toRect(), Qt::AlignRight | Qt::AlignBottom, lineNumStr.c_str());
{
auto centerY = (drawRect.top() + metrics.leading() + drawRect.bottom()) / 2;
auto centerX = (2 * drawRect.right() + avgCharWidth + c_borderSize) / 2;
drawRect.setTop(centerY - (avgCharWidth / 2));
drawRect.setBottom(centerY + (avgCharWidth / 2));
drawRect.setLeft(centerX - (avgCharWidth / 2));
drawRect.setRight(centerX + (avgCharWidth / 2));
}
//breakpoint red dot
if (m_breakpoints.find(lineNum) != m_breakpoints.end())
{
p.setPen(QColor::fromRgb(255, 0, 0));
p.setBrush(QBrush(QColor::fromRgb(255, 0, 0)));
p.drawEllipse(drawRect);
}
//yellow triangle for currently executing line
if (m_currentExecLine == lineNum)
{
const QPointF marker[] = {
{drawRect.right(), drawRect.center().y()},
{drawRect.center().x(), drawRect.top() + 1},
{drawRect.center().x(), drawRect.bottom() - 1}
};
p.setPen(QColor::fromRgb(255, 255, 0));
p.setBrush(QBrush(QColor::fromRgb(255, 255, 0)));
p.drawPolygon(marker, sizeof(marker) / sizeof(QPointF));
}
});
p.setPen(oldPen);
p.setBrush(oldBrush);
}
void LUAEditorBreakpointWidget::AddBreakpoint(int lineNumber)
{
auto block = m_textEdit->document()->findBlockByNumber(lineNumber);
if (block.isValid())
{
if (block.userData() != nullptr)
{
((OriginalLineNumber*)block.userData())->CancelLineDelete();
}
block.setUserData(aznew OriginalLineNumber(lineNumber, [&](int lineNumber){ m_DeletedBreakpoints.push_back(lineNumber); }));
m_breakpoints.insert(lineNumber);
update();
}
}
void LUAEditorBreakpointWidget::RemoveBreakpoint(int lineNumber)
{
auto numRemoved = m_breakpoints.erase(lineNumber);
if (numRemoved != 0)
{
auto block = m_textEdit->document()->findBlockByNumber(lineNumber);
if (block.isValid())
{
if (block.userData() != nullptr)
{
((OriginalLineNumber*)block.userData())->CancelLineDelete();
}
block.setUserData(nullptr);
}
}
update();
}
bool LUAEditorBreakpointWidget::HasBreakpoint(int lineNumber) const
{
return m_breakpoints.find(lineNumber) != m_breakpoints.end();
}
void LUAEditorBreakpointWidget::ClearBreakpoints()
{
for (auto& breakpoint : m_breakpoints)
{
auto block = m_textEdit->document()->findBlockByNumber(breakpoint);
if (block.isValid())
{
if (block.userData() != nullptr)
{
((OriginalLineNumber*)block.userData())->CancelLineDelete();
}
block.setUserData(nullptr);
}
}
m_breakpoints.clear();
update();
}
void LUAEditorBreakpointWidget::mouseReleaseEvent(QMouseEvent* event)
{
if (OnToggleBreakpoint)
{
auto mousePos = event->localPos();
m_textEdit->ForEachVisibleBlock([&](const QTextBlock& block, const QRectF& blockRect)
{
if (mousePos.y() >= blockRect.top() && mousePos.y() <= blockRect.bottom())
{
OnToggleBreakpoint(block.blockNumber() + 1); // offset by one because line number starts from 1
}
});
event->accept();
}
}
void LUAEditorBreakpointWidget::OnBlockCountChange()
{
if (!m_textEdit)
{
return;
}
AZStd::vector<AZStd::pair<int, int> > movedBreakpoints;
for (auto block = m_textEdit->document()->begin(); block != m_textEdit->document()->end(); block = block.next())
{
if (block.userData() != nullptr)
{
if (block.blockNumber() != ((OriginalLineNumber*)block.userData())->GetLineNumber())
{
movedBreakpoints.push_back({ ((OriginalLineNumber*)block.userData())->GetLineNumber(), block.blockNumber() });
}
}
if (block.userData() != nullptr)
{
((OriginalLineNumber*)block.userData())->CancelLineDelete();
}
block.setUserData(nullptr);
}
for (auto& breakpoint : m_breakpoints)
{
auto block = m_textEdit->document()->findBlockByNumber(breakpoint);
if (block.isValid())
{
block.setUserData(aznew OriginalLineNumber(block.blockNumber(), [&](int lineNumber){ m_DeletedBreakpoints.push_back(lineNumber); }));
}
}
for (auto& moves : movedBreakpoints)
{
OnBreakpointLineMoved(moves.first, moves.second);
}
for (auto& deletes : m_DeletedBreakpoints)
{
OnBreakpointLineDeleted(deletes);
}
m_DeletedBreakpoints.clear();
UpdateSize();
update();
}
void LUAEditorBreakpointWidget::OnCharsRemoved(int position, int charsRemoved)
{
if (!m_textEdit)
{
return;
}
if (charsRemoved <= 0)
{
return;
}
auto block = m_textEdit->document()->findBlock(position);
if (block.userData() && block.blockNumber() != ((OriginalLineNumber*)block.userData())->GetLineNumber())
{
m_DeletedBreakpoints.push_back(((OriginalLineNumber*)block.userData())->GetLineNumber());
((OriginalLineNumber*)block.userData())->CancelLineDelete();
block.setUserData(nullptr);
}
}
void LUAEditorBreakpointWidget::SetFont(QFont font)
{
m_font = font;
UpdateSize();
}
void LUAEditorBreakpointWidget::UpdateSize()
{
QFontMetrics metrics(m_font);
auto blockCount = m_textEdit->document()->blockCount();
m_numDigits = 0;
while (blockCount > 0)
{
++m_numDigits;
blockCount /= 10;
}
setFixedWidth(metrics.averageCharWidth() * (m_numDigits + 1) + 2 * c_borderSize); //+1 for the breakpoint
}
}