QDeclarativeIncubator doc
authorAaron Kennedy <aaron.kennedy@nokia.com>
Thu, 29 Sep 2011 06:06:27 +0000 (16:06 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 29 Sep 2011 06:31:26 +0000 (08:31 +0200)
Change-Id: I4fdc9e55112187039dd6210e9bc92b1da183501b
Task-number: QTBUG-21151
Reviewed-on: http://codereview.qt-project.org/5776
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>

src/declarative/qml/qdeclarativecomponent.cpp
src/declarative/qml/qdeclarativecomponent.h
src/declarative/qml/qdeclarativeincubator.cpp
src/declarative/qml/qdeclarativeincubator.h

index 50c6471..0d390b0 100644 (file)
@@ -75,7 +75,7 @@ public:
 
     v8::Persistent<v8::Function> incubationConstructor;
     v8::Persistent<v8::Script> initialProperties;
-    v8::Persistent<v8::Function> forceIncubation;
+    v8::Persistent<v8::Function> forceCompletion;
 };
 static V8_DEFINE_EXTENSION(QDeclarativeComponentExtension, componentExtension);
 
@@ -891,10 +891,8 @@ void QDeclarativeComponent::create(QDeclarativeIncubator &i, QDeclarativeContext
 {
     Q_D(QDeclarativeComponent);
 
-    if (!context) {
-        qWarning("QDeclarativeComponent: Cannot create a component in a null context");
-        return;
-    }
+    if (!context) 
+        context = d->engine->rootContext();
 
     QDeclarativeContextData *contextData = QDeclarativeContextData::get(context);
     QDeclarativeContextData *forContextData = contextData;
@@ -939,9 +937,9 @@ public:
                                               const v8::AccessorInfo& info);
     static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>, 
                                               const v8::AccessorInfo& info);
-    static v8::Handle<v8::Value> ForceIncubationGetter(v8::Local<v8::String>, 
+    static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>, 
                                                        const v8::AccessorInfo& info);
-    static v8::Handle<v8::Value> ForceIncubation(const v8::Arguments &args);
+    static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args);
 
     static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, 
                                     const v8::AccessorInfo& info);
@@ -1165,7 +1163,7 @@ QDeclarativeComponentExtension::QDeclarativeComponentExtension(QV8Engine *engine
     v8::HandleScope handle_scope;
     v8::Context::Scope scope(engine->context());
 
-    forceIncubation = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceIncubation, engine));
+    forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine));
 
     {
     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
@@ -1178,8 +1176,8 @@ QDeclarativeComponentExtension::QDeclarativeComponentExtension(QV8Engine *engine
                                         QV8IncubatorResource::StatusGetter);
     ft->InstanceTemplate()->SetAccessor(v8::String::New("object"), 
                                         QV8IncubatorResource::ObjectGetter); 
-    ft->InstanceTemplate()->SetAccessor(v8::String::New("forceIncubation"), 
-                                        QV8IncubatorResource::ForceIncubationGetter); 
+    ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"), 
+                                        QV8IncubatorResource::ForceCompletionGetter); 
     incubationConstructor = qPersistentNew(ft->GetFunction());
     }
 
@@ -1211,20 +1209,20 @@ v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>,
     return r->engine->newQObject(r->object());
 }
 
-v8::Handle<v8::Value> QV8IncubatorResource::ForceIncubationGetter(v8::Local<v8::String>, 
+v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>, 
                                                                   const v8::AccessorInfo& info)
 {
     QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This());
-    return componentExtension(r->engine)->forceIncubation;
+    return componentExtension(r->engine)->forceCompletion;
 }
 
-v8::Handle<v8::Value> QV8IncubatorResource::ForceIncubation(const v8::Arguments &args) 
+v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args) 
 {
     QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This());
     if (!r)
         V8THROW_TYPE("Not an incubator object");
 
-    r->forceIncubation();
+    r->forceCompletion();
 
     return v8::Undefined();
 }
index 92af689..385d3d3 100644 (file)
@@ -100,7 +100,7 @@ public:
     virtual QObject *beginCreate(QDeclarativeContext *);
     virtual void completeCreate();
 
-    void create(QDeclarativeIncubator &, QDeclarativeContext *context,
+    void create(QDeclarativeIncubator &, QDeclarativeContext *context = 0,
                 QDeclarativeContext *forContext = 0);
 
     QDeclarativeContext *creationContext() const;
index e10575f..4fab283 100644 (file)
@@ -48,6 +48,7 @@
 // XXX TODO 
 //   - check that the Component.onCompleted behavior is the same as 4.8 in the synchronous and 
 //     async if nested cases
+//   - implement QDeclarativeIncubator::clear()
 void QDeclarativeEnginePrivate::incubate(QDeclarativeIncubator &i, QDeclarativeContextData *forContext)
 {
     QDeclarativeIncubatorPrivate *p = i.d;
@@ -88,13 +89,24 @@ void QDeclarativeEnginePrivate::incubate(QDeclarativeIncubator &i, QDeclarativeC
     }
 }
 
-void QDeclarativeEngine::setIncubationController(QDeclarativeIncubationController *i)
+/*!
+Sets the engine's incubation \a controller.  The engine can only have one active controller 
+and it does not take ownership of it.
+
+\sa incubationController()
+*/
+void QDeclarativeEngine::setIncubationController(QDeclarativeIncubationController *controller)
 {
     Q_D(QDeclarativeEngine);
-    d->incubationController = i;
-    if (i) i->d = d;
+    d->incubationController = controller;
+    if (controller) controller->d = d;
 }
 
+/*!
+Returns the currently set incubation controller, or 0 if no controller has been set.
+
+\sa setIncubationController()
+*/
 QDeclarativeIncubationController *QDeclarativeEngine::incubationController() const
 {
     Q_D(const QDeclarativeEngine);
@@ -134,6 +146,12 @@ void QDeclarativeIncubatorPrivate::clear()
 
 }
 
+/*!
+
+\class QDeclarativeIncubationController
+
+*/
+
 QDeclarativeIncubationController::QDeclarativeIncubationController()
 : d(0)
 {
@@ -247,21 +265,141 @@ void QDeclarativeIncubationController::incubateWhile(bool *flag)
     } while (d && d->incubatorCount != 0 && !i.shouldInterrupt());
 }
 
-QDeclarativeIncubator::QDeclarativeIncubator(IncubationMode m)
-: d(new QDeclarativeIncubatorPrivate(this, m))
+/*!
+\class QDeclarativeIncubator
+\brief The QDeclarativeIncubator class allows QML objects to be created asynchronously.
+
+Creating QML objects - like delegates in a view, or a new page in an application - can take
+a noticable amount of time, especially on resource constrained mobile devices.  When an
+application uses QDeclarativeComponent::create() directly, the QML object instance is created
+synchronously which, depending on the complexity of the object,  can cause noticable pauses or 
+stutters in the application.
+
+The use of QDeclarativeIncubator gives more control over the creation of a QML object, 
+including allowing it to be created asynchronously using application idle time.  The following 
+example shows a simple use of QDeclarativeIncubator.
+
+\code
+QDeclarativeIncubator incubator;
+component->create(incubator);
+
+while (incubator.isReady()) {
+    QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
+}
+
+QObject *object = incubator.object();
+\endcode
+
+Asynchronous incubators are controlled by a QDeclarativeIncubationController that is 
+set on the QDeclarativeEngine, which lets the engine know when the application is idle and
+incubating objects should be processed.  If an incubation controller is not set on the
+QDeclarativeEngine, QDeclarativeIncubator creates objects synchronously regardless of the
+specified IncubationMode.  
+
+QDeclarativeIncubator supports three incubation modes:
+\list
+\i Synchronous The creation occurs synchronously.  That is, once the 
+QDeclarativeComponent::create() call returns, the incubator will already be in either the
+Error or Ready state.  A synchronous incubator has no real advantage compared to using
+the synchronous creation methods on QDeclarativeComponent directly, but it may simplify an 
+application's implementation to use the same API for both synchronous and asynchronous 
+creations.
+
+\i Asynchronous (default) The creation occurs asynchronously, assuming a 
+QDeclarativeIncubatorController is set on the QDeclarativeEngine.  
+
+The incubator will remain in the Loading state until either the creation is complete or an error 
+occurs.  The statusChanged() callback can be used to be notified of status changes.
+
+Applications should use the Asynchronous incubation mode to create objects that are not needed
+immediately.  For example, the ListView element uses Asynchronous incubation to create objects
+that are slightly off screen while the list is being scrolled.  If, during asynchronous creation,
+the object is needed immediately the QDeclarativeIncubator::forceCompletion() method can be called
+to complete the creation process synchronously.
+
+\i AsynchronousIfNested The creation will occur asynchronously if part of a nested asynchronous 
+creation, or synchronously if not.  
+
+In most scenarios where a QML element or component wants the appearance of a synchronous 
+instantiation, it should use this mode.  
+
+This mode is best explained with an example.  When the ListView element is first created, it needs
+to populate itself with an initial set of delegates to show.  If the ListView was 400 pixels high, 
+and each delegate was 100 pixels high, it would need to create four initial delegate instances.  If
+the ListView used the Asynchronous incubation mode, the ListView would always be created empty and
+then, sometime later, the four initial elements would appear.  
+
+Conversely, if the ListView was to use the Synchronous incubation mode it would behave correctly 
+but it may introduce stutters into the application.  As QML would have to stop and instantiate the 
+ListView's delegates synchronously, if the ListView was part of a QML component that was being 
+instantiated asynchronously this would undo much of the benefit of asynchronous instantiation.
+
+The AsynchronousIfNested mode reconciles this problem.  By using AsynchronousIfNested, the ListView
+delegates are instantiated asynchronously if the ListView itself is already part of an asynchronous
+instantiation, and synchronously otherwise.  In the case of a nested asynchronous instantiation, the
+outer asynchronous instantiation will not complete until after all the nested instantiations have also
+completed.  This ensures that by the time the outer asynchronous instantitation completes, inner 
+elements like ListView have already completed loading their initial delegates.
+
+It is almost always incorrect to use the Synchronous incubation mode - elements or components that 
+want the appearance of synchronous instantiation, but without the downsides of introducing freezes 
+or stutters into the application, should use the AsynchronousIfNested incubation mode.
+\endlist
+*/
+
+/*!
+Create a new incubator with the specified \a mode
+*/
+QDeclarativeIncubator::QDeclarativeIncubator(IncubationMode mode)
+: d(new QDeclarativeIncubatorPrivate(this, mode))
 {
 }
 
+/*! \internal */
 QDeclarativeIncubator::~QDeclarativeIncubator()
 {
     delete d; d = 0;
 }
 
+/*!
+\enum QDeclarativeIncubator::IncubationMode
+
+Specifies the mode the incubator operates in.  Regardless of the incubation mode, a 
+QDeclarativeIncubator will behave synchronously if the QDeclarativeEngine does not have
+a QDeclarativeIncubationController set.
+
+\value Asynchronous The object will be created asynchronously.
+\value AsynchronousIfNested If the object is being created in a context that is already part
+of an asynchronous creation, this incubator will join that existing incubation and execute 
+asynchronously.  The existing incubation will not become Ready until both it and this 
+incubation have completed.  Otherwise, the incubation will execute synchronously.
+\value Synchronous The object will be created synchronously.
+*/
+
+/*!
+\enum QDeclarativeIncubator::Status
+
+Specifies the status of the QDeclarativeIncubator.
+
+\value Null Incubation is not in progress.  Call QDeclarativeComponent::create() to begin incubating.
+\value Ready The object is fully created and can be accessed by calling object().
+\value Loading The object is in the process of being created.
+\value Error An error occurred.  The errors can be access by calling errors().
+*/
+
+/*!
+Clears the incubator.  Any in-progress incubation is aborted.  If the incubator is in the 
+Ready state, the created object is \b not deleted.
+*/
 void QDeclarativeIncubator::clear()
 {
 }
 
-void QDeclarativeIncubator::forceIncubation()
+/*!
+Force any in-progress incubation to finish synchronously.  Once this call
+returns, the incubator will not be in the Loading state.
+*/
+void QDeclarativeIncubator::forceCompletion()
 {
     QDeclarativeVME::Interrupt i;
     while (Loading == status()) {
@@ -273,36 +411,57 @@ void QDeclarativeIncubator::forceIncubation()
 
 }
 
+/*!
+Returns true if the incubator's status() is Null.
+*/
 bool QDeclarativeIncubator::isNull() const
 {
     return status() == Null;
 }
 
+/*!
+Returns true if the incubator's status() is Ready.
+*/
 bool QDeclarativeIncubator::isReady() const
 {
     return status() == Ready;
 }
 
+/*!
+Returns true if the incubator's status() is Error.
+*/
 bool QDeclarativeIncubator::isError() const
 {
     return status() == Error;
 }
 
+/*!
+Returns true if the incubator's status() is Loading.
+*/
 bool QDeclarativeIncubator::isLoading() const
 {
     return status() == Loading;
 }
 
+/*!
+Return the list of errors encountered while incubating the object.
+*/
 QList<QDeclarativeError> QDeclarativeIncubator::errors() const
 {
     return d->errors;
 }
 
+/*!
+Return the incubation mode passed to the QDeclarativeIncubator constructor.
+*/
 QDeclarativeIncubator::IncubationMode QDeclarativeIncubator::incubationMode() const
 {
     return d->mode;
 }
 
+/*!
+Return the current status of the incubator.
+*/
 QDeclarativeIncubator::Status QDeclarativeIncubator::status() const
 {
     if (!d->errors.isEmpty()) return Error;
@@ -312,16 +471,35 @@ QDeclarativeIncubator::Status QDeclarativeIncubator::status() const
     else return Null;
 }
 
+/*!
+Return the incubated object if the status is Ready, otherwise 0.
+*/
 QObject *QDeclarativeIncubator::object() const
 {
     if (status() != Ready) return 0;
     else return d->result;
 }
 
-void QDeclarativeIncubator::statusChanged(Status)
+/*!
+Called when the status of the incubator changes.  \a status is the new status.
+
+The default implementation does nothing.
+*/
+void QDeclarativeIncubator::statusChanged(Status status)
 {
+    Q_UNUSED(status);
 }
 
-void QDeclarativeIncubator::setInitialState(QObject *)
+/*!
+Called after the object is first created, but before property bindings are
+evaluated and, if applicable, QDeclarativeParserStatus::componentComplete() is
+called.  This is equivalent to the point between QDeclarativeComponent::beginCreate()
+and QDeclarativeComponent::endCreate(), and can be used to assign initial values
+to the object's properties.
+
+The default implementation does nothing.
+*/
+void QDeclarativeIncubator::setInitialState(QObject *object)
 {
+    Q_UNUSED(object);
 }
index 5b57cc8..0c6bc84 100644 (file)
@@ -73,7 +73,7 @@ public:
     virtual ~QDeclarativeIncubator();
 
     void clear();
-    void forceIncubation();
+    void forceCompletion();
 
     bool isNull() const;
     bool isReady() const;