TextEditor: Add folding actions to the extra area context menu

Fixes: QTCREATORBUG-7461
Change-Id: I83c48433781a33af7ea603a08e0c2727f0481cef
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2024-07-11 11:00:59 +02:00
parent 2ceed53503
commit 5fa2cc9ff5
4 changed files with 63 additions and 25 deletions

View File

@@ -633,7 +633,7 @@ void TextDocumentLayout::requestExtraAreaUpdate()
emit updateExtraArea(); emit updateExtraArea();
} }
void TextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold) void TextDocumentLayout::doFoldOrUnfold(const QTextBlock &block, bool unfold, bool recursive)
{ {
if (!canFold(block)) if (!canFold(block))
return; return;
@@ -643,7 +643,10 @@ void TextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold)
while (b.isValid() && foldingIndent(b) > indent && (unfold || b.next().isValid())) { while (b.isValid() && foldingIndent(b) > indent && (unfold || b.next().isValid())) {
b.setVisible(unfold); b.setVisible(unfold);
b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0); b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0);
if (unfold) { // do not unfold folded sub-blocks if (recursive) {
if ((unfold && isFolded(b)) || (!unfold && canFold(b)))
setFolded(b, !unfold);
} else if (unfold) { // do not unfold folded sub-blocks
if (isFolded(b) && b.next().isValid()) { if (isFolded(b) && b.next().isValid()) {
int jndent = foldingIndent(b); int jndent = foldingIndent(b);
b = b.next(); b = b.next();

View File

@@ -199,7 +199,7 @@ public:
static int lexerState(const QTextBlock &block); static int lexerState(const QTextBlock &block);
static void changeFoldingIndent(QTextBlock &block, int delta); static void changeFoldingIndent(QTextBlock &block, int delta);
static bool canFold(const QTextBlock &block); static bool canFold(const QTextBlock &block);
static void doFoldOrUnfold(const QTextBlock& block, bool unfold); static void doFoldOrUnfold(const QTextBlock &block, bool unfold, bool recursive = false);
static bool isFolded(const QTextBlock &block); static bool isFolded(const QTextBlock &block);
static void setFolded(const QTextBlock &block, bool folded); static void setFolded(const QTextBlock &block, bool folded);
static void setExpectedRawStringSuffix(const QTextBlock &block, const QByteArray &suffix); static void setExpectedRawStringSuffix(const QTextBlock &block, const QByteArray &suffix);

View File

@@ -4235,7 +4235,7 @@ void TextEditorWidgetPrivate::registerActions()
.setScriptable(true); .setScriptable(true);
m_unfoldAllAction = ActionBuilder(this, UNFOLD_ALL) m_unfoldAllAction = ActionBuilder(this, UNFOLD_ALL)
.setContext(m_editorContext) .setContext(m_editorContext)
.addOnTriggered([this] { q->unfoldAll(); }) .addOnTriggered([this] { q->toggleFoldAll(); })
.setScriptable(true) .setScriptable(true)
.contextAction(); .contextAction();
ActionBuilder(this, INCREASE_FONT_SIZE).setContext(m_editorContext).addOnTriggered([this] { ActionBuilder(this, INCREASE_FONT_SIZE).setContext(m_editorContext).addOnTriggered([this] {
@@ -7097,8 +7097,38 @@ void TextEditorWidget::extraAreaLeaveEvent(QEvent *)
extraAreaMouseEvent(&me); extraAreaMouseEvent(&me);
} }
static bool xIsInsideFoldingRegion(int x, int extraAreaWidth, const QFontMetrics &fm)
{
int boxWidth = 0;
if (TextEditorSettings::fontSettings().relativeLineSpacing() == 100)
boxWidth = foldBoxWidth(fm);
else
boxWidth = foldBoxWidth();
return x > extraAreaWidth - boxWidth && x <= extraAreaWidth;
}
void TextEditorWidget::extraAreaContextMenuEvent(QContextMenuEvent *e) void TextEditorWidget::extraAreaContextMenuEvent(QContextMenuEvent *e)
{ {
if (d->m_codeFoldingVisible
&& xIsInsideFoldingRegion(e->pos().x(), extraArea()->width(), fontMetrics())) {
const QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
const QTextBlock block = cursor.block();
auto menu = new QMenu(this);
menu->addAction(Tr::tr("Fold"), this, [&] { fold(block); });
menu->addAction(Tr::tr("Fold recursively"), this, [&] { fold(block, true); });
menu->addAction(Tr::tr("Fold all"), this, [this] { unfoldAll(/* unfold = */ false); });
menu->addAction(Tr::tr("Unfold"), this, [&] { unfold(block); });
menu->addAction(Tr::tr("Unfold recursively"), this, [&] { unfold(block, true); });
menu->addAction(Tr::tr("Unfold all"), this, [this] { unfoldAll(/* fold = */ true); });
menu->exec(e->globalPos());
delete menu;
e->accept();
return;
}
if (d->m_marksVisible) { if (d->m_marksVisible) {
QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y())); QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
auto contextMenu = new QMenu(this); auto contextMenu = new QMenu(this);
@@ -7119,14 +7149,8 @@ void TextEditorWidget::updateFoldingHighlight(const QPoint &pos)
return; return;
// Update which folder marker is highlighted // Update which folder marker is highlighted
int boxWidth = 0;
if (TextEditorSettings::fontSettings().relativeLineSpacing() == 100)
boxWidth = foldBoxWidth(fontMetrics());
else
boxWidth = foldBoxWidth();
QTextCursor cursor; QTextCursor cursor;
if (pos.x() > extraArea()->width() - boxWidth) if (xIsInsideFoldingRegion(pos.x(), extraArea()->width(), fontMetrics()))
cursor = cursorForPosition(QPoint(0, pos.y())); cursor = cursorForPosition(QPoint(0, pos.y()));
else if (d->m_displaySettings.m_highlightBlocks) else if (d->m_displaySettings.m_highlightBlocks)
cursor = textCursor(); cursor = textCursor();
@@ -8907,7 +8931,7 @@ void TextEditorWidget::foldCurrentBlock()
fold(textCursor().block()); fold(textCursor().block());
} }
void TextEditorWidget::fold(const QTextBlock &block) void TextEditorWidget::fold(const QTextBlock &block, bool recursive)
{ {
if (singleShotAfterHighlightingDone([this, block] { fold(block); })) if (singleShotAfterHighlightingDone([this, block] { fold(block); }))
return; return;
@@ -8923,14 +8947,14 @@ void TextEditorWidget::fold(const QTextBlock &block)
b = b.previous(); b = b.previous();
} }
if (b.isValid()) { if (b.isValid()) {
TextDocumentLayout::doFoldOrUnfold(b, false); TextDocumentLayout::doFoldOrUnfold(b, false, recursive);
d->moveCursorVisible(); d->moveCursorVisible();
documentLayout->requestUpdate(); documentLayout->requestUpdate();
documentLayout->emitDocumentSizeChanged(); documentLayout->emitDocumentSizeChanged();
} }
} }
void TextEditorWidget::unfold(const QTextBlock &block) void TextEditorWidget::unfold(const QTextBlock &block, bool recursive)
{ {
if (singleShotAfterHighlightingDone([this, block] { unfold(block); })) if (singleShotAfterHighlightingDone([this, block] { unfold(block); }))
return; return;
@@ -8941,7 +8965,7 @@ void TextEditorWidget::unfold(const QTextBlock &block)
QTextBlock b = block; QTextBlock b = block;
while (b.isValid() && !b.isVisible()) while (b.isValid() && !b.isVisible())
b = b.previous(); b = b.previous();
TextDocumentLayout::doFoldOrUnfold(b, true); TextDocumentLayout::doFoldOrUnfold(b, true, recursive);
d->moveCursorVisible(); d->moveCursorVisible();
documentLayout->requestUpdate(); documentLayout->requestUpdate();
documentLayout->emitDocumentSizeChanged(); documentLayout->emitDocumentSizeChanged();
@@ -8952,16 +8976,14 @@ void TextEditorWidget::unfoldCurrentBlock()
unfold(textCursor().block()); unfold(textCursor().block());
} }
void TextEditorWidget::unfoldAll() void TextEditorWidget::toggleFoldAll()
{ {
if (singleShotAfterHighlightingDone([this] { unfoldAll(); })) if (singleShotAfterHighlightingDone([this] { toggleFoldAll(); }))
return; return;
QTextDocument *doc = document(); QTextDocument *doc = document();
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
QTC_ASSERT(documentLayout, return);
QTextBlock block = doc->firstBlock(); QTextBlock block = doc->firstBlock();
bool makeVisible = true; bool makeVisible = true;
while (block.isValid()) { while (block.isValid()) {
if (block.isVisible() && TextDocumentLayout::canFold(block) && block.next().isVisible()) { if (block.isVisible() && TextDocumentLayout::canFold(block) && block.next().isVisible()) {
@@ -8971,11 +8993,23 @@ void TextEditorWidget::unfoldAll()
block = block.next(); block = block.next();
} }
block = doc->firstBlock(); unfoldAll(makeVisible);
}
void TextEditorWidget::unfoldAll(bool unfold)
{
if (singleShotAfterHighlightingDone([this, unfold] { unfoldAll(unfold); }))
return;
QTextDocument *doc = document();
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
QTC_ASSERT(documentLayout, return);
QTextBlock block = doc->firstBlock();
while (block.isValid()) { while (block.isValid()) {
if (TextDocumentLayout::canFold(block)) if (TextDocumentLayout::canFold(block))
TextDocumentLayout::doFoldOrUnfold(block, makeVisible); TextDocumentLayout::doFoldOrUnfold(block, unfold);
block = block.next(); block = block.next();
} }

View File

@@ -387,10 +387,11 @@ public:
void deleteStartOfLine(); void deleteStartOfLine();
void deleteStartOfWord(); void deleteStartOfWord();
void deleteStartOfWordCamelCase(); void deleteStartOfWordCamelCase();
void unfoldAll(); void toggleFoldAll();
void fold(const QTextBlock &block); void unfoldAll(bool unfold);
void fold(const QTextBlock &block, bool recursive = false);
void foldCurrentBlock(); void foldCurrentBlock();
void unfold(const QTextBlock &block); void unfold(const QTextBlock &block, bool recursive = false);
void unfoldCurrentBlock(); void unfoldCurrentBlock();
void selectEncoding(); void selectEncoding();
void updateTextCodecLabel(); void updateTextCodecLabel();