123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- /*
- * 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([[maybe_unused]] QPaintEvent* paintEvent)
- {
- auto colors = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC_CE("LUA Editor Text Settings"), 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);
- }
- }
|