forked from qt-creator/qt-creator
TextEditor: Animate navigation within file
Change-Id: I490d70a785c947cd41809503e15a317152126641 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -173,7 +173,7 @@ void CppOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex)
|
|||||||
Core::EditorManager::addCurrentPositionToNavigationHistory();
|
Core::EditorManager::addCurrentPositionToNavigationHistory();
|
||||||
|
|
||||||
// line has to be 1 based, column 0 based!
|
// 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;
|
m_blockCursorSync = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ void CppEditorOutline::gotoSymbolInEditor()
|
|||||||
|
|
||||||
Core::EditorManager::cutForwardNavigationHistory();
|
Core::EditorManager::cutForwardNavigationHistory();
|
||||||
Core::EditorManager::addCurrentPositionToNavigationHistory();
|
Core::EditorManager::addCurrentPositionToNavigationHistory();
|
||||||
m_editorWidget->gotoLine(link.targetLine, link.targetColumn);
|
m_editorWidget->gotoLine(link.targetLine, link.targetColumn, true, true);
|
||||||
m_editorWidget->activateEditor();
|
m_editorWidget->activateEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ static const char centerCursorOnScrollKey[] = "CenterCursorOnScroll";
|
|||||||
static const char openLinksInNextSplitKey[] = "OpenLinksInNextSplitKey";
|
static const char openLinksInNextSplitKey[] = "OpenLinksInNextSplitKey";
|
||||||
static const char displayFileEncodingKey[] = "DisplayFileEncoding";
|
static const char displayFileEncodingKey[] = "DisplayFileEncoding";
|
||||||
static const char scrollBarHighlightsKey[] = "ScrollBarHighlights";
|
static const char scrollBarHighlightsKey[] = "ScrollBarHighlights";
|
||||||
|
static const char animateNavigationWithinFileKey[] = "AnimateNavigationWithinFile";
|
||||||
|
static const char animateWithinFileTimeMaxKey[] = "AnimateWithinFileTimeMax";
|
||||||
static const char groupPostfix[] = "DisplaySettings";
|
static const char groupPostfix[] = "DisplaySettings";
|
||||||
|
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
@@ -66,6 +68,7 @@ void DisplaySettings::toSettings(const QString &category, QSettings *s) const
|
|||||||
s->setValue(QLatin1String(openLinksInNextSplitKey), m_openLinksInNextSplit);
|
s->setValue(QLatin1String(openLinksInNextSplitKey), m_openLinksInNextSplit);
|
||||||
s->setValue(QLatin1String(displayFileEncodingKey), m_displayFileEncoding);
|
s->setValue(QLatin1String(displayFileEncodingKey), m_displayFileEncoding);
|
||||||
s->setValue(QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights);
|
s->setValue(QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights);
|
||||||
|
s->setValue(QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile);
|
||||||
s->endGroup();
|
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_openLinksInNextSplit = s->value(group + QLatin1String(openLinksInNextSplitKey), m_openLinksInNextSplit).toBool();
|
||||||
m_displayFileEncoding = s->value(group + QLatin1String(displayFileEncodingKey), m_displayFileEncoding).toBool();
|
m_displayFileEncoding = s->value(group + QLatin1String(displayFileEncodingKey), m_displayFileEncoding).toBool();
|
||||||
m_scrollBarHighlights = s->value(group + QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights).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
|
bool DisplaySettings::equals(const DisplaySettings &ds) const
|
||||||
@@ -111,6 +116,8 @@ bool DisplaySettings::equals(const DisplaySettings &ds) const
|
|||||||
&& m_forceOpenLinksInNextSplit == ds.m_forceOpenLinksInNextSplit
|
&& m_forceOpenLinksInNextSplit == ds.m_forceOpenLinksInNextSplit
|
||||||
&& m_displayFileEncoding == ds.m_displayFileEncoding
|
&& m_displayFileEncoding == ds.m_displayFileEncoding
|
||||||
&& m_scrollBarHighlights == ds.m_scrollBarHighlights
|
&& m_scrollBarHighlights == ds.m_scrollBarHighlights
|
||||||
|
&& m_animateNavigationWithinFile == ds.m_animateNavigationWithinFile
|
||||||
|
&& m_animateWithinFileTimeMax == ds.m_animateWithinFileTimeMax
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ public:
|
|||||||
bool m_forceOpenLinksInNextSplit = false;
|
bool m_forceOpenLinksInNextSplit = false;
|
||||||
bool m_displayFileEncoding = false;
|
bool m_displayFileEncoding = false;
|
||||||
bool m_scrollBarHighlights = true;
|
bool m_scrollBarHighlights = true;
|
||||||
|
bool m_animateNavigationWithinFile = false;
|
||||||
|
int m_animateWithinFileTimeMax = 333; // read only setting
|
||||||
|
|
||||||
bool equals(const DisplaySettings &ds) const;
|
bool equals(const DisplaySettings &ds) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ void DisplaySettingsPage::settingsFromUI(DisplaySettings &displaySettings,
|
|||||||
displaySettings.m_openLinksInNextSplit = d->m_page->openLinksInNextSplit->isChecked();
|
displaySettings.m_openLinksInNextSplit = d->m_page->openLinksInNextSplit->isChecked();
|
||||||
displaySettings.m_displayFileEncoding = d->m_page->displayFileEncoding->isChecked();
|
displaySettings.m_displayFileEncoding = d->m_page->displayFileEncoding->isChecked();
|
||||||
displaySettings.m_scrollBarHighlights = d->m_page->scrollBarHighlights->isChecked();
|
displaySettings.m_scrollBarHighlights = d->m_page->scrollBarHighlights->isChecked();
|
||||||
|
displaySettings.m_animateNavigationWithinFile = d->m_page->animateNavigationWithinFile->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplaySettingsPage::settingsToUI()
|
void DisplaySettingsPage::settingsToUI()
|
||||||
@@ -140,6 +141,7 @@ void DisplaySettingsPage::settingsToUI()
|
|||||||
d->m_page->openLinksInNextSplit->setChecked(displaySettings.m_openLinksInNextSplit);
|
d->m_page->openLinksInNextSplit->setChecked(displaySettings.m_openLinksInNextSplit);
|
||||||
d->m_page->displayFileEncoding->setChecked(displaySettings.m_displayFileEncoding);
|
d->m_page->displayFileEncoding->setChecked(displaySettings.m_displayFileEncoding);
|
||||||
d->m_page->scrollBarHighlights->setChecked(displaySettings.m_scrollBarHighlights);
|
d->m_page->scrollBarHighlights->setChecked(displaySettings.m_scrollBarHighlights);
|
||||||
|
d->m_page->animateNavigationWithinFile->setChecked(displaySettings.m_animateNavigationWithinFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DisplaySettings &DisplaySettingsPage::displaySettings() const
|
const DisplaySettings &DisplaySettingsPage::displaySettings() const
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>501</width>
|
<width>501</width>
|
||||||
<height>331</height>
|
<height>339</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
@@ -174,6 +174,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QCheckBox" name="animateNavigationWithinFile">
|
||||||
|
<property name="text">
|
||||||
|
<string>Animate navigation within file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -99,7 +99,9 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPrintDialog>
|
#include <QPrintDialog>
|
||||||
#include <QPrinter>
|
#include <QPrinter>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
#include <QDrag>
|
#include <QDrag>
|
||||||
|
#include <QSequentialAnimationGroup>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
@@ -457,6 +459,8 @@ public:
|
|||||||
bool m_assistRelevantContentAdded = false;
|
bool m_assistRelevantContentAdded = false;
|
||||||
QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
|
QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
|
||||||
|
|
||||||
|
QPointer<QSequentialAnimationGroup> m_navigationAnimation;
|
||||||
|
|
||||||
QPointer<TextEditorAnimator> m_bracketsAnimator;
|
QPointer<TextEditorAnimator> m_bracketsAnimator;
|
||||||
|
|
||||||
// Animation and highlighting of auto completed text
|
// Animation and highlighting of auto completed text
|
||||||
@@ -2515,7 +2519,7 @@ void TextEditorWidget::doSetTextCursor(const QTextCursor &cursor)
|
|||||||
doSetTextCursor(cursor, false);
|
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
|
d->m_lastCursorChangeWasInteresting = false; // avoid adding the previous position to history
|
||||||
const int blockNumber = qMin(line, document()->blockCount()) - 1;
|
const int blockNumber = qMin(line, document()->blockCount()) - 1;
|
||||||
@@ -2531,12 +2535,61 @@ void TextEditorWidget::gotoLine(int line, int column, bool centerLine)
|
|||||||
}
|
}
|
||||||
cursor.setPosition(pos);
|
cursor.setPosition(pos);
|
||||||
}
|
}
|
||||||
setTextCursor(cursor);
|
|
||||||
|
|
||||||
if (centerLine)
|
const DisplaySettings &ds = d->m_displaySettings;
|
||||||
centerCursor();
|
if (animate && ds.m_animateNavigationWithinFile) {
|
||||||
else
|
const QScrollBar *scrollBar = verticalScrollBar();
|
||||||
ensureCursorVisible();
|
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();
|
d->saveCurrentCursorPositionForNavigation();
|
||||||
}
|
}
|
||||||
@@ -5224,7 +5277,12 @@ void TextEditorWidget::extraAreaMouseEvent(QMouseEvent *e)
|
|||||||
|
|
||||||
void TextEditorWidget::ensureCursorVisible()
|
void TextEditorWidget::ensureCursorVisible()
|
||||||
{
|
{
|
||||||
QTextBlock block = textCursor().block();
|
ensureBlockIsUnfolded(textCursor().block());
|
||||||
|
QPlainTextEdit::ensureCursorVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextEditorWidget::ensureBlockIsUnfolded(QTextBlock block)
|
||||||
|
{
|
||||||
if (!block.isVisible()) {
|
if (!block.isVisible()) {
|
||||||
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout());
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout());
|
||||||
QTC_ASSERT(documentLayout, return);
|
QTC_ASSERT(documentLayout, return);
|
||||||
@@ -5246,7 +5304,6 @@ void TextEditorWidget::ensureCursorVisible()
|
|||||||
documentLayout->requestUpdate();
|
documentLayout->requestUpdate();
|
||||||
documentLayout->emitDocumentSizeChanged();
|
documentLayout->emitDocumentSizeChanged();
|
||||||
}
|
}
|
||||||
QPlainTextEdit::ensureCursorVisible();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEditorWidgetPrivate::toggleBlockVisible(const QTextBlock &block)
|
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) {
|
if (!inNextSplit && textDocument()->filePath().toString() == link.targetFileName) {
|
||||||
EditorManager::addCurrentPositionToNavigationHistory();
|
EditorManager::addCurrentPositionToNavigationHistory();
|
||||||
gotoLine(link.targetLine, link.targetColumn);
|
gotoLine(link.targetLine, link.targetColumn, true, true);
|
||||||
setFocus();
|
setFocus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ public:
|
|||||||
// IEditor
|
// IEditor
|
||||||
QByteArray saveState() const;
|
QByteArray saveState() const;
|
||||||
bool restoreState(const QByteArray &state);
|
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 position(TextPositionOperation posOp = CurrentPosition,
|
||||||
int at = -1) const;
|
int at = -1) const;
|
||||||
void convertPosition(int pos, int *line, int *column) const;
|
void convertPosition(int pos, int *line, int *column) const;
|
||||||
@@ -297,6 +297,7 @@ public:
|
|||||||
const BehaviorSettings &behaviorSettings() const;
|
const BehaviorSettings &behaviorSettings() const;
|
||||||
|
|
||||||
void ensureCursorVisible();
|
void ensureCursorVisible();
|
||||||
|
void ensureBlockIsUnfolded(QTextBlock block);
|
||||||
|
|
||||||
static Core::Id FakeVimSelection;
|
static Core::Id FakeVimSelection;
|
||||||
static Core::Id SnippetPlaceholderSelection;
|
static Core::Id SnippetPlaceholderSelection;
|
||||||
|
|||||||
Reference in New Issue
Block a user