From f0a172d19821e37604004cd6b8fa17b998b39857 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 6 May 2009 15:15:27 +1000 Subject: Beginings of an experimental QML debugger view --- src/declarative/debugger/debugger.pri | 3 + src/declarative/debugger/qmldebugger.cpp | 243 +++++++++++++++++++++++++++++ src/declarative/debugger/qmldebugger.h | 82 ++++++++++ src/declarative/declarative.pro | 1 + src/declarative/qml/qmlboundsignal.cpp | 1 + src/declarative/qml/qmlcompiler.cpp | 9 +- src/declarative/qml/qmlcomponent.cpp | 5 + src/declarative/qml/qmlcontext.cpp | 2 +- src/declarative/qml/qmlcontext_p.h | 3 + src/declarative/qml/qmlinstruction_p.h | 1 + src/declarative/qml/qmlparser.cpp | 2 +- src/declarative/qml/qmlparser_p.h | 3 + src/declarative/qml/qmlscriptparser.cpp | 5 + src/declarative/util/qfxview.cpp | 10 ++ src/declarative/util/qmlopenmetaobject.cpp | 1 + 15 files changed, 368 insertions(+), 3 deletions(-) create mode 100644 src/declarative/debugger/debugger.pri create mode 100644 src/declarative/debugger/qmldebugger.cpp create mode 100644 src/declarative/debugger/qmldebugger.h diff --git a/src/declarative/debugger/debugger.pri b/src/declarative/debugger/debugger.pri new file mode 100644 index 0000000..ea5219a --- /dev/null +++ b/src/declarative/debugger/debugger.pri @@ -0,0 +1,3 @@ +SOURCES += debugger/qmldebugger.cpp + +HEADERS += debugger/qmldebugger.h diff --git a/src/declarative/debugger/qmldebugger.cpp b/src/declarative/debugger/qmldebugger.cpp new file mode 100644 index 0000000..1f7fd68 --- /dev/null +++ b/src/declarative/debugger/qmldebugger.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmldebugger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QmlDebugger::QmlDebugger(QWidget *parent) +: QWidget(parent), m_tree(0) +{ + QHBoxLayout *layout = new QHBoxLayout; + setLayout(layout); + QSplitter *splitter = new QSplitter(this); + layout->addWidget(splitter); + + QWidget *treeWid = new QWidget(this); + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->setContentsMargins(0, 0, 0, 0); + treeWid->setLayout(vlayout); + splitter->addWidget(treeWid); + + m_tree = new QTreeWidget(treeWid); + m_tree->setHeaderHidden(true); + QObject::connect(m_tree, SIGNAL(itemPressed(QTreeWidgetItem *, int)), this, SLOT(itemPressed(QTreeWidgetItem *))); + vlayout->addWidget(m_tree); + + QPushButton *pb = new QPushButton("Refresh", treeWid); + QObject::connect(pb, SIGNAL(clicked()), this, SLOT(refresh())); + vlayout->addWidget(pb); + + m_text = new QPlainTextEdit(this); + m_text->setReadOnly(true); + splitter->addWidget(m_text); + splitter->setStretchFactor(1, 2); +} + +class QmlDebuggerItem : public QTreeWidgetItem +{ +public: + QmlDebuggerItem(QTreeWidget *wid) + : QTreeWidgetItem(wid), startLine(-1), endLine(-1) + { + } + + QmlDebuggerItem(QTreeWidgetItem *item) + : QTreeWidgetItem(item), startLine(-1), endLine(-1) + { + } + + int startLine; + int endLine; + QUrl url; +}; + +void QmlDebugger::itemPressed(QTreeWidgetItem *i) +{ + QmlDebuggerItem *item = static_cast(i); + + if(item->url.scheme() == QLatin1String("file")) { + QString f = item->url.toLocalFile(); + QFile file(f); + file.open(QIODevice::ReadOnly); + QByteArray ba = file.readAll(); + QTextStream stream(ba, QIODevice::ReadOnly); + const QString code = stream.readAll(); + + m_text->setPlainText(code); + + if(item->startLine != -1) { + QTextDocument *document = m_text->document(); + QTextCharFormat format; + format.setForeground(Qt::lightGray); + { + QTextCursor cursor(document); + cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, item->startLine - 1); + cursor.setCharFormat(format); + } + + { + QTextCursor cursor(document); + cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, item->endLine); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + cursor.setCharFormat(format); + } + + { + QTextCursor cursor(document); + cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); + cursor.setCharFormat(QTextCharFormat()); + } + + { + QTextCursor cursor(document); + cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, item->startLine - 1); + m_text->setTextCursor(cursor); + m_text->centerCursor(); + } + } + + } +} + +static bool makeItem(QObject *obj, QmlDebuggerItem *item) +{ + bool rv = true; + + QString text; + + if(QmlBindableValue *bv = qobject_cast(obj)) { + text = bv->property().name() + ": " + bv->expression(); + item->setForeground(0, Qt::green); + } else if(QmlBoundSignal *bs = qobject_cast(obj)) { + QMetaMethod method = obj->parent()->metaObject()->method(bs->index()); + QByteArray sig = method.signature(); + if(!sig.isEmpty()) + text = sig + ": "; + text += bs->expression(); + item->setForeground(0, Qt::blue); + rv = false; + } else { + QmlContext *context = qmlContext(obj); + QmlContext *parentContext = qmlContext(obj->parent()); + + text = QLatin1String(obj->metaObject()->className()); + + if(context && context != parentContext) { + QmlContextPrivate *p = static_cast(QObjectPrivate::get(context)); + + QString toolTipString; + if(!p->url.toString().isEmpty()) { + item->url = p->url; + toolTipString = "URL: " + p->url.toString(); + } + + if(!p->typeName.isEmpty()) { + if(!toolTipString.isEmpty()) + toolTipString.prepend("\n"); + toolTipString.prepend("Root type: " + text); + text = p->typeName; + } + + if(!toolTipString.isEmpty()) + item->setToolTip(0, toolTipString); + + item->setForeground(0, QColor("orange")); + + if(p->startLine != -1) { + item->startLine = p->startLine; + item->endLine = p->endLine; + } + + } else { + item->setExpanded(true); + } + } + + item->setText(0, text); + + return rv; +} + +static void buildTree(QObject *obj, QmlDebuggerItem *parent) +{ + QObjectList children = obj->children(); + + for (int ii = 0; ii < children.count(); ++ii) { + QmlDebuggerItem *item = new QmlDebuggerItem(parent); + if(makeItem(children.at(ii), item)) + buildTree(children.at(ii), item); + } +} + +void QmlDebugger::refresh() +{ + setDebugObject(m_object); +} + +void QmlDebugger::setDebugObject(QObject *obj) +{ + m_tree->clear(); + + m_object = obj; + if(!obj) + return; + + QmlDebuggerItem *item = new QmlDebuggerItem(m_tree); + makeItem(obj, item); + buildTree(obj, item); + item->setExpanded(true); + setGeometry(0, 100, 800, 600); +} + diff --git a/src/declarative/debugger/qmldebugger.h b/src/declarative/debugger/qmldebugger.h new file mode 100644 index 0000000..943abef --- /dev/null +++ b/src/declarative/debugger/qmldebugger.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLDEBUGGER_H +#define QMLDEBUGGER_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QTreeWidget; +class QTreeWidgetItem; +class QPlainTextEdit; +class QmlDebugger : public QWidget +{ +Q_OBJECT +public: + QmlDebugger(QWidget *parent = 0); + + void setDebugObject(QObject *); + +public slots: + void refresh(); + +private slots: + void itemPressed(QTreeWidgetItem *); + +private: + QTreeWidget *m_tree; + QPlainTextEdit *m_text; + QPointer m_object; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLDEBUGGER_H + diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index 30704d0..1e8e82b 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -21,5 +21,6 @@ include(timeline/timeline.pri) include(extra/extra.pri) include(widgets/widgets.pri) include(test/test.pri) +include(debugger/debugger.pri) contains(QT_CONFIG, opengles2)|contains(QT_CONFIG, opengles1):include(opengl/opengl.pri) diff --git a/src/declarative/qml/qmlboundsignal.cpp b/src/declarative/qml/qmlboundsignal.cpp index 67e3dcf..5815dc6 100644 --- a/src/declarative/qml/qmlboundsignal.cpp +++ b/src/declarative/qml/qmlboundsignal.cpp @@ -99,6 +99,7 @@ QmlBoundSignalParameters::QmlBoundSignalParameters(const QMetaMethod &method, // ### Ensure only supported types are allowed, otherwise it might crash QMetaObjectBuilder mob; mob.setSuperClass(&QmlBoundSignalParameters::staticMetaObject); + mob.setClassName("QmlBoundSignalParameters"); QList paramTypes = method.parameterTypes(); QList paramNames = method.parameterNames(); diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 67a0a04..9ae1278 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -60,6 +60,7 @@ #include #include #include "private/qmlcustomparser_p_p.h" +#include #include "qmlscriptparser_p.h" @@ -675,6 +676,7 @@ bool QmlCompiler::compileComponentFromRoot(Object *obj, int ctxt) QmlInstruction &create = output->bytecode.last(); create.type = QmlInstruction::CreateComponent; create.line = obj->line; + create.createComponent.endLine = obj->endLine; int count = output->bytecode.count(); QmlInstruction init; @@ -1468,7 +1470,12 @@ QObject *QmlCompiledData::TypeReference::createInstance(QmlContext *ctxt) const QmlEngine::setContextForObject(rv, ctxt); return rv; } else if (component) { - return component->create(ctxt); + QObject *rv = component->create(ctxt); + QmlContext *ctxt = qmlContext(rv); + if(ctxt) { + static_cast(QObjectPrivate::get(ctxt))->typeName = className; + } + return rv; } else { return 0; } diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp index b257d5f..027c2a8 100644 --- a/src/declarative/qml/qmlcomponent.cpp +++ b/src/declarative/qml/qmlcomponent.cpp @@ -462,6 +462,11 @@ QObject *QmlComponent::beginCreate(QmlContext *context) QmlContext *ctxt = new QmlContext(context, 0); static_cast(ctxt->d_ptr)->url = d->cc->url; + if(d->start != -1) { + // ### FIXME + static_cast(ctxt->d_ptr)->startLine = d->cc->bytecode.at(d->start - 1).line; + static_cast(ctxt->d_ptr)->endLine = d->cc->bytecode.at(d->start - 1).createComponent.endLine; + } ctxt->activate(); QmlVME vme; diff --git a/src/declarative/qml/qmlcontext.cpp b/src/declarative/qml/qmlcontext.cpp index 68453c3..7187c4c 100644 --- a/src/declarative/qml/qmlcontext.cpp +++ b/src/declarative/qml/qmlcontext.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE QmlContextPrivate::QmlContextPrivate() - : parent(0), engine(0), highPriorityCount(0) + : parent(0), engine(0), highPriorityCount(0), startLine(-1), endLine(-1) { } diff --git a/src/declarative/qml/qmlcontext_p.h b/src/declarative/qml/qmlcontext_p.h index 40848fb..d7c6d29 100644 --- a/src/declarative/qml/qmlcontext_p.h +++ b/src/declarative/qml/qmlcontext_p.h @@ -70,6 +70,9 @@ public: QScriptValueList scopeChain; QUrl url; + QByteArray typeName; + int startLine; + int endLine; void init(); diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index 01bdfdd..e9c81d6 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -275,6 +275,7 @@ public: } assignSignalObject; struct { int count; + int endLine; } createComponent; struct { int id; diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index d68eb68..a6cb2ca 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE using namespace QmlParser; QmlParser::Object::Object() -: type(-1), metatype(0), extObjectData(0), defaultProperty(0), line(-1), column(-1) +: type(-1), metatype(0), extObjectData(0), defaultProperty(0), line(-1), column(-1), endLine(-1), endColumn(-1) { } diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index 2c9b0f1..aa22928 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -106,6 +106,9 @@ namespace QmlParser qint64 line; qint64 column; + qint64 endLine; + qint64 endColumn; + struct DynamicProperty { DynamicProperty(); DynamicProperty(const DynamicProperty &); diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp index aed17d6..8be0e5a 100644 --- a/src/declarative/qml/qmlscriptparser.cpp +++ b/src/declarative/qml/qmlscriptparser.cpp @@ -235,6 +235,11 @@ Object *ProcessAST::defineObjectBinding_helper(int line, _scope.removeLast(); obj->line = line; + if(initializer) { + obj->endLine = initializer->rbraceToken.startLine; + obj->endColumn = initializer->rbraceToken.startColumn; + } + if (propertyCount) { Property *prop = currentProperty(); Value *v = new Value; diff --git a/src/declarative/util/qfxview.cpp b/src/declarative/util/qfxview.cpp index 1315f19..ea2719d 100644 --- a/src/declarative/util/qfxview.cpp +++ b/src/declarative/util/qfxview.cpp @@ -60,10 +60,12 @@ #include "qfxview.h" #include #include +#include QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(itemTreeDump, ITEMTREE_DUMP); +DEFINE_BOOL_CONFIG_OPTION(qmlDebugger, QML_DEBUGGER); static QVariant stringToPixmap(const QString &str) { @@ -324,6 +326,14 @@ void QFxView::continueExecute() if (itemTreeDump()) item->dump(); + if(qmlDebugger()) { + QmlDebugger *debugger = new QmlDebugger; + debugger->setDebugObject(item); + debugger->show(); + raise(); + debugger->raise(); + } + QPerformanceLog::displayData(); QPerformanceLog::clear(); d->root = item; diff --git a/src/declarative/util/qmlopenmetaobject.cpp b/src/declarative/util/qmlopenmetaobject.cpp index 87d8f4d..fc20fa9 100644 --- a/src/declarative/util/qmlopenmetaobject.cpp +++ b/src/declarative/util/qmlopenmetaobject.cpp @@ -48,6 +48,7 @@ QmlOpenMetaObject::QmlOpenMetaObject(QObject *obj, bool automatic) : autoCreate(automatic), parent(0), mem(0), _object(obj) { mob.setSuperClass(obj->metaObject()); + mob.setClassName(obj->metaObject()->className()); mob.setFlags(QMetaObjectBuilder::DynamicMetaObject); QObjectPrivate *op = QObjectPrivate::get(obj); -- cgit v0.12