|
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 */ |