environment.cpp Example File
script/context2d/environment.cpp
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "environment.h"
#include "qcontext2dcanvas.h"
#include "context2d.h"
#include <QScriptValueIterator>
#include <QDateTime>
struct FakeDomEvent
{
enum KeyCodes {
DOM_VK_UNDEFINED = 0x0 ,
DOM_VK_RIGHT_ALT = 0x12 ,
DOM_VK_LEFT_ALT = 0x12 ,
DOM_VK_LEFT_CONTROL = 0x11 ,
DOM_VK_RIGHT_CONTROL = 0x11 ,
DOM_VK_LEFT_SHIFT = 0x10 ,
DOM_VK_RIGHT_SHIFT = 0x10 ,
DOM_VK_META = 0x9D ,
DOM_VK_BACK_SPACE = 0x08 ,
DOM_VK_CAPS_LOCK = 0x14 ,
DOM_VK_DELETE = 0x7F ,
DOM_VK_END = 0x23 ,
DOM_VK_ENTER = 0x0D ,
DOM_VK_ESCAPE = 0x1B ,
DOM_VK_HOME = 0x24 ,
DOM_VK_NUM_LOCK = 0x90 ,
DOM_VK_PAUSE = 0x13 ,
DOM_VK_PRINTSCREEN = 0x9A ,
DOM_VK_SCROLL_LOCK = 0x91 ,
DOM_VK_SPACE = 0x20 ,
DOM_VK_TAB = 0x09 ,
DOM_VK_LEFT = 0x25 ,
DOM_VK_RIGHT = 0x27 ,
DOM_VK_UP = 0x26 ,
DOM_VK_DOWN = 0x28 ,
DOM_VK_PAGE_DOWN = 0x22 ,
DOM_VK_PAGE_UP = 0x21 ,
DOM_VK_F1 = 0x70 ,
DOM_VK_F2 = 0x71 ,
DOM_VK_F3 = 0x72 ,
DOM_VK_F4 = 0x73 ,
DOM_VK_F5 = 0x74 ,
DOM_VK_F6 = 0x75 ,
DOM_VK_F7 = 0x76 ,
DOM_VK_F8 = 0x77 ,
DOM_VK_F9 = 0x78 ,
DOM_VK_F10 = 0x79 ,
DOM_VK_F11 = 0x7A ,
DOM_VK_F12 = 0x7B ,
DOM_VK_F13 = 0xF000 ,
DOM_VK_F14 = 0xF001 ,
DOM_VK_F15 = 0xF002 ,
DOM_VK_F16 = 0xF003 ,
DOM_VK_F17 = 0xF004 ,
DOM_VK_F18 = 0xF005 ,
DOM_VK_F19 = 0xF006 ,
DOM_VK_F20 = 0xF007 ,
DOM_VK_F21 = 0xF008 ,
DOM_VK_F22 = 0xF009 ,
DOM_VK_F23 = 0xF00A ,
DOM_VK_F24 = 0xF00B
};
static int qtToDomKey(int keyCode);
};
int FakeDomEvent:: qtToDomKey(int keyCode)
{
switch (keyCode) {
case Qt :: Key_Backspace:
return DOM_VK_BACK_SPACE;
case Qt :: Key_Enter:
return DOM_VK_ENTER;
case Qt :: Key_Return:
return DOM_VK_ENTER;
case Qt :: Key_NumLock:
return DOM_VK_NUM_LOCK;
case Qt :: Key_Alt:
return DOM_VK_RIGHT_ALT;
case Qt :: Key_Control:
return DOM_VK_LEFT_CONTROL;
case Qt :: Key_Shift:
return DOM_VK_LEFT_SHIFT;
case Qt :: Key_Meta:
return DOM_VK_META;
case Qt :: Key_CapsLock:
return DOM_VK_CAPS_LOCK;
case Qt :: Key_Delete:
return DOM_VK_DELETE;
case Qt :: Key_End:
return DOM_VK_END;
case Qt :: Key_Escape:
return DOM_VK_ESCAPE;
case Qt :: Key_Home:
return DOM_VK_HOME;
case Qt :: Key_Pause:
return DOM_VK_PAUSE;
case Qt :: Key_Print:
return DOM_VK_PRINTSCREEN;
case Qt :: Key_ScrollLock:
return DOM_VK_SCROLL_LOCK;
case Qt :: Key_Left:
return DOM_VK_LEFT;
case Qt :: Key_Right:
return DOM_VK_RIGHT;
case Qt :: Key_Up:
return DOM_VK_UP;
case Qt :: Key_Down:
return DOM_VK_DOWN;
case Qt :: Key_PageDown:
return DOM_VK_PAGE_DOWN;
case Qt :: Key_PageUp:
return DOM_VK_PAGE_UP;
case Qt :: Key_F1:
return DOM_VK_F1;
case Qt :: Key_F2:
return DOM_VK_F2;
case Qt :: Key_F3:
return DOM_VK_F3;
case Qt :: Key_F4:
return DOM_VK_F4;
case Qt :: Key_F5:
return DOM_VK_F5;
case Qt :: Key_F6:
return DOM_VK_F6;
case Qt :: Key_F7:
return DOM_VK_F7;
case Qt :: Key_F8:
return DOM_VK_F8;
case Qt :: Key_F9:
return DOM_VK_F9;
case Qt :: Key_F10:
return DOM_VK_F10;
case Qt :: Key_F11:
return DOM_VK_F11;
case Qt :: Key_F12:
return DOM_VK_F12;
case Qt :: Key_F13:
return DOM_VK_F13;
case Qt :: Key_F14:
return DOM_VK_F14;
case Qt :: Key_F15:
return DOM_VK_F15;
case Qt :: Key_F16:
return DOM_VK_F16;
case Qt :: Key_F17:
return DOM_VK_F17;
case Qt :: Key_F18:
return DOM_VK_F18;
case Qt :: Key_F19:
return DOM_VK_F19;
case Qt :: Key_F20:
return DOM_VK_F20;
case Qt :: Key_F21:
return DOM_VK_F21;
case Qt :: Key_F22:
return DOM_VK_F22;
case Qt :: Key_F23:
return DOM_VK_F23;
case Qt :: Key_F24:
return DOM_VK_F24;
}
return keyCode;
}
Environment:: Environment(QObject * parent)
: QObject (parent)
{
m_engine = new QScriptEngine (this );
m_document = m_engine- > newQObject(
new Document(this ), QScriptEngine :: QtOwnership ,
QScriptEngine :: ExcludeSuperClassContents);
CanvasGradientPrototype:: setup(m_engine);
m_originalGlobalObject = m_engine- > globalObject();
reset();
}
Environment:: ~ Environment()
{
}
QScriptEngine * Environment:: engine() const
{
return m_engine;
}
QScriptValue Environment:: document() const
{
return m_document;
}
int Environment:: setTimeout(const QScriptValue & expression, int delay)
{
if (expression. isString() | | expression. isFunction()) {
int timerId = startTimer(delay);
m_timeoutHash. insert(timerId, expression);
return timerId;
}
return - 1 ;
}
void Environment:: clearTimeout(int timerId)
{
killTimer(timerId);
m_timeoutHash. remove(timerId);
}
int Environment:: setInterval(const QScriptValue & expression, int delay)
{
if (expression. isString() | | expression. isFunction()) {
int timerId = startTimer(delay);
m_intervalHash. insert(timerId, expression);
return timerId;
}
return - 1 ;
}
void Environment:: clearInterval(int timerId)
{
killTimer(timerId);
m_intervalHash. remove(timerId);
}
void Environment:: timerEvent(QTimerEvent * event)
{
int id = event- > timerId();
QScriptValue expression = m_intervalHash. value(id);
if (! expression. isValid()) {
expression = m_timeoutHash. value(id);
if (expression. isValid())
killTimer(id);
}
if (expression. isString()) {
evaluate(expression. toString());
} else if (expression. isFunction()) {
expression. call();
}
maybeEmitScriptError();
}
void Environment:: addCanvas(QContext2DCanvas * canvas)
{
m_canvases. append(canvas);
}
QContext2DCanvas * Environment:: canvasByName(const QString & name) const
{
for (int i = 0 ; i < m_canvases. size(); + + i) {
QContext2DCanvas * canvas = m_canvases. at(i);
if (canvas- > objectName() = = name)
return canvas;
}
return 0 ;
}
QList < QContext2DCanvas* > Environment:: canvases() const
{
return m_canvases;
}
void Environment:: reset()
{
if (m_engine- > isEvaluating())
m_engine- > abortEvaluation();
{
QHash < int , QScriptValue > :: const_iterator it;
for (it = m_intervalHash. constBegin(); it ! = m_intervalHash. constEnd(); + + it)
killTimer(it. key());
m_intervalHash. clear();
for (it = m_timeoutHash. constBegin(); it ! = m_timeoutHash. constEnd(); + + it)
killTimer(it. key());
m_timeoutHash. clear();
}
for (int i = 0 ; i < m_canvases. size(); + + i)
m_canvases. at(i)- > reset();
QScriptValue self = m_engine- > newQObject(
this , QScriptEngine :: QtOwnership ,
QScriptEngine :: ExcludeSuperClassContents);
{
QScriptValueIterator it(m_originalGlobalObject);
while (it. hasNext()) {
it. next();
self. setProperty(it. scriptName(), it. value(), it. flags());
}
}
self. setProperty("self" , self);
self. setProperty("window" , self);
QScriptValue navigator = m_engine- > newObject();
navigator. setProperty("appCodeName" , "context2d" );
navigator. setProperty("appMinorVersion" , 1 );
navigator. setProperty("appVersion" , 1 );
navigator. setProperty("browserLanguage" , "en_US" );
navigator. setProperty("cookieEnabled" , false );
navigator. setProperty("cpuClass" , "i686" );
navigator. setProperty("onLine" , false );
navigator. setProperty("platform" , "bogus OS" );
navigator. setProperty("systemLanguage" , "en_US" );
navigator. setProperty("userAgent" , "Context2D/1.1" );
navigator. setProperty("userLanguage" , "en_US" );
self. setProperty("navigator" , navigator);
m_engine- > setGlobalObject(self);
m_engine- > collectGarbage();
}
QScriptValue Environment:: evaluate(const QString & code, const QString & fileName)
{
return m_engine- > evaluate(code, fileName);
}
bool Environment:: hasIntervalTimers() const
{
return ! m_intervalHash. isEmpty();
}
// This is used by the Context2D Qt Script benchmark.
void Environment:: triggerTimers()
{
for (int x = 0 ; x < 2 ; + + x) {
QList < int > timerIds = x ? m_intervalHash. keys() : m_timeoutHash. keys();
for (int i = 0 ; i < timerIds. size(); + + i) {
QTimerEvent fakeEvent(timerIds. at(i));
timerEvent(& fakeEvent);
}
}
}
QScriptValue Environment:: toWrapper(QObject * object)
{
return m_engine- > newQObject(object, QScriptEngine :: QtOwnership ,
QScriptEngine :: PreferExistingWrapperObject
| QScriptEngine :: ExcludeSuperClassContents);
}
void Environment:: handleEvent(QContext2DCanvas * canvas, QMouseEvent * e)
{
QString type;
switch (e- > type()) {
case QEvent :: MouseButtonPress:
type = "mousedown" ; break ;
case QEvent :: MouseButtonRelease:
type = "mouseup" ; break ;
case QEvent :: MouseMove:
type = "mousemove" ; break ;
default : break ;
}
if (type. isEmpty())
return ;
QScriptValue handlerObject;
QScriptValue handler = eventHandler(canvas, type, & handlerObject);
if (! handler. isFunction())
return ;
QScriptValue scriptEvent = newFakeDomEvent(type, toWrapper(canvas));
// MouseEvent
scriptEvent. setProperty("screenX" , e- > globalX(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("screenY" , e- > globalY(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("clientX" , e- > x(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("clientY" , e- > y(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("layerX" , e- > x(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("layerY" , e- > y(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("pageX" , e- > x(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("pageY" , e- > y(), QScriptValue :: ReadOnly);
scriptEvent. setProperty("altKey" , (e- > modifiers() & Qt :: AltModifier) ! = 0 ,
QScriptValue :: ReadOnly);
scriptEvent. setProperty("ctrlKey" , (e- > modifiers() & Qt :: ControlModifier) ! = 0 ,
QScriptValue :: ReadOnly);
scriptEvent. setProperty("metaKey" , (e- > modifiers() & Qt :: MetaModifier) ! = 0 ,
QScriptValue :: ReadOnly);
scriptEvent. setProperty("shiftKey" , (e- > modifiers() & Qt :: ShiftModifier) ! = 0 ,
QScriptValue :: ReadOnly);
int button = 0 ;
if (e- > button() = = Qt :: RightButton)
button = 2 ;
else if (e- > button() = = Qt :: MidButton)
button = 1 ;
scriptEvent. setProperty("button" , button);
scriptEvent. setProperty("relatedTarget" , m_engine- > nullValue(),
QScriptValue :: ReadOnly);
handler. call(handlerObject, QScriptValueList () < < scriptEvent);
maybeEmitScriptError();
}
void Environment:: handleEvent(QContext2DCanvas * canvas, QKeyEvent * e)
{
QString type;
switch (e- > type()) {
case QEvent :: KeyPress:
type = "keydown" ; break ;
case QEvent :: KeyRelease:
type = "keyup" ; break ;
default : break ;
}
if (type. isEmpty())
return ;
QScriptValue handlerObject;
QScriptValue handler = eventHandler(canvas, type, & handlerObject);
if (! handler. isFunction())
return ;
QScriptValue scriptEvent = newFakeDomEvent(type, toWrapper(canvas));
// KeyEvent
scriptEvent. setProperty("isChar" , ! e- > text(). isEmpty());
scriptEvent. setProperty("charCode" , e- > text());
scriptEvent. setProperty("keyCode" , FakeDomEvent:: qtToDomKey(e- > key()));
scriptEvent. setProperty("which" , e- > key());
handler. call(handlerObject, QScriptValueList () < < scriptEvent);
maybeEmitScriptError();
}
QScriptValue Environment:: eventHandler(QContext2DCanvas * canvas, const QString & type,
QScriptValue * who)
{
QString handlerName = "on" + type;
QScriptValue obj = toWrapper(canvas);
QScriptValue handler = obj. property(handlerName);
if (! handler. isValid()) {
obj = m_document;
handler = obj. property(handlerName);
}
if (who & & handler. isFunction())
* who = obj;
return handler;
}
QScriptValue Environment:: newFakeDomEvent(const QString & type, const QScriptValue & target)
{
QScriptValue e = m_engine- > newObject();
// Event
e. setProperty("type" , type, QScriptValue :: ReadOnly);
e. setProperty("bubbles" , true , QScriptValue :: ReadOnly);
e. setProperty("cancelable" , false , QScriptValue :: ReadOnly);
e. setProperty("target" , target, QScriptValue :: ReadOnly);
e. setProperty("currentTarget" , target, QScriptValue :: ReadOnly);
e. setProperty("eventPhase" , 3 ); // bubbling
e. setProperty("timeStamp" , QDateTime :: currentDateTime(). toTime_t());
// UIEvent
e. setProperty("detail" , 0 , QScriptValue :: ReadOnly);
e. setProperty("view" , m_engine- > globalObject(), QScriptValue :: ReadOnly);
return e;
}
void Environment:: maybeEmitScriptError()
{
if (m_engine- > hasUncaughtException())
emit scriptError(m_engine- > uncaughtException());
}
Document:: Document(Environment * env)
: QObject (env)
{
}
Document:: ~ Document()
{
}
QScriptValue Document:: getElementById(const QString & id) const
{
Environment * env = qobject_cast< Environment* > (parent());
QContext2DCanvas * canvas = env- > canvasByName(id);
if (! canvas)
return QScriptValue ();
return env- > toWrapper(canvas);
}
QScriptValue Document:: getElementsByTagName(const QString & name) const
{
if (name ! = "canvas" )
return QScriptValue ();
Environment * env = qobject_cast< Environment* > (parent());
QList < QContext2DCanvas* > list = env- > canvases();
QScriptValue result = env- > engine()- > newArray(list. size());
for (int i = 0 ; i < list. size(); + + i)
result. setProperty(i, env- > toWrapper(list. at(i)));
return result;
}
void Document:: addEventListener(const QString & type, const QScriptValue & listener,
bool useCapture)
{
Q_UNUSED(useCapture);
if (listener. isFunction()) {
Environment * env = qobject_cast< Environment* > (parent());
QScriptValue self = env- > toWrapper(this );
self. setProperty("on" + type, listener);
}
}
QColor colorFromString(const QString & name);
CanvasGradientPrototype:: CanvasGradientPrototype(QObject * parent)
: QObject (parent)
{
}
void CanvasGradientPrototype:: addColorStop(qreal offset, const QString & color)
{
CanvasGradient * self = qscriptvalue_cast< CanvasGradient* > (thisObject());
if (! self | | (self- > value. type() = = QGradient :: NoGradient))
return ;
self- > value. setColorAt(offset, colorFromString(color));
}
void CanvasGradientPrototype:: setup(QScriptEngine * engine)
{
CanvasGradientPrototype * proto = new CanvasGradientPrototype();
engine- > setDefaultPrototype(qMetaTypeId < CanvasGradient> (),
engine- > newQObject(proto, QScriptEngine :: ScriptOwnership,
QScriptEngine :: ExcludeSuperClassContents));
}