diff options
author | Martin Jones <martin.jones@nokia.com> | 2009-10-22 01:59:33 (GMT) |
---|---|---|
committer | Martin Jones <martin.jones@nokia.com> | 2009-10-22 01:59:33 (GMT) |
commit | 6c9ac788a60fcd2f946787a5297d7c2f71ad1968 (patch) | |
tree | af53424d7d2adfcbb324624ab93d31845d3db80d | |
parent | 2391cf5d127882ce8811e058115d5c2605328079 (diff) | |
parent | fa85d539decf7bdcb16fa8f52858b13479cea659 (diff) | |
download | Qt-6c9ac788a60fcd2f946787a5297d7c2f71ad1968.zip Qt-6c9ac788a60fcd2f946787a5297d7c2f71ad1968.tar.gz Qt-6c9ac788a60fcd2f946787a5297d7c2f71ad1968.tar.bz2 |
Merge branch 'kinetic-declarativeui' of scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
75 files changed, 3171 insertions, 1030 deletions
diff --git a/doc/src/declarative/propertybinding.qdoc b/doc/src/declarative/propertybinding.qdoc index 50cbf2d..2b8a58c 100644 --- a/doc/src/declarative/propertybinding.qdoc +++ b/doc/src/declarative/propertybinding.qdoc @@ -97,6 +97,6 @@ The implicit binding syntax shown previously is easy to use and works perfectly of bindings. In some advanced cases, it is necessary to create bindings explicitly using the \l Binding element. -One such example is included in the \l {Passing Data Between C++ and QML} documentation. +XXX - need an example */ diff --git a/doc/src/declarative/qmlformat.qdoc b/doc/src/declarative/qmlformat.qdoc deleted file mode 100644 index 72bbe40..0000000 --- a/doc/src/declarative/qmlformat.qdoc +++ /dev/null @@ -1,350 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the documentation 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$ -** -****************************************************************************/ - -/*! -\page qmlformat.html -\title QML Format Reference - -\tableofcontents - -\section1 Overview - -QML is an extension to \l {http://www.ecma-international.org/publications/standards/Ecma-262.htm} -{ECMAScript}. QML adds a mechanism for declaratively building a tree of objects, improved -integration between ECMAScript and Qt's existing QObject based type system, and support for -transparently maintained property value bindings between ECMAScript expressions and QObject -properties. - -Much of a QML file consists of valid ECMAScript \e {Statement}s. Except where constraints imposed -by ECMAScript, C++ or QObject prevented it, the syntactic extensions introduced by QML are designed -to look similar and fit well with existing ECMAScript syntax and concepts. - -\section1 QML engine - -The \l {QmlEngine}{QML engine} executes a \l {QmlComponent}{QML document} in a -\l {QmlContext}{QML context} to produce a \l {QObject}{QML object}. A single QML -document may be executed in one or many contexts to produce many QML objects. A single -QML document may be executed many times in the same context to produce many QML objects. - -The QML engine provides the environment in which QML documents, contexts and objects -exist. It must exist before any of these structures can be created. If the engine is removed, -existing documents, contexts and objects are invalidated, but not destroyed. An invalid - -\list -\i \e {QML document} can no longer be used to create QML objects. -\i \e {QML context} can no longer host QML objects, new context properties cannot be added -and existing context properties cannot be modified. -\i \e {QML object} will no longer evaluate bindings or scripts. -\endlist - -A QML document is a block of QML source code. QML documents generally correspond to files stored -on a disk or network resource, but can be constructed directly from text data. Syntactically a QML -document is self contained; QML does \bold {not} have a preprocessor that modifies the document -before presentation to the compiler. Type references within a QML document are resolved based -exclusively on the import statements present in the document. - -A simple QML document looks like this: - -\table -\row -\o -\code -import Qt 4.6 - -Rectangle { - id: myRect - width: 100; height: 100 - color: background -} -\endcode -\endtable - -To instantiate a QML object, a QML document is executed in a QML context. QML contexts are used by -programmers to pass data to a QML document. QML documents may include property bindings or -ECMAScript blocks that can contain variable references that need to be resolved. Each property -binding and ECMAScript block has an associated QML context that is used to resolve these references -that is determined by the QML context in which the document is executed. The example document above -contains one variable reference, \c background. - -Each QML context defines a scope for variable resolution and each may define local, named context -properties. A QML context may also have a \l {QmlContext::addDefaultObject()}{default object}, -which is an object whose properties are searched \e after the context properties when resolving a -variable name. QML contexts form a tree, starting from a root context that is provided by the QML -engine. When resolving variable references, the QML contexts are searched starting from the -QML objects containing context upwards towards the root context. - -Consider the following QML context tree. If the example QML document is executed in \c Context1, -the \c background variable will resolve to \c Context1's context property. If the document is -executed in \c Context2, the \c background variable will resolve to the root context's context -property. - -\image qml-context-tree.png - -While QML contexts can be created explicitly by the programmer to pass data into QML objects, -the QML engine also creates a new implicit QML context for every object it instantiates. -Property bindings and ECMAScript blocks in the document are associated with this QML engine -created context. Object ids that are defined in the document are added as context properties, and -their value is set to reference the appropriate object, and the instantiated QML object is set as -the context's default object. The following diagram shows the result of executing a simple QML -document. - -\image qml-context-object.png - -The blue rectangle in the diagram represents a property binding. Associated with each property -binding is the QML context to which it belongs, the object property to which it is bound and a -\e {scope object}. The scope object is usually, but not always, the object to which the bound -property belongs. The context properties, context default objects and the scope object are all -involved when resolving a variable name in a binding. The following pseudo code describes the -algorithm used: - -\table -\row -\o -\code -if (scopeObject.hasProperty(name)) - return scopeObject.property(name) - -foreach (context in contextChain) { - if (context.hasContextProperty(name) - return context.contextProperty(name) - - if (context.defaultObject.hasProperty(name)) - return context.defaultObject.property(name) -} -\endcode -\endtable - -QML supports two categories of types: \e builtin types and \e composite types. Builtin types are -those written in C++ and registered with the QML engine. Builtin types form the most basic -building blocks of QML. Composite types are constructed by composing other builtin or composite -types, property bindings and ECMAScript blocks together into a brand new type using the QML -language. Using a composite type is identical to using a builtin type. - -For example, Qt 4.6 includes a builtin type called \c Image that shows a bitmap image. The -\c Image type has \c width and \c height properties that control the size of the displayed image. -A simple composite type, that will be called \c SquareImage can be built that adds a \c size -property that sets both the width and the height. - -\table -\row -\o -\code -import Qt 4.6 -Image { - property int size - width: size - height: size -} -\endcode -\endtable - -To the QML engine, a composite type is just another QML document. When a composite type is -used the engine instantiates it just as it would any other document - by creating a new implicit -QML context and the object tree described by the document. The diagram below shows the -\c SquareImage composite type used from within another QML document. When instantiated, the -\c SquareImage object is created in its own QML context. Any property bindings specified in the -\c SquareImage composite type document are associated with this context. Property bindings created -in the outer document, however, are associated with its context, even those that are applied to the -created \c SquareImage object. That is, the \c size, \c source, \c width and \c height property -bindings all share a common \e {scope object}, but are owned by two different QML contexts. The -difference in containing context results in the \c Root variable resolving differently in the -different property bindings. - -\image qml-context.png - -\section1 Syntax - -\section2 Encoding - -QML files are always encoded in UTF-8 format. - -\section2 Commenting - -The commenting rules in QML are the same as for ECMAScript. Both \e {MultiLineComment} blocks and \e {SingleLineComment}'s are supported. - -\section2 QML Document - -\section3 Syntax - -\e {QMLDocument} \bold {:} - -\quotation -\e {QMLImportList} \sub {opt} \e {QMLObjectDefinition} -\endquotation - -\e {QMLImportList} \bold {:} -\quotation -\e {QMLImportStatement} \e {QMLImportList} \sub {opt} -\endquotation - -\e {QMLImportStatement} \bold {:} - -\quotation -\bold {import} \e {StringLiteral} - -\bold {import} \e {StringLiteral} \e {QMLVersionNumber} - -\bold {import} \e {QMLNamespaceName} \e {QMLVersionNumber} - -\bold {import} \e {StringLiteral} \bold {as} \e {QMLNamespacePrefix} - -\bold {import} \e {StringLiteral} \e {QMLVersionNumber} \bold {as} \e {QMLNamespacePrefix} - -\bold {import} \e {QMLNamespaceName} \e {QMLVersionNumber} \bold {as} \e {QMLNamespacePrefix} -\endquotation - -\e {QMLNamespaceName} \bold {:} -\quotation -\e {QMLQualifiedId} -\endquotation - -\e {QMLVersionNumber} \bold {:} -\quotation -\e {DecimalLiteral} \bold {but not} with \e {ExponentPart} -\endquotation - -\section3 Semantics - -The \e {QMLImportList} is used to statically resolve type references used within the enclosing -QML document. - -An import statement is used to bring a set of types into scope for a QML document. - -\section2 Object Definition - -\section3 Syntax - -\e {QMLObjectDefinition} \bold {:} -\quotation -\e {QMLQualifiedId} \bold {\{} \e {QMLObjectMemberList} \bold {\}} -\endquotation - -\e {QMLObjectMemberList} \bold {:} -\quotation -\e {QMLObjectMember} \e {QMLObjectMemberList} \sub {opt} -\endquotation - -\e {QMLObjectMember} \bold {:} -\quotation -\e {QMLMemberAssignment} - -\e {QMLObjectDefinition} - -\e {QMLObjectExtensionDefinition} -\endquotation - -\e {QMLMemberAssignmentList} \bold {:} -\quotation -\e {QMLMemberAssignment} \e {QMLMemberAssignmentList} \sub {opt} -\endquotation - -\e {QMLMemberAssignment} \bold {:} -\quotation - -\e {QMLQualifiedId} \bold {:} \e {Literal} \bold {but not} \e {NullLiteral} - -\e {QMLQualifiedId} \bold {:} \e {QMLObjectDefinition} - -\e {QMLQualifiedId} \bold {:} \e {QMLObjectDefinitionArray} - -\e {QMLQualifiedId} \bold {:} \e {QMLBindingExpression} - -\e {QMLQualifiedId} \bold {\{} \e {QMLMemberAssignmentList} \bold {\}} - -\endquotation - -\section3 Semantics - -\section2 Object Extension - -\section3 Syntax - -\e {QMLObjectExtensionDefinition} \bold {:} -\quotation -\e {QMLObjectPropertyDefinition} - -\e {QMLObjectSignalDefinition} - -\e {QMLObjectMethodDefinition} -\endquotation - -\e {QMLObjectPropertyDefinition} \bold {:} -\quotation -\bold {property} \e {QmlObjectPropertyType} \e {QMLMemberIdentifier} - -\bold {default} \bold {property} \e {QmlObjectPropertyType} \e {QMLMemberIdentifier} - -\bold {property} \e {QmlObjectPropertyType} \e {QMLMemberIdentifier} \bold {:} \e {Literal} \bold {but not} \e {NullLiteral} - -\bold {property} \e {QmlObjectPropertyType} \e {QMLMemberIdentifier} \bold {:} \e {QmlBindingExpression} - -\bold {default} \bold {property} \e {QmlObjectPropertyType} \e {QMLMemberIdentifier} \bold {:} \e {Literal} \bold {but not} \e {NullLiteral} - -\bold {default} \bold {property} \e {QmlObjectPropertyType} \e {QMLMemberIdentifier} \bold {:} \e {QmlBindingExpression} -\endquotation - -\e {QMLObjectPropertyType} \bold {:: one of} -\quotation -\bold {int} \bold {bool} \bold {double} \bold {real} \bold {string} \bold {url} \bold {color} \bold {date} \bold {var} \bold {variant} \bold {alias} -\endquotation - -\e {QMLObjectSignalDefinition} \bold {:} -\quotation -\bold {signal} \e {QMLMemberIdentifier} - -\bold {signal} \e {QMLMemberIdentifier} \bold {(} QMLMemberTypedParameterList \bold {)} -\endquotation - -\e {QMLObjectMethodDefinition} \bold {:} -\quotation -\e {FunctionDeclaration} \bold {but not} \e {Identifier} \sub {opt} -\endquotation -\section3 Semantics - -\section2 Binding Expression - -\section3 Syntax - -\e {QMLBindingExpression} \bold {:} - -\section3 Semantics - -*/ diff --git a/doc/src/declarative/qmlmodels.qdoc b/doc/src/declarative/qmlmodels.qdoc index 6ebb734..45df29b 100644 --- a/doc/src/declarative/qmlmodels.qdoc +++ b/doc/src/declarative/qmlmodels.qdoc @@ -50,12 +50,18 @@ creates an instance for each item in the model. Models may be static, or have items modified, inserted, removed or moved dynamically. Data is provided to the delegate via named data roles which the -delegate may bind to. A special \e index role containing the -index of the item in the model is also available. Models that do -not have named roles will have the data provided via the \e modelData -role. The \e modelData role is also provided for Models that have -only one role. In this case the \e modelData role contains the same -data as the named role. +delegate may bind to. The roles are exposed as properties of the +\model property, though this property is set as a default property +of the delegate so, unless there is a naming clash with a +property in the delegate, the roles are usually accessed unqualified. + +A special \e index role containing the index of the item in the model +is also available. + +Models that do not have named roles will have the data provided via +the \e modelData role. The \e modelData role is also provided for +Models that have only one role. In this case the \e modelData role +contains the same data as the named role. There are a number of QML elements that operate using data models: @@ -189,4 +195,31 @@ ListView { An Object Instance specifies a model with a single Object element. The properties of the object are provided as roles. +The example below creates a list with one item, showing the color of the +\e myText text. Note the use of the \e model property to specify the model +data rather than the \e color property of the Text element in the delegate. + +\code +Rectangle { + Text { + id: myText + text: "Hello" + color: "#dd44ee" + } + + Component { + id: myDelegate + Text { + text: model.color + } + } + ListView { + anchors.fill: parent + anchors.topMargin: 30 + model: myText + delegate: myDelegate + } +} +\endcode + */ diff --git a/doc/src/declarative/qtbinding.qdoc b/doc/src/declarative/qtbinding.qdoc index 1831cf8..18685ac 100644 --- a/doc/src/declarative/qtbinding.qdoc +++ b/doc/src/declarative/qtbinding.qdoc @@ -44,10 +44,16 @@ \target qtbinding \title Using QML in C++ Applications +\tableofcontents + The QML API is split into three main classes - QmlEngine, QmlComponent and QmlContext. QmlEngine provides the environment in which QML is run, QmlComponent encapsulates \l {QML Documents}, and QmlContext allows applications to expose data to QML component instances. +QML also includes a convenience API, QmlView, for applications that simply want to embed QML +components into a new QGraphicsView. QmlView covers up many of the details discussed below. +While QmlView is mainly intended for rapid prototyping it can have uses in production applications. + \section1 Basic Usage Every application requires at least one QmlEngine. A QmlEngine allows the configuration of @@ -76,134 +82,182 @@ QML components are instantiated in a QmlContext. A context allows the applicati to the QML component instance. A single QmlContext can be used to instantiate all the objects used by an application, or several QmlContext can be created for more fine grained control over the data exposed to each instance. If a context is not passed to the QmlComponent::create() -method, the QmlEngine's \l {QmlEngine::rootContext()}{root context} is used. +method, the QmlEngine's \l {QmlEngine::rootContext()}{root context} is used. Data exposed through +the root context is available to all object instances. -To expose data to a QML component instance, applications set \l {QmlContext::setContextProperty()}{context properties} which are then accessible by name from QML \l {Property Binding}s and -\l {ECMAScript Blocks}. +\section1 Simple Data -\section1 Network Components +To expose data to a QML component instance, applications set \l {QmlContext::setContextProperty()} +{context properties} which are then accessible by name from QML \l {Property Binding}s and +\l {ECMAScript Blocks}. The following example shows how to expose a background color to a QML +file. -If the URL passed to QmlComponent is a network resource, or if the QML document references a -network resource, the QmlComponent has to fetch the network data before it is able to create -objects. In this case, the QmlComponent will have a \l {QmlComponent::Loading}{Loading} -\l {QmlComponent::status()}{status}. An application will have to wait until the component -is \l {QmlComponent::Ready}{Ready} before calling \l {QmlComponent::create()}. - -The following example shows how to load a QML file from a network resource. After creating -the QmlComponent, it tests whether the component is loading. If it is, it connects to the -QmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method -directly. This test is necessary, even for URLs that are known to be remote, just in case -the component has been cached and is ready immediately. +\table +\row +\o +\code +// main.cpp +QmlContext *windowContext = new QmlContext(engine->rootContext()); +windowContext->setContextProperty("backgroundColor", + QColor(Qt::lightsteelblue)); +QmlComponent component(&engine, "main.qml"); +QObject *window = component.create(windowContext); +\endcode +\o \code -MyApplication::MyApplication() -{ - // ... - component = new QmlComponent(engine, QUrl("http://www.example.com/main.qml")); - if (component->isLoading()) - QObject::connect(component, SIGNAL(statusChanged(QmlComponent::Status)), - this, SLOT(continueLoading())); - else - continueLoading(); -} +// main.qml +import Qt 4.6 -void MyApplication::continueLoading() -{ - if (component->isError()) { - qWarning() << component->errors(); - } else { - QObject *myObject = component->create(); +Rectangle { + color: backgroundColor + + Text { + anchors.centerIn: parent + text: "Hello Light Steel Blue World!" } } \endcode +\endtable -\section1 TODO -\list -\o QmlEngine and QmlComponent -\o QmlContext and data -\o QBindableMap -\o QAbstractItemModel Data models -\o QmlView -\endlist - -*/ +Context properties work just like normal properties in QML bindings - if the \c backgroundColor +context property in the previous example was changed to red, the component object instances would +all be automatically updated. -/* -\section1 Overview +QmlContexts form a tree - each QmlContext except for the root context has a parent. Child +QmlContexts effectively inherit the context properties present in their parents. This gives +applications more freedom in partitioning the data exposed to different QML object instances. +If a QmlContext sets a context property that is also set in one of its parents, the new context +property shadows that in the parent. In The following example, the \c background context property +in \c {Context 1} shadows the \c background context property in the root context. -The QML mechanisms of data binding can also be used to bind Qt C++ objects. +\image qml-context-tree.png -The data binding framework is based on Qt's property system (see the Qt documentation for more details on this system). If a binding is meant to be dynamic (where changes in one object are reflected in another object), \c NOTIFY must be specified for the property being tracked. If \c NOTIFY is not specified, any binding to that property will be an 'intialization' binding (the tracking object will be updated only once with the initial value of the tracked object). +\section2 Structured Data -Relevant items can also be bound to the contents of a Qt model. -For example, ListView can make use of data from a QAbstractItemModel-derived model. +Context properties can also be used to expose structured and writable data to QML objects. In +addition to all the types already supported by QVariant, QObject derived types can be assigned to +context properties. QObject context properties allow the data exposed to be more structured, and +allow QML to set values. -\section1 Passing Data Between C++ and QML +The following example creates a \c CustomPalette object, and sets it as the \c palette context +property. -Data binding provides one method of data transfer between C++ and QML. - -For example, lets say you want to implement a slider in QML that changes the screen brightness of the device it is running on. You would start by declaring a brightness property on your QObject-derived class: \code -class MyScreen : public QObject +class CustomPalette : public QObject { - Q_OBJECT +Q_OBJECT +Q_PROPERTY(QColor background READ background WRITE setBackground NOTIFY backgroundChanged) +Q_PROPERTY(QColor text READ text WRITE setText NOTIFY text) public: - MyScreen(QObject *parent=0); - - Q_PROPERTY(int brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged); - int brightness() const; - void setBrightness(int b); - ... - -signals: - void brightnessChanged(); + CustomPalette() : m_background(Qt::white), m_text(Qt::black) {} + + QColor background() const { return m_background; } + void setBackground(const QColor &c) { + if (c != m_background) { + m_background = c; + emit backgroundChanged(); + } + } + QColor text() const { return m_text; } + void setText(const QColor &c) { + if (c != m_text) { + m_text = c; + emit textChanged(); + } + } private: - int m_brightness; + QColor m_background; + QColor m_text; }; -int brightness() const +int main(int argc, char **argv) { - return m_brightness; -} + // ... -void setBrightness(int b) -{ - if (b != m_brightness) { - m_brightness = b; - emit brightnessChanged(); + QmlContext *windowContext = new QmlContext(engine->rootContext()); + windowContext->setContextProperty("palette", new CustomPalette); - //set device brightness - ... - } + QmlComponent component(&engine, "main.qml"); + QObject *window = component.create(windowContext); } \endcode -\note One important thing to keep in mind is that the changed signal should only be emitted when there is a real change ( \c b \c != \c m_brightness ), or you may get an infinite loop. +The QML that follows references the palette object, and its properties, to set the appropriate +background and text colors. When the window is clicked, the palette's text color is changed, and +the window text will update accordingly. -Next, make an instance of this class visible to the QML bind engine: \code -QmlView *view = new QmlView; -view->setUrl("MyUI.qml"); - -MyScreen *screen = new MyScreen; -QmlContext *ctxt = view->rootContext(); -ctxt->setContextProperty("screen", screen); +// main.qml +import Qt 4.6 + +Rectangle { + width: 240 + height: 320 + color: palette.background + + Text { + anchors.centerIn: parent + color: palette.text + text: "Hello Colorful World!" + } -view->execute(); + MouseRegion { + anchors.fill: parent + onClicked: { + palette.text = "blue"; + } + } +} \endcode +\endtable -\note Bindings must be made after setUrl() but before execute(). +To detect when a C++ property value - in this case the \c CustomPalette's \c text property - +changes, the property must have a corresponding NOTIFY signal. The NOTIFY signal specifies a signal +that is emitted whenever the property changes value. Implementers should take care to only emit the +signal if the value \e changes to prevent loops from occuring. Accessing a property from a +binding that does not have a NOTIFY signal will cause QML to issue a warning at runtime. -Finally, in QML you can make the appropriate bindings, so in \c "MyUI.qml": +\section2 Dynamic Structured Data -\code -Slider { value: screen.brightness } -Binding { target: screen; property: "brightness"; value: slider.value } -\endcode +If an application is too dynamic to structure data as compile-time QObject types, dynamically +structured data can be constructed at runtime using the QmlPropertyMap class. -The \l QBindableMap class provides a convenient way to make data visible to the bind engine. +\section1 Network Components + +If the URL passed to QmlComponent is a network resource, or if the QML document references a +network resource, the QmlComponent has to fetch the network data before it is able to create +objects. In this case, the QmlComponent will have a \l {QmlComponent::Loading}{Loading} +\l {QmlComponent::status()}{status}. An application will have to wait until the component +is \l {QmlComponent::Ready}{Ready} before calling \l {QmlComponent::create()}. + +The following example shows how to load a QML file from a network resource. After creating +the QmlComponent, it tests whether the component is loading. If it is, it connects to the +QmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method +directly. This test is necessary, even for URLs that are known to be remote, just in case +the component has been cached and is ready immediately. -C++ \l {qmlmodels}{Data Models} may also be provided to QML. +\code +MyApplication::MyApplication() +{ + // ... + component = new QmlComponent(engine, QUrl("http://www.example.com/main.qml")); + if (component->isLoading()) + QObject::connect(component, SIGNAL(statusChanged(QmlComponent::Status)), + this, SLOT(continueLoading())); + else + continueLoading(); +} +void MyApplication::continueLoading() +{ + if (component->isError()) { + qWarning() << component->errors(); + } else { + QObject *myObject = component->create(); + } +} +\endcode */ + diff --git a/doc/src/declarative/qtdeclarative.qdoc b/doc/src/declarative/qtdeclarative.qdoc index ebf7880..e01b02d 100644 --- a/doc/src/declarative/qtdeclarative.qdoc +++ b/doc/src/declarative/qtdeclarative.qdoc @@ -93,20 +93,15 @@ completely new applications. QML is fully \l {Extending QML}{extensible from C+ \o \l {Extending types from QML} \endlist -\section1 QML Reference: +\section1 Reference: \list -\o \l {QML Format Reference} \o \l {QML Elements} -\endlist - -\section1 C++ Reference: -\list \o \l {Extending QML} -\o \l {qtbinding}{QML/C++ Data Binding} \endlist \section1 Deprecated - +\list \o \l {tutorials-declarative-contacts.html}{Tutorial: 'Introduction to QML'} +\endlist */ diff --git a/doc/src/snippets/declarative/listview/highlight.qml b/doc/src/snippets/declarative/listview/highlight.qml index 2234ee7..7970ede 100644 --- a/doc/src/snippets/declarative/listview/highlight.qml +++ b/doc/src/snippets/declarative/listview/highlight.qml @@ -20,6 +20,18 @@ Rectangle { Text { text: '<b>Name:</b> ' + name } Text { text: '<b>Number:</b> ' + number } } + // Use the ListView.isCurrentItem attached property to + // indent the item if it is the current item. + states: [ + State { + name: "Current" + when: wrapper.ListView.isCurrentItem + PropertyChanges { target: wrapper; x: 10 } + } + ] + transitions: [ + Transition { NumberAnimation { properties: "x"; duration: 200 } } + ] } } //! [0] diff --git a/examples/declarative/animation/animation.qml b/examples/declarative/animation/animation.qml deleted file mode 100644 index 31c75e1..0000000 --- a/examples/declarative/animation/animation.qml +++ /dev/null @@ -1,63 +0,0 @@ -import Qt 4.6 - -Rectangle { - width: 400 - height: 200 - color: "white" - - Rectangle { - width: 40 - height: 40 - y: 80 - color: "#FF0000" - radius: 10 - - // Animate the x property. Setting repeat to true makes the - // animation repeat indefinitely, otherwise it would only run once. - x: SequentialAnimation { - running: true - repeat: true - - // Move from 0 to 360 in 500ms, using the easeInOutQuad easing function - NumberAnimation { - from: 0 - to: 360 - easing: "easeInOutQuad" - duration: 500 - } - - // Then pause for 200ms - PauseAnimation { - duration: 200 - } - - // Then move back to 0 in 2 seconds, using the easeInOutElastic easing function - NumberAnimation { - from: 360 - to: 0 - easing: "easeInOutElastic" - duration: 2000 - } - } - - // Alternate color between red and green - color: SequentialAnimation { - running: true - repeat: true - - ColorAnimation { - property: "color" - from: "#FF0000" - to: "#00FF00" - duration: 5000 - } - - ColorAnimation { - property: "color" - from: "#00FF00" - to: "#FF0000" - duration: 5000 - } - } - } -} diff --git a/examples/declarative/animation/color-animation.qml b/examples/declarative/animation/color-animation.qml new file mode 100644 index 0000000..0cf8a44 --- /dev/null +++ b/examples/declarative/animation/color-animation.qml @@ -0,0 +1,73 @@ +import Qt 4.6 + +Item { + id: window + width: 640; height: 480 + + // Let's draw the sky... + Rectangle { + anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.verticalCenter } + gradient: Gradient { + GradientStop { + position: 0.0 + color: SequentialAnimation { + running: true; repeat: true + ColorAnimation { from: "DeepSkyBlue"; to: "#0E1533"; duration: 5000 } + ColorAnimation { from: "#0E1533"; to: "DeepSkyBlue"; duration: 5000 } + } + } + GradientStop { + position: 1.0 + color: SequentialAnimation { + running: true; repeat: true + ColorAnimation { from: "SkyBlue"; to: "#437284"; duration: 5000 } + ColorAnimation { from: "#437284"; to: "SkyBlue"; duration: 5000 } + } + } + } + } + + // the sun, moon, and stars + Item { + width: parent.width; height: 2 * parent.height + transformOrigin: "Center" + rotation: SequentialAnimation { + running: true; repeat: true + NumberAnimation { from: 0; to: 360; duration: 10000 } + } + Image { + source: "images/sun.png"; y: 10; anchors.horizontalCenter: parent.horizontalCenter + transformOrigin: "Center"; rotation: -3 * parent.rotation + } + Image { + source: "images/moon.png"; y: parent.height - 74; anchors.horizontalCenter: parent.horizontalCenter + transformOrigin: "Center"; rotation: -parent.rotation + } + Particles { + x: 0; y: parent.height/2; width: parent.width; height: parent.height/2 + source: "images/star.png"; angleDeviation: 360; velocity: 0 + velocityDeviation: 0; count: parent.width / 10; fadeInDuration: 2800 + opacity: SequentialAnimation { + running: true; repeat: true + NumberAnimation { from: 0; to: 1; duration: 5000 } + NumberAnimation { from: 1; to: 0; duration: 5000 } + } + } + } + + // ...and the ground. + Rectangle { + anchors { left: parent.left; top: parent.verticalCenter; right: parent.right; bottom: parent.bottom } + gradient: Gradient { + GradientStop { + position: 0.0 + color: SequentialAnimation { + running: true; repeat: true + ColorAnimation { from: "ForestGreen"; to: "#001600"; duration: 5000 } + ColorAnimation { from: "#001600"; to: "ForestGreen"; duration: 5000 } + } + } + GradientStop { position: 1.0; color: "DarkGreen" } + } + } +} diff --git a/examples/declarative/animation/easing.qml b/examples/declarative/animation/easing.qml new file mode 100644 index 0000000..9e0a0d6 --- /dev/null +++ b/examples/declarative/animation/easing.qml @@ -0,0 +1,99 @@ +import Qt 4.6 + +Rectangle { + id: window + width: 600; height: 460; color: "#232323" + + ListModel { + id: easingTypes + ListElement { type: "easeLinear"; ballColor: "DarkRed" } + ListElement { type: "easeInQuad"; ballColor: "IndianRed" } + ListElement { type: "easeOutQuad"; ballColor: "Salmon" } + ListElement { type: "easeInOutQuad"; ballColor: "Tomato" } + ListElement { type: "easeOutInQuad"; ballColor: "DarkOrange" } + ListElement { type: "easeInCubic"; ballColor: "Gold" } + ListElement { type: "easeOutCubic"; ballColor: "Yellow" } + ListElement { type: "easeInOutCubic"; ballColor: "PeachPuff" } + ListElement { type: "easeOutInCubic"; ballColor: "Thistle" } + ListElement { type: "easeInQuart"; ballColor: "Orchid" } + ListElement { type: "easeOutQuart"; ballColor: "Purple" } + ListElement { type: "easeInOutQuart"; ballColor: "SlateBlue" } + ListElement { type: "easeOutInQuart"; ballColor: "Chartreuse" } + ListElement { type: "easeInQuint"; ballColor: "LimeGreen" } + ListElement { type: "easeOutQuint"; ballColor: "SeaGreen" } + ListElement { type: "easeInOutQuint"; ballColor: "DarkGreen" } + ListElement { type: "easeOutInQuint"; ballColor: "Olive" } + ListElement { type: "easeInSine"; ballColor: "DarkSeaGreen" } + ListElement { type: "easeOutSine"; ballColor: "Teal" } + ListElement { type: "easeInOutSine"; ballColor: "Turquoise" } + ListElement { type: "easeOutInSine"; ballColor: "SteelBlue" } + ListElement { type: "easeInExpo"; ballColor: "SkyBlue" } + ListElement { type: "easeOutExpo"; ballColor: "RoyalBlue" } + ListElement { type: "easeInOutExpo"; ballColor: "MediumBlue" } + ListElement { type: "easeOutInExpo"; ballColor: "MidnightBlue" } + ListElement { type: "easeInCirc"; ballColor: "CornSilk" } + ListElement { type: "easeOutCirc"; ballColor: "Bisque" } + ListElement { type: "easeInOutCirc"; ballColor: "RosyBrown" } + ListElement { type: "easeOutInCirc"; ballColor: "SandyBrown" } + ListElement { type: "easeInElastic"; ballColor: "DarkGoldenRod" } + ListElement { type: "easeOutElastic"; ballColor: "Chocolate" } + ListElement { type: "easeInOutElastic"; ballColor: "SaddleBrown" } + ListElement { type: "easeOutInElastic"; ballColor: "Brown" } + ListElement { type: "easeInBack"; ballColor: "Maroon" } + ListElement { type: "easeOutBack"; ballColor: "LavenderBlush" } + ListElement { type: "easeInOutBack"; ballColor: "MistyRose" } + ListElement { type: "easeOutInBack"; ballColor: "Gainsboro" } + ListElement { type: "easeOutBounce"; ballColor: "Silver" } + ListElement { type: "easeInBounce"; ballColor: "DimGray" } + ListElement { type: "easeInOutBounce"; ballColor: "SlateGray" } + ListElement { type: "easeOutInBounce"; ballColor: "DarkSlateGray" } + } + + Component { + id: delegate + + Item { + height: 42; width: window.width + Text { text: type; anchors.centerIn: parent; color: "White" } + Rectangle { + id: slot1; color: "#121212"; x: 30; height: 32; width: 32 + border.color: "#343434"; border.width: 1; radius: 8; anchors.verticalCenter: parent.verticalCenter + } + Rectangle { + id: slot2; color: "#121212"; x: window.width - 62; height: 32; width: 32 + border.color: "#343434"; border.width: 1; radius: 8; anchors.verticalCenter: parent.verticalCenter + } + Rectangle { + id: rect; x: 30; color: "#454545" + border.color: "White"; border.width: 2 + height: 32; width: 32; radius: 8; anchors.verticalCenter: parent.verticalCenter + + MouseRegion { + onClicked: if (rect.state == '') rect.state = "right"; else rect.state = '' + anchors.fill: parent + } + + states : State { + name: "right" + PropertyChanges { target: rect; x: window.width - 62; color: ballColor } + } + + transitions: Transition { + ParallelAnimation { + NumberAnimation { properties: "x"; easing: type; duration: 1000 } + ColorAnimation { properties: "color"; easing: type; duration: 1000 } + } + } + } + } + } + + Flickable { + anchors.fill: parent; viewportHeight: layout.height + Column { + id: layout + anchors.left: window.left; anchors.right: window.right + Repeater { model: easingTypes; delegate: delegate } + } + } +} diff --git a/examples/declarative/animation/images/face-smile.png b/examples/declarative/animation/images/face-smile.png Binary files differnew file mode 100644 index 0000000..3d66d72 --- /dev/null +++ b/examples/declarative/animation/images/face-smile.png diff --git a/examples/declarative/animation/images/moon.png b/examples/declarative/animation/images/moon.png Binary files differnew file mode 100644 index 0000000..9407b2b --- /dev/null +++ b/examples/declarative/animation/images/moon.png diff --git a/examples/declarative/animation/images/shadow.png b/examples/declarative/animation/images/shadow.png Binary files differnew file mode 100644 index 0000000..8270565 --- /dev/null +++ b/examples/declarative/animation/images/shadow.png diff --git a/examples/declarative/animation/images/star.png b/examples/declarative/animation/images/star.png Binary files differnew file mode 100644 index 0000000..27ef924 --- /dev/null +++ b/examples/declarative/animation/images/star.png diff --git a/examples/declarative/animation/images/sun.png b/examples/declarative/animation/images/sun.png Binary files differnew file mode 100644 index 0000000..7713ca5 --- /dev/null +++ b/examples/declarative/animation/images/sun.png diff --git a/examples/declarative/animation/property-animation.qml b/examples/declarative/animation/property-animation.qml new file mode 100644 index 0000000..4e0f6f4 --- /dev/null +++ b/examples/declarative/animation/property-animation.qml @@ -0,0 +1,64 @@ +import Qt 4.6 + +Item { + id: window + width: 320; height: 480 + + // Let's draw the sky... + Rectangle { + anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.verticalCenter } + gradient: Gradient { + GradientStop { position: 0.0; color: "DeepSkyBlue" } + GradientStop { position: 1.0; color: "LightSkyBlue" } + } + } + + // ...and the ground. + Rectangle { + anchors { left: parent.left; top: parent.verticalCenter; right: parent.right; bottom: parent.bottom } + gradient: Gradient { + GradientStop { position: 0.0; color: "ForestGreen" } + GradientStop { position: 1.0; color: "DarkGreen" } + } + } + + // The shadow for the smiley face + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "images/shadow.png"; y: smiley.minHeight + 58 + transformOrigin: "Center" + + // The scale property depends on the y position of the smiley face. + scale: smiley.y * 0.5 / (smiley.minHeight - smiley.maxHeight) + } + + Image { + id: smiley + property int maxHeight: window.height / 3 + property int minHeight: 2 * window.height / 3 + + anchors.horizontalCenter: parent.horizontalCenter + source: "images/face-smile.png"; y: minHeight + + // Animate the y property. Setting repeat to true makes the + // animation repeat indefinitely, otherwise it would only run once. + y: SequentialAnimation { + running: true; repeat: true + + // Move from minHeight to maxHeight in 300ms, using the easeOutExpo easing function + NumberAnimation { + from: smiley.minHeight; to: smiley.maxHeight + easing: "easeOutExpo"; duration: 300 + } + + // Then move back to minHeight in 1 second, using the easeOutBounce easing function + NumberAnimation { + from: smiley.maxHeight; to: smiley.minHeight + easing: "easeOutBounce"; duration: 1000 + } + + // Then pause for 500ms + PauseAnimation { duration: 500 } + } + } +} diff --git a/examples/declarative/easing/easing.qml b/examples/declarative/easing/easing.qml deleted file mode 100644 index 23d7b29..0000000 --- a/examples/declarative/easing/easing.qml +++ /dev/null @@ -1,93 +0,0 @@ -import Qt 4.6 - -Rectangle { - id: window - width: 640 - height: layout.height - color: "white" - - ListModel { - id: easingTypes - ListElement { type: "easeLinear" } - ListElement { type: "easeInQuad" } - ListElement { type: "easeOutQuad" } - ListElement { type: "easeInOutQuad" } - ListElement { type: "easeOutInQuad" } - ListElement { type: "easeInCubic" } - ListElement { type: "easeOutCubic" } - ListElement { type: "easeInOutCubic" } - ListElement { type: "easeOutInCubic" } - ListElement { type: "easeInQuart" } - ListElement { type: "easeOutQuart" } - ListElement { type: "easeInOutQuart" } - ListElement { type: "easeOutInQuart" } - ListElement { type: "easeInQuint" } - ListElement { type: "easeOutQuint" } - ListElement { type: "easeInOutQuint" } - ListElement { type: "easeOutInQuint" } - ListElement { type: "easeInSine" } - ListElement { type: "easeOutSine" } - ListElement { type: "easeInOutSine" } - ListElement { type: "easeOutInSine" } - ListElement { type: "easeInExpo" } - ListElement { type: "easeOutExpo" } - ListElement { type: "easeInOutExpo" } - ListElement { type: "easeOutInExpo" } - ListElement { type: "easeInCirc" } - ListElement { type: "easeOutCirc" } - ListElement { type: "easeInOutCirc" } - ListElement { type: "easeOutInCirc" } - ListElement { type: "easeInElastic" } - ListElement { type: "easeOutElastic" } - ListElement { type: "easeInOutElastic" } - ListElement { type: "easeOutInElastic" } - ListElement { type: "easeInBack" } - ListElement { type: "easeOutBack" } - ListElement { type: "easeInOutBack" } - ListElement { type: "easeOutInBack" } - ListElement { type: "easeOutBounce" } - ListElement { type: "easeInBounce" } - ListElement { type: "easeInOutBounce" } - ListElement { type: "easeOutInBounce" } - } - - Column { - id: layout - anchors.left: window.left - anchors.right: window.right - Repeater { - model: easingTypes - Component { - Text { - text: type - height: 18 - font.italic: true - x: SequentialAnimation { - id: anim - NumberAnimation { - from: 0 - to: window.width / 2 - easing: type - duration: 1000 - } - PauseAnimation { - duration: 300 - } - NumberAnimation { - to: 0 - from: window.width / 2 - easing: type - duration: 1000 - } - } - children: [ - MouseRegion { - onClicked: { anim.running=true } - anchors.fill: parent - } - ] - } - } - } - } -} diff --git a/examples/declarative/loader/loader.pro b/examples/declarative/loader/loader.pro index 9b03b8f..9ee0933 100644 --- a/examples/declarative/loader/loader.pro +++ b/examples/declarative/loader/loader.pro @@ -1,4 +1,5 @@ -SOURCES = main.cpp +SOURCES = main.cpp qmlfolderlistmodel.cpp +HEADERS = qmlfolderlistmodel.h RESOURCES = loader.qrc QT += script declarative network @@ -12,7 +13,7 @@ symbian { # TARGET.UID3 = include($$QT_SOURCE_TREE/examples/symbianpkgrules.pri) TARGET.EPOCHEAPSIZE = 100000 20000000 - HEADERS += $$QT_SOURCE_TREE/examples/network/ftp/sym_iap_util.h + HEADERS += $$QT_SOURCE_TREE/examples/network/qftp/sym_iap_util.h LIBS += -lesock -lconnmon -linsock TARGET.CAPABILITY = NetworkServices } diff --git a/src/declarative/extra/qmlfolderlistmodel.cpp b/examples/declarative/loader/qmlfolderlistmodel.cpp index 0e5c275..4de0edf 100644 --- a/src/declarative/extra/qmlfolderlistmodel.cpp +++ b/examples/declarative/loader/qmlfolderlistmodel.cpp @@ -39,15 +39,12 @@ ** ****************************************************************************/ -#include "private/qobject_p.h" -#include <QDirModel> -#include <qdebug.h> #include "qmlfolderlistmodel.h" +#include <QDirModel> +#include <QDebug> #include <QtDeclarative/qmlcontext.h> -QT_BEGIN_NAMESPACE - -class QmlFolderListModelPrivate : public QObjectPrivate +class QmlFolderListModelPrivate { public: QmlFolderListModelPrivate() @@ -113,9 +110,9 @@ public: */ QmlFolderListModel::QmlFolderListModel(QObject *parent) - : QListModelInterface(*(new QmlFolderListModelPrivate), parent) + : QListModelInterface(parent) { - Q_D(QmlFolderListModel); + d = new QmlFolderListModelPrivate; d->model.setFilter(QDir::AllDirs | QDir::Files | QDir::Drives | QDir::NoDotAndDotDot); connect(&d->model, SIGNAL(rowsInserted(const QModelIndex&,int,int)) , this, SLOT(inserted(const QModelIndex&,int,int))); @@ -129,12 +126,12 @@ QmlFolderListModel::QmlFolderListModel(QObject *parent) QmlFolderListModel::~QmlFolderListModel() { + delete d; } QHash<int,QVariant> QmlFolderListModel::data(int index, const QList<int> &roles) const { Q_UNUSED(roles); - Q_D(const QmlFolderListModel); QHash<int,QVariant> folderData; QModelIndex modelIndex = d->model.index(index, 0, d->folderIndex); if (modelIndex.isValid()) { @@ -147,7 +144,6 @@ QHash<int,QVariant> QmlFolderListModel::data(int index, const QList<int> &roles) int QmlFolderListModel::count() const { - Q_D(const QmlFolderListModel); return d->count; } @@ -180,13 +176,11 @@ QString QmlFolderListModel::toString(int role) const */ QUrl QmlFolderListModel::folder() const { - Q_D(const QmlFolderListModel); return d->folder; } void QmlFolderListModel::setFolder(const QUrl &folder) { - Q_D(QmlFolderListModel); if (folder == d->folder) return; QModelIndex index = d->model.index(folder.toLocalFile()); @@ -199,7 +193,6 @@ void QmlFolderListModel::setFolder(const QUrl &folder) QUrl QmlFolderListModel::parentFolder() const { - Q_D(const QmlFolderListModel); int pos = d->folder.path().lastIndexOf(QLatin1Char('/')); if (pos == -1) return QUrl(); @@ -224,20 +217,17 @@ QUrl QmlFolderListModel::parentFolder() const */ QStringList QmlFolderListModel::nameFilters() const { - Q_D(const QmlFolderListModel); return d->nameFilters; } void QmlFolderListModel::setNameFilters(const QStringList &filters) { - Q_D(QmlFolderListModel); d->nameFilters = filters; d->model.setNameFilters(d->nameFilters); } void QmlFolderListModel::componentComplete() { - Q_D(QmlFolderListModel); if (!d->folder.isValid() || !QDir().exists(d->folder.toLocalFile())) setFolder(QUrl(QLatin1String("file://")+QDir::currentPath())); @@ -247,13 +237,11 @@ void QmlFolderListModel::componentComplete() QmlFolderListModel::SortField QmlFolderListModel::sortField() const { - Q_D(const QmlFolderListModel); return d->sortField; } void QmlFolderListModel::setSortField(SortField field) { - Q_D(QmlFolderListModel); if (field != d->sortField) { d->sortField = field; d->updateSorting(); @@ -262,13 +250,11 @@ void QmlFolderListModel::setSortField(SortField field) bool QmlFolderListModel::sortReversed() const { - Q_D(const QmlFolderListModel); return d->sortReversed; } void QmlFolderListModel::setSortReversed(bool rev) { - Q_D(QmlFolderListModel); if (rev != d->sortReversed) { d->sortReversed = rev; d->updateSorting(); @@ -283,7 +269,6 @@ void QmlFolderListModel::setSortReversed(bool rev) */ bool QmlFolderListModel::isFolder(int index) const { - Q_D(const QmlFolderListModel); if (index != -1) { QModelIndex idx = d->model.index(index, 0, d->folderIndex); if (idx.isValid()) @@ -294,7 +279,6 @@ bool QmlFolderListModel::isFolder(int index) const void QmlFolderListModel::refresh() { - Q_D(QmlFolderListModel); d->folderIndex = QModelIndex(); if (d->count) { int tmpCount = d->count; @@ -310,7 +294,6 @@ void QmlFolderListModel::refresh() void QmlFolderListModel::inserted(const QModelIndex &index, int start, int end) { - Q_D(QmlFolderListModel); if (index == d->folderIndex) { d->count = d->model.rowCount(d->folderIndex); emit itemsInserted(start, end - start + 1); @@ -319,7 +302,6 @@ void QmlFolderListModel::inserted(const QModelIndex &index, int start, int end) void QmlFolderListModel::removed(const QModelIndex &index, int start, int end) { - Q_D(QmlFolderListModel); if (index == d->folderIndex) { d->count = d->model.rowCount(d->folderIndex); emit itemsRemoved(start, end - start + 1); @@ -328,7 +310,6 @@ void QmlFolderListModel::removed(const QModelIndex &index, int start, int end) void QmlFolderListModel::dataChanged(const QModelIndex &start, const QModelIndex &end) { - Q_D(QmlFolderListModel); qDebug() << "data changed"; if (start.parent() == d->folderIndex) emit itemsChanged(start.row(), end.row() - start.row() + 1, roles()); @@ -343,13 +324,11 @@ void QmlFolderListModel::dataChanged(const QModelIndex &start, const QModelIndex */ bool QmlFolderListModel::showDirs() const { - Q_D(const QmlFolderListModel); return d->model.filter() & QDir::AllDirs; } void QmlFolderListModel::setShowDirs(bool on) { - Q_D(QmlFolderListModel); if (!(d->model.filter() & QDir::AllDirs) == !on) return; if (on) @@ -367,13 +346,11 @@ void QmlFolderListModel::setShowDirs(bool on) */ bool QmlFolderListModel::showDotAndDotDot() const { - Q_D(const QmlFolderListModel); return !(d->model.filter() & QDir::NoDotAndDotDot); } void QmlFolderListModel::setShowDotAndDotDot(bool on) { - Q_D(QmlFolderListModel); if (!(d->model.filter() & QDir::NoDotAndDotDot) == on) return; if (on) @@ -391,13 +368,11 @@ void QmlFolderListModel::setShowDotAndDotDot(bool on) */ bool QmlFolderListModel::showOnlyReadable() const { - Q_D(const QmlFolderListModel); return d->model.filter() & QDir::Readable; } void QmlFolderListModel::setShowOnlyReadable(bool on) { - Q_D(QmlFolderListModel); if (!(d->model.filter() & QDir::Readable) == !on) return; if (on) diff --git a/src/declarative/extra/qmlfolderlistmodel.h b/examples/declarative/loader/qmlfolderlistmodel.h index 66e600b..49b508d 100644 --- a/src/declarative/extra/qmlfolderlistmodel.h +++ b/examples/declarative/loader/qmlfolderlistmodel.h @@ -45,17 +45,11 @@ #include <QtDeclarative/qml.h> #include <QtDeclarative/QListModelInterface> -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - class QmlContext; class QModelIndex; class QmlFolderListModelPrivate; -class Q_DECLARATIVE_EXPORT QmlFolderListModel : public QListModelInterface, public QmlParserStatus +class QmlFolderListModel : public QListModelInterface, public QmlParserStatus { Q_OBJECT Q_INTERFACES(QmlParserStatus) @@ -115,14 +109,10 @@ private Q_SLOTS: void dataChanged(const QModelIndex &start, const QModelIndex &end); private: - Q_DECLARE_PRIVATE(QmlFolderListModel) Q_DISABLE_COPY(QmlFolderListModel) + QmlFolderListModelPrivate *d; }; -QT_END_NAMESPACE - QML_DECLARE_TYPE(QmlFolderListModel) -QT_END_HEADER - #endif // QMLFOLDERLISTMODEL_H diff --git a/examples/declarative/sql/README b/examples/declarative/sql/README deleted file mode 100644 index a7baab2..0000000 --- a/examples/declarative/sql/README +++ /dev/null @@ -1,7 +0,0 @@ -Simple demonstration of HTML5-compatible SQL database interface. -Adds another "hello, world" every time you run it. -Database is stored in: - - %APPDATA%/Nokia/Qt/QML/Databases/ (Windows) - - ~/.local/share/Nokia/Qt/QML/Databases/ (Unix) - - ~/Library/Application Support/Nokia/Qt/QML/Databases/ (Mac OS X) -No quota management. diff --git a/examples/declarative/sql/hello.qml b/examples/declarative/sql/hello.qml index 20ea611..dc3ca35 100644 --- a/examples/declarative/sql/hello.qml +++ b/examples/declarative/sql/hello.qml @@ -3,12 +3,17 @@ import Qt 4.6 Text { Script { function findGreetings() { - var db = openDatabase("QmlExampleDB", "", "The Example QML SQL!", 1000000); + var db = openDatabase("QmlExampleDB", "1.0", "The Example QML SQL!", 1000000); db.transaction( function(tx) { + // Create the database if it doesn't already exist tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)', []); + + // Add (another) greeting row tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + + // Show all added greetings tx.executeSql('SELECT * FROM Greeting', [], function(tx, rs) { var r = "" diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 8d34a98..0d5f278 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -228,7 +228,11 @@ void QUnifiedTimer::updateAnimationsTime() void QUnifiedTimer::restartAnimationTimer() { - if (!animationTimer.isActive()) { + if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty()) { + int closestTimeToFinish = closestPauseAnimationTimeToFinish(); + animationTimer.start(closestTimeToFinish, this); + isPauseTimerActive = true; + } else if (!animationTimer.isActive() || isPauseTimerActive) { animationTimer.start(timingInterval, this); isPauseTimerActive = false; } @@ -274,11 +278,11 @@ void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation, bool isTopL void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) { - unregisterRunningAnimation(animation); - if (!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer) return; + unregisterRunningAnimation(animation); + int idx = animations.indexOf(animation); if (idx != -1) { animations.removeAt(idx); @@ -314,6 +318,7 @@ void QUnifiedTimer::unregisterRunningAnimation(QAbstractAnimation *animation) runningPauseAnimations.removeOne(animation); else runningLeafAnimations--; + Q_ASSERT(runningLeafAnimations >= 0); } int QUnifiedTimer::closestPauseAnimationTimeToFinish() @@ -324,9 +329,9 @@ int QUnifiedTimer::closestPauseAnimationTimeToFinish() int timeToFinish; if (animation->direction() == QAbstractAnimation::Forward) - timeToFinish = animation->duration() - QAbstractAnimationPrivate::get(animation)->currentTime; + timeToFinish = animation->duration() - animation->currentTime(); else - timeToFinish = QAbstractAnimationPrivate::get(animation)->totalCurrentTime; + timeToFinish = animation->currentTime(); if (timeToFinish < closestTimeToFinish) closestTimeToFinish = timeToFinish; @@ -644,13 +649,13 @@ int QAbstractAnimation::currentLoop() const */ int QAbstractAnimation::totalDuration() const { - Q_D(const QAbstractAnimation); - if (d->loopCount < 0) - return -1; int dura = duration(); - if (dura == -1) + if (dura <= 0) + return dura; + int loopcount = loopCount(); + if (loopcount < 0) return -1; - return dura * d->loopCount; + return dura * loopcount; } /*! @@ -681,7 +686,7 @@ void QAbstractAnimation::setCurrentTime(int msecs) // Calculate new time and loop. int dura = duration(); - int totalDura = (d->loopCount < 0 || dura == -1) ? -1 : dura * d->loopCount; + int totalDura = dura <= 0 ? dura : ((d->loopCount < 0) ? -1 : dura * d->loopCount); if (totalDura != -1) msecs = qMin(totalDura, msecs); d->totalCurrentTime = msecs; diff --git a/src/declarative/debugger/qmldebug.cpp b/src/declarative/debugger/qmldebug.cpp index 1ef7503..2537ec0 100644 --- a/src/declarative/debugger/qmldebug.cpp +++ b/src/declarative/debugger/qmldebug.cpp @@ -32,10 +32,12 @@ public: static void remove(QmlEngineDebug *, QmlDebugEnginesQuery *); static void remove(QmlEngineDebug *, QmlDebugRootContextQuery *); static void remove(QmlEngineDebug *, QmlDebugObjectQuery *); + static void remove(QmlEngineDebug *, QmlDebugExpressionQuery *); QHash<int, QmlDebugEnginesQuery *> enginesQuery; QHash<int, QmlDebugRootContextQuery *> rootContextQuery; QHash<int, QmlDebugObjectQuery *> objectQuery; + QHash<int, QmlDebugExpressionQuery *> expressionQuery; QHash<int, QmlDebugWatch *> watched; }; @@ -81,6 +83,12 @@ void QmlEngineDebugPrivate::remove(QmlEngineDebug *c, QmlDebugObjectQuery *q) p->objectQuery.remove(q->m_queryId); } +void QmlEngineDebugPrivate::remove(QmlEngineDebug *c, QmlDebugExpressionQuery *q) +{ + QmlEngineDebugPrivate *p = (QmlEngineDebugPrivate *)QObjectPrivate::get(c); + p->expressionQuery.remove(q->m_queryId); +} + Q_DECLARE_METATYPE(QmlDebugObjectReference); void QmlEngineDebugPrivate::decode(QDataStream &ds, QmlDebugObjectReference &o, bool simple) @@ -109,6 +117,7 @@ void QmlEngineDebugPrivate::decode(QDataStream &ds, QmlDebugObjectReference &o, prop.m_name = data.name; prop.m_binding = data.binding; prop.m_hasNotifySignal = data.hasNotifySignal; + prop.m_valueTypeName = data.valueTypeName; if (data.type == QmlEngineDebugServer::QmlObjectProperty::Basic) prop.m_value = data.value; else if (data.type == QmlEngineDebugServer::QmlObjectProperty::Object) { @@ -116,7 +125,6 @@ void QmlEngineDebugPrivate::decode(QDataStream &ds, QmlDebugObjectReference &o, obj.m_debugId = prop.m_value.toInt(); prop.m_value = qVariantFromValue(obj); } - o.m_properties << prop; } @@ -212,6 +220,19 @@ void QmlEngineDebugPrivate::message(const QByteArray &data) query->m_client = 0; query->setState(QmlDebugQuery::Completed); + } else if (type == "EVAL_EXPRESSION_R") { + int queryId; + QVariant result; + ds >> queryId >> result; + + QmlDebugExpressionQuery *query = expressionQuery.value(queryId); + if (!query) + return; + expressionQuery.remove(queryId); + + query->m_result = result; + query->m_client = 0; + query->setState(QmlDebugQuery::Completed); } else if (type == "WATCH_PROPERTY_R") { int queryId; bool ok; @@ -267,7 +288,6 @@ QmlDebugPropertyWatch *QmlEngineDebug::addWatch(const QmlDebugPropertyReference QmlDebugPropertyWatch *watch = new QmlDebugPropertyWatch(parent); if (d->client->isConnected()) { - //query->m_client = this; int queryId = d->getId(); watch->m_queryId = queryId; watch->m_objectDebugId = property.objectDebugId(); @@ -445,6 +465,29 @@ QmlDebugObjectQuery *QmlEngineDebug::queryObjectRecursive(const QmlDebugObjectRe return query; } +QmlDebugExpressionQuery *QmlEngineDebug::queryExpressionResult(int objectDebugId, const QString &expr, QObject *parent) +{ + Q_D(QmlEngineDebug); + + QmlDebugExpressionQuery *query = new QmlDebugExpressionQuery(parent); + if (d->client->isConnected() && objectDebugId != -1) { + query->m_client = this; + query->m_expr = expr; + int queryId = d->getId(); + query->m_queryId = queryId; + d->expressionQuery.insert(queryId, query); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("EVAL_EXPRESSION") << queryId << objectDebugId << expr; + d->client->sendMessage(message); + } else { + query->m_state = QmlDebugQuery::Error; + } + + return query; +} + QmlDebugWatch::QmlDebugWatch(QObject *parent) : QObject(parent), m_state(Waiting), m_queryId(-1), m_objectDebugId(-1) { @@ -566,6 +609,27 @@ QmlDebugObjectReference QmlDebugObjectQuery::object() const return m_object; } +QmlDebugExpressionQuery::QmlDebugExpressionQuery(QObject *parent) +: QmlDebugQuery(parent), m_client(0), m_queryId(-1) +{ +} + +QmlDebugExpressionQuery::~QmlDebugExpressionQuery() +{ + if (m_client && m_queryId != -1) + QmlEngineDebugPrivate::remove(m_client, this); +} + +QString QmlDebugExpressionQuery::expression() const +{ + return m_expr; +} + +QVariant QmlDebugExpressionQuery::result() const +{ + return m_result; +} + QmlDebugEngineReference::QmlDebugEngineReference() : m_debugId(-1) { @@ -748,13 +812,17 @@ QmlDebugPropertyReference::QmlDebugPropertyReference() } QmlDebugPropertyReference::QmlDebugPropertyReference(const QmlDebugPropertyReference &o) -: m_objectDebugId(o.m_objectDebugId), m_name(o.m_name), m_value(o.m_value), m_binding(o.m_binding), m_hasNotifySignal(o.m_hasNotifySignal) +: m_objectDebugId(o.m_objectDebugId), m_name(o.m_name), m_value(o.m_value), + m_valueTypeName(o.m_valueTypeName), m_binding(o.m_binding), + m_hasNotifySignal(o.m_hasNotifySignal) { } QmlDebugPropertyReference &QmlDebugPropertyReference::operator=(const QmlDebugPropertyReference &o) { - m_objectDebugId = o.m_objectDebugId; m_name = o.m_name; m_value = o.m_value; m_binding = o.m_binding; m_hasNotifySignal = o.m_hasNotifySignal; + m_objectDebugId = o.m_objectDebugId; m_name = o.m_name; m_value = o.m_value; + m_valueTypeName = o.m_valueTypeName; m_binding = o.m_binding; + m_hasNotifySignal = o.m_hasNotifySignal; return *this; } @@ -768,6 +836,11 @@ QString QmlDebugPropertyReference::name() const return m_name; } +QString QmlDebugPropertyReference::valueTypeName() const +{ + return m_valueTypeName; +} + QVariant QmlDebugPropertyReference::value() const { return m_value; diff --git a/src/declarative/debugger/qmldebug.h b/src/declarative/debugger/qmldebug.h index 681ee08..bd076ff 100644 --- a/src/declarative/debugger/qmldebug.h +++ b/src/declarative/debugger/qmldebug.h @@ -12,6 +12,7 @@ class QmlDebugObjectExpressionWatch; class QmlDebugEnginesQuery; class QmlDebugRootContextQuery; class QmlDebugObjectQuery; +class QmlDebugExpressionQuery; class QmlDebugPropertyReference; class QmlDebugContextReference; class QmlDebugObjectReference; @@ -44,6 +45,9 @@ public: QObject *parent = 0); QmlDebugObjectQuery *queryObjectRecursive(const QmlDebugObjectReference &, QObject *parent = 0); + QmlDebugExpressionQuery *queryExpressionResult(int objectDebugId, + const QString &expr, + QObject *parent = 0); private: Q_DECLARE_PRIVATE(QmlEngineDebug) @@ -228,6 +232,7 @@ public: int objectDebugId() const; QString name() const; QVariant value() const; + QString valueTypeName() const; QString binding() const; bool hasNotifySignal() const; @@ -236,6 +241,7 @@ private: int m_objectDebugId; QString m_name; QVariant m_value; + QString m_valueTypeName; QString m_binding; bool m_hasNotifySignal; }; @@ -287,4 +293,22 @@ private: }; +class Q_DECLARATIVE_EXPORT QmlDebugExpressionQuery : public QmlDebugQuery +{ +Q_OBJECT +public: + virtual ~QmlDebugExpressionQuery(); + QString expression() const; + QVariant result() const; +private: + friend class QmlEngineDebug; + friend class QmlEngineDebugPrivate; + QmlDebugExpressionQuery(QObject *); + QmlEngineDebug *m_client; + int m_queryId; + QString m_expr; + QVariant m_result; + +}; + #endif // QMLDEBUG_H diff --git a/src/declarative/extra/extra.pri b/src/declarative/extra/extra.pri index 6730550..502504a 100644 --- a/src/declarative/extra/extra.pri +++ b/src/declarative/extra/extra.pri @@ -3,11 +3,9 @@ SOURCES += \ extra/qmlnumberformatter.cpp \ extra/qmldatetimeformatter.cpp \ extra/qfxintegermodel.cpp \ - extra/qmlfolderlistmodel.cpp \ extra/qfxanimatedimageitem.cpp \ extra/qfxparticles.cpp \ extra/qmlbehavior.cpp \ - extra/qbindablemap.cpp \ extra/qmlfontloader.cpp HEADERS += \ @@ -15,12 +13,10 @@ HEADERS += \ extra/qmlnumberformatter.h \ extra/qmldatetimeformatter.h \ extra/qfxintegermodel.h \ - extra/qmlfolderlistmodel.h \ extra/qfxanimatedimageitem.h \ extra/qfxanimatedimageitem_p.h \ extra/qfxparticles.h \ extra/qmlbehavior.h \ - extra/qbindablemap.h \ extra/qmlfontloader.h contains(QT_CONFIG, xmlpatterns) { diff --git a/src/declarative/extra/qbindablemap.cpp b/src/declarative/extra/qbindablemap.cpp deleted file mode 100644 index 5254e2a..0000000 --- a/src/declarative/extra/qbindablemap.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/**************************************************************************** -** -** 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 "qbindablemap.h" -#include <qmlopenmetaobject.h> -#include <QDebug> - -QT_BEGIN_NAMESPACE - -//QBindableMapMetaObject lets us listen for changes coming from QML -//so we can emit the changed signal. -class QBindableMapMetaObject : public QmlOpenMetaObject -{ -public: - QBindableMapMetaObject(QBindableMap *obj) : QmlOpenMetaObject(obj) - { - map = obj; - } - -protected: - virtual void propertyWrite(int index) - { - map->emitChanged(QString::fromUtf8(name(index))); - } - -private: - QBindableMap *map; -}; - -/*! - \class QBindableMap - \brief The QBindableMap class allows you to set key-value pairs that can be used in bindings. - - QBindableMap provides a convenient way to expose domain data to the UI layer. - The following example shows how you might declare data in C++ and then - access it in QML. - - Setup in C++: - \code - //create our data - QBindableMap ownerData; - ownerData.setValue("name", QVariant(QString("John Smith"))); - ownerData.setValue("phone", QVariant(QString("555-5555"))); - - //expose it to the UI layer - QmlContext *ctxt = view->bindContext(); - ctxt->setProperty("owner", &data); - \endcode - - Then, in QML: - \code - Text { text: owner.name } - Text { text: owner.phone } - \endcode - - The binding is dynamic - whenever a key's value is updated, anything bound to that - key will be updated as well. - - To detect value changes made in the UI layer you can connect to the changed() signal. - However, note that changed() is \b NOT emitted when changes are made by calling setValue() - or clearValue() - it is only emitted when a value is updated from QML. -*/ - -// is there a more efficient way to store/return keys? -// (or should we just provide an iterator or something else instead?) -// can we provide a way to clear keys? -// do we want to make any claims regarding key ordering? -// should we have signals for insertion and and deletion -- becoming more model like -// should we emit change for our own changes as well? -// Bug or Feature?: values can be created in QML (owner.somethingElse = "Hello") will create somethingElse property. (need to verify if this is actually the case) -// Bug or Feature?: all values are read-write (there are no read-only values) - -/*! - Constructs a bindable map with parent object \a parent. -*/ -QBindableMap::QBindableMap(QObject *parent) -: QObject(parent) -{ - m_mo = new QBindableMapMetaObject(this); -} - -/*! - Destroys the bindable map. -*/ -QBindableMap::~QBindableMap() -{ -} - -/*! - Clears the value (if any) associated with \a key. -*/ -void QBindableMap::clearValue(const QString &key) -{ - //m_keys.remove(); //### - m_mo->setValue(key.toUtf8(), QVariant()); - //emit changed(key); -} - -/*! - Returns the value associated with \a key. - - If no value has been set for this key (or if the value has been cleared), - an invalid QVariant is returned. -*/ -QVariant QBindableMap::value(const QString &key) const -{ - return m_mo->value(key.toUtf8()); -} - -/*! - Sets the value associated with \a key to \a value. - - If the key doesn't exist, it is automatically created. -*/ -void QBindableMap::setValue(const QString &key, QVariant value) -{ - if (!m_keys.contains(key)) - m_keys.append(key); - m_mo->setValue(key.toUtf8(), value); - //emit changed(key); -} - -/*! - Returns the list of keys. - - Keys that have been cleared will still appear in this list, even though their - associated values are invalid QVariants. -*/ -QStringList QBindableMap::keys() const -{ - return m_keys; -} - -/*! - \fn void QBindableMap::changed(const QString &key) - This signal is emitted whenever one of the values in the map is changed. \a key - is the key corresponding to the value that was changed. - */ - -void QBindableMap::emitChanged(const QString &key) -{ - emit changed(key); -} -QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxlistview.cpp b/src/declarative/fx/qfxlistview.cpp index 4fb0ec1..2ea1cb7 100644 --- a/src/declarative/fx/qfxlistview.cpp +++ b/src/declarative/fx/qfxlistview.cpp @@ -864,6 +864,52 @@ QFxListView::~QFxListView() } /*! + \qmlattachedproperty bool ListView::isCurrentItem + This attched property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current item, for example: + + \snippet doc/src/snippets/declarative/listview/highlight.qml 0 +*/ + +/*! + \qmlattachedproperty ListView ListView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty string ListView::prevSection + This attached property holds the section of the previous element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::sectionExpression}{sectionExpression} property. +*/ + +/*! + \qmlattachedproperty string ListView::section + This attached property holds the section of this element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::sectionExpression}{sectionExpression} property. +*/ + +/*! + \qmlattachedproperty bool ListView::delayRemove + This attached property holds whether the delegate may be destroyed. + + It is attached to each instance of the delegate. + + It is sometimes necessary to delay the destruction of an item + until an animation completes. +*/ + +/*! \qmlproperty model ListView::model This property holds the model providing data for the list. diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 58a18a2..b6e86a8 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -40,6 +40,7 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlpropertycache.cpp \ qml/qmlintegercache.cpp \ qml/qmltypenamecache.cpp \ + qml/qmlscriptstring.cpp \ qml/qmlobjectscriptclass.cpp \ qml/qmlcontextscriptclass.cpp \ qml/qmlglobalscriptclass.cpp \ @@ -102,6 +103,7 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlpropertycache_p.h \ qml/qmlintegercache_p.h \ qml/qmltypenamecache_p.h \ + qml/qmlscriptstring.h \ qml/qmlobjectscriptclass_p.h \ qml/qmlcontextscriptclass_p.h \ qml/qmlglobalscriptclass_p.h \ diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index e2fd7cb..726051e 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -69,6 +69,7 @@ #include <private/qmlexpression_p.h> #include "qmlmetaproperty_p.h" #include "qmlrewrite_p.h" +#include <QtDeclarative/qmlscriptstring.h> #include "qmlscriptparser_p.h" @@ -860,6 +861,17 @@ void QmlCompiler::genObject(QmlParser::Object *obj) void QmlCompiler::genObjectBody(QmlParser::Object *obj) { + typedef QPair<Property *, int> PropPair; + foreach(const PropPair &prop, obj->scriptStringProperties) { + QmlInstruction ss; + ss.type = QmlInstruction::StoreScriptString; + ss.storeScriptString.propertyIndex = prop.first->index; + ss.storeScriptString.value = + output->indexForString(prop.first->values.at(0)->value.asScript()); + ss.storeScriptString.scope = prop.second; + output->bytecode << ss; + } + bool seenDefer = false; foreach(Property *prop, obj->valueProperties) { if (prop->isDeferred) { @@ -1371,6 +1383,10 @@ bool QmlCompiler::buildProperty(QmlParser::Property *prop, COMPILE_CHECK(buildListProperty(prop, obj, ctxt)); + } else if (prop->type == qMetaTypeId<QmlScriptString>()) { + + COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt)); + } else { COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); @@ -1823,6 +1839,22 @@ bool QmlCompiler::buildListProperty(QmlParser::Property *prop, return true; } +// Compiles an assignment to a QmlScriptString property +bool QmlCompiler::buildScriptStringProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + const BindingContext &ctxt) +{ + if (prop->values.count() > 1) + COMPILE_EXCEPTION(prop->values.at(1), qApp->translate("QmlCompiler", "Cannot assign multiple values to a script property")); + + if (prop->values.at(0)->object || !prop->values.at(0)->value.isScript()) + COMPILE_EXCEPTION(prop->values.at(0), qApp->translate("QmlCompiler", "Invalid property assignment: script expected")); + + obj->addScriptStringProperty(prop, ctxt.stack); + + return true; +} + // Compile regular property assignments of the form "property: <value>" // // ### The following problems exist diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index cff4937..8a9ca9c 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -197,6 +197,9 @@ private: bool buildListProperty(QmlParser::Property *prop, QmlParser::Object *obj, const BindingContext &ctxt); + bool buildScriptStringProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + const BindingContext &ctxt); bool buildPropertyAssignment(QmlParser::Property *prop, QmlParser::Object *obj, const BindingContext &ctxt); diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 528e8c9..9fad80b 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -76,9 +76,10 @@ #include <QtCore/qdir.h> #include <QtGui/qcolor.h> #include <QtGui/qvector3d.h> +#include <QtGui/qsound.h> #include <qmlcomponent.h> -#include "private/qmlcomponentjs_p.h" -#include "private/qmlmetaproperty_p.h" +#include <private/qmlcomponentjs_p.h> +#include <private/qmlmetaproperty_p.h> #include <private/qmlbinding_p.h> #include <private/qmlvme_p.h> #include <private/qmlenginedebug_p.h> @@ -87,6 +88,7 @@ #include <private/qmlsqldatabase_p.h> #include <private/qmltypenamescriptclass_p.h> #include <private/qmllistscriptclass_p.h> +#include <QtDeclarative/qmlscriptstring.h> #ifdef Q_OS_WIN // for %APPDATA% #include "qt_windows.h" @@ -153,12 +155,14 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) qtObject.setProperty(QLatin1String("darker"), scriptEngine.newFunction(QmlEnginePrivate::darker, 1)); qtObject.setProperty(QLatin1String("tint"), scriptEngine.newFunction(QmlEnginePrivate::tint, 2)); + //misc methods + qtObject.setProperty(QLatin1String("playSound"), scriptEngine.newFunction(QmlEnginePrivate::playSound, 1)); + scriptEngine.globalObject().setProperty(QLatin1String("createQmlObject"), scriptEngine.newFunction(QmlEnginePrivate::createQmlObject, 1)); scriptEngine.globalObject().setProperty(QLatin1String("createComponent"), scriptEngine.newFunction(QmlEnginePrivate::createComponent, 1)); - //scriptEngine.globalObject().setScriptClass(new QmlGlobalScriptClass(&scriptEngine)); globalClass = new QmlGlobalScriptClass(&scriptEngine); } @@ -216,6 +220,9 @@ Q_GLOBAL_STATIC(QmlEngineDebugServer, qmlEngineDebugServer); void QmlEnginePrivate::init() { Q_Q(QmlEngine); + qRegisterMetaType<QVariant>("QVariant"); + qRegisterMetaType<QmlScriptString>("QmlScriptString"); + scriptEngine.installTranslatorFunctions(); contextClass = new QmlContextScriptClass(q); objectClass = new QmlObjectScriptClass(q); @@ -281,8 +288,6 @@ QmlEngine::QmlEngine(QObject *parent) { Q_D(QmlEngine); d->init(); - - qRegisterMetaType<QVariant>("QVariant"); } /*! @@ -401,7 +406,12 @@ QmlContext *QmlEngine::contextForObject(const QObject *object) QmlDeclarativeData *data = static_cast<QmlDeclarativeData *>(priv->declarativeData); - return data?data->context:0; + if (!data) + return 0; + else if (data->outerContext) + return data->outerContext; + else + return data->context; } /*! @@ -565,6 +575,17 @@ QScriptValue QmlEnginePrivate::qmlScriptObject(QObject* object, } /*! + Returns the QmlContext for the executing QScript \a ctxt. +*/ +QmlContext *QmlEnginePrivate::getContext(QScriptContext *ctxt) +{ + QScriptValue scopeNode = QScriptDeclarativeClass::scopeChainValue(ctxt, -3); + Q_ASSERT(scopeNode.isValid()); + Q_ASSERT(QScriptDeclarativeClass::scriptClass(scopeNode) == contextClass); + return contextClass->contextFromValue(scopeNode); +} + +/*! This function is intended for use inside QML only. In C++ just create a component object as usual. @@ -626,7 +647,7 @@ QScriptValue QmlEnginePrivate::createComponent(QScriptContext *ctxt, static_cast<QmlScriptEngine*>(engine)->p; QmlEngine* activeEngine = activeEnginePriv->q_func(); - QmlContext* context = activeEnginePriv->currentExpression->context(); + QmlContext* context = activeEnginePriv->getContext(ctxt); if(ctxt->argumentCount() != 1) { c = new QmlComponentJS(activeEngine); }else{ @@ -647,7 +668,7 @@ QScriptValue QmlEnginePrivate::createComponent(QScriptContext *ctxt, Example (where targetItem is the id of an existing QML item): \code - newObject = createQmlObject('Rectangle {color: "red"; width: 20; height: 20}', + newObject = createQmlObject('import Qt 4.6; Rectangle {color: "red"; width: 20; height: 20}', targetItem, "dynamicSnippet1"); \endcode @@ -677,9 +698,16 @@ QScriptValue QmlEnginePrivate::createQmlObject(QScriptContext *ctxt, QScriptEngi QUrl url; if(ctxt->argumentCount() > 2) url = QUrl(ctxt->argument(2).toString()); + else + url = QUrl(QLatin1String("DynamicQML")); QObject *parentArg = activeEnginePriv->objectClass->toQObject(ctxt->argument(1)); QmlContext *qmlCtxt = qmlContext(parentArg); - url = qmlCtxt->resolvedUrl(url); + if (url.isEmpty()) { + url = qmlCtxt->resolvedUrl(QUrl(QLatin1String("<Unknown File>"))); + } else { + url = qmlCtxt->resolvedUrl(url); + } + QmlComponent component(activeEngine, qml.toUtf8(), url); if(component.isError()) { QList<QmlError> errors = component.errors(); @@ -836,6 +864,30 @@ QScriptValue QmlEnginePrivate::darker(QScriptContext *ctxt, QScriptEngine *engin return qScriptValueFromValue(engine, qVariantFromValue(color)); } +QScriptValue QmlEnginePrivate::playSound(QScriptContext *ctxt, QScriptEngine *engine) +{ + if (ctxt->argumentCount() < 1) + return engine->undefinedValue(); + + QUrl url(ctxt->argument(0).toString()); + + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + if (url.isRelative()) { + QmlContext *context = enginePriv->getContext(ctxt); + if (!context) + return engine->undefinedValue(); + + url = context->resolvedUrl(url); + } + + if (url.scheme() == QLatin1String("file")) { + + QSound::play(url.toLocalFile()); + + } + return engine->undefinedValue(); +} + /*! This function allows tinting one color with another. diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index a85ac55..a74854d 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -259,10 +259,13 @@ public: static QScriptValue darker(QScriptContext*, QScriptEngine*); static QScriptValue tint(QScriptContext*, QScriptEngine*); + static QScriptValue playSound(QScriptContext*, QScriptEngine*); + static QScriptEngine *getScriptEngine(QmlEngine *e) { return &e->d_func()->scriptEngine; } static QmlEngine *getEngine(QScriptEngine *e) { return static_cast<QmlScriptEngine*>(e)->p->q_func(); } static QmlEnginePrivate *get(QmlEngine *e) { return e->d_func(); } static QmlEnginePrivate *get(QScriptEngine *e) { return static_cast<QmlScriptEngine*>(e)->p; } + QmlContext *getContext(QScriptContext *); }; diff --git a/src/declarative/qml/qmlenginedebug.cpp b/src/declarative/qml/qmlenginedebug.cpp index e20616a..7178e6c 100644 --- a/src/declarative/qml/qmlenginedebug.cpp +++ b/src/declarative/qml/qmlenginedebug.cpp @@ -80,7 +80,8 @@ QDataStream &operator>>(QDataStream &ds, QDataStream &operator<<(QDataStream &ds, const QmlEngineDebugServer::QmlObjectProperty &data) { - ds << (int)data.type << data.name << data.value << data.binding << data.hasNotifySignal; + ds << (int)data.type << data.name << data.value << data.valueTypeName + << data.binding << data.hasNotifySignal; return ds; } @@ -88,7 +89,8 @@ QDataStream &operator>>(QDataStream &ds, QmlEngineDebugServer::QmlObjectProperty &data) { int type; - ds >> type >> data.name >> data.value >> data.binding >> data.hasNotifySignal; + ds >> type >> data.name >> data.value >> data.valueTypeName + >> data.binding >> data.hasNotifySignal; data.type = (QmlEngineDebugServer::QmlObjectProperty::Type)type; return ds; } @@ -101,6 +103,7 @@ QmlEngineDebugServer::propertyData(QObject *obj, int propIdx) QMetaProperty prop = obj->metaObject()->property(propIdx); rv.type = QmlObjectProperty::Unknown; + rv.valueTypeName = QString::fromUtf8(prop.typeName()); rv.name = prop.name(); rv.hasNotifySignal = prop.hasNotifySignal(); QmlAbstractBinding *binding = QmlMetaProperty(obj, rv.name).binding(); @@ -116,6 +119,7 @@ QmlEngineDebugServer::propertyData(QObject *obj, int propIdx) QmlMetaType::isQmlList(prop.userType())) { rv.type = QmlObjectProperty::List; } + return rv; } @@ -149,7 +153,7 @@ void QmlEngineDebugServer::buildObjectList(QDataStream &message, int ctxtId = QmlDebugService::idForObject(ctxt); message << ctxtName << ctxtId; - + int count = 0; for (QSet<QmlContext *>::ConstIterator iter = p->childContexts.begin(); @@ -184,6 +188,32 @@ void QmlEngineDebugServer::buildObjectList(QDataStream &message, } } +QVariant QmlEngineDebugServer::serializableVariant(const QVariant &value) +{ + if (value.type() < QVariant::UserType) + return value; + + if (!value.toString().isEmpty()) + return value.toString(); + + QVariant v; + if (value.type() == QVariant::UserType || QmlMetaType::isObject(value.userType())) { + QObject *o = QmlMetaType::toQObject(value); + if (o) { + QString objectName = o->objectName(); + if (objectName.isEmpty()) + objectName = QLatin1String("<unnamed>"); + v = QString::fromUtf8(o->metaObject()->className()) + + QLatin1String(": ") + objectName; + } + } + + if (v.isNull()) + v = QString::fromUtf8(value.typeName()); + + return v; +} + QmlEngineDebugServer::QmlObjectData QmlEngineDebugServer::objectData(QObject *object) { @@ -310,29 +340,42 @@ void QmlEngineDebugServer::messageReceived(const QByteArray &message) ds >> queryId; m_watch->removeWatch(queryId); + } else if (type == "EVAL_EXPRESSION") { + int queryId; + int objectId; + QString expr; + + ds >> queryId >> objectId >> expr; + + QObject *object = QmlDebugService::objectForId(objectId); + QmlContext *context = qmlContext(object); + QVariant result; + if (object && context) { + QmlExpression *exprObj = new QmlExpression(context, expr, object); + bool undefined = false; + QVariant value = exprObj->value(&undefined); + if (undefined) + result = QLatin1String("<undefined>"); + else + result = serializableVariant(value); + delete exprObj; + } else { + result = QLatin1String("<unknown context>"); + } + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result; + + sendMessage(reply); } } void QmlEngineDebugServer::propertyChanged(int id, int objectId, const QByteArray &property, const QVariant &value) { QByteArray reply; - QVariant v; + QVariant v = serializableVariant(value); QDataStream rs(&reply, QIODevice::WriteOnly); - - if (value.type() == QVariant::UserType || QmlMetaType::isObject(value.userType())) { - QObject *o = QmlMetaType::toQObject(value); - if (o) { - QString objectName = o->objectName(); - if (objectName.isEmpty()) - objectName = QLatin1String("<unnamed>"); - v = QString::fromUtf8(o->metaObject()->className()) + - QLatin1String(": ") + objectName; - } - if (v.isNull()) - v = value.toString(); - } else { - v = value; - } rs << QByteArray("UPDATE_WATCH") << id << objectId << property << v; diff --git a/src/declarative/qml/qmlenginedebug_p.h b/src/declarative/qml/qmlenginedebug_p.h index e2f903c..075a711 100644 --- a/src/declarative/qml/qmlenginedebug_p.h +++ b/src/declarative/qml/qmlenginedebug_p.h @@ -85,6 +85,7 @@ public: Type type; QString name; QVariant value; + QString valueTypeName; QString binding; bool hasNotifySignal; }; @@ -103,6 +104,7 @@ private: void buildObjectDump(QDataStream &, QObject *, bool); QmlObjectData objectData(QObject *); QmlObjectProperty propertyData(QObject *, int); + QVariant serializableVariant(const QVariant &value); static QList<QmlEngine *> m_engines; QmlWatcher *m_watch; diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp index cfa9469..d2bf4a8 100644 --- a/src/declarative/qml/qmlexpression.cpp +++ b/src/declarative/qml/qmlexpression.cpp @@ -281,7 +281,7 @@ void QmlExpressionPrivate::printException(QScriptEngine *scriptEngine) } } -QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope) +QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope, bool *isUndefined) { #ifdef Q_ENABLE_PERFORMANCE_LOG QFxPerfTimer<QFxPerf::BindValueQt> perfqt; @@ -318,6 +318,9 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope) QScriptValue svalue = data->expressionFunction.call(); + if (isUndefined) + *isUndefined = svalue.isUndefined(); + if (scriptEngine->hasUncaughtException()) printException(scriptEngine); @@ -355,7 +358,7 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope) return rv; } -QVariant QmlExpressionPrivate::value(QObject *secondaryScope) +QVariant QmlExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined) { Q_Q(QmlExpression); @@ -382,7 +385,7 @@ QVariant QmlExpressionPrivate::value(QObject *secondaryScope) if (data->sse.isValid()) { rv = evalSSE(); } else { - rv = evalQtScript(secondaryScope); + rv = evalQtScript(secondaryScope, isUndefined); } ep->currentExpression = lastCurrentExpression; @@ -406,11 +409,14 @@ QVariant QmlExpressionPrivate::value(QObject *secondaryScope) /*! Returns the value of the expression, or an invalid QVariant if the expression is invalid or has an error. + + \a isUndefined is set to true if the expression resulted in an + undefined value. */ -QVariant QmlExpression::value() +QVariant QmlExpression::value(bool *isUndefined) { Q_D(QmlExpression); - return d->value(); + return d->value(0, isUndefined); } /*! diff --git a/src/declarative/qml/qmlexpression.h b/src/declarative/qml/qmlexpression.h index 73682f1..96694d6 100644 --- a/src/declarative/qml/qmlexpression.h +++ b/src/declarative/qml/qmlexpression.h @@ -83,7 +83,7 @@ public: QObject *scopeObject() const; public Q_SLOTS: - QVariant value(); + QVariant value(bool *isUndefined = 0); Q_SIGNALS: virtual void valueChanged(); diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h index d9bb27b..3ec8d1c 100644 --- a/src/declarative/qml/qmlexpression_p.h +++ b/src/declarative/qml/qmlexpression_p.h @@ -140,9 +140,9 @@ public: QmlExpressionData *data; - QVariant value(QObject *secondaryScope = 0); + QVariant value(QObject *secondaryScope = 0, bool *isUndefined = 0); QVariant evalSSE(); - QVariant evalQtScript(QObject *secondaryScope); + QVariant evalQtScript(QObject *secondaryScope, bool *isUndefined = 0); void updateGuards(const QPODVector<QmlEnginePrivate::CapturedProperty> &properties); void clearGuards(); diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index 5265d42..dc18b05 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -119,6 +119,7 @@ public: StoreSignal, /* storeSignal */ StoreScript, /* storeScript */ + StoreScriptString, /* storeScriptString */ // // Unresolved single assignment @@ -245,6 +246,11 @@ public: int value; } storeString; struct { + int propertyIndex; + int value; + int scope; + } storeScriptString; + struct { int value; int fileName; int lineNumber; diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index bae263a..f9e3c50 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -80,6 +80,9 @@ QmlParser::Object::~Object() prop->release(); foreach(Property *prop, valueTypeProperties) prop->release(); + typedef QPair<Property *, int> PropPair; + foreach(const PropPair &prop, scriptStringProperties) + prop.first->release(); foreach(const DynamicProperty &prop, dynamicProperties) if (prop.defaultValue) prop.defaultValue->release(); foreach(Object *obj, scriptBlockObjects) @@ -142,6 +145,13 @@ void QmlParser::Object::addValueTypeProperty(Property *p) valueTypeProperties << p; } +void QmlParser::Object::addScriptStringProperty(Property *p, int stack) +{ + p->addref(); + scriptStringProperties << qMakePair(p, stack); +} + + Property *QmlParser::Object::getProperty(const QByteArray &name, bool create) { if (!properties.contains(name)) { diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index 88d7d77..f0867ea 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -160,11 +160,13 @@ namespace QmlParser void addAttachedProperty(Property *); void addGroupedProperty(Property *); void addValueTypeProperty(Property *); + void addScriptStringProperty(Property *, int = 0); QList<Property *> valueProperties; QList<Property *> signalProperties; QList<Property *> attachedProperties; QList<Property *> groupedProperties; QList<Property *> valueTypeProperties; + QList<QPair<Property *, int> > scriptStringProperties; // Script blocks that were nested under this object QStringList scriptBlocks; diff --git a/src/declarative/qml/qmlscriptstring.cpp b/src/declarative/qml/qmlscriptstring.cpp new file mode 100644 index 0000000..8a6afcc --- /dev/null +++ b/src/declarative/qml/qmlscriptstring.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** 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 "qmlscriptstring.h" + +QT_BEGIN_NAMESPACE + +class QmlScriptStringPrivate : public QSharedData +{ +public: + QmlScriptStringPrivate() : context(0), scope(0) {} + + QmlContext *context; + QObject *scope; + QString script; +}; + +/*! +\class QmlScriptString +\brief The QmlScriptString class encapsulates a script and its context. + +The QmlScriptString is used by properties that want to accept a script "assignment" from QML. + +Normally, the following code would result in a binding being established for the \c script +property. If the property had a type of QmlScriptString, the script - \e {print(1921)} - itself +would be passed to the property and it could choose how to handle it. + +\code +MyType { + script: print(1921) +} +\endcode +*/ + +/*! +Construct an empty instance. +*/ +QmlScriptString::QmlScriptString() +: d(new QmlScriptStringPrivate) +{ +} + +/*! +Copy \a other. +*/ +QmlScriptString::QmlScriptString(const QmlScriptString &other) +: d(other.d) +{ +} + +/*! +\internal +*/ +QmlScriptString::~QmlScriptString() +{ +} + +/*! +Assign \a other to this. +*/ +QmlScriptString &QmlScriptString::operator=(const QmlScriptString &other) +{ + d = other.d; + return *this; +} + +/*! +Return the context for the script. +*/ +QmlContext *QmlScriptString::context() const +{ + return d->context; +} + +/*! +Sets the \a context for the script. +*/ +void QmlScriptString::setContext(QmlContext *context) +{ + d->context = context; +} + +/*! +Returns the scope object for the script. +*/ +QObject *QmlScriptString::scopeObject() const +{ + return d->scope; +} + +/*! +Sets the scope \a object for the script. +*/ +void QmlScriptString::setScopeObject(QObject *object) +{ + d->scope = object; +} + +/*! +Returns the script text. +*/ +QString QmlScriptString::script() const +{ + return d->script; +} + +/*! +Sets the \a script text. +*/ +void QmlScriptString::setScript(const QString &script) +{ + d->script = script; +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmlscriptstring.h b/src/declarative/qml/qmlscriptstring.h new file mode 100644 index 0000000..c6067ce --- /dev/null +++ b/src/declarative/qml/qmlscriptstring.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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 QMLSCRIPTSTRING_H +#define QMLSCRIPTSTRING_H + +#include <QtCore/qstring.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QObject; +class QmlContext; +class QmlScriptStringPrivate; +class Q_DECLARATIVE_EXPORT QmlScriptString +{ +public: + QmlScriptString(); + QmlScriptString(const QmlScriptString &); + ~QmlScriptString(); + + QmlScriptString &operator=(const QmlScriptString &); + + QmlContext *context() const; + void setContext(QmlContext *); + + QObject *scopeObject() const; + void setScopeObject(QObject *); + + QString script() const; + void setScript(const QString &); + +private: + QSharedDataPointer<QmlScriptStringPrivate> d; +}; + +Q_DECLARE_METATYPE(QmlScriptString); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLSCRIPTSTRING_H + diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp index 4a5983d..9e39ffc 100644 --- a/src/declarative/qml/qmlsqldatabase.cpp +++ b/src/declarative/qml/qmlsqldatabase.cpp @@ -340,7 +340,7 @@ static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine * QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath + QLatin1String("/Databases/"); QDir().mkpath(basename); basename += dbid; - database.setDatabaseName(basename+QLatin1String(".sqllite")); + database.setDatabaseName(basename+QLatin1String(".sqlite")); QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat); ini.setValue(QLatin1String("Name"), dbname); ini.setValue(QLatin1String("Version"), dbversion); diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 1f3903d..7f673a2 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -67,6 +67,7 @@ #include <private/qmlbinding_p.h> #include <private/qmlcontext_p.h> #include <private/qmlbindingoptimizations_p.h> +#include <QtDeclarative/qmlscriptstring.h> QT_BEGIN_NAMESPACE @@ -552,6 +553,21 @@ QObject *QmlVME::run(QStack<QObject *> &stack, QmlContext *ctxt, } break; + case QmlInstruction::StoreScriptString: + { + QObject *target = stack.top(); + QObject *scope = stack.at(stack.count() - 1 - instr.storeScriptString.scope); + QmlScriptString ss; + ss.setContext(ctxt); + ss.setScopeObject(scope); + ss.setScript(primitives.at(instr.storeScriptString.value)); + + void *a[] = { &ss, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeScriptString.propertyIndex, a); + } + break; + case QmlInstruction::BeginObject: { QObject *target = stack.top(); diff --git a/src/declarative/qml/qmlvmemetaobject.cpp b/src/declarative/qml/qmlvmemetaobject.cpp index e5acc51..62a2a6b 100644 --- a/src/declarative/qml/qmlvmemetaobject.cpp +++ b/src/declarative/qml/qmlvmemetaobject.cpp @@ -55,7 +55,7 @@ QmlVMEMetaObject::QmlVMEMetaObject(QObject *obj, const QMetaObject *other, const QmlVMEMetaData *meta, QmlRefCount *rc) -: object(obj), ref(rc), metaData(meta), parent(0) +: object(obj), ref(rc), ctxt(qmlContext(obj)), metaData(meta), parent(0) { if (ref) ref->addref(); @@ -251,7 +251,6 @@ int QmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) (const QChar *)(((const char*)metaData) + data->bodyOffset); QString code = QString::fromRawData(body, data->bodyLength); - QmlContext *ctxt = qmlContext(object); if (0 == (metaData->methodData() + id)->parameterCount) { QmlExpression expr(ctxt, code, object); diff --git a/src/declarative/qml/qmlvmemetaobject_p.h b/src/declarative/qml/qmlvmemetaobject_p.h index d376f4c..de46853 100644 --- a/src/declarative/qml/qmlvmemetaobject_p.h +++ b/src/declarative/qml/qmlvmemetaobject_p.h @@ -112,6 +112,7 @@ protected: private: QObject *object; QmlRefCount *ref; + QGuard<QmlContext> ctxt; const QmlVMEMetaData *metaData; int propOffset; diff --git a/src/declarative/util/qmlanimation_p.h b/src/declarative/util/qmlanimation_p.h index 4f6edb7..22c4e2d 100644 --- a/src/declarative/util/qmlanimation_p.h +++ b/src/declarative/util/qmlanimation_p.h @@ -92,6 +92,7 @@ private: //performs an action of type QAbstractAnimationAction class QActionAnimation : public QAbstractAnimation { + Q_OBJECT public: QActionAnimation(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped) {} QActionAnimation(QAbstractAnimationAction *action, QObject *parent = 0) @@ -126,6 +127,7 @@ private: //animates QmlTimeLineValue (assumes start and end values will be reals or compatible) class QmlTimeLineValueAnimator : public QVariantAnimation { + Q_OBJECT public: QmlTimeLineValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), fromSourced(0), policy(KeepWhenStopped) {} void setAnimValue(QmlTimeLineValue *value, DeletionPolicy p) diff --git a/src/declarative/util/qmlopenmetaobject.cpp b/src/declarative/util/qmlopenmetaobject.cpp index 7305362..11648f6 100644 --- a/src/declarative/util/qmlopenmetaobject.cpp +++ b/src/declarative/util/qmlopenmetaobject.cpp @@ -136,6 +136,14 @@ QVariant QmlOpenMetaObject::value(const QByteArray &name) const return d->data.at(*iter); } +QVariant &QmlOpenMetaObject::operator[](const QByteArray &name) +{ + QHash<QByteArray, int>::ConstIterator iter = d->names.find(name); + Q_ASSERT(iter != d->names.end()); + + return d->data[*iter]; +} + void QmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) { QHash<QByteArray, int>::ConstIterator iter = d->names.find(name); diff --git a/src/declarative/util/qmlopenmetaobject.h b/src/declarative/util/qmlopenmetaobject.h index f65660d..be0490d 100644 --- a/src/declarative/util/qmlopenmetaobject.h +++ b/src/declarative/util/qmlopenmetaobject.h @@ -64,6 +64,7 @@ public: void setValue(const QByteArray &, const QVariant &); QVariant value(int) const; void setValue(int, const QVariant &); + QVariant &operator[](const QByteArray &); int count() const; QByteArray name(int) const; diff --git a/src/declarative/util/qmlpropertymap.cpp b/src/declarative/util/qmlpropertymap.cpp new file mode 100644 index 0000000..a587af3 --- /dev/null +++ b/src/declarative/util/qmlpropertymap.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** 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 "qmlpropertymap.h" +#include <qmlopenmetaobject.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +//QmlPropertyMapMetaObject lets us listen for changes coming from QML +//so we can emit the changed signal. +class QmlPropertyMapMetaObject : public QmlOpenMetaObject +{ +public: + QmlPropertyMapMetaObject(QmlPropertyMap *obj, QmlPropertyMapPrivate *objPriv); + +protected: + virtual void propertyWrite(int index); + +private: + QmlPropertyMap *map; + QmlPropertyMapPrivate *priv; +}; + +class QmlPropertyMapPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlPropertyMap) +public: + QmlPropertyMapMetaObject *mo; + QStringList keys; + void emitChanged(const QString &key); +}; + +void QmlPropertyMapPrivate::emitChanged(const QString &key) +{ + Q_Q(QmlPropertyMap); + emit q->valueChanged(key); +} + +QmlPropertyMapMetaObject::QmlPropertyMapMetaObject(QmlPropertyMap *obj, QmlPropertyMapPrivate *objPriv) : QmlOpenMetaObject(obj) +{ + map = obj; + priv = objPriv; +} + +void QmlPropertyMapMetaObject::propertyWrite(int index) +{ + priv->emitChanged(QString::fromUtf8(name(index))); +} + +/*! + \class QmlPropertyMap + \brief The QmlPropertyMap class allows you to set key-value pairs that can be used in bindings. + + QmlPropertyMap provides a convenient way to expose domain data to the UI layer. + The following example shows how you might declare data in C++ and then + access it in QML. + + Setup in C++: + \code + //create our data + QmlPropertyMap ownerData; + ownerData.insert("name", QVariant(QString("John Smith"))); + ownerData.insert("phone", QVariant(QString("555-5555"))); + + //expose it to the UI layer + QmlContext *ctxt = view->bindContext(); + ctxt->setProperty("owner", &data); + \endcode + + Then, in QML: + \code + Text { text: owner.name } + Text { text: owner.phone } + \endcode + + The binding is dynamic - whenever a key's value is updated, anything bound to that + key will be updated as well. + + To detect value changes made in the UI layer you can connect to the valueChanged() signal. + However, note that valueChanged() is \b NOT emitted when changes are made by calling insert() + or clear() - it is only emitted when a value is updated from QML. + + \note It is not possible to remove keys from the map; once a key has been added, you can only + modify or clear its associated value. +*/ + +/*! + Constructs a bindable map with parent object \a parent. +*/ +QmlPropertyMap::QmlPropertyMap(QObject *parent) +: QObject(*(new QmlPropertyMapPrivate), parent) +{ + Q_D(QmlPropertyMap); + d->mo = new QmlPropertyMapMetaObject(this, d); +} + +/*! + Destroys the bindable map. +*/ +QmlPropertyMap::~QmlPropertyMap() +{ +} + +/*! + Clears the value (if any) associated with \a key. +*/ +void QmlPropertyMap::clear(const QString &key) +{ + Q_D(QmlPropertyMap); + d->mo->setValue(key.toUtf8(), QVariant()); +} + +/*! + Returns the value associated with \a key. + + If no value has been set for this key (or if the value has been cleared), + an invalid QVariant is returned. +*/ +QVariant QmlPropertyMap::value(const QString &key) const +{ + Q_D(const QmlPropertyMap); + return d->mo->value(key.toUtf8()); +} + +/*! + Sets the value associated with \a key to \a value. + + If the key doesn't exist, it is automatically created. +*/ +void QmlPropertyMap::insert(const QString &key, const QVariant &value) +{ + Q_D(QmlPropertyMap); + if (!d->keys.contains(key)) + d->keys.append(key); + d->mo->setValue(key.toUtf8(), value); +} + +/*! + Returns the list of keys. + + Keys that have been cleared will still appear in this list, even though their + associated values are invalid QVariants. +*/ +QStringList QmlPropertyMap::keys() const +{ + Q_D(const QmlPropertyMap); + return d->keys; +} + +/*! + \overload + + Same as size(). +*/ +int QmlPropertyMap::count() const +{ + Q_D(const QmlPropertyMap); + return d->keys.count(); +} + +/*! + Returns the number of keys in the map. + + \sa isEmpty(), count() +*/ +int QmlPropertyMap::size() const +{ + Q_D(const QmlPropertyMap); + return d->keys.size(); +} + +/*! + Returns true if the map contains no keys; otherwise returns + false. + + \sa size() +*/ +bool QmlPropertyMap::isEmpty() const +{ + Q_D(const QmlPropertyMap); + return d->keys.isEmpty(); +} + +/*! + Returns true if the map contains \a key. + + \sa size() +*/ +bool QmlPropertyMap::contains(const QString &key) const +{ + Q_D(const QmlPropertyMap); + return d->keys.contains(key); +} + +/*! + Returns the value associated with the key \a key as a modifiable + reference. + + If the map contains no item with key \a key, the function inserts + an invalid QVariant into the map with key \a key, and + returns a reference to it. + + \sa insert(), value() +*/ +QVariant &QmlPropertyMap::operator[](const QString &key) +{ + //### optimize + Q_D(QmlPropertyMap); + QByteArray utf8key = key.toUtf8(); + if (!d->keys.contains(key)) { + d->keys.append(key); + d->mo->setValue(utf8key, QVariant()); //force creation -- needed below + } + + return (*(d->mo))[utf8key]; +} + +/*! + \overload + + Same as value(). +*/ +const QVariant QmlPropertyMap::operator[](const QString &key) const +{ + return value(key); +} + +/*! + \fn void QmlPropertyMap::valueChanged(const QString &key) + This signal is emitted whenever one of the values in the map is changed. \a key + is the key corresponding to the value that was changed. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/extra/qbindablemap.h b/src/declarative/util/qmlpropertymap.h index aa1908e..24b4395 100644 --- a/src/declarative/extra/qbindablemap.h +++ b/src/declarative/util/qmlpropertymap.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QBINDABLEMAP_H -#define QBINDABLEMAP_H +#ifndef QMLPROPERTYMAP_H +#define QMLPROPERTYMAP_H #include <QtDeclarative/qfxglobal.h> #include <QtCore/QObject> @@ -54,29 +54,34 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class QBindableMapMetaObject; -class Q_DECLARATIVE_EXPORT QBindableMap : public QObject +class QmlPropertyMapPrivate; +class Q_DECLARATIVE_EXPORT QmlPropertyMap : public QObject { Q_OBJECT public: - QBindableMap(QObject *parent = 0); - virtual ~QBindableMap(); + QmlPropertyMap(QObject *parent = 0); + virtual ~QmlPropertyMap(); QVariant value(const QString &key) const; - void setValue(const QString &key, QVariant value); - void clearValue(const QString &key); + void insert(const QString &key, const QVariant &value); + void clear(const QString &key); Q_INVOKABLE QStringList keys() const; + int count() const; + int size() const; + bool isEmpty() const; + bool contains(const QString &key) const; + + QVariant &operator[](const QString &key); + const QVariant operator[](const QString &key) const; + Q_SIGNALS: - void changed(const QString &key); + void valueChanged(const QString &key); private: - Q_DISABLE_COPY(QBindableMap) - void emitChanged(const QString &key); - QBindableMapMetaObject *m_mo; - QStringList m_keys; - friend class QBindableMapMetaObject; + Q_DECLARE_PRIVATE(QmlPropertyMap) + Q_DISABLE_COPY(QmlPropertyMap) }; QT_END_NAMESPACE diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri index ec9967c..01ad898 100644 --- a/src/declarative/util/util.pri +++ b/src/declarative/util/util.pri @@ -19,7 +19,8 @@ SOURCES += \ util/qmlopenmetaobject.cpp \ util/qmltimeline.cpp \ util/qmltimer.cpp \ - util/qmlbind.cpp + util/qmlbind.cpp \ + util/qmlpropertymap.cpp HEADERS += \ util/qmlview.h \ @@ -46,4 +47,5 @@ HEADERS += \ util/qmlnullablevalue_p.h \ util/qmltimeline_p.h \ util/qmltimer.h \ - util/qmlbind.h + util/qmlbind.h \ + util/qmlpropertymap.h diff --git a/tests/auto/declarative/qbindablemap/tst_qbindablemap.cpp b/tests/auto/declarative/qbindablemap/tst_qbindablemap.cpp deleted file mode 100644 index c1b31c2..0000000 --- a/tests/auto/declarative/qbindablemap/tst_qbindablemap.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include <qtest.h> -#include <QtDeclarative/qmlengine.h> -#include <QtDeclarative/qmlcontext.h> -#include <QtDeclarative/qbindablemap.h> -#include <QtDeclarative/qmlcomponent.h> -#include <QtDeclarative/qfxtext.h> -#include <QSignalSpy> - -class tst_QBindableMap : public QObject -{ - Q_OBJECT -public: - tst_QBindableMap() {} - -private slots: - void insert(); - void clear(); - void changed(); -}; - -void tst_QBindableMap::insert() -{ - QBindableMap map; - map.setValue(QLatin1String("key1"),100); - map.setValue(QLatin1String("key2"),200); - QVERIFY(map.keys().count() == 2); - - QCOMPARE(map.value(QLatin1String("key1")), QVariant(100)); - QCOMPARE(map.value(QLatin1String("key2")), QVariant(200)); - - map.setValue(QLatin1String("key1"),"Hello World"); - QCOMPARE(map.value(QLatin1String("key1")), QVariant("Hello World")); -} - -void tst_QBindableMap::clear() -{ - QBindableMap map; - map.setValue(QLatin1String("key1"),100); - QVERIFY(map.keys().count() == 1); - - QCOMPARE(map.value(QLatin1String("key1")), QVariant(100)); - - map.clearValue(QLatin1String("key1")); - QVERIFY(map.keys().count() == 1); - QCOMPARE(map.value(QLatin1String("key1")), QVariant()); -} - -void tst_QBindableMap::changed() -{ - QBindableMap map; - QSignalSpy spy(&map, SIGNAL(changed(const QString&))); - map.setValue(QLatin1String("key1"),100); - map.setValue(QLatin1String("key2"),200); - QCOMPARE(spy.count(), 0); - - map.clearValue(QLatin1String("key1")); - QCOMPARE(spy.count(), 0); - - //make changes in QML - QmlEngine engine; - QmlContext *ctxt = engine.rootContext(); - ctxt->setContextProperty(QLatin1String("testdata"), &map); - QmlComponent component(&engine, "import Qt 4.6\nText { text: { testdata.key1 = 'Hello World'; 'X' } }", - QUrl("file://")); - QVERIFY(component.isReady()); - QFxText *txt = qobject_cast<QFxText*>(component.create()); - QVERIFY(txt); - QCOMPARE(txt->text(), QString('X')); - QCOMPARE(spy.count(), 1); - QList<QVariant> arguments = spy.takeFirst(); - QCOMPARE(arguments.at(0).toString(),QLatin1String("key1")); - QCOMPARE(map.value(QLatin1String("key1")), QVariant("Hello World")); -} - -QTEST_MAIN(tst_QBindableMap) - -#include "tst_qbindablemap.moc" diff --git a/tests/auto/declarative/qmlecmascript/data/TypeForDynamicCreation.qml b/tests/auto/declarative/qmlecmascript/data/TypeForDynamicCreation.qml new file mode 100644 index 0000000..56e0625 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/TypeForDynamicCreation.qml @@ -0,0 +1,2 @@ +import Qt.test 1.0 +MyQmlObject{objectName:"objectThree"} diff --git a/tests/auto/declarative/qmlecmascript/data/dynamicCreation.qml b/tests/auto/declarative/qmlecmascript/data/dynamicCreation.qml index ef39590..ed5e571 100644 --- a/tests/auto/declarative/qmlecmascript/data/dynamicCreation.qml +++ b/tests/auto/declarative/qmlecmascript/data/dynamicCreation.qml @@ -13,4 +13,9 @@ MyQmlObject{ var component = createComponent('dynamicCreation.helper.qml'); obj.objectProperty = component.createObject(); } + + function createThree() + { + obj.objectProperty = createQmlObject('TypeForDynamicCreation{}', obj); + } } diff --git a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp index f24882a..34b49e6 100644 --- a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp +++ b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp @@ -56,6 +56,7 @@ private slots: void signalParameterTypes(); void objectsCompareAsEqual(); void scriptAccess(); + void dynamicCreation_data(); void dynamicCreation(); void dynamicDestruction(); void objectToString(); @@ -639,27 +640,33 @@ void tst_qmlecmascript::scriptAccess() QCOMPARE(object->property("test3").toInt(), 0); } +void tst_qmlecmascript::dynamicCreation_data() +{ + QTest::addColumn<QString>("method"); + QTest::addColumn<QString>("createdName"); + + QTest::newRow("One") << "createOne" << "objectOne"; + QTest::newRow("Two") << "createTwo" << "objectTwo"; + QTest::newRow("Three") << "createThree" << "objectThree"; +} + /* Test using createQmlObject to dynamically generate an item Also using createComponent is tested. */ void tst_qmlecmascript::dynamicCreation() { + QFETCH(QString, method); + QFETCH(QString, createdName); + QmlComponent component(&engine, TEST_FILE("dynamicCreation.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); QVERIFY(object != 0); - QObject *createdQmlObject = 0; - QObject *createdComponent = 0; - - QMetaObject::invokeMethod(object, "createOne"); - createdQmlObject = object->objectProperty(); - QVERIFY(createdQmlObject); - QCOMPARE(createdQmlObject->objectName(), QString("objectOne")); - QMetaObject::invokeMethod(object, "createTwo"); - createdComponent = object->objectProperty(); - QVERIFY(createdComponent); - QCOMPARE(createdComponent->objectName(), QString("objectTwo")); + QMetaObject::invokeMethod(object, method.toUtf8()); + QObject *created = object->objectProperty(); + QVERIFY(created); + QCOMPARE(created->objectName(), createdName); } /* diff --git a/tests/auto/declarative/qmllanguage/data/scriptString.qml b/tests/auto/declarative/qmllanguage/data/scriptString.qml new file mode 100644 index 0000000..51e6e48 --- /dev/null +++ b/tests/auto/declarative/qmllanguage/data/scriptString.qml @@ -0,0 +1,6 @@ +import Test 1.0 + +MyTypeObject { + scriptProperty: foo + bar + grouped.script: print(1921) +} diff --git a/tests/auto/declarative/qmllanguage/testtypes.cpp b/tests/auto/declarative/qmllanguage/testtypes.cpp index c11e195..58d99f1 100644 --- a/tests/auto/declarative/qmllanguage/testtypes.cpp +++ b/tests/auto/declarative/qmllanguage/testtypes.cpp @@ -7,4 +7,4 @@ QML_DEFINE_TYPE(Test,1,0,0,MyContainer,MyContainer); QML_DEFINE_TYPE(Test,1,0,0,MyPropertyValueSource,MyPropertyValueSource); QML_DEFINE_TYPE(Test,1,0,0,MyDotPropertyObject,MyDotPropertyObject); QML_DEFINE_TYPE(Test,1,0,0,MyNamespacedType,MyNamespace::MyNamespacedType); - +QML_DEFINE_NOCREATE_TYPE(MyGroupedObject); diff --git a/tests/auto/declarative/qmllanguage/testtypes.h b/tests/auto/declarative/qmllanguage/testtypes.h index b1da9fb..355ff8b 100644 --- a/tests/auto/declarative/qmllanguage/testtypes.h +++ b/tests/auto/declarative/qmllanguage/testtypes.h @@ -10,6 +10,7 @@ #include <QtDeclarative/qmlcomponent.h> #include <QtDeclarative/qmlparserstatus.h> #include <QtDeclarative/qmlpropertyvaluesource.h> +#include <QtDeclarative/qmlscriptstring.h> class MyInterface { @@ -112,6 +113,21 @@ private: }; QML_DECLARE_TYPE(MyQmlObject); +class MyGroupedObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QmlScriptString script READ script WRITE setScript); +public: + QmlScriptString script() const { return m_script; } + void setScript(const QmlScriptString &s) { m_script = s; } + +private: + QmlScriptString m_script; +}; + +QML_DECLARE_TYPE(MyGroupedObject); + + class MyTypeObject : public QObject { Q_OBJECT @@ -142,6 +158,9 @@ class MyTypeObject : public QObject Q_PROPERTY(bool boolProperty READ boolProperty WRITE setBoolProperty); Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty); + Q_PROPERTY(QmlScriptString scriptProperty READ scriptProperty WRITE setScriptProperty); + Q_PROPERTY(MyGroupedObject *grouped READ grouped CONSTANT); + public: MyTypeObject() : objectPropertyValue(0), componentPropertyValue(0) {} @@ -334,6 +353,17 @@ public: variantPropertyValue = v; } + QmlScriptString scriptPropertyValue; + QmlScriptString scriptProperty() const { + return scriptPropertyValue; + } + void setScriptProperty(const QmlScriptString &v) { + scriptPropertyValue = v; + } + + MyGroupedObject groupedValue; + MyGroupedObject *grouped() { return &groupedValue; } + void doAction() { emit action(); } signals: void action(); @@ -365,6 +395,7 @@ private: QML_DECLARE_TYPE(MyContainer); + class MyPropertyValueSource : public QObject, public QmlPropertyValueSource { Q_OBJECT diff --git a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp index 8d4ae65..97038e6 100644 --- a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp +++ b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp @@ -63,6 +63,7 @@ private slots: void i18n(); void i18n_data(); void onCompleted(); + void scriptString(); void importsBuiltin_data(); void importsBuiltin(); @@ -739,6 +740,24 @@ void tst_qmllanguage::onCompleted() QVERIFY(object != 0); } +// Check that assignments to QmlScriptString properties work +void tst_qmllanguage::scriptString() +{ + QmlComponent component(&engine, TEST_FILE("scriptString.qml")); + VERIFY_ERRORS(0); + + MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->scriptProperty().script(), QString("foo + bar")); + QCOMPARE(object->scriptProperty().scopeObject(), object); + QCOMPARE(object->scriptProperty().context(), qmlContext(object)); + + QVERIFY(object->grouped() != 0); + QCOMPARE(object->grouped()->script().script(), QString("print(1921)")); + QCOMPARE(object->grouped()->script().scopeObject(), object); + QCOMPARE(object->grouped()->script().context(), qmlContext(object)); +} + // Check that first child of qml is of given type. Empty type insists on error. void tst_qmllanguage::testType(const QString& qml, const QString& type) { diff --git a/tests/auto/declarative/qbindablemap/qbindablemap.pro b/tests/auto/declarative/qmlpropertymap/qmlpropertymap.pro index b042405..94f138f 100644 --- a/tests/auto/declarative/qbindablemap/qbindablemap.pro +++ b/tests/auto/declarative/qmlpropertymap/qmlpropertymap.pro @@ -1,3 +1,3 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative -SOURCES += tst_qbindablemap.cpp +SOURCES += tst_qmlpropertymap.cpp diff --git a/tests/auto/declarative/qmlpropertymap/tst_qmlpropertymap.cpp b/tests/auto/declarative/qmlpropertymap/tst_qmlpropertymap.cpp new file mode 100644 index 0000000..7d3cc43 --- /dev/null +++ b/tests/auto/declarative/qmlpropertymap/tst_qmlpropertymap.cpp @@ -0,0 +1,110 @@ +#include <qtest.h> +#include <QtDeclarative/qmlengine.h> +#include <QtDeclarative/qmlcontext.h> +#include <QtDeclarative/qmlpropertymap.h> +#include <QtDeclarative/qmlcomponent.h> +#include <QtDeclarative/qfxtext.h> +#include <QSignalSpy> + +class tst_QmlPropertyMap : public QObject +{ + Q_OBJECT +public: + tst_QmlPropertyMap() {} + +private slots: + void insert(); + void operatorInsert(); + void clear(); + void changed(); + void count(); +}; + +void tst_QmlPropertyMap::insert() +{ + QmlPropertyMap map; + map.insert(QLatin1String("key1"),100); + map.insert(QLatin1String("key2"),200); + QVERIFY(map.keys().count() == 2); + + QCOMPARE(map.value(QLatin1String("key1")), QVariant(100)); + QCOMPARE(map.value(QLatin1String("key2")), QVariant(200)); + + map.insert(QLatin1String("key1"),"Hello World"); + QCOMPARE(map.value(QLatin1String("key1")), QVariant("Hello World")); +} + +void tst_QmlPropertyMap::operatorInsert() +{ + QmlPropertyMap map; + map[QLatin1String("key1")] = 100; + map[QLatin1String("key2")] = 200; + QVERIFY(map.keys().count() == 2); + + QCOMPARE(map.value(QLatin1String("key1")), QVariant(100)); + QCOMPARE(map.value(QLatin1String("key2")), QVariant(200)); + + map[QLatin1String("key1")] = "Hello World"; + QCOMPARE(map.value(QLatin1String("key1")), QVariant("Hello World")); +} + +void tst_QmlPropertyMap::clear() +{ + QmlPropertyMap map; + map.insert(QLatin1String("key1"),100); + QVERIFY(map.keys().count() == 1); + + QCOMPARE(map.value(QLatin1String("key1")), QVariant(100)); + + map.clear(QLatin1String("key1")); + QVERIFY(map.keys().count() == 1); + QCOMPARE(map.value(QLatin1String("key1")), QVariant()); +} + +void tst_QmlPropertyMap::changed() +{ + QmlPropertyMap map; + QSignalSpy spy(&map, SIGNAL(valueChanged(const QString&))); + map.insert(QLatin1String("key1"),100); + map.insert(QLatin1String("key2"),200); + QCOMPARE(spy.count(), 0); + + map.clear(QLatin1String("key1")); + QCOMPARE(spy.count(), 0); + + //make changes in QML + QmlEngine engine; + QmlContext *ctxt = engine.rootContext(); + ctxt->setContextProperty(QLatin1String("testdata"), &map); + QmlComponent component(&engine, "import Qt 4.6\nText { text: { testdata.key1 = 'Hello World'; 'X' } }", + QUrl("file://")); + QVERIFY(component.isReady()); + QFxText *txt = qobject_cast<QFxText*>(component.create()); + QVERIFY(txt); + QCOMPARE(txt->text(), QString('X')); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toString(),QLatin1String("key1")); + QCOMPARE(map.value(QLatin1String("key1")), QVariant("Hello World")); +} + +void tst_QmlPropertyMap::count() +{ + QmlPropertyMap map; + QCOMPARE(map.isEmpty(), true); + map.insert(QLatin1String("key1"),100); + map.insert(QLatin1String("key2"),200); + QCOMPARE(map.count(), 2); + QCOMPARE(map.isEmpty(), false); + + map.insert(QLatin1String("key3"),"Hello World"); + QCOMPARE(map.count(), 3); + + //clearing doesn't remove the key + map.clear(QLatin1String("key3")); + QCOMPARE(map.count(), 3); +} + +QTEST_MAIN(tst_QmlPropertyMap) + +#include "tst_qmlpropertymap.moc" diff --git a/tools/qdoc3/cppcodemarker.cpp b/tools/qdoc3/cppcodemarker.cpp index 3bee3d9..30ea251 100644 --- a/tools/qdoc3/cppcodemarker.cpp +++ b/tools/qdoc3/cppcodemarker.cpp @@ -353,6 +353,10 @@ QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary) QString name = taggedQmlNode(node); if (summary) { name = linkTag(node,name); + } else if (node->type() == Node::QmlProperty) { + const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(node); + if (pn->isAttached()) + name.prepend(pn->element() + QLatin1Char('.')); } name = "<@name>" + name + "</@name>"; QString synopsis = name; diff --git a/tools/qdoc3/node.h b/tools/qdoc3/node.h index c6f860f..6b19f60 100644 --- a/tools/qdoc3/node.h +++ b/tools/qdoc3/node.h @@ -376,7 +376,7 @@ class QmlPropGroupNode : public FakeNode bool attached); virtual ~QmlPropGroupNode() { } - const QString& element() const { return name(); } + const QString& element() const { return parent()->name(); } void setDefault() { isdefault = true; } bool isDefault() const { return isdefault; } bool isAttached() const { return att; } @@ -405,7 +405,7 @@ class QmlPropertyNode : public LeafNode bool isDesignable() const { return fromTrool(des,false); } bool isAttached() const { return att; } - const QString& element() const { return parent()->name(); } + const QString& element() const { return static_cast<QmlPropGroupNode*>(parent())->element(); } private: enum Trool { Trool_True, Trool_False, Trool_Default }; diff --git a/tools/qdoc3/qmlcodemarker.cpp b/tools/qdoc3/qmlcodemarker.cpp new file mode 100644 index 0000000..1062f9c --- /dev/null +++ b/tools/qdoc3/qmlcodemarker.cpp @@ -0,0 +1,1175 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 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. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cppcodemarker.cpp +*/ + +#include <qdebug.h> +#include "atom.h" +#include "cppcodemarker.h" +#include "node.h" +#include "text.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +static int insertTagAround(QString &result, int pos, int len, const QString &tagName, + const QString &attributes = QString()) +{ + QString s; + //s.reserve(result.size() + tagName.size() * 2 + attributes.size() + 20); + s += result.midRef(0, pos); + s += QLatin1Char('<'); + s += tagName; + if (!attributes.isEmpty()) { + s += QLatin1Char(' '); + s += attributes; + } + s += QLatin1Char('>'); + s += result.midRef(pos, len); + s += QLatin1String("</"); + s += tagName; + s += QLatin1Char('>'); + s += result.midRef(pos + len); + int diff = s.length() - result.length(); + result = s; + return diff; +} + +/*! + The constructor does nothing. + */ +CppCodeMarker::CppCodeMarker() +{ + // nothing. +} + +/*! + The destructor does nothing. + */ +CppCodeMarker::~CppCodeMarker() +{ + // nothing. +} + +/*! + Returns true. + */ +bool CppCodeMarker::recognizeCode(const QString & /* code */) +{ + return true; +} + +/*! + Returns true if \a ext is any of a list of file extensions + for the C++ language. + */ +bool CppCodeMarker::recognizeExtension(const QString& ext) +{ + return ext == "c" || + ext == "c++" || + ext == "cc" || + ext == "cpp" || + ext == "cxx" || + ext == "ch" || + ext == "h" || + ext == "h++" || + ext == "hh" || + ext == "hpp" || + ext == "hxx"; +} + +/*! + Returns true if \a lang is either "C" or "Cpp". + */ +bool CppCodeMarker::recognizeLanguage(const QString &lang) +{ + return lang == "C" || lang == "Cpp"; +} + +/*! + Returns the \a node name, or "()" if \a node is a + Node::Function node. + */ +QString CppCodeMarker::plainName(const Node *node) +{ + QString name = node->name(); + if (node->type() == Node::Function) + name += "()"; + return name; +} + +QString CppCodeMarker::plainFullName(const Node *node, const Node *relative) +{ + if (node->name().isEmpty()) { + return "global"; + } + else { + QString fullName; + for (;;) { + fullName.prepend(plainName(node)); + if (node->parent() == relative || node->parent()->name().isEmpty()) + break; + fullName.prepend("::"); + node = node->parent(); + } + return fullName; + } +} + +QString CppCodeMarker::markedUpCode(const QString &code, + const Node *relative, + const QString &dirPath) +{ + return addMarkUp(protect(code), relative, dirPath); +} + +QString CppCodeMarker::markedUpSynopsis(const Node *node, + const Node * /* relative */, + SynopsisStyle style) +{ + const int MaxEnumValues = 6; + const FunctionNode *func; + const PropertyNode *property; + const VariableNode *variable; + const EnumNode *enume; + const TypedefNode *typedeff; + QString synopsis; + QString extra; + QString name; + + name = taggedNode(node); + if (style != Detailed) + name = linkTag(node, name); + name = "<@name>" + name + "</@name>"; + + if (style == Detailed && !node->parent()->name().isEmpty() && + node->type() != Node::Property) + name.prepend(taggedNode(node->parent()) + "::"); + + switch (node->type()) { + case Node::Namespace: + synopsis = "namespace " + name; + break; + case Node::Class: + synopsis = "class " + name; + break; + case Node::Function: + func = (const FunctionNode *) node; + if (style != SeparateList && !func->returnType().isEmpty()) + synopsis = typified(func->returnType()) + " "; + synopsis += name; + if (func->metaness() != FunctionNode::MacroWithoutParams) { + synopsis += " ("; + if (!func->parameters().isEmpty()) { + synopsis += " "; + QList<Parameter>::ConstIterator p = func->parameters().begin(); + while (p != func->parameters().end()) { + if (p != func->parameters().begin()) + synopsis += ", "; + synopsis += typified((*p).leftType()); + if (style != SeparateList && !(*p).name().isEmpty()) + synopsis += + " <@param>" + protect((*p).name()) + "</@param>"; + synopsis += protect((*p).rightType()); + if (style != SeparateList && !(*p).defaultValue().isEmpty()) + synopsis += " = " + protect((*p).defaultValue()); + ++p; + } + synopsis += " "; + } + synopsis += ")"; + } + if (func->isConst()) + synopsis += " const"; + + if (style == Summary || style == Accessors) { + if (func->virtualness() != FunctionNode::NonVirtual) + synopsis.prepend("virtual "); + if (func->virtualness() == FunctionNode::PureVirtual) + synopsis.append(" = 0"); + } + else if (style == SeparateList) { + if (!func->returnType().isEmpty() && func->returnType() != "void") + synopsis += " : " + typified(func->returnType()); + } + else { + QStringList bracketed; + if (func->isStatic()) { + bracketed += "static"; + } + else if (func->virtualness() != FunctionNode::NonVirtual) { + if (func->virtualness() == FunctionNode::PureVirtual) + bracketed += "pure"; + bracketed += "virtual"; + } + + if (func->access() == Node::Protected) { + bracketed += "protected"; + } + else if (func->access() == Node::Private) { + bracketed += "private"; + } + + if (func->metaness() == FunctionNode::Signal) { + bracketed += "signal"; + } + else if (func->metaness() == FunctionNode::Slot) { + bracketed += "slot"; + } + if (!bracketed.isEmpty()) + extra += " [" + bracketed.join(" ") + "]"; + } + break; + case Node::Enum: + enume = static_cast<const EnumNode *>(node); + synopsis = "enum " + name; + if (style == Summary) { + synopsis += " { "; + + QStringList documentedItems = enume->doc().enumItemNames(); + if (documentedItems.isEmpty()) { + foreach (const EnumItem &item, enume->items()) + documentedItems << item.name(); + } + QStringList omitItems = enume->doc().omitEnumItemNames(); + foreach (const QString &item, omitItems) + documentedItems.removeAll(item); + + if (documentedItems.size() <= MaxEnumValues) { + for (int i = 0; i < documentedItems.size(); ++i) { + if (i != 0) + synopsis += ", "; + synopsis += documentedItems.at(i); + } + } + else { + for (int i = 0; i < documentedItems.size(); ++i) { + if (i < MaxEnumValues-2 || i == documentedItems.size()-1) { + if (i != 0) + synopsis += ", "; + synopsis += documentedItems.at(i); + } + else if (i == MaxEnumValues - 1) { + synopsis += ", ..."; + } + } + } + if (!documentedItems.isEmpty()) + synopsis += " "; + synopsis += "}"; + } + break; + case Node::Typedef: + typedeff = static_cast<const TypedefNode *>(node); + if (typedeff->associatedEnum()) { + synopsis = "flags " + name; + } + else { + synopsis = "typedef " + name; + } + break; + case Node::Property: + property = static_cast<const PropertyNode *>(node); + synopsis = name + " : " + typified(property->qualifiedDataType()); + break; + case Node::Variable: + variable = static_cast<const VariableNode *>(node); + if (style == SeparateList) { + synopsis = name + " : " + typified(variable->dataType()); + } + else { + synopsis = typified(variable->leftType()) + " " + + name + protect(variable->rightType()); + } + break; + default: + synopsis = name; + } + + if (style == Summary) { + if (node->status() == Node::Preliminary) { + extra += " (preliminary)"; + } + else if (node->status() == Node::Deprecated) { + extra += " (deprecated)"; + } + else if (node->status() == Node::Obsolete) { + extra += " (obsolete)"; + } + } + + if (!extra.isEmpty()) { + extra.prepend("<@extra>"); + extra.append("</@extra>"); + } + return synopsis + extra; +} + +#ifdef QDOC_QML +/*! + */ +QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary) +{ + QString name = taggedQmlNode(node); + if (summary) { + name = linkTag(node,name); + } + name = "<@name>" + name + "</@name>"; + QString synopsis = name; + if (node->type() == Node::QmlProperty) { + const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(node); + synopsis += " : " + typified(pn->dataType()); + } + + QString extra; + if (summary) { + if (node->status() == Node::Preliminary) { + extra += " (preliminary)"; + } + else if (node->status() == Node::Deprecated) { + extra += " (deprecated)"; + } + else if (node->status() == Node::Obsolete) { + extra += " (obsolete)"; + } + } + + if (!extra.isEmpty()) { + extra.prepend("<@extra>"); + extra.append("</@extra>"); + } + return synopsis + extra; +} +#endif + +QString CppCodeMarker::markedUpName(const Node *node) +{ + QString name = linkTag(node, taggedNode(node)); + if (node->type() == Node::Function) + name += "()"; + return name; +} + +QString CppCodeMarker::markedUpFullName(const Node *node, const Node *relative) +{ + if (node->name().isEmpty()) { + return "global"; + } + else { + QString fullName; + for (;;) { + fullName.prepend(markedUpName(node)); + if (node->parent() == relative || node->parent()->name().isEmpty()) + break; + fullName.prepend("<@op>::</@op>"); + node = node->parent(); + } + return fullName; + } +} + +QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, + const Node *relative) +{ + const Node *node = relative->parent(); + QString fullName; + while (node->parent()) { + fullName.prepend(markedUpName(node)); + if (node->parent() == relative || node->parent()->name().isEmpty()) + break; + fullName.prepend("<@op>::</@op>"); + node = node->parent(); + } + if (!fullName.isEmpty()) + fullName.append("<@op>::</@op>"); + fullName.append(enumValue); + return fullName; +} + +QString CppCodeMarker::markedUpIncludes(const QStringList& includes) +{ + QString code; + + QStringList::ConstIterator inc = includes.begin(); + while (inc != includes.end()) { + code += "#include <<@headerfile>" + *inc + "</@headerfile>>\n"; + ++inc; + } + return addMarkUp(code, 0, ""); +} + +QString CppCodeMarker::functionBeginRegExp(const QString& funcName) +{ + return "^" + QRegExp::escape(funcName) + "$"; + +} + +QString CppCodeMarker::functionEndRegExp(const QString& /* funcName */) +{ + return "^\\}$"; +} + +#if 0 + FastSection privateReimpFuncs(classe, + "Private Reimplemented Functions", + "private reimplemented function", + "private reimplemented functions"); + FastSection protectedReimpFuncs(classe, + "Protected Reimplemented Functions", + "protected reimplemented function", + "protected reimplemented functions"); + FastSection publicReimpFuncs(classe, + "Public Reimplemented Functions", + "public reimplemented function", + "public reimplemented functions"); +#endif + +QList<Section> CppCodeMarker::sections(const InnerNode *inner, + SynopsisStyle style, + Status status) +{ + QList<Section> sections; + + if (inner->type() == Node::Class) { + const ClassNode *classe = static_cast<const ClassNode *>(inner); + + if (style == Summary) { + FastSection privateFunctions(classe, + "Private Functions", + "private function", + "private functions"); + FastSection privateSlots(classe, "Private Slots", "private slot", "private slots"); + FastSection privateTypes(classe, "Private Types", "private type", "private types"); + FastSection protectedFunctions(classe, + "Protected Functions", + "protected function", + "protected functions"); + FastSection protectedSlots(classe, + "Protected Slots", + "protected slot", + "protected slots"); + FastSection protectedTypes(classe, + "Protected Types", + "protected type", + "protected types"); + FastSection protectedVariables(classe, + "Protected Variables", + "protected type", + "protected variables"); + FastSection publicFunctions(classe, + "Public Functions", + "public function", + "public functions"); + FastSection publicSignals(classe, "Signals", "signal", "signals"); + FastSection publicSlots(classe, "Public Slots", "public slot", "public slots"); + FastSection publicTypes(classe, "Public Types", "public type", "public types"); + FastSection publicVariables(classe, + "Public Variables", + "public type", + "public variables"); + FastSection properties(classe, "Properties", "property", "properties"); + FastSection relatedNonMembers(classe, + "Related Non-Members", + "related non-member", + "related non-members"); + FastSection staticPrivateMembers(classe, + "Static Private Members", + "static private member", + "static private members"); + FastSection staticProtectedMembers(classe, + "Static Protected Members", + "static protected member", + "static protected members"); + FastSection staticPublicMembers(classe, + "Static Public Members", + "static public member", + "static public members"); + FastSection macros(inner, "Macros", "macro", "macros"); + + NodeList::ConstIterator r = classe->relatedNodes().begin(); + while (r != classe->relatedNodes().end()) { + if ((*r)->type() == Node::Function) { + FunctionNode *func = static_cast<FunctionNode *>(*r); + if (func->isMacro()) + insert(macros, *r, style, status); + else + insert(relatedNonMembers, *r, style, status); + } + else { + insert(relatedNonMembers, *r, style, status); + } + ++r; + } + + QStack<const ClassNode *> stack; + stack.push(classe); + + while (!stack.isEmpty()) { + const ClassNode *ancestorClass = stack.pop(); + + NodeList::ConstIterator c = ancestorClass->childNodes().begin(); + while (c != ancestorClass->childNodes().end()) { + bool isSlot = false; + bool isSignal = false; + bool isStatic = false; + if ((*c)->type() == Node::Function) { + const FunctionNode *func = (const FunctionNode *) *c; + isSlot = (func->metaness() == FunctionNode::Slot); + isSignal = (func->metaness() == FunctionNode::Signal); + isStatic = func->isStatic(); + } + else if ((*c)->type() == Node::Variable) { + const VariableNode *var = static_cast<const VariableNode *>(*c); + isStatic = var->isStatic(); + } + + switch ((*c)->access()) { + case Node::Public: + if (isSlot) { + insert(publicSlots, *c, style, status); + } + else if (isSignal) { + insert(publicSignals, *c, style, status); + } + else if (isStatic) { + if ((*c)->type() != Node::Variable + || !(*c)->doc().isEmpty()) + insert(staticPublicMembers,*c,style,status); + } + else if ((*c)->type() == Node::Property) { + insert(properties, *c, style, status); + } + else if ((*c)->type() == Node::Variable) { + if (!(*c)->doc().isEmpty()) + insert(publicVariables, *c, style, status); + } + else if ((*c)->type() == Node::Function) { + if (!insertReimpFunc(publicFunctions,*c,status)) + insert(publicFunctions, *c, style, status); + } + else { + insert(publicTypes, *c, style, status); + } + break; + case Node::Protected: + if (isSlot) { + insert(protectedSlots, *c, style, status); + } + else if (isStatic) { + if ((*c)->type() != Node::Variable + || !(*c)->doc().isEmpty()) + insert(staticProtectedMembers,*c,style,status); + } + else if ((*c)->type() == Node::Variable) { + if (!(*c)->doc().isEmpty()) + insert(protectedVariables,*c,style,status); + } + else if ((*c)->type() == Node::Function) { + if (!insertReimpFunc(protectedFunctions,*c,status)) + insert(protectedFunctions, *c, style, status); + } + else { + insert(protectedTypes, *c, style, status); + } + break; + case Node::Private: + if (isSlot) { + insert(privateSlots, *c, style, status); + } + else if (isStatic) { + if ((*c)->type() != Node::Variable + || !(*c)->doc().isEmpty()) + insert(staticPrivateMembers,*c,style,status); + } + else if ((*c)->type() == Node::Function) { + if (!insertReimpFunc(privateFunctions,*c,status)) + insert(privateFunctions, *c, style, status); + } + else { + insert(privateTypes,*c,style,status); + } + } + ++c; + } + + QList<RelatedClass>::ConstIterator r = + ancestorClass->baseClasses().begin(); + while (r != ancestorClass->baseClasses().end()) { + stack.prepend((*r).node); + ++r; + } + } + + append(sections, publicTypes); + append(sections, properties); + append(sections, publicFunctions); + append(sections, publicSlots); + append(sections, publicSignals); + append(sections, publicVariables); + append(sections, staticPublicMembers); + append(sections, protectedTypes); + append(sections, protectedFunctions); + append(sections, protectedSlots); + append(sections, protectedVariables); + append(sections, staticProtectedMembers); + append(sections, privateTypes); + append(sections, privateFunctions); + append(sections, privateSlots); + append(sections, staticPrivateMembers); + append(sections, relatedNonMembers); + append(sections, macros); + } + else if (style == Detailed) { + FastSection memberFunctions(classe,"Member Function Documentation"); + FastSection memberTypes(classe,"Member Type Documentation"); + FastSection memberVariables(classe,"Member Variable Documentation"); + FastSection properties(classe,"Property Documentation"); + FastSection relatedNonMembers(classe,"Related Non-Members"); + FastSection macros(classe,"Macro Documentation"); + + NodeList::ConstIterator r = classe->relatedNodes().begin(); + while (r != classe->relatedNodes().end()) { + if ((*r)->type() == Node::Function) { + FunctionNode *func = static_cast<FunctionNode *>(*r); + if (func->isMacro()) + insert(macros, *r, style, status); + else + insert(relatedNonMembers, *r, style, status); + } + else { + insert(relatedNonMembers, *r, style, status); + } + ++r; + } + + NodeList::ConstIterator c = classe->childNodes().begin(); + while (c != classe->childNodes().end()) { + if ((*c)->type() == Node::Enum || + (*c)->type() == Node::Typedef) { + insert(memberTypes, *c, style, status); + } + else if ((*c)->type() == Node::Property) { + insert(properties, *c, style, status); + } + else if ((*c)->type() == Node::Variable) { + if (!(*c)->doc().isEmpty()) + insert(memberVariables, *c, style, status); + } + else if ((*c)->type() == Node::Function) { + FunctionNode *function = static_cast<FunctionNode *>(*c); + if (!function->associatedProperty()) + insert(memberFunctions, function, style, status); + } + ++c; + } + + append(sections, memberTypes); + append(sections, properties); + append(sections, memberFunctions); + append(sections, memberVariables); + append(sections, relatedNonMembers); + append(sections, macros); + } + else { + FastSection all(classe); + + QStack<const ClassNode *> stack; + stack.push(classe); + + while (!stack.isEmpty()) { + const ClassNode *ancestorClass = stack.pop(); + + NodeList::ConstIterator c = ancestorClass->childNodes().begin(); + while (c != ancestorClass->childNodes().end()) { + if ((*c)->access() != Node::Private && + (*c)->type() != Node::Property) + insert(all, *c, style, status); + ++c; + } + + QList<RelatedClass>::ConstIterator r = + ancestorClass->baseClasses().begin(); + while (r != ancestorClass->baseClasses().end()) { + stack.prepend((*r).node); + ++r; + } + } + append(sections, all); + } + } + else { + if (style == Summary || style == Detailed) { + FastSection namespaces(inner, + "Namespaces", + "namespace", + "namespaces"); + FastSection classes(inner, + "Classes", + "class", + "classes"); + FastSection types(inner, + style == Summary ? + "Types" : "Type Documentation", + "type", + "types"); + FastSection functions(inner, + style == Summary ? + "Functions" : "Function Documentation", + "function", + "functions"); + FastSection macros(inner, + style == Summary ? + "Macros" : "Macro Documentation", + "macro", + "macros"); + + NodeList nodeList = inner->childNodes(); + nodeList += inner->relatedNodes(); + + NodeList::ConstIterator n = nodeList.begin(); + while (n != nodeList.end()) { + switch ((*n)->type()) { + case Node::Namespace: + insert(namespaces, *n, style, status); + break; + case Node::Class: + insert(classes, *n, style, status); + break; + case Node::Enum: + case Node::Typedef: + insert(types, *n, style, status); + break; + case Node::Function: + { + FunctionNode *func = static_cast<FunctionNode *>(*n); + if (func->isMacro()) + insert(macros, *n, style, status); + else + insert(functions, *n, style, status); + } + break; + default: + ; + } + ++n; + } + append(sections, namespaces); + append(sections, classes); + append(sections, types); + append(sections, functions); + append(sections, macros); + } + } + + return sections; +} + +const Node *CppCodeMarker::resolveTarget(const QString &target, + const Tree *tree, + const Node *relative) +{ + if (target.endsWith("()")) { + const FunctionNode *func; + QString funcName = target; + funcName.chop(2); + + QStringList path = funcName.split("::"); + if ((func = tree->findFunctionNode(path, + relative, + Tree::SearchBaseClasses)) + && func->metaness() != FunctionNode::MacroWithoutParams) + return func; + } + else if (target.contains("#")) { + // ### this doesn't belong here; get rid of TargetNode hack + int hashAt = target.indexOf("#"); + QString link = target.left(hashAt); + QString ref = target.mid(hashAt + 1); + const Node *node; + if (link.isEmpty()) { + node = relative; + } + else { + QStringList path(link); + node = tree->findNode(path, tree->root(), Tree::SearchBaseClasses); + } + if (node && node->isInnerNode()) { + const Atom *atom = node->doc().body().firstAtom(); + while (atom) { + if (atom->type() == Atom::Target && atom->string() == ref) { + Node *parentNode = const_cast<Node *>(node); + return new TargetNode(static_cast<InnerNode*>(parentNode), + ref); + } + atom = atom->next(); + } + } + } + else { + QStringList path = target.split("::"); + const Node *node; + if ((node = tree->findNode(path, + relative, + Tree::SearchBaseClasses | + Tree::SearchEnumValues | + Tree::NonFunction))) + return node; + } + return 0; +} + +QString CppCodeMarker::addMarkUp(const QString& protectedCode, + const Node * /* relative */, + const QString& /* dirPath */) +{ + static QRegExp globalInclude("#include +<([^<>&]+)>"); + static QRegExp yHasTypeX("(?:^|\n *)([a-zA-Z_][a-zA-Z_0-9]*)" + "(?:<[^;{}]+>)?(?: *(?:\\*|&) *| +)" + "([a-zA-Z_][a-zA-Z_0-9]*)? *[,;()=]"); + static QRegExp xNewY("([a-zA-Z_][a-zA-Z_0-9]*) *= *new +([a-zA-Z_0-9]+)"); + static QRegExp xDotY("\\b([a-zA-Z_][a-zA-Z_0-9]*) *(?:\\.|->|,[ \n]*S(?:IGNAL|LOT)\\() *" + "([a-zA-Z_][a-zA-Z_0-9]*)(?= *\\()"); + static QRegExp xIsStaticZOfY("[\n:;{(=] *(([a-zA-Z_0-9]+)::([a-zA-Z_0-9]+))(?= *\\()"); + static QRegExp classX("[:,][ \n]*(?:p(?:ublic|r(?:otected|ivate))[ \n]+)?" + "([a-zA-Z_][a-zA-Z_0-9]*)"); + static QRegExp globalX("[\n{()=] *([a-zA-Z_][a-zA-Z_0-9]*)[ \n]*\\("); + static QRegExp multiLineComment("/(?:( )?\\*(?:[^*]+|\\*(?! /))*\\*\\1/)"); + multiLineComment.setMinimal(true); + static QRegExp singleLineComment("//(?!!)[^!\n]*"); + static QRegExp preprocessor("(?:^|\n)(#[ \t]*(?:include|if|elif|endif|error|pragma|define" + "|warning)(?:(?:\\\\\n|\\n#)[^\n]*)*)"); + static QRegExp literals(""(?:[^\\\\&]|\\\\[^\n]|&(?!quot;))*"" + "|'(?:[^\\\\]|\\\\(?:[^x0-9']|x[0-9a-f]{1,4}|[0-9]{1,3}))'"); + + QString result = protectedCode; + int pos; + + if (!hurryUp()) { + /* + Mark global includes. For example: + + #include <<@headerfile>QString</@headerfile> + */ + pos = 0; + while ((pos = result.indexOf(globalInclude, pos)) != -1) + pos += globalInclude.matchedLength() + + insertTagAround(result, + globalInclude.pos(1), + globalInclude.cap(1).length(), + "@headerfile"); + + /* + Look for variable definitions and similar constructs, mark + the data type, and remember the type of the variable. + */ + QMap<QString, QSet<QString> > typesForVariable; + pos = 0; + while ((pos = yHasTypeX.indexIn(result, pos)) != -1) { + QString x = yHasTypeX.cap(1); + QString y = yHasTypeX.cap(2); + + if (!y.isEmpty()) + typesForVariable[y].insert(x); + + /* + Without the minus one at the end, 'void member(Class + var)' would give 'member' as a variable of type 'void', + but would ignore 'Class var'. (### Is that true?) + */ + pos += yHasTypeX.matchedLength() + + insertTagAround(result, + yHasTypeX.pos(1), + x.length(), + "@type") - 1; + } + + /* + Do syntax highlighting of preprocessor directives. + */ + pos = 0; + while ((pos = preprocessor.indexIn(result, pos)) != -1) + pos += preprocessor.matchedLength() + + insertTagAround(result, + preprocessor.pos(1), + preprocessor.cap(1).length(), + "@preprocessor"); + + /* + Deal with string and character literals. + */ + pos = 0; + while ((pos = literals.indexIn(result, pos)) != -1) + pos += literals.matchedLength() + + insertTagAround(result, + pos, + literals.matchedLength(), + result.at(pos) == + QLatin1Char(' ') ? "@string" : "@char"); + + /* + Look for 'var = new Class'. + */ + pos = 0; + while ((pos = xNewY.indexIn(result, pos)) != -1) { + QString x = xNewY.cap(1); + QString y = xNewY.cap(2); + typesForVariable[x].insert(y); + + pos += xNewY.matchedLength() + insertTagAround(result, + xNewY.pos(2), + y.length(), + "@type"); + } + + /* + Insert some stuff that cannot harm. + */ + typesForVariable["qApp"].insert("QApplication"); + + /* + Add link to ': Class'. + */ + pos = 0; + while ((pos = classX.indexIn(result, pos)) != -1) + pos += classX.matchedLength() + + insertTagAround(result, + classX.pos(1), + classX.cap(1).length(), + "@type") - 1; + + /* + Find use of any of + + var.method() + var->method() + var, SIGNAL(method()) + var, SLOT(method()). + */ + pos = 0; + while ((pos = xDotY.indexIn(result, pos)) != -1) { + QString x = xDotY.cap(1); + QString y = xDotY.cap(2); + + QSet<QString> types = typesForVariable.value(x); + pos += xDotY.matchedLength() + + insertTagAround(result, + xDotY.pos(2), + xDotY.cap(2).length(), + "@func", + (types.count() == 1) ? "target=\"" + + protect(*types.begin() + "::" + y) + + "()\"" : QString()); + } + + /* + Add link to 'Class::method()'. + */ + pos = 0; + while ((pos = xIsStaticZOfY.indexIn(result, pos)) != -1) { + QString x = xIsStaticZOfY.cap(1); + QString z = xIsStaticZOfY.cap(3); + + pos += insertTagAround(result, + xIsStaticZOfY.pos(3), + z.length(), + "@func", + "target=\"" + protect(x) + "()\""); + pos += insertTagAround(result, + xIsStaticZOfY.pos(2), + xIsStaticZOfY.cap(2).length(), + "@type"); + pos += xIsStaticZOfY.matchedLength() - 1; + } + + /* + Add link to 'globalFunction()'. + */ + pos = 0; + while ((pos = globalX.indexIn(result, pos)) != -1) { + QString x = globalX.cap(1); + if (x != "QT_FORWARD_DECLARE_CLASS") { + pos += globalX.matchedLength() + + insertTagAround(result, + globalX.pos(1), + x.length(), + "@func", + "target=\"" + protect(x) + "()\"") - 1; + } + else + pos += globalX.matchedLength(); + } + } + + /* + Do syntax highlighting of comments. Also alter the code in a + minor way, so that we can include comments in documentation + comments. + */ + pos = 0; + while (pos != -1) { + int mlpos; + int slpos; + int len; + slpos = singleLineComment.indexIn(result, pos); + mlpos = multiLineComment.indexIn(result, pos); + + if (slpos == -1 && mlpos == -1) + break; + + if (slpos == -1) { + pos = mlpos; + len = multiLineComment.matchedLength(); + } + else if (mlpos == -1) { + pos = slpos; + len = singleLineComment.matchedLength(); + } + else { + if (slpos < mlpos) { + pos = slpos; + len = singleLineComment.matchedLength(); + } + else { + pos = mlpos; + len = multiLineComment.matchedLength(); + } + } + + if (result.at(pos + 1) == QLatin1Char(' ')) { + result.remove(pos + len - 2, 1); + result.remove(pos + 1, 1); + len -= 2; + + forever { + int endcodePos = result.indexOf("\\ endcode", pos); + if (endcodePos == -1 || endcodePos >= pos + len) + break; + result.remove(endcodePos + 1, 1); + len -= 1; + } + } + pos += len + insertTagAround(result, pos, len, "@comment"); + } + + return result; +} + +#ifdef QDOC_QML +/*! + This function is for documenting QML properties. It returns + the list of documentation sections for the children of the + \a qmlClassNode. + + Currently, it only handles QML property groups. + */ +QList<Section> CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode, + SynopsisStyle style) +{ + QList<Section> sections; + if (qmlClassNode) { + if (style == Summary) { + FastSection qmlproperties(qmlClassNode, + "Properties", + "property", + "properties"); + FastSection qmlsignals(qmlClassNode, + "Signals", + "signal", + "signals"); + FastSection qmlmethods(qmlClassNode, + "Methods", + "method", + "methods"); + + NodeList::ConstIterator c = qmlClassNode->childNodes().begin(); + while (c != qmlClassNode->childNodes().end()) { + if ((*c)->subType() == Node::QmlPropertyGroup) { + const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(*c); + NodeList::ConstIterator p = qpgn->childNodes().begin(); + while (p != qpgn->childNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + insert(qmlproperties,*p,style,Okay); + } + ++p; + } + } + else if ((*c)->type() == Node::QmlSignal) { + insert(qmlsignals,*c,style,Okay); + } + else if ((*c)->type() == Node::QmlMethod) { + insert(qmlmethods,*c,style,Okay); + } + ++c; + } + append(sections,qmlproperties); + append(sections,qmlsignals); + append(sections,qmlmethods); + } + else if (style == Detailed) { + FastSection qmlproperties(qmlClassNode, "Property Documentation"); + FastSection qmlsignals(qmlClassNode,"Signal Documentation"); + FastSection qmlmethods(qmlClassNode,"Method Documentation"); + NodeList::ConstIterator c = qmlClassNode->childNodes().begin(); + while (c != qmlClassNode->childNodes().end()) { + if ((*c)->subType() == Node::QmlPropertyGroup) { + insert(qmlproperties,*c,style,Okay); + } + else if ((*c)->type() == Node::QmlSignal) { + insert(qmlsignals,*c,style,Okay); + } + else if ((*c)->type() == Node::QmlMethod) { + insert(qmlmethods,*c,style,Okay); + } + ++c; + } + append(sections,qmlproperties); + append(sections,qmlsignals); + append(sections,qmlmethods); + } + } + + return sections; +} +#endif + +QT_END_NAMESPACE diff --git a/tools/qdoc3/qmlcodemarker.h b/tools/qdoc3/qmlcodemarker.h new file mode 100644 index 0000000..8c83c04 --- /dev/null +++ b/tools/qdoc3/qmlcodemarker.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 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. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cppcodemarker.h +*/ + +#ifndef CPPCODEMARKER_H +#define CPPCODEMARKER_H + +#include "codemarker.h" + +QT_BEGIN_NAMESPACE + +class CppCodeMarker : public CodeMarker +{ + public: + CppCodeMarker(); + ~CppCodeMarker(); + + bool recognizeCode(const QString& code); + bool recognizeExtension(const QString& ext); + bool recognizeLanguage(const QString& lang); + QString plainName(const Node *node); + QString plainFullName(const Node *node, const Node *relative); + QString markedUpCode(const QString& code, + const Node *relative, + const QString& dirPath); + QString markedUpSynopsis(const Node *node, + const Node *relative, + SynopsisStyle style); +#ifdef QDOC_QML + QString markedUpQmlItem(const Node *node, bool summary); +#endif + QString markedUpName(const Node *node); + QString markedUpFullName(const Node *node, const Node *relative); + QString markedUpEnumValue(const QString &enumValue, const Node *relative); + QString markedUpIncludes(const QStringList& includes); + QString functionBeginRegExp(const QString& funcName); + QString functionEndRegExp(const QString& funcName); + QList<Section> sections(const InnerNode *innerNode, + SynopsisStyle style, + Status status); + QList<Section> qmlSections(const QmlClassNode* qmlClassNode, + SynopsisStyle style); + const Node *resolveTarget(const QString& target, + const Tree *tree, + const Node *relative); + +private: + QString addMarkUp(const QString& protectedCode, + const Node *relative, + const QString& dirPath); +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qmldebugger/expressionquerywidget.cpp b/tools/qmldebugger/expressionquerywidget.cpp new file mode 100644 index 0000000..b29b465 --- /dev/null +++ b/tools/qmldebugger/expressionquerywidget.cpp @@ -0,0 +1,216 @@ +#include <QtGui/qlabel.h> +#include <QtGui/qtextedit.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qpushbutton.h> +#include <QtGui/qevent.h> +#include <QtGui/qgroupbox.h> +#include <QtGui/qtextobject.h> +#include <QtGui/qlayout.h> + +#include "expressionquerywidget.h" + +ExpressionQueryWidget::ExpressionQueryWidget(QmlEngineDebug *client, QWidget *parent) + : QWidget(parent), + m_style(Compact), + m_client(client), + m_query(0), + m_groupBox(0), + m_textEdit(new QTextEdit), + m_lineEdit(0), + m_button(0) +{ + m_prompt = QLatin1String(">> "); + + m_groupBox = new QGroupBox; + QVBoxLayout *vbox = new QVBoxLayout(m_groupBox); + vbox->addWidget(m_textEdit); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(m_groupBox); + + updateTitle(); + + if (m_style == Compact) { + QHBoxLayout *hbox = new QHBoxLayout; + m_button = new QPushButton(tr("Execute")); + m_button->setEnabled(false); + connect(m_button, SIGNAL(clicked()), SLOT(executeExpression())); + m_lineEdit = new QLineEdit; + connect(m_lineEdit, SIGNAL(returnPressed()), SLOT(executeExpression())); + connect(m_lineEdit, SIGNAL(textChanged(QString)), SLOT(lineEditTextChanged(QString))); + hbox->addWidget(new QLabel(tr("Expression:"))); + hbox->addWidget(m_lineEdit); + hbox->addWidget(m_button); + vbox->addLayout(hbox); + + m_textEdit->setReadOnly(true); + m_lineEdit->installEventFilter(this); + } else { + m_textEdit->installEventFilter(this); + } +} + +void ExpressionQueryWidget::updateTitle() +{ + if (m_currObject.debugId() < 0) { + m_groupBox->setTitle(tr("Expression queries")); + } else { + QString desc = QLatin1String("<") + + m_currObject.className() + QLatin1String(": ") + + (m_currObject.name().isEmpty() ? QLatin1String("<unnamed>") : m_currObject.name()) + + QLatin1String(">"); + m_groupBox->setTitle(tr("Expression queries (using context for %1)" + , "Selected object").arg(desc)); + } +} + +void ExpressionQueryWidget::appendPrompt() +{ + m_textEdit->moveCursor(QTextCursor::End); + + if (m_style == Compact) { + m_textEdit->insertPlainText("\n"); + } else { + m_textEdit->setTextColor(Qt::gray); + m_textEdit->append(m_prompt); + } +} + +void ExpressionQueryWidget::setCurrentObject(const QmlDebugObjectReference &obj) +{ + m_currObject = obj; + updateTitle(); +} + +void ExpressionQueryWidget::checkCurrentContext() +{ + m_textEdit->moveCursor(QTextCursor::End); + + if (m_currObject.debugId() != -1 && m_currObject.debugId() != m_objectAtLastFocus.debugId()) + showCurrentContext(); + m_objectAtLastFocus = m_currObject; +} + +void ExpressionQueryWidget::showCurrentContext() +{ + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->setTextColor(Qt::darkGreen); + m_textEdit->append(m_currObject.className() + + QLatin1String(": ") + + (m_currObject.name().isEmpty() ? QLatin1String("<unnamed>") : m_currObject.name())); + appendPrompt(); +} + +void ExpressionQueryWidget::executeExpression() +{ + if (m_style == Compact) + m_expr = m_lineEdit->text().trimmed(); + else + m_expr = m_expr.trimmed(); + + if (!m_expr.isEmpty() && m_currObject.debugId() != -1) { + if (m_query) + delete m_query; + m_query = m_client->queryExpressionResult(m_currObject.debugId(), m_expr, this); + if (!m_query->isWaiting()) + showResult(); + else + QObject::connect(m_query, SIGNAL(stateChanged(State)), + this, SLOT(showResult())); + + m_lastExpr = m_expr; + if (m_lineEdit) + m_lineEdit->clear(); + } +} + +void ExpressionQueryWidget::showResult() +{ + if (m_query) { + m_textEdit->moveCursor(QTextCursor::End); + QString result; + if (m_query->result().isNull()) + result = QLatin1String("<no value>"); + else + result = m_query->result().toString(); + + if (m_style == Compact) { + m_textEdit->setTextColor(Qt::black); + m_textEdit->setFontWeight(QFont::Bold); + m_textEdit->insertPlainText(m_expr + " : "); + m_textEdit->setFontWeight(QFont::Normal); + m_textEdit->insertPlainText(result); + } else { + m_textEdit->append(result); + } + appendPrompt(); + m_expr.clear(); + } +} + +void ExpressionQueryWidget::lineEditTextChanged(const QString &s) +{ + if (m_button) + m_button->setEnabled(!s.isEmpty()); +} + +bool ExpressionQueryWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_textEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter) { + executeExpression(); + return true; + } else if (key == Qt::Key_Backspace) { + // ensure m_expr doesn't contain backspace characters + QTextCursor cursor = m_textEdit->textCursor(); + bool atLastLine = !(cursor.block().next().isValid()); + if (!atLastLine) + return true; + if (cursor.columnNumber() <= m_prompt.count()) + return true; + cursor.deletePreviousChar(); + m_expr = cursor.block().text().mid(m_prompt.count()); + return true; + } else { + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->setTextColor(Qt::black); + m_expr += keyEvent->text(); + } + break; + } + case QEvent::FocusIn: + checkCurrentContext(); + m_textEdit->moveCursor(QTextCursor::End); + break; + default: + break; + } + } else if (obj == m_lineEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Up && m_lineEdit->text() != m_lastExpr) { + m_expr = m_lineEdit->text(); + if (!m_lastExpr.isEmpty()) + m_lineEdit->setText(m_lastExpr); + } else if (key == Qt::Key_Down) { + m_lineEdit->setText(m_expr); + } + break; + } + case QEvent::FocusIn: + checkCurrentContext(); + break; + default: + break; + } + } + return QWidget::eventFilter(obj, event); +} diff --git a/tools/qmldebugger/expressionquerywidget.h b/tools/qmldebugger/expressionquerywidget.h new file mode 100644 index 0000000..8db8f9f --- /dev/null +++ b/tools/qmldebugger/expressionquerywidget.h @@ -0,0 +1,62 @@ +#ifndef EXPRESSIONQUERYWIDGET_H +#define EXPRESSIONQUERYWIDGET_H + +#include <QWidget> + +#include <QtDeclarative/qmldebug.h> + +QT_BEGIN_NAMESPACE + +class QGroupBox; +class QTextEdit; +class QLineEdit; +class QPushButton; + +class ExpressionQueryWidget : public QWidget +{ + Q_OBJECT +public: + enum Style { + Compact, + Shell + }; + + ExpressionQueryWidget(QmlEngineDebug *client, QWidget *parent = 0); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +public slots: + void setCurrentObject(const QmlDebugObjectReference &obj); + +private slots: + void executeExpression(); + void showResult(); + void lineEditTextChanged(const QString &s); + +private: + void appendPrompt(); + void checkCurrentContext(); + void showCurrentContext(); + void updateTitle(); + + Style m_style; + + QmlEngineDebug *m_client; + QmlDebugExpressionQuery *m_query; + QGroupBox *m_groupBox; + QTextEdit *m_textEdit; + QLineEdit *m_lineEdit; + QPushButton *m_button; + QString m_prompt; + QString m_expr; + QString m_lastExpr; + + QmlDebugObjectReference m_currObject; + QmlDebugObjectReference m_objectAtLastFocus; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/tools/qmldebugger/objectpropertiesview.cpp b/tools/qmldebugger/objectpropertiesview.cpp index f725194..61afe3f 100644 --- a/tools/qmldebugger/objectpropertiesview.cpp +++ b/tools/qmldebugger/objectpropertiesview.cpp @@ -22,12 +22,12 @@ public: }; PropertiesViewItem::PropertiesViewItem(QTreeWidget *widget) -: QTreeWidgetItem(widget) + : QTreeWidgetItem(widget) { } PropertiesViewItem::PropertiesViewItem(QTreeWidgetItem *parent) -: QTreeWidgetItem(parent) + : QTreeWidgetItem(parent) { } @@ -108,8 +108,10 @@ void ObjectPropertiesView::setObject(const QmlDebugObjectReference &object) item->setText(0, p.name()); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - if (!p.hasNotifySignal()) - item->setForeground(0, Qt::lightGray); + if (!p.hasNotifySignal()) { + item->setForeground(0, Qt::gray); + item->setForeground(1, Qt::gray); + } if (!p.binding().isEmpty()) { PropertiesViewItem *binding = new PropertiesViewItem(item); @@ -159,15 +161,22 @@ void ObjectPropertiesView::valueChanged(const QByteArray &name, const QVariant & { for (int i=0; i<m_tree->topLevelItemCount(); i++) { PropertiesViewItem *item = static_cast<PropertiesViewItem *>(m_tree->topLevelItem(i)); - if (item->property.name() == name) - item->setText(1, value.toString()); + if (item->property.name() == name) { + if (value.isNull()) { + item->setText(1, QLatin1String("<null>") + + QLatin1String(" : ") + + item->property.valueTypeName()); + } else { + item->setText(1, value.toString()); + } + } } } void ObjectPropertiesView::itemActivated(QTreeWidgetItem *i) { PropertiesViewItem *item = static_cast<PropertiesViewItem *>(i); - if (!item->property.name().isEmpty() && item->property.hasNotifySignal()) + if (!item->property.name().isEmpty()) emit activated(m_object, item->property); } diff --git a/tools/qmldebugger/objecttree.cpp b/tools/qmldebugger/objecttree.cpp index f68e7f1..0b92ceb 100644 --- a/tools/qmldebugger/objecttree.cpp +++ b/tools/qmldebugger/objecttree.cpp @@ -19,8 +19,8 @@ ObjectTree::ObjectTree(QmlEngineDebug *client, QWidget *parent) { setHeaderHidden(true); - connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)), - this, SLOT(handleItemClicked(QTreeWidgetItem *))); + connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(currentItemChanged(QTreeWidgetItem *))); } void ObjectTree::reload(int objectDebugId) @@ -38,7 +38,7 @@ void ObjectTree::reload(int objectDebugId) this, SLOT(objectFetched())); } -void ObjectTree::selectObject(int debugId) +void ObjectTree::setCurrentObject(int debugId) { QTreeWidgetItem *item = findItemByObjectId(debugId); if (item) { @@ -57,14 +57,14 @@ void ObjectTree::objectFetched() m_query = 0; } -void ObjectTree::handleItemClicked(QTreeWidgetItem *item) +void ObjectTree::currentItemChanged(QTreeWidgetItem *item) { QmlDebugObjectReference obj = item->data(0, Qt::UserRole).value<QmlDebugObjectReference>(); if (obj.debugId() < 0) { qWarning("QML Object Tree: bad object id"); return; } - emit objectSelected(obj); + emit currentObjectChanged(obj); } void ObjectTree::buildTree(const QmlDebugObjectReference &obj, QTreeWidgetItem *parent) diff --git a/tools/qmldebugger/objecttree.h b/tools/qmldebugger/objecttree.h index bba6c08..3c0a5c6 100644 --- a/tools/qmldebugger/objecttree.h +++ b/tools/qmldebugger/objecttree.h @@ -20,19 +20,19 @@ public: ObjectTree(QmlEngineDebug *client, QWidget *parent = 0); signals: - void objectSelected(const QmlDebugObjectReference &); + void currentObjectChanged(const QmlDebugObjectReference &); void expressionWatchRequested(const QmlDebugObjectReference &, const QString &); public slots: void reload(int objectDebugId); - void selectObject(int debugId); + void setCurrentObject(int debugId); protected: virtual void mousePressEvent(QMouseEvent *); private slots: void objectFetched(); - void handleItemClicked(QTreeWidgetItem *); + void currentItemChanged(QTreeWidgetItem *); private: QTreeWidgetItem *findItemByObjectId(int debugId) const; diff --git a/tools/qmldebugger/qmldebugger.cpp b/tools/qmldebugger/qmldebugger.cpp index 0f0fc03..e0a76b6 100644 --- a/tools/qmldebugger/qmldebugger.cpp +++ b/tools/qmldebugger/qmldebugger.cpp @@ -57,6 +57,10 @@ QmlDebugger::QmlDebugger(QWidget *parent) QObject::connect(&client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connectionError(QAbstractSocket::SocketError))); + + + m_tabs->setCurrentIndex(1); + connectToHost(); } void QmlDebugger::setHost(const QString &host) diff --git a/tools/qmldebugger/qmldebugger.pri b/tools/qmldebugger/qmldebugger.pri index ce36381..c49d334 100644 --- a/tools/qmldebugger/qmldebugger.pri +++ b/tools/qmldebugger/qmldebugger.pri @@ -7,7 +7,8 @@ HEADERS += $$PWD/qmldebugger.h \ $$PWD/watchtable.h \ $$PWD/engine.h \ $$PWD/objecttree.h \ - $$PWD/objectpropertiesview.h + $$PWD/objectpropertiesview.h \ + $$PWD/expressionquerywidget.h SOURCES += $$PWD/qmldebugger.cpp \ $$PWD/main.cpp \ @@ -15,7 +16,8 @@ SOURCES += $$PWD/qmldebugger.cpp \ $$PWD/watchtable.cpp \ $$PWD/engine.cpp \ $$PWD/objecttree.cpp \ - $$PWD/objectpropertiesview.cpp + $$PWD/objectpropertiesview.cpp \ + $$PWD/expressionquerywidget.cpp RESOURCES += $$PWD/qmldebugger.qrc diff --git a/tools/qmldebugger/watchtable.cpp b/tools/qmldebugger/watchtable.cpp index e4163dc..512bfb2 100644 --- a/tools/qmldebugger/watchtable.cpp +++ b/tools/qmldebugger/watchtable.cpp @@ -193,6 +193,9 @@ void WatchTableModel::addValue(int column, const QVariant &value) void WatchTableModel::togglePropertyWatch(const QmlDebugObjectReference &object, const QmlDebugPropertyReference &property) { + if (!property.hasNotifySignal()) + return; + QmlDebugWatch *watch = findWatch(object.debugId(), property.name()); if (watch) { // watch will be deleted in watchStateChanged() diff --git a/tools/qmlviewer/qmlviewer.cpp b/tools/qmlviewer/qmlviewer.cpp index 95dfd16..60fa13a 100644 --- a/tools/qmlviewer/qmlviewer.cpp +++ b/tools/qmlviewer/qmlviewer.cpp @@ -628,6 +628,11 @@ void QmlViewer::openQml(const QString& fileName) QUrl url(fileName); QFileInfo fi(fileName); if (fi.exists()) { + if (fi.suffix().toLower() != QLatin1String("qml")) { + qWarning() << "qmlviewer cannot open non-QML file" << fileName; + return; + } + url = QUrl::fromLocalFile(fi.absoluteFilePath()); QmlContext *ctxt = canvas->rootContext(); QDir dir(fi.path()+"/dummydata", "*.qml"); @@ -656,6 +661,9 @@ void QmlViewer::openQml(const QString& fileName) dummyData->setParent(this); } } + } else { + qWarning() << "qmlviewer cannot find file:" << fileName; + return; } canvas->setUrl(url); |