All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
Attributes

Attributes are dynamic named properties that can be added to objects in Cornerstone.

Attributes may be defined during compilation or they can be dynamically added and removed during runtime. Attributes can be accessed by their name from native code or from external systems, such as CSS.

In addition to their name, attributes have a type (such as boolean, integer, floating, etc). Cornerstone comes with most of the basic types defined and users can easily add new types if they need to extend the functionality.

Attribute Layers

Each attribute has multiple layers for possible values. These are ordered by priority. Values on more important layers will override value with lower priority. This system allows us a flexible way of defining attribute values. We can have default values that are overriden by values set from CSS and then finally overriden by values specified from C++/JavaScript code.

The attribute layers are defined in increasing priority as follows:

  • Default - default attribute value
  • Style - attribute value set from a CSS stylesheet
  • User - attribute value set from C++/JavaScript code
  • Style Important - attribute value set from CSS stylesheet using !important keyword

Attribute Hierarchy

Valuable::Node is a key class when using attributes. The Valuable::Node itself is a special kind of attribute: it is an attribute itself, but instead of storing a value, it stores a list of other attributes. These attributes are the child attributes of the Valuable::Node.

MultiWidgets::Widget inherits from Valuable::Node and contain a list of widget-specific attributes. These attributes can be used to control the appearance and behaviour of a widget, e.g. text, color, font-size. As all widgets are nodes, custom attributes which contain some application-specific data can be added to them.

The attribute hierarchy is not to be confused with the widget hierarchy. It is completely separate from it. As widgets inherit from Valuable::Node, it is possible to define a widget to be an attribute of another widget. This will not automatically make the added widget a child of the containing widget in widget hierarchy however. If this is what you want, you must explicitly define the attribute widget as child in the widget hierarchy also.

Creating Attributes

Attributes can be defined in C++ code like follows:

Valuable::Node root(nullptr, "root");
Valuable::Node parent(&root, "parent");
// Create two attributes as children of the parent node
Valuable::AttributeInt integer(&parent, "valueA", 0);
Valuable::AttributeBool boolean(&parent, "valueB", false);

When constructing the node root in the code above, we define its parent to be a nullptr. It could be another node object as well if we were creating a deeper hierarchy. After that we create node parent as a child of root. The two latter attributes are defined to be the children of parent. We also define the names and default values for the attributes when creating them.

Adding new attributes to existing nodes can be done like this:

// Create a widget
auto widget = MultiWidgets::create<MultiWidgets::Widget>();
// Create a new string attribute
auto attribute = new Valuable::AttributeString(nullptr, "my-attribute-name", "my-default-value");
// Add the attribute to the widget
widget->addAttribute(attribute);

The code above works for any Valuable::Node (including MultiWidgets::Widget). This allows the user to dynamically add new attributes to existing instances of objects. Adding attribute to node will also transfer responsibility of memory management to the parent node.

Setting Attribute Values

Attribute values can be set using C++ like follows:

// Set attribute value directly
integer.setValue(100);
// Set attribute value via Node and path
root.setValue("parent/valueB", 50);

Values can be set using Attribute::setValue function or using Node::setValue with a parameter path.

Same example using common widget-attribute could be:

auto widget = MultiWidgets::create<MultiWidgets::Widget>();
//set value through attribute:
widget->setAttribute("background-color", Radiant::Color(0.f,0.f, 1.f, 1.f));
//does the same as this:
widget->setBackgroundColor(Radiant::Color(0.f,0.f, 1.f, 1.f));

Accessing Attributes

Accessing attribute values is similar to setting them. We can query the values from C++ like follows:

// Get a value directly. Does an implicit conversion
int a = integer;
// Get value through a node
a = root.getValue("parent/valueA")->asInt();

Again, we can either get the value directly using the attribute or with a path through a Valuable::Node object. HelloImagesExample.cpp shows an example how to parse command line arguments easily using attributes.

Attribute Listeners

It is often important to know when the value of an attribute changes. For example, you might want to perform some special logic if the width of a widget changes. Monitoring attributes is facilitated by attribute listeners. You can easily define callbacks for attributes that get called every time the value of the given attribute changes.

integer.addListener([] { std::cout << "value of A changed!" << std::endl; });

In the example above, we define a simple lambda function that gets called every time the attribute value changes. Changes in attributes can also be monitored using the event system.

AttributesExample shows you more about using attributes with widgets