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:
Eberhard Graether
2014-07-03 10:35:30 +02:00
parent e63106b13b
commit 4c7bbbe28e
8 changed files with 316 additions and 15 deletions
+3 -1
View File
@@ -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
+185
View File
@@ -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;
}
+71
View File
@@ -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
+14 -6
View File
@@ -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 -4
View File
@@ -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
+32 -2
View File
@@ -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());
}
}
+2 -1
View File
@@ -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: