forked from qt-creator/qt-creator
TextEditor: Add line annotations
Displaying short descriptive text of a TextMark at line end. Currently implemented for ClangTextMark and BookMark. Change-Id: Idc6b579bda0382ad94b2e236b715696396b10460 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
@@ -84,6 +84,8 @@ void Bookmark::updateFileName(const QString &fileName)
|
||||
void Bookmark::setNote(const QString ¬e)
|
||||
{
|
||||
setToolTip(note);
|
||||
setLineAnnotation(note);
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
void Bookmark::updateNote(const QString ¬e)
|
||||
|
||||
@@ -79,6 +79,7 @@ ClangTextMark::ClangTextMark(const QString &fileName,
|
||||
setPriority(warning ? TextEditor::TextMark::NormalPriority
|
||||
: TextEditor::TextMark::HighPriority);
|
||||
setIcon(diagnostic.severity());
|
||||
setLineAnnotation(diagnostic.text().toString());
|
||||
}
|
||||
|
||||
void ClangTextMark::setIcon(ClangBackEnd::DiagnosticSeverity severity)
|
||||
@@ -96,7 +97,7 @@ void ClangTextMark::setIcon(ClangBackEnd::DiagnosticSeverity severity)
|
||||
TextMark::setIcon(errorIcon);
|
||||
}
|
||||
|
||||
bool ClangTextMark::addToolTipContent(QLayout *target)
|
||||
bool ClangTextMark::addToolTipContent(QLayout *target) const
|
||||
{
|
||||
using Internal::ClangDiagnosticWidget;
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
private:
|
||||
void setIcon(ClangBackEnd::DiagnosticSeverity severity);
|
||||
bool addToolTipContent(QLayout *target) override;
|
||||
bool addToolTipContent(QLayout *target) const override;
|
||||
void removedFromEditor() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -44,7 +44,7 @@ void QmlProfilerTextMark::addTypeId(int typeId)
|
||||
m_typeIds.append(typeId);
|
||||
}
|
||||
|
||||
void QmlProfilerTextMark::paint(QPainter *painter, const QRect &paintRect) const
|
||||
void QmlProfilerTextMark::paintIcon(QPainter *painter, const QRect &paintRect) const
|
||||
{
|
||||
painter->save();
|
||||
painter->setPen(Qt::black);
|
||||
@@ -108,7 +108,7 @@ void QmlProfilerTextMarkModel::createMarks(QmlProfilerTool *tool, const QString
|
||||
}
|
||||
}
|
||||
|
||||
bool QmlProfilerTextMark::addToolTipContent(QLayout *target)
|
||||
bool QmlProfilerTextMark::addToolTipContent(QLayout *target) const
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
layout->setHorizontalSpacing(10);
|
||||
|
||||
@@ -37,10 +37,10 @@ public:
|
||||
QmlProfilerTextMark(QmlProfilerTool *tool, int typeId, const QString &fileName, int lineNumber);
|
||||
void addTypeId(int typeId);
|
||||
|
||||
void paint(QPainter *painter, const QRect &rect) const override;
|
||||
void paintIcon(QPainter *painter, const QRect &rect) const override;
|
||||
void clicked() override;
|
||||
bool isClickable() const override { return true; }
|
||||
bool addToolTipContent(QLayout *target) override;
|
||||
bool addToolTipContent(QLayout *target) const override;
|
||||
|
||||
private:
|
||||
QmlProfilerTool *m_tool;
|
||||
|
||||
@@ -44,6 +44,7 @@ static const char displayFileEncodingKey[] = "DisplayFileEncoding";
|
||||
static const char scrollBarHighlightsKey[] = "ScrollBarHighlights";
|
||||
static const char animateNavigationWithinFileKey[] = "AnimateNavigationWithinFile";
|
||||
static const char animateWithinFileTimeMaxKey[] = "AnimateWithinFileTimeMax";
|
||||
static const char displayAnnotationsKey[] = "DisplayAnnotations";
|
||||
static const char groupPostfix[] = "DisplaySettings";
|
||||
|
||||
namespace TextEditor {
|
||||
@@ -69,6 +70,7 @@ void DisplaySettings::toSettings(const QString &category, QSettings *s) const
|
||||
s->setValue(QLatin1String(displayFileEncodingKey), m_displayFileEncoding);
|
||||
s->setValue(QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights);
|
||||
s->setValue(QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile);
|
||||
s->setValue(QLatin1String(displayAnnotationsKey), m_displayAnnotations);
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
@@ -97,6 +99,7 @@ void DisplaySettings::fromSettings(const QString &category, const QSettings *s)
|
||||
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();
|
||||
m_displayAnnotations = s->value(group + QLatin1String(displayAnnotationsKey), m_displayAnnotations).toBool();
|
||||
}
|
||||
|
||||
bool DisplaySettings::equals(const DisplaySettings &ds) const
|
||||
@@ -118,6 +121,7 @@ bool DisplaySettings::equals(const DisplaySettings &ds) const
|
||||
&& m_scrollBarHighlights == ds.m_scrollBarHighlights
|
||||
&& m_animateNavigationWithinFile == ds.m_animateNavigationWithinFile
|
||||
&& m_animateWithinFileTimeMax == ds.m_animateWithinFileTimeMax
|
||||
&& m_displayAnnotations == ds.m_displayAnnotations
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
bool m_scrollBarHighlights = true;
|
||||
bool m_animateNavigationWithinFile = false;
|
||||
int m_animateWithinFileTimeMax = 333; // read only setting
|
||||
bool m_displayAnnotations = true;
|
||||
|
||||
bool equals(const DisplaySettings &ds) const;
|
||||
};
|
||||
|
||||
@@ -119,6 +119,7 @@ void DisplaySettingsPage::settingsFromUI(DisplaySettings &displaySettings,
|
||||
displaySettings.m_displayFileEncoding = d->m_page->displayFileEncoding->isChecked();
|
||||
displaySettings.m_scrollBarHighlights = d->m_page->scrollBarHighlights->isChecked();
|
||||
displaySettings.m_animateNavigationWithinFile = d->m_page->animateNavigationWithinFile->isChecked();
|
||||
displaySettings.m_displayAnnotations = d->m_page->displayAnnotations->isChecked();
|
||||
}
|
||||
|
||||
void DisplaySettingsPage::settingsToUI()
|
||||
@@ -142,6 +143,7 @@ void DisplaySettingsPage::settingsToUI()
|
||||
d->m_page->displayFileEncoding->setChecked(displaySettings.m_displayFileEncoding);
|
||||
d->m_page->scrollBarHighlights->setChecked(displaySettings.m_scrollBarHighlights);
|
||||
d->m_page->animateNavigationWithinFile->setChecked(displaySettings.m_animateNavigationWithinFile);
|
||||
d->m_page->displayAnnotations->setChecked(displaySettings.m_displayAnnotations);
|
||||
}
|
||||
|
||||
const DisplaySettings &DisplaySettingsPage::displaySettings() const
|
||||
|
||||
@@ -80,17 +80,10 @@
|
||||
<string>Display</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="displayFoldingMarkers">
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="highlightMatchingParentheses">
|
||||
<property name="text">
|
||||
<string>Display &folding markers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="centerOnScroll">
|
||||
<property name="text">
|
||||
<string>Center &cursor on scroll</string>
|
||||
<string>&Highlight matching parentheses</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -101,20 +94,6 @@
|
||||
</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">
|
||||
<property name="text">
|
||||
<string>&Animate matching parentheses</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="highlightCurrentLine">
|
||||
<property name="text">
|
||||
@@ -129,6 +108,20 @@
|
||||
</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">
|
||||
<property name="text">
|
||||
<string>&Animate matching parentheses</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="openLinksInNextSplit">
|
||||
<property name="text">
|
||||
@@ -143,6 +136,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="centerOnScroll">
|
||||
<property name="text">
|
||||
<string>Center &cursor on scroll</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="visualizeWhitespace">
|
||||
<property name="toolTip">
|
||||
@@ -153,17 +153,17 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="highlightMatchingParentheses">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="displayFoldingMarkers">
|
||||
<property name="text">
|
||||
<string>&Highlight matching parentheses</string>
|
||||
<string>Display &folding markers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="markTextChanges">
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="animateNavigationWithinFile">
|
||||
<property name="text">
|
||||
<string>Mark &text changes</string>
|
||||
<string>Animate navigation within file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -174,10 +174,17 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="animateNavigationWithinFile">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="markTextChanges">
|
||||
<property name="text">
|
||||
<string>Animate navigation within file</string>
|
||||
<string>Mark &text changes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="displayAnnotations">
|
||||
<property name="text">
|
||||
<string>Display annotations behind lines</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -197,12 +204,14 @@
|
||||
<tabstop>centerOnScroll</tabstop>
|
||||
<tabstop>autoFoldFirstComment</tabstop>
|
||||
<tabstop>scrollBarHighlights</tabstop>
|
||||
<tabstop>displayAnnotations</tabstop>
|
||||
<tabstop>highlightCurrentLine</tabstop>
|
||||
<tabstop>highlightBlocks</tabstop>
|
||||
<tabstop>animateMatchingParentheses</tabstop>
|
||||
<tabstop>highlightMatchingParentheses</tabstop>
|
||||
<tabstop>openLinksInNextSplit</tabstop>
|
||||
<tabstop>displayFileEncoding</tabstop>
|
||||
<tabstop>animateNavigationWithinFile</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
||||
@@ -845,6 +845,13 @@ void TextDocument::modificationChanged(bool modified)
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void TextDocument::updateLayout() const
|
||||
{
|
||||
auto documentLayout = qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout());
|
||||
QTC_ASSERT(documentLayout, return);
|
||||
documentLayout->requestUpdate();
|
||||
}
|
||||
|
||||
TextMarks TextDocument::marks() const
|
||||
{
|
||||
return d->m_marksCache;
|
||||
@@ -953,6 +960,7 @@ void TextDocument::removeMark(TextMark *mark)
|
||||
|
||||
removeMarkFromMarksCache(mark);
|
||||
mark->setBaseTextDocument(0);
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
void TextDocument::updateMark(TextMark *mark)
|
||||
@@ -964,9 +972,7 @@ void TextDocument::updateMark(TextMark *mark)
|
||||
userData->removeMark(mark);
|
||||
userData->addMark(mark);
|
||||
}
|
||||
auto documentLayout = qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout());
|
||||
QTC_ASSERT(documentLayout, return);
|
||||
documentLayout->requestUpdate();
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
void TextDocument::moveMark(TextMark *mark, int previousLine)
|
||||
|
||||
@@ -154,6 +154,7 @@ private:
|
||||
void cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, bool inEntireDocument);
|
||||
void ensureFinalNewLine(QTextCursor &cursor);
|
||||
void modificationChanged(bool modified);
|
||||
void updateLayout() const;
|
||||
|
||||
TextDocumentPrivate *d;
|
||||
};
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
#include <coreplugin/manhattanstyle.h>
|
||||
#include <coreplugin/find/basetextfind.h>
|
||||
#include <coreplugin/find/highlightscrollbar.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/asconst.h>
|
||||
#include <utils/linecolumnlabel.h>
|
||||
#include <utils/fileutils.h>
|
||||
@@ -279,6 +280,7 @@ public:
|
||||
bool expanded,
|
||||
bool active,
|
||||
bool hovered) const;
|
||||
void drawLineAnnotation(QPainter &painter, const QTextBlock &block);
|
||||
|
||||
void toggleBlockVisible(const QTextBlock &block);
|
||||
QRect foldBox();
|
||||
@@ -297,6 +299,7 @@ public:
|
||||
bool camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode);
|
||||
|
||||
void processTooltipRequest(const QTextCursor &c);
|
||||
bool processAnnotaionTooltipRequest(const QTextBlock &block, const QPoint &pos) const;
|
||||
|
||||
void transformSelection(TransformationMethod method);
|
||||
void transformBlockSelection(TransformationMethod method);
|
||||
@@ -384,6 +387,13 @@ public:
|
||||
bool snippetCheckCursor(const QTextCursor &cursor);
|
||||
void snippetTabOrBacktab(bool forward);
|
||||
|
||||
struct AnnotationRect
|
||||
{
|
||||
QRectF rect;
|
||||
const TextMark *mark;
|
||||
};
|
||||
QMap<int, QList<AnnotationRect>> m_annotationRects;
|
||||
|
||||
RefactorOverlay *m_refactorOverlay = nullptr;
|
||||
QString m_contextHelpId;
|
||||
|
||||
@@ -3165,6 +3175,42 @@ void TextEditorWidgetPrivate::processTooltipRequest(const QTextCursor &c)
|
||||
highest->showToolTip(q, toolTipPoint);
|
||||
}
|
||||
|
||||
bool TextEditorWidgetPrivate::processAnnotaionTooltipRequest(const QTextBlock &block,
|
||||
const QPoint &pos) const
|
||||
{
|
||||
TextBlockUserData *blockUserData = TextDocumentLayout::testUserData(block);
|
||||
if (!blockUserData)
|
||||
return false;
|
||||
|
||||
for (const AnnotationRect &annotationRect : m_annotationRects[block.blockNumber()]) {
|
||||
if (annotationRect.rect.contains(pos)) {
|
||||
auto layout = new QGridLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(2);
|
||||
annotationRect.mark->addToToolTipLayout(layout);
|
||||
TextMarks marks = blockUserData->marks();
|
||||
if (marks.size() > 1) {
|
||||
QFrame* separator = new QFrame();
|
||||
separator->setFrameShape(QFrame::HLine);
|
||||
layout->addWidget(separator, 2, 0, 1, layout->columnCount());
|
||||
layout->addWidget(new QLabel(tr("Other annotations:")), 3, 0, 1,
|
||||
layout->columnCount());
|
||||
|
||||
Utils::sort(marks, [](const TextMark* mark1, const TextMark* mark2){
|
||||
return mark1->priority() > mark2->priority();
|
||||
});
|
||||
for (const TextMark *mark : Utils::asConst(marks)) {
|
||||
if (mark != annotationRect.mark)
|
||||
mark->addToToolTipLayout(layout);
|
||||
}
|
||||
}
|
||||
ToolTip::show(q->mapToGlobal(pos), layout, q);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextEditorWidget::viewportEvent(QEvent *event)
|
||||
{
|
||||
d->m_contentsChanged = false;
|
||||
@@ -3192,10 +3238,14 @@ bool TextEditorWidget::viewportEvent(QEvent *event)
|
||||
QTC_CHECK(line.isValid());
|
||||
// Only handle tool tip for text cursor if mouse is within the block for the text cursor,
|
||||
// and not if the mouse is e.g. in the empty space behind a short line.
|
||||
if (line.isValid()
|
||||
&& pos.x() <= blockBoundingGeometry(block).left() + line.naturalTextRect().right()) {
|
||||
if (line.isValid()) {
|
||||
if (pos.x() <= blockBoundingGeometry(block).left() + line.naturalTextRect().right()) {
|
||||
d->processTooltipRequest(tc);
|
||||
return true;
|
||||
} else if (d->processAnnotaionTooltipRequest(block, pos)) {
|
||||
return true;
|
||||
}
|
||||
ToolTip::hide();
|
||||
}
|
||||
}
|
||||
return QPlainTextEdit::viewportEvent(event);
|
||||
@@ -3632,6 +3682,52 @@ static QTextLayout::FormatRange createBlockCursorCharFormatRange(int pos, const
|
||||
return o;
|
||||
}
|
||||
|
||||
void TextEditorWidgetPrivate::drawLineAnnotation(QPainter &painter, const QTextBlock &block)
|
||||
{
|
||||
if (!m_displaySettings.m_displayAnnotations)
|
||||
return;
|
||||
|
||||
TextBlockUserData *blockUserData = TextDocumentLayout::testUserData(block);
|
||||
if (!blockUserData)
|
||||
return;
|
||||
|
||||
TextMarks marks = blockUserData->marks();
|
||||
if (marks.isEmpty())
|
||||
return;
|
||||
|
||||
const QTextLayout *layout = block.layout();
|
||||
const int lineCount = layout->lineCount();
|
||||
if (lineCount < 1)
|
||||
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();
|
||||
});
|
||||
|
||||
constexpr qreal itemOffset = 10;
|
||||
qreal x = lineRect.right() + itemOffset;
|
||||
|
||||
const RefactorMarkers refactorMarkers = m_refactorOverlay->markers();
|
||||
for (auto refactorMark : refactorMarkers) {
|
||||
if (refactorMark.cursor.block() == block)
|
||||
x = qMax(x, refactorMark.rect.right() + itemOffset);
|
||||
}
|
||||
|
||||
for (const TextMark *mark : marks) {
|
||||
QRectF annotationRect(x, lineRect.top(), q->viewport()->width() - x, lineRect.height());
|
||||
if (annotationRect.width() <= 0)
|
||||
break;
|
||||
mark->paintAnnotation(&painter, &annotationRect, q->fontMetrics());
|
||||
x += annotationRect.width() + itemOffset;
|
||||
m_annotationRects[block.blockNumber()].append({annotationRect, mark});
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditorWidget::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
// draw backgrond to the right of the wrap column before everything else
|
||||
@@ -3757,6 +3853,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e)
|
||||
int cursor_cpos = 0;
|
||||
QPen cursor_pen;
|
||||
|
||||
d->m_annotationRects.clear();
|
||||
d->m_searchResultOverlay->clear();
|
||||
if (!d->m_searchExpr.isEmpty()) { // first pass for the search result overlays
|
||||
|
||||
@@ -4272,6 +4369,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e)
|
||||
painter.restore();
|
||||
}
|
||||
}
|
||||
d->drawLineAnnotation(painter, block);
|
||||
|
||||
block = nextVisibleBlock;
|
||||
top = bottom;
|
||||
@@ -4557,7 +4655,7 @@ void TextEditorWidget::extraAreaPaintEvent(QPaintEvent *e)
|
||||
const int height = fmLineSpacing - 1;
|
||||
const int width = int(.5 + height * mark->widthFactor());
|
||||
const QRect r(xoffset, top, width, height);
|
||||
mark->paint(&painter, r);
|
||||
mark->paintIcon(&painter, r);
|
||||
xoffset += 2;
|
||||
}
|
||||
}
|
||||
@@ -8075,7 +8173,11 @@ IEditor *BaseTextEditor::duplicate()
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
} // namespace TextEditor
|
||||
|
||||
uint qHash(const QColor &color)
|
||||
{
|
||||
return color.rgba();
|
||||
}
|
||||
|
||||
#include "texteditor.moc"
|
||||
|
||||
@@ -672,4 +672,6 @@ private:
|
||||
|
||||
} // namespace TextEditor
|
||||
|
||||
uint qHash(const QColor &color);
|
||||
|
||||
Q_DECLARE_METATYPE(TextEditor::TextEditorWidget::Link)
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QPainter>
|
||||
|
||||
using namespace Core;
|
||||
using namespace Utils;
|
||||
@@ -57,6 +58,24 @@ private:
|
||||
QHash<Utils::FileName, QSet<TextMark *> > m_marks;
|
||||
};
|
||||
|
||||
class AnnotationColors
|
||||
{
|
||||
public:
|
||||
static AnnotationColors &getAnnotationColors(const QColor &markColor,
|
||||
const QColor &backgroundColor);
|
||||
|
||||
public:
|
||||
using SourceColors = QPair<QColor, QColor>;
|
||||
QColor rectColor;
|
||||
QColor textColor;
|
||||
|
||||
private:
|
||||
static double clipHsl(double value);
|
||||
|
||||
private:
|
||||
static QHash<SourceColors, AnnotationColors> m_colorCache;
|
||||
};
|
||||
|
||||
TextMarkRegistry *m_instance = nullptr;
|
||||
|
||||
TextMark::TextMark(const QString &fileName, int lineNumber, Id category, double widthFactor)
|
||||
@@ -99,11 +118,49 @@ int TextMark::lineNumber() const
|
||||
return m_lineNumber;
|
||||
}
|
||||
|
||||
void TextMark::paint(QPainter *painter, const QRect &rect) const
|
||||
void TextMark::paintIcon(QPainter *painter, const QRect &rect) const
|
||||
{
|
||||
m_icon.paint(painter, rect, Qt::AlignCenter);
|
||||
}
|
||||
|
||||
void TextMark::paintAnnotation(QPainter *painter,
|
||||
QRectF *annotationRect,
|
||||
const QFontMetrics &fm) const
|
||||
{
|
||||
QString text = lineAnnotation();
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
const bool drawIcon = !m_icon.isNull();
|
||||
int textWidth = fm.width(text);
|
||||
constexpr qreal margin = 1;
|
||||
const qreal iconHeight = annotationRect->height() - 2 * margin;
|
||||
const qreal iconWidth = iconHeight * m_widthFactor + 2 * margin;
|
||||
qreal annotationWidth = (drawIcon ? textWidth + iconWidth : textWidth) + margin;
|
||||
if (annotationRect->left() + annotationWidth > annotationRect->right()) {
|
||||
textWidth = int(annotationRect->width() - (drawIcon ? iconWidth + margin : margin));
|
||||
text = fm.elidedText(text, Qt::ElideRight, textWidth);
|
||||
annotationWidth = annotationRect->width();
|
||||
}
|
||||
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();
|
||||
annotationRect->setWidth(annotationWidth);
|
||||
painter->setPen(colors.rectColor);
|
||||
painter->setBrush(colors.rectColor);
|
||||
painter->drawRect(*annotationRect);
|
||||
painter->setPen(colors.textColor);
|
||||
if (drawIcon) {
|
||||
paintIcon(painter, annotationRect->adjusted(
|
||||
margin, margin, -(textWidth + 2 * margin), -margin).toAlignedRect());
|
||||
}
|
||||
painter->drawText(annotationRect->adjusted(iconWidth, 0, 0, 0), Qt::AlignLeft, text);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void TextMark::updateLineNumber(int lineNumber)
|
||||
{
|
||||
m_lineNumber = lineNumber;
|
||||
@@ -176,7 +233,7 @@ void TextMark::dragToLine(int lineNumber)
|
||||
Q_UNUSED(lineNumber);
|
||||
}
|
||||
|
||||
void TextMark::addToToolTipLayout(QGridLayout *target)
|
||||
void TextMark::addToToolTipLayout(QGridLayout *target) const
|
||||
{
|
||||
auto *contentLayout = new QVBoxLayout;
|
||||
addToolTipContent(contentLayout);
|
||||
@@ -191,7 +248,7 @@ void TextMark::addToToolTipLayout(QGridLayout *target)
|
||||
}
|
||||
}
|
||||
|
||||
bool TextMark::addToolTipContent(QLayout *target)
|
||||
bool TextMark::addToolTipContent(QLayout *target) const
|
||||
{
|
||||
QString text = m_toolTip;
|
||||
if (text.isEmpty()) {
|
||||
@@ -304,6 +361,33 @@ void TextMarkRegistry::allDocumentsRenamed(const QString &oldName, const QString
|
||||
mark->updateFileName(newName);
|
||||
}
|
||||
|
||||
QHash<AnnotationColors::SourceColors, AnnotationColors> AnnotationColors::m_colorCache;
|
||||
|
||||
AnnotationColors &AnnotationColors::getAnnotationColors(const QColor &markColor,
|
||||
const QColor &backgroundColor)
|
||||
{
|
||||
AnnotationColors &colors = m_colorCache[{markColor, backgroundColor}];
|
||||
if (!colors.rectColor.isValid() || !colors.textColor.isValid()) {
|
||||
const double backgroundSaturation = clipHsl(markColor.hslSaturationF() / 2);
|
||||
const double backgroundLightness = clipHsl(backgroundColor.lightnessF());
|
||||
const double foregroundLightness = clipHsl(backgroundLightness > 0.5
|
||||
? backgroundLightness - 0.5
|
||||
: backgroundLightness + 0.5);
|
||||
colors.rectColor.setHslF(markColor.hslHueF(),
|
||||
backgroundSaturation,
|
||||
backgroundLightness);
|
||||
colors.textColor.setHslF(markColor.hslHueF(),
|
||||
markColor.hslSaturationF(),
|
||||
foregroundLightness);
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
double AnnotationColors::clipHsl(double value)
|
||||
{
|
||||
return std::max(0.15, std::min(0.85, value));
|
||||
}
|
||||
|
||||
} // namespace TextEditor
|
||||
|
||||
#include "textmark.moc"
|
||||
|
||||
@@ -63,7 +63,8 @@ public:
|
||||
QString fileName() const;
|
||||
int lineNumber() const;
|
||||
|
||||
virtual void paint(QPainter *painter, const QRect &rect) const;
|
||||
virtual void paintIcon(QPainter *painter, const QRect &rect) const;
|
||||
virtual void paintAnnotation(QPainter *painter, QRectF *annotationRect, const QFontMetrics &fm) const;
|
||||
/// called if the filename of the document changed
|
||||
virtual void updateFileName(const QString &fileName);
|
||||
virtual void updateLineNumber(int lineNumber);
|
||||
@@ -74,8 +75,8 @@ public:
|
||||
virtual void clicked();
|
||||
virtual bool isDraggable() const;
|
||||
virtual void dragToLine(int lineNumber);
|
||||
void addToToolTipLayout(QGridLayout *target);
|
||||
virtual bool addToolTipContent(QLayout *target);
|
||||
void addToToolTipLayout(QGridLayout *target) const;
|
||||
virtual bool addToolTipContent(QLayout *target) const;
|
||||
|
||||
void setIcon(const QIcon &icon) { m_icon = icon; }
|
||||
const QIcon &icon() const { return m_icon; }
|
||||
@@ -99,6 +100,9 @@ public:
|
||||
TextDocument *baseTextDocument() const { return m_baseTextDocument; }
|
||||
void setBaseTextDocument(TextDocument *baseTextDocument) { m_baseTextDocument = baseTextDocument; }
|
||||
|
||||
QString lineAnnotation() const { return m_lineAnnotation; }
|
||||
void setLineAnnotation(const QString &lineAnnotation) { m_lineAnnotation = lineAnnotation; }
|
||||
|
||||
QString toolTip() const { return m_toolTip; }
|
||||
void setToolTip(const QString &toolTip) { m_toolTip = toolTip; }
|
||||
|
||||
@@ -115,6 +119,7 @@ private:
|
||||
bool m_hasColor = false;
|
||||
Core::Id m_category;
|
||||
double m_widthFactor = 1.0;
|
||||
QString m_lineAnnotation;
|
||||
QString m_toolTip;
|
||||
QString m_defaultToolTip;
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ CallgrindTextMark::CallgrindTextMark(const QPersistentModelIndex &index,
|
||||
setPriority(TextEditor::TextMark::HighPriority);
|
||||
}
|
||||
|
||||
void CallgrindTextMark::paint(QPainter *painter, const QRect &paintRect) const
|
||||
void CallgrindTextMark::paintIcon(QPainter *painter, const QRect &paintRect) const
|
||||
{
|
||||
if (!m_modelIndex.isValid())
|
||||
return;
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
const Valgrind::Callgrind::Function *function() const;
|
||||
|
||||
virtual void paint(QPainter *painter, const QRect &paintRect) const;
|
||||
virtual void paintIcon(QPainter *painter, const QRect &paintRect) const;
|
||||
|
||||
private:
|
||||
QPersistentModelIndex m_modelIndex;
|
||||
|
||||
Reference in New Issue
Block a user