diff --git a/vstgui/lib/cgraphicspath.cpp b/vstgui/lib/cgraphicspath.cpp index f49d308da..0b14544e6 100644 --- a/vstgui/lib/cgraphicspath.cpp +++ b/vstgui/lib/cgraphicspath.cpp @@ -165,6 +165,11 @@ CGraphicsPath::CGraphicsPath (const PlatformGraphicsPathFactoryPtr& factory, { } +//------------------------------------------------------------------------ +CGraphicsPath::CGraphicsPath (const CGraphicsPath& p) : elements (p.elements), factory (p.factory) +{ +} + //----------------------------------------------------------------------------- CGraphicsPath::~CGraphicsPath () noexcept {} diff --git a/vstgui/lib/cgraphicspath.h b/vstgui/lib/cgraphicspath.h index ed4b4e252..da929d836 100644 --- a/vstgui/lib/cgraphicspath.h +++ b/vstgui/lib/cgraphicspath.h @@ -94,6 +94,7 @@ class CGraphicsPath : public AtomicReferenceCounted CGraphicsPath (const PlatformGraphicsPathFactoryPtr& factory, PlatformGraphicsPathPtr&& path = nullptr); + CGraphicsPath (const CGraphicsPath& p); ~CGraphicsPath () noexcept override; const PlatformGraphicsPathPtr& getPlatformPath (PlatformGraphicsPathFillMode fillMode); diff --git a/vstgui/uidescription-scripting/detail/drawcontextobject.cpp b/vstgui/uidescription-scripting/detail/drawcontextobject.cpp index 9a986f5ce..a59989e06 100644 --- a/vstgui/uidescription-scripting/detail/drawcontextobject.cpp +++ b/vstgui/uidescription-scripting/detail/drawcontextobject.cpp @@ -5,6 +5,7 @@ #include "drawcontextobject.h" #include "converters.h" #include "../../lib/cdrawcontext.h" +#include "../../lib/cgraphicspath.h" #include "../../uidescription/uidescription.h" #include "../../uidescription/uiviewcreator.h" @@ -16,6 +17,153 @@ using namespace std::literals; using namespace TJS; //------------------------------------------------------------------------ +static SharedPointer getGraphicsPath (CScriptVar* var, std::string_view varName, + std::string_view signature) +{ + auto pathVar = getArgument (var, varName, signature); + auto customData = pathVar->getCustomData (); + if (!customData.has_value ()) + throw CScriptException ("Variable is not a graphics path"); + try + { + auto path = std::any_cast> (customData); + return path; + } + catch (const std::bad_any_cast& e) + { + throw CScriptException ("Variable is not a graphics path"); + } + 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 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 (); +} + +//------------------------------------------------------------------------ +struct GraphicsPathScriptObject : ScriptObject +{ + GraphicsPathScriptObject (const SharedPointer& p) + { + scriptVar = new CScriptVar ("", SCRIPTVAR_OBJECT); + scriptVar->addRef (); + scriptVar->setCustomData (p); + + addFunc ("addArc"sv, [p] (auto var) { addArc (p, var); }, + {"rect"sv, "startAngle"sv, "endAngle"sv, "clockwise"sv}); + addFunc ("addEllipse"sv, [p] (auto var) { addEllipse (p, var); }, {"rect"sv}); + addFunc ("addRect"sv, [p] (auto var) { addRect (p, var); }, {"rect"sv}); + addFunc ("addLine"sv, [p] (auto var) { addLine (p, var); }, {"to"sv}); + addFunc ("addBezierCurve"sv, [p] (auto var) { addBezierCurve (p, var); }, + {"control1"sv, "control2"sv, "end"sv}); + addFunc ("beginSubpath"sv, [p] (auto var) { beginSubpath (p, var); }, {"start"sv}); + addFunc ("closeSubpath"sv, [p] (auto var) { closeSubpath (p); }); + addFunc ("addRoundRect"sv, [p] (auto var) { addRoundRect (p, var); }, + {"rect"sv, "radius"sv}); + } + + static void addArc (const SharedPointer& 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& 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& 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& 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& 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& 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& path) + { + static constexpr auto signature = "path.closeSubpath();"sv; + path->closeSubpath (); + } + + static void addRoundRect (const SharedPointer& 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); + } +}; + //------------------------------------------------------------------------ struct DrawContextObject::Impl { @@ -38,36 +186,13 @@ struct DrawContextObject::Impl globalStatesStored = 0; } - CScriptVar* getArgument (CScriptVar* var, std::string_view argName, - std::string_view funcSignature) const - { - auto child = var->findChild (argName); - auto result = child ? child->getVar () : nullptr; - if (!result || result->isUndefined ()) - { - string s ("Missing `"); - s.append (argName); - s.append ("` argument in "); - s.append (funcSignature); - throw CScriptException (std::move (s)); - } - return result; - } - - CScriptVar* getOptionalArgument (CScriptVar* var, std::string_view argName) const - { - if (auto child = var->findChild (argName)) - return child->getVar (); - return nullptr; - } - void checkContextOrThrow () const { if (!context) throw CScriptException ("Native context is missing!"); } - CDrawStyle getDrawStyle (CScriptVar*& styleVar) const + CDrawStyle getDrawStyle (CScriptVar* styleVar) const { CDrawStyle style {}; auto string = styleVar->getString (); @@ -82,6 +207,56 @@ struct DrawContextObject::Impl 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.getVar ()); + } + } + + 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.getVar ()); + } + } + + 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; + context->drawGraphicsPath (path, mode); + } + void drawLine (CScriptVar* var) const { static constexpr auto signature = "drawContext.drawLine(from, to);"sv; @@ -96,9 +271,8 @@ struct DrawContextObject::Impl { static constexpr auto signature = "drawContext.drawRect(rect, style);"sv; checkContextOrThrow (); - auto rectVar = getArgument (var, "rect"sv, signature); + auto rect = getRect (var, "rect"sv, signature); auto styleVar = getArgument (var, "style"sv, signature); - auto rect = fromScriptRect (*rectVar); auto style = getDrawStyle (styleVar); context->drawRect (rect, style); } @@ -106,9 +280,8 @@ struct DrawContextObject::Impl { static constexpr auto signature = "drawContext.drawEllipse(rect, style);"sv; checkContextOrThrow (); - auto rectVar = getArgument (var, "rect"sv, signature); + auto rect = getRect (var, "rect"sv, signature); auto styleVar = getArgument (var, "style"sv, signature); - auto rect = fromScriptRect (*rectVar); auto style = getDrawStyle (styleVar); context->drawEllipse (rect, style); } @@ -117,17 +290,10 @@ struct DrawContextObject::Impl static constexpr auto signature = "drawContext.drawArc(rect, startAngle, endAngle, style);"sv; checkContextOrThrow (); - auto rectVar = getArgument (var, "rect"sv, signature); - auto startAngleVar = getArgument (var, "startAngle"sv, signature); - auto endAngleVar = getArgument (var, "endAngle"sv, signature); + auto rect = getRect (var, "rect"sv, signature); + auto startAngle = getDouble (var, "startAngle"sv, signature); + auto endAngle = getDouble (var, "endAngle"sv, signature); auto styleVar = getArgument (var, "style"sv, signature); - auto rect = fromScriptRect (*rectVar); - if (!startAngleVar->isNumeric ()) - throw CScriptException ("`startAngle` must be a number."); - if (!endAngleVar->isNumeric ()) - throw CScriptException ("`endAngle` must be a number."); - auto startAngle = static_cast (startAngleVar->getDouble ()); - auto endAngle = static_cast (endAngleVar->getDouble ()); auto style = getDrawStyle (styleVar); context->drawArc (rect, startAngle, endAngle, style); } @@ -135,8 +301,7 @@ struct DrawContextObject::Impl { static constexpr auto signature = "drawContext.clearRect(rect);"sv; checkContextOrThrow (); - auto rectVar = getArgument (var, "rect"sv, signature); - auto rect = fromScriptRect (*rectVar); + auto rect = getRect (var, "rect"sv, signature); context->clearRect (rect); } void drawPolygon (CScriptVar* var) const @@ -163,8 +328,7 @@ struct DrawContextObject::Impl { static constexpr auto signature = "drawContext.setClipRect(rect);"sv; checkContextOrThrow (); - auto rectVar = getArgument (var, "rect"sv, signature); - auto rect = fromScriptRect (*rectVar); + auto rect = getRect (var, "rect"sv, signature); context->setClipRect (rect); } void drawBitmap (CScriptVar* var) const @@ -173,13 +337,12 @@ struct DrawContextObject::Impl "drawContext.drawBitmap(name, destRect, offsetPoint?, alpha?);"sv; checkContextOrThrow (); auto nameVar = getArgument (var, "name"sv, signature); - auto destRectVar = getArgument (var, "destRect"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 destRect = fromScriptRect (*destRectVar); auto offset = offsetPointVar ? fromScriptPoint (*offsetPointVar) : CPoint (0, 0); auto alpha = static_cast (alphaVar ? alphaVar->getDouble () : 1.); context->drawBitmap (bitmap, destRect, offset, alpha); @@ -189,10 +352,9 @@ struct DrawContextObject::Impl static constexpr auto signature = "drawContext.drawString(string, rect, align?);"sv; checkContextOrThrow (); auto stringVar = getArgument (var, "string"sv, signature); - auto rectVar = getArgument (var, "rect"sv, signature); + auto rect = getRect (var, "rect"sv, signature); auto alignVar = getOptionalArgument (var, "align?"sv); auto string = stringVar->getString ().data (); - auto rect = fromScriptRect (*rectVar); CHoriTxtAlign align = kCenterText; if (!alignVar || alignVar->isUndefined ()) align = kCenterText; @@ -315,10 +477,8 @@ struct DrawContextObject::Impl { static constexpr auto signature = "drawContext.setGlobalAlpha(alpha);"sv; checkContextOrThrow (); - auto alphaVar = getArgument (var, "alpha"sv, signature); - if (!alphaVar->isNumeric ()) - throw CScriptException ("alpha must be a number."); - context->setGlobalAlpha (alphaVar->getDouble ()); + auto alpha = getDouble (var, "alpha"sv, signature); + context->setGlobalAlpha (alpha); } void saveGlobalState () const { @@ -340,6 +500,18 @@ struct DrawContextObject::Impl 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`"); + } }; //------------------------------------------------------------------------ @@ -350,6 +522,9 @@ DrawContextObject::DrawContextObject () impl = std::make_unique (); scriptVar->setLifeTimeObserver (this); addFunc ("clearRect"sv, [this] (auto var) { impl->clearRect (var); }, {"rect"sv, "style"sv}); + addFunc ("createRoundGraphicsPath"sv, + [this] (auto var) { impl->createRoundGraphicsPath (var); }, {"rect"sv, "radius"sv}); + addFunc ("createGraphicsPath"sv, [this] (auto var) { impl->createGraphicsPath (var); }); 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}); @@ -357,6 +532,8 @@ DrawContextObject::DrawContextObject () {"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}); @@ -374,6 +551,7 @@ DrawContextObject::DrawContextObject () 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}); } //------------------------------------------------------------------------ diff --git a/vstgui/uidescription-scripting/detail/scriptobject.h b/vstgui/uidescription-scripting/detail/scriptobject.h index c5c5f67a3..ac9216478 100644 --- a/vstgui/uidescription-scripting/detail/scriptobject.h +++ b/vstgui/uidescription-scripting/detail/scriptobject.h @@ -67,6 +67,31 @@ inline TJS::CScriptVar* createJSFunction (TJS::JSCallback&& proc, 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 { diff --git a/vstgui/uidescription-scripting/tiny-js/TinyJS.cpp b/vstgui/uidescription-scripting/tiny-js/TinyJS.cpp index adf3edf92..7e14f9072 100644 --- a/vstgui/uidescription-scripting/tiny-js/TinyJS.cpp +++ b/vstgui/uidescription-scripting/tiny-js/TinyJS.cpp @@ -1437,6 +1437,7 @@ CScriptVar* CScriptVar::mathsOp (CScriptVar* b, int op) void CScriptVar::copySimpleData (CScriptVar* val) { variant = val->variant; + customData = val->customData; flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK); } diff --git a/vstgui/uidescription-scripting/tiny-js/TinyJS.h b/vstgui/uidescription-scripting/tiny-js/TinyJS.h index 4ae098bff..3f7666440 100644 --- a/vstgui/uidescription-scripting/tiny-js/TinyJS.h +++ b/vstgui/uidescription-scripting/tiny-js/TinyJS.h @@ -37,6 +37,7 @@ #include #include #include +#include #ifndef TRACE #define TRACE printf @@ -403,6 +404,9 @@ class CScriptVar 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); @@ -415,6 +419,7 @@ class CScriptVar /** the flags determine the type of the variable - int/double/string/etc */ int flags {0}; std::variant variant; + std::any customData; string dataStr; /** Copy the basic data and flags from the variable given, with no