Initial release
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
set(SCRIPTING_SOURCES
|
||||
tiny-js/TinyJS_Functions.cpp
|
||||
tiny-js/TinyJS_Functions.h
|
||||
tiny-js/TinyJS_MathFunctions.cpp
|
||||
tiny-js/TinyJS_MathFunctions.h
|
||||
tiny-js/TinyJS.cpp
|
||||
tiny-js/TinyJS.h
|
||||
detail/scriptobject.h
|
||||
detail/uidescscriptobject.h
|
||||
detail/converters.h
|
||||
detail/drawcontextobject.cpp
|
||||
detail/drawcontextobject.h
|
||||
detail/drawable.cpp
|
||||
detail/drawable.h
|
||||
detail/iscriptcontextinternal.h
|
||||
detail/scriptingviewfactory.cpp
|
||||
detail/scriptingviewfactory.h
|
||||
detail/viewscriptobject.cpp
|
||||
detail/viewscriptobject.h
|
||||
uiscripting.cpp
|
||||
uiscripting.h
|
||||
uiscripting.md
|
||||
)
|
||||
add_library(vstgui_uiscripting STATIC ${SCRIPTING_SOURCES})
|
||||
target_compile_definitions(vstgui_uiscripting ${VSTGUI_COMPILE_DEFINITIONS})
|
||||
vstgui_set_cxx_version(vstgui_uiscripting ${VSTGUI_CXX_VERSION})
|
||||
vstgui_source_group_by_folder(vstgui_uiscripting)
|
||||
|
||||
option(VSTGUI_SCRIPTING_TINYJS_TESTS "Add Tiny-JS test target" OFF)
|
||||
if(VSTGUI_SCRIPTING_TINYJS_TESTS)
|
||||
add_executable(uidescription_scripting_tiny_js_test
|
||||
tiny-js/run_tests.cpp
|
||||
tiny-js/TinyJS_Functions.cpp
|
||||
tiny-js/TinyJS_Functions.h
|
||||
tiny-js/TinyJS_MathFunctions.cpp
|
||||
tiny-js/TinyJS_MathFunctions.h
|
||||
tiny-js/TinyJS.cpp
|
||||
tiny-js/TinyJS.h
|
||||
)
|
||||
target_compile_definitions(uidescription_scripting_tiny_js_test ${VSTGUI_COMPILE_DEFINITIONS})
|
||||
vstgui_set_cxx_version(uidescription_scripting_tiny_js_test ${VSTGUI_CXX_VERSION})
|
||||
endif(VSTGUI_SCRIPTING_TINYJS_TESTS)
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scriptobject.h"
|
||||
#include "../../lib/vstguifwd.h"
|
||||
#include "../../uidescription/uidescriptionfwd.h"
|
||||
#include "../../lib/events.h"
|
||||
#include "../../lib/crect.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline ScriptObject makeScriptRect (const CRect& rect)
|
||||
{
|
||||
using namespace std::literals;
|
||||
ScriptObject obj;
|
||||
obj.addChild ("left"sv, rect.left);
|
||||
obj.addChild ("top"sv, rect.top);
|
||||
obj.addChild ("right"sv, rect.right);
|
||||
obj.addChild ("bottom"sv, rect.bottom);
|
||||
obj.addFunc ("getWidth"sv, "{ return this.right - this.left; }"sv);
|
||||
obj.addFunc ("getHeight"sv, "{ return this.bottom - this.top; }"sv);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline ScriptObject makeScriptPoint (const CPoint& point)
|
||||
{
|
||||
using namespace std::literals;
|
||||
ScriptObject obj;
|
||||
obj.addChild ("x"sv, point.x);
|
||||
obj.addChild ("y"sv, point.y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline CPoint fromScriptPoint (TJS::CScriptVar& var)
|
||||
{
|
||||
using namespace std::literals;
|
||||
CPoint result {};
|
||||
if (auto xVar = var.findChild ("x"sv))
|
||||
result.x = xVar->getVar ()->getDouble ();
|
||||
else
|
||||
throw TJS::CScriptException ("Not a point object, missing 'x' member");
|
||||
if (auto yVar = var.findChild ("y"sv))
|
||||
result.y = yVar->getVar ()->getDouble ();
|
||||
else
|
||||
throw TJS::CScriptException ("Not a point object, missing 'y' member");
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline CRect fromScriptRect (TJS::CScriptVar& var)
|
||||
{
|
||||
using namespace std::literals;
|
||||
CRect result {};
|
||||
auto leftVar = var.findChild ("left"sv);
|
||||
auto topVar = var.findChild ("top"sv);
|
||||
auto rightVar = var.findChild ("right"sv);
|
||||
auto bottomVar = var.findChild ("bottom"sv);
|
||||
if (!leftVar || !topVar || !rightVar || !bottomVar)
|
||||
throw TJS::CScriptException ("Expecting a rect object here");
|
||||
result.left = leftVar->getVar ()->getDouble ();
|
||||
result.top = topVar->getVar ()->getDouble ();
|
||||
result.right = rightVar->getVar ()->getDouble ();
|
||||
result.bottom = bottomVar->getVar ()->getDouble ();
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline ScriptObject makeScriptEvent (const Event& event)
|
||||
{
|
||||
using namespace std::literals;
|
||||
ScriptObject obj;
|
||||
if (auto modifierEvent = asModifierEvent (event))
|
||||
{
|
||||
ScriptObject mod;
|
||||
if (modifierEvent->modifiers.has (ModifierKey::Shift))
|
||||
mod.addChild ("shift"sv, true);
|
||||
if (modifierEvent->modifiers.has (ModifierKey::Alt))
|
||||
mod.addChild ("alt"sv, true);
|
||||
if (modifierEvent->modifiers.has (ModifierKey::Control))
|
||||
mod.addChild ("control"sv, true);
|
||||
if (modifierEvent->modifiers.has (ModifierKey::Super))
|
||||
mod.addChild ("super"sv, true);
|
||||
obj.addChild ("modifiers"sv, std::move (mod));
|
||||
}
|
||||
if (auto mouseEvent = asMousePositionEvent (event))
|
||||
{
|
||||
obj.addChild ("mousePosition"sv, makeScriptPoint (mouseEvent->mousePosition));
|
||||
}
|
||||
if (auto mouseEvent = asMouseEvent (event))
|
||||
{
|
||||
ScriptObject buttons;
|
||||
if (mouseEvent->buttonState.has (MouseButton::Left))
|
||||
buttons.addChild ("left"sv, true);
|
||||
if (mouseEvent->buttonState.has (MouseButton::Right))
|
||||
buttons.addChild ("right"sv, true);
|
||||
if (mouseEvent->buttonState.has (MouseButton::Middle))
|
||||
buttons.addChild ("middle"sv, true);
|
||||
obj.addChild ("mouseButtons"sv, std::move (buttons));
|
||||
}
|
||||
if (event.type == EventType::MouseWheel)
|
||||
{
|
||||
const auto& wheelEvent = castMouseWheelEvent (event);
|
||||
ScriptObject wheel;
|
||||
wheel.addChild ("deltaX"sv, wheelEvent.deltaX);
|
||||
wheel.addChild ("deltaY"sv, wheelEvent.deltaY);
|
||||
if (wheelEvent.flags & MouseWheelEvent::Flags::DirectionInvertedFromDevice)
|
||||
wheel.addChild ("directionInvertedFromDevice"sv, true);
|
||||
if (wheelEvent.flags & MouseWheelEvent::Flags::PreciseDeltas)
|
||||
wheel.addChild ("preciceDelta"sv, true);
|
||||
obj.addChild ("mouseWheel"sv, std::move (wheel));
|
||||
}
|
||||
if (auto keyEvent = asKeyboardEvent (event))
|
||||
{
|
||||
ScriptObject key;
|
||||
key.addChild ("character"sv, static_cast<int> (keyEvent->character));
|
||||
key.addChild ("virtual"sv, static_cast<int> (keyEvent->virt));
|
||||
key.addChild ("isRepeat"sv, keyEvent->isRepeat);
|
||||
obj.addChild ("key"sv, std::move (key));
|
||||
}
|
||||
obj.addChild ("consume"sv, 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "drawable.h"
|
||||
#include "converters.h"
|
||||
#include "../../lib/cframe.h"
|
||||
#include "../../lib/cdrawcontext.h"
|
||||
#include "../../lib/cgraphicspath.h"
|
||||
#include "../../lib/cgraphicstransform.h"
|
||||
#include "../../uidescription/detail/uiviewcreatorattributes.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
using namespace std::literals;
|
||||
using namespace TJS;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
void JavaScriptDrawable::onDraw (CDrawContext* context, const CRect& rect, const CRect& viewSize)
|
||||
{
|
||||
if (!scriptObject)
|
||||
{
|
||||
auto dashLength = std::round ((viewSize.getWidth () * 2 + viewSize.getHeight () * 2) / 40.);
|
||||
CLineStyle ls (CLineStyle::kLineCapButt, CLineStyle::kLineJoinMiter, 0,
|
||||
{dashLength, dashLength});
|
||||
|
||||
auto lineWidth = 1.;
|
||||
auto size = viewSize;
|
||||
size.inset (lineWidth / 2., lineWidth / 2.);
|
||||
context->setLineStyle (ls);
|
||||
context->setLineWidth (lineWidth);
|
||||
context->setFrameColor (kBlackCColor);
|
||||
context->drawRect (size, kDrawStroked);
|
||||
|
||||
ls.setDashPhase (dashLength * lineWidth);
|
||||
context->setLineStyle (ls);
|
||||
context->setFrameColor (kWhiteCColor);
|
||||
context->drawRect (size, kDrawStroked);
|
||||
return;
|
||||
}
|
||||
auto scriptContext = scriptObject->getContext ();
|
||||
if (!scriptContext)
|
||||
return;
|
||||
context->saveGlobalState ();
|
||||
|
||||
drawContext.setDrawContext (context, scriptContext->getUIDescription ());
|
||||
|
||||
CDrawContext::Transform tm (*context, CGraphicsTransform ().translate (viewSize.getTopLeft ()));
|
||||
|
||||
auto rectVar = makeScriptRect (rect);
|
||||
auto scriptRoot = scriptContext->getRoot ();
|
||||
ScriptAddChildScoped scs (*scriptRoot, "view"sv, *scriptObject);
|
||||
ScriptAddChildScoped scs2 (*scriptRoot, "context"sv, drawContext);
|
||||
ScriptAddChildScoped scs3 (*scriptRoot, "rect"sv, rectVar);
|
||||
scriptContext->evalScript ("view.draw(context, rect);"sv);
|
||||
|
||||
drawContext.setDrawContext (nullptr, nullptr);
|
||||
|
||||
context->restoreGlobalState ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void JavaScriptDrawable::setup (ViewScriptObject* inObject) { scriptObject = inObject; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptDrawable::onDrawFocusOnTop ()
|
||||
{
|
||||
auto scriptContext = scriptObject->getContext ();
|
||||
if (!scriptContext)
|
||||
return false;
|
||||
|
||||
if (scriptObject->getVar ()->findChild ("drawFocusOnTop"sv) == nullptr)
|
||||
return false;
|
||||
|
||||
auto scriptRoot = scriptContext->getRoot ();
|
||||
ScriptAddChildScoped scs (*scriptRoot, "view"sv, *scriptObject);
|
||||
auto boolResult = scriptContext->evalScript ("view.drawFocusOnTop();"sv);
|
||||
return boolResult->isNumeric () ? boolResult->getInt () : false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptDrawable::onGetFocusPath (CGraphicsPath& outPath, CCoord focusWidth,
|
||||
const CRect& viewSize)
|
||||
{
|
||||
if (auto scriptContext = scriptObject->getContext ())
|
||||
{
|
||||
if (scriptObject->getVar ()->findChild ("getFocusPath"sv) == nullptr)
|
||||
{
|
||||
auto r = viewSize;
|
||||
outPath.addRect (r);
|
||||
r.extend (focusWidth, focusWidth);
|
||||
outPath.addRect (r);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto scriptRoot = scriptContext->getRoot ();
|
||||
auto path = makeOwned<CGraphicsPath> (outPath);
|
||||
ScriptObject focusWidthVar;
|
||||
focusWidthVar->setDouble (focusWidth);
|
||||
ScriptAddChildScoped scs (*scriptRoot, "view"sv, *scriptObject);
|
||||
ScriptAddChildScoped scs2 (*scriptRoot, "path"sv, makeGraphicsPathScriptObject (path));
|
||||
ScriptAddChildScoped scs3 (*scriptRoot, "focusWidth"sv, focusWidthVar);
|
||||
auto boolResult = scriptContext->evalScript ("view.getFocusPath(path, focusWidth);"sv);
|
||||
if (boolResult->isNumeric ())
|
||||
{
|
||||
if (boolResult->getInt () == 1)
|
||||
{
|
||||
CGraphicsTransform tm;
|
||||
tm.translate (viewSize.left, viewSize.top);
|
||||
outPath.addPath (*path, &tm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
void JavaScriptDrawableView::drawRect (CDrawContext* context, const CRect& rect)
|
||||
{
|
||||
onDraw (context, rect, getViewSize ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptDrawableView::drawFocusOnTop ()
|
||||
{
|
||||
if (wantsFocus ())
|
||||
return onDrawFocusOnTop ();
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptDrawableView::getFocusPath (CGraphicsPath& outPath)
|
||||
{
|
||||
if (wantsFocus ())
|
||||
return onGetFocusPath (outPath, getFrame ()->getFocusWidth (), getViewSize ());
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
void JavaScriptDrawableControl::draw (CDrawContext* context) { drawRect (context, getViewSize ()); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void JavaScriptDrawableControl::drawRect (CDrawContext* context, const CRect& rect)
|
||||
{
|
||||
onDraw (context, rect, getViewSize ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptDrawableControl::drawFocusOnTop ()
|
||||
{
|
||||
if (wantsFocus ())
|
||||
return onDrawFocusOnTop ();
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptDrawableControl::getFocusPath (CGraphicsPath& outPath)
|
||||
{
|
||||
if (wantsFocus ())
|
||||
return onGetFocusPath (outPath, getFrame ()->getFocusWidth (), getViewSize ());
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
IdStringPtr JavaScriptDrawableViewCreator::getViewName () const { return "JavaScriptDrawableView"; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
IdStringPtr JavaScriptDrawableViewCreator::getBaseViewName () const
|
||||
{
|
||||
return UIViewCreator::kCView;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CView* JavaScriptDrawableViewCreator::create (const UIAttributes& attributes,
|
||||
const IUIDescription* description) const
|
||||
{
|
||||
return new JavaScriptDrawableView (CRect ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
IdStringPtr JavaScriptDrawableControlCreator::getViewName () const
|
||||
{
|
||||
return "JavaScriptDrawableControl";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
IdStringPtr JavaScriptDrawableControlCreator::getBaseViewName () const
|
||||
{
|
||||
return UIViewCreator::kCControl;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CView* JavaScriptDrawableControlCreator::create (const UIAttributes& attributes,
|
||||
const IUIDescription* description) const
|
||||
{
|
||||
return new JavaScriptDrawableControl (CRect ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "viewscriptobject.h"
|
||||
#include "drawcontextobject.h"
|
||||
#include "../../lib/cview.h"
|
||||
#include "../../lib/controls/ccontrol.h"
|
||||
#include "../../uidescription/iviewcreator.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct JavaScriptDrawable
|
||||
{
|
||||
void onDraw (CDrawContext* context, const CRect& rect, const CRect& viewSize);
|
||||
bool onDrawFocusOnTop ();
|
||||
bool onGetFocusPath (CGraphicsPath& outPath, CCoord focusWidth, const CRect& viewSize);
|
||||
|
||||
void setup (ViewScriptObject* object);
|
||||
|
||||
private:
|
||||
ViewScriptObject* scriptObject {nullptr};
|
||||
DrawContextObject drawContext;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct JavaScriptDrawableView : CView,
|
||||
IFocusDrawing,
|
||||
JavaScriptDrawable
|
||||
{
|
||||
using CView::CView;
|
||||
|
||||
void drawRect (CDrawContext* context, const CRect& rect) override;
|
||||
bool drawFocusOnTop () override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct JavaScriptDrawableControl : CControl,
|
||||
JavaScriptDrawable
|
||||
{
|
||||
using CControl::CControl;
|
||||
|
||||
void draw (CDrawContext* pContext) override;
|
||||
void drawRect (CDrawContext* context, const CRect& rect) override;
|
||||
bool drawFocusOnTop () override;
|
||||
bool getFocusPath (CGraphicsPath& outPath) override;
|
||||
|
||||
CLASS_METHODS_NOCOPY (JavaScriptDrawableControl, CControl);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct JavaScriptDrawableViewCreator : ViewCreatorAdapter
|
||||
{
|
||||
IdStringPtr getViewName () const override;
|
||||
IdStringPtr getBaseViewName () const override;
|
||||
CView* create (const UIAttributes& attributes,
|
||||
const IUIDescription* description) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct JavaScriptDrawableControlCreator : ViewCreatorAdapter
|
||||
{
|
||||
IdStringPtr getViewName () const override;
|
||||
IdStringPtr getBaseViewName () const override;
|
||||
CView* create (const UIAttributes& attributes,
|
||||
const IUIDescription* description) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+870
@@ -0,0 +1,870 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "drawcontextobject.h"
|
||||
#include "converters.h"
|
||||
#include "../../lib/cdrawcontext.h"
|
||||
#include "../../lib/cgraphicspath.h"
|
||||
#include "../../lib/cgradient.h"
|
||||
#include "../../uidescription/uidescription.h"
|
||||
#include "../../uidescription/uiviewcreator.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
using namespace std::literals;
|
||||
using namespace TJS;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
static T getFromVar (CScriptVar* var, const string& exceptionString)
|
||||
{
|
||||
auto customData = var->getCustomData ();
|
||||
if (!customData.has_value ())
|
||||
throw CScriptException (exceptionString);
|
||||
try
|
||||
{
|
||||
auto result = std::any_cast<T> (customData);
|
||||
return result;
|
||||
}
|
||||
catch (const std::bad_any_cast&)
|
||||
{
|
||||
throw CScriptException (exceptionString);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static SharedPointer<CGraphicsPath> getGraphicsPath (CScriptVar* var, std::string_view varName,
|
||||
std::string_view signature)
|
||||
{
|
||||
auto pathVar = getArgument (var, varName, signature);
|
||||
return getFromVar<SharedPointer<CGraphicsPath>> (pathVar, "Variable is not a graphics path");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static SharedPointer<CGradient> getGradient (CScriptVar* var, std::string_view varName,
|
||||
std::string_view signature)
|
||||
{
|
||||
auto gradientVar = getArgument (var, varName, signature);
|
||||
return getFromVar<SharedPointer<CGradient>> (gradientVar, "Variable is not a gradient");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static std::shared_ptr<CGraphicsTransform> getTransformMatrix (CScriptVar* var,
|
||||
std::string_view varName,
|
||||
std::string_view signature)
|
||||
{
|
||||
auto tmVar = getArgument (var, varName, signature);
|
||||
return getFromVar<std::shared_ptr<CGraphicsTransform>> (tmVar,
|
||||
"Variable is not a transform matrix");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static std::shared_ptr<CGraphicsTransform> getOptionalTransformMatrix (CScriptVar* var,
|
||||
std::string_view varName,
|
||||
std::string_view signature)
|
||||
{
|
||||
if (auto tmVar = getOptionalArgument (var, varName))
|
||||
return getFromVar<std::shared_ptr<CGraphicsTransform>> (
|
||||
tmVar, "Variable is not a transform matrix");
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static CRect getRect (CScriptVar* var, std::string_view varName, std::string_view signature)
|
||||
{
|
||||
auto rectVar = getArgument (var, varName, signature);
|
||||
auto rect = fromScriptRect (*rectVar);
|
||||
return rect;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static CPoint getPoint (CScriptVar* var, std::string_view varName, std::string_view signature)
|
||||
{
|
||||
auto pointVar = getArgument (var, varName, signature);
|
||||
auto point = fromScriptPoint (*pointVar);
|
||||
return point;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static CPoint getOptionalPoint (CScriptVar* var, std::string_view varName,
|
||||
std::string_view signature, CPoint defaultPoint = {})
|
||||
{
|
||||
if (auto pointVar = getOptionalArgument (var, varName))
|
||||
return fromScriptPoint (*pointVar);
|
||||
return defaultPoint;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static double getDouble (CScriptVar* var, std::string_view varName, std::string_view signature)
|
||||
{
|
||||
auto doubleVar = getArgument (var, varName, signature);
|
||||
if (!doubleVar->isNumeric ())
|
||||
{
|
||||
TJS::string s ("'");
|
||||
s.append (varName);
|
||||
s.append ("' must be a number");
|
||||
throw CScriptException (s);
|
||||
}
|
||||
return doubleVar->getDouble ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static int64_t getInt (CScriptVar* var, std::string_view varName, std::string_view signature)
|
||||
{
|
||||
auto intVar = getArgument (var, varName, signature);
|
||||
if (!intVar->isNumeric ())
|
||||
{
|
||||
TJS::string s ("'");
|
||||
s.append (varName);
|
||||
s.append ("' must be numeric");
|
||||
throw CScriptException (s);
|
||||
}
|
||||
return intVar->getInt ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static int64_t getOptionalInt (CScriptVar* var, std::string_view varName,
|
||||
std::string_view signature, int64_t defaultInt = {})
|
||||
{
|
||||
if (auto intVar = getOptionalArgument (var, varName))
|
||||
{
|
||||
if (!intVar->isNumeric ())
|
||||
{
|
||||
TJS::string s ("'");
|
||||
s.append (varName);
|
||||
s.append ("' must be numeric");
|
||||
throw CScriptException (s);
|
||||
}
|
||||
return intVar->getInt ();
|
||||
}
|
||||
return defaultInt;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
static CColor getColor (CScriptVar* var, const IUIDescription* uiDesc, std::string_view varName,
|
||||
std::string_view signature)
|
||||
{
|
||||
auto colorVar = getArgument (var, varName, signature);
|
||||
auto colorStr = colorVar->getString ();
|
||||
CColor color {};
|
||||
if (!UIViewCreator::stringToColor (colorStr, color, uiDesc))
|
||||
{
|
||||
string str ("'");
|
||||
str += colorStr;
|
||||
str += "' is not a color in a call to ";
|
||||
str += signature;
|
||||
throw CScriptException (str);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct TransformMatrixScriptObject : ScriptObject
|
||||
{
|
||||
using TransformMatrixPtr = std::shared_ptr<CGraphicsTransform>;
|
||||
|
||||
TransformMatrixScriptObject (TransformMatrixPtr tm = std::make_shared<CGraphicsTransform> ())
|
||||
: ScriptObject (new CScriptVar ("", SCRIPTVAR_OBJECT))
|
||||
{
|
||||
scriptVar->setCustomData (tm);
|
||||
addFunc ("concat"sv, [tm] (auto var) { concat (tm, var); }, {"transformMatrix"sv});
|
||||
addFunc ("inverse"sv, [tm] (auto var) { inverse (tm, var); });
|
||||
addFunc ("rotate"sv, [tm] (auto var) { rotate (tm, var); }, {"angle"sv, "center?"sv});
|
||||
addFunc ("scale"sv, [tm] (auto var) { scale (tm, var); }, {"x"sv, "y"sv});
|
||||
addFunc ("skewX"sv, [tm] (auto var) { skewX (tm, var); }, {"angle"sv});
|
||||
addFunc ("skewY"sv, [tm] (auto var) { skewY (tm, var); }, {"angle"sv});
|
||||
addFunc ("translate"sv, [tm] (auto var) { translate (tm, var); }, {"x"sv, "y"sv});
|
||||
addFunc ("transform"sv, [tm] (auto var) { transform (tm, var); }, {"pointOrRect"sv});
|
||||
}
|
||||
|
||||
static void inverse (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.inverse();"sv;
|
||||
auto iTm = tm->inverse ();
|
||||
TransformMatrixScriptObject obj (std::make_shared<CGraphicsTransform> (iTm));
|
||||
var->setReturnVar (obj.take ());
|
||||
}
|
||||
|
||||
static void concat (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.concat(transformMatrix);"sv;
|
||||
auto other = getTransformMatrix (var, "transformMatrix"sv, signature);
|
||||
auto result = *(tm.get ()) * *(other.get ());
|
||||
*(tm.get ()) = result;
|
||||
}
|
||||
|
||||
static void translate (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.translate(x, y);"sv;
|
||||
auto x = getDouble (var, "x"sv, signature);
|
||||
auto y = getDouble (var, "y"sv, signature);
|
||||
tm->translate (x, y);
|
||||
}
|
||||
|
||||
static void scale (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.scale(x, y);"sv;
|
||||
auto x = getDouble (var, "x"sv, signature);
|
||||
auto y = getDouble (var, "y"sv, signature);
|
||||
tm->scale (x, y);
|
||||
}
|
||||
|
||||
static void rotate (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.rotate(angle, center?);"sv;
|
||||
auto angle = getDouble (var, "angle"sv, signature);
|
||||
if (auto centerVar = getOptionalArgument (var, "center?"sv))
|
||||
{
|
||||
auto center = fromScriptPoint (*centerVar);
|
||||
tm->rotate (angle, center);
|
||||
}
|
||||
else
|
||||
{
|
||||
tm->rotate (angle);
|
||||
}
|
||||
}
|
||||
|
||||
static void skewX (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.skewX(angle);"sv;
|
||||
auto angle = getDouble (var, "angle"sv, signature);
|
||||
tm->skewX (angle);
|
||||
}
|
||||
|
||||
static void skewY (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.skewY(angle);"sv;
|
||||
auto angle = getDouble (var, "angle"sv, signature);
|
||||
tm->skewY (angle);
|
||||
}
|
||||
|
||||
static void transform (const TransformMatrixPtr& tm, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "matrix.transform(pointOrRect);"sv;
|
||||
auto pointOrRect = getArgument (var, "pointOrRect"sv, signature);
|
||||
auto xVar = getOptionalArgument (pointOrRect, "x"sv);
|
||||
auto yVar = getOptionalArgument (pointOrRect, "y"sv);
|
||||
if (xVar && yVar)
|
||||
{
|
||||
auto x = xVar->getDouble ();
|
||||
auto y = yVar->getDouble ();
|
||||
tm->transform (x, y);
|
||||
xVar->setDouble (x);
|
||||
yVar->setDouble (y);
|
||||
return;
|
||||
}
|
||||
auto leftVar = getOptionalArgument (pointOrRect, "left"sv);
|
||||
auto topVar = getOptionalArgument (pointOrRect, "top"sv);
|
||||
auto rightVar = getOptionalArgument (pointOrRect, "right"sv);
|
||||
auto bottomVar = getOptionalArgument (pointOrRect, "bottom"sv);
|
||||
if (leftVar && topVar && rightVar && bottomVar)
|
||||
{
|
||||
CRect r (leftVar->getDouble (), topVar->getDouble (), rightVar->getDouble (),
|
||||
bottomVar->getDouble ());
|
||||
tm->transform (r);
|
||||
leftVar->setDouble (r.left);
|
||||
topVar->setDouble (r.top);
|
||||
rightVar->setDouble (r.right);
|
||||
bottomVar->setDouble (r.bottom);
|
||||
return;
|
||||
}
|
||||
|
||||
string s ("Argument is not a point or a rect in ");
|
||||
s.append (signature);
|
||||
throw CScriptException (std::move (s));
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
TJS::CScriptVar* makeTransformMatrixObject ()
|
||||
{
|
||||
TransformMatrixScriptObject obj;
|
||||
return obj.take ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct GradientScriptObject : ScriptObject
|
||||
{
|
||||
GradientScriptObject (const SharedPointer<CGradient>& g, const IUIDescription* uiDesc)
|
||||
: ScriptObject (new CScriptVar ("", SCRIPTVAR_OBJECT))
|
||||
{
|
||||
scriptVar->setCustomData (g);
|
||||
|
||||
addFunc ("addColorStop"sv, [g, uiDesc] (auto var) { addColorStop (g, uiDesc, var); },
|
||||
{"position"sv, "color"sv});
|
||||
}
|
||||
|
||||
static void addColorStop (const SharedPointer<CGradient>& g, const IUIDescription* uiDesc,
|
||||
CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "gradient.addColorStop(position, color);"sv;
|
||||
auto position = getDouble (var, "position"sv, signature);
|
||||
auto color = getColor (var, uiDesc, "color"sv, signature);
|
||||
g->addColorStop (position, color);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct GraphicsPathScriptObject : ScriptObject
|
||||
{
|
||||
GraphicsPathScriptObject (const SharedPointer<CGraphicsPath>& p)
|
||||
: ScriptObject (new CScriptVar ("", SCRIPTVAR_OBJECT))
|
||||
{
|
||||
scriptVar->setCustomData (p);
|
||||
|
||||
addFunc ("addEllipse"sv, [p] (auto var) { addEllipse (p, var); }, {"rect"sv});
|
||||
addFunc ("addArc"sv, [p] (auto var) { addArc (p, var); },
|
||||
{"rect"sv, "startAngle"sv, "endAngle"sv, "clockwise"sv});
|
||||
addFunc ("addBezierCurve"sv, [p] (auto var) { addBezierCurve (p, var); },
|
||||
{"control1"sv, "control2"sv, "end"sv});
|
||||
addFunc ("addLine"sv, [p] (auto var) { addLine (p, var); }, {"to"sv});
|
||||
addFunc ("addPath"sv, [p] (auto var) { addPath (p, var); },
|
||||
{"path"sv, "transformMatrix?"sv});
|
||||
addFunc ("addRect"sv, [p] (auto var) { addRect (p, var); }, {"rect"sv});
|
||||
addFunc ("addRoundRect"sv, [p] (auto var) { addRoundRect (p, var); },
|
||||
{"rect"sv, "radius"sv});
|
||||
addFunc ("closeSubpath"sv, [p] (auto var) { closeSubpath (p); });
|
||||
addFunc ("beginSubpath"sv, [p] (auto var) { beginSubpath (p, var); }, {"start"sv});
|
||||
}
|
||||
|
||||
static void addPath (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.addPath(path, transformMatrix?);"sv;
|
||||
auto otherPath = getGraphicsPath (var, "path"sv, signature);
|
||||
auto tm = getOptionalTransformMatrix (var, "transformMatrix?"sv, signature);
|
||||
path->addPath (*otherPath.get (), tm ? tm.get () : nullptr);
|
||||
}
|
||||
|
||||
static void addArc (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.addArc(rect, startAngle, endAngle, clockwise);"sv;
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
auto startAngle = getDouble (var, "startAngle"sv, signature);
|
||||
auto endAngle = getDouble (var, "endAngle"sv, signature);
|
||||
auto clockwise = getInt (var, "clockwise"sv, signature);
|
||||
path->addArc (rect, startAngle, endAngle, clockwise != 0 ? true : false);
|
||||
}
|
||||
|
||||
static void addEllipse (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.addEllipse(rect);"sv;
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
path->addEllipse (rect);
|
||||
}
|
||||
|
||||
static void addRect (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.addRect(rect);"sv;
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
path->addRect (rect);
|
||||
}
|
||||
|
||||
static void addLine (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.addLine(to);"sv;
|
||||
auto point = getPoint (var, "to"sv, signature);
|
||||
path->addLine (point);
|
||||
}
|
||||
|
||||
static void addBezierCurve (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.addBezierCurve(control1, control2, end);"sv;
|
||||
auto control1 = getPoint (var, "control1"sv, signature);
|
||||
auto control2 = getPoint (var, "control2"sv, signature);
|
||||
auto end = getPoint (var, "end"sv, signature);
|
||||
path->addBezierCurve (control1, control2, end);
|
||||
}
|
||||
|
||||
static void beginSubpath (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.beginSubpath(start);"sv;
|
||||
auto start = getPoint (var, "start"sv, signature);
|
||||
path->beginSubpath (start);
|
||||
}
|
||||
|
||||
static void closeSubpath (const SharedPointer<CGraphicsPath>& path)
|
||||
{
|
||||
static constexpr auto signature = "path.closeSubpath();"sv;
|
||||
path->closeSubpath ();
|
||||
}
|
||||
|
||||
static void addRoundRect (const SharedPointer<CGraphicsPath>& path, CScriptVar* var)
|
||||
{
|
||||
static constexpr auto signature = "path.addRoundRect(rect, radius);"sv;
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
auto radius = getDouble (var, "radius"sv, signature);
|
||||
path->addRoundRect (rect, radius);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
ScriptObject makeGraphicsPathScriptObject (const SharedPointer<CGraphicsPath>& p)
|
||||
{
|
||||
return GraphicsPathScriptObject (p);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct DrawContextObject::Impl
|
||||
{
|
||||
CDrawContext* context {nullptr};
|
||||
IUIDescription* uiDesc {nullptr};
|
||||
mutable int32_t globalStatesStored {0};
|
||||
|
||||
void setContext (CDrawContext* inContext, IUIDescription* inUIDesc)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
while (globalStatesStored > 0)
|
||||
{
|
||||
context->restoreGlobalState ();
|
||||
--globalStatesStored;
|
||||
}
|
||||
}
|
||||
context = inContext;
|
||||
uiDesc = inUIDesc;
|
||||
globalStatesStored = 0;
|
||||
}
|
||||
|
||||
void checkContextOrThrow () const
|
||||
{
|
||||
if (!context)
|
||||
throw CScriptException ("Native context is missing!");
|
||||
}
|
||||
|
||||
CDrawStyle getDrawStyle (CScriptVar* styleVar) const
|
||||
{
|
||||
CDrawStyle style {};
|
||||
auto string = styleVar->getString ();
|
||||
if (string == "stroked"sv)
|
||||
style = kDrawStroked;
|
||||
else if (string == "filled"sv)
|
||||
style = kDrawFilled;
|
||||
else if (string == "filledAndStroked"sv)
|
||||
style = kDrawFilledAndStroked;
|
||||
else
|
||||
throw CScriptException ("Unknown draw style: " + string);
|
||||
return style;
|
||||
}
|
||||
|
||||
CDrawContext::PathDrawMode getPathDrawMode (CScriptVar* var) const
|
||||
{
|
||||
CDrawContext::PathDrawMode mode {};
|
||||
auto string = var->getString ();
|
||||
if (string == "stroked"sv)
|
||||
mode = CDrawContext::PathDrawMode::kPathStroked;
|
||||
else if (string == "filled"sv)
|
||||
mode = CDrawContext::PathDrawMode::kPathFilled;
|
||||
else if (string == "filledEvenOdd"sv)
|
||||
mode = CDrawContext::PathDrawMode::kPathFilledEvenOdd;
|
||||
else
|
||||
throw CScriptException ("Unknown path draw mode: " + string);
|
||||
return mode;
|
||||
}
|
||||
|
||||
void createRoundGraphicsPath (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.createRoundGraphicsPath(rect, radius);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
auto radius = getDouble (var, "radius"sv, signature);
|
||||
if (auto path = owned (context->createRoundRectGraphicsPath (rect, radius)))
|
||||
{
|
||||
GraphicsPathScriptObject obj (path);
|
||||
var->setReturnVar (obj);
|
||||
}
|
||||
}
|
||||
|
||||
void createGraphicsPath (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.createGraphicsPath();"sv;
|
||||
checkContextOrThrow ();
|
||||
if (auto path = owned (context->createGraphicsPath ()))
|
||||
{
|
||||
GraphicsPathScriptObject obj (path);
|
||||
var->setReturnVar (obj);
|
||||
}
|
||||
}
|
||||
|
||||
void createGradient (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature =
|
||||
"drawContext.createGradient(startColorPosition, startColor, endColorPosition, endColor);"sv;
|
||||
auto startColorPosition = getDouble (var, "startColorPosition", signature);
|
||||
auto endColorPosition = getDouble (var, "endColorPosition", signature);
|
||||
auto startColor = getColor (var, uiDesc, "startColor"sv, signature);
|
||||
auto endColor = getColor (var, uiDesc, "endColor"sv, signature);
|
||||
if (auto gradient = owned (
|
||||
CGradient::create (startColorPosition, endColorPosition, startColor, endColor)))
|
||||
{
|
||||
GradientScriptObject obj (gradient, uiDesc);
|
||||
var->setReturnVar (obj);
|
||||
}
|
||||
}
|
||||
|
||||
void drawGraphicsPath (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature =
|
||||
"drawContext.drawGraphicsPath(path, mode?, transform?);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto path = getGraphicsPath (var, "path"sv, signature);
|
||||
auto modeVar = getOptionalArgument (var, "mode?");
|
||||
auto mode = modeVar ? getPathDrawMode (modeVar) : CDrawContext::PathDrawMode::kPathFilled;
|
||||
auto tm = getOptionalTransformMatrix (var, "transform?"sv, signature);
|
||||
context->drawGraphicsPath (path, mode, tm ? tm.get () : nullptr);
|
||||
}
|
||||
|
||||
void fillLinearGradient (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature =
|
||||
"drawContext.fillLinearGradient(path, gradient, startPoint, endPoint, evenOdd?, transform?);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto path = getGraphicsPath (var, "path"sv, signature);
|
||||
auto gradient = getGradient (var, "gradient"sv, signature);
|
||||
auto startPoint = getPoint (var, "startPoint"sv, signature);
|
||||
auto endPoint = getPoint (var, "endPoint"sv, signature);
|
||||
auto tm = getOptionalTransformMatrix (var, "transform?"sv, signature);
|
||||
auto evenOdd = getOptionalInt (var, "evenOdd?", signature);
|
||||
context->fillLinearGradient (path, *gradient, startPoint, endPoint, evenOdd > 0, tm.get ());
|
||||
}
|
||||
|
||||
void fillRadialGradient (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature =
|
||||
"drawContext.fillRadialGradient(path, gradient, centerPoint, radius, originOffsetPoint?, evenOdd?, transform?);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto path = getGraphicsPath (var, "path"sv, signature);
|
||||
auto gradient = getGradient (var, "gradient"sv, signature);
|
||||
auto centerPoint = getPoint (var, "centerPoint"sv, signature);
|
||||
auto radius = getDouble (var, "radius"sv, signature);
|
||||
auto originOffsetPoint = getOptionalPoint (var, "originOffsetPoint?"sv, signature);
|
||||
auto evenOdd = getOptionalInt (var, "evenOdd?", signature);
|
||||
auto tm = getOptionalTransformMatrix (var, "transform?"sv, signature);
|
||||
context->fillRadialGradient (path, *gradient, centerPoint, radius, originOffsetPoint,
|
||||
evenOdd, tm.get ());
|
||||
}
|
||||
|
||||
void drawLine (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.drawLine(from, to);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto fromPoint = getArgument (var, "from"sv, signature);
|
||||
auto toPoint = getArgument (var, "to"sv, signature);
|
||||
auto from = fromScriptPoint (*fromPoint);
|
||||
auto to = fromScriptPoint (*toPoint);
|
||||
context->drawLine (from, to);
|
||||
}
|
||||
void drawRect (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.drawRect(rect, style);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
auto styleVar = getArgument (var, "style"sv, signature);
|
||||
auto style = getDrawStyle (styleVar);
|
||||
context->drawRect (rect, style);
|
||||
}
|
||||
void drawEllipse (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.drawEllipse(rect, style);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
auto styleVar = getArgument (var, "style"sv, signature);
|
||||
auto style = getDrawStyle (styleVar);
|
||||
context->drawEllipse (rect, style);
|
||||
}
|
||||
void drawArc (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature =
|
||||
"drawContext.drawArc(rect, startAngle, endAngle, style);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
auto startAngle = static_cast<float> (getDouble (var, "startAngle"sv, signature));
|
||||
auto endAngle = static_cast<float> (getDouble (var, "endAngle"sv, signature));
|
||||
auto styleVar = getArgument (var, "style"sv, signature);
|
||||
auto style = getDrawStyle (styleVar);
|
||||
context->drawArc (rect, startAngle, endAngle, style);
|
||||
}
|
||||
void clearRect (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.clearRect(rect);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
context->clearRect (rect);
|
||||
}
|
||||
void drawPolygon (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.drawPolygon(points, style);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto pointsVar = getArgument (var, "points"sv, signature);
|
||||
auto styleVar = getArgument (var, "style"sv, signature);
|
||||
if (!pointsVar->isArray ())
|
||||
throw CScriptException ("`points` argument must be an array of points in "
|
||||
"drawContext.drawPolygon(points, style);");
|
||||
PointList points;
|
||||
auto numPoints = pointsVar->getArrayLength ();
|
||||
for (auto index = 0; index < numPoints; ++index)
|
||||
{
|
||||
auto pointVar = pointsVar->getArrayIndex (index);
|
||||
vstgui_assert (pointVar != nullptr);
|
||||
points.emplace_back (fromScriptPoint (*pointVar));
|
||||
}
|
||||
auto style = getDrawStyle (styleVar);
|
||||
context->drawPolygon (points, style);
|
||||
}
|
||||
void setClipRect (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setClipRect(rect);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
context->setClipRect (rect);
|
||||
}
|
||||
void drawBitmap (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature =
|
||||
"drawContext.drawBitmap(name, destRect, offsetPoint?, alpha?);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto nameVar = getArgument (var, "name"sv, signature);
|
||||
auto destRect = getRect (var, "destRect"sv, signature);
|
||||
auto offsetPointVar = getOptionalArgument (var, "offsetPoint?"sv);
|
||||
auto alphaVar = getOptionalArgument (var, "alpha?"sv);
|
||||
auto bitmap = uiDesc->getBitmap (nameVar->getString ().data ());
|
||||
if (!bitmap)
|
||||
throw CScriptException ("bitmap not found in uiDescription");
|
||||
auto offset = offsetPointVar ? fromScriptPoint (*offsetPointVar) : CPoint (0, 0);
|
||||
auto alpha = static_cast<float> (alphaVar ? alphaVar->getDouble () : 1.);
|
||||
context->drawBitmap (bitmap, destRect, offset, alpha);
|
||||
}
|
||||
void drawString (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.drawString(string, rect, align?);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto stringVar = getArgument (var, "string"sv, signature);
|
||||
auto rect = getRect (var, "rect"sv, signature);
|
||||
auto alignVar = getOptionalArgument (var, "align?"sv);
|
||||
auto string = stringVar->getString ().data ();
|
||||
CHoriTxtAlign align = kCenterText;
|
||||
if (!alignVar || alignVar->isUndefined ())
|
||||
align = kCenterText;
|
||||
else if (alignVar->getString () == "left"sv)
|
||||
align = kLeftText;
|
||||
else if (alignVar->getString () == "center"sv)
|
||||
align = kCenterText;
|
||||
else if (alignVar->getString () == "right"sv)
|
||||
align = kRightText;
|
||||
else
|
||||
throw CScriptException (
|
||||
"wrong `align` argument. Expecting 'left', 'center' or 'right'");
|
||||
context->drawString (string, rect, align, true);
|
||||
}
|
||||
void setFont (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setFont(name);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto fontVar = getArgument (var, "name"sv, signature);
|
||||
if (auto font = uiDesc->getFont (fontVar->getString ().data ()))
|
||||
context->setFont (font);
|
||||
}
|
||||
void setFontColor (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setFontColor(color);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto colorVar = getArgument (var, "color"sv, signature);
|
||||
CColor color {};
|
||||
UIViewCreator::stringToColor (colorVar->getString (), color, uiDesc);
|
||||
context->setFontColor (color);
|
||||
}
|
||||
void setFillColor (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setFillColor(color);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto color = getColor (var, uiDesc, "color"sv, signature);
|
||||
context->setFillColor (color);
|
||||
}
|
||||
void setFrameColor (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setFrameColor(color);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto color = getColor (var, uiDesc, "color"sv, signature);
|
||||
context->setFrameColor (color);
|
||||
}
|
||||
void setLineWidth (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setLineWidth(width);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto widthVar = getArgument (var, "width"sv, signature);
|
||||
context->setLineWidth (widthVar->getDouble ());
|
||||
}
|
||||
void setLineStyle (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature =
|
||||
"drawContext.setLineStyle(styleOrLineCap, lineJoin?, dashLengths?, dashPhase);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto styleOrLineCapVar = getArgument (var, "styleOrLineCap"sv, signature);
|
||||
auto lineJoinVar = getOptionalArgument (var, "lineJoin?"sv);
|
||||
auto dashLengthsVar = getOptionalArgument (var, "dashLengths?"sv);
|
||||
auto dashPhaseVar = getOptionalArgument (var, "dashPhase?"sv);
|
||||
auto styleOrLineCap = styleOrLineCapVar->getString ();
|
||||
std::unique_ptr<CLineStyle> lineStyle;
|
||||
if (styleOrLineCap == "solid"sv)
|
||||
{
|
||||
lineStyle = std::make_unique<CLineStyle> (kLineSolid);
|
||||
}
|
||||
else if (styleOrLineCap == "dotted"sv)
|
||||
{
|
||||
lineStyle = std::make_unique<CLineStyle> (kLineOnOffDash);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (styleOrLineCap == "butt"sv)
|
||||
lineStyle = std::make_unique<CLineStyle> (CLineStyle::LineCap::kLineCapButt);
|
||||
else if (styleOrLineCap == "round"sv)
|
||||
lineStyle = std::make_unique<CLineStyle> (CLineStyle::LineCap::kLineCapRound);
|
||||
else if (styleOrLineCap == "square"sv)
|
||||
lineStyle = std::make_unique<CLineStyle> (CLineStyle::LineCap::kLineCapSquare);
|
||||
else
|
||||
throw CScriptException ("unknown `line cap` argument");
|
||||
if (lineJoinVar)
|
||||
{
|
||||
auto lineJoin = lineJoinVar->getString ();
|
||||
if (lineJoin == "miter"sv)
|
||||
lineStyle->setLineJoin (CLineStyle::LineJoin::kLineJoinMiter);
|
||||
else if (lineJoin == "round"sv)
|
||||
lineStyle->setLineJoin (CLineStyle::LineJoin::kLineJoinRound);
|
||||
else if (lineJoin == "bevel"sv)
|
||||
lineStyle->setLineJoin (CLineStyle::LineJoin::kLineJoinBevel);
|
||||
}
|
||||
if (dashLengthsVar)
|
||||
{
|
||||
if (!dashLengthsVar->isArray ())
|
||||
throw CScriptException ("`dashLengths` must be an array of numbers");
|
||||
CLineStyle::CoordVector lengths;
|
||||
auto numValues = dashLengthsVar->getArrayLength ();
|
||||
for (auto index = 0; index < numValues; ++index)
|
||||
{
|
||||
if (auto lengthVar = dashLengthsVar->getArrayIndex (index))
|
||||
lengths.push_back (lengthVar->getDouble ());
|
||||
}
|
||||
lineStyle->getDashLengths () = lengths;
|
||||
}
|
||||
if (dashPhaseVar)
|
||||
{
|
||||
lineStyle->setDashPhase (dashPhaseVar->getDouble ());
|
||||
}
|
||||
}
|
||||
if (lineStyle)
|
||||
context->setLineStyle (*lineStyle.get ());
|
||||
}
|
||||
void setGlobalAlpha (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setGlobalAlpha(alpha);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto alpha = static_cast<float> (getDouble (var, "alpha"sv, signature));
|
||||
context->setGlobalAlpha (alpha);
|
||||
}
|
||||
void saveGlobalState () const
|
||||
{
|
||||
checkContextOrThrow ();
|
||||
context->saveGlobalState ();
|
||||
++globalStatesStored;
|
||||
}
|
||||
void restoreGlobalState () const
|
||||
{
|
||||
checkContextOrThrow ();
|
||||
context->restoreGlobalState ();
|
||||
--globalStatesStored;
|
||||
}
|
||||
void getStringWidth (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.getStringWidth(string);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto stringVar = getArgument (var, "string"sv, signature);
|
||||
auto width = context->getStringWidth (stringVar->getString ().data ());
|
||||
var->getReturnVar ()->setDouble (width);
|
||||
}
|
||||
void setDrawMode (CScriptVar* var) const
|
||||
{
|
||||
static constexpr auto signature = "drawContext.setDrawMode(mode);"sv;
|
||||
checkContextOrThrow ();
|
||||
auto modeVar = getArgument (var, "mode"sv, signature);
|
||||
if (modeVar->getString () == "aliasing"sv)
|
||||
context->setDrawMode (kAliasing);
|
||||
else if (modeVar->getString () == "anti-aliasing"sv)
|
||||
context->setDrawMode (kAntiAliasing);
|
||||
else
|
||||
throw CScriptException ("`mode` must be `aliasing` or `anti-aliasing`");
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
DrawContextObject::DrawContextObject ()
|
||||
{
|
||||
impl = std::make_unique<Impl> ();
|
||||
scriptVar->setLifeTimeObserver (this);
|
||||
addFunc ("clearRect"sv, [this] (auto var) { impl->clearRect (var); }, {"rect"sv});
|
||||
addFunc ("createRoundGraphicsPath"sv,
|
||||
[this] (auto var) { impl->createRoundGraphicsPath (var); }, {"rect"sv, "radius"sv});
|
||||
addFunc ("createGraphicsPath"sv, [this] (auto var) { impl->createGraphicsPath (var); });
|
||||
addFunc ("createGradient"sv, [this] (auto var) { impl->createGradient (var); },
|
||||
{"startColorPosition"sv, "startColor"sv, "endColorPosition"sv, "endColor"sv});
|
||||
addFunc ("getStringWidth"sv, [this] (auto var) { impl->getStringWidth (var); }, {"string"sv});
|
||||
addFunc ("drawArc"sv, [this] (auto var) { impl->drawArc (var); },
|
||||
{"rect"sv, "startAngle"sv, "endAngle"sv, "style"sv});
|
||||
addFunc ("drawBitmap"sv, [this] (auto var) { impl->drawBitmap (var); },
|
||||
{"name"sv, "destRect"sv, "offsetPoint?"sv, "alpha?"sv});
|
||||
addFunc ("drawEllipse"sv, [this] (auto var) { impl->drawEllipse (var); },
|
||||
{"rect"sv, "style"sv});
|
||||
addFunc ("drawGraphicsPath"sv, [this] (auto var) { impl->drawGraphicsPath (var); },
|
||||
{"path"sv, "mode?"sv, "transform?"sv});
|
||||
addFunc ("drawLine"sv, [this] (auto var) { impl->drawLine (var); }, {"from"sv, "to"sv});
|
||||
addFunc ("drawPolygon"sv, [this] (auto var) { impl->drawPolygon (var); },
|
||||
{"points"sv, "style"sv});
|
||||
addFunc ("drawRect"sv, [this] (auto var) { impl->drawRect (var); }, {"rect"sv, "style"sv});
|
||||
addFunc ("drawString"sv, [this] (auto var) { impl->drawString (var); },
|
||||
{"string"sv, "rect"sv, "align?"sv});
|
||||
addFunc ("fillLinearGradient"sv, [this] (auto var) { impl->fillLinearGradient (var); },
|
||||
{"path"sv, "gradient"sv, "startPoint"sv, "endPoint"sv, "evenOdd?"sv, "transform?"sv});
|
||||
addFunc ("fillRadialGradient"sv, [this] (auto var) { impl->fillRadialGradient (var); },
|
||||
{"path"sv, "gradient"sv, "centerPoint"sv, "radius"sv, "originOffsetPoint?"sv,
|
||||
"evenOdd?"sv, "transform?"sv});
|
||||
addFunc ("restoreGlobalState"sv, [this] (auto var) { impl->restoreGlobalState (); });
|
||||
addFunc ("saveGlobalState"sv, [this] (auto var) { impl->saveGlobalState (); });
|
||||
addFunc ("setClipRect"sv, [this] (auto var) { impl->setClipRect (var); }, {"rect"sv});
|
||||
addFunc ("setFont"sv, [this] (auto var) { impl->setFont (var); }, {"name"sv});
|
||||
addFunc ("setFontColor"sv, [this] (auto var) { impl->setFontColor (var); }, {"color"sv});
|
||||
addFunc ("setFillColor"sv, [this] (auto var) { impl->setFillColor (var); }, {"color"sv});
|
||||
addFunc ("setFrameColor"sv, [this] (auto var) { impl->setFrameColor (var); }, {"color"sv});
|
||||
addFunc ("setGlobalAlpha", [this] (auto var) { impl->setGlobalAlpha (var); }, {"alpha"sv});
|
||||
addFunc ("setLineWidth"sv, [this] (auto var) { impl->setLineWidth (var); }, {"width"sv});
|
||||
addFunc ("setLineStyle"sv, [this] (auto var) { impl->setLineStyle (var); },
|
||||
{"styleOrLineCap"sv, "lineJoin?"sv, "dashLengths?"sv, "dashPhase?"sv});
|
||||
addFunc ("setDrawMode"sv, [this] (auto var) { impl->setDrawMode (var); }, {"mode"sv});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
DrawContextObject::~DrawContextObject () noexcept
|
||||
{
|
||||
if (scriptVar)
|
||||
scriptVar->setLifeTimeObserver (nullptr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void DrawContextObject::setDrawContext (CDrawContext* inContext, IUIDescription* inUIDesc)
|
||||
{
|
||||
impl->setContext (inContext, inUIDesc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void DrawContextObject::onDestroy (CScriptVar* v)
|
||||
{
|
||||
v->setLifeTimeObserver (nullptr);
|
||||
scriptVar = nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scriptobject.h"
|
||||
#include "../../lib/vstguifwd.h"
|
||||
#include "../../uidescription/uidescriptionfwd.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
TJS::CScriptVar* makeTransformMatrixObject ();
|
||||
ScriptObject makeGraphicsPathScriptObject (const SharedPointer<CGraphicsPath>& p);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct DrawContextObject : ScriptObject,
|
||||
TJS::IScriptVarLifeTimeObserver
|
||||
{
|
||||
DrawContextObject ();
|
||||
~DrawContextObject () noexcept override;
|
||||
|
||||
void setDrawContext (CDrawContext* context, IUIDescription* uiDesc);
|
||||
|
||||
void onDestroy (CScriptVar* v) override;
|
||||
|
||||
private:
|
||||
// CanvasRenderingContext2D API
|
||||
// clang-format off
|
||||
/*
|
||||
fillStyle: string | CanvasGradient | CanvasPattern;
|
||||
filter: string
|
||||
font: string;
|
||||
fontKerning: string;
|
||||
fontStretch: string;
|
||||
fontVariantCaps: string;
|
||||
globalAlpha: number;
|
||||
globalCompositionOperation: string;
|
||||
lineCap: string;
|
||||
lineDashOffset: number;
|
||||
lineJoin: string;
|
||||
lineWidth: number;
|
||||
miterLimit: number;
|
||||
shadowBlur: number;
|
||||
shadowColor: string;
|
||||
shadowOffsetX: number;
|
||||
shadowOffsetY: number;
|
||||
strokeStyle: string;
|
||||
textAlign: string;
|
||||
textBaseline: string;
|
||||
arc: (x: number, y: number, r: number, sAngle: number, eAngle: number, counterClockwise?: boolean) => void;
|
||||
arcTo: (x1: number, y1: number, x2: number, y2: number, r: number) => void;
|
||||
beginPath: () => void;
|
||||
bezierCurveTo: (cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number) => void;
|
||||
clearRect: (x: number, y: number, width: number, height: number) => void;
|
||||
clip: () => void;
|
||||
closePath: () => void;
|
||||
createImageData: (width: number, height: number, imageData: ImageData) => void;
|
||||
createLinearGradient: (x0: number, yo: number, x1: number, y1: number) => CanvasGradient;
|
||||
createPattern: () => CanvasPattern;
|
||||
createRadialGradient: (x0: number, y0: number, r0: number, x1: number, y1: number, r1: number) => CanvasGradient;
|
||||
drawFocusIfNeeded: (html: HTMLElement) => void;
|
||||
drawImage: (image: Image,dx: number,dy: number,sx?: number,sy?: number,sWidth?: number,sHeight?: number,dWidth?: number,dHeight?: number) => void;
|
||||
ellipse: (x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise?: boolean) => void;
|
||||
fill: (Path2D?: Path2D, fillRule?: any) => void;
|
||||
fillRect: (x: number, y: number, width: number, height: number) => void;
|
||||
fillText: (text: string, x: number, y: number, maxWidth?: number) => void;
|
||||
getImageData: (sx: number, sy: number, sw: number, sh: number) => Promise<ImageData>;
|
||||
getLineDash: () => number[];
|
||||
isPointInPath: (x: number, y: number, fillRule: any, path: Path2D) => boolean;
|
||||
isPointInStroke: (x: number, y: number, path: Path2D) => boolean;
|
||||
lineTo: (x: number, y: number) => void;
|
||||
measureText: (text: string) => any;
|
||||
moveTo: (x: number, y: number) => void;
|
||||
putImageData: (imageData: ImageData, dx: number, dy: number, dirtyX?: number, dirtyY?: number, dirtyWidth?: number, dirtyHeight?: number) => void;
|
||||
quadraticCurveTo: (cpx: number, cpy: number, x: number, y: number) => void;
|
||||
rect: (x: number, y: number, width: number, height: number) => void;
|
||||
reset: () => void
|
||||
resetTransform: () => void
|
||||
restore: () => void;
|
||||
rotate: (angle: number) => void;
|
||||
roundRect: (x: number, y: number, width: number, height: number, radii: number) => void
|
||||
save: () => void;
|
||||
scale: (x: number, y: number) => void;
|
||||
setLineDash: (segments: number[]) => void;
|
||||
setTransform: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
||||
stroke: (path?: Path2D) => void;
|
||||
strokeRect: (x: number, y: number, width: number, height: number) => void;
|
||||
strokeText: (text: string, x: number, y: number, maxWidth?: number) => void;
|
||||
transform: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
||||
translate: (x: number, y: number) => void;
|
||||
*/
|
||||
// clang-format on
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../uiscripting.h"
|
||||
#include <string>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
static const std::string kAttrScript = "script";
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct IScriptContextInternal : public IScriptContext
|
||||
{
|
||||
virtual void onViewCreated (CView* view, const std::string& script) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
Vendored
+133
@@ -0,0 +1,133 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "scriptingviewfactory.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
JavaScriptViewFactory::JavaScriptViewFactory (IScriptContextInternal* scripting,
|
||||
IViewFactory* origFactory)
|
||||
: Super (origFactory), scriptContext (scripting)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
JavaScriptViewFactory::~JavaScriptViewFactory () noexcept
|
||||
{
|
||||
std::for_each (viewControllerLinks.begin (), viewControllerLinks.end (),
|
||||
[this] (const auto& el) {
|
||||
el.first->unregisterViewListener (this);
|
||||
el.second->scriptContextDestroyed (scriptContext);
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
CView* JavaScriptViewFactory::createView (const UIAttributes& attributes,
|
||||
const IUIDescription* description) const
|
||||
{
|
||||
if (auto view = Super::createView (attributes, description))
|
||||
{
|
||||
if (auto value = attributes.getAttributeValue (kAttrScript))
|
||||
{
|
||||
std::optional<std::string> verifiedScript;
|
||||
if (auto scriptViewController =
|
||||
dynamic_cast<IScriptControllerExtension*> (description->getController ()))
|
||||
{
|
||||
verifiedScript = scriptViewController->verifyScript (view, *value, scriptContext);
|
||||
view->registerViewListener (const_cast<JavaScriptViewFactory*> (this));
|
||||
viewControllerLinks.emplace_back (view, scriptViewController);
|
||||
}
|
||||
const auto& script = verifiedScript ? *verifiedScript : *value;
|
||||
auto scriptSize = static_cast<uint32_t> (script.size () + 1);
|
||||
view->setAttribute (scriptAttrID, scriptSize, script.data ());
|
||||
if (!disabled)
|
||||
{
|
||||
scriptContext->onViewCreated (view, script);
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptViewFactory::getAttributeNamesForView (CView* view, StringList& attributeNames) const
|
||||
{
|
||||
if (Super::getAttributeNamesForView (view, attributeNames))
|
||||
{
|
||||
attributeNames.emplace_back (kAttrScript);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
auto JavaScriptViewFactory::getAttributeType (CView* view, const std::string& attributeName) const
|
||||
-> IViewCreator::AttrType
|
||||
{
|
||||
if (attributeName == kAttrScript)
|
||||
return IViewCreator::kScriptType;
|
||||
return Super::getAttributeType (view, attributeName);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptViewFactory::getAttributeValue (CView* view, const std::string& attributeName,
|
||||
std::string& stringValue,
|
||||
const IUIDescription* desc) const
|
||||
{
|
||||
if (attributeName == kAttrScript)
|
||||
{
|
||||
uint32_t attrSize = 0;
|
||||
if (view->getAttributeSize (scriptAttrID, attrSize) && attrSize > 0)
|
||||
{
|
||||
stringValue.resize (attrSize - 1);
|
||||
if (!view->getAttribute (scriptAttrID, attrSize, stringValue.data (), attrSize))
|
||||
stringValue = "";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return Super::getAttributeValue (view, attributeName, stringValue, desc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool JavaScriptViewFactory::applyAttributeValues (CView* view, const UIAttributes& attributes,
|
||||
const IUIDescription* desc) const
|
||||
{
|
||||
if (auto value = attributes.getAttributeValue (kAttrScript))
|
||||
{
|
||||
if (value->empty ())
|
||||
view->removeAttribute (scriptAttrID);
|
||||
else
|
||||
view->setAttribute (scriptAttrID, static_cast<uint32_t> (value->size () + 1),
|
||||
value->data ());
|
||||
if (!disabled)
|
||||
scriptContext->onViewCreated (view, *value);
|
||||
return true;
|
||||
}
|
||||
return Super::applyAttributeValues (view, attributes, desc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void JavaScriptViewFactory::setScriptingDisabled (bool state) { disabled = state; }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void JavaScriptViewFactory::viewWillDelete (CView* view)
|
||||
{
|
||||
auto it = std::find_if (viewControllerLinks.begin (), viewControllerLinks.end (),
|
||||
[view] (const auto& el) { return el.first == view; });
|
||||
if (it != viewControllerLinks.end ())
|
||||
{
|
||||
it->second->scriptContextDestroyed (scriptContext);
|
||||
viewControllerLinks.erase (it);
|
||||
}
|
||||
view->unregisterViewListener (this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "iscriptcontextinternal.h"
|
||||
#include "../../uidescription/uiattributes.h"
|
||||
#include "../../uidescription/uiviewfactory.h"
|
||||
#include "../../lib/iviewlistener.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct JavaScriptViewFactory : ViewFactoryDelegate,
|
||||
ViewListenerAdapter
|
||||
{
|
||||
static constexpr CViewAttributeID scriptAttrID = 'scri';
|
||||
|
||||
JavaScriptViewFactory (ScriptingInternal::IScriptContextInternal* scripting,
|
||||
IViewFactory* origFactory);
|
||||
~JavaScriptViewFactory () noexcept;
|
||||
|
||||
CView* createView (const UIAttributes& attributes,
|
||||
const IUIDescription* description) const override;
|
||||
bool getAttributeNamesForView (CView* view, StringList& attributeNames) const override;
|
||||
IViewCreator::AttrType getAttributeType (CView* view,
|
||||
const std::string& attributeName) const override;
|
||||
bool getAttributeValue (CView* view, const std::string& attributeName, std::string& stringValue,
|
||||
const IUIDescription* desc) const override;
|
||||
bool applyAttributeValues (CView* view, const UIAttributes& attributes,
|
||||
const IUIDescription* desc) const override;
|
||||
void setScriptingDisabled (bool state);
|
||||
|
||||
private:
|
||||
void viewWillDelete (CView* view) override;
|
||||
|
||||
using Super = ViewFactoryDelegate;
|
||||
using ViewControllerLink = std::pair<CView*, IScriptControllerExtension*>;
|
||||
using ViewControllerLinkVector = std::vector<ViewControllerLink>;
|
||||
|
||||
ScriptingInternal::IScriptContextInternal* scriptContext;
|
||||
mutable ViewControllerLinkVector viewControllerLinks;
|
||||
bool disabled {false};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../tiny-js/TinyJS.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ScriptAddChildScoped
|
||||
{
|
||||
using CScriptVar = TJS::CScriptVar;
|
||||
using CScriptVarLink = TJS::CScriptVarLink;
|
||||
|
||||
ScriptAddChildScoped (CScriptVar& var, std::string_view name, CScriptVar* obj) : var (var)
|
||||
{
|
||||
if ((link = var.findChild (name)))
|
||||
{
|
||||
oldVar = TJS::owning (link->getVar ());
|
||||
link->setVar (obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
link = var.addChild (name, obj);
|
||||
}
|
||||
}
|
||||
~ScriptAddChildScoped () noexcept
|
||||
{
|
||||
if (oldVar && link)
|
||||
{
|
||||
link->setVar (oldVar);
|
||||
oldVar->release ();
|
||||
}
|
||||
else if (link)
|
||||
{
|
||||
var.removeLink (link);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CScriptVar& var;
|
||||
CScriptVarLink* link {nullptr};
|
||||
CScriptVar* oldVar {nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline TJS::CScriptVar* createJSFunction (TJS::JSCallback&& proc)
|
||||
{
|
||||
auto funcVar = new TJS::CScriptVar (TJS::TINYJS_BLANK_DATA,
|
||||
TJS::SCRIPTVAR_FUNCTION | TJS::SCRIPTVAR_NATIVE);
|
||||
funcVar->setCallback (std::move (proc));
|
||||
return funcVar;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline TJS::CScriptVar* createJSFunction (TJS::JSCallback&& proc,
|
||||
const std::initializer_list<std::string_view>& argNames)
|
||||
{
|
||||
auto f = createJSFunction (std::move (proc));
|
||||
for (auto name : argNames)
|
||||
f->addChildNoDup (name);
|
||||
return f;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline TJS::CScriptVar* getArgument (TJS::CScriptVar* var, std::string_view argName,
|
||||
std::string_view funcSignature)
|
||||
{
|
||||
auto child = var->findChild (argName);
|
||||
auto result = child ? child->getVar () : nullptr;
|
||||
if (!result || result->isUndefined ())
|
||||
{
|
||||
TJS::string s ("Missing `");
|
||||
s.append (argName);
|
||||
s.append ("` argument in ");
|
||||
s.append (funcSignature);
|
||||
throw TJS::CScriptException (std::move (s));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
inline TJS::CScriptVar* getOptionalArgument (TJS::CScriptVar* var, std::string_view argName)
|
||||
{
|
||||
if (auto child = var->findChild (argName))
|
||||
return child->getVar ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ScriptObject
|
||||
{
|
||||
using CScriptVar = TJS::CScriptVar;
|
||||
|
||||
ScriptObject () { scriptVar = TJS::owning (new CScriptVar ()); }
|
||||
ScriptObject (CScriptVar* var) : scriptVar (TJS::owning (var)) {}
|
||||
ScriptObject (ScriptObject&& o) { *this = std::move (o); }
|
||||
ScriptObject& operator= (ScriptObject&& o)
|
||||
{
|
||||
if (scriptVar)
|
||||
scriptVar->release ();
|
||||
scriptVar = nullptr;
|
||||
std::swap (scriptVar, o.scriptVar);
|
||||
return *this;
|
||||
}
|
||||
virtual ~ScriptObject () noexcept
|
||||
{
|
||||
if (scriptVar)
|
||||
{
|
||||
scriptVar->release ();
|
||||
}
|
||||
}
|
||||
|
||||
operator CScriptVar* () const
|
||||
{
|
||||
validate ();
|
||||
return scriptVar;
|
||||
}
|
||||
CScriptVar* operator->() const
|
||||
{
|
||||
validate ();
|
||||
return scriptVar;
|
||||
}
|
||||
|
||||
CScriptVar* getVar () const
|
||||
{
|
||||
validate ();
|
||||
return scriptVar;
|
||||
}
|
||||
CScriptVar* take ()
|
||||
{
|
||||
auto v = scriptVar;
|
||||
scriptVar = nullptr;
|
||||
return v;
|
||||
}
|
||||
void addChild (std::string_view name, ScriptObject&& obj)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->addChild (name, obj);
|
||||
}
|
||||
void addChild (std::string_view name, double d)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->addChild (name, new CScriptVar (d));
|
||||
}
|
||||
void addChild (std::string_view name, int64_t i)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->addChild (name, new CScriptVar (i));
|
||||
}
|
||||
void addChild (std::string_view name, int32_t i)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->addChild (name, new CScriptVar (static_cast<int64_t> (i)));
|
||||
}
|
||||
void addChild (std::string_view name, std::string_view value)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->addChild (name, new CScriptVar (TJS::string {value.data (), value.size ()}));
|
||||
}
|
||||
void addFunc (std::string_view name, std::function<void (CScriptVar*)>&& func)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->addChild (name, createJSFunction (std::move (func)));
|
||||
}
|
||||
void addFunc (std::string_view name, std::function<void (CScriptVar*)>&& func,
|
||||
const std::initializer_list<std::string_view>& argNames)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->addChild (name, createJSFunction (std::move (func), argNames));
|
||||
}
|
||||
void addFunc (std::string_view name, std::string_view code)
|
||||
{
|
||||
validate ();
|
||||
auto funcVar = new TJS::CScriptVar (TJS::TINYJS_BLANK_DATA, TJS::SCRIPTVAR_FUNCTION);
|
||||
funcVar->setFunctionScript (code);
|
||||
scriptVar->addChild (name, funcVar);
|
||||
}
|
||||
|
||||
using OnDestroyFunc = std::function<void (CScriptVar*)>;
|
||||
void setOnDestroy (OnDestroyFunc&& f)
|
||||
{
|
||||
validate ();
|
||||
scriptVar->setLifeTimeObserver (OnDestroy::make (std::move (f)));
|
||||
}
|
||||
|
||||
protected:
|
||||
struct OnDestroy : TJS::IScriptVarLifeTimeObserver
|
||||
{
|
||||
static OnDestroy* make (OnDestroyFunc f) { return new OnDestroy (std::move (f)); }
|
||||
|
||||
private:
|
||||
OnDestroy (OnDestroyFunc f) : func (std::move (f)) {}
|
||||
|
||||
void onDestroy (CScriptVar* var)
|
||||
{
|
||||
func (var);
|
||||
delete this;
|
||||
}
|
||||
OnDestroyFunc func;
|
||||
};
|
||||
|
||||
void validate () const
|
||||
{
|
||||
if (scriptVar == nullptr)
|
||||
scriptVar = TJS::owning (new CScriptVar ());
|
||||
}
|
||||
|
||||
mutable CScriptVar* scriptVar {nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scriptobject.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct UIDescScriptObject : ScriptObject
|
||||
{
|
||||
using StringList = std::list<const std::string*>;
|
||||
using CScriptException = TJS::CScriptException;
|
||||
|
||||
UIDescScriptObject () = default;
|
||||
UIDescScriptObject (IUIDescription* desc, TJS::CTinyJS* scriptContext)
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
addFunc ("colorNames"sv, [desc] (CScriptVar* var) {
|
||||
StringList names;
|
||||
desc->collectColorNames (names);
|
||||
var->setReturnVar (createArrayFromNames (names));
|
||||
});
|
||||
addFunc ("fontNames"sv, [desc] (CScriptVar* var) {
|
||||
StringList names;
|
||||
desc->collectFontNames (names);
|
||||
var->setReturnVar (createArrayFromNames (names));
|
||||
});
|
||||
addFunc ("bitmapNames"sv, [desc] (CScriptVar* var) {
|
||||
StringList names;
|
||||
desc->collectBitmapNames (names);
|
||||
var->setReturnVar (createArrayFromNames (names));
|
||||
});
|
||||
addFunc ("gradientNames"sv, [desc] (CScriptVar* var) {
|
||||
StringList names;
|
||||
desc->collectGradientNames (names);
|
||||
var->setReturnVar (createArrayFromNames (names));
|
||||
});
|
||||
addFunc ("controlTagNames"sv, [desc] (CScriptVar* var) {
|
||||
StringList names;
|
||||
desc->collectControlTagNames (names);
|
||||
var->setReturnVar (createArrayFromNames (names));
|
||||
});
|
||||
addFunc ("getTagForName"sv,
|
||||
[desc] (CScriptVar* var) {
|
||||
auto param = var->getParameter ("name"sv);
|
||||
if (!param)
|
||||
{
|
||||
throw CScriptException ("Expect 'name' argument for getTagForName ");
|
||||
}
|
||||
auto name = param->getString ();
|
||||
auto tag = desc->getTagForName (name.data ());
|
||||
var->setReturnVar (new CScriptVar (static_cast<int64_t> (tag)));
|
||||
},
|
||||
{"name"});
|
||||
addFunc ("lookupTagName"sv,
|
||||
[desc] (CScriptVar* var) {
|
||||
auto param = var->getParameter ("tag"sv);
|
||||
if (!param)
|
||||
{
|
||||
throw CScriptException ("Expect 'tag' argument for lookupTagName ");
|
||||
}
|
||||
if (!param->isInt ())
|
||||
{
|
||||
throw CScriptException ("Expect 'tag' argument to be an integer ");
|
||||
}
|
||||
if (auto tagName =
|
||||
desc->lookupControlTagName (static_cast<int32_t> (param->getInt ())))
|
||||
{
|
||||
var->setReturnVar (new CScriptVar (std::string (tagName)));
|
||||
}
|
||||
},
|
||||
{"tag"});
|
||||
}
|
||||
|
||||
static CScriptVar* createArrayFromNames (const StringList& names)
|
||||
{
|
||||
auto array = new CScriptVar ();
|
||||
array->setArray ();
|
||||
int index = 0;
|
||||
for (auto name : names)
|
||||
{
|
||||
array->addChild (std::to_string (index), new CScriptVar (*name));
|
||||
++index;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "viewscriptobject.h"
|
||||
#include "converters.h"
|
||||
#include "drawable.h"
|
||||
#include "../uiscripting.h"
|
||||
#include "../../uidescription/iviewfactory.h"
|
||||
#include "../../uidescription/uiattributes.h"
|
||||
#include "../../lib/cview.h"
|
||||
#include "../../lib/controls/ccontrol.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
using namespace std::literals;
|
||||
using namespace TJS;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
ViewScriptObject::ViewScriptObject (CView* view, IViewScriptObjectContext* context)
|
||||
: view (view), context (context)
|
||||
{
|
||||
scriptVar->setLifeTimeObserver (this);
|
||||
auto viewType = IViewFactory::getViewName (view);
|
||||
scriptVar->addChild ("type"sv, new CScriptVar (std::string (viewType ? viewType : "unknown")));
|
||||
addFunc ("setAttribute"sv,
|
||||
[uiDesc = context->getUIDescription (), view] (CScriptVar* var) {
|
||||
auto key = var->getParameter ("key"sv);
|
||||
auto value = var->getParameter ("value"sv);
|
||||
UIAttributes attr;
|
||||
attr.setAttribute (key->getString ().data (), value->getString ().data ());
|
||||
auto result = uiDesc->getViewFactory ()->applyAttributeValues (view, attr, uiDesc);
|
||||
var->getReturnVar ()->setInt (result);
|
||||
},
|
||||
{"key", "value"});
|
||||
addFunc ("getAttribute"sv,
|
||||
[uiDesc = context->getUIDescription (), view] (CScriptVar* var) {
|
||||
auto key = var->getParameter ("key"sv);
|
||||
std::string result;
|
||||
if (uiDesc->getViewFactory ()->getAttributeValue (view, key->getString ().data (),
|
||||
result, uiDesc))
|
||||
{
|
||||
var->getReturnVar ()->setString (result);
|
||||
}
|
||||
else
|
||||
{
|
||||
var->getReturnVar ()->setUndefined ();
|
||||
}
|
||||
},
|
||||
{"key"});
|
||||
addFunc ("isTypeOf"sv,
|
||||
[uiDesc = context->getUIDescription (), view] (CScriptVar* var) {
|
||||
auto typeName = var->getParameter ("typeName"sv);
|
||||
auto result =
|
||||
uiDesc->getViewFactory ()->viewIsTypeOf (view, typeName->getString ().data ());
|
||||
var->getReturnVar ()->setInt (result);
|
||||
},
|
||||
{"typeName"});
|
||||
addFunc ("invalid"sv, [view] (CScriptVar* var) { view->invalid (); });
|
||||
addFunc ("invalidRect"sv,
|
||||
[view] (CScriptVar* var) {
|
||||
auto rectVar = var->getParameter ("rect"sv);
|
||||
if (!rectVar)
|
||||
throw CScriptException ("Missing 'rect' argument in view.invalidRect(rect) ");
|
||||
auto rect = fromScriptRect (*rectVar);
|
||||
view->invalidRect (rect);
|
||||
},
|
||||
{"rect"});
|
||||
addFunc ("getBounds"sv, [view] (CScriptVar* var) {
|
||||
auto bounds = view->getViewSize ();
|
||||
bounds.originize ();
|
||||
var->setReturnVar (makeScriptRect (bounds));
|
||||
});
|
||||
addFunc ("getParent"sv, [view, context] (CScriptVar* var) {
|
||||
auto parentView = view->getParentView ();
|
||||
if (!parentView)
|
||||
{
|
||||
var->getReturnVar ()->setUndefined ();
|
||||
return;
|
||||
}
|
||||
auto obj = context->addView (parentView);
|
||||
vstgui_assert (obj);
|
||||
var->setReturnVar (obj->getVar ());
|
||||
obj->getVar ()->release ();
|
||||
});
|
||||
addFunc ("getControllerProperty"sv,
|
||||
[view] (CScriptVar* var) {
|
||||
auto viewController = getViewController (view, true);
|
||||
auto controller = dynamic_cast<IScriptControllerExtension*> (viewController);
|
||||
auto name = var->getParameter ("name"sv);
|
||||
if (!controller || !name)
|
||||
{
|
||||
var->getReturnVar ()->setUndefined ();
|
||||
return;
|
||||
}
|
||||
IScriptControllerExtension::PropertyValue value;
|
||||
if (!controller->getProperty (view, name->getString (), value))
|
||||
{
|
||||
var->getReturnVar ()->setUndefined ();
|
||||
return;
|
||||
}
|
||||
std::visit (
|
||||
[&] (auto&& value) {
|
||||
using T = std::decay_t<decltype (value)>;
|
||||
if constexpr (std::is_same_v<T, int64_t>)
|
||||
var->getReturnVar ()->setInt (value);
|
||||
else if constexpr (std::is_same_v<T, double>)
|
||||
var->getReturnVar ()->setDouble (value);
|
||||
else if constexpr (std::is_same_v<T, std::string>)
|
||||
var->getReturnVar ()->setString (value);
|
||||
else if constexpr (std::is_same_v<T, std::nullptr_t>)
|
||||
var->getReturnVar ()->setUndefined ();
|
||||
},
|
||||
value);
|
||||
},
|
||||
{"name"});
|
||||
addFunc ("setControllerProperty"sv,
|
||||
[view] (CScriptVar* var) {
|
||||
auto viewController = getViewController (view, true);
|
||||
auto controller = dynamic_cast<IScriptControllerExtension*> (viewController);
|
||||
auto name = var->getParameter ("name"sv);
|
||||
auto value = var->getParameter ("value"sv);
|
||||
if (!controller || !name || !value || !(value->isNumeric () || value->isString ()))
|
||||
{
|
||||
var->getReturnVar ()->setUndefined ();
|
||||
return;
|
||||
}
|
||||
IScriptControllerExtension::PropertyValue propValue;
|
||||
if (value->isInt ())
|
||||
propValue = value->getInt ();
|
||||
else if (value->isDouble ())
|
||||
propValue = value->getDouble ();
|
||||
else if (value->isString ())
|
||||
propValue = value->getString ().data ();
|
||||
auto result = controller->setProperty (view, name->getString (), propValue);
|
||||
var->getReturnVar ()->setInt (result);
|
||||
},
|
||||
{"name", "value"});
|
||||
if (auto control = dynamic_cast<CControl*> (view))
|
||||
{
|
||||
addFunc ("setValue"sv,
|
||||
[control] (CScriptVar* var) {
|
||||
auto value = var->getParameter ("value"sv);
|
||||
if (value->isNumeric ())
|
||||
{
|
||||
auto oldValue = control->getValue ();
|
||||
control->setValue (static_cast<float> (value->getDouble ()));
|
||||
if (oldValue != control->getValue ())
|
||||
control->valueChanged ();
|
||||
}
|
||||
},
|
||||
{"value"});
|
||||
addFunc ("getValue"sv, [control] (CScriptVar* var) {
|
||||
var->getReturnVar ()->setDouble (control->getValue ());
|
||||
});
|
||||
addFunc ("setValueNormalized"sv,
|
||||
[control] (CScriptVar* var) {
|
||||
auto value = var->getParameter ("value"sv);
|
||||
if (value->isNumeric ())
|
||||
{
|
||||
auto oldValue = control->getValue ();
|
||||
control->setValueNormalized (static_cast<float> (value->getDouble ()));
|
||||
if (oldValue != control->getValue ())
|
||||
control->valueChanged ();
|
||||
}
|
||||
},
|
||||
{"value"});
|
||||
addFunc ("getValueNormalized"sv, [control] (CScriptVar* var) {
|
||||
var->getReturnVar ()->setDouble (control->getValueNormalized ());
|
||||
});
|
||||
addFunc ("beginEdit"sv, [control] (CScriptVar* var) { control->beginEdit (); });
|
||||
addFunc ("endEdit"sv, [control] (CScriptVar* var) { control->endEdit (); });
|
||||
addFunc ("getMinValue"sv, [control] (CScriptVar* var) {
|
||||
var->getReturnVar ()->setDouble (control->getMin ());
|
||||
});
|
||||
addFunc ("getMaxValue"sv, [control] (CScriptVar* var) {
|
||||
var->getReturnVar ()->setDouble (control->getMax ());
|
||||
});
|
||||
addFunc ("getTag"sv, [control] (CScriptVar* var) {
|
||||
var->getReturnVar ()->setInt (control->getTag ());
|
||||
});
|
||||
}
|
||||
if (auto drawable = dynamic_cast<JavaScriptDrawable*> (view))
|
||||
drawable->setup (this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
ViewScriptObject::~ViewScriptObject () noexcept
|
||||
{
|
||||
if (scriptVar)
|
||||
scriptVar->setLifeTimeObserver (nullptr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void ViewScriptObject::onDestroy (CScriptVar* v)
|
||||
{
|
||||
v->setLifeTimeObserver (nullptr);
|
||||
scriptVar = nullptr;
|
||||
if (context)
|
||||
context->removeView (view);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scriptobject.h"
|
||||
#include "../../lib/vstguifwd.h"
|
||||
#include "../../uidescription/iuidescription.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
namespace ScriptingInternal {
|
||||
|
||||
struct IViewScriptObjectContext;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ViewScriptObject : ScriptObject,
|
||||
TJS::IScriptVarLifeTimeObserver
|
||||
{
|
||||
ViewScriptObject (CView* view, IViewScriptObjectContext* context);
|
||||
~ViewScriptObject () noexcept;
|
||||
|
||||
IViewScriptObjectContext* getContext () const { return context; }
|
||||
|
||||
void onDestroy (CScriptVar* v) override;
|
||||
|
||||
private:
|
||||
CView* view {nullptr};
|
||||
IViewScriptObjectContext* context {nullptr};
|
||||
};
|
||||
|
||||
using ViewScriptMap = std::unordered_map<CView*, std::unique_ptr<ViewScriptObject>>;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct IViewScriptObjectContext
|
||||
{
|
||||
virtual ~IViewScriptObjectContext () = default;
|
||||
|
||||
virtual IUIDescription* getUIDescription () const = 0;
|
||||
virtual ViewScriptObject* addView (CView* view) = 0;
|
||||
virtual ViewScriptMap::iterator removeView (CView* view) = 0;
|
||||
virtual ScriptObject evalScript (std::string_view script) noexcept = 0;
|
||||
virtual TJS::CScriptVar* getRoot () const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,3 @@
|
||||
.vscode/
|
||||
build/
|
||||
build.*/
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
project (tiny-js)
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
|
||||
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set (CMAKE_BUILD_TYPE "Debug")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
else()
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
endif(NOT CMAKE_BUILD_TYPE)
|
||||
|
||||
if (NOT WIN32)
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
|
||||
if(COMPILER_SUPPORTS_CXX14)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall")
|
||||
else()
|
||||
message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER} has no C++14 support.")
|
||||
endif()
|
||||
endif(NOT WIN32)
|
||||
|
||||
FILE(GLOB TINY_JS_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS.h
|
||||
)
|
||||
|
||||
FILE(GLOB TINY_JS_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS_Functions.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS_MathFunctions.cpp
|
||||
)
|
||||
|
||||
add_library(tiny-js STATIC ${TINY_JS_HEADER_FILES} ${TINY_JS_SOURCE_FILES})
|
||||
|
||||
ADD_EXECUTABLE(tiny-js-cli Script.cpp ${TINY_JS_SOURCE_FILES})
|
||||
|
||||
ADD_EXECUTABLE(tiny-js-tests run_tests.cpp ${TINY_JS_SOURCE_FILES})
|
||||
|
||||
add_custom_command(
|
||||
TARGET tiny-js-tests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/tests
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tests)
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Gordon Williams
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
CC=g++
|
||||
CFLAGS=-c -g -Wall -rdynamic -D_DEBUG
|
||||
LDFLAGS=-g -rdynamic
|
||||
|
||||
SOURCES= \
|
||||
TinyJS.cpp \
|
||||
TinyJS_Functions.cpp \
|
||||
TinyJS_MathFunctions.cpp
|
||||
|
||||
OBJECTS=$(SOURCES:.cpp=.o)
|
||||
|
||||
all: run_tests Script
|
||||
|
||||
run_tests: run_tests.o $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) run_tests.o $(OBJECTS) -o $@
|
||||
|
||||
Script: Script.o $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) Script.o $(OBJECTS) -o $@
|
||||
|
||||
.cpp.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f run_tests Script run_tests.o Script.o $(OBJECTS)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
tiny-js
|
||||
=======
|
||||
|
||||
(originally [on Google Code](https://code.google.com/p/tiny-js/))
|
||||
|
||||
This project aims to be an extremely simple (~2000 line) JavaScript interpreter, meant for
|
||||
inclusion in applications that require a simple, familiar script language that can be included
|
||||
with no dependencies other than normal C++ libraries. It currently consists of two source files:
|
||||
one containing the interpreter, another containing built-in functions such as String.substring.
|
||||
|
||||
TinyJS is not designed to be fast or full-featured. However it is great for scripting simple
|
||||
behaviour, or loading & saving settings.
|
||||
|
||||
I make absolutely no guarantees that this is compliant to JavaScript/EcmaScript standard.
|
||||
In fact I am sure it isn't. However I welcome suggestions for changes that will bring it
|
||||
closer to compliance without overly complicating the code, or useful test cases to add to
|
||||
the test suite.
|
||||
|
||||
Currently TinyJS supports:
|
||||
|
||||
* Variables, Arrays, Structures
|
||||
* JSON parsing and output
|
||||
* Functions
|
||||
* Calling C/C++ code from JavaScript
|
||||
* Objects with Inheritance (not fully implemented)
|
||||
|
||||
Please see [CodeExamples](https://github.com/gfwilliams/tiny-js/blob/wiki/CodeExamples.md) for examples of code that works...
|
||||
|
||||
For a list of known issues, please see the comments at the top of the TinyJS.cpp file, as well as the [GitHub issues](https://github.com/gfwilliams/tiny-js/issues)
|
||||
|
||||
There is also the [42tiny-js branch](https://github.com/gfwilliams/tiny-js/tree/42tiny-js) - this is maintained by Armin and provides a more full-featured JavaScript implementation than GitHub master.
|
||||
|
||||
TinyJS is released under an MIT licence.
|
||||
|
||||
Internal Structure
|
||||
------------------------
|
||||
|
||||
TinyJS uses a Recursive Descent Parser, so there is no 'Parser Generator' required. It does not
|
||||
compile to an intermediate code, and instead executes directly from source code. This makes it
|
||||
quite fast for code that is executed infrequently, and slow for loops.
|
||||
|
||||
Variables, Arrays and Objects are stored in a simple linked list tree structure (42tiny-js uses a C++ Map).
|
||||
This is simple, but relatively slow for large structures or arrays.
|
||||
|
||||
JavaScript for Microcontrollers
|
||||
--------------------------------
|
||||
|
||||
If you're after JavaScript for Microcontrollers, take a look at the
|
||||
[Espruino JavaScript Interpreter](http://www.espruino.com ) - it is a complete re-write of TinyJS
|
||||
targeted at processors with extremely low RAM (8kb or more). It is currently available for a range
|
||||
of STM32 ARM Microcontrollers, including [two boards that have it pre-installed](http://www.espruino.com/Order).
|
||||
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a simple program showing how to use TinyJS
|
||||
*/
|
||||
|
||||
#include "TinyJS.h"
|
||||
#include "TinyJS_Functions.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// const char *code = "var a = 5; if (a==5) a=4; else a=3;";
|
||||
// const char *code = "{ var a = 4; var b = 1; while (a>0) { b = b * 2; a = a - 1; } var c = 5; }";
|
||||
// const char *code = "{ var b = 1; for (var i=0;i<4;i=i+1) b = b * 2; }";
|
||||
const char* code = "function myfunc(x, y) { return x + y; } var a = myfunc(1,2); print(a);";
|
||||
|
||||
void js_print (CScriptVar* v, void* userdata)
|
||||
{
|
||||
printf ("> %s\n", v->getParameter ("text")->getString ().c_str ());
|
||||
}
|
||||
|
||||
void js_dump (CScriptVar* v, void* userdata)
|
||||
{
|
||||
CTinyJS* js = (CTinyJS*)userdata;
|
||||
js->root->trace ("> ");
|
||||
}
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
CTinyJS* js = new CTinyJS ();
|
||||
/* add the functions from TinyJS_Functions.cpp */
|
||||
registerFunctions (js);
|
||||
/* Add a native function */
|
||||
js->addNative ("function print(text)", &js_print, 0);
|
||||
js->addNative ("function dump()", &js_dump, js);
|
||||
/* Execute out bit of code - we could call 'evaluate' here if
|
||||
we wanted something returned */
|
||||
try
|
||||
{
|
||||
js->execute ("var lets_quit = 0; function quit() { lets_quit = 1; }");
|
||||
js->execute ("print(\"Interactive mode... Type quit(); to exit, or print(...); to print "
|
||||
"something, or dump() to dump the symbol table!\");");
|
||||
}
|
||||
catch (CScriptException* e)
|
||||
{
|
||||
printf ("ERROR: %s\n", e->text.c_str ());
|
||||
}
|
||||
|
||||
while (js->evaluate ("lets_quit") == "0")
|
||||
{
|
||||
char buffer[2048];
|
||||
fgets (buffer, sizeof (buffer), stdin);
|
||||
try
|
||||
{
|
||||
js->execute (buffer);
|
||||
}
|
||||
catch (CScriptException* e)
|
||||
{
|
||||
printf ("ERROR: %s\n", e->text.c_str ());
|
||||
}
|
||||
}
|
||||
delete js;
|
||||
#ifdef _WIN32
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks ();
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
+2789
File diff suppressed because it is too large
Load Diff
+526
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// If defined, this keeps a note of all calls and where from in memory. This is slower, but good for
|
||||
// debugging
|
||||
#define TINYJS_CALL_STACK
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
#include <limits>
|
||||
#include <any>
|
||||
|
||||
#ifndef TRACE
|
||||
#define TRACE printf
|
||||
#endif // TRACE
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
constexpr int TINYJS_LOOP_MAX_ITERATIONS = 8192;
|
||||
|
||||
enum class LexType : int
|
||||
{
|
||||
Eof = 0,
|
||||
ID = 256,
|
||||
INT,
|
||||
FLOAT,
|
||||
STR,
|
||||
|
||||
EQUAL,
|
||||
TYPEEQUAL,
|
||||
NEQUAL,
|
||||
NTYPEEQUAL,
|
||||
LEQUAL,
|
||||
LSHIFT,
|
||||
LSHIFTEQUAL,
|
||||
GEQUAL,
|
||||
RSHIFT,
|
||||
RSHIFTUNSIGNED,
|
||||
RSHIFTEQUAL,
|
||||
PLUSEQUAL,
|
||||
MINUSEQUAL,
|
||||
PLUSPLUS,
|
||||
MINUSMINUS,
|
||||
ANDEQUAL,
|
||||
ANDAND,
|
||||
OREQUAL,
|
||||
OROR,
|
||||
XOREQUAL,
|
||||
|
||||
// reserved words
|
||||
R_LIST_START,
|
||||
R_IF = R_LIST_START,
|
||||
R_ELSE,
|
||||
R_DO,
|
||||
R_WHILE,
|
||||
R_FOR,
|
||||
R_BREAK,
|
||||
R_CONTINUE,
|
||||
R_FUNCTION,
|
||||
R_RETURN,
|
||||
R_VAR,
|
||||
R_TRUE,
|
||||
R_FALSE,
|
||||
R_NULL,
|
||||
R_UNDEFINED,
|
||||
R_NEW,
|
||||
|
||||
LIST_END /* always the last entry */
|
||||
};
|
||||
inline constexpr int asInteger (LexType t) { return static_cast<int> (t); }
|
||||
inline constexpr LexType asLexType (int t)
|
||||
{
|
||||
if (t < asInteger (LexType::ID) || t > (asInteger (LexType::LIST_END)))
|
||||
return LexType::Eof;
|
||||
return static_cast<LexType> (t);
|
||||
}
|
||||
|
||||
enum SCRIPTVAR_FLAGS
|
||||
{
|
||||
SCRIPTVAR_UNDEFINED = 0,
|
||||
SCRIPTVAR_FUNCTION = 1,
|
||||
SCRIPTVAR_OBJECT = 2,
|
||||
SCRIPTVAR_ARRAY = 4,
|
||||
SCRIPTVAR_DOUBLE = 8, // floating point double
|
||||
SCRIPTVAR_INTEGER = 16, // integer number
|
||||
SCRIPTVAR_STRING = 32, // string
|
||||
SCRIPTVAR_NULL = 64, // it seems null is its own data type
|
||||
|
||||
SCRIPTVAR_NATIVE = 128, // to specify this is a native function
|
||||
SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | SCRIPTVAR_DOUBLE | SCRIPTVAR_INTEGER,
|
||||
SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | SCRIPTVAR_INTEGER | SCRIPTVAR_STRING |
|
||||
SCRIPTVAR_FUNCTION | SCRIPTVAR_OBJECT | SCRIPTVAR_ARRAY |
|
||||
SCRIPTVAR_NULL,
|
||||
|
||||
};
|
||||
|
||||
static constexpr auto TINYJS_RETURN_VAR = "return";
|
||||
static constexpr auto TINYJS_PROTOTYPE_CLASS = "prototype";
|
||||
static constexpr auto TINYJS_TEMP_NAME = "";
|
||||
static constexpr auto TINYJS_BLANK_DATA = "";
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Custom memory allocator
|
||||
using AllocatorFunc = std::function<void*(size_t)>;
|
||||
using DeallocatorFunc = std::function<void (void*, size_t)>;
|
||||
extern AllocatorFunc allocator;
|
||||
extern DeallocatorFunc deallocator;
|
||||
|
||||
void setCustomAllocator (AllocatorFunc&& allocator, DeallocatorFunc&& deallocator);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
struct Allocator
|
||||
{
|
||||
using value_type = T;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
|
||||
Allocator () = default;
|
||||
|
||||
template<class U>
|
||||
constexpr Allocator (const Allocator<U>&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] T* allocate (std::size_t n)
|
||||
{
|
||||
return static_cast<T*> (allocator (n * sizeof (T)));
|
||||
}
|
||||
void deallocate (T* p, std::size_t n) noexcept { deallocator (p, n); }
|
||||
|
||||
bool operator== (const Allocator& other) const { return &other == this; }
|
||||
bool operator!= (const Allocator& other) const { return &other != this; }
|
||||
};
|
||||
|
||||
using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
|
||||
using ostringstream = std::basic_ostringstream<char, std::char_traits<char>, Allocator<char>>;
|
||||
|
||||
/** convert the given string into a quoted string suitable for javascript */
|
||||
string getJSString (std::string_view str);
|
||||
/** convert the given string to an 64 bit integer supporting hex and octal written numbers */
|
||||
int64_t stringToInteger (std::string_view str);
|
||||
|
||||
class CScriptException
|
||||
{
|
||||
public:
|
||||
string text;
|
||||
CScriptException (const string& exceptionText);
|
||||
CScriptException (string&& exceptionText);
|
||||
CScriptException (const CScriptException&) = default;
|
||||
CScriptException (CScriptException&&) = default;
|
||||
~CScriptException () noexcept;
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
};
|
||||
|
||||
class CScriptLex
|
||||
{
|
||||
public:
|
||||
CScriptLex (std::string_view input);
|
||||
~CScriptLex (void);
|
||||
|
||||
/** Get the string representation of the given token */
|
||||
static string getTokenStr (int token);
|
||||
|
||||
/** Lexical match wotsit */
|
||||
void match (int expected_tk);
|
||||
void match (LexType expected_tk);
|
||||
/** Reset this lex so we can start again */
|
||||
void reset ();
|
||||
|
||||
int getToken () const { return token; }
|
||||
size_t getTokenStart () const { return tokenStart; }
|
||||
size_t getTokenEnd () const { return tokenEnd; }
|
||||
const string& getTokenString () const { return tkStr; }
|
||||
|
||||
/** Return a sub-string from the given position up until right now */
|
||||
string getSubString (size_t pos) const;
|
||||
/** Return a sub-lexer from the given position up until right now */
|
||||
CScriptLex* getSubLex (size_t lastPosition) const;
|
||||
/** Return a string representing the position in lines and columns of the character pos given */
|
||||
string getPosition (size_t pos = std::numeric_limits<size_t>::max ()) const;
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
private:
|
||||
void getNextCh ();
|
||||
/** Get the text token from our text string */
|
||||
void getNextToken ();
|
||||
|
||||
/** The type of the token that we have */
|
||||
int token;
|
||||
/** Position in the data at the beginning of the token we have here */
|
||||
size_t tokenStart;
|
||||
/** Position in the data at the last character of the token we have here */
|
||||
size_t tokenEnd;
|
||||
/** Position in the data at the last character of the last token */
|
||||
size_t tokenLastEnd;
|
||||
/** Data contained in the token we have here */
|
||||
string tkStr;
|
||||
|
||||
char currCh, nextCh;
|
||||
|
||||
/** Data string to get tokens from */
|
||||
const char* data;
|
||||
/** Start and end position in data string */
|
||||
size_t dataEnd;
|
||||
/** Position in data (we CAN go past the end of the string here) */
|
||||
size_t dataPos;
|
||||
|
||||
std::vector<size_t, Allocator<size_t>> newLinePositions;
|
||||
};
|
||||
|
||||
class CScriptVar;
|
||||
|
||||
using JSCallback = std::function<void (CScriptVar* var)>;
|
||||
|
||||
class CScriptVarLink
|
||||
{
|
||||
public:
|
||||
CScriptVarLink (CScriptVar* var, const string& name = TINYJS_TEMP_NAME, bool own = false);
|
||||
/** Copy constructor */
|
||||
CScriptVarLink (const CScriptVarLink& link);
|
||||
~CScriptVarLink ();
|
||||
/** Replace the Variable pointed to */
|
||||
void replaceWith (CScriptVar* newVar);
|
||||
/** Replace the Variable pointed to (just dereferences) */
|
||||
void replaceWith (CScriptVarLink* newVar);
|
||||
/** Get the name as an integer (for arrays) */
|
||||
int getIntName () const;
|
||||
/** Set the name as an integer (for arrays) */
|
||||
void setIntName (int n);
|
||||
|
||||
const string& getName () const { return name; }
|
||||
|
||||
void setNextSibling (CScriptVarLink* s) { nextSibling = s; }
|
||||
void setPrevSibling (CScriptVarLink* s) { prevSibling = s; }
|
||||
CScriptVarLink* getNextSibling () const { return nextSibling; }
|
||||
CScriptVarLink* getPrevSibling () const { return prevSibling; }
|
||||
|
||||
void setVar (CScriptVar* v);
|
||||
CScriptVar* getVar () const { return var; }
|
||||
|
||||
bool owned () const { return isOwned; }
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
private:
|
||||
string name;
|
||||
CScriptVarLink* nextSibling {nullptr};
|
||||
CScriptVarLink* prevSibling {nullptr};
|
||||
CScriptVar* var {nullptr};
|
||||
bool isOwned {false};
|
||||
};
|
||||
|
||||
struct IScriptVarLifeTimeObserver
|
||||
{
|
||||
virtual ~IScriptVarLifeTimeObserver () noexcept = default;
|
||||
|
||||
virtual void onDestroy (CScriptVar* var) = 0;
|
||||
};
|
||||
|
||||
/** Variable class (containing a doubly-linked list of children) */
|
||||
class CScriptVar
|
||||
{
|
||||
public:
|
||||
/** Create undefined */
|
||||
CScriptVar ();
|
||||
/** User defined */
|
||||
CScriptVar (const string& varData, int varFlags);
|
||||
/** Create a string */
|
||||
CScriptVar (std::string_view str);
|
||||
/** Create a double */
|
||||
CScriptVar (double varData);
|
||||
/** Create an integer */
|
||||
CScriptVar (int64_t val);
|
||||
/** Create an integer */
|
||||
CScriptVar (bool val);
|
||||
virtual ~CScriptVar (void);
|
||||
|
||||
/** If this is a function, get the result value (for use by native functions) */
|
||||
CScriptVar* getReturnVar ();
|
||||
/** Set the result value. Use this when setting complex return data as it avoids a deepCopy() */
|
||||
void setReturnVar (CScriptVar* var);
|
||||
/** If this is a function, get the parameter with the given name (for use by native functions)
|
||||
*/
|
||||
CScriptVar* getParameter (std::string_view name);
|
||||
|
||||
/** Tries to find a child with the given name, may return 0 */
|
||||
CScriptVarLink* findChild (std::string_view childName);
|
||||
/** Tries to find a child with the given name, or will create it with the given flags */
|
||||
CScriptVarLink* findChildOrCreate (std::string_view childName,
|
||||
int varFlags = SCRIPTVAR_UNDEFINED);
|
||||
/** Tries to find a child with the given path (separated by dots) */
|
||||
CScriptVarLink* findChildOrCreateByPath (const string& path);
|
||||
/** add a child if not already exist */
|
||||
CScriptVarLink* addChild (std::string_view childName, CScriptVar* child = NULL);
|
||||
/** add a child overwriting any with the same name */
|
||||
CScriptVarLink* addChildNoDup (std::string_view childName, CScriptVar* child = NULL);
|
||||
/** remove the child */
|
||||
void removeChild (CScriptVar* child);
|
||||
/** Remove a specific link (this is faster than finding via a child) */
|
||||
void removeLink (CScriptVarLink* link);
|
||||
void removeAllChildren ();
|
||||
/** The the value at an array index */
|
||||
CScriptVar* getArrayIndex (int idx);
|
||||
/** Set the value at an array index */
|
||||
void setArrayIndex (int idx, CScriptVar* value);
|
||||
/** If this is an array, return the number of items in it (else 0) */
|
||||
int getArrayLength ();
|
||||
/** Get the number of children */
|
||||
int getChildren ();
|
||||
|
||||
int64_t getInt ();
|
||||
bool getBool () { return getInt () != 0; }
|
||||
double getDouble ();
|
||||
const string& getString ();
|
||||
/** get Data as a parsable javascript string */
|
||||
string getParsableString ();
|
||||
void setInt (int64_t num);
|
||||
void setDouble (double val);
|
||||
void setString (std::string_view str);
|
||||
void setUndefined ();
|
||||
void setArray ();
|
||||
bool equals (CScriptVar* v);
|
||||
|
||||
bool isInt () { return (flags & SCRIPTVAR_INTEGER) != 0; }
|
||||
bool isDouble () { return (flags & SCRIPTVAR_DOUBLE) != 0; }
|
||||
bool isString () { return (flags & SCRIPTVAR_STRING) != 0; }
|
||||
bool isNumeric () { return (flags & SCRIPTVAR_NUMERICMASK) != 0; }
|
||||
bool isFunction () { return (flags & SCRIPTVAR_FUNCTION) != 0; }
|
||||
bool isObject () { return (flags & SCRIPTVAR_OBJECT) != 0; }
|
||||
bool isArray () { return (flags & SCRIPTVAR_ARRAY) != 0; }
|
||||
bool isNative () { return (flags & SCRIPTVAR_NATIVE) != 0; }
|
||||
bool isUndefined () { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; }
|
||||
bool isNull () { return (flags & SCRIPTVAR_NULL) != 0; }
|
||||
/** Is this *not* an array/object/etc */
|
||||
bool isBasic () { return firstChild == 0; }
|
||||
|
||||
/** do a maths op with another script variable */
|
||||
CScriptVar* mathsOp (CScriptVar* b, int op);
|
||||
/** copy the value from the value given */
|
||||
void copyValue (CScriptVar* val);
|
||||
/** deep copy this node and return the result */
|
||||
CScriptVar* deepCopy ();
|
||||
|
||||
/** Dump out the contents of this using trace */
|
||||
void trace (string indentStr = "", const string& name = "");
|
||||
/** For debugging - just dump a string version of the flags */
|
||||
string getFlagsAsString ();
|
||||
/** Write out all the JS code needed to recreate this script variable to the stream (as JSON) */
|
||||
void getJSON (std::ostream& destination, const string linePrefix = "");
|
||||
/** Set the callback for native functions */
|
||||
void setCallback (const JSCallback& callback);
|
||||
/** Moves in the callback for native functions */
|
||||
void setCallback (JSCallback&& callback);
|
||||
|
||||
void callCallback (CScriptVar* var);
|
||||
|
||||
void setFunctionScript (std::string_view str);
|
||||
|
||||
/// For memory management/garbage collection
|
||||
/** Add reference to this variable */
|
||||
CScriptVar* addRef ();
|
||||
/** Remove a reference, and delete this variable if required */
|
||||
void release ();
|
||||
/** Get the number of references to this script variable */
|
||||
int getRefs ();
|
||||
|
||||
void setLifeTimeObserver (IScriptVarLifeTimeObserver* obs) { lifeTimeObserver = obs; }
|
||||
|
||||
CScriptVarLink* getFirstChild () const { return firstChild; }
|
||||
CScriptVarLink* getLastChild () const { return lastChild; }
|
||||
|
||||
void setCustomData (std::any&& cd) { customData = std::move (cd); }
|
||||
const std::any& getCustomData () const { return customData; }
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
protected:
|
||||
CScriptVarLink* firstChild {nullptr};
|
||||
CScriptVarLink* lastChild {nullptr};
|
||||
|
||||
/** The number of references held to this - used for garbage collection */
|
||||
int refs {0};
|
||||
/** the flags determine the type of the variable - int/double/string/etc */
|
||||
int flags {0};
|
||||
std::variant<string, int64_t, double, JSCallback> variant;
|
||||
std::any customData;
|
||||
string dataStr;
|
||||
|
||||
/** Copy the basic data and flags from the variable given, with no
|
||||
* children. Should be used internally only - by copyValue and deepCopy */
|
||||
void copySimpleData (CScriptVar* val);
|
||||
|
||||
private:
|
||||
IScriptVarLifeTimeObserver* lifeTimeObserver {nullptr};
|
||||
};
|
||||
|
||||
inline CScriptVar* owning (CScriptVar* v) { return v->addRef (); }
|
||||
|
||||
class CTinyJS
|
||||
{
|
||||
public:
|
||||
CTinyJS ();
|
||||
~CTinyJS ();
|
||||
|
||||
void execute (const string& code);
|
||||
/** Evaluate the given code and return a link to a javascript object,
|
||||
* useful for (dangerous) JSON parsing. If nothing to return, will return
|
||||
* 'undefined' variable type. CScriptVarLink is returned as this will
|
||||
* automatically release the result as it goes out of scope. If you want to
|
||||
* keep it, you must use addRef() and release() */
|
||||
CScriptVarLink evaluateComplex (std::string_view code);
|
||||
/** Evaluate the given code and return a string. If nothing to return, will return
|
||||
* 'undefined' */
|
||||
string evaluate (std::string_view code);
|
||||
|
||||
/** add a native function to be called from TinyJS
|
||||
example:
|
||||
\code
|
||||
void scRandInt(CScriptVar *c, void *userdata) { ... }
|
||||
tinyJS->addNative("function randInt(min, max)", scRandInt, 0);
|
||||
\endcode
|
||||
|
||||
or
|
||||
|
||||
\code
|
||||
void scSubstring(CScriptVar *c, void *userdata) { ... }
|
||||
tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0);
|
||||
\endcode
|
||||
*/
|
||||
void addNative (std::string_view funcDesc, const JSCallback& ptr);
|
||||
|
||||
/** Get the given variable specified by a path (var1.var2.etc), or return 0 */
|
||||
CScriptVar* getScriptVariable (const string& path) const;
|
||||
/** Get the value of the given variable, or return 0 */
|
||||
const string* getVariable (const string& path) const;
|
||||
/** set the value of the given variable, return trur if it exists and gets set */
|
||||
bool setVariable (const string& path, const string& varData);
|
||||
|
||||
/** Send all variables to stdout */
|
||||
void trace ();
|
||||
|
||||
CScriptVar* getRoot () const { return root; }
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
private:
|
||||
/** root of symbol table */
|
||||
CScriptVar* root {nullptr};
|
||||
/** current lexer */
|
||||
CScriptLex* lexer {nullptr};
|
||||
/** stack of scopes when parsing */
|
||||
std::vector<CScriptVar*> scopes;
|
||||
#ifdef TINYJS_CALL_STACK
|
||||
/** Names of places called so we can show when erroring */
|
||||
std::vector<string> call_stack;
|
||||
#endif
|
||||
|
||||
/** Built in string class */
|
||||
CScriptVar* stringClass {nullptr};
|
||||
/** Built in object class */
|
||||
CScriptVar* objectClass {nullptr};
|
||||
/** Built in array class */
|
||||
CScriptVar* arrayClass {nullptr};
|
||||
|
||||
// parsing - in order of precedence
|
||||
CScriptVarLink* functionCall (bool& execute, CScriptVarLink* function, CScriptVar* parent);
|
||||
CScriptVarLink* factor (bool& execute);
|
||||
CScriptVarLink* unary (bool& execute);
|
||||
CScriptVarLink* term (bool& execute);
|
||||
CScriptVarLink* expression (bool& execute);
|
||||
CScriptVarLink* shift (bool& execute);
|
||||
CScriptVarLink* condition (bool& execute);
|
||||
CScriptVarLink* logic (bool& execute);
|
||||
CScriptVarLink* ternary (bool& execute);
|
||||
CScriptVarLink* base (bool& execute);
|
||||
void block (bool& execute);
|
||||
void statement (bool& execute);
|
||||
// parsing utility functions
|
||||
CScriptVarLink* parseFunctionDefinition ();
|
||||
void parseFunctionArguments (CScriptVar* funcVar) const;
|
||||
|
||||
/** Finds a child, looking recursively up the scopes */
|
||||
CScriptVarLink* findInScopes (const string& childName) const;
|
||||
/** Look up in any parent classes of the given object */
|
||||
CScriptVarLink* findInParentClasses (CScriptVar* object, const string& name) const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* - Useful language functions
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "TinyJS_Functions.h"
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
using namespace std;
|
||||
using namespace std::literals;
|
||||
// ----------------------------------------------- Actual Functions
|
||||
void scTrace (CScriptVar* c, void* userdata)
|
||||
{
|
||||
CTinyJS* js = (CTinyJS*)userdata;
|
||||
js->getRoot ()->trace ();
|
||||
}
|
||||
|
||||
void scObjectDump (CScriptVar* c) { c->getParameter ("this"sv)->trace ("> "); }
|
||||
|
||||
void scObjectClone (CScriptVar* c)
|
||||
{
|
||||
CScriptVar* obj = c->getParameter ("this"sv);
|
||||
c->getReturnVar ()->copyValue (obj);
|
||||
}
|
||||
|
||||
void scMathRand (CScriptVar* c) { c->getReturnVar ()->setDouble ((double)rand () / RAND_MAX); }
|
||||
|
||||
void scMathRandInt (CScriptVar* c)
|
||||
{
|
||||
auto min = c->getParameter ("min"sv)->getInt ();
|
||||
auto max = c->getParameter ("max"sv)->getInt ();
|
||||
auto val = min + (int64_t)(rand () % (1 + max - min));
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scCharToInt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("ch"sv)->getString ();
|
||||
;
|
||||
int val = 0;
|
||||
if (str.length () > 0)
|
||||
val = (int)str.c_str ()[0];
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scStringIndexOf (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
string search = c->getParameter ("search"sv)->getString ();
|
||||
size_t p = str.find (search);
|
||||
auto val = (p == string::npos) ? -1 : p;
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scStringSubstring (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
auto lo = c->getParameter ("lo"sv)->getInt ();
|
||||
auto hi = c->getParameter ("hi"sv)->getInt ();
|
||||
|
||||
auto l = hi - lo;
|
||||
if (l > 0 && lo >= 0 && lo + l <= static_cast<int64_t> (str.length ()))
|
||||
c->getReturnVar ()->setString (str.substr (lo, l));
|
||||
else
|
||||
c->getReturnVar ()->setString ("");
|
||||
}
|
||||
|
||||
void scStringCharAt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
auto p = c->getParameter ("pos"sv)->getInt ();
|
||||
if (p >= 0 && p < static_cast<int64_t> (str.length ()))
|
||||
c->getReturnVar ()->setString (str.substr (p, 1));
|
||||
else
|
||||
c->getReturnVar ()->setString ("");
|
||||
}
|
||||
|
||||
void scStringCharCodeAt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
auto p = c->getParameter ("pos"sv)->getInt ();
|
||||
if (p >= 0 && p < static_cast<int64_t> (str.length ()))
|
||||
c->getReturnVar ()->setInt (str.at (p));
|
||||
else
|
||||
c->getReturnVar ()->setInt (0);
|
||||
}
|
||||
|
||||
void scStringSplit (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
string sep = c->getParameter ("separator"sv)->getString ();
|
||||
CScriptVar* result = c->getReturnVar ();
|
||||
result->setArray ();
|
||||
int length = 0;
|
||||
|
||||
size_t pos = str.find (sep);
|
||||
while (pos != string::npos)
|
||||
{
|
||||
result->setArrayIndex (length++, new CScriptVar (str.substr (0, pos)));
|
||||
str = str.substr (pos + 1);
|
||||
pos = str.find (sep);
|
||||
}
|
||||
|
||||
if (str.size () > 0)
|
||||
result->setArrayIndex (length++, new CScriptVar (str));
|
||||
}
|
||||
|
||||
void scStringFromCharCode (CScriptVar* c)
|
||||
{
|
||||
char str[2];
|
||||
str[0] = static_cast<char> (c->getParameter ("char"sv)->getInt ());
|
||||
str[1] = 0;
|
||||
c->getReturnVar ()->setString (str);
|
||||
}
|
||||
|
||||
void scIntegerParseInt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("str"sv)->getString ();
|
||||
auto val = stringToInteger (str);
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scIntegerValueOf (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("str"sv)->getString ();
|
||||
|
||||
int val = 0;
|
||||
if (str.length () == 1)
|
||||
val = str[0];
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scJSONStringify (CScriptVar* c)
|
||||
{
|
||||
ostringstream result;
|
||||
c->getParameter ("obj"sv)->getJSON (result);
|
||||
c->getReturnVar ()->setString (result.str ());
|
||||
}
|
||||
|
||||
void scExec (CScriptVar* c, void* data)
|
||||
{
|
||||
CTinyJS* tinyJS = (CTinyJS*)data;
|
||||
string str = c->getParameter ("jsCode"sv)->getString ();
|
||||
tinyJS->execute (str);
|
||||
}
|
||||
|
||||
void scEval (CScriptVar* c, void* data)
|
||||
{
|
||||
CTinyJS* tinyJS = (CTinyJS*)data;
|
||||
string str = c->getParameter ("jsCode"sv)->getString ();
|
||||
c->setReturnVar (tinyJS->evaluateComplex (str).getVar ());
|
||||
}
|
||||
|
||||
void scArrayContains (CScriptVar* c)
|
||||
{
|
||||
CScriptVar* obj = c->getParameter ("obj"sv);
|
||||
CScriptVarLink* v = c->getParameter ("this"sv)->getFirstChild ();
|
||||
|
||||
bool contains = false;
|
||||
while (v)
|
||||
{
|
||||
if (v->getVar ()->equals (obj))
|
||||
{
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
v = v->getNextSibling ();
|
||||
}
|
||||
|
||||
c->getReturnVar ()->setInt (contains);
|
||||
}
|
||||
|
||||
void scArrayRemove (CScriptVar* c)
|
||||
{
|
||||
CScriptVar* obj = c->getParameter ("obj"sv);
|
||||
vector<int> removedIndices;
|
||||
CScriptVarLink* v;
|
||||
// remove
|
||||
v = c->getParameter ("this"sv)->getFirstChild ();
|
||||
while (v)
|
||||
{
|
||||
if (v->getVar ()->equals (obj))
|
||||
{
|
||||
removedIndices.push_back (v->getIntName ());
|
||||
}
|
||||
v = v->getNextSibling ();
|
||||
}
|
||||
// renumber
|
||||
v = c->getParameter ("this"sv)->getFirstChild ();
|
||||
while (v)
|
||||
{
|
||||
int n = v->getIntName ();
|
||||
int newn = n;
|
||||
for (size_t i = 0; i < removedIndices.size (); i++)
|
||||
if (n >= removedIndices[i])
|
||||
newn--;
|
||||
if (newn != n)
|
||||
v->setIntName (newn);
|
||||
v = v->getNextSibling ();
|
||||
}
|
||||
}
|
||||
|
||||
void scArrayJoin (CScriptVar* c)
|
||||
{
|
||||
string sep = c->getParameter ("separator"sv)->getString ();
|
||||
CScriptVar* arr = c->getParameter ("this"sv);
|
||||
|
||||
ostringstream sstr;
|
||||
int l = arr->getArrayLength ();
|
||||
for (int i = 0; i < l; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
sstr << sep;
|
||||
sstr << arr->getArrayIndex (i)->getString ();
|
||||
}
|
||||
|
||||
c->getReturnVar ()->setString (sstr.str ());
|
||||
}
|
||||
|
||||
// ----------------------------------------------- Register Functions
|
||||
void registerFunctions (CTinyJS* tinyJS)
|
||||
{
|
||||
|
||||
tinyJS->addNative ("function exec(jsCode)"sv, [=] (auto scriptVar) {
|
||||
scExec (scriptVar, tinyJS);
|
||||
}); // execute the given code
|
||||
tinyJS->addNative ("function eval(jsCode)"sv, [=] (auto scriptVar) {
|
||||
scEval (scriptVar, tinyJS);
|
||||
}); // execute the given string (an expression) and return the result
|
||||
tinyJS->addNative ("function trace()"sv, [=] (auto scriptVar) { scTrace (scriptVar, tinyJS); });
|
||||
tinyJS->addNative ("function Object.dump()"sv, scObjectDump);
|
||||
tinyJS->addNative ("function Object.clone()"sv, scObjectClone);
|
||||
tinyJS->addNative ("function Math.rand()"sv, scMathRand);
|
||||
tinyJS->addNative ("function Math.randInt(min, max)"sv, scMathRandInt);
|
||||
tinyJS->addNative ("function charToInt(ch)"sv,
|
||||
scCharToInt); // convert a character to an int - get its value
|
||||
tinyJS->addNative ("function String.indexOf(search)"sv,
|
||||
scStringIndexOf); // find the position of a string in a string, -1 if not
|
||||
tinyJS->addNative ("function String.substring(lo,hi)"sv, scStringSubstring);
|
||||
tinyJS->addNative ("function String.charAt(pos)"sv, scStringCharAt);
|
||||
tinyJS->addNative ("function String.charCodeAt(pos)"sv, scStringCharCodeAt);
|
||||
tinyJS->addNative ("function String.fromCharCode(char)"sv, scStringFromCharCode);
|
||||
tinyJS->addNative ("function String.split(separator)"sv, scStringSplit);
|
||||
tinyJS->addNative ("function Integer.parseInt(str)"sv, scIntegerParseInt); // string to int
|
||||
tinyJS->addNative ("function Integer.valueOf(str)"sv,
|
||||
scIntegerValueOf); // value of a single character
|
||||
tinyJS->addNative ("function JSON.stringify(obj, replacer)"sv,
|
||||
scJSONStringify); // convert to JSON. replacer is ignored at the moment
|
||||
// JSON.parse is left out as you can (unsafely!) use eval instead
|
||||
tinyJS->addNative ("function Array.contains(obj)"sv, scArrayContains);
|
||||
tinyJS->addNative ("function Array.remove(obj)"sv, scArrayRemove);
|
||||
tinyJS->addNative ("function Array.join(separator)"sv, scArrayJoin);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TinyJS.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
/// Register useful functions with the TinyJS interpreter
|
||||
void registerFunctions (CTinyJS* tinyJS);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
Vendored
+271
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* - Math and Trigonometry functions
|
||||
*
|
||||
* Authored By O.Z.L.B. <ozlbinfo@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2011 O.Z.L.B.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include "TinyJS_MathFunctions.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define k_E exp (1.0)
|
||||
#define k_PI 3.1415926535897932384626433832795
|
||||
|
||||
#define F_ABS(a) ((a) >= 0 ? (a) : (-(a)))
|
||||
#define F_MIN(a, b) ((a) > (b) ? (b) : (a))
|
||||
#define F_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define F_SGN(a) ((a) > 0 ? 1 : ((a) < 0 ? -1 : 0))
|
||||
#define F_RNG(a, min, max) ((a) < (min) ? min : ((a) > (max) ? max : a))
|
||||
#define F_ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : (int)((a)-0.5))
|
||||
|
||||
// CScriptVar shortcut macro
|
||||
#define scIsInt(a) (c->getParameter (a)->isInt ())
|
||||
#define scIsDouble(a) (c->getParameter (a)->isDouble ())
|
||||
#define scGetInt(a) (c->getParameter (a)->getInt ())
|
||||
#define scGetDouble(a) (c->getParameter (a)->getDouble ())
|
||||
#define scReturnInt(a) (c->getReturnVar ()->setInt (a))
|
||||
#define scReturnDouble(a) (c->getReturnVar ()->setDouble (a))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
namespace {
|
||||
double asinh (const double& value)
|
||||
{
|
||||
double returned;
|
||||
|
||||
if (value > 0)
|
||||
returned = log (value + sqrt (value * value + 1));
|
||||
else
|
||||
returned = -log (-value + sqrt (value * value + 1));
|
||||
|
||||
return (returned);
|
||||
}
|
||||
|
||||
double acosh (const double& value)
|
||||
{
|
||||
double returned;
|
||||
|
||||
if (value > 0)
|
||||
returned = log (value + sqrt (value * value - 1));
|
||||
else
|
||||
returned = -log (-value + sqrt (value * value - 1));
|
||||
|
||||
return (returned);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Math.abs(x) - returns absolute of given value
|
||||
void scMathAbs (CScriptVar* c)
|
||||
{
|
||||
if (scIsInt ("a"))
|
||||
{
|
||||
scReturnInt (F_ABS (scGetInt ("a")));
|
||||
}
|
||||
else if (scIsDouble ("a"))
|
||||
{
|
||||
scReturnDouble (F_ABS (scGetDouble ("a")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.round(a) - returns nearest round of given value
|
||||
void scMathRound (CScriptVar* c)
|
||||
{
|
||||
if (scIsInt ("a"))
|
||||
{
|
||||
scReturnInt (F_ROUND (scGetInt ("a")));
|
||||
}
|
||||
else if (scIsDouble ("a"))
|
||||
{
|
||||
scReturnDouble (F_ROUND (scGetDouble ("a")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.min(a,b) - returns minimum of two given values
|
||||
void scMathMin (CScriptVar* c)
|
||||
{
|
||||
if ((scIsInt ("a")) && (scIsInt ("b")))
|
||||
{
|
||||
scReturnInt (F_MIN (scGetInt ("a"), scGetInt ("b")));
|
||||
}
|
||||
else
|
||||
{
|
||||
scReturnDouble (F_MIN (scGetDouble ("a"), scGetDouble ("b")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.max(a,b) - returns maximum of two given values
|
||||
void scMathMax (CScriptVar* c)
|
||||
{
|
||||
if ((scIsInt ("a")) && (scIsInt ("b")))
|
||||
{
|
||||
scReturnInt (F_MAX (scGetInt ("a"), scGetInt ("b")));
|
||||
}
|
||||
else
|
||||
{
|
||||
scReturnDouble (F_MAX (scGetDouble ("a"), scGetDouble ("b")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.range(x,a,b) - returns value limited between two given values
|
||||
void scMathRange (CScriptVar* c)
|
||||
{
|
||||
if ((scIsInt ("x")))
|
||||
{
|
||||
scReturnInt (F_RNG (scGetInt ("x"), scGetInt ("a"), scGetInt ("b")));
|
||||
}
|
||||
else
|
||||
{
|
||||
scReturnDouble (F_RNG (scGetDouble ("x"), scGetDouble ("a"), scGetDouble ("b")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.sign(a) - returns sign of given value (-1==negative,0=zero,1=positive)
|
||||
void scMathSign (CScriptVar* c)
|
||||
{
|
||||
if (scIsInt ("a"))
|
||||
{
|
||||
scReturnInt (F_SGN (scGetInt ("a")));
|
||||
}
|
||||
else if (scIsDouble ("a"))
|
||||
{
|
||||
scReturnDouble (F_SGN (scGetDouble ("a")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.PI() - returns PI value
|
||||
void scMathPI (CScriptVar* c) { scReturnDouble (k_PI); }
|
||||
|
||||
// Math.toDegrees(a) - returns degree value of a given angle in radians
|
||||
void scMathToDegrees (CScriptVar* c) { scReturnDouble ((180.0 / k_PI) * (scGetDouble ("a"))); }
|
||||
|
||||
// Math.toRadians(a) - returns radians value of a given angle in degrees
|
||||
void scMathToRadians (CScriptVar* c) { scReturnDouble ((k_PI / 180.0) * (scGetDouble ("a"))); }
|
||||
|
||||
// Math.sin(a) - returns trig. sine of given angle in radians
|
||||
void scMathSin (CScriptVar* c) { scReturnDouble (sin (scGetDouble ("a"))); }
|
||||
|
||||
// Math.asin(a) - returns trig. arcsine of given angle in radians
|
||||
void scMathASin (CScriptVar* c) { scReturnDouble (asin (scGetDouble ("a"))); }
|
||||
|
||||
// Math.cos(a) - returns trig. cosine of given angle in radians
|
||||
void scMathCos (CScriptVar* c) { scReturnDouble (cos (scGetDouble ("a"))); }
|
||||
|
||||
// Math.acos(a) - returns trig. arccosine of given angle in radians
|
||||
void scMathACos (CScriptVar* c) { scReturnDouble (acos (scGetDouble ("a"))); }
|
||||
|
||||
// Math.tan(a) - returns trig. tangent of given angle in radians
|
||||
void scMathTan (CScriptVar* c) { scReturnDouble (tan (scGetDouble ("a"))); }
|
||||
|
||||
// Math.atan(a) - returns trig. arctangent of given angle in radians
|
||||
void scMathATan (CScriptVar* c) { scReturnDouble (atan (scGetDouble ("a"))); }
|
||||
|
||||
// Math.sinh(a) - returns trig. hyperbolic sine of given angle in radians
|
||||
void scMathSinh (CScriptVar* c) { scReturnDouble (sinh (scGetDouble ("a"))); }
|
||||
|
||||
// Math.asinh(a) - returns trig. hyperbolic arcsine of given angle in radians
|
||||
void scMathASinh (CScriptVar* c) { scReturnDouble (asinh ((long double)scGetDouble ("a"))); }
|
||||
|
||||
// Math.cosh(a) - returns trig. hyperbolic cosine of given angle in radians
|
||||
void scMathCosh (CScriptVar* c) { scReturnDouble (cosh (scGetDouble ("a"))); }
|
||||
|
||||
// Math.acosh(a) - returns trig. hyperbolic arccosine of given angle in radians
|
||||
void scMathACosh (CScriptVar* c) { scReturnDouble (acosh ((long double)scGetDouble ("a"))); }
|
||||
|
||||
// Math.tanh(a) - returns trig. hyperbolic tangent of given angle in radians
|
||||
void scMathTanh (CScriptVar* c) { scReturnDouble (tanh (scGetDouble ("a"))); }
|
||||
|
||||
// Math.atan(a) - returns trig. hyperbolic arctangent of given angle in radians
|
||||
void scMathATanh (CScriptVar* c) { scReturnDouble (atan (scGetDouble ("a"))); }
|
||||
|
||||
// Math.E() - returns E Neplero value
|
||||
void scMathE (CScriptVar* c) { scReturnDouble (k_E); }
|
||||
|
||||
// Math.log(a) - returns natural logaritm (base E) of given value
|
||||
void scMathLog (CScriptVar* c) { scReturnDouble (log (scGetDouble ("a"))); }
|
||||
|
||||
// Math.log10(a) - returns logaritm(base 10) of given value
|
||||
void scMathLog10 (CScriptVar* c) { scReturnDouble (log10 (scGetDouble ("a"))); }
|
||||
|
||||
// Math.exp(a) - returns e raised to the power of a given number
|
||||
void scMathExp (CScriptVar* c) { scReturnDouble (exp (scGetDouble ("a"))); }
|
||||
|
||||
// Math.pow(a,b) - returns the result of a number raised to a power (a)^(b)
|
||||
void scMathPow (CScriptVar* c) { scReturnDouble (pow (scGetDouble ("a"), scGetDouble ("b"))); }
|
||||
|
||||
// Math.sqr(a) - returns square of given value
|
||||
void scMathSqr (CScriptVar* c) { scReturnDouble ((scGetDouble ("a") * scGetDouble ("a"))); }
|
||||
|
||||
// Math.sqrt(a) - returns square root of given value
|
||||
void scMathSqrt (CScriptVar* c) { scReturnDouble (sqrt (scGetDouble ("a"))); }
|
||||
|
||||
// ----------------------------------------------- Register Functions
|
||||
void registerMathFunctions (CTinyJS* tinyJS)
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
// --- Math and Trigonometry functions ---
|
||||
tinyJS->addNative ("function Math.abs(a)"sv, scMathAbs);
|
||||
tinyJS->addNative ("function Math.round(a)"sv, scMathRound);
|
||||
tinyJS->addNative ("function Math.min(a,b)"sv, scMathMin);
|
||||
tinyJS->addNative ("function Math.max(a,b)"sv, scMathMax);
|
||||
tinyJS->addNative ("function Math.range(x,a,b)"sv, scMathRange);
|
||||
tinyJS->addNative ("function Math.sign(a)"sv, scMathSign);
|
||||
|
||||
tinyJS->addNative ("function Math.PI()"sv, scMathPI);
|
||||
tinyJS->addNative ("function Math.toDegrees(a)"sv, scMathToDegrees);
|
||||
tinyJS->addNative ("function Math.toRadians(a)"sv, scMathToRadians);
|
||||
tinyJS->addNative ("function Math.sin(a)"sv, scMathSin);
|
||||
tinyJS->addNative ("function Math.asin(a)"sv, scMathASin);
|
||||
tinyJS->addNative ("function Math.cos(a)"sv, scMathCos);
|
||||
tinyJS->addNative ("function Math.acos(a)"sv, scMathACos);
|
||||
tinyJS->addNative ("function Math.tan(a)"sv, scMathTan);
|
||||
tinyJS->addNative ("function Math.atan(a)"sv, scMathATan);
|
||||
tinyJS->addNative ("function Math.sinh(a)"sv, scMathSinh);
|
||||
tinyJS->addNative ("function Math.asinh(a)"sv, scMathASinh);
|
||||
tinyJS->addNative ("function Math.cosh(a)"sv, scMathCosh);
|
||||
tinyJS->addNative ("function Math.acosh(a)"sv, scMathACosh);
|
||||
tinyJS->addNative ("function Math.tanh(a)"sv, scMathTanh);
|
||||
tinyJS->addNative ("function Math.atanh(a)"sv, scMathATanh);
|
||||
|
||||
tinyJS->addNative ("function Math.E()"sv, scMathE);
|
||||
tinyJS->addNative ("function Math.log(a)"sv, scMathLog);
|
||||
tinyJS->addNative ("function Math.log10(a)"sv, scMathLog10);
|
||||
tinyJS->addNative ("function Math.exp(a)"sv, scMathExp);
|
||||
tinyJS->addNative ("function Math.pow(a,b)"sv, scMathPow);
|
||||
|
||||
tinyJS->addNative ("function Math.sqr(a)"sv, scMathSqr);
|
||||
tinyJS->addNative ("function Math.sqrt(a)"sv, scMathSqrt);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "TinyJS.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
/// Register useful math. functions with the TinyJS interpreter
|
||||
void registerMathFunctions (CTinyJS* tinyJS);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a program to run all the tests in the tests folder...
|
||||
*/
|
||||
|
||||
#include "TinyJS.h"
|
||||
#include "TinyJS_Functions.h"
|
||||
#include "TinyJS_MathFunctions.h"
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
|
||||
#if __APPLE_CC__
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef MTRACE
|
||||
#include <mcheck.h>
|
||||
#endif
|
||||
|
||||
// #define INSANE_MEMORY_DEBUG
|
||||
|
||||
using namespace TJS;
|
||||
|
||||
#ifdef INSANE_MEMORY_DEBUG
|
||||
// needs -rdynamic when compiling/linking
|
||||
#include <execinfo.h>
|
||||
#include <malloc.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
void** get_stackframe ()
|
||||
{
|
||||
void** trace = (void**)malloc (sizeof (void*) * 17);
|
||||
int trace_size = 0;
|
||||
|
||||
for (int i = 0; i < 17; i++)
|
||||
trace[i] = (void*)0;
|
||||
trace_size = backtrace (trace, 16);
|
||||
return trace;
|
||||
}
|
||||
|
||||
void print_stackframe (char* header, void** trace)
|
||||
{
|
||||
char** messages = (char**)NULL;
|
||||
int trace_size = 0;
|
||||
|
||||
trace_size = 0;
|
||||
while (trace[trace_size])
|
||||
trace_size++;
|
||||
messages = backtrace_symbols (trace, trace_size);
|
||||
|
||||
printf ("%s\n", header);
|
||||
for (int i = 0; i < trace_size; ++i)
|
||||
{
|
||||
printf ("%s\n", messages[i]);
|
||||
}
|
||||
// free(messages);
|
||||
}
|
||||
|
||||
/* Prototypes for our hooks. */
|
||||
static void* my_malloc_hook (size_t, const void*);
|
||||
static void my_free_hook (void*, const void*);
|
||||
static void* (*old_malloc_hook) (size_t, const void*);
|
||||
static void (*old_free_hook) (void*, const void*);
|
||||
|
||||
map<void*, void**> malloced;
|
||||
|
||||
static void* my_malloc_hook (size_t size, const void* caller)
|
||||
{
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
/* Call recursively */
|
||||
void* result = malloc (size);
|
||||
/* we call malloc here, so protect it too. */
|
||||
// printf ("malloc (%u) returns %p\n", (unsigned int) size, result);
|
||||
malloced[result] = get_stackframe ();
|
||||
|
||||
/* Restore our own hooks */
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void my_free_hook (void* ptr, const void* caller)
|
||||
{
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
/* Call recursively */
|
||||
free (ptr);
|
||||
/* we call malloc here, so protect it too. */
|
||||
// printf ("freed pointer %p\n", ptr);
|
||||
if (malloced.find (ptr) == malloced.end ())
|
||||
{
|
||||
/*fprintf(stderr, "INVALID FREE\n");
|
||||
void *trace[16];
|
||||
int trace_size = 0;
|
||||
trace_size = backtrace(trace, 16);
|
||||
backtrace_symbols_fd(trace, trace_size, STDERR_FILENO);*/
|
||||
}
|
||||
else
|
||||
malloced.erase (ptr);
|
||||
/* Restore our own hooks */
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
}
|
||||
|
||||
void memtracing_init ()
|
||||
{
|
||||
old_malloc_hook = __malloc_hook;
|
||||
old_free_hook = __free_hook;
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
}
|
||||
|
||||
long gethash (void** trace)
|
||||
{
|
||||
unsigned long hash = 0;
|
||||
while (*trace)
|
||||
{
|
||||
hash = (hash << 1) ^ (hash >> 63) ^ (unsigned long)*trace;
|
||||
trace++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
void memtracing_kill ()
|
||||
{
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
|
||||
map<long, void**> hashToReal;
|
||||
map<long, int> counts;
|
||||
map<void*, void**>::iterator it = malloced.begin ();
|
||||
while (it != malloced.end ())
|
||||
{
|
||||
long hash = gethash (it->second);
|
||||
hashToReal[hash] = it->second;
|
||||
|
||||
if (counts.find (hash) == counts.end ())
|
||||
counts[hash] = 1;
|
||||
else
|
||||
counts[hash]++;
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
vector<pair<int, long>> sorting;
|
||||
map<long, int>::iterator countit = counts.begin ();
|
||||
while (countit != counts.end ())
|
||||
{
|
||||
sorting.push_back (pair<int, long> (countit->second, countit->first));
|
||||
countit++;
|
||||
}
|
||||
|
||||
// sort
|
||||
bool done = false;
|
||||
while (!done)
|
||||
{
|
||||
done = true;
|
||||
for (int i = 0; i < sorting.size () - 1; i++)
|
||||
{
|
||||
if (sorting[i].first < sorting[i + 1].first)
|
||||
{
|
||||
pair<int, long> t = sorting[i];
|
||||
sorting[i] = sorting[i + 1];
|
||||
sorting[i + 1] = t;
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sorting.size (); i++)
|
||||
{
|
||||
long hash = sorting[i].second;
|
||||
int count = sorting[i].first;
|
||||
char header[256];
|
||||
sprintf (header, "--------------------------- LEAKED %d", count);
|
||||
print_stackframe (header, hashToReal[hash]);
|
||||
}
|
||||
}
|
||||
#endif // INSANE_MEMORY_DEBUG
|
||||
|
||||
bool run_test (const char* filename)
|
||||
{
|
||||
printf ("TEST %s ", filename);
|
||||
struct stat results;
|
||||
if (!stat (filename, &results) == 0)
|
||||
{
|
||||
printf ("Cannot stat file! '%s'\n", filename);
|
||||
return false;
|
||||
}
|
||||
int size = results.st_size;
|
||||
FILE* file = fopen (filename, "rb");
|
||||
/* if we open as text, the number of bytes read may be > the size we read */
|
||||
if (!file)
|
||||
{
|
||||
printf ("Unable to open file! '%s'\n", filename);
|
||||
return false;
|
||||
}
|
||||
char* buffer = new char[size + 1];
|
||||
long actualRead = fread (buffer, 1, size, file);
|
||||
buffer[actualRead] = 0;
|
||||
buffer[size] = 0;
|
||||
fclose (file);
|
||||
|
||||
CTinyJS s;
|
||||
registerFunctions (&s);
|
||||
registerMathFunctions (&s);
|
||||
s.getRoot ()->addChild ("result", new CScriptVar ("0", SCRIPTVAR_INTEGER));
|
||||
try
|
||||
{
|
||||
s.execute (buffer);
|
||||
}
|
||||
catch (CScriptException& e)
|
||||
{
|
||||
printf ("ERROR: %s\n", e.text.c_str ());
|
||||
}
|
||||
bool pass = s.getRoot ()->getParameter ("result")->getBool ();
|
||||
|
||||
if (pass)
|
||||
printf ("PASS\n");
|
||||
else
|
||||
{
|
||||
char fn[PATH_MAX];
|
||||
sprintf (fn, "%s.fail.js", filename);
|
||||
FILE* f = fopen (fn, "wt");
|
||||
if (f)
|
||||
{
|
||||
std::ostringstream symbols;
|
||||
s.getRoot ()->getJSON (symbols);
|
||||
fprintf (f, "%s", symbols.str ().c_str ());
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
printf ("FAIL - symbols written to %s\n", fn);
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
#if __APPLE_CC__
|
||||
struct LeakDetector
|
||||
{
|
||||
~LeakDetector () noexcept
|
||||
{
|
||||
char* env = getenv ("MallocStackLogging");
|
||||
if (env && (!strcmp (env, "1") || !strcmp (env, "lite")))
|
||||
{
|
||||
char command[1024];
|
||||
pid_t pid = getpid ();
|
||||
snprintf (command, std::size (command), "leaks %d", pid);
|
||||
system (command);
|
||||
}
|
||||
}
|
||||
};
|
||||
static LeakDetector gLeakDetector;
|
||||
#endif
|
||||
|
||||
#ifdef MTRACE
|
||||
mtrace ();
|
||||
#endif
|
||||
#ifdef INSANE_MEMORY_DEBUG
|
||||
memtracing_init ();
|
||||
#endif
|
||||
printf ("TinyJS test runner\n");
|
||||
printf ("USAGE:\n");
|
||||
printf (" ./run_tests test.js : run just one test\n");
|
||||
printf (" ./run_tests : run all tests\n");
|
||||
if (argc == 2)
|
||||
{
|
||||
return !run_test (argv[1]);
|
||||
}
|
||||
|
||||
std::string basePath (__FILE__);
|
||||
auto pos = basePath.find_last_of ('/');
|
||||
basePath.erase (pos);
|
||||
|
||||
int test_num = 1;
|
||||
int count = 0;
|
||||
int passed = 0;
|
||||
|
||||
while (test_num < 1000)
|
||||
{
|
||||
auto path = basePath; // copy
|
||||
char fn[PATH_MAX];
|
||||
snprintf (fn, std::size (fn), "/tests/test%03d.js", test_num);
|
||||
// check if the file exists - if not, assume we're at the end of our tests
|
||||
path.append (fn);
|
||||
FILE* f = fopen (path.data (), "r");
|
||||
if (!f)
|
||||
break;
|
||||
fclose (f);
|
||||
|
||||
if (run_test (path.data ()))
|
||||
passed++;
|
||||
count++;
|
||||
test_num++;
|
||||
}
|
||||
|
||||
printf ("Done. %d tests, %d pass, %d fail\n", count, passed, count - passed);
|
||||
#ifdef INSANE_MEMORY_DEBUG
|
||||
memtracing_kill ();
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef _WIN32
|
||||
_CrtDumpMemoryLeaks ();
|
||||
#endif
|
||||
#endif
|
||||
#ifdef MTRACE
|
||||
muntrace ();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
// switch-case-tests
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 1: case with break;
|
||||
////////////////////////////////////////////////////
|
||||
var a1 = 5;
|
||||
var b1 = 6;
|
||||
var r1 = 0;
|
||||
|
||||
switch (a1 + 5)
|
||||
{
|
||||
case 6:
|
||||
r1 = 2;
|
||||
break;
|
||||
case b1 + 4:
|
||||
r1 = 42;
|
||||
break;
|
||||
case 7:
|
||||
r1 = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 2: case with out break;
|
||||
////////////////////////////////////////////////////
|
||||
var a2 = 5;
|
||||
var b2 = 6;
|
||||
var r2 = 0;
|
||||
|
||||
switch (a2 + 4)
|
||||
{
|
||||
case 6:
|
||||
r2 = 2;
|
||||
break;
|
||||
case b2 + 3:
|
||||
r2 = 40;
|
||||
// break;
|
||||
case 7:
|
||||
r2 += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 3: case with default;
|
||||
////////////////////////////////////////////////////
|
||||
var a3 = 5;
|
||||
var b3 = 6;
|
||||
var r3 = 0;
|
||||
|
||||
switch (a3 + 44)
|
||||
{
|
||||
case 6:
|
||||
r3 = 2;
|
||||
break;
|
||||
case b3 + 3:
|
||||
r3 = 1;
|
||||
break;
|
||||
default:
|
||||
r3 = 42;
|
||||
break;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 4: case default before case;
|
||||
////////////////////////////////////////////////////
|
||||
var a4 = 5;
|
||||
var b4 = 6;
|
||||
var r4 = 0;
|
||||
|
||||
switch (a4 + 44)
|
||||
{
|
||||
default:
|
||||
r4 = 42;
|
||||
break;
|
||||
case 6:
|
||||
r4 = 2;
|
||||
break;
|
||||
case b4 + 3:
|
||||
r4 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
result = r1 == 42 && r2 == 42 && r3 == 42 && r4 == 42;
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
// function-closure
|
||||
|
||||
var a = 40; // a global var
|
||||
|
||||
function closure ()
|
||||
{
|
||||
var a = 39; // a local var;
|
||||
return function () { return a; };
|
||||
}
|
||||
|
||||
var b = closure (); // the local var a is now hidden
|
||||
|
||||
result = b () + 3 == 42 && a + 2 == 42;
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
// with-test
|
||||
|
||||
var a;
|
||||
|
||||
with (Math) a = PI;
|
||||
|
||||
var b = {get_member: function () { return this.member; }, member: 41};
|
||||
|
||||
with (b)
|
||||
{
|
||||
let a = get_member (); //<--- a is local for this block
|
||||
var c = a + 1;
|
||||
}
|
||||
|
||||
result = a == Math.PI && c == 42;
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
// generator-test
|
||||
|
||||
function fibonacci ()
|
||||
{
|
||||
var fn1 = 1;
|
||||
var fn2 = 1;
|
||||
while (1)
|
||||
{
|
||||
var current = fn2;
|
||||
fn2 = fn1;
|
||||
fn1 = fn1 + current;
|
||||
var reset = yield current;
|
||||
if (reset)
|
||||
{
|
||||
fn1 = 1;
|
||||
fn2 = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var generator = fibonacci ();
|
||||
|
||||
generator.next(); // 1
|
||||
generator.next(); // 1
|
||||
generator.next(); // 2
|
||||
generator.next(); // 3
|
||||
generator.next(); // 5
|
||||
|
||||
result = generator.next() == 8 && generator.send(true) == 1;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// simply testing we can return the correct value
|
||||
result = 1;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// comparison
|
||||
var a = 42;
|
||||
result = a == 42;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// simple for loop
|
||||
var a = 0;
|
||||
var i;
|
||||
for (i = 1; i < 10; i++)
|
||||
a = a + i;
|
||||
result = a == 45;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// simple if
|
||||
var a = 42;
|
||||
if (a < 43)
|
||||
result = 1;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// simple for loop containing initialisation, using +=
|
||||
var a = 0;
|
||||
for (var i = 1; i < 10; i++)
|
||||
a += i;
|
||||
result = a == 45;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// simple function
|
||||
function add (x, y) { return x + y; }
|
||||
result = add (3, 6) == 9;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// simple function scoping test
|
||||
var a = 7;
|
||||
function add (x, y)
|
||||
{
|
||||
var a = x + y;
|
||||
return a;
|
||||
}
|
||||
result = add (3, 6) == 9 && a == 7;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// functions in variables
|
||||
var bob = {};
|
||||
bob.add = function (x, y) { return x + y; };
|
||||
|
||||
result = bob.add(3, 6) == 9;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// functions in variables using JSON-style initialisation
|
||||
var bob = {add: function (x, y) { return x + y; }};
|
||||
|
||||
result = bob.add(3, 6) == 9;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// double function calls
|
||||
function a (x) { return x + 2; }
|
||||
function b (x) { return a (x) + 1; }
|
||||
result = a (3) == 5 && b (3) == 6;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// recursion
|
||||
function a (x)
|
||||
{
|
||||
if (x > 1)
|
||||
return x * a (x - 1);
|
||||
return 1;
|
||||
}
|
||||
result = a (5) == 1 * 2 * 3 * 4 * 5;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// if .. else
|
||||
var a = 42;
|
||||
if (a != 42)
|
||||
result = 0;
|
||||
else
|
||||
result = 1;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// if .. else with blocks
|
||||
var a = 42;
|
||||
if (a != 42)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 1;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Variable creation and scope from http://en.wikipedia.org/wiki/JavaScript_syntax
|
||||
x = 0; // A global variable
|
||||
var y = 'Hello!'; // Another global variable
|
||||
z = 0; // yet another global variable
|
||||
|
||||
function f ()
|
||||
{
|
||||
var z = 'foxes'; // A local variable
|
||||
twenty = 20; // Global because keyword var is not used
|
||||
return x; // We can use x here because it is global
|
||||
}
|
||||
// The value of z is no longer available
|
||||
|
||||
// testing
|
||||
blah = f ();
|
||||
result = blah == 0 && z != 'foxes' && twenty == 20;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Number definition from http://en.wikipedia.org/wiki/JavaScript_syntax
|
||||
a = 345; // an "integer", although there is only one numeric type in JavaScript
|
||||
b = 34.5; // a floating-point number
|
||||
c = 3.45e2; // another floating-point, equivalent to 345
|
||||
d = 0377; // an octal integer equal to 255
|
||||
e = 0xFF; // a hexadecimal integer equal to 255, digits represented by the letters A-F may be upper
|
||||
// or lowercase
|
||||
|
||||
result = a == 345 && b * 10 == 345 && c == 345 && d == 255 && e == 255;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Undefined/null from http://en.wikipedia.org/wiki/JavaScript_syntax
|
||||
var testUndefined; // variable declared but not defined, set to value of undefined
|
||||
var testObj = {};
|
||||
|
||||
result = 1;
|
||||
if (("" + testUndefined) != "undefined")
|
||||
result = 0; // test variable exists but value not defined, displays undefined
|
||||
if (("" + testObj.myProp) != "undefined")
|
||||
result = 0; // testObj exists, property does not, displays undefined
|
||||
if (!(undefined == null))
|
||||
result = 0; // unenforced type during check, displays true
|
||||
if (undefined === null)
|
||||
result = 0; // enforce type during check, displays false
|
||||
|
||||
if (null != undefined)
|
||||
result = 0; // unenforced type during check, displays true
|
||||
if (null === undefined)
|
||||
result = 0; // enforce type during check, displays false
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// references for arrays
|
||||
|
||||
var a = [];
|
||||
a[0] = 10;
|
||||
a[1] = 22;
|
||||
|
||||
b = a;
|
||||
|
||||
b[0] = 5;
|
||||
|
||||
result = a[0] == 5 && a[1] == 22 && b[1] == 22;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// references with functions
|
||||
|
||||
var a = 42;
|
||||
var b = [];
|
||||
b[0] = 43;
|
||||
|
||||
function foo (myarray) { myarray[0]++; }
|
||||
|
||||
function bar (myvalue) { myvalue++; }
|
||||
|
||||
foo (b);
|
||||
bar (a);
|
||||
|
||||
result = a == 42 && b[0] == 44;
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// built-in functions
|
||||
|
||||
foo = "foo bar stuff";
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript this function is called Math.random()
|
||||
// r = Math.rand();
|
||||
r = Math.random();
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript parseInt is a methode in the global scope (root-scope)
|
||||
// parsed = Integer.parseInt("42");
|
||||
parsed = parseInt ("42");
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
aStr = "ABCD";
|
||||
aChar = aStr.charAt(0);
|
||||
|
||||
obj1 = new Object ();
|
||||
obj1.food = "cake";
|
||||
obj1.desert = "pie";
|
||||
|
||||
obj2 = obj1.clone();
|
||||
obj2.food = "kittens";
|
||||
|
||||
result = foo.length == 13 && foo.indexOf("bar") == 4 && foo.substring(8, 13) == "stuff" &&
|
||||
parsed == 42 &&
|
||||
// 42-tiny-js change begin --->
|
||||
// in 42tiny-js the Integer-Objecte will be removed
|
||||
// Integer.valueOf can be replaced by String.charCodeAt
|
||||
// Integer.valueOf(aChar)==65 && obj1.food=="cake" && obj2.desert=="pie";
|
||||
aChar.charCodeAt() == 65 && obj1.food == "cake" && obj2.desert == "pie";
|
||||
//<--- 42-tiny-js change end
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// built-in functions
|
||||
|
||||
foo = "foo bar stuff";
|
||||
r = Math.rand();
|
||||
|
||||
parsed = Integer.parseInt("42");
|
||||
parsedHex = Integer.parseInt("0xFF");
|
||||
parsedOct = Integer.parseInt("011");
|
||||
parsedBig = Integer.parseInt("4294967296");
|
||||
|
||||
aStr = "ABCD";
|
||||
aChar = aStr.charAt(0);
|
||||
|
||||
obj1 = new Object ();
|
||||
obj1.food = "cake";
|
||||
obj1.desert = "pie";
|
||||
|
||||
obj2 = obj1.clone();
|
||||
obj2.food = "kittens";
|
||||
|
||||
result = foo.length == 13 && foo.indexOf("bar") == 4 && foo.substring(8, 13) == "stuff" &&
|
||||
parsed == 42 && Integer.valueOf(aChar) == 65 && obj1.food == "cake" &&
|
||||
obj2.desert == "pie" && parsedHex == 255 && parsedOct == 9 && parsedBig == 4294967296;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Test reported by sterowang, Variable attribute defines conflict with function.
|
||||
/*
|
||||
What steps will reproduce the problem?
|
||||
1. function a (){};
|
||||
2. b = {};
|
||||
3. b.a = {};
|
||||
4. a();
|
||||
|
||||
What is the expected output? What do you see instead?
|
||||
Function "a" should be called. But the error message "Error Expecting 'a'
|
||||
to be a function at (line: 1, col: 1)" received.
|
||||
|
||||
What version of the product are you using? On what operating system?
|
||||
Version 1.6 is used on Cent OS 5.4
|
||||
|
||||
|
||||
Please provide any additional information below.
|
||||
When using dump() to show symbols, found the function "a" is reassigned to
|
||||
"{}" by "b.a = {};" call.
|
||||
*/
|
||||
|
||||
function a () {};
|
||||
b = {};
|
||||
b.a = {};
|
||||
a ();
|
||||
|
||||
result = 1;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/* Javascript eval */
|
||||
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript eval is not JSON.parse
|
||||
// use parentheses or JSON.parse instead
|
||||
// myfoo = eval("{ foo: 42 }");
|
||||
myfoo = eval ("(" +
|
||||
"{ foo: 42 }" +
|
||||
")");
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
result = eval ("4*10+2") == 42 && myfoo.foo == 42;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
/* Javascript eval */
|
||||
|
||||
myfoo = eval ("{ foo: 42 }");
|
||||
|
||||
result = eval ("4*10+2") == 42 && myfoo.foo == 42;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/* Javascript eval */
|
||||
|
||||
mystructure = {
|
||||
a: 39,
|
||||
b: 3,
|
||||
addStuff: function (c, d) { return c + d; }
|
||||
};
|
||||
|
||||
mystring = JSON.stringify(mystructure, undefined);
|
||||
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript eval is not JSON.parse
|
||||
// use parentheses or JSON.parse instead
|
||||
// mynewstructure = eval(mystring);
|
||||
mynewstructure = eval ("(" + mystring + ")");
|
||||
mynewstructure2 = JSON.parse(mystring);
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
result = mynewstructure.addStuff(mynewstructure.a, mynewstructure.b) == 42 &&
|
||||
mynewstructure2.addStuff(mynewstructure2.a, mynewstructure2.b) == 42;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/* Javascript eval */
|
||||
|
||||
mystructure = {
|
||||
a: 39,
|
||||
b: 3,
|
||||
addStuff: function (c, d) { return c + d; }
|
||||
};
|
||||
|
||||
mystring = JSON.stringify(mystructure, undefined);
|
||||
|
||||
mynewstructure = eval (mystring);
|
||||
|
||||
result = mynewstructure.addStuff(mynewstructure.a, mynewstructure.b);
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// mikael.kindborg@mobilesorcery.com - Function symbol is evaluated in bracket-less body of false
|
||||
// if-statement
|
||||
var foo; // a var is only created automated by assignment
|
||||
|
||||
if (foo !== undefined)
|
||||
foo ();
|
||||
|
||||
result = 1;
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/* Mandelbrot! */
|
||||
|
||||
X1 = -2.0;
|
||||
Y1 = -2.0;
|
||||
X2 = 2.0;
|
||||
Y2 = 2.0;
|
||||
PX = 32;
|
||||
PY = 32;
|
||||
|
||||
lines = [];
|
||||
for (y = 0; y < PY; y++)
|
||||
{
|
||||
line = "";
|
||||
for (x = 0; x < PX; x++)
|
||||
{
|
||||
Xr = 0;
|
||||
Xi = 0;
|
||||
Cr = X1 + ((X2 - X1) * x / PX);
|
||||
Ci = Y1 + ((Y2 - Y1) * y / PY);
|
||||
iterations = 0;
|
||||
while ((iterations < 32) && ((Xr * Xr + Xi * Xi) < 4))
|
||||
{
|
||||
t = Xr * Xr - Xi * Xi + Cr;
|
||||
Xi = 2 * Xr * Xi + Ci;
|
||||
Xr = t;
|
||||
iterations++;
|
||||
}
|
||||
if (iterations & 1)
|
||||
line += "*";
|
||||
else
|
||||
line += " ";
|
||||
}
|
||||
lines[y] = line;
|
||||
}
|
||||
|
||||
result = lines[0] == "********************************" &&
|
||||
lines[1] == "*********** **********" &&
|
||||
lines[2] == "********* ********" &&
|
||||
lines[3] == "******* ******" &&
|
||||
lines[4] == "****** *****" &&
|
||||
lines[5] == "***** ****" &&
|
||||
lines[6] == "**** ******* ***" &&
|
||||
lines[7] == "*** ******* ** ** **" &&
|
||||
lines[8] == "*** ****** * * * **" &&
|
||||
lines[9] == "** ******* ** ** ** *" &&
|
||||
lines[10] == "** ****** * * ** ** *" &&
|
||||
lines[11] == "* ***** *** ** ** " &&
|
||||
lines[12] == "****** *** ***** " &&
|
||||
lines[13] == "*** * * * ** ** " &&
|
||||
lines[14] == "* * * * * ** " &&
|
||||
lines[15] == "* *** ** ** " &&
|
||||
lines[16] == "* ** ** " &&
|
||||
lines[17] == "* *** ** ** " &&
|
||||
lines[18] == "* * * * * ** " &&
|
||||
lines[19] == "*** * * * ** ** " &&
|
||||
lines[20] == "****** *** ***** " &&
|
||||
lines[21] == "* ***** *** ** ** " &&
|
||||
lines[22] == "** ****** * * ** ** *" &&
|
||||
lines[23] == "** ******* ** ** ** *" &&
|
||||
lines[24] == "*** ****** * * * **" &&
|
||||
lines[25] == "*** ******* ** ** **" &&
|
||||
lines[26] == "**** ******* ***" &&
|
||||
lines[27] == "***** ****" &&
|
||||
lines[28] == "****** *****" &&
|
||||
lines[29] == "******* ******" &&
|
||||
lines[30] == "********* ********" &&
|
||||
lines[31] == "*********** **********";
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// Array length test
|
||||
|
||||
myArray = [1, 2, 3, 4, 5];
|
||||
myArray2 = [1, 2, 3, 4, 5];
|
||||
myArray2[8] = 42;
|
||||
|
||||
result = myArray.length == 5 && myArray2.length == 9;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// check for undefined-ness
|
||||
a = undefined;
|
||||
b = "foo";
|
||||
result = a == undefined && b != undefined;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// test for postincrement working as expected
|
||||
var foo = 5;
|
||||
result = (foo++) == 5;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// test for array contains
|
||||
var a = [1, 2, 4, 5, 7];
|
||||
var b = ["bread", "cheese", "sandwich"];
|
||||
|
||||
result = a.contains(1) && !a.contains(42) && b.contains("cheese") && !b.contains("eggs");
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// test for array remove
|
||||
var a = [1, 2, 4, 5, 7];
|
||||
|
||||
a.remove(2);
|
||||
a.remove(5);
|
||||
|
||||
result = a.length == 3 && a[0] == 1 && a[1] == 4 && a[2] == 7;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// test for array join
|
||||
var a = [1, 2, 4, 5, 7];
|
||||
|
||||
result = a.join(",") == "1,2,4,5,7";
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// test for string split
|
||||
var b = "1,4,7";
|
||||
var a = b.split(",");
|
||||
|
||||
result = a.length == 3 && a[0] == 1 && a[1] == 4 && a[2] == 7;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
function Foo () { this.__proto__ = Foo.prototype; }
|
||||
Foo.prototype = {
|
||||
value: function () { return this.x + this.y; }
|
||||
};
|
||||
|
||||
var a = {__proto__: Foo.prototype, x: 1, y: 2};
|
||||
var b = new Foo ();
|
||||
b.x = 2;
|
||||
b.y = 3;
|
||||
|
||||
var result1 = a.value();
|
||||
var result2 = b.value();
|
||||
result = result1 == 3 && result2 == 5;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
var Foo = {value: function () { return this.x + this.y; }};
|
||||
|
||||
var a = {prototype: Foo, x: 1, y: 2};
|
||||
var b = new Foo ();
|
||||
b.x = 2;
|
||||
b.y = 3;
|
||||
|
||||
var result1 = a.value();
|
||||
var result2 = b.value();
|
||||
result = result1 == 3 && result2 == 5;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// test for shift
|
||||
var a = (2 << 2);
|
||||
var b = (16 >> 3);
|
||||
var c = (-1 >>> 16);
|
||||
result = a == 8 && b == 2 && c == 0xFFFF;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// test for ternary
|
||||
|
||||
result = (true ? 3 : 4) == 3 && (false ? 5 : 6) == 6;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
function Person (name)
|
||||
{
|
||||
this.name = name;
|
||||
this.kill = function () { this.name += " is dead"; };
|
||||
}
|
||||
|
||||
var a = new Person ("Kenny");
|
||||
a.kill();
|
||||
result = a.name == "Kenny is dead";
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// the 'lf' in the printf caused issues writing doubles on some compilers
|
||||
var a = 5.0 / 10.0 * 100.0;
|
||||
var b = 5.0 * 110.0;
|
||||
var c = 50.0 / 10.0;
|
||||
a.dump();
|
||||
b.dump();
|
||||
c.dump();
|
||||
result = a == 50 && b == 550 && c == 5;
|
||||
+803
@@ -0,0 +1,803 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#include "uiscripting.h"
|
||||
#include "detail/converters.h"
|
||||
#include "detail/scriptobject.h"
|
||||
#include "detail/uidescscriptobject.h"
|
||||
#include "detail/drawcontextobject.h"
|
||||
#include "detail/drawable.h"
|
||||
#include "detail/iscriptcontextinternal.h"
|
||||
#include "detail/scriptingviewfactory.h"
|
||||
#include "../uidescription/uiattributes.h"
|
||||
#include "../uidescription/uiviewfactory.h"
|
||||
#include "../uidescription/uidescriptionaddonregistry.h"
|
||||
#include "../lib/iviewlistener.h"
|
||||
#include "../lib/cresourcedescription.h"
|
||||
#include "../lib/cvstguitimer.h"
|
||||
#include "../lib/platform/platformfactory.h"
|
||||
#include "../lib/platform/iplatformresourceinputstream.h"
|
||||
|
||||
#include "tiny-js/TinyJS.h"
|
||||
#include "tiny-js/TinyJS_Functions.h"
|
||||
#include "tiny-js/TinyJS_MathFunctions.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
using namespace std::literals;
|
||||
using namespace TJS;
|
||||
|
||||
namespace ScriptingInternal {
|
||||
|
||||
struct ViewScriptObject;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class ScriptContext : public IScriptContextInternal
|
||||
{
|
||||
public:
|
||||
using OnScriptExceptionFunc = UIScripting::OnScriptExceptionFunc;
|
||||
using ReadScriptContentsFunc = UIScripting::ReadScriptContentsFunc;
|
||||
|
||||
ScriptContext (IUIDescription* uiDesc, OnScriptExceptionFunc&& onExceptionFunc,
|
||||
ReadScriptContentsFunc&& readContentsFunc);
|
||||
~ScriptContext () noexcept;
|
||||
|
||||
void init (const std::string& initScript);
|
||||
void onViewCreated (CView* view, const std::string& script) override;
|
||||
|
||||
void reset ();
|
||||
|
||||
private:
|
||||
std::string eval (std::string_view script) const override;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct TimerScriptObject : ScriptObject
|
||||
{
|
||||
template<typename Proc>
|
||||
TimerScriptObject (uint64_t fireTime, CScriptVar* _callback, Proc timerProc)
|
||||
{
|
||||
auto cb = owning (_callback);
|
||||
auto t = makeOwned<CVSTGUITimer> (
|
||||
[timerProc, cb] (auto timer) {
|
||||
if (!timerProc (cb))
|
||||
timer->stop ();
|
||||
},
|
||||
static_cast<uint32_t> (fireTime), false);
|
||||
scriptVar->setCustomData (t);
|
||||
addFunc ("invalid"sv, [t] (auto v) mutable {
|
||||
t = nullptr;
|
||||
v->setCustomData (nullptr);
|
||||
});
|
||||
addFunc ("start"sv, [t] (auto) { t->start (); });
|
||||
addFunc ("stop"sv, [t] (auto) { t->stop (); });
|
||||
setOnDestroy ([cb] (auto) { cb->release (); });
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
struct ScriptContext::Impl : ViewListenerAdapter,
|
||||
ViewEventListenerAdapter,
|
||||
ViewContainerListenerAdapter,
|
||||
IControlListener,
|
||||
ScriptingInternal::IViewScriptObjectContext
|
||||
{
|
||||
using ViewScriptObject = ScriptingInternal::ViewScriptObject;
|
||||
using ScriptAddChildScoped = ScriptingInternal::ScriptAddChildScoped;
|
||||
using ScriptObject = ScriptingInternal::ScriptObject;
|
||||
using TimerScriptObject = ScriptingInternal::TimerScriptObject;
|
||||
using UIDescScriptObject = ScriptingInternal::UIDescScriptObject;
|
||||
|
||||
IUIDescription* uiDesc {nullptr};
|
||||
|
||||
std::unique_ptr<CTinyJS> jsContext;
|
||||
OnScriptExceptionFunc onScriptException;
|
||||
ReadScriptContentsFunc readScriptContents;
|
||||
using ViewScriptMap = ScriptingInternal::ViewScriptMap;
|
||||
ViewScriptMap viewScriptMap;
|
||||
|
||||
UIDescScriptObject uiDescObject;
|
||||
|
||||
Impl (IUIDescription* uiDesc, OnScriptExceptionFunc&& onExceptionFunc,
|
||||
ReadScriptContentsFunc&& readContentsFunc)
|
||||
: uiDesc (uiDesc)
|
||||
, onScriptException (std::move (onExceptionFunc))
|
||||
, readScriptContents (std::move (readContentsFunc))
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
~Impl () noexcept { reset (true); }
|
||||
|
||||
void init ()
|
||||
{
|
||||
jsContext = std::make_unique<CTinyJS> ();
|
||||
uiDescObject = UIDescScriptObject (uiDesc, jsContext.get ());
|
||||
|
||||
registerFunctions (jsContext.get ());
|
||||
registerMathFunctions (jsContext.get ());
|
||||
|
||||
jsContext->getRoot ()->addChild ("uiDesc"sv, uiDescObject);
|
||||
|
||||
jsContext->addNative (
|
||||
"function createTimer(context, fireTime, callback)"sv, [this] (CScriptVar* var) {
|
||||
auto context = var->getParameter ("context"sv);
|
||||
auto fireTime = var->getParameter ("fireTime"sv);
|
||||
auto callback = var->getParameter ("callback"sv);
|
||||
if (!fireTime->isInt ())
|
||||
{
|
||||
throw CScriptException ("Expect integer as first parameter on timer creation");
|
||||
}
|
||||
if (!callback->isFunction ())
|
||||
{
|
||||
throw CScriptException (
|
||||
"Expect function as second parameter on timer creation");
|
||||
}
|
||||
auto timerObj = TimerScriptObject (
|
||||
fireTime->getInt (), callback->deepCopy (), [this, context] (auto callback) {
|
||||
using namespace ScriptingInternal;
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "timerContext"sv,
|
||||
context);
|
||||
return evalScript (callback, "timerCallback (timerContext);"sv,
|
||||
"timerCallback");
|
||||
});
|
||||
var->setReturnVar (timerObj);
|
||||
});
|
||||
jsContext->addNative (
|
||||
"function iterateSubViews(view, context, callback)"sv, [this] (CScriptVar* var) {
|
||||
auto view = var->getParameter ("view"sv);
|
||||
auto context = var->getParameter ("context"sv);
|
||||
auto callback = var->getParameter ("callback"sv);
|
||||
if (!view->isObject ())
|
||||
{
|
||||
throw CScriptException ("Expect object as first parameter on iterateSubViews");
|
||||
}
|
||||
if (!callback->isFunction ())
|
||||
{
|
||||
throw CScriptException (
|
||||
"Expect function as second parameter on iterateSubViews");
|
||||
}
|
||||
auto it =
|
||||
std::find_if (viewScriptMap.begin (), viewScriptMap.end (),
|
||||
[&] (const auto& el) { return el.second->getVar () == view; });
|
||||
if (it == viewScriptMap.end ())
|
||||
throw CScriptException ("View not found in iterateSubViews");
|
||||
auto container = it->first->asViewContainer ();
|
||||
if (!container)
|
||||
return; // no sub views
|
||||
container->forEachChild ([&] (auto child) {
|
||||
using namespace ScriptingInternal;
|
||||
auto childScriptObject = viewScriptMap.find (child);
|
||||
if (childScriptObject != viewScriptMap.end ())
|
||||
{
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv,
|
||||
childScriptObject->second->getVar ());
|
||||
ScriptAddChildScoped scs2 (*jsContext->getRoot (), "context"sv, context);
|
||||
evalScript (callback, "callback (child, context);"sv, "callback");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto scriptObj =
|
||||
addView (child, std::make_unique<ViewScriptObject> (child, this));
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv,
|
||||
scriptObj->getVar ());
|
||||
ScriptAddChildScoped scs2 (*jsContext->getRoot (), "context"sv, context);
|
||||
evalScript (callback, "callback (child, context);"sv, "callback");
|
||||
}
|
||||
});
|
||||
});
|
||||
jsContext->addNative ("function log(obj)",
|
||||
[this] (CScriptVar* var) { log (var->getParameter ("obj"sv)); });
|
||||
|
||||
jsContext->addNative ("function makeTransformMatrix()", [] (CScriptVar* var) {
|
||||
auto tm = makeTransformMatrixObject ();
|
||||
var->setReturnVar (tm);
|
||||
tm->release ();
|
||||
});
|
||||
}
|
||||
|
||||
void reset (bool terminate = false)
|
||||
{
|
||||
for (auto it = viewScriptMap.begin (); it != viewScriptMap.end ();)
|
||||
{
|
||||
viewRemoved (it->first);
|
||||
uninstallListeners (it->first);
|
||||
it = eraseViewFromMap (it);
|
||||
}
|
||||
if (!terminate)
|
||||
init ();
|
||||
}
|
||||
|
||||
void initWithScript (const std::string* script)
|
||||
{
|
||||
if (script)
|
||||
{
|
||||
auto scriptContent = getScriptFileContent (*script);
|
||||
if (!scriptContent.empty ())
|
||||
script = &scriptContent;
|
||||
evalScript (*script);
|
||||
}
|
||||
}
|
||||
CScriptVar* getRoot () const override { return jsContext->getRoot (); }
|
||||
IUIDescription* getUIDescription () const override { return uiDesc; }
|
||||
|
||||
void installListeners (CView* view)
|
||||
{
|
||||
view->registerViewListener (this);
|
||||
view->registerViewEventListener (this);
|
||||
if (auto viewContainer = view->asViewContainer ())
|
||||
viewContainer->registerViewContainerListener (this);
|
||||
else if (auto control = dynamic_cast<CControl*> (view))
|
||||
control->registerControlListener (this);
|
||||
}
|
||||
|
||||
void uninstallListeners (CView* view)
|
||||
{
|
||||
view->unregisterViewListener (this);
|
||||
view->unregisterViewEventListener (this);
|
||||
if (auto viewContainer = view->asViewContainer ())
|
||||
viewContainer->unregisterViewContainerListener (this);
|
||||
if (auto control = dynamic_cast<CControl*> (view))
|
||||
control->unregisterControlListener (this);
|
||||
}
|
||||
|
||||
ScriptObject evalScript (std::string_view script) noexcept override
|
||||
{
|
||||
try
|
||||
{
|
||||
auto result = jsContext->evaluateComplex (script);
|
||||
#if 0 // DEBUG
|
||||
if (result.getVar () && !result.getVar ()->isUndefined ())
|
||||
{
|
||||
DebugPrint ("%s\n", result.getVar ()->getString ().data ());
|
||||
}
|
||||
#endif
|
||||
return result.getVar ();
|
||||
}
|
||||
catch (const CScriptException& exc)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugPrint ("Scripting Exception: %s\n", exc.text.data ());
|
||||
#endif
|
||||
if (onScriptException)
|
||||
onScriptException (exc.text);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
ScriptObject evalScript (CScriptVar* object, std::string_view script,
|
||||
const std::string& objectName = "view") noexcept
|
||||
{
|
||||
if (!object || script.empty ())
|
||||
return {};
|
||||
vstgui_assert (object->getRefs () > 0);
|
||||
object->addRef ();
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), objectName, object);
|
||||
auto result = evalScript (script);
|
||||
object->release ();
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename Proc>
|
||||
void callWhenScriptHasFunction (CView* view, std::string_view funcName, Proc proc)
|
||||
{
|
||||
auto it = viewScriptMap.find (view);
|
||||
if (it == viewScriptMap.end ())
|
||||
return;
|
||||
if (it->second->getVar ()->findChild (funcName))
|
||||
proc (this, it->second);
|
||||
}
|
||||
|
||||
void viewAttached (CView* view) override
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onAttached"sv, [] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onAttached(view);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
|
||||
void viewRemoved (CView* view) override
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onRemoved"sv, [] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onRemoved(view);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
|
||||
void viewSizeChanged (CView* view, const CRect& oldSize) override
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onSizeChanged"sv, [&] (auto This, auto& obj) {
|
||||
auto newSize = view->getViewSize ();
|
||||
ScriptObject newSizeObject = ScriptingInternal::makeScriptRect (newSize);
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "newSize"sv, newSizeObject);
|
||||
static constexpr auto script = R"(view.onSizeChanged(view, newSize);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
|
||||
void viewLostFocus (CView* view) override
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onLostFocus"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onLostFocus(view);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
|
||||
void viewTookFocus (CView* view) override
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onTookFocus"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onTookFocus(view);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
|
||||
void viewOnMouseEnabled (CView* view, bool state) override
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onMouseEnabled"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto scriptEnabled = R"(view.onMouseEnabled(view, true);)"sv;
|
||||
static constexpr auto scriptDisabled = R"(view.onMouseEnabled(view, false);)"sv;
|
||||
This->evalScript (obj->getVar (), state ? scriptEnabled : scriptDisabled);
|
||||
});
|
||||
}
|
||||
|
||||
void callViewAddedOrRemoved (CViewContainer* container, CView* view, std::string_view function,
|
||||
std::string_view script)
|
||||
{
|
||||
callWhenScriptHasFunction (container, function, [&] (auto This, auto& obj) {
|
||||
auto childScriptObject = viewScriptMap.find (view);
|
||||
if (childScriptObject != viewScriptMap.end ())
|
||||
{
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv,
|
||||
childScriptObject->second->getVar ());
|
||||
This->evalScript (obj->getVar (), script);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto scriptObj = addView (view, std::make_unique<ViewScriptObject> (view, this));
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv, scriptObj->getVar ());
|
||||
This->evalScript (obj->getVar (), script);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void viewContainerViewAdded (CViewContainer* container, CView* view) override
|
||||
{
|
||||
callViewAddedOrRemoved (container, view, "onViewAdded"sv,
|
||||
R"(view.onViewAdded(view, child);)"sv);
|
||||
}
|
||||
|
||||
void viewContainerViewRemoved (CViewContainer* container, CView* view) override
|
||||
{
|
||||
callViewAddedOrRemoved (container, view, "onViewRemoved"sv,
|
||||
R"(view.onViewRemoved(view, child);)"sv);
|
||||
}
|
||||
|
||||
void checkEventConsumed (ScriptObject& obj, Event& event)
|
||||
{
|
||||
if (auto consume = obj->findChild ("consumed"))
|
||||
{
|
||||
if (consume->getVar ()->isInt () && consume->getVar ()->getInt ())
|
||||
event.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void callEventFunction (CScriptVar* var, Event& event, std::string_view script) noexcept
|
||||
{
|
||||
auto scriptEvent = ScriptingInternal::makeScriptEvent (event);
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "event"sv, scriptEvent);
|
||||
evalScript (var, script);
|
||||
checkEventConsumed (scriptEvent, event);
|
||||
}
|
||||
|
||||
void viewOnEvent (CView* view, Event& event) override
|
||||
{
|
||||
auto applyEventMouseLocalPosition = [&] (auto proc) {
|
||||
auto& mouseEvent = castMousePositionEvent (event);
|
||||
auto oldPos = mouseEvent.mousePosition;
|
||||
mouseEvent.mousePosition -= view->getViewSize ().getTopLeft ();
|
||||
proc ();
|
||||
mouseEvent.mousePosition = oldPos;
|
||||
};
|
||||
switch (event.type)
|
||||
{
|
||||
case EventType::MouseEnter:
|
||||
{
|
||||
applyEventMouseLocalPosition ([&] () {
|
||||
callWhenScriptHasFunction (view, "onMouseEnter"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onMouseEnter(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType::MouseExit:
|
||||
{
|
||||
applyEventMouseLocalPosition ([&] () {
|
||||
callWhenScriptHasFunction (view, "onMouseExit"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onMouseExit(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType::MouseDown:
|
||||
{
|
||||
applyEventMouseLocalPosition ([&] () {
|
||||
callWhenScriptHasFunction (view, "onMouseDown"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onMouseDown(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType::MouseUp:
|
||||
{
|
||||
applyEventMouseLocalPosition ([&] () {
|
||||
callWhenScriptHasFunction (view, "onMouseUp"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onMouseUp(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType::MouseMove:
|
||||
{
|
||||
applyEventMouseLocalPosition ([&] () {
|
||||
callWhenScriptHasFunction (view, "onMouseMove"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onMouseMove(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType::MouseWheel:
|
||||
{
|
||||
applyEventMouseLocalPosition ([&] () {
|
||||
callWhenScriptHasFunction (view, "onMouseWheel"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onMouseWheel(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType::KeyDown:
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onKeyDown"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onKeyDown(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType::KeyUp:
|
||||
{
|
||||
callWhenScriptHasFunction (view, "onKeyUp"sv, [&] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onKeyUp(view, event);)"sv;
|
||||
callEventFunction (obj->getVar (), event, script);
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void valueChanged (CControl* control) override
|
||||
{
|
||||
callWhenScriptHasFunction (control, "onValueChanged"sv, [&] (auto This, auto& obj) {
|
||||
ScriptObject controlValue;
|
||||
controlValue->setDouble (control->getValue ());
|
||||
ScriptAddChildScoped scs (*jsContext->getRoot (), "value"sv, controlValue);
|
||||
static constexpr auto script = R"(view.onValueChanged(view, value);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
|
||||
void controlBeginEdit (CControl* control) override
|
||||
{
|
||||
callWhenScriptHasFunction (control, "onBeginEdit"sv, [] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onBeginEdit(view);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
void controlEndEdit (CControl* control) override
|
||||
{
|
||||
callWhenScriptHasFunction (control, "onEndEdit"sv, [] (auto This, auto& obj) {
|
||||
static constexpr auto script = R"(view.onEndEdit(view);)"sv;
|
||||
This->evalScript (obj->getVar (), script);
|
||||
});
|
||||
}
|
||||
|
||||
void viewWillDelete (CView* view) override { removeView (view); }
|
||||
|
||||
ViewScriptObject* addView (CView* view, std::unique_ptr<ViewScriptObject>&& scriptObject)
|
||||
{
|
||||
installListeners (view);
|
||||
viewScriptMap[view] = std::move (scriptObject);
|
||||
return viewScriptMap[view].get ();
|
||||
}
|
||||
|
||||
ViewScriptMap::iterator eraseViewFromMap (ViewScriptMap::iterator el)
|
||||
{
|
||||
return viewScriptMap.erase (el);
|
||||
}
|
||||
|
||||
ViewScriptMap::iterator removeView (CView* view) override
|
||||
{
|
||||
uninstallListeners (view);
|
||||
auto it = viewScriptMap.find (view);
|
||||
if (it != viewScriptMap.end ())
|
||||
return eraseViewFromMap (it);
|
||||
return viewScriptMap.end ();
|
||||
}
|
||||
|
||||
ViewScriptObject* addView (CView* view) override
|
||||
{
|
||||
auto it = viewScriptMap.find (view);
|
||||
if (it != viewScriptMap.end ())
|
||||
return it->second.get ();
|
||||
return addView (view, std::make_unique<ViewScriptObject> (view, this));
|
||||
}
|
||||
|
||||
void addView (CView* view, const std::string* script) noexcept
|
||||
{
|
||||
addView (view);
|
||||
if (script)
|
||||
{
|
||||
auto scriptContent = getScriptFileContent (*script);
|
||||
if (!scriptContent.empty ())
|
||||
script = &scriptContent;
|
||||
evalScript (viewScriptMap[view]->getVar (), *script);
|
||||
}
|
||||
}
|
||||
|
||||
std::string getScriptFileContent (const std::string& script) const
|
||||
{
|
||||
constexpr std::string_view scriptFileCommand ("//scriptfile ");
|
||||
std::string_view scriptView (script);
|
||||
if (scriptView.length () > scriptFileCommand.length ())
|
||||
{
|
||||
if (scriptView.substr (0, scriptFileCommand.length ()) == scriptFileCommand)
|
||||
{
|
||||
auto scriptFileStr = scriptView.substr (scriptFileCommand.length ());
|
||||
if (scriptFileStr.length () < 255)
|
||||
{
|
||||
return readScriptContents (scriptFileStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void log (CScriptVar* var) const
|
||||
{
|
||||
#if DEBUG
|
||||
static constexpr size_t bufferSize = 512;
|
||||
|
||||
struct DebugOutputStream : private std::streambuf,
|
||||
public std::ostream
|
||||
{
|
||||
|
||||
DebugOutputStream () : std::ostream (this) { data.reserve (bufferSize); }
|
||||
~DebugOutputStream () noexcept
|
||||
{
|
||||
if (!data.empty ())
|
||||
flush ();
|
||||
}
|
||||
|
||||
private:
|
||||
int overflow (int c) override
|
||||
{
|
||||
if (data.length () >= bufferSize)
|
||||
flush ();
|
||||
data += static_cast<char> (c);
|
||||
return 0;
|
||||
}
|
||||
void flush ()
|
||||
{
|
||||
DebugPrint ("%s", data.data ());
|
||||
data.clear ();
|
||||
data.reserve (bufferSize);
|
||||
}
|
||||
std::string data;
|
||||
};
|
||||
DebugOutputStream logStream;
|
||||
var->getJSON (logStream);
|
||||
logStream << "\n";
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
ScriptContext::ScriptContext (IUIDescription* uiDesc, OnScriptExceptionFunc&& onExceptionFunc,
|
||||
ReadScriptContentsFunc&& readContentsFunc)
|
||||
{
|
||||
impl =
|
||||
std::make_unique<Impl> (uiDesc, std::move (onExceptionFunc), std::move (readContentsFunc));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
ScriptContext::~ScriptContext () noexcept {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void ScriptContext::init (const std::string& initScript) { impl->initWithScript (&initScript); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void ScriptContext::onViewCreated (CView* view, const std::string& script)
|
||||
{
|
||||
impl->addView (view, &script);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void ScriptContext::reset () { impl->reset (); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
std::string ScriptContext::eval (std::string_view script) const
|
||||
{
|
||||
if (!impl->jsContext)
|
||||
return {};
|
||||
try
|
||||
{
|
||||
auto result = impl->jsContext->evaluateComplex (script);
|
||||
std::stringstream stream;
|
||||
result.getVar ()->getJSON (stream);
|
||||
return stream.str ();
|
||||
}
|
||||
catch (const CScriptException& exc)
|
||||
{
|
||||
if (impl->onScriptException)
|
||||
impl->onScriptException (exc.text);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // ScriptingInternal
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------
|
||||
struct UIScripting::Impl
|
||||
{
|
||||
using JSViewFactoryPtr = std::unique_ptr<ScriptingInternal::JavaScriptViewFactory>;
|
||||
using ScriptContextPtr = std::unique_ptr<ScriptingInternal::ScriptContext>;
|
||||
|
||||
std::unordered_map<const IUIDescription*, std::pair<JSViewFactoryPtr, ScriptContextPtr>> map;
|
||||
|
||||
static OnScriptExceptionFunc onScriptExceptionFunc;
|
||||
static ReadScriptContentsFunc readScriptContentsFunc;
|
||||
|
||||
static std::string readScriptContentsFromResource (std::string_view filename);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
UIScripting::OnScriptExceptionFunc UIScripting::Impl::onScriptExceptionFunc;
|
||||
UIScripting::ReadScriptContentsFunc UIScripting::Impl::readScriptContentsFunc;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
std::string UIScripting::Impl::readScriptContentsFromResource (std::string_view filename)
|
||||
{
|
||||
std::string fileStr (filename);
|
||||
CResourceDescription desc (fileStr.data ());
|
||||
if (auto stream = getPlatformFactory ().createResourceInputStream (desc))
|
||||
{
|
||||
std::string fileContent;
|
||||
while (true)
|
||||
{
|
||||
char buffer[256];
|
||||
auto numRead = stream->readRaw (buffer, static_cast<uint32_t> (std::size (buffer)));
|
||||
if (numRead == kStreamIOError)
|
||||
return {};
|
||||
fileContent.append (buffer, numRead);
|
||||
if (numRead < std::size (buffer))
|
||||
break;
|
||||
}
|
||||
return fileContent;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
UIScripting::UIScripting () { impl = std::make_unique<Impl> (); }
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
UIScripting::~UIScripting () noexcept = default;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void UIScripting::afterParsing (IUIDescription* desc) {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void UIScripting::beforeSaving (IUIDescription* desc) {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void UIScripting::onDestroy (IUIDescription* desc)
|
||||
{
|
||||
auto it = impl->map.find (desc);
|
||||
if (it != impl->map.end ())
|
||||
impl->map.erase (it);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
auto UIScripting::onCreateTemplateView (const IUIDescription* desc, const CreateTemplateViewFunc& f)
|
||||
-> CreateTemplateViewFunc
|
||||
{
|
||||
return [=] (auto name, auto controller) {
|
||||
return f (name, controller);
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
IViewFactory* UIScripting::getViewFactory (IUIDescription* desc, IViewFactory* originalFactory)
|
||||
{
|
||||
using namespace ScriptingInternal;
|
||||
|
||||
auto it = impl->map.find (desc);
|
||||
if (it != impl->map.end ())
|
||||
return it->second.first.get ();
|
||||
auto onScriptException = Impl::onScriptExceptionFunc;
|
||||
if (!onScriptException)
|
||||
onScriptException = [] (std::string_view reason) {
|
||||
std::cerr << reason << '\n';
|
||||
};
|
||||
auto readScriptContentsFunc = [] (auto filename) {
|
||||
std::string result;
|
||||
if (Impl::readScriptContentsFunc)
|
||||
result = Impl::readScriptContentsFunc (filename);
|
||||
if (result.empty ())
|
||||
{
|
||||
result = Impl::readScriptContentsFromResource (filename);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
auto scripting = std::make_unique<ScriptContext> (desc, std::move (onScriptException),
|
||||
std::move (readScriptContentsFunc));
|
||||
auto viewFactory = std::make_unique<JavaScriptViewFactory> (scripting.get (), originalFactory);
|
||||
auto result =
|
||||
impl->map.emplace (desc, std::make_pair (std::move (viewFactory), std::move (scripting)));
|
||||
return result.first->second.first.get ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void UIScripting::onEditingStart (IUIDescription* desc)
|
||||
{
|
||||
auto it = impl->map.find (desc);
|
||||
if (it != impl->map.end ())
|
||||
{
|
||||
it->second.second->reset ();
|
||||
it->second.first->setScriptingDisabled (true);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void UIScripting::onEditingEnd (IUIDescription* desc)
|
||||
{
|
||||
auto it = impl->map.find (desc);
|
||||
if (it != impl->map.end ())
|
||||
it->second.first->setScriptingDisabled (false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void UIScripting::init (const OnScriptExceptionFunc& onExceptionFunc,
|
||||
const ReadScriptContentsFunc& readScriptContentsFunc)
|
||||
{
|
||||
if (onExceptionFunc)
|
||||
Impl::onScriptExceptionFunc = onExceptionFunc;
|
||||
if (readScriptContentsFunc)
|
||||
Impl::readScriptContentsFunc = readScriptContentsFunc;
|
||||
UIDescriptionAddOnRegistry::add (std::make_unique<UIScripting> ());
|
||||
static ScriptingInternal::JavaScriptDrawableViewCreator jsViewCreator;
|
||||
UIViewFactory::registerViewCreator (jsViewCreator);
|
||||
static ScriptingInternal::JavaScriptDrawableControlCreator jsControlCreator;
|
||||
UIViewFactory::registerViewCreator (jsControlCreator);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,152 @@
|
||||
// This file is part of VSTGUI. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this
|
||||
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../uidescription/icontroller.h"
|
||||
#include "../uidescription/iviewfactory.h"
|
||||
#include "../uidescription/iuidescription.h"
|
||||
#include "../uidescription/iuidescriptionaddon.h"
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace VSTGUI {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** UIDescription scripting support
|
||||
*
|
||||
* @ingroup new_in_4_15
|
||||
*/
|
||||
class UIScripting : public UIDescriptionAddOnAdapter
|
||||
{
|
||||
public:
|
||||
using OnScriptExceptionFunc = std::function<void (std::string_view reason)>;
|
||||
using ReadScriptContentsFunc = std::function<std::string (std::string_view filename)>;
|
||||
|
||||
/** Initialize the UIScripting library
|
||||
*
|
||||
* Must be called once before creating UIDescription objects
|
||||
*
|
||||
* @param onExceptionFunc [optional] Called when a script context throws an exception
|
||||
* @param readScriptContentsFunc [optional] Called to load the script from a filename,
|
||||
* uses the resource folder as default.
|
||||
*/
|
||||
static void init (const OnScriptExceptionFunc& onExceptionFunc = {},
|
||||
const ReadScriptContentsFunc& readScriptContentsFunc = {});
|
||||
|
||||
~UIScripting () noexcept;
|
||||
|
||||
private:
|
||||
UIScripting ();
|
||||
|
||||
void afterParsing (IUIDescription* desc) override;
|
||||
void beforeSaving (IUIDescription* desc) override;
|
||||
void onDestroy (IUIDescription* desc) override;
|
||||
CreateTemplateViewFunc onCreateTemplateView (const IUIDescription* desc,
|
||||
const CreateTemplateViewFunc& f) override;
|
||||
IViewFactory* getViewFactory (IUIDescription* desc, IViewFactory* originalFactory) override;
|
||||
void onEditingStart (IUIDescription* desc) override;
|
||||
void onEditingEnd (IUIDescription* desc) override;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
friend std::unique_ptr<UIScripting> std::make_unique<UIScripting> ();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Script context interface
|
||||
*
|
||||
* @ingroup new_in_4_15
|
||||
*/
|
||||
struct IScriptContext
|
||||
{
|
||||
virtual ~IScriptContext () = default;
|
||||
|
||||
/** Evaluate custom code in the script context
|
||||
*
|
||||
* @param script The script to execute
|
||||
* @return Result object as json string
|
||||
*/
|
||||
virtual std::string eval (std::string_view script) const = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Extends IController
|
||||
*
|
||||
* The script controller extension adds script related methods to the controller.
|
||||
*
|
||||
* It can alter the scripts for the views if needed and scripts can get and set properties.
|
||||
*
|
||||
* @ingroup new_in_4_15
|
||||
*/
|
||||
struct IScriptControllerExtension
|
||||
{
|
||||
/** A property value is either an integer, double, string or undefined (nullptr_t) */
|
||||
using PropertyValue = std::variant<std::nullptr_t, int64_t, double, std::string>;
|
||||
|
||||
/** Verify the script for a view
|
||||
*
|
||||
* called before the script is executed
|
||||
*
|
||||
* @param view The view
|
||||
* @param script The script
|
||||
* @param context The script context where the script is executed in
|
||||
* @return Optional new script. If the optional is empty the original script is used.
|
||||
*/
|
||||
virtual std::optional<std::string> verifyScript (CView* view, const std::string& script,
|
||||
const IScriptContext* context) = 0;
|
||||
|
||||
/** Notification that the script context is destroyed
|
||||
*
|
||||
* don't call the context anymore after this call
|
||||
*
|
||||
* @param context The context which is destroyed
|
||||
*/
|
||||
virtual void scriptContextDestroyed (const IScriptContext* context) = 0;
|
||||
|
||||
/** Get a property
|
||||
*
|
||||
* called from a script
|
||||
*
|
||||
* if the propery exists, the value should be set and the return value should be true.
|
||||
* Otherwise return false.
|
||||
*
|
||||
* @param view The view
|
||||
* @param name The name of the property
|
||||
* @param value The property value
|
||||
* @return True on success.
|
||||
*/
|
||||
virtual bool getProperty (CView* view, std::string_view name, PropertyValue& value) const = 0;
|
||||
|
||||
/** Set a property
|
||||
*
|
||||
* called from a script
|
||||
*
|
||||
* @param view The view
|
||||
* @param name The name of the property
|
||||
* @param value The value of the property
|
||||
* @return True on success.
|
||||
*/
|
||||
virtual bool setProperty (CView* view, std::string_view name, const PropertyValue& value) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Adapter for IScriptControllerExtension */
|
||||
struct ScriptControllerExtensionAdapter : IScriptControllerExtension
|
||||
{
|
||||
std::optional<std::string> verifyScript (CView*, const std::string&,
|
||||
const IScriptContext*) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
void scriptContextDestroyed (const IScriptContext* context) override {}
|
||||
bool getProperty (CView*, std::string_view, PropertyValue&) const override { return false; }
|
||||
bool setProperty (CView*, std::string_view, const PropertyValue&) override { return false; }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // VSTGUI
|
||||
@@ -0,0 +1,468 @@
|
||||
## UI Description Scripting
|
||||
|
||||
### Introduction
|
||||
|
||||
There is simple scripting support added to the uidescription editor when the cmake
|
||||
option `VSTGUI_UISCRIPTING` is turned on.
|
||||
This scripting language is based on JavaScript via the TinyJS library and supports
|
||||
variables, arrays, structures and objects with inheritance (not fully implemented).
|
||||
|
||||
### Getting started
|
||||
|
||||
You need to add the scripting library to your target. With cmake this is done by adding
|
||||
`target_link_libraries(${target} PRIVATE vstgui_uiscripting)` to your CMakeLists.txt.
|
||||
|
||||
The library needs to be initialized once at runtime before any UIDescription object is created.
|
||||
This is done with a call to `VSTGUI::UIScripting::init ();`. You need to include
|
||||
`"vstgui/uiscripting/uiscripting.h"` for this symbol.
|
||||
|
||||
The init function accepts two optional parameters. The first parameter is a callback
|
||||
function that will be called whenever an exception occurs within a script, allowing you to
|
||||
handle any errors or exceptions that may arise. The second parameter is also a function
|
||||
that is invoked when a script is loaded, providing its name as input. This feature can be
|
||||
used, for instance, to load scripts directly from your source repository instead of the
|
||||
default location in the resource folder of your plug-in or application.
|
||||
|
||||
```cpp
|
||||
UIScripting::ReadScriptContentsFunc loadScriptFromRepositoryPath = {};
|
||||
#if DEBUG
|
||||
// in Debug mode, we want to load the scripts from the repository instead of from the app
|
||||
// resource folder as the scripts in the app resource folder are only synchronized when we build
|
||||
// the app and not in-between.
|
||||
loadScriptFromRepositoryPath = [] (auto filename) -> std::string {
|
||||
std::filesystem::path path (__FILE__);
|
||||
if (!path.empty ())
|
||||
{
|
||||
path = path.parent_path ().parent_path ();
|
||||
path.append ("resource");
|
||||
path.append ("scripts");
|
||||
path.append (filename);
|
||||
if (std::filesystem::exists (path))
|
||||
{
|
||||
std::ifstream f (path, std::ios::in | std::ios::binary);
|
||||
const auto sz = std::filesystem::file_size (path);
|
||||
std::string result (sz, '\0');
|
||||
f.read (result.data (), sz);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
#endif
|
||||
|
||||
UIScripting::init ({}, loadScriptFromRepositoryPath);
|
||||
```
|
||||
|
||||
All scripts of one UIDescription object will be executed in the same JavaScript context. So you
|
||||
can access global variables from all your scripts if needed.
|
||||
|
||||
### The view script
|
||||
|
||||
A script runs when a view is created and has a script assigned to its `script` attribute.
|
||||
The script attribute either contains the script text or a reference to a file inside
|
||||
the resource directory of the plugin/application containing the script text.
|
||||
|
||||
A reference to a script file is done via
|
||||
|
||||
```js
|
||||
//scriptfile $SCRIPT_FILENAME
|
||||
```
|
||||
|
||||
The script is executed directly after the view was created and before its other attributes are set and before child views are added.
|
||||
|
||||
### The **view** variable
|
||||
|
||||
The script contains a view variable which among other things can be used to add listeners on actions on the view.
|
||||
To install a function that is executed whenever the mouse enters the view do:
|
||||
|
||||
```js
|
||||
view.onMouseEnter = function (view, event) {
|
||||
// do something when the mouse enters this view...
|
||||
};
|
||||
```
|
||||
|
||||
See [**View Listeners**](#view-listeners) which listeners can be installed on views.
|
||||
And see [**View methods and properties**](#view-methods-and-properties) for the other methods a view supports.
|
||||
|
||||
If the view is a control it additionally supports the methods described in [**Control methods and properties**](#control-methods-and-properties)
|
||||
and the listeners as described in [**Control listeners**](#control-listeners).
|
||||
|
||||
If the view is a view container then it supports the additional listeners described in [**View container listeners**](#view-container-listeners).
|
||||
|
||||
### The JavaScriptDrawableView
|
||||
|
||||
You can add a `JavaScriptDrawableView` from the view types to your view hierarchy to create a view
|
||||
that you need to completely program via the script.
|
||||
|
||||
Event handling is done via the previously mentioned listeners and drawing is done by implementing
|
||||
the draw function of the view:
|
||||
|
||||
```js
|
||||
view.draw = function(drawContext, dirtyRect) {
|
||||
}
|
||||
```
|
||||
|
||||
The [drawContext](#the-drawcontext-object) parameter is the object where you call its method to draw things into the view
|
||||
and the dirtyRect parameter contains the rectangle that needs to be drawn.
|
||||
|
||||
Additionally the following two functions can be implemented to provide custom focus drawing:
|
||||
```js
|
||||
view.drawFocusOnTop = function () { return false; };
|
||||
|
||||
view.getFocusPath = function(path, focusWidth) {
|
||||
var bounds = view.getBounds();
|
||||
path.addRoundRect (bounds, 4);
|
||||
bounds.left -= focusWidth;
|
||||
bounds.right += focusWidth;
|
||||
bounds.top -= focusWidth;
|
||||
bounds.bottom += focusWidth;
|
||||
path.addRoundRect (bounds, 4);
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
See the C++ IFocusDrawing interface for a description of these methods.
|
||||
|
||||
### The JavaScriptDrawableControl
|
||||
|
||||
You can add a `JavaScriptDrawableControl` from the view types to your view hierarchy to create a
|
||||
control that you need to completely program via the script.
|
||||
|
||||
In addition to the `JavaScriptDrawableView` you can also use the
|
||||
[**Control methods and properties**](#control-methods-and-properties) and
|
||||
[**Control listeners**](#control-listeners) on this view.
|
||||
|
||||
### The **uiDesc** variable
|
||||
|
||||
The script also contains the uiDesc variable you can use to query information on the uiDesc file.
|
||||
The following methods are implemented on that object:
|
||||
|
||||
|name |arguments |return type |comments |
|
||||
|---------------|-------------|---------------|----------------------------------------------|
|
||||
|colorNames | |`array<string>`|get all color names from the UIDesc file |
|
||||
|fontNames | |`array<string>`|get all font names from the UIDesc file |
|
||||
|bitmapNames | |`array<string>`|get all bitmap names from the UIDesc file |
|
||||
|gradientNames | |`array<string>`|get all gradient names from the UIDesc file |
|
||||
|controlTagNames| |`array<string>`|get all control tag names from the UIDesc file|
|
||||
|getTagForName |name:`string`|`integer` |get the tag for the control tag name |
|
||||
|lookupTagName |tag:`integer`|`string` |get the control tag name from the tag |
|
||||
|
||||
### Interacting with c++ code
|
||||
|
||||
To interact with the c++ code set a subcontroller on the view or its parents and extend the `IController` with `IScriptControllerExtension`
|
||||
and implement its methods. From the script you can call the view methods `getControllerProperty` or `setControllerProperty`.
|
||||
|
||||
A property can either be an integer, floating point, string or undefined.
|
||||
|
||||
### View methods and properties
|
||||
|
||||
|type |name |arguments |return type|comments |
|
||||
|--------|---------------------|------------------------------|-----------|----------------------------------------------------|
|
||||
|property|type | |`string` |contains the type of the view like `CAnimKnob` |
|
||||
|method |isTypeOf |name:`string` |`integer` |returns true if the view is of type `name` |
|
||||
|method |getParent | |`object` |returns the parent view or undefined if it has none |
|
||||
|method |getAttribute |key:`string` |`string` |get the attribute with name `key` |
|
||||
|method |setAttribute |key:`string`,value:`string` |`string` |set the attribute value with name `key` to `value` |
|
||||
|method |getControllerProperty|name:`string` |`property` |set a controller property. returns true if succeeded|
|
||||
|method |setControllerProperty|name:`string`,value:`property`|`property` |get a controller property. returns a property |
|
||||
|method |getBounds | |`object` |returns the bounds of the view as a rectangle |
|
||||
|
||||
For example to set the opacity attribute of a view write:
|
||||
|
||||
```js
|
||||
view.setAttribute("opacity", 0.5);
|
||||
```
|
||||
|
||||
You can set any attribute as shown in the WYSIWYG editor.
|
||||
|
||||
### View listeners
|
||||
|
||||
|name |arguments |
|
||||
|--------------|-----------------------------|
|
||||
|onAttached |view:`object` |
|
||||
|onRemoved |view:`object` |
|
||||
|onSizeChanged |view:`object`,newSize:`rect` |
|
||||
|onLostFocus |view:`object` |
|
||||
|onTookFocus |view:`object` |
|
||||
|onMouseEnabled|view:`object`,state:`boolean`|
|
||||
|onMouseEnter |view:`object`,event:`object` |
|
||||
|onMouseExit |view:`object`,event:`object` |
|
||||
|onMouseDown |view:`object`,event:`object` |
|
||||
|onMouseUp |view:`object`,event:`object` |
|
||||
|onMouseMove |view:`object`,event:`object` |
|
||||
|onMouseWheel |view:`object`,event:`object` |
|
||||
|onKeyDown |view:`object`,event:`object` |
|
||||
|onKeyUp |view:`object`,event:`object` |
|
||||
|
||||
TODO: Describe Event object
|
||||
|
||||
### Control methods and properties
|
||||
|
||||
|type |name |arguments |return type|comments |
|
||||
|--------|---------------------|--------------|-----------|----------------------------------------------------|
|
||||
|method |setValue |value:`double`|`void` | set the control value to `value` |
|
||||
|method |getValue | |`double` | get the control value |
|
||||
|method |setValueNormalized |value:`double`|`void` | set the control value from the normalized `value` |
|
||||
|method |getValueNormalized | |`double` | get the normalized control value |
|
||||
|method |beginEdit | |`void` | begin editing the control |
|
||||
|method |endEdit | |`void` | end editing the control |
|
||||
|method |getMinValue | |`double` | get the minimum value of the control |
|
||||
|method |getMaxValue | |`double` | get the maximum value of the control |
|
||||
|method |getTag | |`integer` | get the control tag |
|
||||
|
||||
### Control listeners
|
||||
|
||||
|name |arguments |
|
||||
|--------------|----------------------------|
|
||||
|onValueChanged|view:`object`,value:`double`|
|
||||
|onBeginEdit |view:`object` |
|
||||
|onEndEdit |view:`object` |
|
||||
|
||||
### View container listeners
|
||||
|
||||
|name |arguments |
|
||||
|-------------|----------------------------|
|
||||
|onViewAdded |view:`object`,child:`object`|
|
||||
|onViewRemoved|view:`object`,child:`object`|
|
||||
|
||||
### The drawcontext object
|
||||
|
||||
|name |arguments |return type|comments |
|
||||
|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|-----------|----------------------------------------------------|
|
||||
|clearRect | rect:`Rect` |`void` |
|
||||
|createRoundGraphicsPath | rect:`Rect`,radius:`double` |`Path` |
|
||||
|createGraphicsPath | |`Path` |
|
||||
|createGradient | startColorPosition:`double`,startColor:`Color`,endColorPosition:`double`,endColor:`Color` |`Gradient` |
|
||||
|getStringWidth | string:`string` |`double` |
|
||||
|drawArc | rect:`Rect`,startAngle:`double`,endAngle:`double`,style:`string` |`void` |style can be `stroked`,`filled` or `filledAndStroked`
|
||||
|drawBitmap | name:`string`,destRect:`Rect`,offsetPoint?:`Point`,alpha?:`double` |`void` |name is the bitmapname as declared in the uidesc file
|
||||
|drawEllipse | rect:`Rect`,style:`string` |`void` |style can be `stroked`,`filled` or `filledAndStroked`
|
||||
|drawGraphicsPath | path:`Path`,mode?:`string`,transform?:`TransformMatrix` |`void` |mode can be `stroked`, `filled` or `filledEvenOdd`
|
||||
|drawLine | from:`Point`,to:`Point` |`void` |
|
||||
|drawPolygon | points:`PointArray`,style:`string` |`void` |style can be `stroked`,`filled` or `filledAndStroked`
|
||||
|drawRect | rect:`Rect`,style:`string` |`void` |style can be `stroked`,`filled` or `filledAndStroked`
|
||||
|drawString | string:`string`,rect:`Rect`,align?:`string` |`void` |align can be `left`, `center` or `right`
|
||||
|fillLinearGradient | path:`Path`,gradient:`Gradient`,startPoint:`Point`,endPoint:`Point`,evenOdd?:`bool`,transform?:`TransformMatrix` |`void` |
|
||||
|fillRadialGradient | path:`Path`,gradient:`Gradient`,centerPoint:`Point`,radius:`double`,originOffsetPoint?:`Point`,evenOdd?:`bool`,transform?:`TransformMatrix`|`void` |
|
||||
|restoreGlobalState | |`void` |
|
||||
|saveGlobalState | |`void` |
|
||||
|setClipRect | rect:`Rect` |`void` |
|
||||
|setFont | name:`string` |`void` |name is the fontname as declared in the uidesc file
|
||||
|setFontColor | color:`Color` |`void` |
|
||||
|setFillColor | color:`Color` |`void` |
|
||||
|setFrameColor | color:`Color` |`void` |
|
||||
|setGlobalAlpha | alpha:`double` |`void` |
|
||||
|setLineWidth | width:`double` |`void` |
|
||||
|setLineStyle | styleOrLineCap:`string`,lineJoin?:`string`,dashLengths?:`doubleArray`,dashPhase?:`double` |`void` |
|
||||
|setDrawMode | mode:`string` |`void` |
|
||||
|
||||
The `setLineStyle` method takes either one argument: the `style which can be `solid` or `dotted`. Or it can take 4 arguments:
|
||||
* lineCap [required] : `butt`, `round` or `square`
|
||||
* lineJoin [opt] : `miter, `round` or `bevel`
|
||||
* dashLength [opt] : `doubleArray`
|
||||
* dashPhase [opt] : `double`
|
||||
|
||||
The `Rect` object has 4 properties: `left`, `top`, `right` and `bottom`.
|
||||
|
||||
The `Point` object has 2 properties: `x` and `y`.
|
||||
|
||||
The `Color` object is either a CSS color name, a colorname as described in the uidesc file or an rgba value in hex form: `#FF00FFAA`.
|
||||
|
||||
### The path object
|
||||
|
||||
The path object can be created via the drawcontext object methods `createGraphicsPath` and `createRoundGraphicsPath` and has these methods:
|
||||
|
||||
|name |arguments |
|
||||
|----------------|------------------------------------------------------------------|
|
||||
|addEllipse |rect:`Rect` |
|
||||
|addArc |rect:`Rect`,startAngle:`double`,endAngle:`double`,clockwise:`bool`|
|
||||
|addBezierCurve |control1:`Point`,control2:`Point`,end:`Point` |
|
||||
|addLine |to:`Point` |
|
||||
|addPath |path:`Path`,transformMatrix?:`TransformMatrix` |
|
||||
|addRect |rect:`Rect` |
|
||||
|addRoundRect |rect:`Rect`,radius:`double` |
|
||||
|closeSubpath | |
|
||||
|beginSubpath |start:`Point` |
|
||||
|
||||
### The TransformMatrix object
|
||||
|
||||
A transform matrix object is created via the global `makeTransformMatrix ()` function and has these methods:
|
||||
|
||||
|name |arguments |return type |
|
||||
|--------------|---------------------------------|-----------------|
|
||||
|concat |transformMatrix:`TransformMatrix`|`void` |
|
||||
|inverse | |`TransformMatrix`|
|
||||
|rotate |angle:`double`, center?:`Point` |`void` |
|
||||
|scale |x:`double`, y:`double` |`void` |
|
||||
|skewX |angle:`double` |`void` |
|
||||
|skewY |angle:`double` |`void` |
|
||||
|translate |x:`double`, y:`double` |`void` |
|
||||
|transform |pointOrRect:`Point` or `Rect` |`Point` or `Rect`|
|
||||
|
||||
### Global functions
|
||||
|
||||
- `createTimer(context, fireTime, callback) -> TimerObject`
|
||||
- creates a new stopped timer object
|
||||
- fire time is in milliseconds
|
||||
- the context will be provided on every timer callback
|
||||
- the callback has the signature `function(context)`
|
||||
- a timer object has the methods:
|
||||
- start()
|
||||
- stop()
|
||||
- invalid()
|
||||
- `iterateSubViews(view, context, callback) -> Void`
|
||||
- calls the callback with context as parameter for every child view of view
|
||||
- `log(obj) -> Void`
|
||||
- logs the object to the debug console
|
||||
- `makeTransformMatrix() -> TransformMatrix`
|
||||
- see [TransformMatrix](#the-transformmatrix-object)
|
||||
|
||||
### Other built in functions (from the TinyJS library)
|
||||
|
||||
- `exec(jsCode)`
|
||||
- `eval(jsCode)`
|
||||
- `trace()`
|
||||
- `charToInt(ch)`
|
||||
- `Object.dump()`
|
||||
- `Object.clone()`
|
||||
- `String.indexOf(search)`
|
||||
- `String.substring(lo,hi)`
|
||||
- `String.charAt(pos)`
|
||||
- `String.charCodeAt(pos)`
|
||||
- `String.fromCharCode(cha`
|
||||
- `String.split(separator)`
|
||||
- `Integer.parseInt(str)`
|
||||
- `Integer.valueOf(str)`
|
||||
- `JSON.stringify(obj)`
|
||||
- `Array.contains(obj)`
|
||||
- `Array.remove(obj)`
|
||||
- `Array.join(separator)`
|
||||
- `Math.rand()`
|
||||
- `Math.randInt(min, max)`
|
||||
- `Math.abs(a)`
|
||||
- `Math.round(a)`
|
||||
- `Math.min(a,b)`
|
||||
- `Math.max(a,b)`
|
||||
- `Math.range(x,a,b)`
|
||||
- `Math.sign(a)`
|
||||
- `Math.PI()`
|
||||
- `Math.toDegrees(a)`
|
||||
- `Math.toRadians(a)`
|
||||
- `Math.sin(a)`
|
||||
- `Math.asin(a)`
|
||||
- `Math.cos(a)`
|
||||
- `Math.acos(a)`
|
||||
- `Math.tan(a)`
|
||||
- `Math.atan(a)`
|
||||
- `Math.sinh(a)`
|
||||
- `Math.asinh(a)`
|
||||
- `Math.cosh(a)`
|
||||
- `Math.acosh(a)`
|
||||
- `Math.tanh(a)`
|
||||
- `Math.atanh(a)`
|
||||
- `Math.E()`
|
||||
- `Math.log(a)`
|
||||
- `Math.log10(a)`
|
||||
- `Math.exp(a)`
|
||||
- `Math.pow(a,b)`
|
||||
- `Math.sqr(a)`
|
||||
- `Math.sqrt(a)`
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
// Hover Opacity Animation Script
|
||||
// This example script changes the opacity of the view
|
||||
// when the mouse enters or exits the view
|
||||
|
||||
/* the default opacity of the view is stored in view.default_opacity */
|
||||
view.default_opacity = 0.6;
|
||||
|
||||
/* the current opacity of the view is stored in view.opacity */
|
||||
view.opacity = view.default_opacity;
|
||||
|
||||
/* the timer to change the opacity is stored in view.opacity_timer */
|
||||
view.opacity_timer = createTimer(view, 16, function(view) {
|
||||
view.opacity += view.opacity_change;
|
||||
if (view.opacity_change > 0)
|
||||
{
|
||||
if (view.opacity > 1)
|
||||
{
|
||||
view.opacity = 1;
|
||||
view.opacity_timer.stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (view.opacity <= view.default_opacity)
|
||||
{
|
||||
view.opacity = view.default_opacity;
|
||||
view.opacity_timer.stop();
|
||||
}
|
||||
}
|
||||
view.setAttribute("opacity", view.opacity);
|
||||
});
|
||||
|
||||
/* the view will be shown with full opacity when focused so the state of the focus is stored in view.hasFocus */
|
||||
view.has_focus = false;
|
||||
|
||||
/* to correctly restore the hover state after focus lost, the state if the mouse is inside the view or outside
|
||||
is stored in view.mouseInside
|
||||
*/
|
||||
view.mouse_inside = false;
|
||||
|
||||
/* we install a mouse enter listener
|
||||
when the mouse enters the view we start the opacity change timer
|
||||
*/
|
||||
view.onMouseEnter = function(view, event) {
|
||||
view.mouse_inside = true;
|
||||
if (view.has_focus)
|
||||
return;
|
||||
view.opacity_change = 0.075;
|
||||
view.opacity_timer.start();
|
||||
event.consume = true;
|
||||
};
|
||||
|
||||
/* we also install a mouse exit listener
|
||||
when the mouse exits the view we start the opacity change timer again
|
||||
now with a negative opacity_change variable so that in the timer callback
|
||||
the opacity is going back to the default opacity
|
||||
*/
|
||||
view.onMouseExit = function(view, event) {
|
||||
view.mouse_inside = false;
|
||||
if (view.has_focus)
|
||||
return;
|
||||
view.opacity_change = -0.05;
|
||||
view.opacity_timer.start();
|
||||
event.consumed = true;
|
||||
};
|
||||
|
||||
/* we also install a view removed listener so that we can cleanup and stop the timer */
|
||||
view.onRemoved = function(view) {
|
||||
// cleanup, when the view is removed, stop the timer
|
||||
view.opacity_timer.stop();
|
||||
};
|
||||
|
||||
/* when the view takes focus we show the view with full opacity */
|
||||
view.onTookFocus = function(view) {
|
||||
view.has_focus = true;
|
||||
view.opacity_timer.stop();
|
||||
view.setAttribute("opacity", 1);
|
||||
view.opacity = 1;
|
||||
};
|
||||
|
||||
/* when the view lost focus we start the opacity animation when the mouse is not inside this view */
|
||||
view.onLostFocus = function(view) {
|
||||
view.has_focus = false;
|
||||
if (!view.mouse_inside)
|
||||
{
|
||||
view.onMouseExit(view, undefind);
|
||||
}
|
||||
};
|
||||
|
||||
/* enable the mouse, otherwise no mouse listener is called */
|
||||
view.setAttribute("mouse-enabled", true);
|
||||
|
||||
/* set the initial view opacity*/
|
||||
view.setAttribute("opacity", view.opacity);
|
||||
```
|
||||
Reference in New Issue
Block a user