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:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user