diff options
author | Jarek Kobus <jaroslaw.kobus@qt.io> | 2017-10-11 14:14:10 +0200 |
---|---|---|
committer | Jarek Kobus <jaroslaw.kobus@qt.io> | 2017-11-01 09:13:17 +0000 |
commit | 96aeabd0a64a409a2587bac24ba3b536cce7f5ed (patch) | |
tree | 1e21b7b334972b432da0f2d57244af08f6063d8e /src/plugins | |
parent | 8a2adfef6cb64d1cd3a31da8dd61c23867afaf5d (diff) |
Add folding to Side-by-side diff editor
Add folding for files and chunks.
Change-Id: I76476351e88f0b3e71e3cccbca0fa17b02c26226
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
Diffstat (limited to 'src/plugins')
7 files changed, 160 insertions, 9 deletions
diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.cpp b/src/plugins/diffeditor/selectabletexteditorwidget.cpp index 9ae9deace6..68158fdf44 100644 --- a/src/plugins/diffeditor/selectabletexteditorwidget.cpp +++ b/src/plugins/diffeditor/selectabletexteditorwidget.cpp @@ -25,6 +25,7 @@ #include "selectabletexteditorwidget.h" #include <texteditor/textdocument.h> +#include <texteditor/textdocumentlayout.h> #include <QPainter> #include <QTextBlock> @@ -99,6 +100,12 @@ void SelectableTextEditorWidget::setSelections(const QMap<int, QList<DiffSelecti } } +void SelectableTextEditorWidget::setFoldingIndent(const QTextBlock &block, int indent) +{ + if (TextEditor::TextBlockUserData *userData = TextEditor::TextDocumentLayout::userData(block)) + userData->setFoldingIndent(indent); +} + void SelectableTextEditorWidget::paintBlock(QPainter *painter, const QTextBlock &block, const QPointF &offset, diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.h b/src/plugins/diffeditor/selectabletexteditorwidget.h index 2d04c7b39e..36a90adf49 100644 --- a/src/plugins/diffeditor/selectabletexteditorwidget.h +++ b/src/plugins/diffeditor/selectabletexteditorwidget.h @@ -50,6 +50,8 @@ public: ~SelectableTextEditorWidget() override; void setSelections(const QMap<int, QList<DiffSelection> > &selections); + static void setFoldingIndent(const QTextBlock &block, int indent); + private: void paintBlock(QPainter *painter, const QTextBlock &block, diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp index 03768a6a16..d69cf11a47 100644 --- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp +++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp @@ -86,6 +86,8 @@ public: void saveState(); void restoreState(); + void setFolded(int blockNumber, bool folded); + void setDisplaySettings(const DisplaySettings &ds) override; signals: @@ -95,6 +97,7 @@ signals: void contextMenuRequested(QMenu *menu, int diffFileIndex, int chunkIndex); + void foldChanged(int blockNumber, bool folded); protected: int extraAreaWidth(int *markWidthPtr = 0) const override { @@ -113,6 +116,14 @@ protected: void contextMenuEvent(QContextMenuEvent *e) override; void paintEvent(QPaintEvent *e) override; void scrollContentsBy(int dx, int dy) override; + void customDrawCollapsedBlockPopup(QPainter &painter, + const QTextBlock &block, + QPointF offset, + const QRect &clip); + void drawCollapsedBlockPopup(QPainter &painter, + const QTextBlock &block, + QPointF offset, + const QRect &clip); private: void paintSeparator(QPainter &painter, QColor &color, const QString &text, @@ -134,6 +145,11 @@ private: QColor m_chunkLineForeground; QColor m_textForeground; QByteArray m_state; + + QTextBlock m_drawCollapsedBlock; + QPointF m_drawCollapsedOffset; + QRect m_drawCollapsedClip; + }; SideDiffEditorWidget::SideDiffEditorWidget(QWidget *parent) @@ -143,7 +159,6 @@ SideDiffEditorWidget::SideDiffEditorWidget(QWidget *parent) settings.m_textWrapping = false; settings.m_displayLineNumbers = true; settings.m_highlightCurrentLine = false; - settings.m_displayFoldingMarkers = true; settings.m_markTextChanges = false; settings.m_highlightBlocks = false; SelectableTextEditorWidget::setDisplaySettings(settings); @@ -156,6 +171,12 @@ SideDiffEditorWidget::SideDiffEditorWidget(QWidget *parent) else ToolTip::hide(); }); + + TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout()); + if (documentLayout) + connect(documentLayout, &TextDocumentLayout::foldChanged, + this, &SideDiffEditorWidget::foldChanged); + setCodeFoldingSupported(true); } void SideDiffEditorWidget::saveState() @@ -175,10 +196,27 @@ void SideDiffEditorWidget::restoreState() m_state.clear(); } +void SideDiffEditorWidget::setFolded(int blockNumber, bool folded) +{ + QTextBlock block = document()->findBlockByNumber(blockNumber); + if (!block.isValid()) + return; + + if (TextDocumentLayout::isFolded(block) == folded) + return; + + TextDocumentLayout::doFoldOrUnfold(block, !folded); + + TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout()); + documentLayout->requestUpdate(); + documentLayout->emitDocumentSizeChanged(); +} + void SideDiffEditorWidget::setDisplaySettings(const DisplaySettings &ds) { DisplaySettings settings = displaySettings(); settings.m_visualizeWhitespace = ds.m_visualizeWhitespace; + settings.m_displayFoldingMarkers = ds.m_displayFoldingMarkers; SelectableTextEditorWidget::setDisplaySettings(settings); } @@ -481,6 +519,91 @@ void SideDiffEditorWidget::paintEvent(QPaintEvent *e) } currentBlock = currentBlock.next(); } + + if (m_drawCollapsedBlock.isValid()) { + // draw it now + customDrawCollapsedBlockPopup(painter, + m_drawCollapsedBlock, + m_drawCollapsedOffset, + m_drawCollapsedClip); + // reset the data for the drawing + m_drawCollapsedBlock = QTextBlock(); + } +} + +void SideDiffEditorWidget::customDrawCollapsedBlockPopup(QPainter &painter, + const QTextBlock &block, + QPointF offset, + const QRect &clip) +{ + int margin = block.document()->documentMargin(); + qreal maxWidth = 0; + qreal blockHeight = 0; + QTextBlock b = block; + + while (!b.isVisible()) { + const int blockNumber = b.blockNumber(); + if (!m_skippedLines.contains(blockNumber) && !m_separators.contains(blockNumber)) { + b.setVisible(true); // make sure block bounding rect works + QRectF r = blockBoundingRect(b).translated(offset); + + QTextLayout *layout = b.layout(); + for (int i = layout->lineCount()-1; i >= 0; --i) + maxWidth = qMax(maxWidth, layout->lineAt(i).naturalTextWidth() + 2*margin); + + blockHeight += r.height(); + + b.setVisible(false); // restore previous state + b.setLineCount(0); // restore 0 line count for invisible block + } + b = b.next(); + } + + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.translate(.5, .5); + QBrush brush = palette().base(); + const QTextCharFormat &ifdefedOutFormat + = textDocument()->fontSettings().toTextCharFormat(C_DISABLED_CODE); + if (ifdefedOutFormat.hasProperty(QTextFormat::BackgroundBrush)) + brush = ifdefedOutFormat.background(); + painter.setBrush(brush); + painter.drawRoundedRect(QRectF(offset.x(), + offset.y(), + maxWidth, blockHeight).adjusted(0, 0, 0, 0), 3, 3); + painter.restore(); + + QTextBlock end = b; + b = block; + while (b != end) { + const int blockNumber = b.blockNumber(); + if (!m_skippedLines.contains(blockNumber) && !m_separators.contains(blockNumber)) { + b.setVisible(true); // make sure block bounding rect works + QRectF r = blockBoundingRect(b).translated(offset); + QTextLayout *layout = b.layout(); + QVector<QTextLayout::FormatRange> selections; + layout->draw(&painter, offset, selections, clip); + + b.setVisible(false); // restore previous state + b.setLineCount(0); // restore 0 line count for invisible block + offset.ry() += r.height(); + } + b = b.next(); + } +} + +void SideDiffEditorWidget::drawCollapsedBlockPopup(QPainter &painter, + const QTextBlock &block, + QPointF offset, + const QRect &clip) +{ + Q_UNUSED(painter) + // Called from inside SelectableTextEditorWidget::paintEvent(). + // Postpone the drawing for now, do it after our paintEvent's + // custom painting. Store the data for the future redraw. + m_drawCollapsedBlock = block; + m_drawCollapsedOffset = offset; + m_drawCollapsedClip = clip; } ////////////////// @@ -545,6 +668,12 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent) connect(m_rightEditor, &QPlainTextEdit::cursorPositionChanged, this, &SideBySideDiffEditorWidget::rightCursorPositionChanged); + connect(m_leftEditor, &SideDiffEditorWidget::foldChanged, + m_rightEditor, &SideDiffEditorWidget::setFolded); + connect(m_rightEditor, &SideDiffEditorWidget::foldChanged, + m_leftEditor, &SideDiffEditorWidget::setFolded); + + m_splitter = new MiniSplitter(this); m_splitter->addWidget(m_leftEditor); m_splitter->addWidget(m_rightEditor); @@ -678,9 +807,11 @@ void SideBySideDiffEditorWidget::showDiff() QString leftTexts, rightTexts; int blockNumber = 0; QChar separator = QLatin1Char('\n'); + QHash<int, int> foldingIndent; for (const FileData &contextFileData : m_controller.m_contextFileData) { QString leftText, rightText; + foldingIndent.insert(blockNumber, 1); leftFormats[blockNumber].append(DiffSelection(&m_controller.m_fileLineFormat)); rightFormats[blockNumber].append(DiffSelection(&m_controller.m_fileLineFormat)); m_leftEditor->setFileInfo(blockNumber, contextFileData.leftFileInfo); @@ -692,6 +823,7 @@ void SideBySideDiffEditorWidget::showDiff() int lastLeftLineNumber = -1; if (contextFileData.binaryFiles) { + foldingIndent.insert(blockNumber, 2); leftFormats[blockNumber].append(DiffSelection(&m_controller.m_chunkLineFormat)); rightFormats[blockNumber].append(DiffSelection(&m_controller.m_chunkLineFormat)); m_leftEditor->setSkippedLines(blockNumber, -2); @@ -709,6 +841,7 @@ void SideBySideDiffEditorWidget::showDiff() if (!chunkData.contextChunk) { const int skippedLines = leftLineNumber - lastLeftLineNumber - 1; if (skippedLines > 0) { + foldingIndent.insert(blockNumber, 2); leftFormats[blockNumber].append(DiffSelection(&m_controller.m_chunkLineFormat)); rightFormats[blockNumber].append(DiffSelection(&m_controller.m_chunkLineFormat)); m_leftEditor->setSkippedLines(blockNumber, skippedLines, chunkData.contextInfo); @@ -813,6 +946,13 @@ void SideBySideDiffEditorWidget::showDiff() m_rightEditor->setPlainText(rightTexts); m_controller.m_ignoreCurrentIndexChange = oldIgnore; + QTextBlock block = m_leftEditor->document()->firstBlock(); + for (int b = 0; block.isValid(); block = block.next(), ++b) + SelectableTextEditorWidget::setFoldingIndent(block, foldingIndent.value(b, 3)); + block = m_rightEditor->document()->firstBlock(); + for (int b = 0; block.isValid(); block = block.next(), ++b) + SelectableTextEditorWidget::setFoldingIndent(block, foldingIndent.value(b, 3)); + m_leftEditor->setSelections(leftFormats); m_rightEditor->setSelections(rightFormats); } diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.h b/src/plugins/diffeditor/sidebysidediffeditorwidget.h index 4f97494b80..7543fec05d 100644 --- a/src/plugins/diffeditor/sidebysidediffeditorwidget.h +++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.h @@ -40,6 +40,7 @@ class TextEditorWidget; QT_BEGIN_NAMESPACE class QMenu; class QSplitter; +class QTextBlock; QT_END_NAMESPACE namespace DiffEditor { diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp index 61b62d9b51..a2bd5829c6 100644 --- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp +++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp @@ -451,13 +451,6 @@ QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData, return diffText; } - -static void setFoldingIndent(const QTextBlock &block, int indent) -{ - if (TextEditor::TextBlockUserData *userData = TextEditor::TextDocumentLayout::userData(block)) - userData->setFoldingIndent(indent); -} - void UnifiedDiffEditorWidget::showDiff() { QString diffText; @@ -478,8 +471,8 @@ void UnifiedDiffEditorWidget::showDiff() const QString rightFileInfo = QLatin1String("+++ ") + fileData.rightFileInfo.fileName + QLatin1Char('\n'); setFileInfo(blockNumber, fileData.leftFileInfo, fileData.rightFileInfo); - selections[blockNumber].append(DiffSelection(&m_controller.m_fileLineFormat)); foldingIndent.insert(blockNumber, 1); + selections[blockNumber].append(DiffSelection(&m_controller.m_fileLineFormat)); blockNumber++; foldingIndent.insert(blockNumber, 1); selections[blockNumber].append(DiffSelection(&m_controller.m_fileLineFormat)); diff --git a/src/plugins/texteditor/textdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp index 666afeeaee..454819294d 100644 --- a/src/plugins/texteditor/textdocumentlayout.cpp +++ b/src/plugins/texteditor/textdocumentlayout.cpp @@ -526,6 +526,13 @@ void TextDocumentLayout::setFolded(const QTextBlock &block, bool folded) userData(block)->setFolded(true); else if (TextBlockUserData *userData = testUserData(block)) userData->setFolded(false); + else + return; + + TextDocumentLayout *layout = qobject_cast<TextDocumentLayout *>( + block.document()->documentLayout()); + if (layout) + emit layout->foldChanged(block.blockNumber(), folded); } void TextDocumentLayout::requestExtraAreaUpdate() diff --git a/src/plugins/texteditor/textdocumentlayout.h b/src/plugins/texteditor/textdocumentlayout.h index 928ed7dbb6..737566ab18 100644 --- a/src/plugins/texteditor/textdocumentlayout.h +++ b/src/plugins/texteditor/textdocumentlayout.h @@ -219,6 +219,7 @@ public: signals: void updateExtraArea(); + void foldChanged(const int blockNumber, bool folded); }; } // namespace TextEditor |