forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class TextEditorWidget;
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMenu;
|
||||
class QSplitter;
|
||||
class QTextBlock;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace DiffEditor {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -219,6 +219,7 @@ public:
|
||||
|
||||
signals:
|
||||
void updateExtraArea();
|
||||
void foldChanged(const int blockNumber, bool folded);
|
||||
};
|
||||
|
||||
} // namespace TextEditor
|
||||
|
||||
Reference in New Issue
Block a user