All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
Operators

It is a common situation that a Widget needs some extra behaviour that would require deriving a new widget class and overriding some of the virtual functions.

For example, one would want to limit the size of a widget to a certain maximum, or make the widget rotate towards the hands of the user. It is also very common that the same behaviour is required over and over again.

Operators provide a way to enrich widget behaviour without having to create new widget classes. This makes it a very flexible way to add functionality to any existing widget. This kind of behaviour-adding is known as the Decorator design pattern.

In many cases the new behaviour is related to animating some of the widgets attributes (location, scale, rotation, etc). For this purpose there is a special category of operators called Animators, which are explained more thoroughly in the Animations-section.

How Operators Work?

Operators always operate on a host MultiWidgets::Widget which is the target of the operations and the operator is attached to the widget it manipulates. The host widget assumes the ownership of the operator and makes sure the operator is deleted when the widget gets deleted.

All operators are derived from MultiWidgets::Operator class. Here are the two main functions of the class:

Both functions are by default empty and are overloaded in derived classes. The host widget is given as a reference which can therefore be targeted for the wanted behaviour. In most cases the operator logic is inside the update function, which is called after widgets motion for that frame has been updated. This way the operator can react immediately to new coordinates before they are rendered on the screen.

Adding Operators

Operators are added to widgets using the addOperator-method, which takes a shared pointer of the operator as an argument. Use std::make_shared to create the operator. The widget takes care of the instance after that, making sure it is cleaned up properly.

Some of the operators are usually added at the time of widget creation. For example, it might be meaningful to limit the possible scale of the widget. This could be achived using a MultiWidgets::LimitScaleOperator.

// Limit the scale range from 0.5x to 2x the original size
auto limiter = std::make_shared<LimitScaleOperator>(0.5f, 2.0f);
widget->addOperator(limiter);

Some of the operators are usually added as a result of some event such as user input or timed event. Following code might be added as a result for some user input, which would remove the widget after certain timeout.

if (somethingHappened) {
// Remove the widget after 15 seconds
auto remover = std::make_shared<WidgetRemoveOperator>(15.f);
widget->addOperator(remover);
}

There is no limit how many operators can be added to a widget. Single operators are usually quite simple but by adding multiple operators it is possible to achieve more complex behaviour. List of standard operators implemented in Cornerstone can be found in the MultiWidgets-namespace.

Creating Your Own Operators

Creating a new operator is quite easy and straightforward:

  • Derive from MultiWidgets::Operator
  • Overload one or more virtual functions in Operator to implement the behaviour on the target widget

Notice that if the host widget class has overloaded it's update-method, it is possible that the widget implementation starts to "fight against" an operator. This would be the case when both the widget and the operator want to change same attribute into different directions. This can show up as a strange behaviour.

It is also recommended that you create a plugin from the operator. This makes the serialization work for your operators. Creating a plugin is very easy, just add following macro to your operator source file:

//add this line with unique id to your operators .cpp file
OPERATOR_PLUGIN(WidgetRemoveOperator, "widget-remove", "Widget Remove");
...
//load the plugin in the application
OperatorPtr op = Operators::createOperator("widget-remove");
widget->addOperator(op);