From 2b83869236d08b8e635c94c99095630d9a12130e Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 23 May 2017 14:34:22 +0200 Subject: TextEditor: Animate navigation within file Change-Id: I490d70a785c947cd41809503e15a317152126641 Reviewed-by: Alessandro Portale --- src/plugins/cppeditor/cppoutline.cpp | 2 +- src/plugins/cpptools/cppeditoroutline.cpp | 2 +- src/plugins/texteditor/displaysettings.cpp | 7 +++ src/plugins/texteditor/displaysettings.h | 2 + src/plugins/texteditor/displaysettingspage.cpp | 2 + src/plugins/texteditor/displaysettingspage.ui | 9 +++- src/plugins/texteditor/texteditor.cpp | 75 ++++++++++++++++++++++---- src/plugins/texteditor/texteditor.h | 3 +- 8 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/plugins/cppeditor/cppoutline.cpp b/src/plugins/cppeditor/cppoutline.cpp index 3b1209c1c8..def7d11a6e 100644 --- a/src/plugins/cppeditor/cppoutline.cpp +++ b/src/plugins/cppeditor/cppoutline.cpp @@ -173,7 +173,7 @@ void CppOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex) Core::EditorManager::addCurrentPositionToNavigationHistory(); // line has to be 1 based, column 0 based! - m_editor->gotoLine(symbol->line(), symbol->column() - 1); + m_editor->gotoLine(symbol->line(), symbol->column() - 1, true, true); m_blockCursorSync = false; } } diff --git a/src/plugins/cpptools/cppeditoroutline.cpp b/src/plugins/cpptools/cppeditoroutline.cpp index a5192f4441..8bd16ac98b 100644 --- a/src/plugins/cpptools/cppeditoroutline.cpp +++ b/src/plugins/cpptools/cppeditoroutline.cpp @@ -252,7 +252,7 @@ void CppEditorOutline::gotoSymbolInEditor() Core::EditorManager::cutForwardNavigationHistory(); Core::EditorManager::addCurrentPositionToNavigationHistory(); - m_editorWidget->gotoLine(link.targetLine, link.targetColumn); + m_editorWidget->gotoLine(link.targetLine, link.targetColumn, true, true); m_editorWidget->activateEditor(); } diff --git a/src/plugins/texteditor/displaysettings.cpp b/src/plugins/texteditor/displaysettings.cpp index a2a06eff78..d15cf52dca 100644 --- a/src/plugins/texteditor/displaysettings.cpp +++ b/src/plugins/texteditor/displaysettings.cpp @@ -42,6 +42,8 @@ static const char centerCursorOnScrollKey[] = "CenterCursorOnScroll"; static const char openLinksInNextSplitKey[] = "OpenLinksInNextSplitKey"; static const char displayFileEncodingKey[] = "DisplayFileEncoding"; static const char scrollBarHighlightsKey[] = "ScrollBarHighlights"; +static const char animateNavigationWithinFileKey[] = "AnimateNavigationWithinFile"; +static const char animateWithinFileTimeMaxKey[] = "AnimateWithinFileTimeMax"; static const char groupPostfix[] = "DisplaySettings"; namespace TextEditor { @@ -66,6 +68,7 @@ void DisplaySettings::toSettings(const QString &category, QSettings *s) const s->setValue(QLatin1String(openLinksInNextSplitKey), m_openLinksInNextSplit); s->setValue(QLatin1String(displayFileEncodingKey), m_displayFileEncoding); s->setValue(QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights); + s->setValue(QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile); s->endGroup(); } @@ -92,6 +95,8 @@ void DisplaySettings::fromSettings(const QString &category, const QSettings *s) m_openLinksInNextSplit = s->value(group + QLatin1String(openLinksInNextSplitKey), m_openLinksInNextSplit).toBool(); m_displayFileEncoding = s->value(group + QLatin1String(displayFileEncodingKey), m_displayFileEncoding).toBool(); m_scrollBarHighlights = s->value(group + QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights).toBool(); + m_animateNavigationWithinFile = s->value(group + QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile).toBool(); + m_animateWithinFileTimeMax = s->value(group + QLatin1String(animateWithinFileTimeMaxKey), m_animateWithinFileTimeMax).toInt(); } bool DisplaySettings::equals(const DisplaySettings &ds) const @@ -111,6 +116,8 @@ bool DisplaySettings::equals(const DisplaySettings &ds) const && m_forceOpenLinksInNextSplit == ds.m_forceOpenLinksInNextSplit && m_displayFileEncoding == ds.m_displayFileEncoding && m_scrollBarHighlights == ds.m_scrollBarHighlights + && m_animateNavigationWithinFile == ds.m_animateNavigationWithinFile + && m_animateWithinFileTimeMax == ds.m_animateWithinFileTimeMax ; } diff --git a/src/plugins/texteditor/displaysettings.h b/src/plugins/texteditor/displaysettings.h index 08d2077768..fabebd96d8 100644 --- a/src/plugins/texteditor/displaysettings.h +++ b/src/plugins/texteditor/displaysettings.h @@ -56,6 +56,8 @@ public: bool m_forceOpenLinksInNextSplit = false; bool m_displayFileEncoding = false; bool m_scrollBarHighlights = true; + bool m_animateNavigationWithinFile = false; + int m_animateWithinFileTimeMax = 333; // read only setting bool equals(const DisplaySettings &ds) const; }; diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp index 946234c0ec..147fa97080 100644 --- a/src/plugins/texteditor/displaysettingspage.cpp +++ b/src/plugins/texteditor/displaysettingspage.cpp @@ -118,6 +118,7 @@ void DisplaySettingsPage::settingsFromUI(DisplaySettings &displaySettings, displaySettings.m_openLinksInNextSplit = d->m_page->openLinksInNextSplit->isChecked(); displaySettings.m_displayFileEncoding = d->m_page->displayFileEncoding->isChecked(); displaySettings.m_scrollBarHighlights = d->m_page->scrollBarHighlights->isChecked(); + displaySettings.m_animateNavigationWithinFile = d->m_page->animateNavigationWithinFile->isChecked(); } void DisplaySettingsPage::settingsToUI() @@ -140,6 +141,7 @@ void DisplaySettingsPage::settingsToUI() d->m_page->openLinksInNextSplit->setChecked(displaySettings.m_openLinksInNextSplit); d->m_page->displayFileEncoding->setChecked(displaySettings.m_displayFileEncoding); d->m_page->scrollBarHighlights->setChecked(displaySettings.m_scrollBarHighlights); + d->m_page->animateNavigationWithinFile->setChecked(displaySettings.m_animateNavigationWithinFile); } const DisplaySettings &DisplaySettingsPage::displaySettings() const diff --git a/src/plugins/texteditor/displaysettingspage.ui b/src/plugins/texteditor/displaysettingspage.ui index ee8b15cfdf..b91965a192 100644 --- a/src/plugins/texteditor/displaysettingspage.ui +++ b/src/plugins/texteditor/displaysettingspage.ui @@ -7,7 +7,7 @@ 0 0 501 - 331 + 339 @@ -174,6 +174,13 @@ + + + + Animate navigation within file + + + diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 2eed90f3c7..86d74f28ad 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -99,7 +99,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -457,6 +459,8 @@ public: bool m_assistRelevantContentAdded = false; QList m_hoverHandlers; // Not owned + QPointer m_navigationAnimation; + QPointer m_bracketsAnimator; // Animation and highlighting of auto completed text @@ -2515,7 +2519,7 @@ void TextEditorWidget::doSetTextCursor(const QTextCursor &cursor) doSetTextCursor(cursor, false); } -void TextEditorWidget::gotoLine(int line, int column, bool centerLine) +void TextEditorWidget::gotoLine(int line, int column, bool centerLine, bool animate) { d->m_lastCursorChangeWasInteresting = false; // avoid adding the previous position to history const int blockNumber = qMin(line, document()->blockCount()) - 1; @@ -2531,12 +2535,61 @@ void TextEditorWidget::gotoLine(int line, int column, bool centerLine) } cursor.setPosition(pos); } - setTextCursor(cursor); - if (centerLine) - centerCursor(); - else - ensureCursorVisible(); + const DisplaySettings &ds = d->m_displaySettings; + if (animate && ds.m_animateNavigationWithinFile) { + const QScrollBar *scrollBar = verticalScrollBar(); + const int start = scrollBar->value(); + + setTextCursor(cursor); + ensureBlockIsUnfolded(block); + + const int visibleLines = lastVisibleLine() - firstVisibleLine(); + + int end = 0; + auto it = document()->firstBlock(); + while (it.isValid() && it != block) { + if (it.isVisible()) + ++end; + it = it.next(); + } + + if (centerLine) + end = qMin(scrollBar->maximum(), qMax(scrollBar->minimum(), end - visibleLines / 2)); + + const int delta = end - start; + // limit the number of steps for the animation otherwise you wont be able to tell + // the direction of the animantion for large delta values + const int steps = qMax(-ds.m_animateWithinFileTimeMax, + qMin(ds.m_animateWithinFileTimeMax, delta)); + // limit the duration of the animation to at least 4 pictures on a 60Hz Monitor and + // at most to the number of absolute steps + const int durationMinimum = int (4 // number of pictures + * float(1) / 60 // on a 60 Hz Monitor + * 1000); // milliseconds + const int duration = qMax(durationMinimum, qAbs(steps)); + + d->m_navigationAnimation = new QSequentialAnimationGroup(this); + auto startAnimation = new QPropertyAnimation(verticalScrollBar(), "value"); + startAnimation->setEasingCurve(QEasingCurve::InExpo); + startAnimation->setStartValue(start); + startAnimation->setEndValue(start + steps / 2); + startAnimation->setDuration(duration / 2); + d->m_navigationAnimation->addAnimation(startAnimation); + auto endAnimation = new QPropertyAnimation(verticalScrollBar(), "value"); + endAnimation->setEasingCurve(QEasingCurve::OutExpo); + endAnimation->setStartValue(end - steps / 2); + endAnimation->setEndValue(end); + endAnimation->setDuration(duration / 2); + d->m_navigationAnimation->addAnimation(endAnimation); + d->m_navigationAnimation->start(QAbstractAnimation::DeleteWhenStopped); + } else { + setTextCursor(cursor); + if (centerLine) + centerCursor(); + else + ensureCursorVisible(); + } } d->saveCurrentCursorPositionForNavigation(); } @@ -5224,7 +5277,12 @@ void TextEditorWidget::extraAreaMouseEvent(QMouseEvent *e) void TextEditorWidget::ensureCursorVisible() { - QTextBlock block = textCursor().block(); + ensureBlockIsUnfolded(textCursor().block()); + QPlainTextEdit::ensureCursorVisible(); +} + +void TextEditorWidget::ensureBlockIsUnfolded(QTextBlock block) +{ if (!block.isVisible()) { TextDocumentLayout *documentLayout = qobject_cast(document()->documentLayout()); QTC_ASSERT(documentLayout, return); @@ -5246,7 +5304,6 @@ void TextEditorWidget::ensureCursorVisible() documentLayout->requestUpdate(); documentLayout->emitDocumentSizeChanged(); } - QPlainTextEdit::ensureCursorVisible(); } void TextEditorWidgetPrivate::toggleBlockVisible(const QTextBlock &block) @@ -5493,7 +5550,7 @@ bool TextEditorWidget::openLink(const Link &link, bool inNextSplit) if (!inNextSplit && textDocument()->filePath().toString() == link.targetFileName) { EditorManager::addCurrentPositionToNavigationHistory(); - gotoLine(link.targetLine, link.targetColumn); + gotoLine(link.targetLine, link.targetColumn, true, true); setFocus(); return true; } diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 9b996dd61b..841324891c 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -183,7 +183,7 @@ public: // IEditor QByteArray saveState() const; bool restoreState(const QByteArray &state); - void gotoLine(int line, int column = 0, bool centerLine = true); + void gotoLine(int line, int column = 0, bool centerLine = true, bool animate = false); int position(TextPositionOperation posOp = CurrentPosition, int at = -1) const; void convertPosition(int pos, int *line, int *column) const; @@ -297,6 +297,7 @@ public: const BehaviorSettings &behaviorSettings() const; void ensureCursorVisible(); + void ensureBlockIsUnfolded(QTextBlock block); static Core::Id FakeVimSelection; static Core::Id SnippetPlaceholderSelection; -- cgit v1.2.1