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

389 lines
15 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 "LUAEditorFoldingWidget.hxx"
#include "LUAEditorBlockState.h"
#include "LUAEditorPlainTextEdit.hxx"
#include "LUAEditorStyleMessages.h"
#include <Source/LUA/moc_LUAEditorFoldingWidget.cpp>
#include <QTextBlock>
#include <QStyleOption>
#include <QStyle>
#include <QPainter>
namespace LUAEditor
{
FoldingWidget::FoldingWidget(QWidget* pParent)
: QWidget(pParent)
{
setEnabled(false);
}
FoldingWidget::~FoldingWidget()
{
}
void FoldingWidget::paintEvent(QPaintEvent* paintEvent)
{
paintEvent;
auto colors = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC("LUA Editor Text Settings", 0xb6e15565), AZ::UserSettings::CT_GLOBAL);
auto cursor = m_textEdit->textCursor();
auto selectedBlock = m_textEdit->document()->findBlock(cursor.position());
int startSelectedFold = 0;
int endSelectedFold = 0;
if (selectedBlock.isValid()) //detect if this block closes a fold
{
QTBlockState blockState;
blockState.m_qtBlockState = selectedBlock.userState();
auto prevBlock = selectedBlock.previous();
if (!blockState.m_blockState.m_uninitialized && prevBlock.isValid())
{
QTBlockState prevBlockState;
prevBlockState.m_qtBlockState = prevBlock.userState();
if (blockState.m_blockState.m_foldLevel < prevBlockState.m_blockState.m_foldLevel)
{
selectedBlock = prevBlock;
}
}
}
if (selectedBlock.isValid())
{
startSelectedFold = selectedBlock.blockNumber();
endSelectedFold = selectedBlock.blockNumber() + 1;
QTBlockState blockState;
blockState.m_qtBlockState = selectedBlock.userState();
if (!blockState.m_blockState.m_uninitialized && blockState.m_blockState.m_foldLevel > 0)
{
auto startBlock = selectedBlock;
while (startBlock.isValid())
{
auto prevBlock = startBlock.previous();
QTBlockState prevBlockState;
prevBlockState.m_qtBlockState = prevBlock.userState();
if (prevBlockState.m_blockState.m_foldLevel >= blockState.m_blockState.m_foldLevel)
{
startBlock = prevBlock;
}
else
{
break;
}
}
startSelectedFold = startBlock.blockNumber();
auto endBlock = selectedBlock;
while (endBlock.isValid())
{
auto nextBlock = endBlock.next();
QTBlockState nextBlockState;
nextBlockState.m_qtBlockState = nextBlock.userState();
if (nextBlockState.m_blockState.m_foldLevel >= blockState.m_blockState.m_foldLevel)
{
endBlock = nextBlock;
}
else //less than, which is included in this fold
{
endBlock = nextBlock;
break;
}
}
endSelectedFold = endBlock.blockNumber() + 1;
}
}
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
AZ::u32 lastFoldLevel = 0;
bool firstBlock = true;
m_textEdit->ForEachVisibleBlock([&](const QTextBlock& block, const QRectF& blockRect)
{
QTBlockState blockState;
blockState.m_qtBlockState = block.userState();
if (!blockState.m_blockState.m_uninitialized)
{
if (firstBlock)
{
lastFoldLevel = 0;
auto prevBlock = block.previous();
if (prevBlock.isValid())
{
QTBlockState prevBlockState;
prevBlockState.m_qtBlockState = prevBlock.userState();
lastFoldLevel = prevBlockState.m_blockState.m_foldLevel;
}
firstBlock = false;
}
auto centerToBorder = (m_singleSize - 2 * c_borderSize) / 2;
QRect drawRect = blockRect.toRect();
drawRect.setLeft(0);
drawRect.setRight(m_singleSize);
auto oldPen = p.pen();
auto oldBrush = p.brush();
if (block.blockNumber() >= startSelectedFold && block.blockNumber() < endSelectedFold)
{
p.setPen(colors->GetFoldingSelectedColor());
}
else
{
p.setPen(colors->GetFoldingColor());
}
p.setBrush(QBrush(Qt::BrushStyle::NoBrush));
if (blockState.m_blockState.m_foldLevel > lastFoldLevel)
{
auto centerEdge = AZStd::max(m_singleSize / 10, 2); //for center lines, 10% of our size
auto center = drawRect.center();
QRect square(center.x() - centerToBorder, center.y() - centerToBorder, 2 * centerToBorder, 2 * centerToBorder);
p.drawRect(square);
p.drawLine(square.left() + centerEdge, center.y(), square.right() - centerEdge + 1, center.y());
if (blockState.m_blockState.m_folded)
{
p.drawLine(center.x(), square.top() + centerEdge, center.x(), square.bottom() - centerEdge + 1);
}
p.drawLine(center.x(), square.bottom() + 1, center.x(), drawRect.bottom());
if (blockState.m_blockState.m_foldLevel > 1)
{
if (block.blockNumber() == startSelectedFold)
{
p.setPen(colors->GetFoldingColor());
}
p.drawLine(center.x(), drawRect.top(), center.x(), square.top() - 1);
}
}
else if (blockState.m_blockState.m_foldLevel < lastFoldLevel)
{
auto center = drawRect.center();
p.drawLine(center.x(), drawRect.top(), center.x(), center.y());
p.drawLine(center.x(), center.y(), drawRect.right(), center.y());
if (blockState.m_blockState.m_foldLevel > 0)
{
if (block.blockNumber() == endSelectedFold - 1)
{
p.setPen(colors->GetFoldingColor());
}
p.drawLine(center.x(), center.y(), center.x(), drawRect.bottom());
}
}
else if (blockState.m_blockState.m_foldLevel > 0)
{
auto center = drawRect.center();
p.drawLine(center.x(), drawRect.top(), center.x(), drawRect.bottom());
}
lastFoldLevel = blockState.m_blockState.m_foldLevel;
p.setPen(oldPen);
p.setBrush(oldBrush);
}
});
}
void FoldingWidget::mouseReleaseEvent(QMouseEvent* event)
{
auto mousePos = event->localPos();
m_textEdit->ForEachVisibleBlock([&](QTextBlock& blockClicked, const QRectF& blockRect)
{
if (mousePos.y() >= blockRect.top() && mousePos.y() <= blockRect.bottom())
{
auto prevBlock = blockClicked.previous();
QTBlockState state;
state.m_qtBlockState = blockClicked.userState();
AZ::u32 prevFoldLevel = 0;
if (prevBlock.isValid())
{
QTBlockState prevBlockState;
prevBlockState.m_qtBlockState = prevBlock.userState();
prevFoldLevel = prevBlockState.m_blockState.m_foldLevel;
}
if (!state.m_blockState.m_uninitialized && state.m_blockState.m_foldLevel > prevFoldLevel)
{
state.m_blockState.m_folded = 1 - state.m_blockState.m_folded;
blockClicked.setUserState(state.m_qtBlockState);
int startDirty = blockClicked.position();
int dirtyLength = (blockClicked.position() + blockClicked.length()) - startDirty;
auto nextBlock = blockClicked.next();
QTBlockState nextState;
nextState.m_qtBlockState = nextBlock.userState();
bool currentChildFolded = false;
AZ::u32 currentChildFoldLevel = state.m_blockState.m_foldLevel;
while (nextBlock.isValid() && nextState.m_blockState.m_foldLevel >= state.m_blockState.m_foldLevel)
{
if (state.m_blockState.m_folded)
{
nextBlock.setVisible(false);
}
else // we are unfolding, need to preserve any child folds that were already folded before we were
{
if (!currentChildFolded)
{
nextBlock.setVisible(true);
if (nextState.m_blockState.m_foldLevel > currentChildFoldLevel)
{
currentChildFoldLevel = nextState.m_blockState.m_foldLevel;
if (nextState.m_blockState.m_folded)
{
currentChildFolded = true;
}
}
}
else
{
bool visible = false;
if (nextState.m_blockState.m_foldLevel < currentChildFoldLevel)
{
currentChildFolded = false;
visible = true;
}
nextBlock.setVisible(visible);
}
}
dirtyLength = (nextBlock.position() + nextBlock.length()) - startDirty;
nextBlock = nextBlock.next();
nextState.m_qtBlockState = nextBlock.userState();
}
update();
m_textEdit->document()->markContentsDirty(startDirty, dirtyLength);
TextBlockFoldingChanged();
}
}
});
event->accept();
}
void FoldingWidget::OnContentChanged(int from, int, int charsAdded)
{
auto startBlock = m_textEdit->document()->findBlock(from);
if (!startBlock.isValid())
{
return;
}
auto endBlock = m_textEdit->document()->findBlock(from + charsAdded);
if (!endBlock.isValid())
{
endBlock = startBlock;
}
auto prevBlock = startBlock.previous();
bool atLeastOne = false;
while (prevBlock.isValid() && !prevBlock.isVisible())
{
startBlock = prevBlock;
prevBlock = prevBlock.previous();
atLeastOne = true;
}
if (atLeastOne) //need to grab the opening fold
{
startBlock = prevBlock;
}
auto nextBlock = endBlock.next();
while (nextBlock.isValid() && !nextBlock.isVisible())
{
endBlock = nextBlock;
nextBlock = nextBlock.next();
}
int startDirty = startBlock.position();
int dirtyLength = (endBlock.position() + endBlock.length()) - startDirty;
while (startBlock.isValid() && startBlock.blockNumber() <= endBlock.blockNumber())
{
QTBlockState state;
state.m_qtBlockState = startBlock.userState();
if (state.m_blockState.m_folded)
{
state.m_blockState.m_folded = 0;
startBlock.setUserState(state.m_qtBlockState);
}
startBlock.setVisible(true);
startBlock = startBlock.next();
}
update();
m_textEdit->document()->markContentsDirty(startDirty, dirtyLength);
TextBlockFoldingChanged();
}
void FoldingWidget::FoldAll()
{
AZ::u32 lastFoldLevel = 0;
for (auto block = m_textEdit->document()->begin(); block != m_textEdit->document()->end(); block = block.next())
{
block.setVisible(true);
QTBlockState state;
state.m_qtBlockState = block.userState();
if (state.m_blockState.m_foldLevel > lastFoldLevel)
{
state.m_blockState.m_folded = 1;
block.setUserState(state.m_qtBlockState);
if (lastFoldLevel != 0)
{
block.setVisible(false);
}
}
else if (state.m_blockState.m_foldLevel > 0)
{
block.setVisible(false);
}
lastFoldLevel = state.m_blockState.m_foldLevel;
}
update();
m_textEdit->document()->markContentsDirty(0, m_textEdit->document()->characterCount());
TextBlockFoldingChanged();
}
void FoldingWidget::UnfoldAll()
{
for (auto block = m_textEdit->document()->begin(); block != m_textEdit->document()->end(); block = block.next())
{
block.setVisible(true);
QTBlockState state;
state.m_qtBlockState = block.userState();
state.m_blockState.m_folded = 0;
block.setUserState(state.m_qtBlockState);
}
update();
m_textEdit->document()->markContentsDirty(0, m_textEdit->document()->characterCount());
TextBlockFoldingChanged();
}
void FoldingWidget::SetFont(QFont font)
{
QFontMetrics metrics(font);
m_singleSize = metrics.height();
setFixedWidth(m_singleSize);
}
}