From 01ddb61ab7f7a7c605481c206f44b4392b52796b Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 29 Sep 2022 11:44:52 +0200 Subject: [PATCH] Editor: Fix reassigning a document to an editor Moving all initializations that are not influenced by the document to the TextEditorWidget(Private) constructor. So we do not reinitialize or double connect unintendedly. Change-Id: I42f4e8166c21aec9c8b780033b12eb8dae5f72a6 Reviewed-by: Jarek Kobus Reviewed-by: --- src/plugins/texteditor/texteditor.cpp | 407 ++++++++++++++------------ 1 file changed, 218 insertions(+), 189 deletions(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 676417dc600..dc28b7f3bfc 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -492,7 +492,6 @@ public: TextEditorWidgetPrivate(TextEditorWidget *parent); ~TextEditorWidgetPrivate() override; - void setupDocumentSignals(); void updateLineSelectionColor(); void print(QPrinter *printer); @@ -503,7 +502,7 @@ public: void collectToCircularClipboard(); void setClipboardSelection(); - void ctor(const QSharedPointer &doc); + void setDocument(const QSharedPointer &doc); void handleHomeKey(bool anchor, bool block); void handleBackspaceKey(); void moveLineUpDown(bool up); @@ -653,15 +652,12 @@ public: bool m_lastCursorChangeWasInteresting = false; QSharedPointer m_document; + QList m_documentConnections; QByteArray m_tempState; QByteArray m_tempNavigationState; bool m_parenthesesMatchingEnabled = false; - - // parentheses matcher - bool m_formatRange = false; QTimer m_parenthesesMatchingTimer; - // end parentheses matcher QWidget *m_extraArea = nullptr; @@ -892,19 +888,23 @@ void TextEditorWidgetFind::cancelCurrentSelectAll() } TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) - : q(parent), - m_marksVisible(false), - m_codeFoldingVisible(false), - m_codeFoldingSupported(false), - m_revisionsVisible(false), - m_lineNumbersVisible(true), - m_highlightCurrentLine(true), - m_requestMarkEnabled(true), - m_lineSeparatorsAllowed(false), - m_maybeFakeTooltipEvent(false), - m_hoverHandlerRunner(parent, m_hoverHandlers), - m_clipboardAssistProvider(new ClipboardAssistProvider), - m_autoCompleter(new AutoCompleter) + : q(parent) + , m_marksVisible(false) + , m_codeFoldingVisible(false) + , m_codeFoldingSupported(false) + , m_revisionsVisible(false) + , m_lineNumbersVisible(true) + , m_highlightCurrentLine(true) + , m_requestMarkEnabled(true) + , m_lineSeparatorsAllowed(false) + , m_maybeFakeTooltipEvent(false) + , m_hoverHandlerRunner(parent, m_hoverHandlers) + , m_clipboardAssistProvider(new ClipboardAssistProvider) + , m_autoCompleter(new AutoCompleter) + , m_overlay(new TextEditorOverlay(q)) + , m_snippetOverlay(new SnippetOverlay(q)) + , m_searchResultOverlay(new TextEditorOverlay(q)) + , m_refactorOverlay(new RefactorOverlay(q)) { auto aggregate = new Aggregation::Aggregate; m_find = new TextEditorWidgetFind(q); @@ -948,6 +948,68 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) m_fileEncodingLabelAction = m_toolBar->addWidget(m_fileEncodingLabel); m_extraSelections.reserve(NExtraSelectionKinds); + + connect(&m_codeAssistant, &CodeAssistant::finished, + q, &TextEditorWidget::assistFinished); + + connect(q, &QPlainTextEdit::blockCountChanged, + this, &TextEditorWidgetPrivate::slotUpdateExtraAreaWidth); + + connect(q, &QPlainTextEdit::modificationChanged, + m_extraArea, QOverload<>::of(&QWidget::update)); + + connect(q, &QPlainTextEdit::cursorPositionChanged, + q, &TextEditorWidget::slotCursorPositionChanged); + + connect(q, &QPlainTextEdit::cursorPositionChanged, + this, &TextEditorWidgetPrivate::updateCursorPosition); + + connect(q, &QPlainTextEdit::updateRequest, + this, &TextEditorWidgetPrivate::slotUpdateRequest); + + connect(q, &QPlainTextEdit::selectionChanged, + this, &TextEditorWidgetPrivate::slotSelectionChanged); + + m_parenthesesMatchingTimer.setSingleShot(true); + m_parenthesesMatchingTimer.setInterval(50); + connect(&m_parenthesesMatchingTimer, &QTimer::timeout, + this, &TextEditorWidgetPrivate::_q_matchParentheses); + + m_highlightBlocksTimer.setSingleShot(true); + connect(&m_highlightBlocksTimer, &QTimer::timeout, + this, &TextEditorWidgetPrivate::_q_highlightBlocks); + + m_scrollBarUpdateTimer.setSingleShot(true); + connect(&m_scrollBarUpdateTimer, &QTimer::timeout, + this, &TextEditorWidgetPrivate::highlightSearchResultsInScrollBar); + + m_delayedUpdateTimer.setSingleShot(true); + connect(&m_delayedUpdateTimer, &QTimer::timeout, + q->viewport(), QOverload<>::of(&QWidget::update)); + + connect(m_fileEncodingLabel, &FixedSizeClickLabel::clicked, + q, &TextEditorWidget::selectEncoding); + + connect(m_fileLineEnding, &QComboBox::currentIndexChanged, + q, &TextEditorWidget::selectLineEnding); + + TextEditorSettings *settings = TextEditorSettings::instance(); + + // Connect to settings change signals + connect(settings, &TextEditorSettings::typingSettingsChanged, + q, &TextEditorWidget::setTypingSettings); + connect(settings, &TextEditorSettings::storageSettingsChanged, + q, &TextEditorWidget::setStorageSettings); + connect(settings, &TextEditorSettings::behaviorSettingsChanged, + q, &TextEditorWidget::setBehaviorSettings); + connect(settings, &TextEditorSettings::marginSettingsChanged, + q, &TextEditorWidget::setMarginSettings); + connect(settings, &TextEditorSettings::displaySettingsChanged, + q, &TextEditorWidget::setDisplaySettings); + connect(settings, &TextEditorSettings::completionSettingsChanged, + q, &TextEditorWidget::setCompletionSettings); + connect(settings, &TextEditorSettings::extraEncodingSettingsChanged, + q, &TextEditorWidget::setExtraEncodingSettings); } TextEditorWidgetPrivate::~TextEditorWidgetPrivate() @@ -1090,11 +1152,16 @@ TextEditorWidget::TextEditorWidget(QWidget *parent) // passed to this object's event() which uses 'd'. d = nullptr; d = new TextEditorWidgetPrivate(this); + + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + setLayoutDirection(Qt::LeftToRight); + viewport()->setMouseTracking(true); + setFrameStyle(QFrame::NoFrame); } void TextEditorWidget::setTextDocument(const QSharedPointer &doc) { - d->ctor(doc); + d->setDocument(doc); } void TextEditorWidgetPrivate::setupScrollBar() @@ -1112,105 +1179,160 @@ void TextEditorWidgetPrivate::setupScrollBar() } } -void TextEditorWidgetPrivate::ctor(const QSharedPointer &doc) +void TextEditorWidgetPrivate::setDocument(const QSharedPointer &doc) { - q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + QSharedPointer previousDocument = m_document; + for (const QMetaObject::Connection &connection : m_documentConnections) + disconnect(connection); + m_documentConnections.clear(); - m_overlay = new TextEditorOverlay(q); - m_snippetOverlay = new SnippetOverlay(q); - m_searchResultOverlay = new TextEditorOverlay(q); - m_refactorOverlay = new RefactorOverlay(q); + m_document = doc; + q->QPlainTextEdit::setDocument(doc->document()); + previousDocument.clear(); + q->setCursorWidth(2); // Applies to the document layout - { - // QPlainTextEdit keeps pointer to the old QTextDocumentLayout, - // and QPlainTextEdit::setDocument() disconnects unconditionally from the old layout. - // Since the old layout it being deleted together with the old text document when - // dropping the shared pointer reference, QPlainTextEdit::setDocument() will crash. - // We prolong the lifetime of the old document and its layout by keeping the - // shared pointer still for a while. - QSharedPointer lifetimeProlonger = m_document; - m_document = doc; - setupDocumentSignals(); - } + auto documentLayout = qobject_cast( + m_document->document()->documentLayout()); + QTC_CHECK(documentLayout); + + m_documentConnections << connect(documentLayout, + &QPlainTextDocumentLayout::updateBlock, + this, + &TextEditorWidgetPrivate::slotUpdateBlockNotify); + + m_documentConnections << connect(documentLayout, + &TextDocumentLayout::updateExtraArea, + m_extraArea, + QOverload<>::of(&QWidget::update)); + + m_documentConnections << connect(q, + &TextEditorWidget::requestBlockUpdate, + documentLayout, + &QPlainTextDocumentLayout::updateBlock); + + m_documentConnections << connect(documentLayout, + &TextDocumentLayout::updateExtraArea, + this, + &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar); + + m_documentConnections << connect(documentLayout, + &TextDocumentLayout::parenthesesChanged, + &m_parenthesesMatchingTimer, + QOverload<>::of(&QTimer::start)); + + m_documentConnections << connect(documentLayout, + &QAbstractTextDocumentLayout::documentSizeChanged, + this, + &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar); + + m_documentConnections << connect(documentLayout, + &QAbstractTextDocumentLayout::update, + this, + &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar); + + m_documentConnections << connect(m_document->document(), + &QTextDocument::contentsChange, + this, + &TextEditorWidgetPrivate::editorContentsChange); + + m_documentConnections << connect(m_document->document(), + &QTextDocument::modificationChanged, + q, + &TextEditorWidget::updateTextCodecLabel); + + m_documentConnections << connect(m_document->document(), + &QTextDocument::modificationChanged, + q, + &TextEditorWidget::updateTextLineEndingLabel); + + m_documentConnections << connect(m_document.data(), + &TextDocument::aboutToReload, + this, + &TextEditorWidgetPrivate::documentAboutToBeReloaded); + + m_documentConnections << connect(m_document.data(), + &TextDocument::reloadFinished, + this, + &TextEditorWidgetPrivate::documentReloadFinished); + + m_documentConnections << connect(m_document.data(), + &TextDocument::tabSettingsChanged, + this, + &TextEditorWidgetPrivate::applyTabSettings); + + m_documentConnections << connect(m_document.data(), + &TextDocument::fontSettingsChanged, + this, + &TextEditorWidgetPrivate::applyFontSettingsDelayed); + + m_documentConnections << connect(m_document.data(), + &TextDocument::markRemoved, + this, + &TextEditorWidgetPrivate::markRemoved); + + m_documentConnections << connect(m_document.data(), + &TextDocument::aboutToOpen, + q, + &TextEditorWidget::aboutToOpen); + + m_documentConnections << connect(m_document.data(), + &TextDocument::openFinishedSuccessfully, + q, + &TextEditorWidget::openFinishedSuccessfully); + + m_documentConnections << connect(TextEditorSettings::instance(), + &TextEditorSettings::fontSettingsChanged, + m_document.data(), + &TextDocument::setFontSettings); + + slotUpdateExtraAreaWidth(); + + // Apply current settings + // the document might already have the same settings as we set here in which case we do not + // get an update, so we have to trigger updates manually here + const FontSettings fontSettings = TextEditorSettings::fontSettings(); + if (m_document->fontSettings() == fontSettings) + applyFontSettingsDelayed(); + else + m_document->setFontSettings(fontSettings); + const TabSettings tabSettings = TextEditorSettings::codeStyle()->tabSettings(); + if (m_document->tabSettings() == tabSettings) + applyTabSettings(); + else + m_document->setTabSettings(tabSettings); // also set through code style ??? + + q->setTypingSettings(TextEditorSettings::typingSettings()); + q->setStorageSettings(TextEditorSettings::storageSettings()); + q->setBehaviorSettings(TextEditorSettings::behaviorSettings()); + q->setMarginSettings(TextEditorSettings::marginSettings()); + q->setDisplaySettings(TextEditorSettings::displaySettings()); + q->setCompletionSettings(TextEditorSettings::completionSettings()); + q->setExtraEncodingSettings(TextEditorSettings::extraEncodingSettings()); + q->setCodeStyle(TextEditorSettings::codeStyle(m_tabSettingsId)); m_blockCount = doc->document()->blockCount(); // from RESEARCH - q->setLayoutDirection(Qt::LeftToRight); - q->viewport()->setMouseTracking(true); - extraAreaSelectionAnchorBlockNumber = -1; extraAreaToggleMarkBlockNumber = -1; extraAreaHighlightFoldedBlockNumber = -1; visibleFoldedBlockNumber = -1; suggestedVisibleFoldedBlockNumber = -1; - QObject::connect(&m_codeAssistant, &CodeAssistant::finished, - q, &TextEditorWidget::assistFinished); - - QObject::connect(q, &QPlainTextEdit::blockCountChanged, - this, &TextEditorWidgetPrivate::slotUpdateExtraAreaWidth); - - QObject::connect(q, &QPlainTextEdit::modificationChanged, - m_extraArea, QOverload<>::of(&QWidget::update)); - - QObject::connect(q, &QPlainTextEdit::cursorPositionChanged, - q, &TextEditorWidget::slotCursorPositionChanged); - - QObject::connect(q, &QPlainTextEdit::cursorPositionChanged, - this, &TextEditorWidgetPrivate::updateCursorPosition); - - QObject::connect(q, &QPlainTextEdit::updateRequest, - this, &TextEditorWidgetPrivate::slotUpdateRequest); - - QObject::connect(q, &QPlainTextEdit::selectionChanged, - this, &TextEditorWidgetPrivate::slotSelectionChanged); - - // parentheses matcher - m_formatRange = true; - m_parenthesesMatchingTimer.setSingleShot(true); - m_parenthesesMatchingTimer.setInterval(50); - QObject::connect(&m_parenthesesMatchingTimer, &QTimer::timeout, - this, &TextEditorWidgetPrivate::_q_matchParentheses); - - m_highlightBlocksTimer.setSingleShot(true); - QObject::connect(&m_highlightBlocksTimer, &QTimer::timeout, - this, &TextEditorWidgetPrivate::_q_highlightBlocks); - - m_scrollBarUpdateTimer.setSingleShot(true); - QObject::connect(&m_scrollBarUpdateTimer, &QTimer::timeout, - this, &TextEditorWidgetPrivate::highlightSearchResultsInScrollBar); - - m_bracketsAnimator = nullptr; - m_autocompleteAnimator = nullptr; + if (m_bracketsAnimator) + m_bracketsAnimator->finish(); + if (m_autocompleteAnimator) + m_autocompleteAnimator->finish(); slotUpdateExtraAreaWidth(); updateHighlights(); - q->setFrameStyle(QFrame::NoFrame); - - m_delayedUpdateTimer.setSingleShot(true); - QObject::connect(&m_delayedUpdateTimer, &QTimer::timeout, - q->viewport(), QOverload<>::of(&QWidget::update)); m_moveLineUndoHack = false; updateCannotDecodeInfo(); - QObject::connect(m_document.data(), &TextDocument::aboutToOpen, - q, &TextEditorWidget::aboutToOpen); - QObject::connect(m_document.data(), &TextDocument::openFinishedSuccessfully, - q, &TextEditorWidget::openFinishedSuccessfully); - connect(m_fileEncodingLabel, &FixedSizeClickLabel::clicked, - q, &TextEditorWidget::selectEncoding); - connect(m_document->document(), &QTextDocument::modificationChanged, - q, &TextEditorWidget::updateTextCodecLabel); q->updateTextCodecLabel(); - - connect(m_fileLineEnding, &QComboBox::currentIndexChanged, - q, &TextEditorWidget::selectLineEnding); - connect(m_document->document(), &QTextDocument::modificationChanged, - q, &TextEditorWidget::updateTextLineEndingLabel); q->updateTextLineEndingLabel(); setupFromDefinition(currentDefinition()); } @@ -3446,99 +3568,6 @@ AutoCompleter *TextEditorWidget::autoCompleter() const // TextEditorWidgetPrivate // -void TextEditorWidgetPrivate::setupDocumentSignals() -{ - QTextDocument *doc = m_document->document(); - q->QPlainTextEdit::setDocument(doc); - q->setCursorWidth(2); // Applies to the document layout - - auto documentLayout = qobject_cast(doc->documentLayout()); - QTC_CHECK(documentLayout); - - QObject::connect(documentLayout, &QPlainTextDocumentLayout::updateBlock, - this, &TextEditorWidgetPrivate::slotUpdateBlockNotify); - - QObject::connect(documentLayout, &TextDocumentLayout::updateExtraArea, - m_extraArea, QOverload<>::of(&QWidget::update)); - - QObject::connect(q, &TextEditorWidget::requestBlockUpdate, - documentLayout, &QPlainTextDocumentLayout::updateBlock); - - QObject::connect(documentLayout, &TextDocumentLayout::updateExtraArea, - this, &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar); - - QObject::connect(documentLayout, &TextDocumentLayout::parenthesesChanged, - &m_parenthesesMatchingTimer, QOverload<>::of(&QTimer::start)); - - QObject::connect(documentLayout, &QAbstractTextDocumentLayout::documentSizeChanged, - this, &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar); - - QObject::connect(documentLayout, &QAbstractTextDocumentLayout::update, - this, &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar); - - QObject::connect(doc, &QTextDocument::contentsChange, - this, &TextEditorWidgetPrivate::editorContentsChange); - - QObject::connect(m_document.data(), &TextDocument::aboutToReload, - this, &TextEditorWidgetPrivate::documentAboutToBeReloaded); - - QObject::connect(m_document.data(), &TextDocument::reloadFinished, - this, &TextEditorWidgetPrivate::documentReloadFinished); - - QObject::connect(m_document.data(), &TextDocument::tabSettingsChanged, - this, &TextEditorWidgetPrivate::applyTabSettings); - - QObject::connect(m_document.data(), &TextDocument::fontSettingsChanged, - this, &TextEditorWidgetPrivate::applyFontSettingsDelayed); - - QObject::connect(m_document.data(), &TextDocument::markRemoved, - this, &TextEditorWidgetPrivate::markRemoved); - - slotUpdateExtraAreaWidth(); - - TextEditorSettings *settings = TextEditorSettings::instance(); - - // Connect to settings change signals - connect(settings, &TextEditorSettings::fontSettingsChanged, - m_document.data(), &TextDocument::setFontSettings); - connect(settings, &TextEditorSettings::typingSettingsChanged, - q, &TextEditorWidget::setTypingSettings); - connect(settings, &TextEditorSettings::storageSettingsChanged, - q, &TextEditorWidget::setStorageSettings); - connect(settings, &TextEditorSettings::behaviorSettingsChanged, - q, &TextEditorWidget::setBehaviorSettings); - connect(settings, &TextEditorSettings::marginSettingsChanged, - q, &TextEditorWidget::setMarginSettings); - connect(settings, &TextEditorSettings::displaySettingsChanged, - q, &TextEditorWidget::setDisplaySettings); - connect(settings, &TextEditorSettings::completionSettingsChanged, - q, &TextEditorWidget::setCompletionSettings); - connect(settings, &TextEditorSettings::extraEncodingSettingsChanged, - q, &TextEditorWidget::setExtraEncodingSettings); - - // Apply current settings - // the document might already have the same settings as we set here in which case we do not - // get an update, so we have to trigger updates manually here - const FontSettings fontSettings = TextEditorSettings::fontSettings(); - if (m_document->fontSettings() == fontSettings) - applyFontSettingsDelayed(); - else - m_document->setFontSettings(fontSettings); - const TabSettings tabSettings = TextEditorSettings::codeStyle()->tabSettings(); - if (m_document->tabSettings() == tabSettings) - applyTabSettings(); - else - m_document->setTabSettings(tabSettings); // also set through code style ??? - q->setTypingSettings(TextEditorSettings::typingSettings()); - q->setStorageSettings(TextEditorSettings::storageSettings()); - q->setBehaviorSettings(TextEditorSettings::behaviorSettings()); - q->setMarginSettings(TextEditorSettings::marginSettings()); - q->setDisplaySettings(TextEditorSettings::displaySettings()); - q->setCompletionSettings(TextEditorSettings::completionSettings()); - q->setExtraEncodingSettings(TextEditorSettings::extraEncodingSettings()); - q->setCodeStyle(TextEditorSettings::codeStyle(m_tabSettingsId)); -} - bool TextEditorWidgetPrivate::snippetCheckCursor(const QTextCursor &cursor) { if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())