diff options
-rw-r--r-- | src/plugins/texteditor/displaysettings.cpp | 9 | ||||
-rw-r--r-- | src/plugins/texteditor/displaysettings.h | 13 | ||||
-rw-r--r-- | src/plugins/texteditor/displaysettingspage.cpp | 11 | ||||
-rw-r--r-- | src/plugins/texteditor/displaysettingspage.ui | 126 | ||||
-rw-r--r-- | src/plugins/texteditor/texteditor.cpp | 84 | ||||
-rw-r--r-- | src/plugins/texteditor/textmark.cpp | 67 | ||||
-rw-r--r-- | src/plugins/texteditor/textmark.h | 8 |
7 files changed, 227 insertions, 91 deletions
diff --git a/src/plugins/texteditor/displaysettings.cpp b/src/plugins/texteditor/displaysettings.cpp index 196b9434c2f..3d2eb5c084f 100644 --- a/src/plugins/texteditor/displaysettings.cpp +++ b/src/plugins/texteditor/displaysettings.cpp @@ -45,6 +45,8 @@ static const char scrollBarHighlightsKey[] = "ScrollBarHighlights"; static const char animateNavigationWithinFileKey[] = "AnimateNavigationWithinFile"; static const char animateWithinFileTimeMaxKey[] = "AnimateWithinFileTimeMax"; static const char displayAnnotationsKey[] = "DisplayAnnotations"; +static const char annotationAlignmentKey[] = "AnnotationAlignment"; +static const char minimalAnnotationContentKey[] = "MinimalAnnotationContent"; static const char groupPostfix[] = "DisplaySettings"; namespace TextEditor { @@ -71,6 +73,7 @@ void DisplaySettings::toSettings(const QString &category, QSettings *s) const s->setValue(QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights); s->setValue(QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile); s->setValue(QLatin1String(displayAnnotationsKey), m_displayAnnotations); + s->setValue(QLatin1String(annotationAlignmentKey), static_cast<int>(m_annotationAlignment)); s->endGroup(); } @@ -100,6 +103,10 @@ void DisplaySettings::fromSettings(const QString &category, const QSettings *s) m_animateNavigationWithinFile = s->value(group + QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile).toBool(); m_animateWithinFileTimeMax = s->value(group + QLatin1String(animateWithinFileTimeMaxKey), m_animateWithinFileTimeMax).toInt(); m_displayAnnotations = s->value(group + QLatin1String(displayAnnotationsKey), m_displayAnnotations).toBool(); + m_annotationAlignment = static_cast<TextEditor::AnnotationAlignment>( + s->value(group + QLatin1String(annotationAlignmentKey), + static_cast<int>(m_annotationAlignment)).toInt()); + m_minimalAnnotationContent = s->value(group + QLatin1String(minimalAnnotationContentKey), m_minimalAnnotationContent).toInt(); } bool DisplaySettings::equals(const DisplaySettings &ds) const @@ -122,6 +129,8 @@ bool DisplaySettings::equals(const DisplaySettings &ds) const && m_animateNavigationWithinFile == ds.m_animateNavigationWithinFile && m_animateWithinFileTimeMax == ds.m_animateWithinFileTimeMax && m_displayAnnotations == ds.m_displayAnnotations + && m_annotationAlignment == ds.m_annotationAlignment + && m_minimalAnnotationContent == ds.m_minimalAnnotationContent ; } diff --git a/src/plugins/texteditor/displaysettings.h b/src/plugins/texteditor/displaysettings.h index a37343bb486..326f9aad069 100644 --- a/src/plugins/texteditor/displaysettings.h +++ b/src/plugins/texteditor/displaysettings.h @@ -27,12 +27,21 @@ #include "texteditor_global.h" +#include "QMetaType" + QT_BEGIN_NAMESPACE class QSettings; QT_END_NAMESPACE namespace TextEditor { +enum class AnnotationAlignment +{ + NextToContent, + NextToMargin, + RightSide +}; + class TEXTEDITOR_EXPORT DisplaySettings { public: @@ -59,6 +68,8 @@ public: bool m_animateNavigationWithinFile = false; int m_animateWithinFileTimeMax = 333; // read only setting bool m_displayAnnotations = true; + AnnotationAlignment m_annotationAlignment = AnnotationAlignment::RightSide; + int m_minimalAnnotationContent = 15; bool equals(const DisplaySettings &ds) const; }; @@ -67,3 +78,5 @@ inline bool operator==(const DisplaySettings &t1, const DisplaySettings &t2) { r inline bool operator!=(const DisplaySettings &t1, const DisplaySettings &t2) { return !t1.equals(t2); } } // namespace TextEditor + +Q_DECLARE_METATYPE(TextEditor::AnnotationAlignment) diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp index 7ae27e11d73..2289ba2135a 100644 --- a/src/plugins/texteditor/displaysettingspage.cpp +++ b/src/plugins/texteditor/displaysettingspage.cpp @@ -120,6 +120,12 @@ void DisplaySettingsPage::settingsFromUI(DisplaySettings &displaySettings, displaySettings.m_scrollBarHighlights = d->m_page->scrollBarHighlights->isChecked(); displaySettings.m_animateNavigationWithinFile = d->m_page->animateNavigationWithinFile->isChecked(); displaySettings.m_displayAnnotations = d->m_page->displayAnnotations->isChecked(); + if (d->m_page->leftAligned->isChecked()) + displaySettings.m_annotationAlignment = AnnotationAlignment::NextToContent; + else if (d->m_page->atMargin->isChecked()) + displaySettings.m_annotationAlignment = AnnotationAlignment::NextToMargin; + else if (d->m_page->rightAligned->isChecked()) + displaySettings.m_annotationAlignment = AnnotationAlignment::RightSide; } void DisplaySettingsPage::settingsToUI() @@ -144,6 +150,11 @@ void DisplaySettingsPage::settingsToUI() d->m_page->scrollBarHighlights->setChecked(displaySettings.m_scrollBarHighlights); d->m_page->animateNavigationWithinFile->setChecked(displaySettings.m_animateNavigationWithinFile); d->m_page->displayAnnotations->setChecked(displaySettings.m_displayAnnotations); + switch (displaySettings.m_annotationAlignment) { + case AnnotationAlignment::NextToContent: d->m_page->leftAligned->setChecked(true); break; + case AnnotationAlignment::NextToMargin: d->m_page->atMargin->setChecked(true); break; + case AnnotationAlignment::RightSide: d->m_page->rightAligned->setChecked(true); break; + } } const DisplaySettings &DisplaySettingsPage::displaySettings() const diff --git a/src/plugins/texteditor/displaysettingspage.ui b/src/plugins/texteditor/displaysettingspage.ui index 1663840272e..aa8dd2cd446 100644 --- a/src/plugins/texteditor/displaysettingspage.ui +++ b/src/plugins/texteditor/displaysettingspage.ui @@ -6,11 +6,24 @@ <rect> <x>0</x> <y>0</y> - <width>501</width> - <height>339</height> + <width>452</width> + <height>458</height> </rect> </property> <layout class="QGridLayout" name="gridLayout_3"> + <item row="3" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>8</height> + </size> + </property> + </spacer> + </item> <item row="0" column="0"> <widget class="QGroupBox" name="groupBoxText"> <property name="title"> @@ -61,43 +74,30 @@ </layout> </widget> </item> - <item row="2" column="0"> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>8</height> - </size> - </property> - </spacer> - </item> <item row="1" column="0"> <widget class="QGroupBox" name="groupBoxDisplay"> <property name="title"> <string>Display</string> </property> <layout class="QGridLayout" name="gridLayout_2"> - <item row="4" column="1"> - <widget class="QCheckBox" name="highlightMatchingParentheses"> + <item row="6" column="1"> + <widget class="QCheckBox" name="displayFileEncoding"> <property name="text"> - <string>&Highlight matching parentheses</string> + <string>Display file encoding</string> </property> </widget> </item> - <item row="1" column="1"> - <widget class="QCheckBox" name="highlightBlocks"> + <item row="2" column="1"> + <widget class="QCheckBox" name="animateMatchingParentheses"> <property name="text"> - <string>Highlight &blocks</string> + <string>&Animate matching parentheses</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="highlightCurrentLine"> + <item row="5" column="0"> + <widget class="QCheckBox" name="centerOnScroll"> <property name="text"> - <string>Highlight current &line</string> + <string>Center &cursor on scroll</string> </property> </widget> </item> @@ -108,17 +108,10 @@ </property> </widget> </item> - <item row="6" column="0"> - <widget class="QCheckBox" name="autoFoldFirstComment"> - <property name="text"> - <string>Auto-fold first &comment</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="animateMatchingParentheses"> + <item row="4" column="1"> + <widget class="QCheckBox" name="highlightMatchingParentheses"> <property name="text"> - <string>&Animate matching parentheses</string> + <string>&Highlight matching parentheses</string> </property> </widget> </item> @@ -129,27 +122,34 @@ </property> </widget> </item> - <item row="6" column="1"> - <widget class="QCheckBox" name="displayFileEncoding"> + <item row="4" column="0"> + <widget class="QCheckBox" name="visualizeWhitespace"> + <property name="toolTip"> + <string>Shows tabs and spaces.</string> + </property> <property name="text"> - <string>Display file encoding</string> + <string>&Visualize whitespace</string> </property> </widget> </item> - <item row="5" column="0"> - <widget class="QCheckBox" name="centerOnScroll"> + <item row="0" column="1"> + <widget class="QCheckBox" name="highlightCurrentLine"> <property name="text"> - <string>Center &cursor on scroll</string> + <string>Highlight current &line</string> </property> </widget> </item> - <item row="4" column="0"> - <widget class="QCheckBox" name="visualizeWhitespace"> - <property name="toolTip"> - <string>Shows tabs and spaces.</string> + <item row="6" column="0"> + <widget class="QCheckBox" name="autoFoldFirstComment"> + <property name="text"> + <string>Auto-fold first &comment</string> </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="highlightBlocks"> <property name="text"> - <string>&Visualize whitespace</string> + <string>Highlight &blocks</string> </property> </widget> </item> @@ -181,10 +181,39 @@ </property> </widget> </item> - <item row="8" column="0"> - <widget class="QCheckBox" name="displayAnnotations"> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QGroupBox" name="displayAnnotations"> + <property name="title"> + <string>Annotations next to lines</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QRadioButton" name="leftAligned"> + <property name="text"> + <string>Next to editor content</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="atMargin"> + <property name="text"> + <string>Next to right margin</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="rightAligned"> <property name="text"> - <string>Display annotations behind lines</string> + <string>Aligned at right side</string> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> </item> @@ -204,7 +233,6 @@ <tabstop>centerOnScroll</tabstop> <tabstop>autoFoldFirstComment</tabstop> <tabstop>scrollBarHighlights</tabstop> - <tabstop>displayAnnotations</tabstop> <tabstop>highlightCurrentLine</tabstop> <tabstop>highlightBlocks</tabstop> <tabstop>animateMatchingParentheses</tabstop> diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index aa917a22221..692faa1c509 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -393,7 +393,7 @@ public: bool expanded, bool active, bool hovered) const; - void drawLineAnnotation(QPainter &painter, const QTextBlock &block); + void drawLineAnnotation(QPainter &painter, const QTextBlock &block, qreal start); void toggleBlockVisible(const QTextBlock &block); QRect foldBox(); @@ -484,6 +484,7 @@ public: Id m_tabSettingsId; ICodeStylePreferences *m_codeStylePreferences = nullptr; DisplaySettings m_displaySettings; + bool m_annotationsrRight = true; MarginSettings m_marginSettings; // apply when making visible the first time, for the split case bool m_fontSettingsNeedsApply = true; @@ -506,6 +507,7 @@ public: const TextMark *mark; }; QMap<int, QList<AnnotationRect>> m_annotationRects; + QRectF getLastLineLineRect(const QTextBlock &block); RefactorOverlay *m_refactorOverlay = nullptr; QString m_contextHelpId; @@ -3788,7 +3790,41 @@ static QTextLayout::FormatRange createBlockCursorCharFormatRange(int pos, const return o; } -void TextEditorWidgetPrivate::drawLineAnnotation(QPainter &painter, const QTextBlock &block) +static TextMarks availableMarks(const TextMarks &marks, + QRectF &boundingRect, + const QFontMetrics &fm, + const qreal itemOffset) +{ + TextMarks ret; + bool first = true; + for (TextMark *mark : marks) { + const TextMark::AnnotationRects &rects = mark->annotationRects( + boundingRect, fm, first ? 0 : itemOffset, 0); + if (rects.annotationRect.isEmpty()) + break; + boundingRect.setLeft(rects.fadeOutRect.right()); + ret.append(mark); + if (boundingRect.isEmpty()) + break; + first = false; + } + return ret; +} + +QRectF TextEditorWidgetPrivate::getLastLineLineRect(const QTextBlock &block) +{ + const QTextLayout *layout = block.layout(); + const int lineCount = layout->lineCount(); + if (lineCount < 1) + return QRectF(); + const QTextLine line = layout->lineAt(lineCount - 1); + const QPointF contentOffset = q->contentOffset(); + const qreal top = q->blockBoundingGeometry(block).translated(contentOffset).top(); + return line.naturalTextRect().translated(contentOffset.x(), top).adjusted(0, 0, -1, -1); +} + +void TextEditorWidgetPrivate::drawLineAnnotation( + QPainter &painter, const QTextBlock &block, qreal rightMargin) { if (!m_displaySettings.m_displayAnnotations) return; @@ -3801,30 +3837,44 @@ void TextEditorWidgetPrivate::drawLineAnnotation(QPainter &painter, const QTextB if (marks.isEmpty()) return; - const QTextLayout *layout = block.layout(); - const int lineCount = layout->lineCount(); - if (lineCount < 1) + const QRectF lineRect = getLastLineLineRect(block); + if (lineRect.isNull()) return; - const QTextLine line = layout->lineAt(lineCount - 1); - const QPointF contentOffset = q->contentOffset(); - const qreal top = q->blockBoundingGeometry(block).translated(contentOffset).top(); - const QRectF lineRect = - line.naturalTextRect().translated(contentOffset.x(), top).adjusted(0, 0, -1, -1); Utils::sort(marks, [](const TextMark* mark1, const TextMark* mark2){ return mark1->priority() > mark2->priority(); }); const qreal itemOffset = q->fontMetrics().lineSpacing(); - qreal x = lineRect.right() + itemOffset * 2; + const qreal initialOffset = itemOffset * 2; + const qreal minimalContentWidth = q->fontMetrics().width('X') + * m_displaySettings.m_minimalAnnotationContent; + QRectF boundingRect(lineRect.topLeft().x(), lineRect.topLeft().y(), + q->viewport()->width() - lineRect.right(), lineRect.height()); + qreal offset = initialOffset; + if (marks.isEmpty()) + return; + if (m_displaySettings.m_annotationAlignment == AnnotationAlignment::NextToMargin + && rightMargin > lineRect.right() + offset + && q->viewport()->width() > rightMargin + minimalContentWidth) { + offset = rightMargin - lineRect.right(); + } else if (m_displaySettings.m_annotationAlignment != AnnotationAlignment::NextToContent) { + marks = availableMarks(marks, boundingRect, q->fontMetrics(), itemOffset); + if (boundingRect.width() > 0) + offset = qMax(boundingRect.width(), initialOffset); + } + qreal x = lineRect.right(); for (const TextMark *mark : marks) { - QRectF annotationRect(x, lineRect.top(), q->viewport()->width() - x, lineRect.height()); - if (annotationRect.width() <= 0) + boundingRect = QRectF(x, lineRect.top(), q->viewport()->width() - x, lineRect.height()); + if (boundingRect.isEmpty()) break; - mark->paintAnnotation(&painter, &annotationRect); - x += annotationRect.width() + itemOffset; - m_annotationRects[block.blockNumber()].append({annotationRect, mark}); + + // paint annotation + mark->paintAnnotation(painter, &boundingRect, offset, itemOffset / 2); + x = boundingRect.right(); + offset = itemOffset / 2; + m_annotationRects[block.blockNumber()].append({boundingRect, mark}); } } @@ -4469,7 +4519,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) painter.restore(); } } - d->drawLineAnnotation(painter, block); + d->drawLineAnnotation(painter, block, lineX < viewportRect.width() ? lineX : 0); block = nextVisibleBlock; top = bottom; diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp index 76050e1902e..1e3c7bd4b50 100644 --- a/src/plugins/texteditor/textmark.cpp +++ b/src/plugins/texteditor/textmark.cpp @@ -121,43 +121,58 @@ void TextMark::paintIcon(QPainter *painter, const QRect &rect) const m_icon.paint(painter, rect, Qt::AlignCenter); } -void TextMark::paintAnnotation(QPainter *painter, QRectF *annotationRect) const +void TextMark::paintAnnotation(QPainter &painter, QRectF *annotationRect, + const qreal fadeInOffset, const qreal fadeOutOffset) const { QString text = lineAnnotation(); if (text.isEmpty()) return; - const AnnotationRects &rects = annotationRects(*annotationRect, painter->fontMetrics()); - - const QColor markColor = m_hasColor ? Utils::creatorTheme()->color(m_color).toHsl() - : painter->pen().color(); - const AnnotationColors &colors = - AnnotationColors::getAnnotationColors(markColor, painter->background().color()); - - painter->save(); - painter->setPen(colors.rectColor); - painter->setBrush(colors.rectColor); - painter->drawRect(rects.annotationRect); - painter->setPen(colors.textColor); - paintIcon(painter, rects.iconRect.toAlignedRect()); - painter->drawText(rects.textRect, Qt::AlignLeft, rects.text); - painter->restore(); - *annotationRect = rects.annotationRect; + const AnnotationRects &rects = annotationRects(*annotationRect, painter.fontMetrics(), + fadeInOffset, fadeOutOffset); + const QColor &markColor = m_hasColor ? Utils::creatorTheme()->color(m_color).toHsl() + : painter.pen().color(); + const AnnotationColors &colors = AnnotationColors::getAnnotationColors( + markColor, painter.background().color()); + + painter.save(); + QLinearGradient grad(rects.fadeInRect.topLeft(), rects.fadeInRect.topRight()); + grad.setColorAt(0.0, Qt::transparent); + grad.setColorAt(1.0, colors.rectColor); + painter.fillRect(rects.fadeInRect, grad); + painter.fillRect(rects.annotationRect, colors.rectColor); + painter.setPen(colors.textColor); + paintIcon(&painter, rects.iconRect.toAlignedRect()); + painter.drawText(rects.textRect, Qt::AlignLeft, rects.text); + if (rects.fadeOutRect.isValid()) { + grad = QLinearGradient(rects.fadeOutRect.topLeft(), rects.fadeOutRect.topRight()); + grad.setColorAt(0.0, colors.rectColor); + grad.setColorAt(1.0, Qt::transparent); + painter.fillRect(rects.fadeOutRect, grad); + } + painter.restore(); + annotationRect->setRight(rects.fadeOutRect.right()); } TextMark::AnnotationRects TextMark::annotationRects(const QRectF &boundingRect, - const QFontMetrics &fm) const + const QFontMetrics &fm, + const qreal fadeInOffset, + const qreal fadeOutOffset) const { AnnotationRects rects; - rects.annotationRect = boundingRect; rects.text = lineAnnotation(); + if (rects.text.isEmpty()) + return rects; + rects.fadeInRect = boundingRect; + rects.fadeInRect.setWidth(fadeInOffset); + rects.annotationRect = boundingRect; + rects.annotationRect.setLeft(rects.fadeInRect.right()); const bool drawIcon = !m_icon.isNull(); constexpr qreal margin = 1; - rects.iconRect = QRectF(boundingRect.left() + margin, boundingRect.top() + margin, 0, 0); - if (drawIcon) { - rects.iconRect.setHeight(boundingRect.height() - 2 * margin); + rects.iconRect = QRectF(rects.annotationRect.left(), boundingRect.top(), + 0, boundingRect.height()); + if (drawIcon) rects.iconRect.setWidth(rects.iconRect.height() * m_widthFactor); - } rects.textRect = QRectF(rects.iconRect.right() + margin, boundingRect.top(), qreal(fm.width(rects.text)), boundingRect.height()); rects.annotationRect.setRight(rects.textRect.right() + margin); @@ -165,6 +180,12 @@ TextMark::AnnotationRects TextMark::annotationRects(const QRectF &boundingRect, rects.textRect.setRight(boundingRect.right() - margin); rects.text = fm.elidedText(rects.text, Qt::ElideRight, int(rects.textRect.width())); rects.annotationRect.setRight(boundingRect.right()); + rects.fadeOutRect = QRectF(rects.annotationRect.topRight(), + rects.annotationRect.bottomRight()); + } else { + rects.fadeOutRect = boundingRect; + rects.fadeOutRect.setLeft(rects.annotationRect.right()); + rects.fadeOutRect.setWidth(fadeOutOffset); } return rects; } diff --git a/src/plugins/texteditor/textmark.h b/src/plugins/texteditor/textmark.h index ca484a85388..f744da7dc9b 100644 --- a/src/plugins/texteditor/textmark.h +++ b/src/plugins/texteditor/textmark.h @@ -64,15 +64,19 @@ public: int lineNumber() const; virtual void paintIcon(QPainter *painter, const QRect &rect) const; - virtual void paintAnnotation(QPainter *painter, QRectF *annotationRect) const; + virtual void paintAnnotation(QPainter &painter, QRectF *annotationRect, + const qreal fadeInOffset, const qreal fadeOutOffset) const; struct AnnotationRects { + QRectF fadeInRect; QRectF annotationRect; QRectF iconRect; QRectF textRect; + QRectF fadeOutRect; QString text; }; - virtual AnnotationRects annotationRects(const QRectF &boundingRect, const QFontMetrics &fm) const; + AnnotationRects annotationRects(const QRectF &boundingRect, const QFontMetrics &fm, + const qreal fadeInOffset, const qreal fadeOutOffset) const; /// called if the filename of the document changed virtual void updateFileName(const QString &fileName); virtual void updateLineNumber(int lineNumber); |