# UI Property
UI property is a concept used in Chromium to set a property value accessible from everywhere who has a owner of the property such as aura::Window or ui::View.
This is very convenient to handle a many properties, features tied to an object.
Let's see its mechanism.
## ClassProperty
[ClassProperty](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/class_property.h;l=70;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771) has following structure.
```cpp=
template<typename T>
struct ClassProperty {
T default_value;
const char* name;
bool cascading = false;
PropertyDeallocator deallocator;
};
```
The type of the value can be specified as a template parameter `T`. This `T` must be smaller than `int64_t` due to the restriction of the maximum property size as each object will carry many properties.
Each property has a `default_value` and this is registered by default.
`name` is usually the name of parameter we use such as `kImemrsiveIsActive`.
`cascade` is used for the property that the accessor would like to search up an instance hierarchy for the first defined property, not only for the specified property. I seldomly see this in my experiece.
`deallocator` is used when `T` is heap-allocated type, not like bool nor int.
## How to define property key
We can define a property key like the following:
```cpp=
// .h file
COMPONENT_EXPORT(CHROMEOS_UI_BASE)
extern const ui::ClassProperty<bool>* const kImmersiveIsActive;
// .cc file
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveIsActive, false)
```
The macro method `DEFINE_UI_CLASS_PROPERTY_KEY` is defined as follows:
```cpp=
#define DEFINE_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
static_assert(sizeof(TYPE) <= sizeof(int64_t), "property type too large"); \
const ::ui::ClassProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, false, \
nullptr}; \
const ::ui::ClassProperty<TYPE>* const NAME = &NAME##_Value;
```
By calling this macro, we construct a const param with type of `ui::ClassProperty<TYPE>` named `NAME`.
ClassProperty type is described in the previous section.
`DEFAULT` must be the same type as `TYPE`, if not it fails to match template.
There is similar, but a bit different macros:
```cpp=
#define DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
namespace { \
void Deallocator##NAME(int64_t p) { \
enum { type_must_be_complete = sizeof(TYPE) }; \
delete ::ui::ClassPropertyCaster<TYPE*>::FromInt64(p); \
} \
const ::ui::ClassProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, false, \
&Deallocator##NAME}; \
} /* namespace */ \
const ::ui::ClassProperty<TYPE*>* const NAME = &NAME##_Value;
```
As you can see from the code, this is used for a property with a heap allocaed object as `TYPE` so that is must be freed by the owner. The class is an owner of this value, so the macro name is `DEFINED_OWNED_UI_CLSS_PROPERTY_KEY`.
## How to set/get/clear property
We have following methods to set, get or clear property.
```cpp=
template <typename T>
void SetProperty(const ClassProperty<T>* property, T value);
template <typename T>
T GetProperty(const ClassProperty<T>* property) const;
template <typename T>
void ClearProperty(const ClassProperty<T>* property);
```
The usage is quite simple.
Here are the examples:
```cpp=
// Set
GetContentWindow()->SetProperty(chromeos::kImmersiveIsActive, enabled);
// Get
return window()->widget()->GetNativeWindow()->GetProperty(
chromeos::kImmersiveIsActive);
// Clear
window->ClearProperty(aura::client::kModalKey);
```
## Implementation
Let's see the SetProperty impl:
```cpp=
template <typename T>
void PropertyHandler::SetProperty(const ClassProperty<T*>* property,
const T& value) {
T* const old = subtle::PropertyHelper::Get<T*>(this, property, false);
if (old) {
T temp(*old);
*old = value;
AfterPropertyChange(property, reinterpret_cast<int64_t>(&temp));
} else {
SetProperty(property, std::make_unique<T>(value));
}
}
```
The behavior depends on whether there is a value already set.
If there is one, `old` exists, and its value is overriden by new `value`, and then trigger AfterPropertyChange.
If not, it creates unique_ptr with `value` and trigger property settings pass, and eventually comes to [SetPropertyInternal](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/class_property.cc;l=31;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771) with relatively straight forwrd way which calls AfterPropertyChange as well.
This [AfterPropertyChange](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/class_property.h;l=142;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771) is a virtual function and overriden by, for example, [aura::Window](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/window.cc;l=935;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771).
```cpp=
void Window::AfterPropertyChange(const void* key, int64_t old_value) {
for (WindowObserver& observer : observers_)
observer.OnWindowPropertyChanged(this, key, old_value);
}
```
When the property is updated from somewhere, it triggers WindowObserver::OnWindowPropertyChanged so that propery change will be propagated to everywhere who is observing this specific window.