diff options
author | Christiaan Janssen <christiaan.janssen@nokia.com> | 2011-01-18 17:17:17 +0100 |
---|---|---|
committer | Christiaan Janssen <christiaan.janssen@nokia.com> | 2011-01-20 15:11:39 +0100 |
commit | f5039a4a022a31cc4c1330d35350ae39cf11b5dd (patch) | |
tree | 896474a7d5413705a93042d8347add6acc939f84 /src/plugins | |
parent | 6a829f5a8fa272bfb4bef92cf40d6d024756be69 (diff) | |
download | qt-creator-f5039a4a022a31cc4c1330d35350ae39cf11b5dd.tar.gz |
QmlJsInspector: implemented Property Inspector
Reviewed-by: Kai Koehne
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/qmljseditor/qmljseditor.cpp | 18 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmlinspectortoolbar.cpp | 9 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmlinspectortoolbar.h | 4 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljsclientproxy.cpp | 70 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljsclientproxy.h | 8 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp | 21 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljscontextcrumblepath.h | 4 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljsinspector.cpp | 268 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljsinspector.h | 15 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljsinspector.pro | 4 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljsobjecttree.cpp | 242 | ||||
-rw-r--r-- | src/plugins/qmljsinspector/qmljsobjecttree.h | 86 |
12 files changed, 336 insertions, 413 deletions
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index 4037994470..0eac867b7e 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -1054,22 +1054,6 @@ protected: return 0; } - inline bool hasVisualPresentation(Node *ast) - { - Bind *bind = m_lookupContext->document()->bind(); - const Interpreter::ObjectValue *objValue = bind->findQmlObject(ast); - if (!objValue) - return false; - - QStringList prototypes; - foreach (const Interpreter::ObjectValue *value, - Interpreter::PrototypeIterator(objValue, m_lookupContext->context()).all()) { - prototypes.append(value->className()); - } - - return prototypes.contains(QString("QGraphicsObject")); - } - inline bool isIdBinding(UiObjectMember *member) const { if (UiScriptBinding *script = cast<UiScriptBinding *>(member)) { @@ -1116,7 +1100,7 @@ protected: if ((isRangeSelected() && intersectsCursor(begin, end)) || (!isRangeSelected() && containsCursor(begin, end))) { - if (initializer(member) && isSelectable(member) && hasVisualPresentation(member)) { + if (initializer(member) && isSelectable(member)) { m_selectedMembers << member; // move start towards end; this facilitates multiselection so that root is usually ignored. m_cursorPositionStart = qMin(end, m_cursorPositionEnd); diff --git a/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp b/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp index c0d3b42e6a..9006f0f09e 100644 --- a/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp +++ b/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp @@ -43,13 +43,13 @@ #include <projectexplorer/projectexplorerconstants.h> #include <utils/styledbar.h> -#include <utils/filterlineedit.h> #include <QAction> #include <QActionGroup> #include <QHBoxLayout> #include <QMenu> #include <QToolButton> +#include <QLineEdit> namespace QmlJSInspector { namespace Internal { @@ -78,6 +78,7 @@ QmlInspectorToolbar::QmlInspectorToolbar(QObject *parent) : m_menuPauseAction(0), m_playIcon(QIcon(QLatin1String(":/qml/images/play-small.png"))), m_pauseIcon(QIcon(QLatin1String(":/qml/images/pause-small.png"))), + m_filterExp(0), m_colorBox(0), m_emitSignals(true), m_isRunning(false), @@ -98,6 +99,7 @@ void QmlInspectorToolbar::setEnabled(bool value) m_zoomAction->setEnabled(value); m_colorPickerAction->setEnabled(value); m_colorBox->setEnabled(value); + m_filterExp->setEnabled(value); } void QmlInspectorToolbar::enable() @@ -295,6 +297,10 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) m_colorBox->setInnerBorderColor(QColor(192,192,192)); m_colorBox->setOuterBorderColor(QColor(58,58,58)); configBarLayout->addWidget(m_colorBox); + + m_filterExp = new QLineEdit(m_barWidget); + m_filterExp->setPlaceholderText("<filter property list>"); + configBarLayout->addWidget(m_filterExp); configBarLayout->addStretch(); setEnabled(false); @@ -307,6 +313,7 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) connect(m_selectAction, SIGNAL(triggered()), SLOT(activateSelectToolOnClick())); connect(m_zoomAction, SIGNAL(triggered()), SLOT(activateZoomOnClick())); connect(m_colorPickerAction, SIGNAL(triggered()), SLOT(activateColorPickerOnClick())); + connect(m_filterExp, SIGNAL(textChanged(QString)), SIGNAL(filterTextChanged(QString))); } QWidget *QmlInspectorToolbar::widget() const diff --git a/src/plugins/qmljsinspector/qmlinspectortoolbar.h b/src/plugins/qmljsinspector/qmlinspectortoolbar.h index 990b60f682..6cc78e9b27 100644 --- a/src/plugins/qmljsinspector/qmlinspectortoolbar.h +++ b/src/plugins/qmljsinspector/qmlinspectortoolbar.h @@ -40,6 +40,7 @@ QT_FORWARD_DECLARE_CLASS(QAction) QT_FORWARD_DECLARE_CLASS(QColor) QT_FORWARD_DECLARE_CLASS(QToolButton) +QT_FORWARD_DECLARE_CLASS(QLineEdit) namespace Core { class Context; @@ -98,6 +99,7 @@ signals: void showAppOnTopSelected(bool isChecked); void animationSpeedChanged(qreal slowdownFactor = 1.0f); + void filterTextChanged(const QString &); private slots: void activateDesignModeOnClick(); @@ -140,6 +142,8 @@ private: QIcon m_playIcon; QIcon m_pauseIcon; + QLineEdit *m_filterExp; + ToolBarColorBox *m_colorBox; bool m_emitSignals; diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.cpp b/src/plugins/qmljsinspector/qmljsclientproxy.cpp index 58fb50852f..29c5b07817 100644 --- a/src/plugins/qmljsinspector/qmljsclientproxy.cpp +++ b/src/plugins/qmljsinspector/qmljsclientproxy.cpp @@ -157,6 +157,8 @@ void ClientProxy::disconnectFromServer() qDeleteAll(m_objectTreeQuery); m_objectTreeQuery.clear(); + removeAllObjectWatches(); + updateConnected(); } @@ -226,7 +228,7 @@ QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId, if (objectRef.debugId() == debugId) return objectRef; - foreach(const QDeclarativeDebugObjectReference &child, objectRef.children()) { + foreach (const QDeclarativeDebugObjectReference &child, objectRef.children()) { QDeclarativeDebugObjectReference result = objectReferenceForId(debugId, child); if (result.debugId() == debugId) return result; @@ -261,7 +263,7 @@ QDeclarativeDebugObjectReference ClientProxy::objectReferenceForLocation(const i QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences() const { QList<QDeclarativeDebugObjectReference> result; - foreach(const QDeclarativeDebugObjectReference &it, m_rootObjects) { + foreach (const QDeclarativeDebugObjectReference &it, m_rootObjects) { result.append(objectReferences(it)); } return result; @@ -272,7 +274,7 @@ QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences(const QDec QList<QDeclarativeDebugObjectReference> result; result.append(objectRef); - foreach(const QDeclarativeDebugObjectReference &child, objectRef.children()) { + foreach (const QDeclarativeDebugObjectReference &child, objectRef.children()) { result.append(objectReferences(child)); } @@ -331,6 +333,64 @@ void ClientProxy::clearComponentCache() m_observerClient->clearComponentCache(); } +bool ClientProxy::addObjectWatch(int objectDebugId) +{ + if (debug) + qDebug() << "addObjectWatch():" << objectDebugId; + if (objectDebugId == -1) + return false; + + // already set + if (m_objectWatches.keys().contains(objectDebugId)) + return true; + + QDeclarativeDebugObjectReference ref = objectReferenceForId(objectDebugId); + if (ref.debugId() != objectDebugId) + return false; + + QDeclarativeDebugWatch *watch = m_engineClient->addWatch(ref, this); + m_objectWatches.insert(objectDebugId, watch); + + connect(watch,SIGNAL(valueChanged(QByteArray,QVariant)),this,SLOT(objectWatchTriggered(QByteArray,QVariant))); + + return false; +} + +void ClientProxy::objectWatchTriggered(const QByteArray &propertyName, const QVariant &propertyValue) +{ + QDeclarativeDebugWatch *watch = dynamic_cast<QDeclarativeDebugWatch *>(QObject::sender()); + if (watch) + emit propertyChanged(watch->objectDebugId(),propertyName, propertyValue); + +} + +bool ClientProxy::removeObjectWatch(int objectDebugId) +{ + if (debug) + qDebug() << "removeObjectWatch():" << objectDebugId; + if (objectDebugId == -1) + return false; + + if (!m_objectWatches.keys().contains(objectDebugId)) + return false; + + QDeclarativeDebugWatch *watch = m_objectWatches.value(objectDebugId); + disconnect(watch,SIGNAL(valueChanged(QByteArray,QVariant)), this, SLOT(objectWatchTriggered(QByteArray,QVariant))); + m_engineClient->removeWatch(watch); + delete watch; + m_objectWatches.remove(objectDebugId); + + + return true; +} + +void ClientProxy::removeAllObjectWatches() +{ + foreach (int watchedObject, m_objectWatches.keys()) + removeObjectWatch(watchedObject); + Q_ASSERT(m_objectWatches.count() == 0); +} + void ClientProxy::queryEngineContext(int id) { if (id < 0) @@ -403,7 +463,7 @@ void ClientProxy::objectTreeFetched(QDeclarativeDebugQuery::State state) int old_count = m_debugIdHash.count(); m_debugIdHash.clear(); m_debugIdHash.reserve(old_count + 1); - foreach(const QDeclarativeDebugObjectReference &it, m_rootObjects) + foreach (const QDeclarativeDebugObjectReference &it, m_rootObjects) buildDebugIdHashRecursive(it); emit objectTreeUpdated(); @@ -445,7 +505,7 @@ void ClientProxy::buildDebugIdHashRecursive(const QDeclarativeDebugObjectReferen // append the debug ids in the hash m_debugIdHash[qMakePair<QString, int>(filename, rev)][qMakePair<int, int>(lineNum, colNum)].append(ref.debugId()); - foreach(const QDeclarativeDebugObjectReference &it, ref.children()) + foreach (const QDeclarativeDebugObjectReference &it, ref.children()) buildDebugIdHashRecursive(it); } diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.h b/src/plugins/qmljsinspector/qmljsclientproxy.h index c88cc839f3..ac97539863 100644 --- a/src/plugins/qmljsinspector/qmljsclientproxy.h +++ b/src/plugins/qmljsinspector/qmljsclientproxy.h @@ -70,6 +70,10 @@ public: QDeclarativeDebugExpressionQuery *queryExpressionResult(int objectDebugId, const QString &expr, QObject *parent=0); void clearComponentCache(); + bool addObjectWatch(int objectDebugId); + bool removeObjectWatch(int objectDebugId); + void removeAllObjectWatches(); + // returns the object references QList<QDeclarativeDebugObjectReference> objectReferences() const; QDeclarativeDebugObjectReference objectReferenceForId(int debugId) const; @@ -108,6 +112,7 @@ signals: void serverReloaded(); void selectedColorChanged(const QColor &color); void contextPathUpdated(const QStringList &contextPath); + void propertyChanged(int debugId, const QByteArray &propertyName, const QVariant &propertyValue); public slots: void refreshObjectTree(); @@ -139,6 +144,7 @@ private slots: void objectTreeFetched(QDeclarativeDebugQuery::State state = QDeclarativeDebugQuery::Completed); void fetchContextObjectRecursive(const QmlJsDebugClient::QDeclarativeDebugContextReference& context); void newObjects(); + void objectWatchTriggered(const QByteArray &propertyName, const QVariant &propertyValue); private: void updateConnected(); @@ -164,6 +170,8 @@ private: QTimer m_requestObjectsTimer; DebugIdHash m_debugIdHash; + QHash<int, QDeclarativeDebugWatch *> m_objectWatches; + bool m_isConnected; }; diff --git a/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp b/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp index bd4c4fec4c..8ea623cb3a 100644 --- a/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp +++ b/src/plugins/qmljsinspector/qmljscontextcrumblepath.cpp @@ -42,7 +42,7 @@ ContextCrumblePath::ContextCrumblePath(QWidget *parent) : CrumblePath(parent), m_isEmpty(true) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - updateContextPath(QStringList()); + updateContextPath(QStringList(),QList<int>()); } ContextCrumblePath::~ContextCrumblePath() @@ -50,11 +50,14 @@ ContextCrumblePath::~ContextCrumblePath() } -void ContextCrumblePath::updateContextPath(const QStringList &path) +void ContextCrumblePath::updateContextPath(const QStringList &path, const QList<int> &debugIds) { + Q_ASSERT(path.count() == debugIds.count()); + clear(); - foreach(const QString &pathPart, path) { - pushElement(pathPart); + + for (int i=0; i<path.count(); i++) { + pushElement(path[i],QVariant(debugIds[i])); } m_isEmpty = path.isEmpty(); @@ -63,10 +66,20 @@ void ContextCrumblePath::updateContextPath(const QStringList &path) } } +void ContextCrumblePath::selectIndex(int index) +{ + CrumblePath::selectIndex(index); +} + bool ContextCrumblePath::isEmpty() const { return m_isEmpty; } +int ContextCrumblePath::debugIdForIndex(int index) const +{ + return CrumblePath::dataForIndex(index).toInt(); +} + } // namespace Internal } // namespace QmlJSInspector diff --git a/src/plugins/qmljsinspector/qmljscontextcrumblepath.h b/src/plugins/qmljsinspector/qmljscontextcrumblepath.h index b1c38ba3f4..2349cb4f97 100644 --- a/src/plugins/qmljsinspector/qmljscontextcrumblepath.h +++ b/src/plugins/qmljsinspector/qmljscontextcrumblepath.h @@ -46,9 +46,11 @@ public: ContextCrumblePath(QWidget *parent = 0); virtual ~ContextCrumblePath(); bool isEmpty() const; + int debugIdForIndex(int index) const; public slots: - void updateContextPath(const QStringList &path); + void updateContextPath(const QStringList &path, const QList<int> &debugIds); + void selectIndex(int index); private: bool m_isEmpty; }; diff --git a/src/plugins/qmljsinspector/qmljsinspector.cpp b/src/plugins/qmljsinspector/qmljsinspector.cpp index f651a596cb..03367a4c34 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.cpp +++ b/src/plugins/qmljsinspector/qmljsinspector.cpp @@ -39,7 +39,7 @@ #include "qmljsprivateapi.h" #include "qmljscontextcrumblepath.h" #include "qmljsinspectorsettings.h" -#include "qmljsobjecttree.h" +#include "qmljspropertyinspector.h" #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsdocument.h> @@ -126,13 +126,13 @@ InspectorUi::InspectorUi(QObject *parent) , m_listeningToEditorManager(false) , m_toolbar(0) , m_crumblePath(0) - , m_objectTreeWidget(0) + , m_propertyInspector(0) , m_settings(new InspectorSettings(this)) , m_clientProxy(0) , m_qmlEngine(0) , m_debugQuery(0) - , m_lastSelectedDebugId(-1) , m_debugProject(0) + , m_selectionCallbackExpected(false) { m_instance = this; m_toolbar = new QmlInspectorToolbar(this); @@ -225,7 +225,7 @@ void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextE 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()) { + foreach (QDeclarativeDebugPropertyReference property, ref.properties()) { if (property.name() == wordAtCursor && !property.valueTypeName().isEmpty()) { query = doubleQuote + property.name() + QLatin1Char(':') @@ -239,7 +239,7 @@ void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextE + QLatin1Char('+') + refToLook; } else { // show properties - foreach(QDeclarativeDebugPropertyReference property, ref.properties()) { + foreach (QDeclarativeDebugPropertyReference property, ref.properties()) { if (property.name() == wordAtCursor && !property.valueTypeName().isEmpty()) { query = doubleQuote + property.name() + QLatin1Char(':') + doubleQuote + QLatin1Char('+') + property.name(); @@ -250,7 +250,7 @@ void InspectorUi::showDebuggerTooltip(const QPoint &mousePos, TextEditor::ITextE } if (!query.isEmpty()) { - m_debugQuery = m_clientProxy->queryExpressionResult(ref.debugId(),query); + m_debugQuery = m_clientProxy->queryExpressionResult(ref.debugId(),query, this); connect(m_debugQuery, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)), this, SLOT(debugQueryUpdated(QDeclarativeDebugQuery::State))); } @@ -281,13 +281,23 @@ void InspectorUi::connected(ClientProxy *clientProxy) { m_clientProxy = clientProxy; + QmlJS::Snapshot snapshot = modelManager()->snapshot(); + for (QHash<QString, QmlJSLiveTextPreview *>::const_iterator it = m_textPreviews.constBegin(); + it != m_textPreviews.constEnd(); ++it) { + Document::Ptr doc = snapshot.document(it.key()); + it.value()->resetInitialDoc(doc); + } + connect(m_clientProxy, SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)), - SLOT(gotoObjectReferenceDefinition(QList<QDeclarativeDebugObjectReference>))); + SLOT(selectItems(QList<QDeclarativeDebugObjectReference>))); connect(m_clientProxy, SIGNAL(enginesChanged()), SLOT(updateEngineList())); connect(m_clientProxy, SIGNAL(serverReloaded()), this, SLOT(serverReloaded())); - connect(m_clientProxy, SIGNAL(contextPathUpdated(QStringList)), - m_crumblePath, SLOT(updateContextPath(QStringList))); + connect(m_clientProxy, SIGNAL(propertyChanged(int, QByteArray,QVariant)), + m_propertyInspector, SLOT(propertyValueChanged(int, QByteArray,QVariant))); + connect(m_propertyInspector, SIGNAL(changePropertyValue(int,QString,QString)), + this, SLOT(changePropertyValue(int,QString,QString))); + connect(m_clientProxy,SIGNAL(objectTreeUpdated()),this,SLOT(objectTreeReady())); m_debugProject = ProjectExplorer::ProjectExplorerPlugin::instance()->startupProject(); if (m_debugProject->activeTarget() @@ -307,22 +317,34 @@ void InspectorUi::connected(ClientProxy *clientProxy) initializeDocuments(); QHashIterator<QString, QmlJSLiveTextPreview *> iter(m_textPreviews); - while(iter.hasNext()) { + while (iter.hasNext()) { iter.next(); iter.value()->setClientProxy(m_clientProxy); iter.value()->updateDebugIds(); } + + +} + +void InspectorUi::objectTreeReady() +{ + // Should only run once, after debugger startup + if (!m_clientProxy->rootObjectReference().isEmpty()) { + selectItems(m_clientProxy->rootObjectReference()); + disconnect(m_clientProxy,SIGNAL(objectTreeUpdated()),this,SLOT(objectTreeReady())); + } } void InspectorUi::disconnected() { disconnect(m_clientProxy, SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)), - this, SLOT(gotoObjectReferenceDefinition(QList<QDeclarativeDebugObjectReference>))); - + this, SLOT(selectItems(QList<QDeclarativeDebugObjectReference>))); disconnect(m_clientProxy, SIGNAL(enginesChanged()), this, SLOT(updateEngineList())); disconnect(m_clientProxy, SIGNAL(serverReloaded()), this, SLOT(serverReloaded())); - disconnect(m_clientProxy, SIGNAL(contextPathUpdated(QStringList)), - m_crumblePath, SLOT(updateContextPath(QStringList))); + disconnect(m_clientProxy, SIGNAL(propertyChanged(int, QByteArray,QVariant)), + m_propertyInspector, SLOT(propertyValueChanged(int, QByteArray,QVariant))); + disconnect(m_propertyInspector, SIGNAL(changePropertyValue(int,QString,QString)), + this, SLOT(changePropertyValue(int,QString,QString))); m_debugProject = 0; m_qmlEngine = 0; @@ -332,12 +354,12 @@ void InspectorUi::disconnected() applyChangesToQmlObserverHelper(false); QHashIterator<QString, QmlJSLiveTextPreview *> iter(m_textPreviews); - while(iter.hasNext()) { + while (iter.hasNext()) { iter.next(); iter.value()->setClientProxy(0); } m_clientProxy = 0; - m_objectTreeWidget->clear(); + m_propertyInspector->clear(); m_pendingPreviewDocumentNames.clear(); } @@ -357,19 +379,20 @@ void InspectorUi::updateEngineList() void InspectorUi::changeSelectedItems(const QList<QDeclarativeDebugObjectReference> &objects) { - if (m_lastSelectedDebugId >= 0) { - foreach (const QDeclarativeDebugObjectReference &ref, objects) { - if (ref.debugId() == m_lastSelectedDebugId) { - // this is only the 'call back' after we have programatically set a new cursor - // position in - m_lastSelectedDebugId = -1; - return; - } - } - m_lastSelectedDebugId = -1; + if (m_selectionCallbackExpected) { + m_selectionCallbackExpected = false; + return; + } + + // QmlJSLiveTextPreview doesn't provide valid references, only correct debugIds. We need to remap them + QList <QDeclarativeDebugObjectReference> realList; + foreach (const QDeclarativeDebugObjectReference &obj, objects) { + QDeclarativeDebugObjectReference clientRef = m_clientProxy->objectReferenceForId(obj.debugId()); + realList << clientRef; } - m_clientProxy->setSelectedItemsByObjectId(objects); + m_clientProxy->setSelectedItemsByObjectId(realList); + selectItems(realList); } void InspectorUi::initializeDocuments() @@ -474,7 +497,8 @@ void InspectorUi::currentDebugProjectRemoved() void InspectorUi::resetViews() { - m_crumblePath->updateContextPath(QStringList()); + m_propertyInspector->clear(); + m_crumblePath->clear(); } void InspectorUi::reloadQmlViewer() @@ -483,31 +507,170 @@ void InspectorUi::reloadQmlViewer() m_clientProxy->reloadQmlViewer(); } -void InspectorUi::gotoObjectReferenceDefinition(QList<QDeclarativeDebugObjectReference> - objectReferences) +inline QDeclarativeDebugObjectReference findParentRecursive( int goalDebugId, + const QList< QDeclarativeDebugObjectReference > &objectsToSearch) +{ + if (goalDebugId == -1) + return QDeclarativeDebugObjectReference(); + + foreach (const QDeclarativeDebugObjectReference &possibleParent, objectsToSearch) { + // Am I a root object? No parent + if ( possibleParent.debugId() == goalDebugId ) + return QDeclarativeDebugObjectReference(); + + // Is the goal one of my children? + foreach (const QDeclarativeDebugObjectReference &child, possibleParent.children()) + if ( child.debugId() == goalDebugId ) + return possibleParent; + + // no luck? pass this on + QDeclarativeDebugObjectReference candidate = findParentRecursive(goalDebugId, possibleParent.children()); + if (candidate.debugId() != -1) + return candidate; + } + + return QDeclarativeDebugObjectReference(); +} + +void InspectorUi::selectItems(const QList<QDeclarativeDebugObjectReference> &objectReferences) { - if (objectReferences.length()) - gotoObjectReferenceDefinition(objectReferences.first()); + foreach (const QDeclarativeDebugObjectReference &objref, objectReferences) + if (objref.debugId() != -1) { + // select only the first valid element of the list + + m_clientProxy->removeAllObjectWatches(); + m_clientProxy->addObjectWatch(objref.debugId()); + QList <QDeclarativeDebugObjectReference> selectionList; + selectionList << objref; + m_propertyInspector->setCurrentObjects(selectionList); + populateCrumblePath(objref); + gotoObjectReferenceDefinition(objref); + return; + } + + // empty list + m_propertyInspector->clear(); + m_crumblePath->clear(); +} + +inline QString displayName(const QDeclarativeDebugObjectReference &obj) +{ + // special! state names + if (obj.className() == "State") { + foreach (const QDeclarativeDebugPropertyReference &prop, obj.properties()) { + if (prop.name() == "name") + return prop.value().toString(); + } + } + + // has id? + if (!obj.idString().isEmpty()) + return obj.idString(); + + // return the simplified class name then + QString objTypeName = obj.className(); + QString declarativeString("QDeclarative"); + if (objTypeName.startsWith(declarativeString)) { + objTypeName = objTypeName.mid(declarativeString.length()).section('_',0,0); + } + return QString("<%1>").arg(objTypeName); +} + +bool InspectorUi::isRoot(const QDeclarativeDebugObjectReference &obj) const +{ + foreach (const QDeclarativeDebugObjectReference &rootObj, m_clientProxy->rootObjectReference()) + if (obj.debugId() == rootObj.debugId()) + return true; + return false; +} + +void InspectorUi::populateCrumblePath(const QDeclarativeDebugObjectReference &objRef) +{ + QStringList crumbleStrings; + QList <int> crumbleData; + + // first find path by climbing the hierarchy + QDeclarativeDebugObjectReference ref = objRef; + crumbleData << objRef.debugId(); + crumbleStrings << displayName(objRef); + + while ((!isRoot(ref)) && (ref.debugId()!=-1)) { + ref = findParentRecursive(ref.debugId(), m_clientProxy->rootObjectReference()); + crumbleData.push_front( ref.debugId() ); + crumbleStrings.push_front( displayName(ref) ); + } + + int itemIndex = crumbleData.length()-1; + + // now append the children + foreach (const QDeclarativeDebugObjectReference &child, objRef.children()) { + crumbleData.push_back(child.debugId()); + crumbleStrings.push_back( displayName(child) ); + } + + m_crumblePath->updateContextPath(crumbleStrings, crumbleData); + m_crumblePath->selectIndex(itemIndex); +} + +void InspectorUi::selectItems(const QList<int> &objectIds) +{ + QList<QDeclarativeDebugObjectReference> objectReferences; + foreach (int objectId, objectIds) + { + QDeclarativeDebugObjectReference ref = m_clientProxy->objectReferenceForId(objectId); + if (ref.debugId() == objectId) + objectReferences.append(ref); + } + if (objectReferences.length() > 0) + selectItems(objectReferences); +} + +void InspectorUi::changePropertyValue(int debugId,const QString &propertyName, const QString &valueExpression) +{ + QString query = propertyName + '=' + valueExpression; + m_clientProxy->queryExpressionResult(debugId,query, this); } void InspectorUi::enable() { m_toolbar->enable(); m_crumblePath->setEnabled(true); - m_objectTreeWidget->setEnabled(true); + m_propertyInspector->setEnabled(true); } void InspectorUi::disable() { m_toolbar->disable(); m_crumblePath->setEnabled(false); - m_objectTreeWidget->setEnabled(false); + m_propertyInspector->setEnabled(false); } -void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectReference &obj) +QDeclarativeDebugObjectReference InspectorUi::objectReferenceForLocation(const QString &fileName, int cursorPosition) const { - Q_UNUSED(obj); + Core::EditorManager *editorManager = Core::EditorManager::instance(); + Core::IEditor *editor = editorManager->openEditor(fileName); + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor); + + if (textEditor && m_clientProxy && textEditor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID) { + if (cursorPosition == -1) + cursorPosition = textEditor->position(); + QmlJSEditor::QmlJSTextEditor *qmlEditor = + static_cast<QmlJSEditor::QmlJSTextEditor*>(textEditor->widget()); + + if (QmlJS::AST::Node *node + = qmlEditor->semanticInfo().declaringMemberNoProperties(cursorPosition)) { + if (QmlJS::AST::UiObjectMember *objMember = node->uiObjectMemberCast()) { + return m_clientProxy->objectReferenceForLocation( + objMember->firstSourceLocation().startLine, + objMember->firstSourceLocation().startColumn); + } + } + } + return QDeclarativeDebugObjectReference(); +} +void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectReference &obj) +{ QDeclarativeDebugFileReference source = obj.source(); QString fileName = source.url().toLocalFile(); @@ -521,11 +684,14 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor); if (textEditor) { - m_lastSelectedDebugId = obj.debugId(); - - editorManager->addCurrentPositionToNavigationHistory(); - textEditor->gotoLine(source.lineNumber()); - textEditor->widget()->setFocus(); + QDeclarativeDebugObjectReference ref = objectReferenceForLocation(fileName); + if (ref.debugId() != obj.debugId()) + { + m_selectionCallbackExpected = true; + editorManager->addCurrentPositionToNavigationHistory(); + textEditor->gotoLine(source.lineNumber()); + textEditor->widget()->setFocus(); + } } } @@ -553,7 +719,7 @@ void InspectorUi::setupDockWidgets() m_crumblePath->setWindowTitle(tr("Context Path")); connect(m_crumblePath, SIGNAL(elementClicked(int)), SLOT(crumblePathElementClicked(int))); - m_objectTreeWidget = new QmlJSObjectTree; + m_propertyInspector = new QmlJSPropertyInspector; QWidget *observerWidget = new QWidget; observerWidget->setWindowTitle(tr("QML Observer")); @@ -564,7 +730,7 @@ void InspectorUi::setupDockWidgets() wlay->setSpacing(0); observerWidget->setLayout(wlay); wlay->addWidget(m_toolbar->widget()); - wlay->addWidget(m_objectTreeWidget); + wlay->addWidget(m_propertyInspector); wlay->addWidget(m_crumblePath); Debugger::DebuggerMainWindow *mw = Debugger::DebuggerPlugin::mainWindow(); @@ -575,9 +741,9 @@ void InspectorUi::setupDockWidgets() void InspectorUi::crumblePathElementClicked(int pathIndex) { - if (m_clientProxy && m_clientProxy->isConnected() && !m_crumblePath->isEmpty()) { - m_clientProxy->setContextPathIndex(pathIndex); - } + QList <int> l; + l << m_crumblePath->debugIdForIndex(pathIndex); + selectItems(l); } bool InspectorUi::showExperimentalWarning() @@ -623,7 +789,7 @@ void InspectorUi::setApplyChangesToQmlObserver(bool applyChanges) void InspectorUi::applyChangesToQmlObserverHelper(bool applyChanges) { QHashIterator<QString, QmlJSLiveTextPreview *> iter(m_textPreviews); - while(iter.hasNext()) { + while (iter.hasNext()) { iter.next(); iter.value()->setApplyChangesToQmlObserver(applyChanges); } @@ -648,7 +814,7 @@ void InspectorUi::updatePendingPreviewDocuments(QmlJS::Document::Ptr doc) QmlJSLiveTextPreview *preview = createPreviewForEditor(editors.first()); editors.removeFirst(); - foreach(Core::IEditor *editor, editors) { + foreach (Core::IEditor *editor, editors) { preview->associateEditor(editor); } } @@ -682,7 +848,8 @@ void InspectorUi::setupToolbar(bool doConnect) this, SLOT(setApplyChangesToQmlObserver(bool))); connect(m_toolbar, SIGNAL(showAppOnTopSelected(bool)), m_clientProxy, SLOT(showAppOnTop(bool))); - + connect(m_toolbar, SIGNAL(filterTextChanged(QString)), + m_propertyInspector,SLOT(filterBy(QString))); connect(m_clientProxy, SIGNAL(colorPickerActivated()), m_toolbar, SLOT(activateColorPicker())); connect(m_clientProxy, SIGNAL(selectToolActivated()), @@ -720,7 +887,8 @@ void InspectorUi::setupToolbar(bool doConnect) this, SLOT(setApplyChangesToQmlObserver(bool))); disconnect(m_toolbar, SIGNAL(showAppOnTopSelected(bool)), m_clientProxy, SLOT(showAppOnTop(bool))); - + disconnect(m_toolbar, SIGNAL(filterTextChanged(QString)), + m_propertyInspector,SLOT(filterBy(QString))); disconnect(m_clientProxy, SIGNAL(colorPickerActivated()), m_toolbar, SLOT(activateColorPicker())); disconnect(m_clientProxy, SIGNAL(selectToolActivated()), diff --git a/src/plugins/qmljsinspector/qmljsinspector.h b/src/plugins/qmljsinspector/qmljsinspector.h index 7d3940e233..c890ce5371 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.h +++ b/src/plugins/qmljsinspector/qmljsinspector.h @@ -67,7 +67,7 @@ namespace QmlJSInspector { namespace Internal { class QmlInspectorToolbar; -class QmlJSObjectTree; +class QmlJSPropertyInspector; class ClientProxy; class InspectorSettings; class ContextCrumblePath; @@ -120,8 +120,11 @@ private slots: void enable(); void disable(); void gotoObjectReferenceDefinition(const QDeclarativeDebugObjectReference &obj); - void gotoObjectReferenceDefinition(QList<QDeclarativeDebugObjectReference> objectReferences); + void selectItems(const QList<QDeclarativeDebugObjectReference> &objectReferences); + void selectItems(const QList<int> &objectIds); void changeSelectedItems(const QList<QDeclarativeDebugObjectReference> &objects); + void changePropertyValue(int debugId,const QString &propertyName, const QString &valueExpression); + void objectTreeReady(); void updateEngineList(); @@ -146,19 +149,20 @@ private: void setupToolbar(bool doConnect); void setupDockWidgets(); QString filenameForShadowBuildFile(const QString &filename) const; + void populateCrumblePath(const QDeclarativeDebugObjectReference &objRef); + bool isRoot(const QDeclarativeDebugObjectReference &obj) const; + QDeclarativeDebugObjectReference objectReferenceForLocation(const QString &fileName, int cursorPosition=-1) const; private: bool m_listeningToEditorManager; - QmlInspectorToolbar *m_toolbar; ContextCrumblePath *m_crumblePath; - QmlJSObjectTree *m_objectTreeWidget; + QmlJSPropertyInspector *m_propertyInspector; InspectorSettings *m_settings; ClientProxy *m_clientProxy; QObject *m_qmlEngine; QDeclarativeDebugExpressionQuery *m_debugQuery; - int m_lastSelectedDebugId; // Qml/JS integration QHash<QString, QmlJSLiveTextPreview *> m_textPreviews; @@ -172,6 +176,7 @@ private: Utils::FileInProjectFinder m_projectFinder; static InspectorUi *m_instance; + bool m_selectionCallbackExpected; }; } // Internal diff --git a/src/plugins/qmljsinspector/qmljsinspector.pro b/src/plugins/qmljsinspector/qmljsinspector.pro index 61e71483f3..6459785bdb 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.pro +++ b/src/plugins/qmljsinspector/qmljsinspector.pro @@ -19,7 +19,7 @@ qmljstoolbarcolorbox.h \ qmljsobserverclient.h \ qmljscontextcrumblepath.h \ qmljsinspectorsettings.h \ -qmljsobjecttree.h +qmljspropertyinspector.h SOURCES += \ qmljsinspectorplugin.cpp \ @@ -31,7 +31,7 @@ qmljstoolbarcolorbox.cpp \ qmljsobserverclient.cpp \ qmljscontextcrumblepath.cpp \ qmljsinspectorsettings.cpp \ -qmljsobjecttree.cpp +qmljspropertyinspector.cpp include(../../libs/qmljsdebugclient/qmljsdebugclient-lib.pri) diff --git a/src/plugins/qmljsinspector/qmljsobjecttree.cpp b/src/plugins/qmljsinspector/qmljsobjecttree.cpp deleted file mode 100644 index 937c6c2ffc..0000000000 --- a/src/plugins/qmljsinspector/qmljsobjecttree.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** No Commercial Usage -** -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -**************************************************************************/ -#include "qmljsobjecttree.h" - -#include <QContextMenuEvent> -#include <QEvent> -#include <QtGui/QMenu> -#include <QtGui/QAction> -#include <QApplication> -#include <QInputDialog> - -#include <QDebug> - -namespace QmlJSInspector { -namespace Internal { - -// ************************************************************************* -// ObjectTreeItem -// ************************************************************************* - -class ObjectTreeItem : public QTreeWidgetItem -{ -public: - explicit ObjectTreeItem(QTreeWidget *widget, int type = 0); - ObjectTreeItem(QTreeWidgetItem *parentItem, int type = 0); - QVariant data (int column, int role) const; - void setData (int column, int role, const QVariant & value); - - void setHasValidDebugId(bool value); - - -private: - bool m_hasValidDebugId; -}; - -ObjectTreeItem::ObjectTreeItem(QTreeWidget *widget, int type) : - QTreeWidgetItem(widget, type), m_hasValidDebugId(true) -{ - -} - -ObjectTreeItem::ObjectTreeItem(QTreeWidgetItem *parentItem, int type) : - QTreeWidgetItem(parentItem, type), m_hasValidDebugId(true) -{ - -} - -QVariant ObjectTreeItem::data (int column, int role) const -{ - if (role == Qt::ForegroundRole) - return m_hasValidDebugId ? qApp->palette().color(QPalette::Foreground) : qApp->palette().color(QPalette::Disabled, QPalette::Foreground); - - return QTreeWidgetItem::data(column, role); -} - -void ObjectTreeItem::setData (int column, int role, const QVariant & value) -{ - QTreeWidgetItem::setData(column, role, value); -} - -void ObjectTreeItem::setHasValidDebugId(bool value) -{ - m_hasValidDebugId = value; -} - -// ************************************************************************* -// QmlJSObjectTree -// ************************************************************************* - -QmlJSObjectTree::QmlJSObjectTree(QWidget *parent) - : QTreeWidget(parent) - , m_clickedItem(0) - , m_currentObjectDebugId(0) -{ - setAttribute(Qt::WA_MacShowFocusRect, false); - setFrameStyle(QFrame::NoFrame); - setHeaderHidden(true); - setExpandsOnDoubleClick(false); - - m_goToFileAction = new QAction(tr("Go to file"), this); - connect(m_goToFileAction, SIGNAL(triggered()), SLOT(goToFile())); - - connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), - SLOT(currentItemChanged(QTreeWidgetItem *))); - connect(this, SIGNAL(itemActivated(QTreeWidgetItem *, int)), - SLOT(activated(QTreeWidgetItem *))); - connect(this, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); -} - -void QmlJSObjectTree::selectionChanged() -{ - if (selectedItems().isEmpty()) - return; - - // TODO -} - -void QmlJSObjectTree::setCurrentObject(int debugId) -{ - QTreeWidgetItem *item = findItemByObjectId(debugId); - if (item) { - setCurrentItem(item); - scrollToItem(item); - item->setExpanded(true); - } -} - -void QmlJSObjectTree::currentItemChanged(QTreeWidgetItem *item) -{ - if (!item) - return; - - QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); - if (obj.debugId() >= 0) - emit currentObjectChanged(obj); -} - -void QmlJSObjectTree::activated(QTreeWidgetItem *item) -{ - if (!item) - return; - - QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); - if (obj.debugId() >= 0) - emit activated(obj); -} - -void QmlJSObjectTree::buildTree(const QDeclarativeDebugObjectReference &obj, QTreeWidgetItem *parent) -{ - if (!parent) - clear(); - - if (obj.contextDebugId() < 0) - return; - - ObjectTreeItem *item = parent ? new ObjectTreeItem(parent) : new ObjectTreeItem(this); - if (obj.idString().isEmpty()) - item->setText(0, QString("<%1>").arg(obj.className())); - else - item->setText(0, obj.idString()); - item->setData(0, Qt::UserRole, qVariantFromValue(obj)); - - if (parent && obj.contextDebugId() >= 0 - && obj.contextDebugId() != parent->data(0, Qt::UserRole - ).value<QDeclarativeDebugObjectReference>().contextDebugId()) - { - - QDeclarativeDebugFileReference source = obj.source(); - if (!source.url().isEmpty()) { - QString toolTipString = tr("Url: ") + source.url().toString(); - item->setToolTip(0, toolTipString); - } - } else { - item->setExpanded(true); - } - - if (obj.contextDebugId() < 0) - item->setHasValidDebugId(false); - - for (int ii = 0; ii < obj.children().count(); ++ii) - buildTree(obj.children().at(ii), item); -} - -QTreeWidgetItem *QmlJSObjectTree::findItemByObjectId(int debugId) const -{ - for (int i=0; i<topLevelItemCount(); ++i) { - QTreeWidgetItem *item = findItem(topLevelItem(i), debugId); - if (item) - return item; - } - - return 0; -} - -QTreeWidgetItem *QmlJSObjectTree::findItem(QTreeWidgetItem *item, int debugId) const -{ - if (item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>().debugId() == debugId) - return item; - - QTreeWidgetItem *child; - for (int i=0; i<item->childCount(); ++i) { - child = findItem(item->child(i), debugId); - if (child) - return child; - } - - return 0; -} - -void QmlJSObjectTree::goToFile() -{ - QDeclarativeDebugObjectReference obj = - currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); - - if (obj.debugId() >= 0) - emit activated(obj); -} - -void QmlJSObjectTree::contextMenuEvent(QContextMenuEvent *event) -{ - m_clickedItem = itemAt(QPoint(event->pos().x(), - event->pos().y() )); - if (!m_clickedItem) - return; - - QMenu menu; - menu.addAction(m_goToFileAction); - menu.exec(event->globalPos()); -} - -} // Internal -} // QmlJSInspector diff --git a/src/plugins/qmljsinspector/qmljsobjecttree.h b/src/plugins/qmljsinspector/qmljsobjecttree.h deleted file mode 100644 index 3864903b14..0000000000 --- a/src/plugins/qmljsinspector/qmljsobjecttree.h +++ /dev/null @@ -1,86 +0,0 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** No Commercial Usage -** -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -**************************************************************************/ -#ifndef OBJECTTREE_H -#define OBJECTTREE_H - -#include <qmljsprivateapi.h> -#include <QtGui/QTreeWidget> - -QT_BEGIN_NAMESPACE - -class QTreeWidgetItem; - -QT_END_NAMESPACE - -namespace QmlJSInspector { -namespace Internal { - -class QmlJSObjectTree : public QTreeWidget -{ - Q_OBJECT -public: - QmlJSObjectTree(QWidget *parent = 0); - -signals: - void currentObjectChanged(const QDeclarativeDebugObjectReference &); - void activated(const QDeclarativeDebugObjectReference &); - void expressionWatchRequested(const QDeclarativeDebugObjectReference &, const QString &); - void contextHelpIdChanged(const QString &contextHelpId); - -public slots: - void setCurrentObject(int debugId); // select an object in the tree - -protected: - virtual void contextMenuEvent(QContextMenuEvent *); - -private slots: - void currentItemChanged(QTreeWidgetItem *); - void activated(QTreeWidgetItem *); - void selectionChanged(); - void goToFile(); - -private: - QTreeWidgetItem *findItemByObjectId(int debugId) const; - QTreeWidgetItem *findItem(QTreeWidgetItem *item, int debugId) const; - void buildTree(const QDeclarativeDebugObjectReference &, QTreeWidgetItem *parent); - - QTreeWidgetItem *m_clickedItem; - QAction *m_addWatchAction; - QAction *m_goToFileAction; - int m_currentObjectDebugId; -}; - -} // Internal -} // QmlJSInspector - -#endif |