ui: Added custom tooltipping to code and graph (issue #195)

* Tooltips show node type with access specifier, reference count and fully qualified name
* Fields and global variables include the type name
* Methods and functinos show whole signature. Gets broken into multiple lines if too long.

Included changes:

* Upgraded to C++14
* fixed clang warnings
* Pass location of .srctrlprj file to build.sh script to load on launch
* Split off QtCodeField for showing highlighted and annotated code fom QtCodeArea
* removed TokenComponentSignature
* added TaskDecoratorDelay

bug id = 195
This commit is contained in:
Eberhard Graether
2017-07-31 12:45:22 +02:00
parent 01b1e0d6f1
commit 01864b4d0c
79 changed files with 2340 additions and 1009 deletions
+6
View File
@@ -14,6 +14,8 @@ add_files(
qt/element/QtBookmarkCategory.h
qt/element/QtCodeArea.cpp
qt/element/QtCodeArea.h
qt/element/QtCodeField.cpp
qt/element/QtCodeField.h
qt/element/QtCodeFile.cpp
qt/element/QtCodeFile.h
qt/element/QtCodeFileList.cpp
@@ -54,6 +56,8 @@ add_files(
qt/element/QtStatusBar.h
qt/element/QtTable.cpp
qt/element/QtTable.h
qt/element/QtTooltip.cpp
qt/element/QtTooltip.h
qt/element/QtUndoRedo.cpp
qt/element/QtUndoRedo.h
@@ -143,6 +147,8 @@ add_files(
qt/view/QtStatusView.h
qt/view/QtTabbedView.cpp
qt/view/QtTabbedView.h
qt/view/QtTooltipView.cpp
qt/view/QtTooltipView.h
qt/view/QtUndoRedoView.cpp
qt/view/QtUndoRedoView.h
qt/view/QtViewFactory.cpp
+35 -513
View File
@@ -12,6 +12,7 @@
#include <QTextBlock>
#include <QToolTip>
#include "data/location/SourceLocationFile.h"
#include "utility/messaging/type/MessageActivateLocalSymbols.h"
#include "utility/messaging/type/MessageActivateSourceLocations.h"
#include "utility/messaging/type/MessageActivateTokenIds.h"
@@ -21,16 +22,10 @@
#include "utility/messaging/type/MessageShowErrors.h"
#include "utility/utility.h"
#include "data/location/SourceLocation.h"
#include "data/location/SourceLocationFile.h"
#include "qt/element/QtCodeNavigator.h"
#include "qt/utility/QtContextMenu.h"
#include "qt/utility/QtHighlighter.h"
#include "settings/ApplicationSettings.h"
#include "settings/ColorScheme.h"
std::vector<QtCodeArea::AnnotationColor> QtCodeArea::s_annotationColors;
MouseWheelOverScrollbarFilter::MouseWheelOverScrollbarFilter()
{
}
@@ -76,11 +71,6 @@ void QtCodeArea::LineNumberArea::paintEvent(QPaintEvent *event)
m_codeArea->lineNumberAreaPaintEvent(event);
}
void QtCodeArea::clearAnnotationColors()
{
s_annotationColors.clear();
}
QtCodeArea::QtCodeArea(
uint startLineNumber,
const std::string& code,
@@ -88,11 +78,8 @@ QtCodeArea::QtCodeArea(
QtCodeNavigator* navigator,
QWidget* parent
)
: QPlainTextEdit(parent)
: QtCodeField(startLineNumber, code, locationFile, parent)
, m_navigator(navigator)
, m_startLineNumber(startLineNumber)
, m_code(code)
, m_locationFile(locationFile)
, m_digits(0)
, m_isSelecting(false)
, m_isPanning(false)
@@ -100,53 +87,22 @@ QtCodeArea::QtCodeArea(
, m_eventPosition(0, 0)
, m_isActiveFile(false)
, m_lineNumbersHidden(false)
, m_wasAnnotated(false)
, m_endTextEditPosition(0)
{
setObjectName("code_area");
setReadOnly(true);
setFrameStyle(QFrame::NoFrame);
setLineWrapMode(QPlainTextEdit::NoWrap);
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
viewport()->setCursor(Qt::ArrowCursor);
m_lineNumberArea = new LineNumberArea(this);
std::string displayCode = m_code;
if (!displayCode.empty() && *displayCode.rbegin() == '\n')
{
displayCode.pop_back();
}
setPlainText(QString::fromUtf8(displayCode.c_str()));
createLineLengthCache();
m_digits = lineNumberDigits();
updateLineNumberAreaWidth();
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
this->setMouseTracking(true);
// MouseWheelOverScrollbarFilter is deleted by parent.
horizontalScrollBar()->installEventFilter(new MouseWheelOverScrollbarFilter());
m_scrollSpeedChangeListener.setScrollBar(horizontalScrollBar());
createActions();
createAnnotations(locationFile);
FilePath path = m_locationFile->getFilePath();
LanguageType language = LANGUAGE_UNKNOWN;
if (!path.empty())
{
language = (path.extension() == ".java" ? LANGUAGE_JAVA : LANGUAGE_CPP);
}
m_highlighter = new QtHighlighter(document(), language);
m_highlighter->highlightDocument();
}
QtCodeArea::~QtCodeArea()
@@ -160,39 +116,23 @@ QtCodeArea::~QtCodeArea()
QSize QtCodeArea::sizeHint() const
{
QTextBlock block = document()->firstBlock();
double height = 0;
double width = lineNumberAreaWidth() + blockBoundingGeometry(block).translated(contentOffset()).left();
double width = 0;
while (block.isValid())
for (QTextBlock block = document()->firstBlock(); block.isValid(); block = block.next())
{
height += blockBoundingRect(block).height();
width = std::max(blockBoundingRect(block).width(), width);
block = block.next();
QRectF rect = blockBoundingGeometry(block);
height += rect.height();
width = std::max(rect.width(), width);
}
int scrollHeight = 0;
if (horizontalScrollBar()->minimum() != horizontalScrollBar()->maximum())
{
height += horizontalScrollBar()->height();
scrollHeight = horizontalScrollBar()->height();
}
return QSize(width + 1, height + 5);
}
uint QtCodeArea::getStartLineNumber() const
{
return m_startLineNumber;
}
uint QtCodeArea::getEndLineNumber() const
{
return m_startLineNumber + blockCount() - 1;
}
std::shared_ptr<SourceLocationFile> QtCodeArea::getSourceLocationFile() const
{
return m_locationFile;
return QSize(width + lineNumberAreaWidth() + 1, height + scrollHeight + 5);
}
void QtCodeArea::lineNumberAreaPaintEvent(QPaintEvent *event)
@@ -218,7 +158,7 @@ void QtCodeArea::lineNumberAreaPaintEvent(QPaintEvent *event)
{
if (block.isVisible() && bottom >= event->rect().top())
{
int number = blockNumber + m_startLineNumber;
int number = blockNumber + getStartLineNumber();
p.setColor(textColor);
@@ -232,7 +172,8 @@ void QtCodeArea::lineNumberAreaPaintEvent(QPaintEvent *event)
}
painter.setPen(p);
painter.drawText(0, top, m_lineNumberArea->width() - 16, fontMetrics().height(), Qt::AlignRight, QString::number(number));
painter.drawText(
0, top, m_lineNumberArea->width() - 16, fontMetrics().height(), Qt::AlignRight, QString::number(number));
}
block = block.next();
@@ -244,7 +185,7 @@ void QtCodeArea::lineNumberAreaPaintEvent(QPaintEvent *event)
int QtCodeArea::lineNumberDigits() const
{
int max = qMax(1, int(m_startLineNumber) + blockCount());
int max = qMax(1, int(getStartLineNumber()) + blockCount());
return utility::digits(max);
}
@@ -354,15 +295,10 @@ QRectF QtCodeArea::getLineRectForLineNumber(uint lineNumber) const
lineNumber = getEndLineNumber();
}
QTextBlock block = document()->findBlockByLineNumber(lineNumber - m_startLineNumber);
QTextBlock block = document()->findBlockByLineNumber(lineNumber - getStartLineNumber());
return blockBoundingGeometry(block);
}
std::string QtCodeArea::getCode() const
{
return m_code;
}
void QtCodeArea::hideLineNumbers()
{
m_lineNumberArea->hide();
@@ -377,101 +313,6 @@ void QtCodeArea::resizeEvent(QResizeEvent *e)
m_lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
void QtCodeArea::showEvent(QShowEvent* event)
{
int tabWidth = ApplicationSettings::getInstance()->getCodeTabWidth();
setTabStopWidth(tabWidth * fontMetrics().width('9'));
}
void QtCodeArea::paintEvent(QPaintEvent* event)
{
QPainter painter(viewport());
QTextBlock block = firstVisibleBlock();
int top = blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + blockBoundingRect(block).height();
int blockHeight = blockBoundingRect(block).height();
int firstVisibleLine = -1;
int lastVisibleLine = -1;
while (block.isValid() && top <= event->rect().bottom())
{
if (block.isVisible())
{
if (firstVisibleLine < 0 && bottom >= event->rect().top())
{
firstVisibleLine = block.blockNumber();
}
lastVisibleLine = block.blockNumber();
}
block = block.next();
top = bottom;
bottom = top + static_cast<int>(blockBoundingRect(block).height());
}
std::vector<std::pair<int, int>> ranges;
for (size_t i : m_colorChangedAnnotationIndices)
{
Annotation& annotation = m_annotations[i];
ranges.push_back(std::pair<int, int>(annotation.start, annotation.end));
}
m_highlighter->highlightRange(firstVisibleLine, lastVisibleLine, ranges);
firstVisibleLine += m_startLineNumber;
lastVisibleLine += m_startLineNumber;
int borderRadius = 3;
for (const Annotation& annotation : m_annotations)
{
if (annotation.startLine > lastVisibleLine || annotation.endLine < firstVisibleLine)
{
continue;
}
const AnnotationColor& color = getAnnotationColorForAnnotation(annotation);
if (color.border == "transparent" && color.fill == "transparent")
{
continue;
}
painter.setPen(QPen(color.border.c_str()));
painter.setBrush(QBrush(color.fill.c_str()));
if (annotation.locationType == LOCATION_SCOPE)
{
painter.drawRoundedRect(
0, top + (annotation.startLine - m_startLineNumber) * blockHeight,
width(), (annotation.endLine - annotation.startLine + 1) * blockHeight,
borderRadius, borderRadius
);
}
else
{
std::vector<QRect> rects = getCursorRectsForAnnotation(annotation);
for (QRect rect : rects)
{
rect.adjust(-1, 0, 1, 1);
painter.drawRoundedRect(rect, borderRadius, borderRadius);
}
}
}
QPlainTextEdit::paintEvent(event);
}
void QtCodeArea::enterEvent(QEvent* event)
{
}
void QtCodeArea::leaveEvent(QEvent* event)
{
setHoveredAnnotations(std::vector<const Annotation*>());
}
void QtCodeArea::mousePressEvent(QMouseEvent* event)
{
clearSelection();
@@ -612,10 +453,10 @@ void QtCodeArea::contextMenuEvent(QContextMenuEvent* event)
m_eventPosition = event->pos();
QtContextMenu menu(event, this);
if (!m_locationFile->getFilePath().empty())
if (!getSourceLocationFile()->getFilePath().empty())
{
menu.addSeparator();
menu.addFileActions(m_locationFile->getFilePath());
menu.addFileActions(getSourceLocationFile()->getFilePath());
menu.addSeparator();
menu.addAction(m_setIDECursorPositionAction);
}
@@ -623,6 +464,16 @@ void QtCodeArea::contextMenuEvent(QContextMenuEvent* event)
}
}
void QtCodeArea::focusTokenIds(const std::vector<Id>& tokenIds)
{
MessageFocusIn(tokenIds, TOOLTIP_ORIGIN_CODE).dispatch();
}
void QtCodeArea::defocusTokenIds(const std::vector<Id>& tokenIds)
{
MessageFocusOut(tokenIds).dispatch();
}
void QtCodeArea::updateLineNumberAreaWidth(int /* newBlockCount */)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
@@ -668,24 +519,7 @@ void QtCodeArea::setIDECursorPosition()
{
std::pair<int, int> lineColumn = toLineColumn(this->cursorForPosition(m_eventPosition).position());
MessageMoveIDECursor(m_locationFile->getFilePath().str(), lineColumn.first, lineColumn.second).dispatch();
}
std::vector<const QtCodeArea::Annotation*> QtCodeArea::getInteractiveAnnotationsForPosition(int pos) const
{
std::vector<const QtCodeArea::Annotation*> annotations;
for (const Annotation& annotation : m_annotations)
{
const LocationType& type = annotation.locationType;
if ((type == LOCATION_TOKEN || type == LOCATION_LOCAL_SYMBOL || type == LOCATION_ERROR)
&& pos >= annotation.start && pos <= annotation.end)
{
annotations.push_back(&annotation);
}
}
return annotations;
MessageMoveIDECursor(getSourceLocationFile()->getFilePath().str(), lineColumn.first, lineColumn.second).dispatch();
}
void QtCodeArea::activateSourceLocations(const std::vector<const Annotation*>& annotations)
@@ -772,215 +606,21 @@ void QtCodeArea::activateErrors(const std::vector<const Annotation*>& annotation
}
}
void QtCodeArea::createAnnotations(std::shared_ptr<SourceLocationFile> locationFile)
{
uint endLineNumber = getEndLineNumber();
std::set<Id> locationIds;
locationFile->forEachSourceLocation(
[&](const SourceLocation* location)
{
if (locationIds.find(location->getLocationId()) != locationIds.end())
{
return;
}
locationIds.insert(location->getLocationId());
Annotation annotation;
const SourceLocation* startLocation = location->getStartLocation();
if (!startLocation || startLocation->getLineNumber() < m_startLineNumber)
{
annotation.start = startTextEditPosition();
annotation.startLine = m_startLineNumber;
annotation.startCol = 0;
}
else if (startLocation->getLineNumber() <= endLineNumber)
{
annotation.start = toTextEditPosition(startLocation->getLineNumber(), startLocation->getColumnNumber() - 1);
annotation.startLine = startLocation->getLineNumber();
annotation.startCol = startLocation->getColumnNumber() - 1;
}
else
{
return;
}
const SourceLocation* endLocation = location->getEndLocation();
if (!endLocation || endLocation->getLineNumber() > endLineNumber)
{
annotation.end = endTextEditPosition();
annotation.endLine = endLineNumber;
annotation.endCol = m_lineLengths[document()->blockCount() - 1];
}
else if (endLocation->getLineNumber() >= m_startLineNumber)
{
annotation.end = toTextEditPosition(endLocation->getLineNumber(), endLocation->getColumnNumber());
annotation.endLine = endLocation->getLineNumber();
annotation.endCol = endLocation->getColumnNumber();
}
else
{
return;
}
annotation.tokenIds.insert(location->getTokenIds().begin(), location->getTokenIds().end());
annotation.locationId = location->getLocationId();
annotation.locationType = location->getType();
annotation.isActive = false;
annotation.isFocused = false;
m_annotations.push_back(annotation);
}
);
}
void QtCodeArea::annotateText()
{
const std::set<Id>& currentActiveTokenIds = m_navigator->getCurrentActiveTokenIds();
const std::set<Id>& currentActiveLocationIds = m_navigator->getCurrentActiveLocationIds();
std::set<Id> activeSymbolIds = m_navigator->getCurrentActiveTokenIds();
utility::append(activeSymbolIds, m_navigator->getActiveLocalSymbolIds());
const std::set<Id>& activeTokenIds = m_navigator->getActiveTokenIds();
const std::set<Id>& activeLocalSymbolIds = m_navigator->getActiveLocalSymbolIds();
const std::set<Id>& focusIds = m_navigator->getFocusedTokenIds();
const std::set<Id>& activeLocationIds = m_navigator->getCurrentActiveLocationIds();
std::vector<int> linesToRehighlight;
std::set<Id> focusedSymbolIds = m_navigator->getActiveTokenIds();
utility::append(focusedSymbolIds, m_navigator->getFocusedTokenIds());
bool needsUpdate = false;
for (size_t i = 0; i < m_annotations.size(); i++)
{
Annotation& annotation = m_annotations[i];
bool wasActive = annotation.isActive;
bool wasFocused = annotation.isFocused;
const AnnotationColor& oldColor = getAnnotationColorForAnnotation(annotation);
annotation.isActive = (
utility::shareElement(currentActiveTokenIds, annotation.tokenIds) ||
utility::shareElement(activeLocalSymbolIds, annotation.tokenIds) ||
currentActiveLocationIds.find(annotation.locationId) != currentActiveLocationIds.end()
);
if (!annotation.isActive)
{
annotation.isFocused = (
utility::shareElement(focusIds, annotation.tokenIds) ||
utility::shareElement(activeTokenIds, annotation.tokenIds)
);
}
const AnnotationColor& newColor = getAnnotationColorForAnnotation(annotation);
if (newColor.text != oldColor.text || (!m_wasAnnotated && newColor.text != "transparent"))
{
if (newColor.text.size() > 0 && newColor.text != "transparent")
{
if (!annotation.oldTextColor.isValid())
{
annotation.oldTextColor = m_highlighter->getFormat(annotation.start, annotation.end).foreground().color();
}
setTextColorForAnnotation(annotation, QColor(newColor.text.c_str()));
m_colorChangedAnnotationIndices.insert(i);
}
else if (annotation.oldTextColor.isValid())
{
setTextColorForAnnotation(annotation, annotation.oldTextColor);
annotation.oldTextColor = QColor();
m_colorChangedAnnotationIndices.erase(i);
linesToRehighlight.push_back(annotation.startLine - 1);
}
}
if (wasFocused != annotation.isFocused || wasActive != annotation.isActive)
{
needsUpdate = true;
}
}
if (linesToRehighlight.size())
{
m_highlighter->rehighlightLines(linesToRehighlight);
}
if (m_wasAnnotated && needsUpdate)
bool needsUpdate = QtCodeField::annotateText(activeSymbolIds, activeLocationIds, focusedSymbolIds);
if (needsUpdate)
{
m_lineNumberArea->update();
viewport()->update();
}
m_wasAnnotated = true;
}
void QtCodeArea::setHoveredAnnotations(const std::vector<const Annotation*>& annotations)
{
if (m_hoveredAnnotations.size())
{
std::vector<Id> tokenIds;
for (const Annotation* annotation : m_hoveredAnnotations)
{
tokenIds.insert(tokenIds.end(), annotation->tokenIds.begin(), annotation->tokenIds.end());
}
MessageFocusOut(tokenIds).dispatch();
}
m_hoveredAnnotations = annotations;
if (annotations.size())
{
std::vector<Id> tokenIds;
for (const Annotation* annotation : annotations)
{
tokenIds.insert(tokenIds.end(), annotation->tokenIds.begin(), annotation->tokenIds.end());
}
MessageFocusIn(tokenIds).dispatch();
}
}
int QtCodeArea::toTextEditPosition(int lineNumber, int columnNumber) const
{
lineNumber -= m_startLineNumber - 1;
int position = 0;
for (int i = 0; i < lineNumber - 1; i++)
{
position += m_lineLengths[i];
}
position += columnNumber;
return position;
}
std::pair<int, int> QtCodeArea::toLineColumn(int textEditPosition) const
{
int lineNumber = m_startLineNumber;
for (int i = 0; i < document()->lineCount(); i++)
{
int nextTextEditPosition = textEditPosition - m_lineLengths[i];
if (nextTextEditPosition >= 0)
{
textEditPosition = nextTextEditPosition;
lineNumber++;
}
else
{
break;
}
}
return std::make_pair(lineNumber, textEditPosition);
}
int QtCodeArea::startTextEditPosition() const
{
return 0;
}
int QtCodeArea::endTextEditPosition() const
{
return m_endTextEditPosition;
}
std::set<int> QtCodeArea::getActiveLineNumbers() const
@@ -1001,111 +641,6 @@ std::set<int> QtCodeArea::getActiveLineNumbers() const
return activeLineNumbers;
}
std::vector<QRect> QtCodeArea::getCursorRectsForAnnotation(const Annotation& annotation) const
{
std::vector<QRect> rects;
QTextCursor cursor = QTextCursor(document());
cursor.setPosition(annotation.start);
QRect rectStart = cursorRect(cursor);
QRect rectEnd;
int line = annotation.startLine;
while (line <= annotation.endLine)
{
if (line == annotation.endLine)
{
// Avoid that annotations at line end span down to first column of the next line.
if (annotation.startLine != annotation.endLine ||
m_lineLengths[line - m_startLineNumber] != annotation.endCol)
{
cursor.setPosition(annotation.end);
}
}
else
{
cursor.setPosition(toTextEditPosition(line, m_lineLengths[line - m_startLineNumber] - 1));
}
rectEnd = cursorRect(cursor);
rects.push_back(QRect(
rectStart.left(),
rectStart.top(),
rectEnd.right() - rectStart.left(),
rectEnd.bottom() - rectStart.top()
));
line++;
if (int(line - m_startLineNumber) < document()->blockCount())
{
cursor.setPosition(toTextEditPosition(line, 0));
rectStart = cursorRect(cursor);
}
}
return rects;
}
const QtCodeArea::AnnotationColor& QtCodeArea::getAnnotationColorForAnnotation(const Annotation& annotation)
{
if (!s_annotationColors.size())
{
ColorScheme* scheme = ColorScheme::getInstance().get();
std::vector<std::string> types = { "token", "local_symbol", "scope", "error", "fulltext" };
std::vector<ColorScheme::ColorState> states = { ColorScheme::NORMAL, ColorScheme::FOCUS, ColorScheme::ACTIVE };
for (const std::string& type : types)
{
for (const ColorScheme::ColorState& state : states)
{
AnnotationColor color;
color.border = scheme->getCodeAnnotationTypeColor(type, "border", state);
color.fill = scheme->getCodeAnnotationTypeColor(type, "fill", state);
color.text = scheme->getCodeAnnotationTypeColor(type, "text", state);
s_annotationColors.push_back(color);
}
}
}
size_t i = 0;
if (annotation.locationType == LOCATION_LOCAL_SYMBOL)
{
i = 3;
}
else if (annotation.locationType == LOCATION_SCOPE)
{
i = 6;
}
else if (annotation.locationType == LOCATION_ERROR)
{
i = 9;
}
else if (annotation.locationType == LOCATION_FULLTEXT)
{
i = 12;
}
if (annotation.isActive)
{
i += 2;
}
else if (annotation.isFocused)
{
i += 1;
}
return s_annotationColors[i];
}
void QtCodeArea::setTextColorForAnnotation(Annotation& annotation, QColor color) const
{
QTextCharFormat format;
format.setForeground(color);
m_highlighter->applyFormat(annotation.start, annotation.end, format);
}
void QtCodeArea::createActions()
{
m_setIDECursorPositionAction = new QAction(tr("Set IDE Cursor"), this);
@@ -1113,16 +648,3 @@ void QtCodeArea::createActions()
m_setIDECursorPositionAction->setToolTip(tr("Set the IDE Cursor to this code position"));
connect(m_setIDECursorPositionAction, SIGNAL(triggered()), this, SLOT(setIDECursorPosition()));
}
void QtCodeArea::createLineLengthCache()
{
m_endTextEditPosition = -1;
m_lineLengths.clear();
for (QTextBlock it = document()->begin(); it != document()->end(); it = it.next())
{
m_lineLengths.push_back(it.length());
m_endTextEditPosition += it.length();
}
}
+5 -85
View File
@@ -2,25 +2,17 @@
#define QT_CODE_AREA_H
#include <memory>
#include <set>
#include <vector>
#include <QPlainTextEdit>
#include "data/location/LocationType.h"
#include "qt/element/QtCodeField.h"
#include "qt/utility/QtScrollSpeedChangeListener.h"
#include "utility/types.h"
class QDragMoveEvent;
class QPaintEvent;
class QResizeEvent;
class QSize;
class QtCodeNavigator;
class QtHighlighter;
class QWidget;
class SourceLocation;
class SourceLocationFile;
class MouseWheelOverScrollbarFilter
: public QObject
@@ -36,7 +28,7 @@ protected:
class QtCodeArea
: public QPlainTextEdit
: public QtCodeField
{
Q_OBJECT
@@ -57,8 +49,6 @@ public:
QtCodeArea* m_codeArea;
};
static void clearAnnotationColors();
QtCodeArea(
uint startLineNumber,
const std::string& code,
@@ -70,11 +60,6 @@ public:
virtual QSize sizeHint() const Q_DECL_OVERRIDE;
uint getStartLineNumber() const;
uint getEndLineNumber() const;
std::shared_ptr<SourceLocationFile> getSourceLocationFile() const;
void lineNumberAreaPaintEvent(QPaintEvent* event);
int lineNumberDigits() const;
int lineNumberAreaWidth() const;
@@ -94,16 +79,10 @@ public:
QRectF getLineRectForLineNumber(uint lineNumber) const;
std::string getCode() const;
void hideLineNumbers();
protected:
virtual void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE;
virtual void showEvent(QShowEvent* event) Q_DECL_OVERRIDE;
virtual void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE;
virtual void enterEvent(QEvent* event) Q_DECL_OVERRIDE;
virtual void leaveEvent(QEvent* event) Q_DECL_OVERRIDE;
virtual void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
virtual void mousePressEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
virtual void mouseMoveEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
@@ -111,6 +90,9 @@ protected:
virtual void contextMenuEvent(QContextMenuEvent* event) Q_DECL_OVERRIDE;
virtual void focusTokenIds(const std::vector<Id>& tokenIds) override;
virtual void defocusTokenIds(const std::vector<Id>& tokenIds) override;
private slots:
void updateLineNumberAreaWidth(int newBlockCount = 0);
void updateLineNumberArea(const QRect&, int);
@@ -119,76 +101,18 @@ private slots:
void setIDECursorPosition();
private:
struct Annotation
{
int startLine;
int endLine;
int startCol;
int endCol;
int start;
int end;
std::set<Id> tokenIds;
Id locationId;
LocationType locationType;
bool isActive;
bool isFocused;
QColor oldTextColor;
};
struct AnnotationColor
{
std::string border;
std::string fill;
std::string text;
};
std::vector<const Annotation*> getInteractiveAnnotationsForPosition(int pos) const;
void activateSourceLocations(const std::vector<const Annotation*>& annotations);
void activateLocalSymbols(const std::vector<const Annotation*>& annotations);
void activateErrors(const std::vector<const Annotation*>& annotations);
void createAnnotations(std::shared_ptr<SourceLocationFile> locationFile);
void annotateText();
void setHoveredAnnotations(const std::vector<const Annotation*>& annotations);
int toTextEditPosition(int lineNumber, int columnNumber) const;
std::pair<int, int> toLineColumn(int textEditPosition) const;
int startTextEditPosition() const;
int endTextEditPosition() const;
std::set<int> getActiveLineNumbers() const;
std::vector<QRect> getCursorRectsForAnnotation(const Annotation& annotation) const;
const AnnotationColor& getAnnotationColorForAnnotation(const Annotation& annotation);
void setTextColorForAnnotation(Annotation& annotation, QColor color) const;
void createActions();
void createLineLengthCache();
static std::vector<AnnotationColor> s_annotationColors;
QtCodeNavigator* m_navigator;
QWidget* m_lineNumberArea;
QtHighlighter* m_highlighter;
const uint m_startLineNumber;
const std::string m_code;
std::shared_ptr<SourceLocationFile> m_locationFile;
std::vector<Annotation> m_annotations;
std::vector<const Annotation*> m_hoveredAnnotations;
std::set<size_t> m_colorChangedAnnotationIndices;
int m_digits;
@@ -203,10 +127,6 @@ private:
bool m_isActiveFile;
bool m_lineNumbersHidden;
bool m_wasAnnotated;
std::vector<int> m_lineLengths;
int m_endTextEditPosition;
QtScrollSpeedChangeListener m_scrollSpeedChangeListener;
};
+613
View File
@@ -0,0 +1,613 @@
#include "QtCodeField.h"
#include <QPainter>
#include <QTextBlock>
#include "data/location/SourceLocation.h"
#include "data/location/SourceLocationFile.h"
#include "qt/utility/QtHighlighter.h"
#include "settings/ApplicationSettings.h"
#include "settings/ColorScheme.h"
#include "utility/messaging/type/MessageActivateTokenIds.h"
#include "utility/utility.h"
std::vector<QtCodeField::AnnotationColor> QtCodeField::s_annotationColors;
void QtCodeField::clearAnnotationColors()
{
s_annotationColors.clear();
}
QtCodeField::QtCodeField(
uint startLineNumber,
const std::string& code,
std::shared_ptr<SourceLocationFile> locationFile,
QWidget* parent
)
: QPlainTextEdit(parent)
, m_startLineNumber(startLineNumber)
, m_code(code)
, m_locationFile(locationFile)
, m_endTextEditPosition(0)
, m_wasAnnotated(false)
{
setObjectName("code_area");
setReadOnly(true);
setFrameStyle(QFrame::NoFrame);
setLineWrapMode(QPlainTextEdit::NoWrap);
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
viewport()->setCursor(Qt::ArrowCursor);
std::string displayCode = m_code;
if (!displayCode.empty() && *displayCode.rbegin() == '\n')
{
displayCode.pop_back();
}
setPlainText(QString::fromUtf8(displayCode.c_str()));
createLineLengthCache();
this->setMouseTracking(true);
createAnnotations(locationFile);
FilePath path = m_locationFile->getFilePath();
LanguageType language = LANGUAGE_UNKNOWN;
if (!path.empty())
{
language = (path.extension() == ".java" ? LANGUAGE_JAVA : LANGUAGE_CPP);
}
m_highlighter = new QtHighlighter(document(), language);
m_highlighter->highlightDocument();
ApplicationSettings* appSettings = ApplicationSettings::getInstance().get();
setFont(QFont(appSettings->getFontName().c_str(), appSettings->getFontSize()));
setTabStopWidth(appSettings->getCodeTabWidth() * fontMetrics().width('9'));
}
QtCodeField::~QtCodeField()
{
}
QSize QtCodeField::sizeHint() const
{
double height = 0;
int width = 0;
QFontMetrics fm = fontMetrics();
int maxTextSize = 0;
for (QTextBlock block = document()->firstBlock(); block.isValid(); block = block.next())
{
QRectF rect = blockBoundingGeometry(block);
height += rect.height();
{
int blockWidth = fm.boundingRect(
0, 0, 1000000, 1000000,
Qt::AlignLeft | Qt::AlignTop | Qt::TextExpandTabs, block.text(), tabStopWidth()).width();
width = std::max(blockWidth, width);
maxTextSize = block.text().size();
}
}
return QSize(width + 1, height + 5);
}
uint QtCodeField::getStartLineNumber() const
{
return m_startLineNumber;
}
uint QtCodeField::getEndLineNumber() const
{
return m_startLineNumber + blockCount() - 1;
}
std::string QtCodeField::getCode() const
{
return m_code;
}
std::shared_ptr<SourceLocationFile> QtCodeField::getSourceLocationFile() const
{
return m_locationFile;
}
void QtCodeField::annotateText()
{
annotateText(std::set<Id>(), std::set<Id>(), std::set<Id>());
}
void QtCodeField::paintEvent(QPaintEvent* event)
{
QPainter painter(viewport());
QTextBlock block = firstVisibleBlock();
int top = blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + blockBoundingRect(block).height();
int blockHeight = blockBoundingRect(block).height();
int firstVisibleLine = -1;
int lastVisibleLine = -1;
while (block.isValid() && top <= event->rect().bottom())
{
if (block.isVisible())
{
if (firstVisibleLine < 0 && bottom >= event->rect().top())
{
firstVisibleLine = block.blockNumber();
}
lastVisibleLine = block.blockNumber();
}
block = block.next();
top = bottom;
bottom = top + static_cast<int>(blockBoundingRect(block).height());
}
std::vector<std::pair<int, int>> ranges;
for (size_t i : m_colorChangedAnnotationIndices)
{
Annotation& annotation = m_annotations[i];
ranges.push_back(std::pair<int, int>(annotation.start, annotation.end));
}
m_highlighter->highlightRange(firstVisibleLine, lastVisibleLine, ranges);
firstVisibleLine += m_startLineNumber;
lastVisibleLine += m_startLineNumber;
int borderRadius = 3;
for (const Annotation& annotation : m_annotations)
{
if (annotation.startLine > lastVisibleLine || annotation.endLine < firstVisibleLine)
{
continue;
}
const AnnotationColor& color = getAnnotationColorForAnnotation(annotation);
if (color.border == "transparent" && color.fill == "transparent")
{
continue;
}
painter.setPen(QPen(color.border.c_str()));
painter.setBrush(QBrush(color.fill.c_str()));
if (annotation.locationType == LOCATION_SCOPE)
{
painter.drawRoundedRect(
0, top + (annotation.startLine - m_startLineNumber) * blockHeight,
width(), (annotation.endLine - annotation.startLine + 1) * blockHeight,
borderRadius, borderRadius
);
}
else
{
std::vector<QRect> rects = getCursorRectsForAnnotation(annotation);
for (QRect rect : rects)
{
rect.adjust(-1, 0, 1, 1);
painter.drawRoundedRect(rect, borderRadius, borderRadius);
}
}
}
QPlainTextEdit::paintEvent(event);
}
void QtCodeField::enterEvent(QEvent* event)
{
}
void QtCodeField::leaveEvent(QEvent* event)
{
setHoveredAnnotations(std::vector<const Annotation*>());
}
void QtCodeField::mouseMoveEvent(QMouseEvent* event)
{
QTextCursor cursor = this->cursorForPosition(event->pos());
std::vector<const Annotation*> annotations = getInteractiveAnnotationsForPosition(cursor.position());
bool same = annotations.size() == m_hoveredAnnotations.size();
if (same)
{
for (size_t i = 0; i < annotations.size(); i++)
{
if (annotations[i] != m_hoveredAnnotations[i])
{
same = false;
break;
}
}
}
if (!same)
{
setHoveredAnnotations(annotations);
}
}
void QtCodeField::mouseReleaseEvent(QMouseEvent* event)
{
if (event->button() != Qt::LeftButton)
{
return;
}
viewport()->setCursor(Qt::ArrowCursor);
QTextCursor cursor = this->cursorForPosition(event->pos());
std::vector<const Annotation*> annotations = getInteractiveAnnotationsForPosition(cursor.position());
if (!annotations.size())
{
return;
}
std::set<Id> tokenIds;
for (const Annotation* annotation : annotations)
{
tokenIds.insert(annotation->tokenIds.begin(), annotation->tokenIds.end());
}
if (tokenIds.size())
{
MessageActivateTokenIds(utility::toVector(tokenIds)).dispatch();
}
}
void QtCodeField::focusTokenIds(const std::vector<Id>& tokenIds)
{
annotateText(std::set<Id>(), std::set<Id>(), std::set<Id>(tokenIds.begin(), tokenIds.end()));
}
void QtCodeField::defocusTokenIds(const std::vector<Id>& tokenIds)
{
annotateText(std::set<Id>(), std::set<Id>(), std::set<Id>());
}
bool QtCodeField::annotateText(
const std::set<Id>& activeSymbolIds, const std::set<Id>& activeLocationIds, const std::set<Id>& focusedSymbolIds)
{
std::vector<int> linesToRehighlight;
bool needsUpdate = false;
for (size_t i = 0; i < m_annotations.size(); i++)
{
Annotation& annotation = m_annotations[i];
bool wasActive = annotation.isActive;
bool wasFocused = annotation.isFocused;
const AnnotationColor& oldColor = getAnnotationColorForAnnotation(annotation);
annotation.isActive = (
utility::shareElement(activeSymbolIds, annotation.tokenIds) ||
activeLocationIds.find(annotation.locationId) != activeLocationIds.end()
);
if (!annotation.isActive)
{
annotation.isFocused = utility::shareElement(focusedSymbolIds, annotation.tokenIds);
}
const AnnotationColor& newColor = getAnnotationColorForAnnotation(annotation);
if (newColor.text != oldColor.text || (!m_wasAnnotated && newColor.text != "transparent"))
{
if (newColor.text.size() > 0 && newColor.text != "transparent")
{
if (!annotation.oldTextColor.isValid())
{
annotation.oldTextColor =
m_highlighter->getFormat(annotation.start, annotation.end).foreground().color();
}
setTextColorForAnnotation(annotation, QColor(newColor.text.c_str()));
m_colorChangedAnnotationIndices.insert(i);
}
else if (annotation.oldTextColor.isValid())
{
setTextColorForAnnotation(annotation, annotation.oldTextColor);
annotation.oldTextColor = QColor();
m_colorChangedAnnotationIndices.erase(i);
linesToRehighlight.push_back(annotation.startLine - 1);
}
}
if (wasFocused != annotation.isFocused || wasActive != annotation.isActive)
{
needsUpdate = true;
}
}
if (linesToRehighlight.size())
{
m_highlighter->rehighlightLines(linesToRehighlight);
}
needsUpdate = (needsUpdate && m_wasAnnotated);
if (needsUpdate)
{
viewport()->update();
}
m_wasAnnotated = true;
return needsUpdate;
}
void QtCodeField::createAnnotations(std::shared_ptr<SourceLocationFile> locationFile)
{
uint endLineNumber = getEndLineNumber();
std::set<Id> locationIds;
locationFile->forEachSourceLocation(
[&](const SourceLocation* location)
{
if (locationIds.find(location->getLocationId()) != locationIds.end())
{
return;
}
locationIds.insert(location->getLocationId());
Annotation annotation;
const SourceLocation* startLocation = location->getStartLocation();
if (!startLocation || startLocation->getLineNumber() < m_startLineNumber)
{
annotation.start = startTextEditPosition();
annotation.startLine = m_startLineNumber;
annotation.startCol = 0;
}
else if (startLocation->getLineNumber() <= endLineNumber)
{
annotation.start = toTextEditPosition(startLocation->getLineNumber(), startLocation->getColumnNumber() - 1);
annotation.startLine = startLocation->getLineNumber();
annotation.startCol = startLocation->getColumnNumber() - 1;
}
else
{
return;
}
const SourceLocation* endLocation = location->getEndLocation();
if (!endLocation || endLocation->getLineNumber() > endLineNumber)
{
annotation.end = endTextEditPosition();
annotation.endLine = endLineNumber;
annotation.endCol = m_lineLengths[document()->blockCount() - 1];
}
else if (endLocation->getLineNumber() >= m_startLineNumber)
{
annotation.end = toTextEditPosition(endLocation->getLineNumber(), endLocation->getColumnNumber());
annotation.endLine = endLocation->getLineNumber();
annotation.endCol = endLocation->getColumnNumber();
}
else
{
return;
}
annotation.tokenIds.insert(location->getTokenIds().begin(), location->getTokenIds().end());
annotation.locationId = location->getLocationId();
annotation.locationType = location->getType();
annotation.isActive = false;
annotation.isFocused = false;
m_annotations.push_back(annotation);
}
);
}
int QtCodeField::toTextEditPosition(int lineNumber, int columnNumber) const
{
lineNumber -= m_startLineNumber - 1;
int position = 0;
for (int i = 0; i < lineNumber - 1; i++)
{
position += m_lineLengths[i];
}
position += columnNumber;
return position;
}
std::pair<int, int> QtCodeField::toLineColumn(int textEditPosition) const
{
int lineNumber = m_startLineNumber;
for (int i = 0; i < document()->lineCount(); i++)
{
int nextTextEditPosition = textEditPosition - m_lineLengths[i];
if (nextTextEditPosition >= 0)
{
textEditPosition = nextTextEditPosition;
lineNumber++;
}
else
{
break;
}
}
return std::make_pair(lineNumber, textEditPosition);
}
int QtCodeField::startTextEditPosition() const
{
return 0;
}
int QtCodeField::endTextEditPosition() const
{
return m_endTextEditPosition;
}
void QtCodeField::setHoveredAnnotations(const std::vector<const Annotation*>& annotations)
{
if (m_hoveredAnnotations.size())
{
std::vector<Id> tokenIds;
for (const Annotation* annotation : m_hoveredAnnotations)
{
tokenIds.insert(tokenIds.end(), annotation->tokenIds.begin(), annotation->tokenIds.end());
}
defocusTokenIds(tokenIds);
}
m_hoveredAnnotations = annotations;
if (annotations.size())
{
std::vector<Id> tokenIds;
for (const Annotation* annotation : annotations)
{
tokenIds.insert(tokenIds.end(), annotation->tokenIds.begin(), annotation->tokenIds.end());
}
focusTokenIds(tokenIds);
}
}
std::vector<QRect> QtCodeField::getCursorRectsForAnnotation(const Annotation& annotation) const
{
std::vector<QRect> rects;
QTextCursor cursor = QTextCursor(document());
cursor.setPosition(annotation.start);
QRect rectStart = cursorRect(cursor);
QRect rectEnd;
int line = annotation.startLine;
while (line <= annotation.endLine)
{
if (line == annotation.endLine)
{
// Avoid that annotations at line end span down to first column of the next line.
if (annotation.startLine != annotation.endLine ||
m_lineLengths[line - m_startLineNumber] != annotation.endCol)
{
cursor.setPosition(annotation.end);
}
}
else
{
cursor.setPosition(toTextEditPosition(line, m_lineLengths[line - m_startLineNumber] - 1));
}
rectEnd = cursorRect(cursor);
rects.push_back(QRect(
rectStart.left(),
rectStart.top(),
rectEnd.right() - rectStart.left(),
rectEnd.bottom() - rectStart.top()
));
line++;
if (int(line - m_startLineNumber) < document()->blockCount())
{
cursor.setPosition(toTextEditPosition(line, 0));
rectStart = cursorRect(cursor);
}
}
return rects;
}
const QtCodeField::AnnotationColor& QtCodeField::getAnnotationColorForAnnotation(const Annotation& annotation)
{
if (!s_annotationColors.size())
{
ColorScheme* scheme = ColorScheme::getInstance().get();
std::vector<std::string> types = { "token", "local_symbol", "scope", "error", "fulltext" };
std::vector<ColorScheme::ColorState> states = { ColorScheme::NORMAL, ColorScheme::FOCUS, ColorScheme::ACTIVE };
for (const std::string& type : types)
{
for (const ColorScheme::ColorState& state : states)
{
AnnotationColor color;
color.border = scheme->getCodeAnnotationTypeColor(type, "border", state);
color.fill = scheme->getCodeAnnotationTypeColor(type, "fill", state);
color.text = scheme->getCodeAnnotationTypeColor(type, "text", state);
s_annotationColors.push_back(color);
}
}
}
size_t i = 0;
if (annotation.locationType == LOCATION_LOCAL_SYMBOL)
{
i = 3;
}
else if (annotation.locationType == LOCATION_SCOPE)
{
i = 6;
}
else if (annotation.locationType == LOCATION_ERROR)
{
i = 9;
}
else if (annotation.locationType == LOCATION_FULLTEXT)
{
i = 12;
}
if (annotation.isActive)
{
i += 2;
}
else if (annotation.isFocused)
{
i += 1;
}
return s_annotationColors[i];
}
void QtCodeField::setTextColorForAnnotation(Annotation& annotation, QColor color) const
{
QTextCharFormat format;
format.setForeground(color);
m_highlighter->applyFormat(annotation.start, annotation.end, format);
}
std::vector<const QtCodeField::Annotation*> QtCodeField::getInteractiveAnnotationsForPosition(int pos) const
{
std::vector<const QtCodeField::Annotation*> annotations;
for (const Annotation& annotation : m_annotations)
{
const LocationType& type = annotation.locationType;
if ((type == LOCATION_TOKEN || type == LOCATION_LOCAL_SYMBOL || type == LOCATION_ERROR)
&& pos >= annotation.start && pos <= annotation.end)
{
annotations.push_back(&annotation);
}
}
return annotations;
}
void QtCodeField::createLineLengthCache()
{
m_endTextEditPosition = -1;
m_lineLengths.clear();
for (QTextBlock it = document()->begin(); it != document()->end(); it = it.next())
{
m_lineLengths.push_back(it.length());
m_endTextEditPosition += it.length();
}
}
+122
View File
@@ -0,0 +1,122 @@
#ifndef QT_CODE_FIELD_H
#define QT_CODE_FIELD_H
#include <set>
#include <QPlainTextEdit>
#include "data/location/LocationType.h"
#include "utility/types.h"
class QtHighlighter;
class SourceLocation;
class SourceLocationFile;
class QtCodeField
: public QPlainTextEdit
{
Q_OBJECT
public:
static void clearAnnotationColors();
QtCodeField(
uint startLineNumber,
const std::string& code,
std::shared_ptr<SourceLocationFile> locationFile,
QWidget* parent = nullptr);
~QtCodeField();
virtual QSize sizeHint() const Q_DECL_OVERRIDE;
uint getStartLineNumber() const;
uint getEndLineNumber() const;
std::string getCode() const;
std::shared_ptr<SourceLocationFile> getSourceLocationFile() const;
void annotateText();
protected:
virtual void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE;
virtual void enterEvent(QEvent* event) Q_DECL_OVERRIDE;
virtual void leaveEvent(QEvent* event) Q_DECL_OVERRIDE;
virtual void mouseMoveEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
virtual void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
virtual void focusTokenIds(const std::vector<Id>& tokenIds);
virtual void defocusTokenIds(const std::vector<Id>& tokenIds);
struct Annotation
{
int startLine;
int endLine;
int startCol;
int endCol;
int start;
int end;
std::set<Id> tokenIds;
Id locationId;
LocationType locationType;
bool isActive;
bool isFocused;
QColor oldTextColor;
};
struct AnnotationColor
{
std::string border;
std::string fill;
std::string text;
};
bool annotateText(
const std::set<Id>& activeSymbolIds, const std::set<Id>& activeLocationIds, const std::set<Id>& focusedSymbolIds);
void createAnnotations(std::shared_ptr<SourceLocationFile> locationFile);
int toTextEditPosition(int lineNumber, int columnNumber) const;
std::pair<int, int> toLineColumn(int textEditPosition) const;
int startTextEditPosition() const;
int endTextEditPosition() const;
void setHoveredAnnotations(const std::vector<const Annotation*>& annotations);
std::vector<QRect> getCursorRectsForAnnotation(const Annotation& annotation) const;
const AnnotationColor& getAnnotationColorForAnnotation(const Annotation& annotation);
void setTextColorForAnnotation(Annotation& annotation, QColor color) const;
std::vector<const Annotation*> getInteractiveAnnotationsForPosition(int pos) const;
std::vector<Annotation> m_annotations;
std::vector<const Annotation*> m_hoveredAnnotations;
private:
static std::vector<AnnotationColor> s_annotationColors;
void createLineLengthCache();
const uint m_startLineNumber;
const std::string m_code;
std::shared_ptr<SourceLocationFile> m_locationFile;
QtHighlighter* m_highlighter;
std::vector<int> m_lineLengths;
std::set<size_t> m_colorChangedAnnotationIndices;
int m_endTextEditPosition;
bool m_wasAnnotated;
};
#endif // QT_CODE_FIELD_H
+9 -6
View File
@@ -876,10 +876,13 @@ void QtCodeNavigator::handleMessage(MessageSwitchColorScheme* message)
void QtCodeNavigator::handleMessage(MessageWindowFocus* message)
{
m_onQtThread(
[=]()
{
m_current->onWindowFocus();
}
);
if (message->focusIn)
{
m_onQtThread(
[=]()
{
m_current->onWindowFocus();
}
);
}
}
+179
View File
@@ -0,0 +1,179 @@
#include "qt/element/QtTooltip.h"
#include <QApplication>
#include <QCursor>
#include <QHBoxLayout>
#include <QLabel>
#include <QPoint>
#include <QStyle>
#include <QTimer>
#include "data/location/SourceLocationFile.h"
#include "data/tooltip/TooltipInfo.h"
#include "qt/element/QtCodeField.h"
QtTooltip::QtTooltip(QWidget* parent)
: QFrame(parent)
, m_parentView(nullptr)
, m_isHovered(false)
{
QWidget::setWindowFlags(Qt::ToolTip);
setObjectName("tooltip");
QVBoxLayout* layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
setLayout(layout);
}
QtTooltip::~QtTooltip()
{
}
void QtTooltip::setTooltipInfo(TooltipInfo info)
{
if (info.title.size())
{
addTitle(info.title.c_str(), info.count, info.countText.c_str());
}
for (TooltipSnippet snippet : info.snippets)
{
QtCodeField* field = new QtCodeField(1, snippet.code, snippet.locationFile);
QSize size = field->sizeHint() + QSize(15, 5);
if (size.width() > 600)
{
field->setMinimumSize(QSize(600, size.height() + QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent)));
}
else
{
field->setMinimumSize(size);
field->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
field->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
field->annotateText();
addWidget(field);
}
m_offset = QPoint(info.offset.x(), info.offset.y());
}
void QtTooltip::setParentView(QWidget* parentView)
{
m_parentView = parentView;
}
bool QtTooltip::isHovered() const
{
return m_isHovered;
}
void QtTooltip::show()
{
QWidget* parent = m_parentView ? m_parentView : parentWidget();
if (!parent)
{
return;
}
QWidget::show();
QPoint pos = QCursor::pos() + m_offset;
if (pos.x() + width() > parent->pos().x() + parent->width())
{
pos.setX(pos.x() - width() - m_offset.x() * 2);
}
if (pos.x() < parent->pos().x())
{
pos.setX(parent->pos().x() + 10);
}
if (pos.y() + height() > parent->pos().y() + parent->height())
{
pos.setY(pos.y() - height() - m_offset.y() * 2);
}
if (pos.y() < parent->pos().y())
{
pos.setY(parent->pos().y() + 10);
}
move(pos);
}
void QtTooltip::hide(bool force)
{
if (!m_isHovered || force)
{
QWidget::hide();
clearLayout(layout());
m_parentView = nullptr;
m_offset = QPoint();
}
}
void QtTooltip::leaveEvent(QEvent *event)
{
m_isHovered = false;
QTimer::singleShot(500, this, SLOT(hide()));
}
void QtTooltip::enterEvent(QEvent *event)
{
m_isHovered = true;
}
void QtTooltip::addTitle(QString title, int count, QString countText)
{
QHBoxLayout* titleLayout = new QHBoxLayout();
titleLayout->setContentsMargins(0, 0, 0, 0);
titleLayout->setSpacing(0);
QLabel* titleLabel = new QLabel(title);
titleLabel->setObjectName("tooltip_title");
titleLayout->addWidget(titleLabel);
if (count >= 0)
{
QLabel* referenceLabel = new QLabel(QString::number(count) + " " + countText + (count != 1 ? "s" : ""));
referenceLabel->setObjectName("tooltip_references");
titleLayout->addWidget(referenceLabel, 0, Qt::AlignRight);
}
QWidget* titleWidget = new QWidget();
titleWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
titleWidget->setLayout(titleLayout);
layout()->addWidget(titleWidget);
}
void QtTooltip::addWidget(QWidget *widget)
{
widget->setObjectName("tooltip_widget");
layout()->addWidget(widget);
}
void QtTooltip::clearLayout(QLayout* layout)
{
while (QLayoutItem* item = layout->takeAt(0))
{
if (QWidget* widget = item->widget())
{
widget->deleteLater();
}
if (QLayout* childLayout = item->layout())
{
clearLayout(childLayout);
}
delete item;
}
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef QT_TOOLTIP_H
#define QT_TOOLTIP_H
#include <QFrame>
struct TooltipInfo;
class QtTooltip
: public QFrame
{
Q_OBJECT
public:
QtTooltip(QWidget* parent = nullptr);
virtual ~QtTooltip();
void setTooltipInfo(TooltipInfo info);
void setParentView(QWidget* parentView);
bool isHovered() const;
public slots:
virtual void show();
virtual void hide(bool force = false);
protected:
virtual void leaveEvent(QEvent* event);
virtual void enterEvent(QEvent* event);
private:
void addTitle(QString title, int count, QString countText);
void addWidget(QWidget* widget);
void clearLayout(QLayout* layout);
QWidget* m_parentView;
QPoint m_offset;
bool m_isHovered;
};
#endif // QT_TOOLTIP_H
+5
View File
@@ -55,6 +55,11 @@ void QtMainView::hideView(View* view)
);
}
View* QtMainView::findFloatingView(const std::string& name) const
{
return m_window->findFloatingView(name);
}
void QtMainView::loadLayout()
{
m_window->loadLayout();
+2
View File
@@ -38,6 +38,8 @@ public:
virtual void showView(View* view);
virtual void hideView(View* view);
virtual View* findFloatingView(const std::string& name) const;
virtual QStatusBar* getStatusBar();
virtual void setStatusBar(QStatusBar* statusBar);
+77
View File
@@ -0,0 +1,77 @@
#include "qt/view/QtTooltipView.h"
#include "qt/element/QtTooltip.h"
#include "qt/utility/utilityQt.h"
#include "qt/view/QtMainView.h"
#include "qt/view/QtViewWidgetWrapper.h"
#include "qt/window/QtMainWindow.h"
#include "settings/ColorScheme.h"
#include "utility/ResourcePaths.h"
QtTooltipView::QtTooltipView(ViewLayout* viewLayout)
: TooltipView(viewLayout)
{
m_widget = new QtTooltip(dynamic_cast<QtMainView*>(getViewLayout())->getMainWindow());
}
QtTooltipView::~QtTooltipView()
{
}
void QtTooltipView::createWidgetWrapper()
{
setWidgetWrapper(std::make_shared<QtViewWidgetWrapper>(m_widget));
}
void QtTooltipView::initView()
{
}
void QtTooltipView::refreshView()
{
m_onQtThread([=]()
{
setStyleSheet();
});
}
void QtTooltipView::showTooltip(TooltipInfo info, const View* parent)
{
m_onQtThread([=]()
{
if (m_widget->isHovered())
{
return;
}
m_widget->hide();
m_widget->setTooltipInfo(info);
if (parent)
{
m_widget->setParentView(QtViewWidgetWrapper::getWidgetOfView(parent)->parentWidget());
}
m_widget->show();
});
}
void QtTooltipView::hideTooltip(bool force)
{
m_onQtThread([=]()
{
m_widget->hide(force);
});
}
bool QtTooltipView::tooltipVisible() const
{
return m_widget->isVisible();
}
void QtTooltipView::setStyleSheet()
{
m_widget->setStyleSheet(
utility::getStyleSheet(ResourcePaths::getGuiPath().concat(FilePath("tooltip_view/tooltip.css"))).c_str()
);
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef QT_TOOLTIP_VIEW
#define QT_TOOLTIP_VIEW
#include "component/view/TooltipView.h"
#include "qt/utility/QtThreadedFunctor.h"
class QTimer;
class QtTooltip;
class QtTooltipView
: public TooltipView
{
public:
QtTooltipView(ViewLayout* viewLayout);
~QtTooltipView();
// View implementation
virtual void createWidgetWrapper();
virtual void initView();
virtual void refreshView();
virtual void showTooltip(TooltipInfo info, const View* parent);
virtual void hideTooltip(bool force);
virtual bool tooltipVisible() const;
private:
void setStyleSheet();
void doRefreshView();
QtThreadedLambdaFunctor m_onQtThread;
QtTooltip* m_widget;
};
#endif // QT_TOOLTIP_VIEW
+6
View File
@@ -15,6 +15,7 @@
#include "qt/view/QtStatusBarView.h"
#include "qt/view/QtStatusView.h"
#include "qt/view/QtTabbedView.h"
#include "qt/view/QtTooltipView.h"
#include "qt/view/QtUndoRedoView.h"
QtViewFactory::QtViewFactory()
@@ -93,6 +94,11 @@ std::shared_ptr<StatusBarView> QtViewFactory::createStatusBarView(ViewLayout* vi
return View::createAndInit<QtStatusBarView>(viewLayout);
}
std::shared_ptr<TooltipView> QtViewFactory::createTooltipView(ViewLayout* viewLayout) const
{
return View::createAndInit<QtTooltipView>(viewLayout);
}
std::shared_ptr<UndoRedoView> QtViewFactory::createUndoRedoView(ViewLayout* viewLayout) const
{
return View::createInitAndAddToLayout<QtUndoRedoView>(viewLayout);
+1
View File
@@ -23,6 +23,7 @@ public:
virtual std::shared_ptr<SearchView> createSearchView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<StatusBarView> createStatusBarView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<StatusView> createStatusView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<TooltipView> createTooltipView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<UndoRedoView> createUndoRedoView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<DialogView> createDialogView(ViewLayout* viewLayout, StorageAccess* storageAccess) const;
@@ -16,8 +16,11 @@
#include "utility/messaging/type/MessageFocusIn.h"
#include "utility/messaging/type/MessageFocusOut.h"
#include "utility/messaging/type/MessageGraphNodeBundleSplit.h"
#include "utility/messaging/type/MessageTooltipShow.h"
#include "utility/messaging/type/MessageTooltipHide.h"
#include "utility/utility.h"
QtGraphEdge* QtGraphEdge::s_focusedEdge = nullptr;
QtGraphEdge* QtGraphEdge::s_focusedBezierEdge = nullptr;
QtGraphEdge::QtGraphEdge(
@@ -52,6 +55,7 @@ QtGraphEdge::QtGraphEdge(
m_fromActive = m_owner.lock()->getIsActive();
m_toActive = m_target.lock()->getIsActive();
s_focusedEdge = nullptr;
s_focusedBezierEdge = nullptr;
}
@@ -85,26 +89,7 @@ void QtGraphEdge::updateLine()
return;
}
Edge::EdgeType type;
if (getData())
{
type = getData()->getType();
}
else
{
type = Edge::EDGE_AGGREGATION;
}
QString toolTip = Edge::getReadableTypeString(type).c_str();
if (type == Edge::EDGE_AGGREGATION)
{
toolTip += ": " + QString::number(m_weight) + " edge";
if (m_weight != 1)
{
toolTip += "s";
}
}
Edge::EdgeType type = (getData() ? getData()->getType() : Edge::EDGE_AGGREGATION);
GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(type, m_isActive | m_isFocused, false, m_isTrailEdge);
if (m_useBezier)
@@ -130,10 +115,8 @@ void QtGraphEdge::updateLine()
bezier->updateLine(ownerRect, rect, ownerParentRect, rect, style, m_weight, false);
bezier->setRoute(route);
bezier->setPivot(QtLineItemBase::PIVOT_MIDDLE);
bezier->setToolTip(toolTip);
QtLineItemStraight* line = new QtLineItemStraight(this);
line->setToolTip(toolTip);
if (route == QtLineItemBase::ROUTE_HORIZONTAL)
{
line->updateLine(Vec2i(rect.x(), (rect.y() + rect.w()) / 2), Vec2i(rect.z(), (rect.y() + rect.w()) / 2), style);
@@ -155,7 +138,6 @@ void QtGraphEdge::updateLine()
style, m_weight, showArrow);
bezier->setRoute(route);
bezier->setPivot(QtLineItemBase::PIVOT_MIDDLE);
bezier->setToolTip(toolTip);
if (owner->getLastParent() == target->getLastParent())
{
@@ -221,8 +203,6 @@ void QtGraphEdge::updateLine()
owner->getBoundingRect(), target->getBoundingRect(),
owner->getParentBoundingRect(), target->getParentBoundingRect(),
style, m_weight, showArrow);
child->setToolTip(toolTip);
}
this->setZValue(style.zValue);
@@ -301,6 +281,22 @@ void QtGraphEdge::focusIn()
{
m_isFocused = true;
updateLine();
if (s_focusedEdge == this)
{
Edge::EdgeType type = (getData() ? getData()->getType() : Edge::EDGE_AGGREGATION);
TooltipInfo info;
info.title = Edge::getReadableTypeString(type);
if (type == Edge::EDGE_AGGREGATION)
{
info.count = m_weight;
info.countText = "edge";
}
info.offset = Vec2i(10, 20);
MessageTooltipShow(info, TOOLTIP_ORIGIN_GRAPH).dispatch();
}
}
}
@@ -310,6 +306,11 @@ void QtGraphEdge::focusOut()
{
m_isFocused = false;
updateLine();
if (s_focusedEdge == this)
{
MessageTooltipHide().dispatch();
}
}
}
@@ -349,9 +350,11 @@ void QtGraphEdge::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
s_focusedBezierEdge = this;
}
s_focusedEdge = this;
if (getData() && !m_useBezier)
{
MessageFocusIn(std::vector<Id>(1, getData()->getId())).dispatch();
MessageFocusIn(std::vector<Id>(1, getData()->getId()), TOOLTIP_ORIGIN_GRAPH).dispatch();
}
else
{
@@ -371,6 +374,8 @@ void QtGraphEdge::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
{
focusOut();
}
s_focusedEdge = nullptr;
}
void QtGraphEdge::setDirection(TokenComponentAggregation::Direction direction)
@@ -64,6 +64,9 @@ protected:
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
private:
// used to send tooltip message on focusIn(), because both focus messages are filtered out if sent close together
static QtGraphEdge* s_focusedEdge;
// used to unfocus recent edge, because hover leave event is not always received for bezier edges
static QtGraphEdge* s_focusedBezierEdge;
@@ -8,7 +8,6 @@
#include "utility/messaging/type/MessageGraphNodeMove.h"
#include "data/graph/token_component/TokenComponentFilePath.h"
#include "data/graph/token_component/TokenComponentSignature.h"
QtGraphNodeData::QtGraphNodeData(const Node* data, const std::string& name, bool hasParent, bool childVisible, bool hasQualifier)
: m_data(data)
@@ -18,39 +17,6 @@ QtGraphNodeData::QtGraphNodeData(const Node* data, const std::string& name, bool
this->setAcceptHoverEvents(true);
this->setName(name);
std::string toolTip = data->getReadableTypeString();
if (!data->isDefined() && data->isType(Node::NODE_FILE))
{
toolTip = "incomplete " + toolTip;
}
else if (!data->isDefined() && !data->isType(Node::NODE_NON_INDEXED))
{
toolTip = "non-indexed " + toolTip;
}
else if (data->isImplicit())
{
toolTip = "implicit " + toolTip;
}
if (data->isType(Node::NODE_FUNCTION | Node::NODE_METHOD))
{
TokenComponentSignature* sig = data->getComponent<TokenComponentSignature>();
if (sig)
{
toolTip += ": " + sig->getSignature();
}
}
else
{
FilePath path = getFilePath();
if (!path.empty())
{
toolTip += ": " + path.str();
}
}
this->setToolTip(QString::fromStdString(toolTip));
}
QtGraphNodeData::~QtGraphNodeData()
@@ -113,7 +79,7 @@ void QtGraphNodeData::updateStyle()
void QtGraphNodeData::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
MessageFocusIn(std::vector<Id>(1, m_data->getId())).dispatch();
MessageFocusIn(std::vector<Id>(1, m_data->getId()), TOOLTIP_ORIGIN_GRAPH).dispatch();
}
void QtGraphNodeData::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
+18 -1
View File
@@ -200,6 +200,19 @@ void QtMainWindow::hideView(View* view)
getDockWidgetForView(view)->widget->setHidden(true);
}
View* QtMainWindow::findFloatingView(const std::string& name) const
{
for (size_t i = 0; i < m_dockWidgets.size(); i++)
{
if (std::string(m_dockWidgets[i].view->getName()) == name && m_dockWidgets[i].widget->isFloating())
{
return m_dockWidgets[i].view;
}
}
return nullptr;
}
void QtMainWindow::loadLayout()
{
QSettings settings(UserPaths::getWindowSettingsPath().str().c_str(), QSettings::IniFormat);
@@ -337,7 +350,11 @@ bool QtMainWindow::event(QEvent* event)
{
if (event->type() == QEvent::WindowActivate)
{
MessageWindowFocus().dispatch();
MessageWindowFocus(true).dispatch();
}
else if (event->type() == QEvent::WindowDeactivate)
{
MessageWindowFocus(false).dispatch();
}
return QMainWindow::event(event);
+2
View File
@@ -63,6 +63,8 @@ public:
void showView(View* view);
void hideView(View* view);
View* findFloatingView(const std::string& name) const;
void loadLayout();
void saveLayout();
@@ -107,7 +107,7 @@ public:
std::shared_ptr<SourceGroupSettings> settings, QtProjectWizzardWindow* window, bool isCDB = false);
// QtProjectWizzardContent implementation
virtual void populate(QGridLayout* layout, int& row);
virtual void populate(QGridLayout* layout, int& row) override;
virtual void load() override;
virtual void save() override;
virtual bool isScrollAble() const override;