diff --git a/vstgui/uidescription-scripting/detail/drawable.cpp b/vstgui/uidescription-scripting/detail/drawable.cpp index b31f35c6a..4840dae14 100644 --- a/vstgui/uidescription-scripting/detail/drawable.cpp +++ b/vstgui/uidescription-scripting/detail/drawable.cpp @@ -52,9 +52,9 @@ void JavaScriptDrawable::onDraw (CDrawContext* context, const CRect& rect, const auto rectVar = makeScriptRect (rect); auto scriptRoot = scriptContext->getRoot (); - ScriptAddChildScoped scs (*scriptRoot, "view", *scriptObject); - ScriptAddChildScoped scs2 (*scriptRoot, "context", drawContext); - ScriptAddChildScoped scs3 (*scriptRoot, "rect", rectVar); + ScriptAddChildScoped scs (*scriptRoot, "view"sv, *scriptObject); + ScriptAddChildScoped scs2 (*scriptRoot, "context"sv, drawContext); + ScriptAddChildScoped scs3 (*scriptRoot, "rect"sv, rectVar); scriptContext->evalScript ("view.draw(context, rect);"sv); drawContext.setDrawContext (nullptr, nullptr); @@ -72,8 +72,11 @@ bool JavaScriptDrawable::onDrawFocusOnTop () if (!scriptContext) return false; + if (scriptObject->getVar ()->findChild ("drawFocusOnTop"sv) == nullptr) + return false; + auto scriptRoot = scriptContext->getRoot (); - ScriptAddChildScoped scs (*scriptRoot, "view", *scriptObject); + ScriptAddChildScoped scs (*scriptRoot, "view"sv, *scriptObject); auto boolResult = scriptContext->evalScript ("view.drawFocusOnTop();"sv); return boolResult->isNumeric () ? boolResult->getInt () : false; } @@ -84,13 +87,22 @@ bool JavaScriptDrawable::onGetFocusPath (CGraphicsPath& outPath, CCoord focusWid { if (auto scriptContext = scriptObject->getContext ()) { + if (scriptObject->getVar ()->findChild ("getFocusPath"sv) == nullptr) + { + auto r = viewSize; + outPath.addRect (r); + r.extend (focusWidth, focusWidth); + outPath.addRect (r); + return true; + } + auto scriptRoot = scriptContext->getRoot (); auto path = makeOwned (outPath); ScriptObject focusWidthVar; focusWidthVar->setDouble (focusWidth); - ScriptAddChildScoped scs (*scriptRoot, "view", *scriptObject); - ScriptAddChildScoped scs2 (*scriptRoot, "path", makeGraphicsPathScriptObject (path)); - ScriptAddChildScoped scs3 (*scriptRoot, "focusWidth", focusWidthVar); + ScriptAddChildScoped scs (*scriptRoot, "view"sv, *scriptObject); + ScriptAddChildScoped scs2 (*scriptRoot, "path"sv, makeGraphicsPathScriptObject (path)); + ScriptAddChildScoped scs3 (*scriptRoot, "focusWidth"sv, focusWidthVar); auto boolResult = scriptContext->evalScript ("view.getFocusPath(path, focusWidth);"sv); if (boolResult->isNumeric ()) { @@ -116,12 +128,19 @@ void JavaScriptDrawableView::drawRect (CDrawContext* context, const CRect& rect) } //------------------------------------------------------------------------ -bool JavaScriptDrawableView::drawFocusOnTop () { return onDrawFocusOnTop (); } +bool JavaScriptDrawableView::drawFocusOnTop () +{ + if (wantsFocus ()) + return onDrawFocusOnTop (); + return false; +} //------------------------------------------------------------------------ bool JavaScriptDrawableView::getFocusPath (CGraphicsPath& outPath) { - return onGetFocusPath (outPath, getFrame ()->getFocusWidth (), getViewSize ()); + if (wantsFocus ()) + return onGetFocusPath (outPath, getFrame ()->getFocusWidth (), getViewSize ()); + return false; } //------------------------------------------------------------------------ @@ -136,12 +155,19 @@ void JavaScriptDrawableControl::drawRect (CDrawContext* context, const CRect& re } //------------------------------------------------------------------------ -bool JavaScriptDrawableControl::drawFocusOnTop () { return onDrawFocusOnTop (); } +bool JavaScriptDrawableControl::drawFocusOnTop () +{ + if (wantsFocus ()) + return onDrawFocusOnTop (); + return false; +} //------------------------------------------------------------------------ bool JavaScriptDrawableControl::getFocusPath (CGraphicsPath& outPath) { - return onGetFocusPath (outPath, getFrame ()->getFocusWidth (), getViewSize ()); + if (wantsFocus ()) + return onGetFocusPath (outPath, getFrame ()->getFocusWidth (), getViewSize ()); + return false; } //------------------------------------------------------------------------ diff --git a/vstgui/uidescription-scripting/uiscripting.cpp b/vstgui/uidescription-scripting/uiscripting.cpp index f9509c810..1fab48cd8 100644 --- a/vstgui/uidescription-scripting/uiscripting.cpp +++ b/vstgui/uidescription-scripting/uiscripting.cpp @@ -143,8 +143,9 @@ struct ScriptContext::Impl : ViewListenerAdapter, auto timerObj = TimerScriptObject ( fireTime->getInt (), callback->deepCopy (), [this, context] (auto callback) { using namespace ScriptingInternal; - ScriptAddChildScoped scs (*jsContext->getRoot (), "timerContext", context); - return evalScript (callback, "timerCallback (timerContext); ", + ScriptAddChildScoped scs (*jsContext->getRoot (), "timerContext"sv, + context); + return evalScript (callback, "timerCallback (timerContext);"sv, "timerCallback"); }); var->setReturnVar (timerObj); @@ -176,19 +177,19 @@ struct ScriptContext::Impl : ViewListenerAdapter, auto childScriptObject = viewScriptMap.find (child); if (childScriptObject != viewScriptMap.end ()) { - ScriptAddChildScoped scs (*jsContext->getRoot (), "child", + ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv, childScriptObject->second->getVar ()); - ScriptAddChildScoped scs2 (*jsContext->getRoot (), "context", context); - evalScript (callback, "callback (child, context); ", "callback"); + ScriptAddChildScoped scs2 (*jsContext->getRoot (), "context"sv, context); + evalScript (callback, "callback (child, context);"sv, "callback"); } else { auto scriptObj = addView (child, std::make_unique (child, this)); - ScriptAddChildScoped scs (*jsContext->getRoot (), "child", + ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv, scriptObj->getVar ()); - ScriptAddChildScoped scs2 (*jsContext->getRoot (), "context", context); - evalScript (callback, "callback (child, context); ", "callback"); + ScriptAddChildScoped scs2 (*jsContext->getRoot (), "context"sv, context); + evalScript (callback, "callback (child, context);"sv, "callback"); } }); }); @@ -252,12 +253,12 @@ struct ScriptContext::Impl : ViewListenerAdapter, try { auto result = jsContext->evaluateComplex (script); +#if 0 // DEBUG if (result.getVar () && !result.getVar ()->isUndefined ()) { -#if DEBUG DebugPrint ("%s\n", result.getVar ()->getString ().data ()); -#endif } +#endif return result.getVar (); } catch (const CScriptException& exc) @@ -316,7 +317,7 @@ struct ScriptContext::Impl : ViewListenerAdapter, callWhenScriptHasFunction (view, "onSizeChanged"sv, [&] (auto This, auto& obj) { auto newSize = view->getViewSize (); ScriptObject newSizeObject = ScriptingInternal::makeScriptRect (newSize); - ScriptAddChildScoped scs (*jsContext->getRoot (), "newSize", newSizeObject); + ScriptAddChildScoped scs (*jsContext->getRoot (), "newSize"sv, newSizeObject); static constexpr auto script = R"(view.onSizeChanged(view, newSize);)"sv; This->evalScript (obj->getVar (), script); }); @@ -354,14 +355,14 @@ struct ScriptContext::Impl : ViewListenerAdapter, auto childScriptObject = viewScriptMap.find (view); if (childScriptObject != viewScriptMap.end ()) { - ScriptAddChildScoped scs (*jsContext->getRoot (), "child", + ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv, childScriptObject->second->getVar ()); This->evalScript (obj->getVar (), script); } else { auto scriptObj = addView (view, std::make_unique (view, this)); - ScriptAddChildScoped scs (*jsContext->getRoot (), "child", scriptObj->getVar ()); + ScriptAddChildScoped scs (*jsContext->getRoot (), "child"sv, scriptObj->getVar ()); This->evalScript (obj->getVar (), script); } }); @@ -391,7 +392,7 @@ struct ScriptContext::Impl : ViewListenerAdapter, void callEventFunction (CScriptVar* var, Event& event, std::string_view script) noexcept { auto scriptEvent = ScriptingInternal::makeScriptEvent (event); - ScriptAddChildScoped scs (*jsContext->getRoot (), "event", scriptEvent); + ScriptAddChildScoped scs (*jsContext->getRoot (), "event"sv, scriptEvent); evalScript (var, script); checkEventConsumed (scriptEvent, event); } @@ -493,7 +494,7 @@ struct ScriptContext::Impl : ViewListenerAdapter, callWhenScriptHasFunction (control, "onValueChanged"sv, [&] (auto This, auto& obj) { ScriptObject controlValue; controlValue->setDouble (control->getValue ()); - ScriptAddChildScoped scs (*jsContext->getRoot (), "value", controlValue); + ScriptAddChildScoped scs (*jsContext->getRoot (), "value"sv, controlValue); static constexpr auto script = R"(view.onValueChanged(view, value);)"sv; This->evalScript (obj->getVar (), script); }); diff --git a/vstgui/uidescription-scripting/uiscripting.md b/vstgui/uidescription-scripting/uiscripting.md index 158a398a2..00bc063e4 100644 --- a/vstgui/uidescription-scripting/uiscripting.md +++ b/vstgui/uidescription-scripting/uiscripting.md @@ -16,6 +16,43 @@ The library needs to be initialized once at runtime before any UIDescription obj This is done with a call to `VSTGUI::UIScripting::init ();`. You need to include `"vstgui/uiscripting/uiscripting.h"` for this symbol. +The init function accepts two optional parameters. The first parameter is a callback +function that will be called whenever an exception occurs within a script, allowing you to +handle any errors or exceptions that may arise. The second parameter is also a function +that is invoked when a script is loaded, providing its name as input. This feature can be +used, for instance, to load scripts directly from your source repository instead of the +default location in the resource folder of your plug-in or application. + +```cpp +UIScripting::ReadScriptContentsFunc loadScriptFromRepositoryPath = {}; +#if DEBUG +// in Debug mode, we want to load the scripts from the repository instead of from the app +// resource folder as the scripts in the app resource folder are only synchronized when we build +// the app and not in-between. +loadScriptFromRepositoryPath = [] (auto filename) -> std::string { + std::filesystem::path path (__FILE__); + if (!path.empty ()) + { + path = path.parent_path ().parent_path (); + path.append ("resource"); + path.append ("scripts"); + path.append (filename); + if (std::filesystem::exists (path)) + { + std::ifstream f (path, std::ios::in | std::ios::binary); + const auto sz = std::filesystem::file_size (path); + std::string result (sz, '\0'); + f.read (result.data (), sz); + return result; + } + } + return {}; +}; +#endif + +UIScripting::init ({}, loadScriptFromRepositoryPath); +``` + All scripts of one UIDescription object will be executed in the same JavaScript context. So you can access global variables from all your scripts if needed.