doc/src/examples/context2d.qdoc
branchRCL_3
changeset 7 3f74d0d4af4c
equal deleted inserted replaced
6:dee5afe5301f 7:3f74d0d4af4c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the documentation of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /*!
       
    43     \example script/context2d
       
    44     \title Context2D Example
       
    45 
       
    46     This Qt Script example is an implementation of the Context2D API.
       
    47 
       
    48     \image context2d-example.png
       
    49 
       
    50     Context2D is part of the specification for the HTML \c{<canvas>}
       
    51     element. It can be used to draw graphics via scripting. A good
       
    52     resource for learning more about the HTML \c{<canvas>} element is
       
    53     the \l{http://developer.mozilla.org/en/docs/HTML:Canvas}{Mozilla Developer Center}.
       
    54 
       
    55     \section1 Using The HTML Canvas Element in a Web Browser
       
    56 
       
    57     First, let's look at how the \c{<canvas>} element is typically
       
    58     used in a web browser. The following HTML snippet defines a
       
    59     canvas of size 400x400 pixels with id \c{mycanvas}:
       
    60 
       
    61     \code
       
    62     <canvas width="400" height="400" id="mycanvas">Fallback content goes here.</canvas>
       
    63     \endcode
       
    64 
       
    65     To draw on the canvas, we must first obtain a reference to the
       
    66     DOM element corresponding to the \c{<canvas>} tag and then call
       
    67     the element's getContext() function. The resulting object
       
    68     implements the Context2D API that we use to draw.
       
    69 
       
    70     \code
       
    71     <script>
       
    72     var canvas = document.getElementById("mycanvas");
       
    73     var ctx = canvas.getContext("2d");
       
    74 
       
    75     // Draw a face
       
    76     ctx.beginPath();
       
    77     ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
       
    78     ctx.moveTo(110,75);
       
    79     ctx.arc(75,75,35,0,Math.PI,false);   // Mouth
       
    80     ctx.moveTo(65,65);
       
    81     ctx.arc(60,65,5,0,Math.PI*2,true);  // Left eye
       
    82     ctx.moveTo(95,65);
       
    83     ctx.arc(90,65,5,0,Math.PI*2,true);  // Right eye
       
    84     ctx.stroke();
       
    85     </script>
       
    86     \endcode
       
    87 
       
    88     When the page is rendered by a browser that supports the
       
    89     \c{<canvas>} tag, this would be the result:
       
    90 
       
    91     \image context2d-example-smileysmile.png
       
    92 
       
    93     \section1 Using Qt Script to script a Canvas
       
    94 
       
    95     The goal of this example is to be able to evaluate scripts
       
    96     that use the Context2D API, and render the results. Basic
       
    97     interaction (mouse, keyboard) should also be supported.
       
    98     In other words, we want to present scripts with an execution
       
    99     environment that very much resembles that of a web browser. Of
       
   100     course, our environment is only a small subset of what a browser
       
   101     provides; i.e. we don't provide a full DOM API, only what is
       
   102     needed to run "self-contained" Context2D scripts (i.e. scripts
       
   103     that don't depend on other parts of the DOM document).
       
   104 
       
   105     Our "Context2D-browser" is set up through the following steps:
       
   106     \list
       
   107     \o Create an Environment.
       
   108     \o Create a Context2D, and a QContext2DCanvas widget to render it.
       
   109     \o Add the canvas object to the environment; this will enable
       
   110        scripts to obtain a reference to it.
       
   111     \o Evaluate scripts in the environment.
       
   112     \endlist
       
   113 
       
   114     Once a script has been evaluated, the application handles any
       
   115     timer events and input events that occur subsequently
       
   116     (i.e. forwards events to their associated script targets).
       
   117 
       
   118     \section1 The Context2D Class
       
   119 
       
   120     The "heart" of this example is the Context2D C++ class that implements
       
   121     the drawing API. Its interface is defined in terms of properties
       
   122     and slots. Note that this class isn't tied to Qt Script in any
       
   123     way.
       
   124 
       
   125     \snippet examples/script/context2d/context2d.h 0
       
   126 
       
   127     The properties define various aspects of the Context2D
       
   128     configuration.
       
   129     
       
   130     \snippet examples/script/context2d/context2d.h 1
       
   131 
       
   132     The slots define the operations that can be performed.
       
   133     
       
   134     \snippet examples/script/context2d/context2d.h 2
       
   135 
       
   136     The changed() signal is emitted when the contents of the drawing
       
   137     area has changed, so that clients associated with the Context2D
       
   138     object (i.e. the canvas widget that renders it) are notified.
       
   139 
       
   140     \section2 Implementation
       
   141 
       
   142     Conveniently enough, the concepts, data structures and operations
       
   143     of the Context2D API map more or less directly to Qt's painting
       
   144     API. Conceptually, all we have to do is initialize a QPainter
       
   145     according to the Context2D properties, and use functions like
       
   146     QPainter::strokePath() to do the painting. Painting is done on a
       
   147     QImage.
       
   148 
       
   149     \snippet examples/script/context2d/context2d.cpp 0
       
   150 
       
   151     The property accessors and most of the slots manipulate the
       
   152     internal Context2D state in some way. For the \c{lineCap}
       
   153     property, Context2D uses a string representation; we therefore
       
   154     have to map it from/to a Qt::PenCapStyle. The \c{lineJoin}
       
   155     property is handled in the same fashion. All the property setters
       
   156     also set a \e{dirty flag} for the property; this is used to
       
   157     decide which aspects of the QPainter that need to be updated
       
   158     before doing the next painting operation.
       
   159 
       
   160     \snippet examples/script/context2d/context2d.cpp 3
       
   161 
       
   162     The implementation of the \c{fillStyle} property is interesting,
       
   163     since the value can be either a string or a \c{CanvasGradient}.
       
   164     We handle this by having the property be of type QVariant,
       
   165     and check the actual type of the value to see how to handle the
       
   166     write.
       
   167 
       
   168     \snippet examples/script/context2d/context2d.cpp 1
       
   169 
       
   170     Context2D does not have a concept of a paint event; painting
       
   171     operations can happen at any time. We would like to be efficient,
       
   172     and not have to call QPainter::begin() and QPainter::end() for
       
   173     every painting operation, since typically many painting operations
       
   174     will follow in quick succession. The implementations of the
       
   175     painting operations use a helper function, beginPainting(), that
       
   176     activates the QPainter if it isn't active already, and updates
       
   177     the state of the QPainter (brush, pen, etc.) so that it reflects
       
   178     the current Context2D state.
       
   179 
       
   180     \snippet examples/script/context2d/context2d.cpp 2
       
   181 
       
   182     The implementation of each painting operation ends by calling
       
   183     scheduleChange(), which will post a zero-timer event if one is
       
   184     not already pending. When the application returns to the event
       
   185     loop later (presumably after all the drawing operations have
       
   186     finished), the timer will trigger, QPainter::end() will be
       
   187     called, and the changed() signal is emitted with the new
       
   188     image as argument. The net effect is that there will typically
       
   189     be only a single (QPainter::begin(), QPainter::end()) pair
       
   190     executed for the full sequence of painting operations.
       
   191 
       
   192     \section1 The Canvas Widget
       
   193 
       
   194     \snippet examples/script/context2d/qcontext2dcanvas.h 0
       
   195 
       
   196     The QContext2DCanvas class provides a widget that renders
       
   197     the contents of a Context2D object. It also provides a
       
   198     minimal scripting API, most notably the getContext() function.
       
   199 
       
   200     \snippet examples/script/context2d/qcontext2dcanvas.cpp 3
       
   201 
       
   202     The constructor connects to the changed() signal of the
       
   203     Context2D object, so that the widget can update itself
       
   204     when it needs to do so. Mouse tracking is enabled so that
       
   205     mouse move events will be received even when no mouse
       
   206     buttons are depressed.
       
   207 
       
   208     \snippet examples/script/context2d/qcontext2dcanvas.cpp 0
       
   209 
       
   210     The getContext() function asks the environment to wrap the
       
   211     Context2D object; the resulting proxy object makes the
       
   212     Context2D API available to scripts.
       
   213 
       
   214     \snippet examples/script/context2d/qcontext2dcanvas.cpp 1
       
   215 
       
   216     The paintEvent() function simply paints the contents that
       
   217     was last received from the Context2D object.
       
   218 
       
   219     \snippet examples/script/context2d/qcontext2dcanvas.cpp 2
       
   220 
       
   221     The canvas widget reimplements mouse and key event handlers, and
       
   222     forwards these events to the scripting environment. The
       
   223     environment will take care of delivering the event to the proper
       
   224     script target, if any.
       
   225 
       
   226     \section1 The Environment
       
   227 
       
   228     \snippet examples/script/context2d/environment.h 0
       
   229 
       
   230     The Environment class provides a scripting environment where a
       
   231     Canvas C++ object can be registered, looked up by ID (name),
       
   232     and where scripts can be evaluated. The environment has a
       
   233     \c{document} property, just like the scripting environment of a
       
   234     web browser, so that scripts can call
       
   235     \c{document.getElementById()} to obtain a reference to a canvas.
       
   236 
       
   237     \snippet examples/script/context2d/environment.h 1
       
   238 
       
   239     The Environment class provides the timer attributes of the DOM
       
   240     Window Object interface. This enables us to support scripts that
       
   241     do animation, for example.
       
   242     
       
   243     \snippet examples/script/context2d/environment.h 2
       
   244 
       
   245     The scriptError() signal is emitted when evaluation of a script
       
   246     causes a script exception. For example, if a mouse press handler
       
   247     or timeout handler causes an exception, the environment's client(s)
       
   248     will be notified of this and can report the error.
       
   249 
       
   250     \snippet examples/script/context2d/environment.cpp 0
       
   251 
       
   252     The constructor initializes the environment. First it creates
       
   253     the QScriptEngine that will be used to evaluate scripts. It
       
   254     creates the Document object that provides the getElementById()
       
   255     function. Note that the QScriptEngine::ExcludeSuperClassContents
       
   256     flag is specified to avoid the wrapper objects from exposing properties
       
   257     and methods inherited from QObject. Next, the environment wraps
       
   258     a pointer to \e{itself}; this is to prepare for setting this object
       
   259     as the script engine's Global Object. The properties of the standard
       
   260     Global Object are copied, so that these will also be available in
       
   261     our custom Global Object. We also create two self-references to the
       
   262     object; again, this is to provide a minimal level of compabilitity
       
   263     with the scripting environment that web browsers provide.
       
   264 
       
   265     \snippet examples/script/context2d/environment.cpp 5
       
   266 
       
   267     The addCanvas() function adds the given canvas to the list of
       
   268     registered canvas objects. The canvasByName() function looks up
       
   269     a canvas by QObject::objectName(). This function is used to
       
   270     implement the \c{document.getElementById()} script function.
       
   271 
       
   272     \snippet examples/script/context2d/environment.cpp 1
       
   273 
       
   274     The setInterval() and clearInterval() implementations use a QHash
       
   275     to map from timer ID to the QScriptValue that holds the expression
       
   276     to evaluate when the timer is triggered. A helper function,
       
   277     maybeEmitScriptError(), is called after invoking the script handler;
       
   278     it will emit the scriptError() signal if the script engine has an
       
   279     uncaught exception.
       
   280 
       
   281     \snippet examples/script/context2d/environment.cpp 2
       
   282 
       
   283     The toWrapper() functions creates a QScriptValue that wraps the
       
   284     given QObject. Note that the QScriptEngine::PreferExistingWrapperObject
       
   285     flag is specified; this guarantees that a single, unique wrapper
       
   286     object will be returned, even if toWrapper() is called several times
       
   287     with the same argument. This is important, since it is possible that
       
   288     a script can set new properties on the resulting wrapper object (e.g.
       
   289     event handlers like \c{onmousedown}), and we want these to persist.
       
   290 
       
   291     \snippet examples/script/context2d/environment.cpp 3
       
   292 
       
   293     The handleEvent() function determines if there exists a handler
       
   294     for the given event in the environment, and if so, invokes that
       
   295     handler. Since the script expects a DOM event, the Qt C++ event
       
   296     must be converted to a DOM event before it is passed to the
       
   297     script. This mapping is relatively straightforward, but again,
       
   298     we only implement a subset of the full DOM API; just enough to
       
   299     get most scripts to work.
       
   300 
       
   301     \snippet examples/script/context2d/environment.cpp 4
       
   302 
       
   303     The newFakeDomEvent() function is a helper function that creates
       
   304     a new script object and initializes it with default values for
       
   305     the attributes defined in the DOM Event and DOM UIEvent
       
   306     interfaces.
       
   307 
       
   308     \snippet examples/script/context2d/environment.h 3
       
   309 
       
   310     The Document class defines two slots that become available to
       
   311     scripts: getElementById() and getElementsByTagName().
       
   312     When the tag name is "canvas", getElementsByTagName() will
       
   313     return a list of all canvas objects that are registered in
       
   314     the environment.
       
   315 
       
   316     \section1 The Application Window
       
   317 
       
   318     \snippet examples/script/context2d/window.cpp 0
       
   319 
       
   320     The Window constructor creates an Environment object and
       
   321     connects to its scriptError() signal. It then creates a
       
   322     Context2D object, and a QContext2DCanvas widget to hold it.
       
   323     The canvas widget is given the name \c{tutorial}, and added to the
       
   324     environment; scripts can access the canvas by e.g.
       
   325     \c{document.getElementById('tutorial')}.
       
   326 
       
   327     \snippet examples/script/context2d/window.cpp 1
       
   328 
       
   329     The window contains a list widget that is populated with
       
   330     available scripts (read from a \c{scripts/} folder).
       
   331 
       
   332     \snippet examples/script/context2d/window.cpp 2
       
   333 
       
   334     When an item is selected, the corresponding script is
       
   335     evaluated in the environment.
       
   336 
       
   337     \snippet examples/script/context2d/window.cpp 3
       
   338 
       
   339     When the "Run in Debugger" button is clicked, the Qt Script debugger will
       
   340     automatically be invoked when the first statement of the script is
       
   341     reached. This enables the user to inspect the scripting environment and
       
   342     control further execution of the script; e.g. he can single-step through
       
   343     the script and/or set breakpoints. It is also possible to enter script
       
   344     statements in the debugger's console widget, e.g. to perform custom
       
   345     Context2D drawing operations, interactively.
       
   346 
       
   347     \snippet examples/script/context2d/window.cpp 4
       
   348 
       
   349     If the evaluation of a script causes an uncaught exception, the Qt Script
       
   350     debugger will automatically be invoked; this enables the user to get an
       
   351     idea of what went wrong.
       
   352 
       
   353 */