diff --git a/vstgui/uidescription-scripting/CMakeLists.txt b/vstgui/uidescription-scripting/CMakeLists.txt index 7b4bfc59e..b404e5565 100644 --- a/vstgui/uidescription-scripting/CMakeLists.txt +++ b/vstgui/uidescription-scripting/CMakeLists.txt @@ -5,6 +5,15 @@ set(SCRIPTING_SOURCES 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/viewscriptobject.cpp + detail/viewscriptobject.h uiscripting.cpp uiscripting.h uiscripting.md diff --git a/vstgui/uidescription-scripting/detail/converters.h b/vstgui/uidescription-scripting/detail/converters.h new file mode 100644 index 000000000..dbf2680e9 --- /dev/null +++ b/vstgui/uidescription-scripting/detail/converters.h @@ -0,0 +1,125 @@ +// 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); + 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; + mod.addChild ("shift"sv, modifierEvent->modifiers.has (ModifierKey::Shift)); + mod.addChild ("alt"sv, modifierEvent->modifiers.has (ModifierKey::Alt)); + mod.addChild ("control"sv, modifierEvent->modifiers.has (ModifierKey::Control)); + mod.addChild ("super"sv, modifierEvent->modifiers.has (ModifierKey::Super)); + 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; + buttons.addChild ("left"sv, mouseEvent->buttonState.has (MouseButton::Left)); + buttons.addChild ("right"sv, mouseEvent->buttonState.has (MouseButton::Right)); + buttons.addChild ("middle"sv, mouseEvent->buttonState.has (MouseButton::Middle)); + 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); + wheel.addChild ( + "directionInvertedFromDevice"sv, + wheelEvent.flags & MouseWheelEvent::Flags::DirectionInvertedFromDevice ? true : false); + wheel.addChild ("preciceDelta"sv, + wheelEvent.flags & MouseWheelEvent::Flags::PreciseDeltas ? true : false); + obj.addChild ("mouseWheel"sv, std::move (wheel)); + } + if (auto keyEvent = asKeyboardEvent (event)) + { + ScriptObject key; + key.addChild ("character"sv, static_cast (keyEvent->character)); + key.addChild ("virtual"sv, static_cast (keyEvent->virt)); + key.addChild ("isRepeat"sv, keyEvent->isRepeat); + } + obj.addChild ("consume"sv, 0); + return obj; +} + +//------------------------------------------------------------------------ +} // ScriptingInternal +} // VSTGUI diff --git a/vstgui/uidescription-scripting/detail/drawable.cpp b/vstgui/uidescription-scripting/detail/drawable.cpp new file mode 100644 index 000000000..a0ed85f0d --- /dev/null +++ b/vstgui/uidescription-scripting/detail/drawable.cpp @@ -0,0 +1,125 @@ +// 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/cdrawcontext.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 rectObj = makeScriptRect (rect); + auto scriptRoot = scriptContext->getRoot (); + ScriptAddChildScoped scs (*scriptRoot, "view", scriptObject->getVar ()); + ScriptAddChildScoped scs2 (*scriptRoot, "context", drawContext.getVar ()); + ScriptAddChildScoped scs3 (*scriptRoot, "rect", rectObj.getVar ()); + scriptContext->evalScript ("view.draw(context, rect);"sv); + + drawContext.setDrawContext (nullptr, nullptr); + + context->restoreGlobalState (); +} + +//------------------------------------------------------------------------ +void JavaScriptDrawable::setup (ViewScriptObject* inObject) { scriptObject = inObject; } + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +void JavaScriptDrawableView::drawRect (CDrawContext* context, const CRect& rect) +{ + onDraw (context, rect, getViewSize ()); +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +void JavaScriptDrawableControl::draw (CDrawContext* context) { drawRect (context, getViewSize ()); } + +//------------------------------------------------------------------------ +void JavaScriptDrawableControl::drawRect (CDrawContext* context, const CRect& rect) +{ + onDraw (context, rect, getViewSize ()); +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +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 diff --git a/vstgui/uidescription-scripting/detail/drawable.h b/vstgui/uidescription-scripting/detail/drawable.h new file mode 100644 index 000000000..c45b66b42 --- /dev/null +++ b/vstgui/uidescription-scripting/detail/drawable.h @@ -0,0 +1,70 @@ +// 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); + + void setup (ViewScriptObject* object); + +private: + ViewScriptObject* scriptObject {nullptr}; + DrawContextObject drawContext; +}; + +//------------------------------------------------------------------------ +struct JavaScriptDrawableView : CView, + JavaScriptDrawable +{ + using CView::CView; + + void drawRect (CDrawContext* context, const CRect& rect) override; +}; + +//------------------------------------------------------------------------ +struct JavaScriptDrawableControl : CControl, + JavaScriptDrawable +{ + using CControl::CControl; + + void draw (CDrawContext* pContext) override; + void drawRect (CDrawContext* context, const CRect& rect) 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 diff --git a/vstgui/uidescription-scripting/detail/drawcontextobject.cpp b/vstgui/uidescription-scripting/detail/drawcontextobject.cpp new file mode 100644 index 000000000..d6d8b72ce --- /dev/null +++ b/vstgui/uidescription-scripting/detail/drawcontextobject.cpp @@ -0,0 +1,290 @@ +// 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 "../../uidescription/uidescription.h" +#include "../../uidescription/uiviewcreator.h" + +//------------------------------------------------------------------------ +namespace VSTGUI { +namespace ScriptingInternal { + +using namespace std::literals; +using namespace TJS; + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +DrawContextObject::DrawContextObject () +{ + // TODO: decide if this object should expose the same interface as "CanvasRenderingContext2D" + scriptVar->setLifeTimeObserver (this); +#if 0 +#else + addFunc ("drawLine"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto fromPoint = var->getParameter ("from"sv); + if (!fromPoint) + throw CScriptException ( + "Missing `from` argument in drawContext.drawLine(from, to);"); + auto toPoint = var->getParameter ("to"sv); + if (!toPoint) + throw CScriptException ( + "Missing `to` argument in drawContext.drawLine(from, to);"); + auto from = fromScriptPoint (*fromPoint); + auto to = fromScriptPoint (*toPoint); + context->drawLine (from, to); + }, + {"from", "to"}); + addFunc ("drawRect"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto rectVar = var->getParameter ("rect"sv); + if (!rectVar) + throw CScriptException ( + "Missing `rect` argument in drawContext.drawRect(rect, style);"); + auto styleVar = var->getParameter ("style"sv); + if (!styleVar) + throw CScriptException ( + "Missing `style` argument in drawContext.drawRect(rect, style);"); + auto rect = fromScriptRect (*rectVar); + CDrawStyle style {}; + if (styleVar->getString () == "stroked") + style = kDrawStroked; + else if (styleVar->getString () == "filled") + style = kDrawFilled; + else if (styleVar->getString () == "filledAndStroked") + style = kDrawFilledAndStroked; + context->drawRect (rect, style); + }, + {"rect", "style"}); + addFunc ("drawEllipse"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto rectVar = var->getParameter ("rect"sv); + if (!rectVar) + throw CScriptException ( + "Missing `rect` argument in drawContext.drawEllipse(rect, style);"); + auto styleVar = var->getParameter ("style"sv); + if (!styleVar) + throw CScriptException ( + "Missing `style` argument in drawContext.drawEllipse(rect, style);"); + auto rect = fromScriptRect (*rectVar); + CDrawStyle style {}; + if (styleVar->getString () == "stroked") + style = kDrawStroked; + else if (styleVar->getString () == "filled") + style = kDrawFilled; + else if (styleVar->getString () == "filledAndStroked") + style = kDrawFilledAndStroked; + context->drawEllipse (rect, style); + }, + {"rect", "style"}); + addFunc ("clearRect"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto rectVar = var->getParameter ("rect"sv); + if (!rectVar) + throw CScriptException ( + "Missing `rect` argument in drawContext.clearRect(rect);"); + auto rect = fromScriptRect (*rectVar); + context->clearRect (rect); + }, + {"rect", "style"}); + addFunc ("drawPolygon"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto pointsVar = var->getParameter ("points"sv); + if (!pointsVar) + throw CScriptException ( + "Missing `points` argument in drawContext.drawPolygon(points, style);"); + if (!pointsVar->isArray ()) + throw CScriptException ("`points` argument must be an array of points in " + "drawContext.drawPolygon(points, style);"); + auto styleVar = var->getParameter ("style"sv); + if (!styleVar) + throw CScriptException ( + "Missing `style` argument 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)); + } + CDrawStyle style {}; + if (styleVar->getString () == "stroked") + style = kDrawStroked; + else if (styleVar->getString () == "filled") + style = kDrawFilled; + else if (styleVar->getString () == "filledAndStroked") + style = kDrawFilledAndStroked; + context->drawPolygon (points, style); + }, + {"points", "style"}); + addFunc ("setClipRect"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto rectVar = var->getParameter ("rect"sv); + if (!rectVar) + throw CScriptException ( + "Missing `rect` argument in drawContext.setClipRect(rect);"); + auto rect = fromScriptRect (*rectVar); + context->setClipRect (rect); + }, + {"rect"}); +#if 1 // TODO: make a bitmap js object instead + addFunc ("drawBitmap"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto bitmapNameVar = var->getParameter ("bitmap"sv); + if (!bitmapNameVar) + throw CScriptException ( + "Missing `bitmap` argument in drawContext.drawBitmap(bitmap, destRect, " + "offsetPoint, alpha);"); + auto destRectVar = var->getParameter ("destRect"sv); + if (!destRectVar) + throw CScriptException ( + "Missing `destRect` argument in drawContext.drawBitmap(bitmap, destRect, " + "offsetPoint, alpha);"); + auto offsetPointVar = var->findChild ("offsetPoint"sv); + auto alphaVar = var->findChild ("alpha"sv); + auto bitmap = uiDesc->getBitmap (bitmapNameVar->getString ().data ()); + if (!bitmap) + throw CScriptException ("bitmap not found in uiDescription"); + auto destRect = fromScriptRect (*destRectVar); + auto offset = + offsetPointVar ? fromScriptPoint (*offsetPointVar->getVar ()) : CPoint (0, 0); + auto alpha = alphaVar ? alphaVar->getVar ()->getDouble () : 1.f; + context->drawBitmap (bitmap, destRect, offset, alpha); + }, + {"bitmap", "destRect"}); +#endif + addFunc ( + "drawString"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto stringVar = var->getParameter ("string"sv); + if (!stringVar) + throw CScriptException ( + "Missing `string` argument in drawContext.drawString(string, rect, align);"); + auto rectVar = var->getParameter ("rect"sv); + if (!rectVar) + throw CScriptException ( + "Missing `rect` argument in drawContext.drawString(string, rect, align);"); + auto alignVar = var->getParameter ("align"sv); + if (!alignVar) + throw CScriptException ( + "Missing `align` argument in drawContext.drawString(string, rect, align);"); + auto string = stringVar->getString ().data (); + auto rect = fromScriptRect (*rectVar); + CHoriTxtAlign align = kCenterText; + if (alignVar->getString () == "left") + align = kLeftText; + else if (alignVar->getString () == "center") + align = kCenterText; + else if (alignVar->getString () == "right") + align = kRightText; + else + throw CScriptException ( + "wrong `align` argument. expected 'left', 'center' or 'right'"); + context->drawString (string, rect, align, true); + }, + {"string", "rect", "align"}); + addFunc ("setFont"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto fontVar = var->getParameter ("font"sv); + if (!fontVar) + throw CScriptException ( + "Missing `font` argument in drawContext.setFont(font);"); + if (auto font = uiDesc->getFont (fontVar->getString ().data ())) + context->setFont (font); + }, + {"font"}); + addFunc ("setFontColor"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto colorVar = var->getParameter ("color"sv); + if (!colorVar) + throw CScriptException ( + "Missing `color` argument in drawContext.setFontColor(color);"); + CColor color {}; + UIViewCreator::stringToColor (&colorVar->getString (), color, uiDesc); + context->setFontColor (color); + }, + {"color"}); + addFunc ("setFillColor"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto colorVar = var->getParameter ("color"sv); + if (!colorVar) + throw CScriptException ( + "Missing `color` argument in drawContext.setFillColor(color);"); + CColor color {}; + UIViewCreator::stringToColor (&colorVar->getString (), color, uiDesc); + context->setFillColor (color); + }, + {"color"}); + addFunc ("setFrameColor"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto colorVar = var->getParameter ("color"sv); + if (!colorVar) + throw CScriptException ( + "Missing `color` argument in drawContext.setFrameColor(color);"); + CColor color {}; + if (!UIViewCreator::stringToColor (&colorVar->getString (), color, uiDesc)) + throw CScriptException ( + "Unknown `color` argument in drawContext.setFrameColor(color);"); + context->setFrameColor (color); + }, + {"color"}); + addFunc ("setLineWidth"sv, + [this] (CScriptVar* var) { + if (!context) + throw CScriptException ("Native context is missing!"); + auto widthVar = var->getParameter ("width"sv); + if (!widthVar) + throw CScriptException ( + "Missing `width` argument in drawContext.setLineWidth(width);"); + context->setLineWidth (widthVar->getDouble ()); + }, + {"width"}); +#endif +} + +//------------------------------------------------------------------------ +void DrawContextObject::setDrawContext (CDrawContext* inContext, IUIDescription* inUIDesc) +{ + context = inContext; + uiDesc = inUIDesc; +} + +//------------------------------------------------------------------------ +void DrawContextObject::onDestroy (CScriptVar* v) +{ + v->setLifeTimeObserver (nullptr); + scriptVar = nullptr; +} + +//------------------------------------------------------------------------ +} // ScriptingInternal +} // VSTGUI diff --git a/vstgui/uidescription-scripting/detail/drawcontextobject.h b/vstgui/uidescription-scripting/detail/drawcontextobject.h new file mode 100644 index 000000000..c98d7e5c4 --- /dev/null +++ b/vstgui/uidescription-scripting/detail/drawcontextobject.h @@ -0,0 +1,99 @@ +// 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 { + +//------------------------------------------------------------------------ +struct DrawContextObject : ScriptObject, + TJS::IScriptVarLifeTimeObserver +{ + DrawContextObject (); + + 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; + 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 + + CDrawContext* context {nullptr}; + IUIDescription* uiDesc {nullptr}; +}; + +//------------------------------------------------------------------------ +} // ScriptingInternal +} // VSTGUI diff --git a/vstgui/uidescription-scripting/detail/scriptobject.h b/vstgui/uidescription-scripting/detail/scriptobject.h new file mode 100644 index 000000000..0bca69e02 --- /dev/null +++ b/vstgui/uidescription-scripting/detail/scriptobject.h @@ -0,0 +1,143 @@ +// 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 = link->getVar (); + oldVar->addRef (); + 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& argNames) +{ + auto f = createJSFunction (std::move (proc)); + for (auto name : argNames) + f->addChildNoDup (name); + return f; +} + +//------------------------------------------------------------------------ +struct ScriptObject +{ + using CScriptVar = TJS::CScriptVar; + + ScriptObject () + { + scriptVar = new CScriptVar (); + scriptVar->addRef (); + } + 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->removeAllChildren (); + scriptVar->release (); + } + } + + CScriptVar* getVar () const { return scriptVar; } + CScriptVar* take () + { + auto var = scriptVar; + scriptVar = nullptr; + return var; + } + + void addChild (std::string_view name, ScriptObject&& obj) + { + auto var = obj.take (); + scriptVar->addChild (name, var); + } + void addChild (std::string_view name, double d) + { + scriptVar->addChild (name, new CScriptVar (d)); + } + void addChild (std::string_view name, int64_t i) + { + scriptVar->addChild (name, new CScriptVar (i)); + } + void addChild (std::string_view name, int32_t i) + { + scriptVar->addChild (name, new CScriptVar (static_cast (i))); + } + void addChild (std::string_view name, const std::string& value) + { + scriptVar->addChild (name, new CScriptVar (value)); + } + void addFunc (std::string_view name, std::function&& func) + { + scriptVar->addChild (name, createJSFunction (std::move (func))); + } + void addFunc (std::string_view name, std::function&& func, + const std::initializer_list& argNames) + { + scriptVar->addChild (name, createJSFunction (std::move (func), argNames)); + } + +protected: + CScriptVar* scriptVar {nullptr}; +}; + +//------------------------------------------------------------------------ +} // ScriptingInternal +} // VSTGUI diff --git a/vstgui/uidescription-scripting/detail/uidescscriptobject.h b/vstgui/uidescription-scripting/detail/uidescscriptobject.h new file mode 100644 index 000000000..b89fcd286 --- /dev/null +++ b/vstgui/uidescription-scripting/detail/uidescscriptobject.h @@ -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; + 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 "); + } + std::string name = param->getString (); + auto tag = desc->getTagForName (name.data ()); + var->setReturnVar (new CScriptVar (static_cast (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 (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 diff --git a/vstgui/uidescription-scripting/detail/viewscriptobject.cpp b/vstgui/uidescription-scripting/detail/viewscriptobject.cpp new file mode 100644 index 000000000..09f456634 --- /dev/null +++ b/vstgui/uidescription-scripting/detail/viewscriptobject.cpp @@ -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 (), value->getString ()); + 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 (), 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 ()); + 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).take ()); + }); + 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 (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; + if constexpr (std::is_same_v) + var->getReturnVar ()->setInt (value); + else if constexpr (std::is_same_v) + var->getReturnVar ()->setDouble (value); + else if constexpr (std::is_same_v) + var->getReturnVar ()->setString (value); + else if constexpr (std::is_same_v) + var->getReturnVar ()->setUndefined (); + }, + value); + }, + {"name"}); + addFunc ("setControllerProperty"sv, + [view] (CScriptVar* var) { + auto viewController = getViewController (view, true); + auto controller = dynamic_cast (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 (); + auto result = controller->setProperty (view, name->getString (), propValue); + var->getReturnVar ()->setInt (result); + }, + {"name", "value"}); + if (auto control = dynamic_cast (view)) + { + addFunc ("setValue"sv, + [control] (CScriptVar* var) { + auto value = var->getParameter ("value"sv); + if (value->isNumeric ()) + { + auto oldValue = control->getValue (); + control->setValue (static_cast (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 (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 (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 diff --git a/vstgui/uidescription-scripting/detail/viewscriptobject.h b/vstgui/uidescription-scripting/detail/viewscriptobject.h new file mode 100644 index 000000000..917d3a8b0 --- /dev/null +++ b/vstgui/uidescription-scripting/detail/viewscriptobject.h @@ -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 + +//------------------------------------------------------------------------ +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>; + +//------------------------------------------------------------------------ +struct IViewScriptObjectContext +{ + virtual ~IViewScriptObjectContext () = default; + + virtual IUIDescription* getUIDescription () const = 0; + virtual ViewScriptObject* addView (CView* view) = 0; + virtual ViewScriptMap::iterator removeView (CView* view) = 0; + virtual bool evalScript (std::string_view script) noexcept = 0; + virtual TJS::CScriptVar* getRoot () const = 0; +}; + +//------------------------------------------------------------------------ +} // ScriptingInternal +} // VSTGUI diff --git a/vstgui/uidescription-scripting/uiscripting.cpp b/vstgui/uidescription-scripting/uiscripting.cpp index bf9e8923f..78dc61e97 100644 --- a/vstgui/uidescription-scripting/uiscripting.cpp +++ b/vstgui/uidescription-scripting/uiscripting.cpp @@ -3,28 +3,24 @@ // 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 "../uidescription/uiattributes.h" -#include "../uidescription/iviewfactory.h" #include "../uidescription/uiviewfactory.h" -#include "../uidescription/iviewcreator.h" -#include "../uidescription/uiviewcreator.h" #include "../uidescription/uidescriptionaddonregistry.h" -#include "../uidescription/detail/uiviewcreatorattributes.h" #include "../lib/iviewlistener.h" -#include "../lib/events.h" #include "../lib/cresourcedescription.h" #include "../lib/cvstguitimer.h" #include "../lib/platform/platformfactory.h" #include "../lib/platform/iplatformresourceinputstream.h" -#include "../lib/controls/ccontrol.h" -#include "../lib/cdrawcontext.h" #include "tiny-js/TinyJS.h" #include "tiny-js/TinyJS_Functions.h" #include "tiny-js/TinyJS_MathFunctions.h" -#include -#include #include #include @@ -60,59 +56,6 @@ class ScriptContext : public IScriptContext std::unique_ptr impl; }; -//------------------------------------------------------------------------ -struct ScriptAddChildScoped -{ - ScriptAddChildScoped (CScriptVar& var, const std::string& name, CScriptVar* obj) : var (var) - { - if ((link = var.findChild (name))) - { - oldVar = link->getVar (); - oldVar->addRef (); - 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 CScriptVar* createJSFunction (JSCallback&& proc) -{ - auto funcVar = new CScriptVar (TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION | SCRIPTVAR_NATIVE); - funcVar->setCallback (std::move (proc)); - return funcVar; -} - -//------------------------------------------------------------------------ -inline CScriptVar* createJSFunction (JSCallback&& proc, - const std::initializer_list& argNames) -{ - auto f = createJSFunction (std::move (proc)); - for (auto name : argNames) - f->addChildNoDup (name); - return f; -} - //------------------------------------------------------------------------ struct TimerScriptObject : CScriptVar { @@ -147,423 +90,6 @@ struct TimerScriptObject : CScriptVar CScriptVar* callback; }; -//------------------------------------------------------------------------ -struct ScriptObject -{ - ScriptObject () - { - scriptVar = new CScriptVar (); - scriptVar->addRef (); - } - 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->removeAllChildren (); - scriptVar->release (); - } - } - - CScriptVar* getVar () const { return scriptVar; } - CScriptVar* take () - { - auto var = scriptVar; - scriptVar = nullptr; - return var; - } - - void addChild (std::string_view name, ScriptObject&& obj) - { - auto var = obj.take (); - scriptVar->addChild (name, var); - } - void addChild (std::string_view name, double d) - { - scriptVar->addChild (name, new CScriptVar (d)); - } - void addChild (std::string_view name, int64_t i) - { - scriptVar->addChild (name, new CScriptVar (i)); - } - void addChild (std::string_view name, int32_t i) - { - scriptVar->addChild (name, new CScriptVar (static_cast (i))); - } - void addChild (std::string_view name, const std::string& value) - { - scriptVar->addChild (name, new CScriptVar (value)); - } - void addFunc (std::string_view name, std::function&& func) - { - scriptVar->addChild (name, createJSFunction (std::move (func))); - } - void addFunc (std::string_view name, std::function&& func, - const std::initializer_list& argNames) - { - scriptVar->addChild (name, createJSFunction (std::move (func), argNames)); - } - -protected: - CScriptVar* scriptVar {nullptr}; -}; - -//------------------------------------------------------------------------ -inline ScriptObject makeScriptRect (const CRect& rect) -{ - 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); - return obj; -} - -//------------------------------------------------------------------------ -inline ScriptObject makeScriptPoint (const CPoint& point) -{ - ScriptObject obj; - obj.addChild ("x"sv, point.x); - obj.addChild ("y"sv, point.y); - return obj; -} - -//------------------------------------------------------------------------ -inline CPoint fromScriptPoint (CScriptVar& var) -{ - CPoint result {}; - if (auto xVar = var.findChild ("x"sv)) - result.x = xVar->getVar ()->getDouble (); - else - throw CScriptException ("Not a point object, missing 'x' member"); - if (auto yVar = var.findChild ("y"sv)) - result.y = yVar->getVar ()->getDouble (); - else - throw CScriptException ("Not a point object, missing 'y' member"); - return result; -} - -//------------------------------------------------------------------------ -inline CRect fromScriptRect (CScriptVar& var) -{ - CRect result {}; - auto leftVar = var.findChild ("left"); - auto topVar = var.findChild ("top"); - auto rightVar = var.findChild ("right"); - auto bottomVar = var.findChild ("bottom"); - if (!leftVar || !topVar || !rightVar || !bottomVar) - throw 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) -{ - ScriptObject obj; - if (auto modifierEvent = asModifierEvent (event)) - { - ScriptObject mod; - mod.addChild ("shift"sv, modifierEvent->modifiers.has (ModifierKey::Shift)); - mod.addChild ("alt"sv, modifierEvent->modifiers.has (ModifierKey::Alt)); - mod.addChild ("control"sv, modifierEvent->modifiers.has (ModifierKey::Control)); - mod.addChild ("super"sv, modifierEvent->modifiers.has (ModifierKey::Super)); - 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; - buttons.addChild ("left"sv, mouseEvent->buttonState.has (MouseButton::Left)); - buttons.addChild ("right"sv, mouseEvent->buttonState.has (MouseButton::Right)); - buttons.addChild ("middle"sv, mouseEvent->buttonState.has (MouseButton::Middle)); - 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); - wheel.addChild ( - "directionInvertedFromDevice"sv, - wheelEvent.flags & MouseWheelEvent::Flags::DirectionInvertedFromDevice ? true : false); - wheel.addChild ("preciceDelta"sv, - wheelEvent.flags & MouseWheelEvent::Flags::PreciseDeltas ? true : false); - obj.addChild ("mouseWheel"sv, std::move (wheel)); - } - if (auto keyEvent = asKeyboardEvent (event)) - { - ScriptObject key; - key.addChild ("character"sv, static_cast (keyEvent->character)); - key.addChild ("virtual"sv, static_cast (keyEvent->virt)); - key.addChild ("isRepeat"sv, keyEvent->isRepeat); - } - obj.addChild ("consume"sv, 0); - return obj; -} - -//------------------------------------------------------------------------ -struct UIDescScriptObject : ScriptObject -{ - using StringList = std::list; - UIDescScriptObject () = default; - UIDescScriptObject (IUIDescription* desc, CTinyJS* scriptContext) - { - 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 "); - } - std::string name = param->getString (); - auto tag = desc->getTagForName (name.data ()); - var->setReturnVar (new CScriptVar (static_cast (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 (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 colorName : names) - { - array->addChild (std::to_string (index), new CScriptVar (*colorName)); - ++index; - } - return array; - } -}; - -//------------------------------------------------------------------------ -struct DrawContextObject : ScriptObject, - IScriptVarLifeTimeObserver -{ - DrawContextObject (); - - 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; - 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 - - CDrawContext* context {nullptr}; - IUIDescription* uiDesc {nullptr}; -}; - -//------------------------------------------------------------------------ -struct JavaScriptDrawable -{ - void onDraw (CDrawContext* context, const CRect& rect, const CRect& viewSize); - - void setup (ViewScriptObject* object); - -private: - ViewScriptObject* scriptObject {nullptr}; - DrawContextObject drawContext; -}; - -//------------------------------------------------------------------------ -struct JavaScriptDrawableView : CView, - JavaScriptDrawable -{ - using CView::CView; - - void drawRect (CDrawContext* context, const CRect& rect) override; -}; - -//------------------------------------------------------------------------ -struct JavaScriptDrawableControl : CControl, - JavaScriptDrawable -{ - using CControl::CControl; - - void draw (CDrawContext* pContext) override; - void drawRect (CDrawContext* context, const CRect& rect) override; - - CLASS_METHODS_NOCOPY (JavaScriptDrawableControl, CControl); -}; - -//------------------------------------------------------------------------ -struct JavaScriptDrawableViewCreator : ViewCreatorAdapter -{ - IdStringPtr getViewName () const override { return "JavaScriptDrawableView"; } - IdStringPtr getBaseViewName () const override { return UIViewCreator::kCView; } - CView* create (const UIAttributes& attributes, const IUIDescription* description) const override - { - return new JavaScriptDrawableView (CRect ()); - } -}; - -//------------------------------------------------------------------------ -struct JavaScriptDrawableControlCreator : ViewCreatorAdapter -{ - IdStringPtr getViewName () const override { return "JavaScriptDrawableControl"; } - IdStringPtr getBaseViewName () const override { return UIViewCreator::kCControl; } - CView* create (const UIAttributes& attributes, const IUIDescription* description) const override - { - return new JavaScriptDrawableControl (CRect ()); - } -}; - -struct IViewScriptObjectContext; - -//------------------------------------------------------------------------ -struct ViewScriptObject : ScriptObject, - 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>; - -//------------------------------------------------------------------------ -struct IViewScriptObjectContext -{ - virtual ~IViewScriptObjectContext () = default; - - virtual IUIDescription* getUIDescription () const = 0; - virtual ViewScriptObject* addView (CView* view) = 0; - virtual ViewScriptMap::iterator removeView (CView* view) = 0; - virtual bool evalScript (std::string_view script) noexcept = 0; - virtual CScriptVar* getRoot () const = 0; -}; - //------------------------------------------------------------------------ struct ScriptContext::Impl : ViewListenerAdapter, ViewEventListenerAdapter, @@ -1130,530 +656,6 @@ std::string ScriptContext::eval (std::string_view script) const return {}; } -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -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 (), value->getString ()); - 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 (), 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 ()); - 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).take ()); - }); - 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 (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; - if constexpr (std::is_same_v) - var->getReturnVar ()->setInt (value); - else if constexpr (std::is_same_v) - var->getReturnVar ()->setDouble (value); - else if constexpr (std::is_same_v) - var->getReturnVar ()->setString (value); - else if constexpr (std::is_same_v) - var->getReturnVar ()->setUndefined (); - }, - value); - }, - {"name"}); - addFunc ("setControllerProperty"sv, - [view] (CScriptVar* var) { - auto viewController = getViewController (view, true); - auto controller = dynamic_cast (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 (); - auto result = controller->setProperty (view, name->getString (), propValue); - var->getReturnVar ()->setInt (result); - }, - {"name", "value"}); - if (auto control = dynamic_cast (view)) - { - addFunc ("setValue"sv, - [control] (CScriptVar* var) { - auto value = var->getParameter ("value"sv); - if (value->isNumeric ()) - { - auto oldValue = control->getValue (); - control->setValue (static_cast (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 (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 (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); -} - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -DrawContextObject::DrawContextObject () -{ - // TODO: decide if this object should expose the same interface as "CanvasRenderingContext2D" - scriptVar->setLifeTimeObserver (this); -#if 0 -#else - addFunc ("drawLine"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto fromPoint = var->getParameter ("from"sv); - if (!fromPoint) - throw CScriptException ( - "Missing `from` argument in drawContext.drawLine(from, to);"); - auto toPoint = var->getParameter ("to"sv); - if (!toPoint) - throw CScriptException ( - "Missing `to` argument in drawContext.drawLine(from, to);"); - auto from = fromScriptPoint (*fromPoint); - auto to = fromScriptPoint (*toPoint); - context->drawLine (from, to); - }, - {"from", "to"}); - addFunc ("drawRect"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto rectVar = var->getParameter ("rect"sv); - if (!rectVar) - throw CScriptException ( - "Missing `rect` argument in drawContext.drawRect(rect, style);"); - auto styleVar = var->getParameter ("style"sv); - if (!styleVar) - throw CScriptException ( - "Missing `style` argument in drawContext.drawRect(rect, style);"); - auto rect = fromScriptRect (*rectVar); - CDrawStyle style {}; - if (styleVar->getString () == "stroked") - style = kDrawStroked; - else if (styleVar->getString () == "filled") - style = kDrawFilled; - else if (styleVar->getString () == "filledAndStroked") - style = kDrawFilledAndStroked; - context->drawRect (rect, style); - }, - {"rect", "style"}); - addFunc ("drawEllipse"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto rectVar = var->getParameter ("rect"sv); - if (!rectVar) - throw CScriptException ( - "Missing `rect` argument in drawContext.drawEllipse(rect, style);"); - auto styleVar = var->getParameter ("style"sv); - if (!styleVar) - throw CScriptException ( - "Missing `style` argument in drawContext.drawEllipse(rect, style);"); - auto rect = fromScriptRect (*rectVar); - CDrawStyle style {}; - if (styleVar->getString () == "stroked") - style = kDrawStroked; - else if (styleVar->getString () == "filled") - style = kDrawFilled; - else if (styleVar->getString () == "filledAndStroked") - style = kDrawFilledAndStroked; - context->drawEllipse (rect, style); - }, - {"rect", "style"}); - addFunc ("clearRect"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto rectVar = var->getParameter ("rect"sv); - if (!rectVar) - throw CScriptException ( - "Missing `rect` argument in drawContext.clearRect(rect);"); - auto rect = fromScriptRect (*rectVar); - context->clearRect (rect); - }, - {"rect", "style"}); - addFunc ("drawPolygon"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto pointsVar = var->getParameter ("points"sv); - if (!pointsVar) - throw CScriptException ( - "Missing `points` argument in drawContext.drawPolygon(points, style);"); - if (!pointsVar->isArray ()) - throw CScriptException ("`points` argument must be an array of points in " - "drawContext.drawPolygon(points, style);"); - auto styleVar = var->getParameter ("style"sv); - if (!styleVar) - throw CScriptException ( - "Missing `style` argument 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)); - } - CDrawStyle style {}; - if (styleVar->getString () == "stroked") - style = kDrawStroked; - else if (styleVar->getString () == "filled") - style = kDrawFilled; - else if (styleVar->getString () == "filledAndStroked") - style = kDrawFilledAndStroked; - context->drawPolygon (points, style); - }, - {"points", "style"}); - addFunc ("setClipRect"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto rectVar = var->getParameter ("rect"sv); - if (!rectVar) - throw CScriptException ( - "Missing `rect` argument in drawContext.setClipRect(rect);"); - auto rect = fromScriptRect (*rectVar); - context->setClipRect (rect); - }, - {"rect"}); -#if 1 // TODO: make a bitmap js object instead - addFunc ("drawBitmap"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto bitmapNameVar = var->getParameter ("bitmap"sv); - if (!bitmapNameVar) - throw CScriptException ( - "Missing `bitmap` argument in drawContext.drawBitmap(bitmap, destRect, " - "offsetPoint, alpha);"); - auto destRectVar = var->getParameter ("destRect"sv); - if (!destRectVar) - throw CScriptException ( - "Missing `destRect` argument in drawContext.drawBitmap(bitmap, destRect, " - "offsetPoint, alpha);"); - auto offsetPointVar = var->findChild ("offsetPoint"sv); - auto alphaVar = var->findChild ("alpha"sv); - auto bitmap = uiDesc->getBitmap (bitmapNameVar->getString ().data ()); - if (!bitmap) - throw CScriptException ("bitmap not found in uiDescription"); - auto destRect = fromScriptRect (*destRectVar); - auto offset = - offsetPointVar ? fromScriptPoint (*offsetPointVar->getVar ()) : CPoint (0, 0); - auto alpha = alphaVar ? alphaVar->getVar ()->getDouble () : 1.f; - context->drawBitmap (bitmap, destRect, offset, alpha); - }, - {"bitmap", "destRect"}); -#endif - addFunc ( - "drawString"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto stringVar = var->getParameter ("string"sv); - if (!stringVar) - throw CScriptException ( - "Missing `string` argument in drawContext.drawString(string, rect, align);"); - auto rectVar = var->getParameter ("rect"sv); - if (!rectVar) - throw CScriptException ( - "Missing `rect` argument in drawContext.drawString(string, rect, align);"); - auto alignVar = var->getParameter ("align"sv); - if (!alignVar) - throw CScriptException ( - "Missing `align` argument in drawContext.drawString(string, rect, align);"); - auto string = stringVar->getString ().data (); - auto rect = fromScriptRect (*rectVar); - CHoriTxtAlign align = kCenterText; - if (alignVar->getString () == "left") - align = kLeftText; - else if (alignVar->getString () == "center") - align = kCenterText; - else if (alignVar->getString () == "right") - align = kRightText; - else - throw CScriptException ( - "wrong `align` argument. expected 'left', 'center' or 'right'"); - context->drawString (string, rect, align, true); - }, - {"string", "rect", "align"}); - addFunc ("setFont"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto fontVar = var->getParameter ("font"sv); - if (!fontVar) - throw CScriptException ( - "Missing `font` argument in drawContext.setFont(font);"); - if (auto font = uiDesc->getFont (fontVar->getString ().data ())) - context->setFont (font); - }, - {"font"}); - addFunc ("setFontColor"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto colorVar = var->getParameter ("color"sv); - if (!colorVar) - throw CScriptException ( - "Missing `color` argument in drawContext.setFontColor(color);"); - CColor color {}; - UIViewCreator::stringToColor (&colorVar->getString (), color, uiDesc); - context->setFontColor (color); - }, - {"color"}); - addFunc ("setFillColor"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto colorVar = var->getParameter ("color"sv); - if (!colorVar) - throw CScriptException ( - "Missing `color` argument in drawContext.setFillColor(color);"); - CColor color {}; - UIViewCreator::stringToColor (&colorVar->getString (), color, uiDesc); - context->setFillColor (color); - }, - {"color"}); - addFunc ("setFrameColor"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto colorVar = var->getParameter ("color"sv); - if (!colorVar) - throw CScriptException ( - "Missing `color` argument in drawContext.setFrameColor(color);"); - CColor color {}; - if (!UIViewCreator::stringToColor (&colorVar->getString (), color, uiDesc)) - throw CScriptException ( - "Unknown `color` argument in drawContext.setFrameColor(color);"); - context->setFrameColor (color); - }, - {"color"}); - addFunc ("setLineWidth"sv, - [this] (CScriptVar* var) { - if (!context) - throw CScriptException ("Native context is missing!"); - auto widthVar = var->getParameter ("width"sv); - if (!widthVar) - throw CScriptException ( - "Missing `width` argument in drawContext.setLineWidth(width);"); - context->setLineWidth (widthVar->getDouble ()); - }, - {"width"}); -#endif -} - -//------------------------------------------------------------------------ -void DrawContextObject::setDrawContext (CDrawContext* inContext, IUIDescription* inUIDesc) -{ - context = inContext; - uiDesc = inUIDesc; -} - -//------------------------------------------------------------------------ -void DrawContextObject::onDestroy (CScriptVar* v) -{ - v->setLifeTimeObserver (nullptr); - scriptVar = nullptr; -} - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -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 rectObj = makeScriptRect (rect); - auto scriptRoot = scriptContext->getRoot (); - ScriptAddChildScoped scs (*scriptRoot, "view", scriptObject->getVar ()); - ScriptAddChildScoped scs2 (*scriptRoot, "context", drawContext.getVar ()); - ScriptAddChildScoped scs3 (*scriptRoot, "rect", rectObj.getVar ()); - scriptContext->evalScript ("view.draw(context, rect);"sv); - - drawContext.setDrawContext (nullptr, nullptr); - - context->restoreGlobalState (); -} - -//------------------------------------------------------------------------ -void JavaScriptDrawable::setup (ViewScriptObject* inObject) { scriptObject = inObject; } - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -void JavaScriptDrawableView::drawRect (CDrawContext* context, const CRect& rect) -{ - onDraw (context, rect, getViewSize ()); -} - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -void JavaScriptDrawableControl::draw (CDrawContext* context) { drawRect (context, getViewSize ()); } - -//------------------------------------------------------------------------ -void JavaScriptDrawableControl::drawRect (CDrawContext* context, const CRect& rect) -{ - onDraw (context, rect, getViewSize ()); -} - //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------