# 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.