Skip to content

Commit

Permalink
add IScriptControllerExtension
Browse files Browse the repository at this point in the history
  • Loading branch information
scheffle committed Dec 20, 2023
1 parent 8ea60ee commit 74f173a
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 4 deletions.
2 changes: 1 addition & 1 deletion vstgui/standalone/examples/standalone/resource/test.uidesc
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
"opacity": "1",
"origin": "15, 200",
"row-height": "16",
"script": "// Hover Opacity Animation Script\n// This example script changes the opacity of the view\n// when the mouse enters or exits the view\n\n/* the default opacity of the view is stored in view.default_opacity */\nview.default_opacity = 0.6;\n\n/* the current opacity of the view is stored in view.opacity */\nview.opacity = view.default_opacity;\n\n/* the timer to change the opacity is stored in view.opacity_timer */\nview.opacity_timer = createTimer (view, 16, function (view) {\n\tview.opacity += view.opacity_change;\n\tif (view.opacity_change > 0)\n\t{\n\t\tif (view.opacity > 1)\n\t\t{\n\t\t\tview.opacity = 1;\n\t\t\tview.opacity_timer.stop();\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (view.opacity <= view.default_opacity)\n\t\t{\n\t\t\tview.opacity = view.default_opacity;\n\t\t\tview.opacity_timer.stop();\n\t\t}\n\t}\n\tview.setAttribute(\"opacity\", view.opacity);\n});\n\n/* we install a mouse enter listener\n when the mouse enters the view we start the opacity change timer \n*/\nview.onMouseEnter = function (view, event) {\n\tview.opacity_change = 0.075;\n\tview.opacity_timer.start();\n\tevent.consumed = true;\n};\n\n/* we also install a mouse exit listener\n when the mouse exits the view we start the opacity change timer again \n now with a negative opacity_change variable so that in the timer callback \n the opacity is going back to the default opacity \n*/\nview.onMouseExit = function (view, event) {\n\tview.opacity_change = -0.05;\n\tview.opacity_timer.start();\n\tevent.consumed = true;\n};\n\n/* we also install a view removed listener so that we can cleanup and stop the timer */\nview.onRemoved = function (view) {\n\t// cleanup, when the view is removed, stop the timer\n\tview.opacity_timer.stop();\n};\n\nview.setAttribute(\"mouse-enabled\", true);\nview.setAttribute(\"opacity\", view.opacity);\n\n",
"script": "// Hover Opacity Animation Script\n// This example script changes the opacity of the view\n// when the mouse enters or exits the view\n\n/* the default opacity of the view is stored in view.default_opacity */\nview.default_opacity = 0.6;\n\n/* the current opacity of the view is stored in view.opacity */\nview.opacity = view.default_opacity;\n\n/* the timer to change the opacity is stored in view.opacity_timer */\nview.opacity_timer = createTimer (view, 16, function (view) {\n\tview.opacity += view.opacity_change;\n\tif (view.opacity_change > 0)\n\t{\n\t\tif (view.opacity > 1)\n\t\t{\n\t\t\tview.opacity = 1;\n\t\t\tview.opacity_timer.stop();\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (view.opacity <= view.default_opacity)\n\t\t{\n\t\t\tview.opacity = view.default_opacity;\n\t\t\tview.opacity_timer.stop();\n\t\t}\n\t}\n\tview.setAttribute(\"opacity\", view.opacity);\n});\n\n/* we install a mouse enter listener\n when the mouse enters the view we start the opacity change timer \n*/\nview.onMouseEnter = function (view, event) {\n\tview.opacity_change = 0.075;\n\tview.opacity_timer.start();\n\tevent.consumed = true;\n\tvar val = view.getControllerProperty (\"integer\");\n\tlog (val);\n};\n\n/* we also install a mouse exit listener\n when the mouse exits the view we start the opacity change timer again \n now with a negative opacity_change variable so that in the timer callback \n the opacity is going back to the default opacity \n*/\nview.onMouseExit = function (view, event) {\n\tview.opacity_change = -0.05;\n\tview.opacity_timer.start();\n\tevent.consumed = true;\n};\n\n/* we also install a view removed listener so that we can cleanup and stop the timer */\nview.onRemoved = function (view) {\n\t// cleanup, when the view is removed, stop the timer\n\tview.opacity_timer.stop();\n};\n\nview.setAttribute(\"mouse-enabled\", true);\nview.setAttribute(\"opacity\", view.opacity);\n\n",
"size": "110, 144",
"style-hover": "true",
"sub-controller": "WeekdaysController",
Expand Down
29 changes: 29 additions & 0 deletions vstgui/standalone/examples/standalone/source/testappdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#ifdef VSTGUI_UISCRIPTING
#include "vstgui/uidescription-scripting/uiscripting.h"
#include <iostream>
#endif

#include <memory>
Expand Down Expand Up @@ -157,6 +158,10 @@ class WeekdaysListConfigurator : public StaticListControlConfigurator

//------------------------------------------------------------------------
class WeekdaysController : public DelegationController
#ifdef VSTGUI_UISCRIPTING
,
public ScriptControllerExtensionAdapter
#endif
{
public:
WeekdaysController (IController* parent) : DelegationController (parent) {}
Expand All @@ -175,6 +180,30 @@ class WeekdaysController : public DelegationController
}
return controller->verifyView (view, attributes, description);
}
#ifdef VSTGUI_UISCRIPTING
bool getProperty (CView* view, std::string_view name, PropertyValue& value) const override
{
using namespace std::literals;
if (name == "integer"sv)
value = 24;
else if (name == "double"sv)
value = 13.3333;
else if (name == "string"sv)
value = "Hello World"s;
else
value = nullptr;
return true;
}
bool setProperty (CView* view, std::string_view name, const PropertyValue& value) override
{
std::visit ([] (auto&& v) { std::cout << v << '\n'; }, value);
return true;
}
std::optional<std::string> verifyScript (CView* view, const std::string& script) override
{
return {script};
}
#endif
};

//------------------------------------------------------------------------
Expand Down
70 changes: 67 additions & 3 deletions vstgui/uidescription-scripting/uiscripting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,63 @@ ViewScriptObject::ViewScriptObject (CView* view, IViewScriptObjectContext* conte
var->setReturnVar (obj->getVar ());
obj->getVar ()->unref ();
}));
scriptVar->addChild ("getControllerProperty"sv,
createJSFunction (
[view] (CScriptVar* var) {
auto viewController = getViewController (view, true);
auto controller =
dynamic_cast<IScriptControllerExtension*> (viewController);
auto name = var->getParameter ("name"sv);
if (!controller || !name)
{
var->getReturnVar ()->setUndefined ();
return;
}
IScriptControllerExtension::PropertyValue value;
if (!controller->getProperty (view, name->getString (), value))
{
var->getReturnVar ()->setUndefined ();
return;
}
std::visit (
[&] (auto&& value) {
using T = std::decay_t<decltype (value)>;
if constexpr (std::is_same_v<T, int64_t>)
var->getReturnVar ()->setInt (value);
else if constexpr (std::is_same_v<T, double>)
var->getReturnVar ()->setDouble (value);
else if constexpr (std::is_same_v<T, std::string>)
var->getReturnVar ()->setString (value);
else if constexpr (std::is_same_v<T, nullptr_t>)
var->getReturnVar ()->setUndefined ();
},
value);
},
{"name"}));
scriptVar->addChild (
"setControllerProperty"sv,
createJSFunction (
[view] (CScriptVar* var) {
auto viewController = getViewController (view, true);
auto controller = dynamic_cast<IScriptControllerExtension*> (viewController);
auto name = var->getParameter ("name"sv);
auto value = var->getParameter ("value"sv);
if (!controller || !name || !value || !(value->isNumeric () || value->isString ()))
{
var->getReturnVar ()->setUndefined ();
return;
}
IScriptControllerExtension::PropertyValue propValue;
if (value->isInt ())
propValue = value->getInt ();
else if (value->isDouble ())
propValue = value->getDouble ();
else if (value->isString ())
propValue = value->getString ();
auto result = controller->setProperty (view, name->getString (), propValue);
var->getReturnVar ()->setInt (result);
},
{"name", "value"}));
if (auto control = dynamic_cast<CControl*> (view))
{
scriptVar->addChild (
Expand Down Expand Up @@ -1069,10 +1126,17 @@ struct JavaScriptViewFactory : ViewFactoryDelegate
{
if (auto value = attributes.getAttributeValue (ScriptingInternal::kAttrScript))
{
view->setAttribute (scriptAttrID, static_cast<uint32_t> (value->size () + 1),
value->data ());
std::optional<std::string> verifiedScript;
if (auto scriptViewController =
dynamic_cast<IScriptControllerExtension*> (description->getController ()))
{
verifiedScript = scriptViewController->verifyScript (view, *value);
}
const auto& script = verifiedScript ? *verifiedScript : *value;
auto scriptSize = static_cast<uint32_t> (script.size () + 1);
view->setAttribute (scriptAttrID, scriptSize, script.data ());
if (!disabled)
scriptContext->onViewCreated (view, *value);
scriptContext->onViewCreated (view, script);
}
return view;
}
Expand Down
59 changes: 59 additions & 0 deletions vstgui/uidescription-scripting/uiscripting.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include "../uidescription/iviewfactory.h"
#include "../uidescription/iuidescription.h"
#include "../uidescription/iuidescriptionaddon.h"
#include <functional>

//------------------------------------------------------------------------
namespace VSTGUI {

//------------------------------------------------------------------------
Expand Down Expand Up @@ -39,5 +41,62 @@ class UIScripting : public UIDescriptionAddOnAdapter
friend std::unique_ptr<UIScripting> std::make_unique<UIScripting> ();
};

//------------------------------------------------------------------------
/** extends IController
*
* The script controller extension adds script related methods to the controller.
*
* It can alter the scripts for the views if needed and scripts can get and set properties.
*/
struct IScriptControllerExtension
{
/** a property value is either an integer, double, string or undefined (nullptr_t) */
using PropertyValue = std::variant<nullptr_t, int64_t, double, std::string>;

/** verify the script for a view
*
* called before the script is executed
*
* @param view the view
* @param script the script
* @return optional new script. if the optional is empty the original script is used.
*/
virtual std::optional<std::string> verifyScript (CView* view, const std::string& script) = 0;

/** get a property
*
* called from a script
*
* if the propery exists, the value should be set and the return value should be true.
* Otherwise return false.
*
* @param view the view
* @param name the name of the property
* @param value the property value
* @return true on success.
*/
virtual bool getProperty (CView* view, std::string_view name, PropertyValue& value) const = 0;

/** set a property
*
* called from a script
*
* @param view the view
* @param name the name of the property
* @param value the value of the property
* @return true on success.
*/
virtual bool setProperty (CView* view, std::string_view name, const PropertyValue& value) = 0;
};

//------------------------------------------------------------------------
/** adapter for IScriptControllerExtension */
struct ScriptControllerExtensionAdapter : IScriptControllerExtension
{
std::optional<std::string> verifyScript (CView*, const std::string&) override { return {}; }
bool getProperty (CView*, std::string_view, PropertyValue&) const override { return false; }
bool setProperty (CView*, std::string_view, const PropertyValue&) override { return false; }
};

//------------------------------------------------------------------------
} // VSTGUI
9 changes: 9 additions & 0 deletions vstgui/uidescription-scripting/uiscripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ The following methods are implemented on that object:
- `getTagForName(String: name) -> Integer`
- `lookupTagName(Integer: tag) -> String`

### Interaction with c++ code

To interact with the c++ code extend the IController with IScriptControllerExtension
and implement its methods and call from the script the view methods `getControllerProperty` or `setControllerProperty`.
A property can either be an integer, floating point, string or undefined.

### View methods and properties

Expand All @@ -73,6 +78,10 @@ The following methods are implemented on that object:
- get the attribute with name "key"
- `setAttribute(String: key, String: value) -> Void`
- set the attribute value with name "key" to "value"
- `getControllerProperty(String: name, Property: value) -> Integer`
- set a controller property. returns true if succeeded
- `setControllerProperty(String: name) -> Property`
- get a controller property. returns a property

For example to set the opacity attribute of a view write:

Expand Down

0 comments on commit 74f173a

Please sign in to comment.