Initial commit for code editor
MODIFY
RISC-V-sim.pro
RISC-V-sim.pro
@@ -25,7 +25,8 @@ SOURCES += src/main.cpp \
src/memorydisplaydelegate.cpp \
src/memoryview.cpp \
src/gotocombobox.cpp \
- src/addressdialog.cpp
+ src/addressdialog.cpp \
+ src/codeeditor.cpp
HEADERS += \
src/parser.h \
@@ -48,7 +49,8 @@ HEADERS += \
src/memoryview.h \
src/gotocombobox.h \
src/addressdialog.h \
- src/addressdialog.h
+ src/addressdialog.h \
+ src/codeeditor.h
FORMS += \
src/mainwindow.ui \

ADD
resources/breakpoint_disabled.png
resources/breakpoint_disabled.png
Binary files /dev/null and b/resources/breakpoint_disabled.png differ

ADD
resources/breakpoint_enabled.png
resources/breakpoint_enabled.png
Binary files /dev/null and b/resources/breakpoint_enabled.png differ

MODIFY
resources/images.qrc
resources/images.qrc
<RCC>
<qresource prefix="/logos">
<file>logo.png</file>
+ <file>breakpoint_disabled.png</file>
+ <file>breakpoint_enabled.png</file>
</qresource>
</RCC>

MODIFY
resources/logo.png
resources/logo.png
Binary files a/resources/logo.png and b/resources/logo.png differ

ADD
src/codeeditor.cpp
+#include "codeeditor.h"
+
+#include <QPainter>
+#include <QTextBlock>
+
+CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent) {
+ m_lineNumberArea = new LineNumberArea(this);
+ m_breakpointArea = new BreakpointArea(this);
+
+ connect(this, SIGNAL(blockCountChanged(int)), this,
+ SLOT(updateSidebarWidth(int)));
+
+ connect(this, SIGNAL(updateRequest(QRect, int)), this,
+ SLOT(updateSidebar(QRect, int)));
+
+ connect(this, SIGNAL(cursorPositionChanged()), this,
+ SLOT(highlightCurrentLine()));
+
+ // add some breakpoints
+ m_breakpoints = QList<int>() << 3 << 5 << 10 << 20;
+
+ updateSidebarWidth(0);
+ highlightCurrentLine();
+}
+
+int CodeEditor::lineNumberAreaWidth() {
+ int digits = 1;
+ int max = qMax(1, blockCount());
+ while (max >= 10) {
+ max /= 10;
+ ++digits;
+ }
+ int space = 3 + fontMetrics().width(QLatin1Char('15')) * digits;
+ return space;
+}
+
+void CodeEditor::updateSidebarWidth(int /* newBlockCount */) {
+ // Set margins of the text edit area
+ setViewportMargins(lineNumberAreaWidth() + m_breakpointArea->width(), 0, 0,
+ 0);
+}
+
+void CodeEditor::updateSidebar(const QRect &rect, int dy) {
+ if (dy) {
+ m_lineNumberArea->scroll(0, dy);
+ m_breakpointArea->scroll(0, dy);
+ } else {
+ m_lineNumberArea->update(0, rect.y(), m_lineNumberArea->width(),
+ rect.height());
+ m_breakpointArea->update(0, rect.y(), m_breakpointArea->width(),
+ rect.height());
+ }
+
+ if (rect.contains(viewport()->rect()))
+ updateSidebarWidth(0);
+}
+
+void CodeEditor::resizeEvent(QResizeEvent *e) {
+ QPlainTextEdit::resizeEvent(e);
+
+ QRect cr = contentsRect();
+ m_lineNumberArea->setGeometry(QRect(cr.left() + m_breakpointArea->width(),
+ cr.top(), lineNumberAreaWidth(),
+ cr.height()));
+
+ m_breakpointArea->setGeometry(cr.left(), cr.top(), m_breakpointArea->width(),
+ cr.height());
+}
+
+void CodeEditor::highlightCurrentLine() {
+ QList<QTextEdit::ExtraSelection> extraSelections;
+
+ if (!isReadOnly()) {
+ QTextEdit::ExtraSelection selection;
+
+ QColor lineColor = QColor(Qt::yellow).lighter(160);
+
+ selection.format.setBackground(lineColor);
+ selection.format.setProperty(QTextFormat::FullWidthSelection, true);
+ selection.cursor = textCursor();
+ selection.cursor.clearSelection();
+ extraSelections.append(selection);
+ }
+
+ setExtraSelections(extraSelections);
+}
+
+void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) {
+ QPainter painter(m_lineNumberArea);
+ painter.fillRect(event->rect(), Qt::lightGray);
+
+ QTextBlock block = firstVisibleBlock();
+ int blockNumber = block.blockNumber();
+ int top = (int)blockBoundingGeometry(block).translated(contentOffset()).top();
+ int bottom = top + (int)blockBoundingRect(block).height();
+
+ while (block.isValid() && top <= event->rect().bottom()) {
+ if (block.isVisible() && bottom >= event->rect().top()) {
+ QString number = QString::number(blockNumber + 1);
+ painter.setPen(Qt::black);
+ painter.drawText(0, top, m_lineNumberArea->width(),
+ fontMetrics().height(), Qt::AlignRight, number);
+ }
+
+ block = block.next();
+ top = bottom;
+ bottom = top + (int)blockBoundingRect(block).height();
+ ++blockNumber;
+ }
+}
+
+void CodeEditor::breakpointAreaPaintEvent(QPaintEvent *event) {
+ QPainter painter(m_breakpointArea);
+ painter.fillRect(event->rect(), Qt::lightGray);
+
+ QTextBlock block = firstVisibleBlock();
+ int blockNumber = block.blockNumber();
+ int top = (int)blockBoundingGeometry(block).translated(contentOffset()).top();
+ int bottom = top + (int)blockBoundingRect(block).height();
+
+ while (block.isValid() && top <= event->rect().bottom()) {
+ if (block.isVisible() && bottom >= event->rect().top()) {
+ if (m_breakpoints.contains(blockNumber)) {
+ painter.drawPixmap(m_breakpointArea->padding, top,
+ m_breakpointArea->imageWidth,
+ m_breakpointArea->imageHeight,
+ m_breakpointArea->m_breakpoint.scaledToHeight(
+ fontMetrics().height() * 0.7));
+ }
+ }
+
+ block = block.next();
+ top = bottom;
+ bottom = top + (int)blockBoundingRect(block).height();
+ ++blockNumber;
+ }
+}
+
+void BreakpointArea::mouseReleaseEvent(QMouseEvent *event) { return; }

ADD
src/codeeditor.h
+#ifndef CODEEDITOR_H
+#define CODEEDITOR_H
+
+#include <QList>
+#include <QPlainTextEdit>
+
+// Extended version of Qt's CodeEditor example
+// http://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html
+
+class LineNumberArea;
+class BreakpointArea;
+
+class CodeEditor : public QPlainTextEdit {
+ Q_OBJECT
+public:
+ CodeEditor(QWidget *parent = 0);
+
+ void lineNumberAreaPaintEvent(QPaintEvent *event);
+ void breakpointAreaPaintEvent(QPaintEvent *event);
+ int lineNumberAreaWidth();
+
+protected:
+ void resizeEvent(QResizeEvent *event) override;
+
+private slots:
+ void updateSidebarWidth(int newBlockCount);
+ void highlightCurrentLine();
+ void updateSidebar(const QRect &, int);
+
+private:
+ LineNumberArea *m_lineNumberArea;
+ BreakpointArea *m_breakpointArea;
+
+ QList<int> m_breakpoints;
+};
+
+// base class for side area widgets that are attached to the code editor
+class SideArea : public QWidget {
+ Q_OBJECT
+public:
+ SideArea(QWidget *parent = nullptr);
+
+private:
+};
+
+class LineNumberArea : public QWidget {
+public:
+ LineNumberArea(CodeEditor *editor) : QWidget(editor) { codeEditor = editor; }
+
+ QSize sizeHint() const override {
+ return QSize(codeEditor->lineNumberAreaWidth(), 0);
+ }
+
+protected:
+ void paintEvent(QPaintEvent *event) override {
+ codeEditor->lineNumberAreaPaintEvent(event);
+ }
+
+private:
+ CodeEditor *codeEditor;
+};
+
+class BreakpointArea : public QWidget {
+public:
+ BreakpointArea(CodeEditor *editor) : QWidget(editor) { codeEditor = editor; }
+
+ QSize sizeHint() const override { return QSize(width(), 0); }
+ int width() const { return imageWidth + padding * 2; }
+ QSize breakpointSize() { return QSize(imageWidth, imageHeight); }
+
+ int imageWidth = 16;
+ int imageHeight = 16;
+ int padding = 3; // padding on each side of the breakpoint
+ QPixmap m_breakpoint =
+ QPixmap(":/logos/breakpoint_enabled.png").scaled(imageWidth, imageHeight);
+ QPixmap m_breakpoint_disabled =
+ QPixmap(":/logos/breakpoint_disabled.png").scaled(imageWidth, imageWidth);
+
+protected:
+ void paintEvent(QPaintEvent *event) override {
+ codeEditor->breakpointAreaPaintEvent(event);
+ }
+
+private:
+ CodeEditor *codeEditor;
+
+ void mouseReleaseEvent(QMouseEvent *event);
+};
+
+#endif // CODEEDITOR_H

MODIFY
src/programfiletab.ui
src/programfiletab.ui
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
- <widget class="QPlainTextEdit" name="assemblyedit">
+ <widget class="CodeEditor" name="assemblyedit">
<property name="tabStopWidth">
<number>20</number>
</property>
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>CodeEditor</class>
+ <extends>QPlainTextEdit</extends>
+ <header>codeeditor.h</header>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections/>
</ui>

Initial commit for code editor