Kanzi  3.9.7
Kanzi Engine API
kanzi::AbstractBinding Class Referenceabstract

AbstractBinding is the base class for binding classes. More...

#include <kanzi/core.ui/binding/abstract_binding.hpp>

Inheritance diagram for kanzi::AbstractBinding:
[legend]

Public Types

using BindingProcessorConstIterator = BindingProcessorContainer::const_iterator
 Binding processor iterator type. More...
 
using BindingProcessorContainer = vector< BindingProcessorSharedPtr >
 Binding processor container type. More...
 

Public Member Functions

void addProcessor (BindingProcessorSharedPtr processor)
 Add a binding processor. More...
 
void addReverseProcessor (BindingProcessorSharedPtr processor)
 Add a binding processor (reverse direction). More...
 
BindingProcessorConstIterator beginProcessors () const
 Gets an iterator to the beginning of binding processors. More...
 
BindingProcessorConstIterator beginReverseProcessors () const
 Gets an iterator to the beginning of reverse direction binding processors. More...
 
AbstractBindingRuntimeSharedPtr createRuntime (AbstractBindingSharedPtr binding, BindingLookupContextPtr sourceLookupContext, BindingTargetRuntimePtr targetRuntime)
 Creates a binding runtime for this binding. More...
 
BindingProcessorConstIterator endProcessors () const
 Gets an iterator to the end of binding processors. More...
 
BindingProcessorConstIterator endReverseProcessors () const
 Gets an iterator to the end of reverse direction binding processors. More...
 
BindingSourcegetSource ()
 Gets the binding source for this binding. More...
 
bool isCreatedFromKZB () const
 Indicates whether the binding is sourced from a KZB? Used to check whether or not to remove the binding during patching. More...
 
void removeProcessor (BindingProcessor &processor)
 Remove a binding processor. More...
 
void removeReverseProcessor (BindingProcessor &processor)
 Remove a binding processor (reverse direction). More...
 
void setCreatedFromKZB (bool flag)
 Sets the created from KZB flag. More...
 
virtual ~AbstractBinding ()=default
 Destructor. More...
 

Protected Member Functions

 AbstractBinding (BindingSourcePtr source)
 Constructor. More...
 
virtual void addReverseProcessorOverride (BindingProcessorSharedPtr processor)
 Implementation-dependent binding processor add (reverse direction). More...
 
virtual BindingProcessorConstIterator beginReverseProcessorsOverride () const
 Implementation-dependent access to reverse direction binding processors. More...
 
virtual AbstractBindingRuntimeSharedPtr createRuntimeOverride (AbstractBindingSharedPtr binding, BindingLookupContextPtr sourceLookupContext, BindingTargetRuntimePtr targetRuntime)=0
 Implementation-dependent runtime creation. More...
 
virtual BindingProcessorConstIterator endReverseProcessorsOverride () const
 Implementation-dependent access to reverse direction binding processors. More...
 
virtual void removeReverseProcessorOverride (BindingProcessor &processor)
 Implementation-dependent binding processor remove (reverse direction). More...
 

Protected Attributes

bool m_createdFromKZB
 Is the binding sourced from a KZB? This field is used to determine if the binding should be removed during patching. More...
 
BindingProcessorContainer m_processors
 Binding processor (forward direction). More...
 
BindingSourcePtr m_source
 Binding source. More...
 

Detailed Description

AbstractBinding is the base class for binding classes.

A binding is a relation between two property values. In most cases, this means a relation between a property in a source node or render pass, and a target property in the node or render pass to which the binding is set.

Bindings can also bind values from sources other than object properties and write to other targets. The relationship is abstracted into binding sources and targets.

AbstractBinding contains the immutable data of a binding. To use a binding, create an AbstractBindingRuntime that references the host AbstractBinding for the data. All bindings also contain a BindingSource that describes how to get the source value, and just like the AbstractBindingRuntime, binding sources create a BindingSourceRuntime that references the immutable data in the binding source to perform the read. Unlike BindingSource, BindingTargetRuntime has no binding target counterpart. When you use a binding, Kanzi creates a BindingTargetRuntime and sets it to the AbstractBindingRuntime. Kanzi usually does this for you when you set the binding to a Node or RenderPass.

Using bindings

To use a binding, first create a binding source which determines how to get the source values. To create a binding source that reads a property value from an object:

// Create a binding source that reads a property from a sibling node named siblingNode.

Use a binding to combine the binding source that you created. The binding determines how Kanzi copies the source value to the target. In this case, a source value is read from source and written to target without caching:

// Create an one-to-one property binding using the specified source.
AbstractBindingSharedPtr binding = Binding::create(kanzi::move(source));

You can now use the binding. To set the binding to a node, use Node::setBinding and specify the binding and target property:

// Calling setBinding() will install the binding runtime as a property value source.
BindingRuntimeHandle runtime = node->setBinding(binding, Node2D::OpacityProperty);

There is now a binding from a property in a sibling node to a property in a node. To remove the binding, use the runtime returned by Node::setBinding:

// Remove the binding using the runtime created earlier.
node->removeBinding(runtime);

Binding types

Kanzi supports these binding types:

  • One-way
  • To Source
  • Two-way

The basic form of a binding is called Binding. A binding performs a copy from a source to a target.

To create a one-way binding from a property in a source node to a property in another node:

// Create a binding from the Opacity property of a sibling node named "siblingNode".
// Set the binding to the Opacity property of a node.
BindingRuntimeHandle runtime = node->setBinding(binding, Node2D::OpacityProperty);

Most bindings in nodes are one-way bindings. In this case, a property written to the sibling node copies the value to the target.

// Setting the value of the Opacity property of the sibling node copies the value to the Opacity property of the node.
siblingNode->setOpacity(0.8f);
float opacityValue = node->getOpacity(); // opacityValue is set to 0.8f.

You can also create two-way bindings. To create a two-way binding, use TwoWayBinding. In a two-way binding setting a property to the source copies the value to the target, the same way as in a one-way binding. In a two-way binding setting a value to the target also copies the value to the source.

To create a two-way binding between two nodes and their properties:

// Create a binding between the Opacity property of a sibling node named siblingNode, and the Opacity property of a node.
BindingRuntimeHandle runtime = node->setBinding(binding, Node2D::OpacityProperty);

Setting the source value now functions the same way as in a one-way binding.

// Setting the opacity value of sibling node copies the value to the node.
siblingNode->setOpacity(0.5f);
float opacityValue = node->getOpacity(); // opacityValue is set to 0.5f.

Setting the value in the target node copies the value to the source.

// Setting the value of the Opacity property of the node copies the value to the Opacity property of the sibling node.
node->setOpacity(0.7f);
float opacityValue = siblingNode->getOpacity(); // opacityValue is set to 0.7f.

Bindings can also collect multiple source values, perform mathematical operations on the values, and write a result value for these source values. See ExpressionBinding.

Derivatives of ForwardingAction and others can house manually executed bindings that can be executed in one or two parts. These bindings are executed as part of the action, such as SetPropertyAction or DispatchMessageAction, taking place. See ManualBinding.

To write to other objects, such as another Node or RenderPass, or to write to a property of a RenderPass from a Node, use ToSourceBinding.

Binding source types

BindingSource abstracts the way to get a value for a binding. BindingSource provides the details on how to get the value, and creates a BindingSourceRuntime that refers to the host binding source for the details, but holds the per-binding runtime information.

To create an ObjectPropertyBindingSource that reads properties from objects such as Node or RenderPass:

// Create a binding source that reads a property from a sibling node named siblingNode.

You can use data sources as binding sources. To use the current effective data source to get values specified by the path within the data source:

// Create a data source binding source.
BindingSourcePtr source = DataSourceBindingSource::create("cluster.speed.kmh");

Binding sources can resolve resources from a local context. In this case, a resource is resolved using the Node or RenderPass and the result is written to the BindingTargetRuntime.

To create a resource binding source:

// Create a resource binding source using the kzb URL of a resource.
BindingSourcePtr source = ResourceBindingSource::create("kzb://myproject/Brushes/MyBrush");

To create a resource binding using a shorthand:

// Create a resource binding to the MyResourceIdToSomething resource.
BindingSharedPtr binding = Binding::create(ResourceID("MyResourceIdToSomething"));

Expression bindings use a specific type of source called an ExpressionBindingSource to host the information required for executing mathematical operations.

See also
DataSourceBindingSource, ExpressionBinding

Target runtimes

When the binding is set to a Node or a RenderPass, a BindingTargetRuntime for the binding is internally created. Binding target runtimes do not refer to a host binding target, as BindingSourceRuntime refers to a BindingSource - they contain all the runtime information within themselves. You do not need to create the binding target runtime manually because it is abstracted behind the setBinding() interface of a Node or RenderPass. It is, however, important to understand the differences between the target runtimes.

Bindings are normally installed using setBinding() the function, which creates a ValueSourceBindingTargetRuntime. A value source binding target runtime is just another value source for a property, and does not functionally differ from a custom value source added by PropertyObject::addPropertyValueSource. When the binding is executed, the value is set to the internal value source of the ValueSourceBindingTargetRuntime.

The setBinding() function installs the value source with local value priority by default. This has several implications:

  • Setting a binding to a property of an Object does not hide a previously set local value, but removes that value. If there is an existing binding to that property, that binding is removed.
  • Setting a local value of a property after a binding has been set for that property removes the binding.
  • Removing a local property value removes the binding.
  • Removing the binding removes the value.

Note also these caveats as compared to normal value source operation:

  • If the binding is not executed, the value source does not assume any specific value. The getProperty() function returns the value from an underlying value source or the default value.
  • Creating a ValueSourceBindingTarget to a field of a property assumes other fields of that property from the current value acquired when that binding is first executed. Only bind to whole values with a value source binding target unless you really know what you are doing.

To remove a binding by removing the local value:

// setBinding() creates a value source that owns the binding runtime.
BindingRuntimeHandle runtime = node->setBinding(binding, Node2D::OpacityProperty);
node->removeLocalValue(Node2D::OpacityProperty);

To bind to a value source other than local precedence:

// Set the binding to bind to a precedence lower than local.
BindingRuntimeHandle runtime = node->setBinding(binding, Node2D::OpacityProperty, PropertyValuePrecedenceStyle);

To install a binding as a property modifier:

// Calling setModifierBinding() installs the binding runtime as a property modifier.
BindingRuntimeHandle runtime = node->setModifierBinding(binding, Node2D::OpacityProperty);

Bindings installed as modifiers do not differ from regular property modifiers. The modifiers are executed in the order of addition. Adding a binding as a property modifier has these implications:

  • Binding a property modifier to the whole property replaces the property value.
  • Binding into a property field replaces only that field, and takes other fields from the value sources or default values below.

Binding to a property modifier as one-on-one binding makes little sense, because you can achieve the same result with a value source with less overhead. You can use an ExpressionBinding to read the underlying value from value sources or earlier property modifiers and modify it for more fine-grained control.

To remove a binding that is installed as a property modifier, removing the local property value is not enough. You must remove the binding.

Binding owners

In some situations, it makes sense to specify binding owners to tag the bindings into a group that you can easily identify later. For example, a StateManager or a Style modifying an object can install a binding. Tagging the installed bindings as being owned by a StateObjectRuntime or AppliedStyleEntry enables easy removal of all installed bindings.

To add a binding with an owner pointer:

// Create two bindings.
// Bind to opacity and texture properties, use the sibling node as owner pointer so the binding can be later removed at the same time.
BindingRuntimeHandle runtime1 = node->setBindingWithOwner(binding1, siblingNode, Node2D::OpacityProperty);
BindingRuntimeHandle runtime2 = node->setBindingWithOwner(binding2, siblingNode, StandardMaterial::TextureProperty);

To remove bindings tagged with an owner pointer:

// Use owner pointer from earlier to remove both tagged bindings at the same time.
node->removeBindingsWithOwner(siblingNode);

Binding owners should generally not be required in normal use, but may be important if implementing some state interactions similar to the internal engine use case with states and styles.

Binding processors

You can use binding processors to validate the binding values and to add side effects to bindings. In most cases you add processors to bindings to:

  • Run arbitrary code when the binding is executed.
  • Validate binding values and either accept or decline the values from being written.

This example defines a binding processor that accepts (validates) the binding result only if the result is an integer value between 1 and 5. The processor does not validate other values, and returning false from the validation causes the result value to not be written.

class ExampleProcessor : public BindingProcessor
{
public:
// Create function for the example processor.
static shared_ptr<ExampleProcessor> create(Domain* domain)
{
return shared_ptr<ExampleProcessor>(new ExampleProcessor(domain));
}
// Destructor.
virtual ~ExampleProcessor() override = default;
protected:
// Constructor.
explicit ExampleProcessor(Domain* domain) :
BindingProcessor(domain)
{
}
};
class ExampleProcessorRuntime : public BindingProcessorRuntime
{
public:
static unique_ptr<ExampleProcessorRuntime> create(ExampleProcessor& processor)
{
return unique_ptr<ExampleProcessorRuntime>(new ExampleProcessorRuntime(processor));
}
~ExampleProcessorRuntime() override = default;
protected:
explicit ExampleProcessorRuntime(ExampleProcessor& processor) :
BindingProcessorRuntime(processor)
{
}
void attachOverride(AbstractBindingRuntime& bindingRuntime) override
{
}
void detachOverride() override
{
}
bool validateOverride(Variant& value) override
{
// Only accept integers.
if (!holds_alternative<int>(value))
{
return false;
}
// Get the value from variant.
int integerValue = get<int>(value);
// Accept values >= 1 and <= 5.
return (integerValue >= 1) && (integerValue <= 5);
}
protected:
};
BindingProcessorRuntimePtr ExampleProcessor::createRuntimeOverride()
{
return ExampleProcessorRuntime::create(*this);
}
// Shared pointer type.
typedef shared_ptr<ExampleProcessor> ExampleProcessorSharedPtr;

To use the binding processor defined above:

// Create a binding.
// Create processor.
ExampleProcessorSharedPtr processor = ExampleProcessor::create(domain);
// Add processor to binding. You must do this before adding the binding to a node.
binding->addProcessor(processor);
// Bind from the Opacity property of a sibling node to the Opacity property of this node.
node->setBinding(binding, Node2D::OpacityProperty);
// The example binding processor always rejects the property because it is not an integer, so this binding does not execute.

You do not have to define a new binding processor class for every possible validation. To run arbitrary code, use CallbackBindingProcessor and pass an arbitrary function. This example shows how to do the same thing as above using CallbackBindingProcessor:

// Individual validation function.
bool staticValidationFunction(Variant& value)
{
// Only accept integers.
if (!holds_alternative<int>(value))
{
return false;
}
// Get the value from variant.
int integerValue = get<int>(value);
// Accept values >= 1 and <= 5.
return (integerValue >= 1) && (integerValue <= 5);
}

To use the callback processor with the given function:

// Create a binding.
// Create callback processor.
CallbackBindingProcessorSharedPtr processor = CallbackBindingProcessor::create(domain, staticValidationFunction);
// Add processor to binding. You must do this before adding the binding to a node.
binding->addProcessor(processor);
// Bind from the Opacity property of a sibling node to the Opacity property of this node.
node->setBinding(binding, Node2D::OpacityProperty);
// The example binding processor always rejects the property because it is not an integer, so this binding does not execute.
Since
Kanzi 3.7.0

Member Typedef Documentation

using kanzi::AbstractBinding::BindingProcessorConstIterator = BindingProcessorContainer::const_iterator

Binding processor iterator type.

Constructor & Destructor Documentation

virtual kanzi::AbstractBinding::~AbstractBinding ( )
virtualdefault

Destructor.

kanzi::AbstractBinding::AbstractBinding ( BindingSourcePtr  source)
inlineexplicitprotected

Constructor.

Parameters
sourceBinding source for the binding.

Member Function Documentation

AbstractBindingRuntimeSharedPtr kanzi::AbstractBinding::createRuntime ( AbstractBindingSharedPtr  binding,
BindingLookupContextPtr  sourceLookupContext,
BindingTargetRuntimePtr  targetRuntime 
)

Creates a binding runtime for this binding.

Source runtime is created internally by the contained binding source, target runtime is passed.

Parameters
bindingShared pointer to this binding.
sourceLookupContextLookup context to use for binding source(s).
targetRuntimeAn existing binding target.
Returns
Binding runtime for this binding.
BindingSource* kanzi::AbstractBinding::getSource ( )
inline

Gets the binding source for this binding.

Returns
Binding source.
void kanzi::AbstractBinding::addProcessor ( BindingProcessorSharedPtr  processor)
inline

Add a binding processor.

Parameters
processorProcessor to add.
void kanzi::AbstractBinding::removeProcessor ( BindingProcessor processor)
inline

Remove a binding processor.

Parameters
processorProcessor to remove.
BindingProcessorConstIterator kanzi::AbstractBinding::beginProcessors ( ) const
inline

Gets an iterator to the beginning of binding processors.

Returns
Iterator to beginning of processor array.
BindingProcessorConstIterator kanzi::AbstractBinding::endProcessors ( ) const
inline

Gets an iterator to the end of binding processors.

Returns
Iterator to end of processor array.
void kanzi::AbstractBinding::addReverseProcessor ( BindingProcessorSharedPtr  processor)
inline

Add a binding processor (reverse direction).

Parameters
processorProcessor to add.
void kanzi::AbstractBinding::removeReverseProcessor ( BindingProcessor processor)
inline

Remove a binding processor (reverse direction).

Parameters
processorProcessor to remove.
BindingProcessorConstIterator kanzi::AbstractBinding::beginReverseProcessors ( ) const
inline

Gets an iterator to the beginning of reverse direction binding processors.

Returns
Iterator to the beginning of reverse processors.
BindingProcessorConstIterator kanzi::AbstractBinding::endReverseProcessors ( ) const
inline

Gets an iterator to the end of reverse direction binding processors.

Returns
Iterator to the end of reverse processors.
bool kanzi::AbstractBinding::isCreatedFromKZB ( ) const
inline

Indicates whether the binding is sourced from a KZB? Used to check whether or not to remove the binding during patching.

Returns
True if yes, false if created by other means or by user.
void kanzi::AbstractBinding::setCreatedFromKZB ( bool  flag)
inline

Sets the created from KZB flag.

Should be set for bindings added from KZB data.

Parameters
flagNew flag state.
virtual AbstractBindingRuntimeSharedPtr kanzi::AbstractBinding::createRuntimeOverride ( AbstractBindingSharedPtr  binding,
BindingLookupContextPtr  sourceLookupContext,
BindingTargetRuntimePtr  targetRuntime 
)
protectedpure virtual

Implementation-dependent runtime creation.

Parameters
bindingShared pointer to this binding.
sourceLookupContextLookup context to use for binding source(s).
targetRuntimeBinding target runtime.
Returns
Created type-specific runtime.

Implemented in kanzi::ExpressionBinding, kanzi::ManualBinding, kanzi::ToSourceBinding, kanzi::TwoWayBinding, and kanzi::Binding.

virtual void kanzi::AbstractBinding::addReverseProcessorOverride ( BindingProcessorSharedPtr  processor)
protectedvirtual

Implementation-dependent binding processor add (reverse direction).

Default implementation does nothing, since normal bindings are not supposed to have reverse processors.

Parameters
processorProcessor to add.

Reimplemented in kanzi::TwoWayBinding.

virtual void kanzi::AbstractBinding::removeReverseProcessorOverride ( BindingProcessor processor)
protectedvirtual

Implementation-dependent binding processor remove (reverse direction).

Default implementation does nothing, since normal bindings are not supposed to have reverse processors.

Parameters
processorProcessor to remove.

Reimplemented in kanzi::TwoWayBinding.

virtual BindingProcessorConstIterator kanzi::AbstractBinding::beginReverseProcessorsOverride ( ) const
protectedvirtual

Implementation-dependent access to reverse direction binding processors.

Default implementation throws an exception, since normal bindings are not supposed to have reverse processors.

Returns
Iterator to the beginning of reverse processors.

Reimplemented in kanzi::TwoWayBinding.

virtual BindingProcessorConstIterator kanzi::AbstractBinding::endReverseProcessorsOverride ( ) const
protectedvirtual

Implementation-dependent access to reverse direction binding processors.

Default implementation throws an exception, since normal bindings are not supposed to have reverse processors.

Returns
Iterator to the end of reverse processors.

Reimplemented in kanzi::TwoWayBinding.

Member Data Documentation

BindingSourcePtr kanzi::AbstractBinding::m_source
protected

Binding source.

BindingProcessorContainer kanzi::AbstractBinding::m_processors
protected

Binding processor (forward direction).

Inheriting class may add processor for reverse direction.

bool kanzi::AbstractBinding::m_createdFromKZB
protected

Is the binding sourced from a KZB? This field is used to determine if the binding should be removed during patching.


The documentation for this class was generated from the following file: