diff --git a/src/libs/timeline/qml/MainView.qml b/src/libs/timeline/qml/MainView.qml index 6b3e171e356..0c475183132 100644 --- a/src/libs/timeline/qml/MainView.qml +++ b/src/libs/timeline/qml/MainView.qml @@ -150,6 +150,11 @@ Rectangle { } } + function selectByIndices(modelIndex, eventIndex) + { + content.select(modelIndex, eventIndex); + } + focus: true property bool shiftPressed: false; Keys.onPressed: shiftPressed = (event.key === Qt.Key_Shift); diff --git a/src/libs/timeline/timelinemodelaggregator.cpp b/src/libs/timeline/timelinemodelaggregator.cpp index a01b70e3f69..cfd97871796 100644 --- a/src/libs/timeline/timelinemodelaggregator.cpp +++ b/src/libs/timeline/timelinemodelaggregator.cpp @@ -118,6 +118,15 @@ int TimelineModelAggregator::modelCount() const return d->modelList.count(); } +int TimelineModelAggregator::modelIndexById(int modelId) const +{ + for (int i = 0; i < d->modelList.count(); ++i) { + if (d->modelList.at(i)->modelId() == modelId) + return i; + } + return -1; +} + QVariantMap TimelineModelAggregator::nextItem(int selectedModel, int selectedItem, qint64 time) const { diff --git a/src/libs/timeline/timelinemodelaggregator.h b/src/libs/timeline/timelinemodelaggregator.h index c7cd12aedc2..325c694cb82 100644 --- a/src/libs/timeline/timelinemodelaggregator.h +++ b/src/libs/timeline/timelinemodelaggregator.h @@ -55,6 +55,7 @@ public: TimelineNotesModel *notes() const; void clear(); int modelCount() const; + int modelIndexById(int modelId) const; Q_INVOKABLE int modelOffset(int modelIndex) const; diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp index a7a0e84ef38..99a89d8532a 100644 --- a/src/plugins/qmlprofiler/qmlprofilertool.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -108,6 +109,12 @@ public: QTime m_recordingElapsedTime; QLabel *m_timeLabel; + // search field + QToolButton *m_searchButton; + QLineEdit *m_searchField; + QTimer *m_searchFieldTimer; + int m_lastSearchResult; + // save and load actions QAction *m_saveQmlTrace; QAction *m_loadQmlTrace; @@ -126,6 +133,10 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent) d->m_featuresMenu = 0; d->m_clearButton = 0; d->m_timeLabel = 0; + d->m_searchButton = 0; + d->m_searchField = 0; + d->m_searchFieldTimer = 0; + d->m_lastSearchResult = -1; d->m_profilerState = new QmlProfilerStateManager(this); connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged())); @@ -285,6 +296,32 @@ QWidget *QmlProfilerTool::createWidgets() updateTimeDisplay(); layout->addWidget(d->m_timeLabel); + d->m_searchButton = new QToolButton; + d->m_searchButton->setIcon(QIcon(QStringLiteral(":/timeline/ico_zoom.png"))); + d->m_searchButton->setToolTip(tr("Toggle the event search field.")); + d->m_searchButton->setCheckable(true); + layout->addWidget(d->m_searchButton); + + d->m_searchField = new QLineEdit; + d->m_searchField->setToolTip(tr("Find events that have a specific note.")); + d->m_searchField->hide(); + layout->addWidget(d->m_searchField); + + connect(d->m_searchButton, &QToolButton::toggled, [this] (bool checked) { + d->m_searchField->setVisible(checked); + if (checked) { + d->m_searchButton->setText(d->m_searchButton->text() + QLatin1Char(':')); + d->m_searchField->setFocus(); + d->m_searchField->selectAll(); + } else { + QString str = d->m_searchButton->text(); + str.chop(1); + d->m_searchButton->setText(str); + } + }); + connect(d->m_searchField, &QLineEdit::returnPressed, this, &QmlProfilerTool::findEvent); + + layout->addStretch(); toolbarWidget->setLayout(layout); // When the widgets are requested we assume that the session data @@ -399,6 +436,45 @@ void QmlProfilerTool::updateTimeDisplay() d->m_timeLabel->setText(tr("Elapsed: %1").arg(profilerTimeStr)); } +void QmlProfilerTool::findEvent() +{ + const QString substr = d->m_searchField->text(); + QmlProfilerNotesModel *model = d->m_profilerModelManager->notesModel(); + + bool found = false; + forever { + for (int i = d->m_lastSearchResult + 1; i < model->count(); ++i) { + if (model->text(i).contains(substr)) { + d->m_lastSearchResult = i; + found = true; + break; + } + } + if (found || d->m_lastSearchResult == -1) + break; + d->m_lastSearchResult = -1; + } + + if (found) { + emit selectTimelineElement(model->timelineModel(d->m_lastSearchResult), + model->timelineIndex(d->m_lastSearchResult)); + d->m_searchField->setFocus(); + } else { + QPalette p = d->m_searchField->palette(); + p.setColor(QPalette::Text, Qt::red); + d->m_searchField->setPalette(p); + if (!d->m_searchFieldTimer) { + d->m_searchFieldTimer = new QTimer(this); + connect(d->m_searchFieldTimer, &QTimer::timeout, [this] () { + d->m_searchField->setPalette(d->m_searchField->parentWidget()->palette()); + }); + } + if (d->m_searchFieldTimer->isActive()) + d->m_searchFieldTimer->stop(); + d->m_searchFieldTimer->start(1500); + } +} + void QmlProfilerTool::clearData() { d->m_profilerModelManager->clear(); diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h index fd9db08423a..3af7881eb87 100644 --- a/src/plugins/qmlprofiler/qmlprofilertool.h +++ b/src/plugins/qmlprofiler/qmlprofilertool.h @@ -65,6 +65,9 @@ public: static void logError(const QString &msg); static void showNonmodalWarning(const QString &warningMsg); +signals: + void selectTimelineElement(int modelId, int eventIndex); + public slots: void profilerStateChanged(); void clientRecordingChanged(); @@ -82,6 +85,7 @@ private slots: void showErrorDialog(const QString &error); void profilerDataModelStateChanged(); void updateTimeDisplay(); + void findEvent(); void showSaveOption(); void showLoadOption(); diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp index 2615cea0666..17e8f810408 100644 --- a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp @@ -123,6 +123,8 @@ QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerT d->m_modelProxy = new Timeline::TimelineModelAggregator(modelManager->notesModel(), this); d->m_modelManager = modelManager; + connect(qobject_cast(profilerTool), &QmlProfilerTool::selectTimelineElement, + this, &QmlProfilerTraceView::selectByEventIndex); connect(modelManager,SIGNAL(dataAvailable()), d->m_modelProxy,SIGNAL(dataAvailable())); // external models pushed on top @@ -218,6 +220,19 @@ void QmlProfilerTraceView::selectBySourceLocation(const QString &filename, int l } } +void QmlProfilerTraceView::selectByEventIndex(int modelId, int eventIndex) +{ + QQuickItem *rootObject = d->m_mainView->rootObject(); + if (!rootObject) + return; + + const int modelIndex = d->m_modelProxy->modelIndexById(modelId); + QTC_ASSERT(modelIndex != -1, return); + QMetaObject::invokeMethod(rootObject, "selectByIndices", + Q_ARG(QVariant, QVariant(modelIndex)), + Q_ARG(QVariant, QVariant(eventIndex))); +} + ///////////////////////////////////////////////////////// // Goto source location void QmlProfilerTraceView::updateCursorPosition() diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.h b/src/plugins/qmlprofiler/qmlprofilertraceview.h index a2a79b1d5bb..e468f410b5b 100644 --- a/src/plugins/qmlprofiler/qmlprofilertraceview.h +++ b/src/plugins/qmlprofiler/qmlprofilertraceview.h @@ -65,6 +65,7 @@ public slots: void clear(); void selectByTypeId(int typeId); void selectBySourceLocation(const QString &filename, int line, int column); + void selectByEventIndex(int modelId, int eventIndex); private slots: void updateCursorPosition();