diff --git a/bin/app/data/gui/code_view/code_view.css b/bin/app/data/gui/code_view/code_view.css index e3420beb..9ca89eb7 100644 --- a/bin/app/data/gui/code_view/code_view.css +++ b/bin/app/data/gui/code_view/code_view.css @@ -27,6 +27,7 @@ font-family: "Source Code Pro"; font-size: 14px; margin-bottom: 2px; + background-color: transparent; } #code_area #line_number_area { diff --git a/src/app/qt/element/QtCodeArea.cpp b/src/app/qt/element/QtCodeArea.cpp index 92f72a17..30b90f5b 100644 --- a/src/app/qt/element/QtCodeArea.cpp +++ b/src/app/qt/element/QtCodeArea.cpp @@ -14,6 +14,7 @@ #include "data/location/TokenLocation.h" #include "data/location/TokenLocationFile.h" #include "qt/element/QtCodeFile.h" +#include "qt/element/QtCodeSnippet.h" #include "qt/utility/QtHighlighter.h" #include "settings/ApplicationSettings.h" @@ -43,14 +44,14 @@ QtCodeArea::QtCodeArea( uint startLineNumber, const std::string& code, std::shared_ptr locationFile, - QtCodeFile* parent + QtCodeFile* file, + QtCodeSnippet* parent ) : QPlainTextEdit(parent) - , m_parent(parent) + , m_fileWidget(file) , m_maximizeButton(nullptr) , m_startLineNumber(startLineNumber) - , m_focusedTokenId(0) - , m_isFocused(false) + , m_hoveredAnnotation(nullptr) , m_digits(0) { setObjectName("code_area"); @@ -160,7 +161,7 @@ void QtCodeArea::updateLineNumberAreaWidthForDigits(int digits) updateLineNumberAreaWidth(0); } -void QtCodeArea::update() +void QtCodeArea::updateContent() { annotateText(); } @@ -181,6 +182,42 @@ void QtCodeArea::showEvent(QShowEvent* event) setMaximumHeight(sizeHint().height()); } +void QtCodeArea::paintEvent(QPaintEvent* event) +{ + QPainter painter(viewport()); + painter.fillRect(rect(), Qt::white); + + QTextBlock block = firstVisibleBlock(); + int top = blockBoundingGeometry(block).translated(contentOffset()).top(); + int blockHeight = blockBoundingRect(block).height(); + + Colori color = ApplicationSettings::getInstance()->getCodeScopeColor(); + Colori colorFocused = ApplicationSettings::getInstance()->getCodeActiveLinkColor(); + colorFocused.a /= 2; + + QColor qColor; + + for (const ScopeAnnotation& scope : m_scopeAnnotations) + { + if (scope.isFocused) + { + qColor.setRgb(colorFocused.r, colorFocused.g, colorFocused.b, colorFocused.a); + } + else + { + qColor.setRgb(color.r, color.g, color.b, color.a); + } + + painter.fillRect( + 0, top + scope.startLine * blockHeight, + width(), (scope.endLine - scope.startLine + 1) * blockHeight, + qColor + ); + } + + QPlainTextEdit::paintEvent(event); +} + void QtCodeArea::enterEvent(QEvent* event) { if (m_maximizeButton) @@ -196,13 +233,7 @@ void QtCodeArea::leaveEvent(QEvent* event) m_maximizeButton->setEnabled(false); } - if(m_isFocused) - { - MessageFocusOut(m_focusedTokenId).dispatch(); - } - m_isFocused = false; - - annotateText(); + setHoveredAnnotation(nullptr); } void QtCodeArea::mouseReleaseEvent(QMouseEvent* event) @@ -235,32 +266,22 @@ void QtCodeArea::mouseMoveEvent(QMouseEvent* event) if (annotation && annotation->isScope) { annotation = nullptr; - MessageFocusOut(m_focusedTokenId).dispatch(); } - if (annotation && annotation->tokenId != m_focusedTokenId) + if (annotation != m_hoveredAnnotation) { - if(m_isFocused) - { - MessageFocusOut(m_focusedTokenId).dispatch(); - } + setHoveredAnnotation(annotation); - m_focusedTokenId = annotation->tokenId; - m_isFocused = true; - MessageFocusIn(m_focusedTokenId).dispatch(); - - const std::vector& errorMessages = m_parent->getErrorMessages(); + const std::vector& errorMessages = m_fileWidget->getErrorMessages(); if (annotation && errorMessages.size() > annotation->tokenId) { - QToolTip::showText(event->globalPos(), QString::fromStdString(m_parent->getErrorMessages()[annotation->tokenId])); + QToolTip::showText(event->globalPos(), QString::fromStdString(m_fileWidget->getErrorMessages()[annotation->tokenId])); } else { QToolTip::hideText(); } - - annotateText(); } } @@ -288,7 +309,7 @@ void QtCodeArea::updateLineNumberArea(const QRect &rect, int dy) void QtCodeArea::clickedMaximizeButton() { - MessageShowFile(m_parent->getFilePath().absoluteStr(), m_startLineNumber, m_startLineNumber + blockCount() - 1).dispatch(); + MessageShowFile(m_fileWidget->getFilePath().absoluteStr(), m_startLineNumber, m_startLineNumber + blockCount() - 1).dispatch(); } void QtCodeArea::clearSelection() @@ -298,6 +319,19 @@ void QtCodeArea::clearSelection() setTextCursor(cursor); } +QtCodeArea::ScopeAnnotation::ScopeAnnotation() + : startLine(0) + , endLine(0) + , tokenId(0) + , isFocused(false) +{ +} + +bool QtCodeArea::ScopeAnnotation::operator!=(const ScopeAnnotation& other) const +{ + return (startLine != other.startLine || endLine != other.endLine || tokenId != other.tokenId || isFocused != other.isFocused); +} + const QtCodeArea::Annotation* QtCodeArea::findAnnotationForPosition(int pos) const { const Annotation* annotationPtr = nullptr; @@ -325,7 +359,7 @@ void QtCodeArea::createAnnotations(std::shared_ptr locationFi [&](TokenLocation* startLocation) { Annotation annotation; - int endLineNumber = m_startLineNumber + blockCount() - 1; + unsigned int endLineNumber = m_startLineNumber + blockCount() - 1; if (startLocation->getLineNumber() <= endLineNumber) { if (startLocation->getLineNumber() < m_startLineNumber) @@ -370,38 +404,29 @@ void QtCodeArea::createAnnotations(std::shared_ptr locationFi void QtCodeArea::annotateText() { Colori color; - const std::vector& ids = m_parent->getActiveTokenIds(); - const std::vector& errorMessages = m_parent->getErrorMessages(); + Id focusedTokenId = m_fileWidget->getFocusedTokenId(); + const std::vector& ids = m_fileWidget->getActiveTokenIds(); + const std::vector& errorMessages = m_fileWidget->getErrorMessages(); QList extraSelections; + std::vector scopeAnnotations; + for (const Annotation& annotation: m_annotations) { bool isActive = std::find(ids.begin(), ids.end(), annotation.tokenId) != ids.end(); + bool isFocused = (annotation.tokenId == focusedTokenId); - if (annotation.tokenId == m_focusedTokenId && errorMessages.size()) + if (&annotation == m_hoveredAnnotation && errorMessages.size()) { color = Colori(255, 0, 0, 128); } - else if(annotation.tokenId == m_focusedTokenId) - { - color = ApplicationSettings::getInstance()->getCodeActiveLinkColor(); - } else if (errorMessages.size()) { color = Colori(255, 0, 0, 255); } - else if (isActive) + else if (isActive || isFocused) { color = ApplicationSettings::getInstance()->getCodeActiveLinkColor(); - - if (annotation.isScope) - { - color.a /= 2; - } - } - else if (annotation.isScope) - { - color = ApplicationSettings::getInstance()->getCodeScopeColor(); } else { @@ -411,15 +436,73 @@ void QtCodeArea::annotateText() QTextEdit::ExtraSelection selection; selection.format.setBackground(QColor(color.r, color.g, color.b, color.a)); + ScopeAnnotation scopeAnnotation; + selection.cursor = textCursor(); selection.cursor.clearSelection(); selection.cursor.setPosition(annotation.start); + scopeAnnotation.startLine = selection.cursor.blockNumber(); selection.cursor.setPosition(annotation.end, QTextCursor::KeepAnchor); + scopeAnnotation.endLine = selection.cursor.blockNumber(); - extraSelections.append(selection); + if (annotation.isScope) + { + if (isActive || isFocused) + { + scopeAnnotation.tokenId = annotation.tokenId; + if (!isActive) + { + scopeAnnotation.isFocused = isFocused; + } + scopeAnnotations.push_back(scopeAnnotation); + } + } + else + { + extraSelections.append(selection); + } } setExtraSelections(extraSelections); + + bool needsUpdate = false; + if (scopeAnnotations.size() != m_scopeAnnotations.size()) + { + needsUpdate = true; + } + else + { + for (size_t i = 0; i < scopeAnnotations.size(); i++) + { + if (scopeAnnotations[i] != m_scopeAnnotations[i]) + { + needsUpdate = true; + break; + } + } + } + + m_scopeAnnotations = scopeAnnotations; + + if (needsUpdate) + { + viewport()->update(); + } +} + +void QtCodeArea::setHoveredAnnotation(const Annotation* annotation) +{ + if (m_hoveredAnnotation) + { + MessageFocusOut(m_hoveredAnnotation->tokenId).dispatch(); + } + + m_hoveredAnnotation = annotation; + + if (annotation) + { + MessageFocusIn(annotation->tokenId).dispatch(); + } } int QtCodeArea::toTextEditPosition(int lineNumber, int columnNumber) const @@ -452,15 +535,3 @@ int QtCodeArea::endTextEditPosition() const return position - 1; } - -void QtCodeArea::focusToken(Id tokenId) -{ - m_focusedTokenId = tokenId; - annotateText(); -} - -void QtCodeArea::defocusToken() -{ - m_focusedTokenId = -1; - annotateText(); -} diff --git a/src/app/qt/element/QtCodeArea.h b/src/app/qt/element/QtCodeArea.h index a9f3ff47..6fe7cc69 100644 --- a/src/app/qt/element/QtCodeArea.h +++ b/src/app/qt/element/QtCodeArea.h @@ -13,6 +13,7 @@ class QPushButton; class QResizeEvent; class QSize; class QtCodeFile; +class QtCodeSnippet; class QtHighlighter; class QWidget; class TokenLocation; @@ -42,7 +43,8 @@ public: uint startLineNumber, const std::string& code, std::shared_ptr locationFile, - QtCodeFile* parent + QtCodeFile* file, + QtCodeSnippet* parent ); virtual ~QtCodeArea(); @@ -55,14 +57,12 @@ public: int lineNumberAreaWidth() const; void updateLineNumberAreaWidthForDigits(int digits); - void update(); - - void focusToken(Id tokenId); - void defocusToken(); + void updateContent(); protected: virtual void resizeEvent(QResizeEvent *event); virtual void showEvent(QShowEvent* event); + virtual void paintEvent(QPaintEvent* event); virtual void enterEvent(QEvent* event); virtual void leaveEvent(QEvent* event); virtual void mouseReleaseEvent(QMouseEvent* event); @@ -85,17 +85,30 @@ private: bool isScope; }; + struct ScopeAnnotation + { + ScopeAnnotation(); + bool operator!=(const ScopeAnnotation& other) const; + + int startLine; + int endLine; + Id tokenId; + bool isFocused; + }; + const Annotation* findAnnotationForPosition(int pos) const; void createAnnotations(std::shared_ptr locationFile); void annotateText(); + void setHoveredAnnotation(const Annotation* annotation); + bool locationBelongsToSnippet(TokenLocation* location) const; int toTextEditPosition(int lineNumber, int columnNumber) const; int startTextEditPosition() const; int endTextEditPosition() const; - QtCodeFile* m_parent; + QtCodeFile* m_fileWidget; QWidget* m_lineNumberArea; QtHighlighter* m_highlighter; @@ -105,9 +118,9 @@ private: const uint m_startLineNumber; std::vector m_annotations; + std::vector m_scopeAnnotations; + const Annotation* m_hoveredAnnotation; - Id m_focusedTokenId; - bool m_isFocused; int m_digits; }; diff --git a/src/app/qt/element/QtCodeFile.cpp b/src/app/qt/element/QtCodeFile.cpp index 35edf3d9..6540e39c 100644 --- a/src/app/qt/element/QtCodeFile.cpp +++ b/src/app/qt/element/QtCodeFile.cpp @@ -49,6 +49,11 @@ std::string QtCodeFile::getFileName() const return m_filePath.fileName(); } +Id QtCodeFile::getFocusedTokenId() const +{ + return m_parent->getFocusedTokenId(); +} + const std::vector& QtCodeFile::getActiveTokenIds() const { return m_parent->getActiveTokenIds(); @@ -88,11 +93,11 @@ void QtCodeFile::addCodeSnippet( } } -void QtCodeFile::update() +void QtCodeFile::updateContent() { for (std::shared_ptr snippet : m_snippets) { - snippet->update(); + snippet->updateContent(); } m_title->setEnabled(m_parent->getShowMaximizeButton()); @@ -102,19 +107,3 @@ void QtCodeFile::clickedTitle() { MessageShowFile(m_filePath.absoluteStr(), 0, 0).dispatch(); } - -void QtCodeFile::focusToken(Id tokenId) -{ - for (std::shared_ptr snippet : m_snippets) - { - snippet->focusToken(tokenId); - } -} - -void QtCodeFile::defocusToken() -{ - for (std::shared_ptr snippet : m_snippets) - { - snippet->defocusToken(); - } -} diff --git a/src/app/qt/element/QtCodeFile.h b/src/app/qt/element/QtCodeFile.h index adda99b2..f388b535 100644 --- a/src/app/qt/element/QtCodeFile.h +++ b/src/app/qt/element/QtCodeFile.h @@ -26,6 +26,7 @@ public: const FilePath& getFilePath() const; std::string getFileName() const; + Id getFocusedTokenId() const; const std::vector& getActiveTokenIds() const; const std::vector& getErrorMessages() const; @@ -36,9 +37,7 @@ public: std::shared_ptr locationFile ); - void update(); - void focusToken(Id tokenId); - void defocusToken(); + void updateContent(); private slots: void clickedTitle(); diff --git a/src/app/qt/element/QtCodeFileList.cpp b/src/app/qt/element/QtCodeFileList.cpp index 9f7fc634..cea53e15 100644 --- a/src/app/qt/element/QtCodeFileList.cpp +++ b/src/app/qt/element/QtCodeFileList.cpp @@ -10,6 +10,7 @@ QtCodeFileList::QtCodeFileList(QWidget* parent) : QScrollArea(parent) , m_showMaximizeButton(true) + , m_focusedTokenId(0) { m_frame = std::make_shared(this); @@ -70,6 +71,11 @@ void QtCodeFileList::clearCodeSnippets() this->verticalScrollBar()->setValue(0); } +Id QtCodeFileList::getFocusedTokenId() const +{ + return m_focusedTokenId; +} + const std::vector& QtCodeFileList::getActiveTokenIds() const { return m_activeTokenIds; @@ -107,22 +113,18 @@ void QtCodeFileList::updateFiles() { for (std::shared_ptr file: m_files) { - file->update(); + file->updateContent(); } } void QtCodeFileList::focusToken(Id tokenId) { - for (std::shared_ptr file: m_files) - { - file->focusToken(tokenId); - } + m_focusedTokenId = tokenId; + updateFiles(); } void QtCodeFileList::defocusToken() { - for (std::shared_ptr file: m_files) - { - file->defocusToken(); - } + m_focusedTokenId = 0; + updateFiles(); } diff --git a/src/app/qt/element/QtCodeFileList.h b/src/app/qt/element/QtCodeFileList.h index c987d3c8..fdaed52c 100644 --- a/src/app/qt/element/QtCodeFileList.h +++ b/src/app/qt/element/QtCodeFileList.h @@ -31,6 +31,8 @@ public: void clearCodeSnippets(); + Id getFocusedTokenId() const; + const std::vector& getActiveTokenIds() const; void setActiveTokenIds(const std::vector& activeTokenIds); @@ -49,6 +51,7 @@ private: std::shared_ptr m_frame; std::vector> m_files; + Id m_focusedTokenId; std::vector m_activeTokenIds; std::vector m_errorMessages; bool m_showMaximizeButton; diff --git a/src/app/qt/element/QtCodeSnippet.cpp b/src/app/qt/element/QtCodeSnippet.cpp index 9bdf5d19..5935c18b 100644 --- a/src/app/qt/element/QtCodeSnippet.cpp +++ b/src/app/qt/element/QtCodeSnippet.cpp @@ -10,11 +10,10 @@ QtCodeSnippet::QtCodeSnippet( const std::string& title, const std::string& code, std::shared_ptr locationFile, - QtCodeFile* parent + QtCodeFile* file ) - : QWidget(parent) - , m_parent(parent) - , m_codeArea(std::make_shared(startLineNumber, code, locationFile, parent)) + : QWidget(file) + , m_codeArea(std::make_shared(startLineNumber, code, locationFile, file, this)) { setObjectName("code_snippet"); @@ -53,17 +52,7 @@ void QtCodeSnippet::updateLineNumberAreaWidthForDigits(int digits) m_codeArea->updateLineNumberAreaWidthForDigits(digits); } -void QtCodeSnippet::update() +void QtCodeSnippet::updateContent() { - m_codeArea->update(); -} - -void QtCodeSnippet::focusToken(Id tokenId) -{ - m_codeArea->focusToken(tokenId); -} - -void QtCodeSnippet::defocusToken() -{ - m_codeArea->defocusToken(); + m_codeArea->updateContent(); } diff --git a/src/app/qt/element/QtCodeSnippet.h b/src/app/qt/element/QtCodeSnippet.h index ee32813a..998d79f9 100644 --- a/src/app/qt/element/QtCodeSnippet.h +++ b/src/app/qt/element/QtCodeSnippet.h @@ -16,13 +16,12 @@ class QtCodeSnippet: public QWidget Q_OBJECT public: - QtCodeSnippet( uint startLineNumber, const std::string& title, const std::string& code, std::shared_ptr locationFile, - QtCodeFile* parent + QtCodeFile* file ); virtual ~QtCodeSnippet(); @@ -31,13 +30,9 @@ public: int lineNumberDigits() const; void updateLineNumberAreaWidthForDigits(int digits); - void update(); - - void focusToken(Id tokenId); - void defocusToken(); + void updateContent(); private: - QtCodeFile* m_parent; // need this? QPushButton* m_title; std::shared_ptr m_codeArea; };