Creating custom layouts

A layout positions and defines the size of its children. Usually the definition and use of a custom layout are similar to that of custom controls, but with the addition of implementing the Measure and Arrange functionality of the KzuUiComponentNode class. If you need to define custom message types for interactive layout components, create a separate custom control for handling interactivity.

Include the custom layout as a static library in a project and register it to factory in function that is registered as onStartup.

KZ_CALLBACK void kzApplicationConfigure(const struct KzaSystemProperties* systemProperties, struct KzaApplicationProperties* configuration)
{
	configuration->memoryPoolSize = 20 * 1024 * 1024;
	configuration->binaryName = "Custom_layout.kzb.cfg";

	configuration->onKeyInputEvent = keyInputEventHandler;
	configuration->onStartup = startup;
}

The Custom_control example includes a Custom_component_toolmodule configuration, which you can use to generate a toolmodule plugin (.dll). By adding this preview module into your application configuration in Kanzi Studio, you can see the behavior of your custom control in the Preview. Kanzi Studio also imports all properties and message types that are defined in the registerToFactory implementation, so you can use them in Kanzi Studio similarly to any other components and message types.

The list of other callbacks should generally be passed as the default callbacks (*_protected):

const struct KzuUiComponentNodeClass CUSTOM_MYLAYOUT_CLASS =
{
    {
        &KZU_UI_COMPONENT_NODE_CLASS,
        myLayoutInitialize,
        myLayoutUninitialize,
        kzuObjectNodeOnAttached_protected,
        kzuObjectNodeOnDetached_protected,
        kzuUiComponentNodeCopy_protected,
        kzuUiComponentNodeRender_protected,
        kzuUiComponentNodeGetBoundingVolume_protected
    },
    myLayoutMeasure,
    myLayoutArrange,
    kzuUiComponentNodeValidateRender_protected,
    kzuUiComponentNodeCopyComponent_protected
};

Implementing the myLayoutInitialize and myLayoutUninitialize functions is similar to implementing the respective kzApplicationInitialize and kzApplicationUnitialize functions in the application framework. Component specific data and reference classes are created and deleted according to the component’s lifetime. The measure and arrange functions for their part define the actual layout behavior of the layout component.

myLayoutMeasure should calculate and provide the desired size that the layout will take in the scene, normally based on:

myLayoutArrange should apply a layout transformation to all its children based on the manner in which the children are organized inside the layout using kzuObjectNodeSetLayoutTransformation. This transformation is applied after the transformation of the parent layout, meaning that transformation should be relative to the parent layout’s transformation origin. The object node’s own transformation, if other than identity is added on top of this.

The following code shows a simple implementation for myLayoutInitialize, myLayoutUninitialize, myLayoutMeasure and myLayuoutArrange.

KZ_CALLBACK static kzsError myLayoutInitialize(struct KzuObjectNode* objectNode)
{
    struct KzuUiComponentNode* componentNode = 
        kzuUiComponentNodeFromObjectNode(objectNode);

    /* Initialize parent class. */
    kzuUiComponentNodeInitialize_private(objectNode);

    /* Register the current component class as a layout. */
    kzuUiComponentNodeSetIsLayout(componentNode, KZ_TRUE);

    /* Possible other layout-specific custom allocations. */
    ...

    kzsSuccess();
}
KZ_CALLBACK static kzsError myLayoutUninitialize(struct KzuObjectNode* objectNode)
{
    /* Possible unitializations of custom allocations. */
    ...
 
    /* Uninitialize parent class. */
    kzuUiComponentNodeUninitialize_private(objectNode);
}
KZ_CALLBACK static kzsError myLayoutMeasure
    (struct KzuUiComponentNode* component, struct KzcVector3* out_coreDesiredSize)
{
    struct KzuObjectNode* objectNode = kzuUiComponentNodeToObjectNode(component);
    struct KzcDynamicArrayIterator it = kzuObjectNodeGetChildren(objectNode);

    while (kzcDynamicArrayIterate(it))
    {
        /* Iterate through layout’s children and calculate the desired size. */
    }
    
    /* Provide the computed desired size as a vector. */
    *out_coreDesiredSize = XYZ;

    kzsSuccess();
}
KZ_CALLBACK static kzsError myLayoutArrange(struct KzuUiComponentNode* component)
{
    struct KzuObjectNode* objectNode = kzuUiComponentNodeToObjectNode(component);
    struct KzcDynamicArrayIterator it = kzuObjectNodeGetChildren(objectNode);

    while (kzcDynamicArrayIterate(it))
    {
        /* Iterate through myLayout’s children and apply layout-transforms. */
        ...
        kzuObjectNodeSetAllocatedSize(childNode, &childAllocatedSize);
        kzuObjectNodeSetLayoutTransformation(childNode, &layoutTransform);
    }

    kzsSuccess();
}