Advanced Metadata Definitions and Usages

The core documentation for Views Metadata and properties usage can be found here. For most instances, this is sufficient to add metadata to any given view descendant class.

There are, however, some cases where extra care is needed to properly add metadata to a class.

Nested or Private Classes

In some cases a View sub-class is declared as a nested class of varying visibility. In order to properly attach metadata to such a class the following should be used. Consider the following example class:

In the header it is declared thusly:

class ASH_EXPORT MyViewClass : public views::View {
 public:
  METADATA_HEADER(MyViewClass, views::View);

  // ... Public API goes here ...

 private:
  // ... A private nested view ...
  class MyNestedView;
};

Then in the .cc file:

class MyViewClass::MyNestedView : public views::View {
 public:
  METADATA_HEADER(MyNestedView, views::View);

 // ... Public API goes here …

};

Then, to properly declare the metadata for MyViewClass::MyNestedView along with any necessary properties, the BEGIN_METADATA macro takes an extra parameter to define the outer class or scope:

BEGIN_METADATA(MyViewClass, MyNestedView)
END_METADATA

This ensures the internal metadata classes are defined with the properly nested scope. The rest of the definition remains the same as the above linked document comment.

Read-only Properties

While this may seem strange that a property would be read-only, it’s a good debugging tool by allowing the ui-devtools to peer into the object instance for more insight about it’s internal state. In those cases, you may want to define such a property in the metadata. A read-only property only has a “getter” (see the core documentation about how to define a “getter”). It is as simple as using a different macro when defining the class’ metadata.

ADD_READONLY_PROPERTY_METADATA(bool, IsDrawn);

In this case, the IsDrawn property is calculated and not something that can be directly set. However, it is likely useful during debugging to see whether the view considers itself to be drawn. Sometimes you will need to refactor the “getter” to properly match the naming requirements.

Custom Type-converters for POD (Plain Old Data) Types

While this may be rare, there are cases where a type has been declared as a mere alias to a POD type such as int, unsigned, float, etc. In most circumstances the default type converter which will convert to/from a string using the natural string format is sufficient. However in some cases this type alias is meant to convey that this POD type is to be treated differently and thus needs to convert to/from strings using a different syntax. Unfortunately, C++ doesn’t have the concept of making a truly uniquely identifiable type from a POD type. In those cases some extra care is needed when defining a custom type converter for such a type.

Consider the following type:

using datetime_t = double;

If a property were using that type, the compiler would select the following type converter:

template <>
struct TypeConverter<double> {
  //...
};

This is most likely not what was desired since the likely intent is to allow the use of a date/time string to display or set the value. There is no way to force the compiler to select a TypeConverter<datetime_t> specialization. We need to do a little extra work.

First of all, make the type unique with the following macro:

MAKE_TYPE_UNIQUE(SkColor);

Then use the UNIQUE_TYPE_NAME macro to wrap the name you want to be unique.

template <>
struct VIEWS_EXPORT TypeConverter<UNIQUE_TYPE_NAME(datetime_t)>
    : BaseTypeConverter<true, false> {
  // Define implementations for ToString and FromString here. See type_conversion.h
};

It’s also helpful to define your own type-alias to later reference.

using DateTimeConverter = TypeConverter<UNIQUE_TYPE_NAME(datetime_t)>;

Finally, you need to be explicit about when to select that type converter when defining the property in the metadata. Add the reference to the type converter specialization to the macro.

BEGIN_METADATA(MyDateTimeView, views::View)
ADD_PROPERTY_METADATA(datetime_t, CurrentDateTime, DateTimeConverter)
END_METADATA

NOTE: SkColor is such a case where the above technique has been used. For properties of that type, use the SkColorConverter type alias when defining the metadata for a property of that type.

ADD_PROPERTY_METADATA(SkColor, BackgroundColor, SkColorConverter)

Non-Serializable Types

Due to their very nature, some property types may not be readily convertible to or from strings. Common instances of this are pointers or a unique_ptr<T>. However, knowing that something has been set for such a property can be helpful. Unlike the read-only properties described above, a property of a non-serializable type may still have a “getter” and a “setter”. Within the code at run-time, getting or setting the value directly via the getter and setter is perfectly valid.

The type converter for a non-serializable type may return something from the ToString() method, but it will typically return absl::nullopt from the FromString() method. For a non-serializable type, the ui-devtools front-end won’t call the setter since whatever “value” it has is presumed to be unconvertable to a valid value.

For pointers and unique_ptr<T> a partial specialization is already available in which the compiler should already select. Most non-serializable types are already unique, so creating a type converter specialization is relatively straightforward. Otherwise see the above section to resolve this.

For other instances that may need this you will need to define your own type converter specialization. This is done the same as other type converter specializations, except for the ancestor specialization. The ancestor BaseTypeConverter template takes a few bool parameters. The first of which indicates whether a property type is serializable.