ui: Improved QtCodeView to annotate code
Added some features to QTCodeSnippet: * Code is now annotated using information from TokenLocationFile * Start of line numbering can be set * Clicking on an annotation in the code gives the corresponding Token Id fortune cookie message = Your kindness will be repaid.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
add_files(
|
||||
APP_FILES
|
||||
|
||||
qt/element/QtCodeSnippet.cpp
|
||||
qt/element/QtCodeSnippet.h
|
||||
qt/element/QtMainWindow.cpp
|
||||
qt/element/QtMainWindow.h
|
||||
|
||||
@@ -12,7 +14,7 @@ add_files(
|
||||
|
||||
qt/view/graphElements/QtGraphNode.cpp
|
||||
qt/view/graphElements/QtGraphNode.h
|
||||
|
||||
|
||||
qt/view/QtCodeView.cpp
|
||||
qt/view/QtCodeView.h
|
||||
qt/view/QtGraphView.cpp
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
#include "qt/element/QtCodeSnippet.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "data/location/TokenLocation.h"
|
||||
#include "data/location/TokenLocationFile.h"
|
||||
#include "data/location/TokenLocationLine.h"
|
||||
#include "qt/view/QtCodeView.h"
|
||||
|
||||
QtCodeSnippet::LineNumberArea::LineNumberArea(QtCodeSnippet *codeSnippet)
|
||||
: QWidget(codeSnippet)
|
||||
, m_codeSnippet(codeSnippet)
|
||||
{
|
||||
}
|
||||
|
||||
QtCodeSnippet::LineNumberArea::~LineNumberArea()
|
||||
{
|
||||
}
|
||||
|
||||
QSize QtCodeSnippet::LineNumberArea::sizeHint() const
|
||||
{
|
||||
return QSize(m_codeSnippet->lineNumberAreaWidth(), 0);
|
||||
}
|
||||
|
||||
void QtCodeSnippet::LineNumberArea::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
m_codeSnippet->lineNumberAreaPaintEvent(event);
|
||||
}
|
||||
|
||||
|
||||
QtCodeSnippet::QtCodeSnippet(QtCodeView* parentView, int startLineNumber, QWidget *parent)
|
||||
: QPlainTextEdit(parent)
|
||||
, m_parentView(parentView)
|
||||
, m_startLineNumber(startLineNumber)
|
||||
{
|
||||
m_lineNumberArea = new LineNumberArea(this);
|
||||
|
||||
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
|
||||
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
|
||||
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(clickTokenLocation()));
|
||||
|
||||
updateLineNumberAreaWidth(0);
|
||||
}
|
||||
|
||||
QtCodeSnippet::~QtCodeSnippet()
|
||||
{
|
||||
}
|
||||
|
||||
void QtCodeSnippet::lineNumberAreaPaintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(m_lineNumberArea);
|
||||
painter.fillRect(event->rect(), QColor(225,225,225));
|
||||
|
||||
QTextBlock block = firstVisibleBlock();
|
||||
int blockNumber = block.blockNumber();
|
||||
int top = static_cast<int>(blockBoundingGeometry(block).translated(contentOffset()).top());
|
||||
int bottom = top + static_cast<int>(blockBoundingRect(block).height());
|
||||
|
||||
while (block.isValid() && top <= event->rect().bottom())
|
||||
{
|
||||
if (block.isVisible() && bottom >= event->rect().top())
|
||||
{
|
||||
QString number = QString::number(blockNumber + m_startLineNumber);
|
||||
painter.setPen(Qt::black);
|
||||
painter.drawText(0, top, m_lineNumberArea->width() - 1, fontMetrics().height(), Qt::AlignRight, number);
|
||||
}
|
||||
|
||||
block = block.next();
|
||||
top = bottom;
|
||||
bottom = top + static_cast<int>(blockBoundingRect(block).height());
|
||||
blockNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
int QtCodeSnippet::lineNumberAreaWidth()
|
||||
{
|
||||
int digits = 1;
|
||||
int max = qMax(1, m_startLineNumber + blockCount());
|
||||
|
||||
while (max >= 10)
|
||||
{
|
||||
max /= 10;
|
||||
digits++;
|
||||
}
|
||||
|
||||
int width = 2 + fontMetrics().width(QLatin1Char('9')) * digits;
|
||||
return width;
|
||||
}
|
||||
|
||||
void QtCodeSnippet::annotateText(const TokenLocationFile& locationFile)
|
||||
{
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
|
||||
for (const TokenLocationFile::TokenLocationLinePairType& lineItem : locationFile.getTokenLocationLines())
|
||||
{
|
||||
for (const TokenLocationLine::TokenLocationPairType& locationItem : lineItem.second->getTokenLocations())
|
||||
{
|
||||
TokenLocation* location = locationItem.second.get();
|
||||
if (!location->isStartTokenLocation())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Annotation annotation;
|
||||
annotation.start = toTextEditPosition(location->getLineNumber(), location->getColumnNumber() - 1);
|
||||
annotation.end = toTextEditPosition(
|
||||
location->getEndTokenLocation()->getLineNumber(),
|
||||
location->getEndTokenLocation()->getColumnNumber());
|
||||
annotation.tokenId = location->getTokenId();
|
||||
m_annotations.push_back(annotation);
|
||||
|
||||
QTextEdit::ExtraSelection selection;
|
||||
|
||||
QColor color = QColor(Qt::red);
|
||||
color.setAlphaF(0.3f);
|
||||
selection.format.setBackground(color);
|
||||
|
||||
selection.cursor = textCursor();
|
||||
selection.cursor.clearSelection();
|
||||
selection.cursor.setPosition(annotation.start);
|
||||
selection.cursor.setPosition(annotation.end, QTextCursor::KeepAnchor);
|
||||
|
||||
extraSelections.append(selection);
|
||||
}
|
||||
}
|
||||
|
||||
setExtraSelections(extraSelections);
|
||||
}
|
||||
|
||||
void QtCodeSnippet::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
QPlainTextEdit::resizeEvent(e);
|
||||
|
||||
QRect cr = contentsRect();
|
||||
m_lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
|
||||
}
|
||||
|
||||
void QtCodeSnippet::updateLineNumberAreaWidth(int /* newBlockCount */)
|
||||
{
|
||||
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
|
||||
}
|
||||
|
||||
void QtCodeSnippet::updateLineNumberArea(const QRect &rect, int dy)
|
||||
{
|
||||
if (dy)
|
||||
{
|
||||
m_lineNumberArea->scroll(0, dy);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lineNumberArea->update(0, rect.y(), m_lineNumberArea->width(), rect.height());
|
||||
}
|
||||
|
||||
if (rect.contains(viewport()->rect()))
|
||||
{
|
||||
updateLineNumberAreaWidth(0);
|
||||
}
|
||||
}
|
||||
|
||||
void QtCodeSnippet::clickTokenLocation()
|
||||
{
|
||||
int clickPosition = textCursor().position();
|
||||
for (Annotation annotation : m_annotations)
|
||||
{
|
||||
if (clickPosition >= annotation.start && clickPosition <= annotation.end)
|
||||
{
|
||||
m_parentView->activateToken(annotation.tokenId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int QtCodeSnippet::toTextEditPosition(int lineNumber, int columnNumber) const
|
||||
{
|
||||
lineNumber -= m_startLineNumber - 1;
|
||||
|
||||
int position = 0;
|
||||
for (int i = 0; i < lineNumber - 1; i++)
|
||||
{
|
||||
position += document()->findBlockByLineNumber(i).length();
|
||||
}
|
||||
position += columnNumber;
|
||||
|
||||
return position;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef QT_CODE_SNIPPET_H
|
||||
#define QT_CODE_SNIPPET_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
#include <QObject>
|
||||
|
||||
#include "utility/types.h"
|
||||
|
||||
class QPaintEvent;
|
||||
class QResizeEvent;
|
||||
class QSize;
|
||||
class QtCodeView;
|
||||
class QWidget;
|
||||
class TokenLocationFile;
|
||||
|
||||
class QtCodeSnippet: public QPlainTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
class LineNumberArea: public QWidget
|
||||
{
|
||||
public:
|
||||
LineNumberArea(QtCodeSnippet *codeSnippet);
|
||||
virtual ~LineNumberArea();
|
||||
|
||||
QSize sizeHint() const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
|
||||
private:
|
||||
QtCodeSnippet *m_codeSnippet;
|
||||
};
|
||||
|
||||
QtCodeSnippet(QtCodeView* parentView, int startLineNumber, QWidget *parent = 0);
|
||||
virtual ~QtCodeSnippet();
|
||||
|
||||
void lineNumberAreaPaintEvent(QPaintEvent *event);
|
||||
int lineNumberAreaWidth();
|
||||
|
||||
void annotateText(const TokenLocationFile& locationFile);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
private slots:
|
||||
void updateLineNumberAreaWidth(int newBlockCount);
|
||||
void updateLineNumberArea(const QRect &, int);
|
||||
void clickTokenLocation();
|
||||
|
||||
private:
|
||||
struct Annotation
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
Id tokenId;
|
||||
};
|
||||
|
||||
int toTextEditPosition(int lineNumber, int columnNumber) const;
|
||||
|
||||
QWidget *m_lineNumberArea;
|
||||
QtCodeView* m_parentView;
|
||||
|
||||
const int m_startLineNumber;
|
||||
std::vector<Annotation> m_annotations;
|
||||
};
|
||||
|
||||
#endif // QT_CODE_SNIPPET_H
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "qt/view/QtCodeView.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "data/location/TokenLocationFile.h"
|
||||
#include "qt/element/QtCodeSnippet.h"
|
||||
#include "qt/QtWidgetWrapper.h"
|
||||
#include "qt/utility/QtHighLighter.h"
|
||||
#include "qt/utility/utilityQt.h"
|
||||
@@ -9,7 +12,7 @@
|
||||
QtCodeView::QtCodeView(ViewLayout* viewLayout)
|
||||
: CodeView(viewLayout)
|
||||
, m_clearCodeSnippetsFunctor(std::bind(&QtCodeView::doClearCodeSnippets, this))
|
||||
, m_addCodeSnippetFunctor(std::bind(&QtCodeView::doAddCodeSnippet, this, std::placeholders::_1))
|
||||
, m_addCodeSnippetFunctor(std::bind(&QtCodeView::doAddCodeSnippet, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -37,9 +40,9 @@ void QtCodeView::initGui()
|
||||
m_font.setPointSize(10);
|
||||
}
|
||||
|
||||
void QtCodeView::addCodeSnippet(std::string str)
|
||||
void QtCodeView::addCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber)
|
||||
{
|
||||
m_addCodeSnippetFunctor(str);
|
||||
m_addCodeSnippetFunctor(str, locationFile, startLineNumber);
|
||||
}
|
||||
|
||||
void QtCodeView::clearCodeSnippets()
|
||||
@@ -47,17 +50,22 @@ void QtCodeView::clearCodeSnippets()
|
||||
m_clearCodeSnippetsFunctor();
|
||||
}
|
||||
|
||||
void QtCodeView::doAddCodeSnippet(std::string str)
|
||||
void QtCodeView::activateToken(Id tokenId) const
|
||||
{
|
||||
std::cout << "TODO: activate Token: " << tokenId << std::endl;
|
||||
}
|
||||
|
||||
void QtCodeView::doAddCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber)
|
||||
{
|
||||
std::shared_ptr<Snippet> snippet = std::make_shared<Snippet>();
|
||||
|
||||
snippet->textField = std::make_shared<QTextEdit>();
|
||||
snippet->textField = std::make_shared<QtCodeSnippet>(this, startLineNumber);
|
||||
snippet->textField->setReadOnly(true);
|
||||
snippet->textField->setFont(m_font);
|
||||
|
||||
snippet->highlighter = std::make_shared<QtHighlighter>(snippet->textField->document());
|
||||
|
||||
snippet->textField->setPlainText(QString::fromUtf8(str.c_str()));
|
||||
snippet->textField->annotateText(locationFile);
|
||||
|
||||
QWidget* widget = QtWidgetWrapper::getWidgetOfView(this);
|
||||
widget->layout()->addWidget(snippet->textField.get());
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
#include "component/view/CodeView.h"
|
||||
#include "qt/utility/QtThreadedFunctor.h"
|
||||
#include "utility/types.h"
|
||||
|
||||
class QtCodeSnippet;
|
||||
class QtHighlighter;
|
||||
class QTextEdit;
|
||||
|
||||
@@ -23,24 +25,26 @@ public:
|
||||
virtual void initGui();
|
||||
|
||||
// CodeView implementation
|
||||
virtual void addCodeSnippet(std::string str);
|
||||
virtual void addCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber);
|
||||
virtual void clearCodeSnippets();
|
||||
|
||||
void activateToken(Id tokenId) const;
|
||||
|
||||
private:
|
||||
struct Snippet
|
||||
{
|
||||
std::shared_ptr<QTextEdit> textField;
|
||||
std::shared_ptr<QtCodeSnippet> textField;
|
||||
std::shared_ptr<QtHighlighter> highlighter;
|
||||
};
|
||||
std::vector<std::shared_ptr<Snippet>> m_snippets;
|
||||
|
||||
void doAddCodeSnippet(std::string str);
|
||||
void doAddCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber);
|
||||
void doClearCodeSnippets();
|
||||
|
||||
QFont m_font;
|
||||
|
||||
QtThreadedFunctor<void> m_clearCodeSnippetsFunctor;
|
||||
QtThreadedFunctor<std::string> m_addCodeSnippetFunctor;
|
||||
QtThreadedFunctor<const std::string&, const TokenLocationFile&, int> m_addCodeSnippetFunctor;
|
||||
};
|
||||
|
||||
# endif // QT_CODE_VIEW_H
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "component/view/GraphView.h"
|
||||
#include "component/view/ViewLayout.h"
|
||||
|
||||
#include "data/location/TokenLocationFile.h"
|
||||
|
||||
std::shared_ptr<ComponentManager> ComponentManager::create(
|
||||
GuiFactory* guiFactory,
|
||||
ViewLayout* viewLayout,
|
||||
@@ -33,8 +35,36 @@ void ComponentManager::setup()
|
||||
m_components.push_back(codeComponent);
|
||||
|
||||
CodeView* codeView = codeComponent->getView<CodeView>();
|
||||
codeView->addCodeSnippet("class HelloWorld;");
|
||||
codeView->addCodeSnippet("static int n = 42; // the answer.");
|
||||
|
||||
std::string code =
|
||||
"class HelloWorld;\n"
|
||||
"static int n = 42; // the answer.\n"
|
||||
"\n"
|
||||
"int sum(int a, int b)\n"
|
||||
"{\n"
|
||||
" return a + b;\n"
|
||||
"}\n";
|
||||
|
||||
TokenLocationFile locationFile("test.cpp");
|
||||
locationFile.addTokenLocation(1, 1, 7, 1, 16);
|
||||
locationFile.addTokenLocation(2, 2, 8, 2, 10);
|
||||
locationFile.addTokenLocation(3, 2, 12, 2, 12);
|
||||
locationFile.addTokenLocation(4, 4, 1, 7, 1);
|
||||
|
||||
codeView->addCodeSnippet(code, locationFile, 1);
|
||||
|
||||
std::string code2 =
|
||||
"const char* name = new char[10];\n"
|
||||
"\n"
|
||||
"name = \"MetaVizz\\0\";";
|
||||
|
||||
TokenLocationFile locationFile2("test.cpp");
|
||||
locationFile2.addTokenLocation(5, 123, 1, 123, 11);
|
||||
locationFile2.addTokenLocation(6, 123, 13, 123, 16);
|
||||
locationFile2.addTokenLocation(7, 125, 1, 125, 4);
|
||||
|
||||
codeView->addCodeSnippet(code2, locationFile2, 123);
|
||||
|
||||
// codeView->clearCodeSnippets();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ void CodeController::setActiveTokenLocationId(Id id)
|
||||
{
|
||||
getView()->clearCodeSnippets();
|
||||
std::shared_ptr<TextAccess> textAccess = TextAccess::createFromFile(tokenLocation->getFilePath());
|
||||
getView()->addCodeSnippet(textAccess->getText());
|
||||
// getView()->addCodeSnippet(textAccess->getText());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "component/view/View.h"
|
||||
|
||||
class CodeController;
|
||||
class TokenLocationFile;
|
||||
|
||||
class CodeView: public View
|
||||
{
|
||||
@@ -13,7 +14,7 @@ public:
|
||||
|
||||
virtual std::string getName() const;
|
||||
|
||||
virtual void addCodeSnippet(std::string str) = 0;
|
||||
virtual void addCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber) = 0;
|
||||
virtual void clearCodeSnippets() = 0;
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user