From 1c702357a6617ce2fac1ecb4c4dab7d639a76bbf Mon Sep 17 00:00:00 2001 From: Christiaan Janssen <christiaan.janssen@nokia.com> Date: Wed, 8 Sep 2010 16:44:42 +0200 Subject: [PATCH] QmlInspector: tooltips in qmldebug mode Reviewed by: Lasse Holmstedt --- src/plugins/debugger/qml/qmlengine.cpp | 8 +- src/plugins/debugger/qml/qmlengine.h | 5 + src/plugins/qmljseditor/qmljseditor.h | 2 +- .../qmljsinspector/qmljsclientproxy.cpp | 30 +++++ src/plugins/qmljsinspector/qmljsclientproxy.h | 3 + src/plugins/qmljsinspector/qmljsinspector.cpp | 105 ++++++++++++++++++ src/plugins/qmljsinspector/qmljsinspector.h | 16 +++ .../qmljsinspector/qmljsinspectorplugin.cpp | 11 ++ .../qmljsinspector/qmljslivetextpreview.cpp | 10 +- 9 files changed, 177 insertions(+), 13 deletions(-) diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index d8c2ac69c46..bde16e30dc5 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -30,6 +30,7 @@ #include "qmlengine.h" #include "qmladapter.h" +#include "debuggertooltip.h" #include "debuggerconstants.h" #include "debuggerplugin.h" #include "debuggerdialogs.h" @@ -170,6 +171,7 @@ void QmlEngine::connectionEstablished() ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); pluginManager->addObject(m_adapter); + pluginManager->addObject(this); m_addedAdapterToObjectPool = true; plugin()->showMessage(tr("QML Debugger connected."), StatusBar); @@ -233,6 +235,7 @@ void QmlEngine::shutdownEngineAsSlave() if (m_addedAdapterToObjectPool) { ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); pluginManager->removeObject(m_adapter); + pluginManager->removeObject(this); } if (m_attachToRunningExternalApp) { @@ -445,9 +448,8 @@ static QHash<QString, WatchData> m_toolTipCache; void QmlEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos) { - Q_UNUSED(mousePos) - Q_UNUSED(editor) - Q_UNUSED(cursorPos) + // this is processed by QML inspector, which has deps to qml js editor. Makes life easier. + emit tooltipRequested(mousePos, editor, cursorPos); } ////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 5c0854acf28..31f08d02798 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -46,6 +46,10 @@ #include <projectexplorer/applicationlauncher.h> +namespace Core { + class TextEditor; +} + namespace Debugger { namespace Internal { @@ -120,6 +124,7 @@ private: signals: void sendMessage(const QByteArray &msg); + void tooltipRequested(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos); private slots: void connectionEstablished(); diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index b95cd0d7efd..b70ad1ae889 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -116,7 +116,7 @@ public: // attributes class SemanticHighlighter; -class SemanticInfo +class QMLJSEDITOR_EXPORT SemanticInfo { public: SemanticInfo() {} diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.cpp b/src/plugins/qmljsinspector/qmljsclientproxy.cpp index 9db56a375ae..4ca4a1d4923 100644 --- a/src/plugins/qmljsinspector/qmljsclientproxy.cpp +++ b/src/plugins/qmljsinspector/qmljsclientproxy.cpp @@ -198,6 +198,29 @@ QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId, return QDeclarativeDebugObjectReference(); } +QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(const QString &objectId) const +{ + if (!objectId.isEmpty() && objectId[0].isLower()) { + const QList<QDeclarativeDebugObjectReference> refs = objectReferences(); + foreach (const QDeclarativeDebugObjectReference &ref, refs) { + if (ref.idString() == objectId) + return ref; + } + } + return QDeclarativeDebugObjectReference(); +} + +QDeclarativeDebugObjectReference ClientProxy::objectReferenceForLocation(const int line, const int column) const +{ + const QList<QDeclarativeDebugObjectReference> refs = objectReferences(); + foreach (const QDeclarativeDebugObjectReference &ref, refs) { + if (ref.source().lineNumber() == line && ref.source().columnNumber() == column) + return ref; + } + + return QDeclarativeDebugObjectReference(); +} + QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences() const { QList<QDeclarativeDebugObjectReference> result; @@ -253,6 +276,13 @@ bool ClientProxy::resetBindingForObject(int objectDebugId, const QString& proper return m_client->resetBindingForObject(objectDebugId, propertyName); } +QDeclarativeDebugExpressionQuery *ClientProxy::queryExpressionResult(int objectDebugId, const QString &expr, QObject *parent) +{ + if (objectDebugId != -1) + return m_client->queryExpressionResult(objectDebugId,expr,parent); + return 0; +} + void ClientProxy::clearComponentCache() { if (isDesignClientConnected()) diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.h b/src/plugins/qmljsinspector/qmljsclientproxy.h index ca87e6f7d09..245e04c7160 100644 --- a/src/plugins/qmljsinspector/qmljsclientproxy.h +++ b/src/plugins/qmljsinspector/qmljsclientproxy.h @@ -65,11 +65,14 @@ public: bool setMethodBodyForObject(int objectDebugId, const QString &methodName, const QString &methodBody); bool resetBindingForObject(int objectDebugId, const QString &propertyName); + QDeclarativeDebugExpressionQuery *queryExpressionResult(int objectDebugId, const QString &expr, QObject *parent=0); void clearComponentCache(); // returns the object references QList<QDeclarativeDebugObjectReference> objectReferences() const; QDeclarativeDebugObjectReference objectReferenceForId(int debugId) const; + QDeclarativeDebugObjectReference objectReferenceForId(const QString &objectId) const; + QDeclarativeDebugObjectReference objectReferenceForLocation(const int line, const int column) const; QList<QDeclarativeDebugObjectReference> rootObjectReference() const; DebugIdHash debugIdHash() const { return m_debugIdHash; }; diff --git a/src/plugins/qmljsinspector/qmljsinspector.cpp b/src/plugins/qmljsinspector/qmljsinspector.cpp index 79dbae1e5a7..fc2d5957a1c 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.cpp +++ b/src/plugins/qmljsinspector/qmljsinspector.cpp @@ -38,9 +38,11 @@ #include <qmljseditor/qmljseditorconstants.h> +#include <qmljseditor/qmljseditor.h> #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsdocument.h> +#include <qmljs/parser/qmljsast_p.h> #include <debugger/debuggerrunner.h> #include <debugger/debuggerconstants.h> #include <debugger/debuggerengine.h> @@ -49,6 +51,7 @@ #include <debugger/debuggerrunner.h> #include <debugger/debuggeruiswitcher.h> #include <debugger/debuggerconstants.h> +#include <debugger/qml/qmlengine.h> #include <utils/qtcassert.h> #include <utils/styledbar.h> @@ -100,6 +103,8 @@ #include <QtGui/QMessageBox> #include <QtGui/QTextBlock> +#include <QtGui/QToolTip> +#include <QtGui/QCursor> #include <QtNetwork/QHostAddress> using namespace QmlJS; @@ -131,6 +136,8 @@ InspectorUi::InspectorUi(QObject *parent) , m_inspectorDockWidget(0) , m_settings(new InspectorSettings(this)) , m_clientProxy(0) + , m_qmlEngine(0) + , m_debugQuery(0) , m_debugProject(0) { m_instance = this; @@ -157,6 +164,103 @@ void InspectorUi::restoreSettings() m_settings->restoreSettings(Core::ICore::instance()->settings()); } +void InspectorUi::setDebuggerEngine(Debugger::Internal::QmlEngine *qmlEngine) +{ + if (m_qmlEngine && !qmlEngine) { + disconnect(m_qmlEngine, SIGNAL(tooltipRequested(QPoint, TextEditor::ITextEditor*, int)), + this, SLOT(showDebuggerTooltip(QPoint, TextEditor::ITextEditor*, int))); + } + + m_qmlEngine = qmlEngine; + if (m_qmlEngine) { + connect(m_qmlEngine, SIGNAL(tooltipRequested(QPoint, TextEditor::ITextEditor*, int)), + this, SLOT(showDebuggerTooltip(QPoint, TextEditor::ITextEditor*, int))); + } +} + +Debugger::Internal::QmlEngine *InspectorUi::debuggerEngine() const +{ + return m_qmlEngine; +} + +void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos) +{ + Q_UNUSED(mousePos); + if (editor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID) { + QmlJSEditor::Internal::QmlJSTextEditor *qmlEditor = static_cast<QmlJSEditor::Internal::QmlJSTextEditor*>(editor->widget()); + + QTextCursor tc(qmlEditor->document()); + tc.setPosition(cursorPos); + tc.movePosition(QTextCursor::StartOfWord); + tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + + QString wordAtCursor = tc.selectedText(); + QString query; + QLatin1Char doubleQuote('"'); + + QmlJS::AST::Node *qmlNode = qmlEditor->semanticInfo().nodeUnderCursor(cursorPos); + if (!qmlNode) + return; + QmlJS::AST::Node *node = qmlEditor->semanticInfo().declaringMemberNoProperties(cursorPos); + if (!node) + return; + QDeclarativeDebugObjectReference ref = m_clientProxy->objectReferenceForLocation(node->uiObjectMemberCast()->firstSourceLocation().startLine, node->uiObjectMemberCast()->firstSourceLocation().startColumn); + + if (ref.debugId() == -1) + return; + + if (wordAtCursor == QString("id")) { + query = QString("\"id:") + ref.idString() + doubleQuote; + } else { + if ((qmlNode->kind == QmlJS::AST::Node::Kind_IdentifierExpression) || + (qmlNode->kind == QmlJS::AST::Node::Kind_FieldMemberExpression)) { + tc.setPosition(qmlNode->expressionCast()->firstSourceLocation().begin()); + tc.setPosition(qmlNode->expressionCast()->lastSourceLocation().end(),QTextCursor::KeepAnchor); + QString refToLook = tc.selectedText(); + if ((qmlNode->kind == QmlJS::AST::Node::Kind_IdentifierExpression) && + (m_clientProxy->objectReferenceForId(refToLook).debugId() == -1)) { + query = doubleQuote + QString("local: ") + refToLook + doubleQuote; + foreach(QDeclarativeDebugPropertyReference property, ref.properties()) { + if (property.name() == wordAtCursor && !property.valueTypeName().isEmpty()) { + query = doubleQuote + property.name() + QLatin1Char(':') + doubleQuote + QLatin1Char('+') + property.name(); + break; + } + } + } + else + query =doubleQuote + refToLook + QLatin1Char(':') + doubleQuote + QLatin1Char('+') + refToLook; + } else { + // show properties + foreach(QDeclarativeDebugPropertyReference property, ref.properties()) { + if (property.name() == wordAtCursor && !property.valueTypeName().isEmpty()) { + query = doubleQuote + property.name() + QLatin1Char(':') + doubleQuote + QLatin1Char('+') + property.name(); + break; + } + } + } + } + + if (!query.isEmpty()) { + m_debugQuery = m_clientProxy->queryExpressionResult(ref.debugId(),query); + connect(m_debugQuery,SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),this,SLOT(debugQueryUpdated(QDeclarativeDebugQuery::State))); + } + } +} + +void InspectorUi::debugQueryUpdated(QDeclarativeDebugQuery::State newState) +{ + if (newState != QDeclarativeDebugExpressionQuery::Completed) + return; + if (!m_debugQuery) + return; + + QString text = m_debugQuery->result().toString(); + if (!text.isEmpty()) + QToolTip::showText(QCursor::pos(), text); + + disconnect(m_debugQuery,SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),this,SLOT(debugQueryUpdated(QDeclarativeDebugQuery::State))); +} + void InspectorUi::connected(ClientProxy *clientProxy) { m_clientProxy = clientProxy; @@ -203,6 +307,7 @@ void InspectorUi::disconnected() m_crumblePath, SLOT(updateContextPath(QStringList))); m_debugProject = 0; + m_qmlEngine = 0; resetViews(); setupToolbar(false); diff --git a/src/plugins/qmljsinspector/qmljsinspector.h b/src/plugins/qmljsinspector/qmljsinspector.h index 3c53ce2daad..8f1006fed74 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.h +++ b/src/plugins/qmljsinspector/qmljsinspector.h @@ -47,6 +47,10 @@ namespace ProjectExplorer { class Environment; } +namespace TextEditor { + class ITextEditor; +} + namespace Core { class IContext; } @@ -55,6 +59,12 @@ namespace QmlJS { class ModelManagerInterface; } +namespace Debugger { +namespace Internal { + class QmlEngine; +} +} + QT_FORWARD_DECLARE_CLASS(QDockWidget) namespace QmlJSInspector { @@ -97,6 +107,8 @@ public: void setupUi(); void connected(ClientProxy *clientProxy); void disconnected(); + void setDebuggerEngine(Debugger::Internal::QmlEngine *qmlEngine); + Debugger::Internal::QmlEngine *debuggerEngine() const; signals: void statusMessage(const QString &text); @@ -125,6 +137,8 @@ private slots: void currentDebugProjectRemoved(); void updatePendingPreviewDocuments(QmlJS::Document::Ptr doc); + void showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos); + void debugQueryUpdated(QDeclarativeDebugQuery::State); private: bool addQuotesForData(const QVariant &value) const; @@ -146,6 +160,8 @@ private: InspectorSettings *m_settings; ClientProxy *m_clientProxy; + Debugger::Internal::QmlEngine *m_qmlEngine; + QDeclarativeDebugExpressionQuery *m_debugQuery; // Qml/JS integration QHash<QString, QmlJSLiveTextPreview *> m_textPreviews; diff --git a/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp b/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp index befce483c8f..bff091ab214 100644 --- a/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp +++ b/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp @@ -36,6 +36,7 @@ #include <debugger/debuggeruiswitcher.h> #include <debugger/debuggerconstants.h> #include <debugger/qml/qmladapter.h> +#include <debugger/qml/qmlengine.h> #include <qmlprojectmanager/qmlproject.h> #include <qmljseditor/qmljseditorconstants.h> @@ -130,6 +131,12 @@ void InspectorPlugin::objectAdded(QObject *object) if (adapter) { m_clientProxy = new ClientProxy(adapter); m_inspectorUi->connected(m_clientProxy); + return; + } + + Debugger::Internal::QmlEngine *engine = qobject_cast<Debugger::Internal::QmlEngine*>(object); + if (engine) { + m_inspectorUi->setDebuggerEngine(engine); } } @@ -140,6 +147,10 @@ void InspectorPlugin::aboutToRemoveObject(QObject *obj) delete m_clientProxy; m_clientProxy = 0; } + + if (m_inspectorUi->debuggerEngine() == obj) { + m_inspectorUi->setDebuggerEngine(0); + } } Q_EXPORT_PLUGIN(InspectorPlugin) diff --git a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp index b3682395725..94dbe08ee72 100644 --- a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp +++ b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp @@ -206,15 +206,7 @@ void QmlJSLiveTextPreview::changeSelectedElements(QList<int> offsets, const QStr return; QDeclarativeDebugObjectReference objectRefUnderCursor; - if (!wordAtCursor.isEmpty() && wordAtCursor[0].isLower()) { - QList<QDeclarativeDebugObjectReference> refs = m_clientProxy.data()->objectReferences(); - foreach (const QDeclarativeDebugObjectReference &ref, refs) { - if (ref.idString() == wordAtCursor) { - objectRefUnderCursor = ref; - break; - } - } - } + objectRefUnderCursor = m_clientProxy.data()->objectReferenceForId(wordAtCursor); QList<int> selectedReferences; bool containsReferenceUnderCursor = false; -- GitLab