Description

The List Decorators example shows how the user can extend the behaviour and enhance the appearance of ListView by extending and implementing additional functionality in the DataModel/DataQuery decorators.

Overview

In this example we'll learn how to extend the DataModelDecorator or the DataQueryDecorator. By extending these decorators it allows as to insert new data or to modify existing data to enrich the ListView appearance. This can also be useful when we want to reinterpret the data before it is used by ListView.

The UI

The UI of this sample application consists of a ListView that simply shows the content based on the selected custom decorator.

We create custom decorators in C++, register their types with qml and define them as attached objects.

The ActionItem definitions for each decorator. Each ActionItem loads the appropriate data model and assigns the decorator model to the ListView when triggered. These definitions instantiate the decorators that will be used by this action item as attached objects.

The ListView uses the defined data decorators as its data model based on which one is selected. This data model loading and changing is accomplished through the ActionItem's that were previously defined for each decorator. It also defines the custom ListItemComponent's to deal with the various decorator item types.

Instantiates one of the default provided data model extensions as the default model for the ListView upon sample startup, the list view data model is always reverted to this model when user selects the "Reset" menu item.

This custom component declares a DataLoggerDecorator ActionItem, which loads the Decorator DataModel and assigns it to the ListView when triggered. This DataModel decorator is defined as an attached object. The amount of logging is increased upon triggering this item.

This custom component is a good example of how to chain the various decorator classes together with the default library convenience classes that are included with the library. With this component we provide an action item that triggers the loading of custom decorator classes that are designed to decorate the list items with icons based on their "data_quality" field data content.

The rest of the defined Decorator qml components follow the same above pattern, each defining the ActionItem representing the decorator, and the decorator as an attached object, which is loaded and assigned to the ListView upon it's triggering.

These types of components define the custom ListItemComponent's that are used by the ListView to represent data from the model for items that are of a particular type (i.e. group). It contains whatever visual controls that you wish to use in order to represent the data in your list item.

The ApplicatoinUI class

ApplicatoinUI is the central class of the application that creates the UI and registers the various decorator classes.

The intended purpose of this class is to enable highly-verbose logging for debugging only.

It reimplements all the virtual methods and additionally provides methods to enable the logging details of the item's various stages. It contains three member variables, each one representing a different logging granularity that can be enabled.

The constructor takes and holds the original IndexMapper class and the DataModelLoggerDecorator instance. The IndexMapper virtual methods are re-implemented to provide additional logging on top of invoking the original method implementations.

This method creates a new QList of DataItem's, where the DataItem QVariantMap is injected with additional data specifying the image name to represent it's data quality value. Afterwards, the DataItem payload is reset to the map containing the additional data and the list of DataItems replaces the old list.

The DataQualityModelDecorator class

This class is meant to be chained with the DataQualityDataQueryDecorator class, changing the item type to correspond to the defined ListItemComponent for that type, which is specifically designed for that data layout.

Inside the data() method we test again whether the data for a header item or a normal item are requested. For a header item we want to enrich the original data (the color of the container background) with the current expansion state. We use a QVariantMap for this purpose, add a "expanded" entry with the original data from the source model with a boolean value.

The following methods manipulate item child count based on whether the item is set for expansion or not. If item is not to be expanded (i.e. not matching expand index) than the child count is set to zero, otherwise the real child count is returned. If the return is '0' the ListView won't show any child items underneath this header item. In all other cases, we simply forward the call to the source model.

The expandHeader() method is a Qml invokable function, which sets the expand index to the current index if item is to be expanded or resets it to default value if item is already expanded and is to be collapsed.

The most important members of this class is the QSet which holds all the highlighted indexes, and the changeSelection() slot which adds or removes the index from the set based on whether it has already been selected or not. This enriches the ListView functionality by providing the ability for multi-item-selections.

Inside the data() method we test again whether the data for a header item or a normal item are requested. We want to enrich the original data with the color of the container(ListItemComponent) background. We use a QVariantMap for this purpose, add a "selected" entry with the original data from the source model with a color hex value, this allows us to visually indicate which items have been selected by grouping them using the same color.

The itemType() method changes the type to be of type 'selectable', which corresponds to the ListItemComponent of that type, that has the logic to deal with the additional data in order to show visually multiple selection functionality.

The changeSelection() method retrieves the selected data item and it's "id", it than verifies if this "id" is in the set of selected items, and is either added or removed into the set depending on it's existence in the set. Afterwards, the itemUpdated() signal is emited, causing the data() method to be called, which in turn changes the items container background to indicate a selection or not pending if it's in the selection set.