From df7c5ea9b717fe080521d9d4080378cb240c3858 Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Fri, 13 Nov 2015 10:39:30 +0100 Subject: [PATCH] ui: code view scrolls directly to active line This change makes the code view first look for the rect of the first active line and then tries to scroll this line into the middle of the view. --- src/app/qt/element/QtCodeArea.cpp | 16 +++++ src/app/qt/element/QtCodeArea.h | 2 + src/app/qt/element/QtCodeFile.cpp | 4 +- src/app/qt/element/QtCodeFile.h | 4 +- src/app/qt/element/QtCodeFileList.cpp | 68 +++++-------------- src/app/qt/element/QtCodeFileList.h | 7 +- src/app/qt/element/QtCodeSnippet.cpp | 5 ++ src/app/qt/element/QtCodeSnippet.h | 2 + src/app/qt/view/QtCodeView.cpp | 18 +++-- src/app/qt/view/QtCodeView.h | 8 ++- .../component/controller/CodeController.cpp | 4 +- src/lib/component/view/CodeView.h | 2 +- 12 files changed, 72 insertions(+), 68 deletions(-) diff --git a/src/app/qt/element/QtCodeArea.cpp b/src/app/qt/element/QtCodeArea.cpp index 6241ebb3..bbfc9e1a 100644 --- a/src/app/qt/element/QtCodeArea.cpp +++ b/src/app/qt/element/QtCodeArea.cpp @@ -248,6 +248,22 @@ void QtCodeArea::setIsActiveFile(bool isActiveFile) m_isActiveFile = isActiveFile; } +QRectF QtCodeArea::getFirstActiveLineRect() const +{ + int lineNumber = 0; + for (const Annotation& annotation : m_annotations) + { + if (annotation.isActive) + { + lineNumber = annotation.startLine; + break; + } + } + + QTextBlock block = document()->findBlockByLineNumber(lineNumber - m_startLineNumber); + return blockBoundingGeometry(block); +} + void QtCodeArea::resizeEvent(QResizeEvent *e) { QPlainTextEdit::resizeEvent(e); diff --git a/src/app/qt/element/QtCodeArea.h b/src/app/qt/element/QtCodeArea.h index d95511dc..d425e84a 100644 --- a/src/app/qt/element/QtCodeArea.h +++ b/src/app/qt/element/QtCodeArea.h @@ -83,6 +83,8 @@ public: void setIsActiveFile(bool isActiveFile); + QRectF getFirstActiveLineRect() const; + protected: virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; virtual void showEvent(QShowEvent* event) Q_DECL_OVERRIDE; diff --git a/src/app/qt/element/QtCodeFile.cpp b/src/app/qt/element/QtCodeFile.cpp index 1e6e7ac6..4ce23203 100644 --- a/src/app/qt/element/QtCodeFile.cpp +++ b/src/app/qt/element/QtCodeFile.cpp @@ -179,7 +179,7 @@ void QtCodeFile::addCodeSnippet( updateRefCount(refCount); } -QWidget* QtCodeFile::insertCodeSnippet( +QtCodeSnippet* QtCodeFile::insertCodeSnippet( uint startLineNumber, const std::string& title, Id titleId, @@ -229,7 +229,7 @@ QWidget* QtCodeFile::insertCodeSnippet( return snippet.get(); } -QWidget* QtCodeFile::findFirstActiveSnippet() const +QtCodeSnippet* QtCodeFile::findFirstActiveSnippet() const { if (m_locationFile) { diff --git a/src/app/qt/element/QtCodeFile.h b/src/app/qt/element/QtCodeFile.h index 013e5a56..ee4795d8 100644 --- a/src/app/qt/element/QtCodeFile.h +++ b/src/app/qt/element/QtCodeFile.h @@ -49,7 +49,7 @@ public: int refCount ); - QWidget* insertCodeSnippet( + QtCodeSnippet* insertCodeSnippet( uint startLineNumber, const std::string& title, Id titleId, @@ -58,7 +58,7 @@ public: int refCount ); - QWidget* findFirstActiveSnippet() const; + QtCodeSnippet* findFirstActiveSnippet() const; bool openCollapsedActiveSnippet() const; void updateContent(); diff --git a/src/app/qt/element/QtCodeFileList.cpp b/src/app/qt/element/QtCodeFileList.cpp index 2e2ff24a..c71a7900 100644 --- a/src/app/qt/element/QtCodeFileList.cpp +++ b/src/app/qt/element/QtCodeFileList.cpp @@ -9,6 +9,7 @@ #include "data/location/TokenLocationFile.h" #include "qt/element/QtCodeFile.h" +#include "qt/element/QtCodeSnippet.h" QtCodeFileList::QtCodeFileList(QWidget* parent) : QScrollArea(parent) @@ -27,7 +28,7 @@ QtCodeFileList::QtCodeFileList(QWidget* parent) setWidgetResizable(true); setWidget(m_frame.get()); - connect(this, SIGNAL(shouldScrollToSnippet(QWidget*)), this, SLOT(scrollToSnippet(QWidget*)), Qt::QueuedConnection); + connect(this, SIGNAL(shouldScrollToSnippet(QtCodeSnippet*)), this, SLOT(scrollToSnippet(QtCodeSnippet*)), Qt::QueuedConnection); } QtCodeFileList::~QtCodeFileList() @@ -53,7 +54,7 @@ void QtCodeFileList::addCodeSnippet( if (insert) { - QWidget* snippet = file->insertCodeSnippet(startLineNumber, title, titleId, code, locationFile, refCount); + QtCodeSnippet* snippet = file->insertCodeSnippet(startLineNumber, title, titleId, code, locationFile, refCount); emit shouldScrollToSnippet(snippet); } else @@ -110,18 +111,18 @@ bool QtCodeFileList::scrollToFirstActiveSnippet() { updateFiles(); - QWidget* widget = nullptr; + QtCodeSnippet* snippet = nullptr; for (std::shared_ptr file: m_files) { - widget = file->findFirstActiveSnippet(); - if (widget) + snippet = file->findFirstActiveSnippet(); + if (snippet) { - if (!widget->isVisible()) + if (!snippet->isVisible()) { file->clickedSnippetButton(); } - emit shouldScrollToSnippet(widget); + emit shouldScrollToSnippet(snippet); return true; } } @@ -152,9 +153,9 @@ void QtCodeFileList::defocusTokenIds() updateFiles(); } -void QtCodeFileList::scrollToSnippet(QWidget* widget) +void QtCodeFileList::scrollToSnippet(QtCodeSnippet* snippet) { - this->ensureWidgetVisibleAnimated(widget); + this->ensureWidgetVisibleAnimated(snippet, snippet->getFirstActiveLineRect()); } QtCodeFile* QtCodeFileList::getFile(std::shared_ptr locationFile) @@ -191,7 +192,7 @@ void QtCodeFileList::updateFiles() } } -void QtCodeFileList::ensureWidgetVisibleAnimated(QWidget *childWidget, int xmargin, int ymargin) +void QtCodeFileList::ensureWidgetVisibleAnimated(QWidget *childWidget, QRectF rect) { if (!widget()->isAncestorOf(childWidget)) { @@ -205,54 +206,21 @@ void QtCodeFileList::ensureWidgetVisibleAnimated(QWidget *childWidget, int xmarg : QRect(childWidget->mapTo(widget(), QPoint(0, 0)), childWidget->size()); const QRect visibleRect(-widget()->pos(), viewport()->size()); - if (visibleRect.contains(focusRect)) + if (rect.height() > 0) { - return; + focusRect = QRect(childWidget->mapTo(widget(), rect.topLeft().toPoint()), rect.size().toSize()); + focusRect.adjust(0, 0, 0, 100); } - focusRect.adjust(-xmargin, -ymargin, xmargin, ymargin); + QScrollBar* scrollBar = verticalScrollBar(); + int value = focusRect.center().y() - visibleRect.center().y(); - QScrollBar* scrollBar = nullptr; - int value = 0; - - if (focusRect.width() > visibleRect.width()) - { - scrollBar = horizontalScrollBar(); - value = focusRect.center().x() - viewport()->width() / 2; - } - else if (focusRect.right() > visibleRect.right()) - { - scrollBar = horizontalScrollBar(); - value = focusRect.right() - viewport()->width(); - } - else if (focusRect.left() < visibleRect.left()) - { - scrollBar = horizontalScrollBar(); - value = focusRect.left(); - } - - if (focusRect.height() > visibleRect.height()) - { - scrollBar = verticalScrollBar(); - value = focusRect.center().y() - viewport()->height() / 2; - } - else if (focusRect.bottom() > visibleRect.bottom()) - { - scrollBar = verticalScrollBar(); - value = focusRect.bottom() - viewport()->height(); - } - else if (focusRect.top() < visibleRect.top()) - { - scrollBar = verticalScrollBar(); - value = focusRect.top(); - } - - if (scrollBar) + if (scrollBar && value != 0) { QPropertyAnimation* anim = new QPropertyAnimation(scrollBar, "value"); anim->setDuration(500); anim->setStartValue(scrollBar->value()); - anim->setEndValue(value); + anim->setEndValue(scrollBar->value() + value); anim->setEasingCurve(QEasingCurve::InOutQuad); anim->start(); } diff --git a/src/app/qt/element/QtCodeFileList.h b/src/app/qt/element/QtCodeFileList.h index c68cb5a8..6b035860 100644 --- a/src/app/qt/element/QtCodeFileList.h +++ b/src/app/qt/element/QtCodeFileList.h @@ -11,6 +11,7 @@ #include "utility/types.h" class QtCodeFile; +class QtCodeSnippet; class TokenLocationFile; class QtCodeFileList @@ -19,7 +20,7 @@ class QtCodeFileList Q_OBJECT signals: - void shouldScrollToSnippet(QWidget* widget); + void shouldScrollToSnippet(QtCodeSnippet* widget); public: QtCodeFileList(QWidget* parent = 0); @@ -58,14 +59,14 @@ public: void defocusTokenIds(); private slots: - void scrollToSnippet(QWidget* widget); + void scrollToSnippet(QtCodeSnippet* snippet); private: QtCodeFile* getFile(std::shared_ptr locationFile); void updateFiles(); - void ensureWidgetVisibleAnimated(QWidget *childWidget, int xmargin = 50, int ymargin = 50); + void ensureWidgetVisibleAnimated(QWidget *childWidget, QRectF rect); std::shared_ptr m_frame; std::vector> m_files; diff --git a/src/app/qt/element/QtCodeSnippet.cpp b/src/app/qt/element/QtCodeSnippet.cpp index 3985547d..72f5f418 100644 --- a/src/app/qt/element/QtCodeSnippet.cpp +++ b/src/app/qt/element/QtCodeSnippet.cpp @@ -145,6 +145,11 @@ void QtCodeSnippet::setIsActiveFile(bool isActiveFile) m_codeArea->setIsActiveFile(isActiveFile); } +QRectF QtCodeSnippet::getFirstActiveLineRect() const +{ + return m_codeArea->getFirstActiveLineRect(); +} + void QtCodeSnippet::contextMenuEvent(QContextMenuEvent* event) { QMenu menu(this); diff --git a/src/app/qt/element/QtCodeSnippet.h b/src/app/qt/element/QtCodeSnippet.h index 1762af73..b9697e03 100644 --- a/src/app/qt/element/QtCodeSnippet.h +++ b/src/app/qt/element/QtCodeSnippet.h @@ -44,6 +44,8 @@ public: void setIsActiveFile(bool isActiveFile); + QRectF getFirstActiveLineRect() const; + protected: virtual void contextMenuEvent(QContextMenuEvent* event) Q_DECL_OVERRIDE; diff --git a/src/app/qt/view/QtCodeView.cpp b/src/app/qt/view/QtCodeView.cpp index 0dcdc242..47b671e5 100644 --- a/src/app/qt/view/QtCodeView.cpp +++ b/src/app/qt/view/QtCodeView.cpp @@ -12,11 +12,12 @@ QtCodeView::QtCodeView(ViewLayout* viewLayout) : CodeView(viewLayout) , m_refreshViewFunctor(std::bind(&QtCodeView::doRefreshView, this)) , m_showCodeSnippetsFunctor(std::bind(&QtCodeView::doShowCodeSnippets, this, std::placeholders::_1)) - , m_addCodeSnippetsFunctor(std::bind(&QtCodeView::doAddCodeSnippets, this, std::placeholders::_1)) + , m_addCodeSnippetsFunctor(std::bind(&QtCodeView::doAddCodeSnippets, this, std::placeholders::_1, std::placeholders::_2)) , m_showCodeFileFunctor(std::bind(&QtCodeView::doShowCodeFile, this, std::placeholders::_1)) , m_doShowFirstActiveSnippetFunctor(std::bind(&QtCodeView::doShowFirstActiveSnippet, this)) , m_focusTokenIdsFunctor(std::bind(&QtCodeView::doFocusTokenIds, this, std::placeholders::_1)) , m_defocusTokenIdsFunctor(std::bind(&QtCodeView::doDefocusTokenIds, this)) + , m_isExpanding(false) { m_widget = new QtCodeFileList(); setStyleSheet(); @@ -55,9 +56,9 @@ void QtCodeView::showCodeSnippets(const std::vector& snippets m_showCodeSnippetsFunctor(snippets); } -void QtCodeView::addCodeSnippets(const std::vector& snippets) +void QtCodeView::addCodeSnippets(const std::vector& snippets, bool insert) { - m_addCodeSnippetsFunctor(snippets); + m_addCodeSnippetsFunctor(snippets, insert); } void QtCodeView::showCodeFile(const CodeSnippetParams& params) @@ -117,7 +118,7 @@ void QtCodeView::doShowCodeSnippets(const std::vector& snippe setStyleSheet(); // so property "isLast" of QtCodeSnippet is computed correctly } -void QtCodeView::doAddCodeSnippets(const std::vector& snippets) +void QtCodeView::doAddCodeSnippets(const std::vector& snippets, bool insert) { for (const CodeSnippetParams& params : snippets) { @@ -129,11 +130,17 @@ void QtCodeView::doAddCodeSnippets(const std::vector& snippet params.locationFile, params.refCount, params.modificationTime, - true + insert ); } setStyleSheet(); // so property "isLast" of QtCodeSnippet is computed correctly + + if (m_isExpanding) + { + m_widget->scrollToFirstActiveSnippet(); + m_isExpanding = false; + } } void QtCodeView::doShowCodeFile(const CodeSnippetParams& params) @@ -148,6 +155,7 @@ void QtCodeView::doShowFirstActiveSnippet() if (!m_widget->scrollToFirstActiveSnippet()) { m_widget->expandActiveSnippetFile(); + m_isExpanding = true; } } diff --git a/src/app/qt/view/QtCodeView.h b/src/app/qt/view/QtCodeView.h index 4f343637..2fe022f7 100644 --- a/src/app/qt/view/QtCodeView.h +++ b/src/app/qt/view/QtCodeView.h @@ -30,7 +30,7 @@ public: virtual void setErrorMessages(const std::vector& errorMessages); virtual void showCodeSnippets(const std::vector& snippets); - virtual void addCodeSnippets(const std::vector& snippets); + virtual void addCodeSnippets(const std::vector& snippets, bool insert); virtual void showCodeFile(const CodeSnippetParams& params); virtual void showFirstActiveSnippet(); @@ -42,7 +42,7 @@ private: void doRefreshView(); void doShowCodeSnippets(const std::vector& snippets); - void doAddCodeSnippets(const std::vector& snippets); + void doAddCodeSnippets(const std::vector& snippets, bool insert); void doShowCodeFile(const CodeSnippetParams& params); void doShowFirstActiveSnippet(); @@ -54,7 +54,7 @@ private: QtThreadedFunctor<> m_refreshViewFunctor; QtThreadedFunctor&> m_showCodeSnippetsFunctor; - QtThreadedFunctor&> m_addCodeSnippetsFunctor; + QtThreadedFunctor&, bool> m_addCodeSnippetsFunctor; QtThreadedFunctor m_showCodeFileFunctor; QtThreadedFunctor<> m_doShowFirstActiveSnippetFunctor; QtThreadedFunctor&> m_focusTokenIdsFunctor; @@ -64,6 +64,8 @@ private: std::vector m_activeTokenIds; std::vector m_errorMessages; + + bool m_isExpanding; }; # endif // QT_CODE_VIEW_H diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index f3ad6981..31e0055b 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -155,12 +155,12 @@ void CodeController::handleMessage(MessageShowScope* message) return; } - getView()->addCodeSnippets(snippets); + getView()->addCodeSnippets(snippets, true); } void CodeController::handleMessage(MessageShowSnippets* message) { - getView()->addCodeSnippets(getSnippetsForActiveTokenLocationsInFile(message->locationFile)); + getView()->addCodeSnippets(getSnippetsForActiveTokenLocationsInFile(message->locationFile), false); } CodeView* CodeController::getView() diff --git a/src/lib/component/view/CodeView.h b/src/lib/component/view/CodeView.h index 98e85acb..721037c3 100644 --- a/src/lib/component/view/CodeView.h +++ b/src/lib/component/view/CodeView.h @@ -47,7 +47,7 @@ public: virtual void setErrorMessages(const std::vector& errorMessages) = 0; virtual void showCodeSnippets(const std::vector& snippets) = 0; - virtual void addCodeSnippets(const std::vector& snippets) = 0; + virtual void addCodeSnippets(const std::vector& snippets, bool insert) = 0; virtual void showCodeFile(const CodeSnippetParams& params) = 0; virtual void showFirstActiveSnippet() = 0;