diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index d8c2ac69c4601073808b53d219774da96366d570..bde16e30dc540fb6edce3bced1c13f1fef503ad8 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 5c0854acf28045f7a4f777215365a76e00eceebd..31f08d027986262721adc5986345b586d2b28efd 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 b95cd0d7efddc6586713831e754c2d25d077ee32..b70ad1ae8895e9dfda4bcd84af25f6667c536245 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 9db56a375ae26ab9ed6ebb62c5ced4c7b316323e..4ca4a1d49233a03d6f51b7e60772c3005b007b82 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 ca87e6f7d09ba9b62e445906c310512190a29447..245e04c7160d7a67c8d2cd4c54afec1ba8b40565 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 79dbae1e5a789e5f151cc6a6f5181d51809e87eb..fc2d5957a1cc84f7a5b060be04f59c16befeb9df 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 3c53ce2daad362002420f1289da5b3a9dec8387f..8f1006fed74c4bcfb1138ea82f76cf0f69bdd55f 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 befce483c8f8aeeb091f8a24f789c5ee3f2079b1..bff091ab21491efc5affa461dce131f4666f6ec2 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 b3682395725d0860ac7332e0edd3a4a0c7edcc33..94dbe08ee725b6a85c71dd70d8da4c6eeff895ee 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;