Commit d21cd57e authored by David Schulz's avatar David Schulz

Editor: Animate automatic completed text.

This gives a clear hint that something has changed.
And gets the attention of the author in the case he didn't
expect some kind of automatic brace or quote completion.

Change-Id: I33e383db9a1e797ecb285a407e46671f41be7051
Reviewed-by: default avatarAlessandro Portale <alessandro.portale@theqtcompany.com>
parent e108d6fe
......@@ -28,6 +28,7 @@
<style name="Operator" foreground="#aaaaaa"/>
<style name="Parentheses" foreground="#ff5555" background="#333333"/>
<style name="ParenthesesMismatch" background="#800080"/>
<style name="AutoComplete" foreground="#a0a0ff" background="#333333"/>
<style name="Preprocessor" foreground="#5555ff"/>
<style name="SearchResult" background="#555500"/>
<style name="SearchScope" background="#222200"/>
......
......@@ -25,6 +25,7 @@
<style name="Operator"/>
<style name="Parentheses" background="#e3e3e3" bold="true"/>
<style name="ParenthesesMismatch" background="#808080"/>
<style name="AutoComplete" foreground="#303030" background="#d0d0d0"/>
<style name="Preprocessor" foreground="#5b5b5b" bold="true"/>
<style name="RemovedLine" foreground="#a0a0a4"/>
<style name="Static" italic="true"/>
......
......@@ -34,6 +34,7 @@
<style name="Operator" foreground="#cfbfad"/>
<style name="Parentheses" foreground="#ffff00" background="#4e4e8f"/>
<style name="ParenthesesMismatch" background="#404040"/>
<style name="AutoComplete" foreground="#ffff00" background="#4e4e8f"/>
<style name="Preprocessor" foreground="#409090"/>
<style name="RemovedLine" foreground="#ff0000"/>
<style name="SearchResult" foreground="#000000" background="#ffef0b"/>
......
......@@ -17,6 +17,7 @@
<style name="Operator" foreground="#000000"/>
<style name="Parentheses" foreground="#ff0000" background="#c3e1ff"/>
<style name="ParenthesesMismatch" background="#ff00ff"/>
<style name="AutoComplete" foreground="#ff0000" background="#c3e1ff"/>
<style name="Preprocessor" foreground="#000080" bold="true"/>
<style name="RemovedLine" foreground="#ff0000"/>
<style name="String" foreground="#008000" bold="true"/>
......
......@@ -38,6 +38,7 @@ static const char surroundingAutoQuotesKey[] = "SurroundingAutoQuotes";
static const char partiallyCompleteKey[] = "PartiallyComplete";
static const char spaceAfterFunctionNameKey[] = "SpaceAfterFunctionName";
static const char autoSplitStringsKey[] = "AutoSplitStrings";
static const char animateAutoCompleteKey[] = "AnimateAutoComplete";
using namespace TextEditor;
......@@ -54,6 +55,7 @@ void CompletionSettings::toSettings(QSettings *s) const
s->setValue(partiallyCompleteKey, m_partiallyComplete);
s->setValue(spaceAfterFunctionNameKey, m_spaceAfterFunctionName);
s->setValue(autoSplitStringsKey, m_autoSplitStrings);
s->setValue(animateAutoCompleteKey, m_animateAutoComplete);
s->endGroup();
}
......@@ -82,6 +84,8 @@ void CompletionSettings::fromSettings(QSettings *s)
s->value(spaceAfterFunctionNameKey, m_spaceAfterFunctionName).toBool();
m_autoSplitStrings =
s->value(autoSplitStringsKey, m_autoSplitStrings).toBool();
m_animateAutoComplete =
s->value(animateAutoCompleteKey, m_animateAutoComplete).toBool();
s->endGroup();
}
......@@ -97,5 +101,6 @@ bool CompletionSettings::equals(const CompletionSettings &cs) const
&& m_partiallyComplete == cs.m_partiallyComplete
&& m_spaceAfterFunctionName == cs.m_spaceAfterFunctionName
&& m_autoSplitStrings == cs.m_autoSplitStrings
&& m_animateAutoComplete == cs.m_animateAutoComplete
;
}
......@@ -66,6 +66,7 @@ public:
bool m_partiallyComplete = true;
bool m_spaceAfterFunctionName = false;
bool m_autoSplitStrings = true;
bool m_animateAutoComplete = true;
};
inline bool operator==(const CompletionSettings &t1, const CompletionSettings &t2) { return t1.equals(t2); }
......
......@@ -102,6 +102,7 @@ QWidget *CompletionSettingsPage::widget()
m_page->partiallyComplete->setChecked(m_completionSettings.m_partiallyComplete);
m_page->spaceAfterFunctionName->setChecked(m_completionSettings.m_spaceAfterFunctionName);
m_page->autoSplitStrings->setChecked(m_completionSettings.m_autoSplitStrings);
m_page->animateAutoComplete->setChecked(m_completionSettings.m_animateAutoComplete);
m_page->enableDoxygenCheckBox->setChecked(m_commentsSettings.m_enableDoxygen);
m_page->generateBriefCheckBox->setChecked(m_commentsSettings.m_generateBrief);
......@@ -175,6 +176,7 @@ void CompletionSettingsPage::settingsFromUi(CompletionSettings &completion, Comm
completion.m_partiallyComplete = m_page->partiallyComplete->isChecked();
completion.m_spaceAfterFunctionName = m_page->spaceAfterFunctionName->isChecked();
completion.m_autoSplitStrings = m_page->autoSplitStrings->isChecked();
completion.m_animateAutoComplete = m_page->animateAutoComplete->isChecked();
comment.m_enableDoxygen = m_page->enableDoxygenCheckBox->isChecked();
comment.m_generateBrief = m_page->generateBriefCheckBox->isChecked();
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TextEditor::Internal::CompletionSettingsPage</class>
<widget class="QWidget" name="CppTools::Internal::CompletionSettingsPage">
<widget class="QWidget" name="TextEditor::Internal::CompletionSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
......@@ -214,6 +214,19 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="animateAutoComplete">
<property name="toolTip">
<string>Show a visual hint when for example a brace or a quote is automatically inserted by the editor.</string>
</property>
<property name="text">
<string>Animate automatically inserted text</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
......@@ -302,6 +315,7 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
<tabstop>surroundBrackets</tabstop>
<tabstop>surroundQuotes</tabstop>
<tabstop>spaceAfterFunctionName</tabstop>
<tabstop>animateAutoComplete</tabstop>
<tabstop>enableDoxygenCheckBox</tabstop>
<tabstop>generateBriefCheckBox</tabstop>
<tabstop>leadingAsterisksCheckBox</tabstop>
......
......@@ -263,6 +263,8 @@ QColor FormatDescription::foreground() const
return m_format.foreground();
} else if (m_id == C_PARENTHESES) {
return QColor(Qt::red);
} else if (m_id == C_AUTOCOMPLETE) {
return QColor(Qt::darkBlue);
}
return m_format.foreground();
}
......@@ -279,6 +281,8 @@ QColor FormatDescription::background() const
return QColor(0xb4, 0xee, 0xb4);
} else if (m_id == C_PARENTHESES_MISMATCH) {
return QColor(Qt::magenta);
} else if (m_id == C_AUTOCOMPLETE) {
return QColor(192, 192, 255);
} else if (m_id == C_CURRENT_LINE || m_id == C_SEARCH_SCOPE) {
const QPalette palette = QApplication::palette();
const QColor &fg = palette.color(QPalette::Highlight);
......
......@@ -329,6 +329,9 @@ public:
// parentheses matcher
void _q_matchParentheses();
void _q_highlightBlocks();
void _q_autocompleterHighlight(const QTextCursor &cursor = QTextCursor());
void updateAnimator(QPointer<TextEditorAnimator> animator, QPainter &painter);
void cancelCurrentAnimations();
void slotSelectionChanged();
void _q_animateUpdate(int position, QPointF lastPos, QRectF rect);
void updateCodeFoldingVisible();
......@@ -450,7 +453,9 @@ public:
bool m_assistRelevantContentAdded;
QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
QPointer<TextEditorAnimator> m_animator;
QPointer<TextEditorAnimator> m_bracketsAnimator;
QPointer<TextEditorAnimator> m_autocompleteAnimator;
bool m_animateAutoComplete = true;
int m_cursorBlockNumber;
int m_blockCount;
......@@ -738,7 +743,8 @@ void TextEditorWidgetPrivate::ctor(const QSharedPointer<TextDocument> &doc)
QObject::connect(&m_scrollBarUpdateTimer, &QTimer::timeout,
this, &TextEditorWidgetPrivate::highlightSearchResultsInScrollBar);
m_animator = 0;
m_bracketsAnimator = 0;
m_autocompleteAnimator = 0;
slotUpdateExtraAreaWidth();
updateHighlights();
......@@ -1104,8 +1110,8 @@ TextEditorWidget *TextEditorWidget::currentTextEditorWidget()
void TextEditorWidgetPrivate::editorContentsChange(int position, int charsRemoved, int charsAdded)
{
if (m_animator)
m_animator->finish();
if (m_bracketsAnimator)
m_bracketsAnimator->finish();
m_contentsChanged = true;
QTextDocument *doc = q->document();
......@@ -2152,6 +2158,13 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
d->m_document->autoIndent(ensureVisible);
else if (!previousIndentationString.isEmpty())
ensureVisible.insertText(previousIndentationString);
if (d->m_animateAutoComplete) {
QTextCursor tc = ensureVisible;
tc.movePosition(QTextCursor::EndOfBlock);
tc.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
tc.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
d->_q_autocompleterHighlight(tc);
}
}
setTextCursor(ensureVisible);
}
......@@ -2424,6 +2437,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
cursor.insertText(autoText);
//Select the inserted text, to be able to re-indent the inserted text
cursor.setPosition(pos, QTextCursor::KeepAnchor);
d->_q_autocompleterHighlight(cursor);
}
if (!electricChar.isNull() && d->m_autoCompleter->contextAllowsElectricCharacters(cursor))
d->m_document->autoIndent(cursor, electricChar);
......@@ -4224,11 +4238,8 @@ void TextEditorWidget::paintEvent(QPaintEvent *e)
bottom = top + blockBoundingRect(block).height();
}
if (d->m_animator && d->m_animator->isRunning()) {
QTextCursor cursor = textCursor();
cursor.setPosition(d->m_animator->position());
d->m_animator->draw(&painter, cursorRect(cursor).topLeft());
}
d->updateAnimator(d->m_bracketsAnimator, painter);
d->updateAnimator(d->m_autocompleteAnimator, painter);
// draw the overlays, but only if we do not have a find scope, otherwise the
// view becomes too noisy.
......@@ -4735,7 +4746,7 @@ void TextEditorWidgetPrivate::updateHighlights()
if (m_parenthesesMatchingEnabled && q->hasFocus()) {
// Delay update when no matching is displayed yet, to avoid flicker
if (q->extraSelections(TextEditorWidget::ParenthesesMatchingSelection).isEmpty()
&& m_animator == 0) {
&& m_bracketsAnimator == 0) {
m_parenthesesMatchingTimer.start(50);
} else {
// when we uncheck "highlight matching parentheses"
......@@ -5995,15 +6006,15 @@ void TextEditorWidgetPrivate::_q_matchParentheses()
}
if (animatePosition >= 0) {
if (m_animator)
m_animator->finish(); // one animation is enough
m_animator = new TextEditorAnimator(this);
m_animator->setPosition(animatePosition);
cancelCurrentAnimations();// one animation is enough
m_bracketsAnimator = new TextEditorAnimator(this);
m_bracketsAnimator->setPosition(animatePosition);
QPalette pal;
pal.setBrush(QPalette::Text, matchFormat.foreground());
pal.setBrush(QPalette::Base, matchFormat.background());
m_animator->setData(q->font(), pal, q->document()->characterAt(m_animator->position()));
connect(m_animator.data(), &TextEditorAnimator::updateRequest,
m_bracketsAnimator->setData(
q->font(), pal, q->document()->characterAt(m_bracketsAnimator->position()));
connect(m_bracketsAnimator.data(), &TextEditorAnimator::updateRequest,
this, &TextEditorWidgetPrivate::_q_animateUpdate);
}
if (m_displaySettings.m_highlightMatchingParentheses)
......@@ -6071,6 +6082,43 @@ void TextEditorWidgetPrivate::_q_highlightBlocks()
}
}
void TextEditorWidgetPrivate::_q_autocompleterHighlight(const QTextCursor &cursor)
{
if (!m_animateAutoComplete || q->isReadOnly() || !cursor.hasSelection())
return;
const QTextCharFormat &matchFormat
= q->textDocument()->fontSettings().toTextCharFormat(C_AUTOCOMPLETE);
cancelCurrentAnimations();// one animation is enough
m_autocompleteAnimator = new TextEditorAnimator(this);
m_autocompleteAnimator->setPosition(cursor.selectionStart());
QPalette pal;
pal.setBrush(QPalette::Text, matchFormat.foreground());
pal.setBrush(QPalette::Base, matchFormat.background());
m_autocompleteAnimator->setData(q->font(), pal, cursor.selectedText());
connect(m_autocompleteAnimator.data(), &TextEditorAnimator::updateRequest,
this, &TextEditorWidgetPrivate::_q_animateUpdate);
}
void TextEditorWidgetPrivate::updateAnimator(QPointer<TextEditorAnimator> animator,
QPainter &painter)
{
if (animator && animator->isRunning()) {
QTextCursor cursor = q->textCursor();
cursor.setPosition(animator->position());
animator->draw(&painter, q->cursorRect(cursor).topLeft());
}
}
void TextEditorWidgetPrivate::cancelCurrentAnimations()
{
if (m_autocompleteAnimator)
m_autocompleteAnimator->finish();
if (m_bracketsAnimator)
m_bracketsAnimator->finish();
}
void TextEditorWidget::changeEvent(QEvent *e)
{
QPlainTextEdit::changeEvent(e);
......@@ -6549,6 +6597,7 @@ void TextEditorWidget::setCompletionSettings(const CompletionSettings &completio
d->m_autoCompleter->setSurroundWithBracketsEnabled(completionSettings.m_surroundingAutoBrackets);
d->m_autoCompleter->setAutoInsertQuotesEnabled(completionSettings.m_autoInsertQuotes);
d->m_autoCompleter->setSurroundWithQuotesEnabled(completionSettings.m_surroundingAutoQuotes);
d->m_animateAutoComplete = completionSettings.m_animateAutoComplete;
}
void TextEditorWidget::setExtraEncodingSettings(const ExtraEncodingSettings &extraEncodingSettings)
......
......@@ -42,6 +42,7 @@ const char *nameForStyle(TextStyle style)
case C_SEARCH_SCOPE: return "SearchScope";
case C_PARENTHESES: return "Parentheses";
case C_PARENTHESES_MISMATCH:return "ParenthesesMismatch";
case C_AUTOCOMPLETE: return "AutoComplete";
case C_CURRENT_LINE: return "CurrentLine";
case C_CURRENT_LINE_NUMBER: return "CurrentLineNumber";
case C_OCCURRENCES: return "Occurrences";
......
......@@ -40,6 +40,7 @@ enum TextStyle : quint8 {
C_SEARCH_SCOPE,
C_PARENTHESES,
C_PARENTHESES_MISMATCH,
C_AUTOCOMPLETE,
C_CURRENT_LINE,
C_CURRENT_LINE_NUMBER,
C_OCCURRENCES,
......
......@@ -118,6 +118,9 @@ TextEditorSettings::TextEditorSettings(QObject *parent)
formatDescr.emplace_back(C_PARENTHESES_MISMATCH, tr("Mismatched Parentheses"),
tr("Displayed when mismatched parentheses, "
"square brackets, or curly brackets are found."));
formatDescr.emplace_back(C_AUTOCOMPLETE, tr("Auto Complete"),
tr("Displayed when a character is automatically inserted "
"like brackets or quotes."));
formatDescr.emplace_back(C_CURRENT_LINE, tr("Current Line"),
tr("Line where the cursor is placed in."),
FormatDescription::ShowBackgroundControl);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment