Creating components from templates: Slider Item

With this template you can add a new Slider Item component to your project. Slider Item is a simple widget the user can interact with in order to change a number value by touching and dragging a knob. Each time the user interacts with the Slider Item, the component sends signals to associated slot methods where your particular implementation is executed. If the Slider Item is focused the component can also be controlled by pressing keys on the keyboard or by using hardware buttons. Generally menu items are used within menus only. Therefore when using a Slider Item you will also need to add at least one Menu component to your project.

Components created with this template are intended to be adapted to your particular design expectations. After adding the new Slider Item you should edit the component, change its appearance and if desired also its behavior. Once you have adapted the component, you can embed instances of this Slider Item wherever you need in your GUI project. Because it serves as template, it is intentionally kept very simple. Nevertheless, Slider Items created by the template are working widgets. If desired, they can already be used as they are. The following figure demonstrates the default appearance of the Slider Item created by using the here described component template:

The approach with component templates has two functions. Primarily the templates should simplify the development of new components. Instead of creating the Slider Item from scratch you can use the available template. The second function is more educative. The template implements fully working Slider Item component you can investigate and learn about the corresponding programming aspects. The template is well documented. It contains annotations and inline comments with instructions helping you to understand how the component works and how it can be adapted to your particular needs.

This chapter provides an overview how the Slider Item component template is used within your own application and how you adapt this component according to your particular needs. You will find here also further details concerning the internal implementation of the Slider Item component.

Add new Slider Item component

To create a new Slider Item component from a template you simply Drag & Drop it between the Templates window and the Composer with an opened unit. This is important in that by using the component templates you add in fact a new class to your project. Classes, in turn, can exist within units only. The following are the typical steps to create a new Slider Item component from a template:

First switch to the Composer page for the respective unit, where you want to add the new Slider Item component.

Then ensure that the Templates window is visible.

In Templates window switch to the folder Component Templates.

In the folder locate the Slider Item template.

Drag & Drop the template into the Composer window:

Eventually name the new added component.

The new created Slider Item component appears accompanied by annotation providing helpful tips how to proceed. If undesired, you can select and delete the annotation.

Use the Slider Item component

Once you have created the Slider Item component, you can use it to assemble menu components. Technically seen, you embed an instance of the Slider Item class in-place within a superior Menu GUI component. At the runtime, the superior Menu GUI component takes care of the correct initialization, arrangement and the displaying of all embedded item components.

Step 1. Add new Slider Item instance

The following are the typical steps to create a new instance of an already existing Slider Item component:

First switch to the Composer page for the respective Menu GUI component, where you want to add the new Slider Item.

Then ensure that the Browser window is visible.

Within the Browser locate the class of the previously created Slider Item. This can be done easily with Browser's own filter function.

Select the found class in the Browser window.

Drag & Drop the selected class into the Composer area.

Eventually name the new instance according to its function within the menu component.

Component templates are intended to create widgets which can be modified and adapted to your particular design expectations. In the following sections you will learn how to do this. Originally, if not yet modified, the Slider Item will appear composed of rectangles with item caption text. Our intention is to keep the component templates as minimalistic as possible so they don't distract you with less important design details.

Step 2. Inspect the Slider Item instance

As long as the Slider Item is selected you can inspect and modify its properties conveniently in the Inspector window as demonstrated with the property Bounds in the screenshot below. This is in so far worth mentioning as diverse features of the Slider Item are controlled by the corresponding properties. If you are not familiar with the concept of a property and the usage of Inspector window, please read first the preceding chapter Compositing component appearance.

The Slider Item component descends from the Mosaic class Core::Group. Consequently, most of the properties listed in the above screenshot are inherited from this base class. Particular to the Slider Item are only few following properties:

Property

Description

Caption

The property Caption stores the text to display within the item.

CurrentValue

The property CurrentValue stores the momentary value of the slider item. The value is limited automatically to the range between MinValue and MaxValue.

MaxValue

The property MaxValue defines the slider item's possible value at the right end of the slider's movement range.

MinValue

The property MinValue defines the slider item's possible value at the left end of the slider's movement range.

OnApply

The property OnApply can refer to a slot method, which will receive a signal when the user has released the item after dragging its knob. Thereupon the method's logic will be executed. In the associated slot method you can evaluate the item's current value CurrentValue.

OnChange

The property OnChange can refer to a slot method, which will receive a signal each time the user drags the item's knob. Thereupon the method's logic will be executed. In the associated slot method you can evaluate the item's current value CurrentValue.

Outlet

The property Outlet can refer to any other int32 property the item should remain synchronized with. When the user drags the slider's knob, the affected property is automatically updated to reflect the slider's current value. On the other hand, when the referred property is modified by another component, the item is automatically notified to remain in sync with the property.

This approach follows the Model-View-Controller (MVC) programming paradigm. Here the item represents the view and controller. The property referred via Outlet can be seen as a part of the model. See also Outlet properties.

Step 3. Arrange the Slider Item within the Menu component

The position of the Slider Item doesn't matter. The Menu component will automatically arrange the items within its area and if necessary enlarge or reduce their size. Moreover, the size of the menu itself is adapted to the number and to the size of the enclosed items. This layout algorithm is part of the Menu component template. Consequently, when you move an item within the menu, it does not have any effect on its final position.

However, the order in which the items will be displayed at the runtime does correspond to the order in which the item objects are stored within the menu component. This is the order in which the items have been originally added. Consequently, the first added item appears at the top of the menu while the last added item is arranged at the end of the menu. See also the column Order in the members area of the Inspector window:

If necessary you can reorder the item objects in the Inspector window to correspond to the order in which the items should appear in the menu.

Even if the position of an item doesn't matter, we recommend to move the item objects within the menu so you have a good overview of the items existing inside it. If the menu area is too small, you can enlarge it by clicking and dragging the thick blue Canvas border. Resizing the Canvas area of the menu manually has no effect on the final size of the menu. At the runtime the menu will adapt its size to the number and the size of the enclosed items.

Step 4. Implement Slider Item's slot methods

While the user interacts with the Slider Item, the component sends signals to associated slot methods. Within the slot method your particular implementation can react and process the event. If the Slider Item is controlled by keyboard events, the item can also be controlled by using the keys Plus or Minus. This is the default behavior implemented in the Slider Item template.

The slot methods are connected to the item by simply storing them in the for this purpose available properties OnChange and OnApply. The slot method connected to property OnChange is signaled each time the user drags the slider's knob. It means, while the user interacts with the item. In turn, the slot method connected to property OnApply is signaled when the user has released the item after dragging the slider's knob. It means, when the user has finalized the interaction. You can initialize the properties OnApply and OnChange with already existing slot methods or you add a new one and implement it as desired. The following steps describe how to do this:

First add a new slot method to your GUI component (the component where you have embedded the menu item).

Assign the slot method to the property OnChange of the affected Slider Item if you want to handle the events while the user drags the slider. If you want to handle the event when the user has finalized the interaction, use the property OnApply.

Open the slot method for editing.

In the Code Editor implement your desired operation to execute when the event occurs. Especially your implementation can evaluate the item's property CurrentValue to determine the actual value of the slider. For example:

// User has adjusted the volume slider SetVolumeLevel( VolumeSliderItem.CurrentValue );

When you start the Prototyper now, you can interact with the slider item. Thereupon the item will trigger the associated slot method.

Step 5. Connect the Slider Item with a data provider

To simplify the development of GUI applications, the Slider Item implements a technique permitting you to connect an item directly to a data provider. Once connected, the item will remain in sync with the value stored actually in this provider. Similarly, if the user drags the item's knob, the associated value changes automatically. This technique corresponds to the model-view-controller (MVC) programming paradigm, where the Slider Item has the function of the view and controller and the associated data provider serves as model. If you associate in your application several Slider Items to one and the same data provider value, the underlying mechanisms will ensure, that after interacting with one item all other affected items do update their state automatically.

The connection between the Slider Item and the data provider is established by assigning to the Slider Item's property Outlet a reference to a property existing within the data provider and storing the interesting value. Since Slider Item is intended to deal with integer values, the property suitable to be connected via reference to the item has to be declared with int32 as its data type. Accordingly, the value of the referenced property corresponds to the item's current value.

Summarized, after assigning a reference to an existing int32 property to the Slider Item's own property Outlet, the item adapts its own state to automatically correspond to the actual value of the associated property. When the user drags the item's knob, the associated property is modified. You don't need to write a single line of code to benefit from this mechanisms. The aspects underlying this technique are explained in the sections Outlet properties and Notifications and Observer.

Open the component for editing

Component templates are intended to create widgets which can be adapted and enhanced to your particular design expectations. For this purpose, once you have added a new Slider Item component to your project, you can open the component class for editing. Thereupon the implementation of the component appears in a new Composer page:

Originally, if not yet modified, the Slider Item appears as a white rectangle with a caption text and an embedded horizontal slider composed of a track and a knob. When the user interacts with the item, it changes color to provide visual feedback. Our intention is to keep the component templates as minimalistic as possible so they don't distract you with less important design details. The Slider Item can react to touch, mouse and keyboard inputs. The color and position of the knob rectangle and the border reflect the actual state of the item.

This default functionality is implemented by following members belonging to the Slider Item. These members are explicitly intended to be modified. Understanding this internal structure is thus the first step before you start to adapt the Slider Item to your particular needs:

Icon

Member

Description

Caption

The property Caption stores the text to display within the item.

OnSetCaption

The onset method belongs to the property Caption. Each time the value of the property is changed, the code implemented in the method is executed to update the caption text view and trigger layout recalculation in the owner menu.

CurrentValue

The property CurrentValue stores the momentary value of the slider item. This value corresponds to the actual position of the item's knob and it is limited automatically to the range specified by the properties MinValue and MaxValue.

MinValue

The property MinValue defines the minimum value for the item's property CurrentValue. This corresponds to the value at the left end of the slider's movement range.

MaxValue

The property MaxValue defines the maximum value for the item's property CurrentValue. This corresponds to the value at the right end of the slider's movement range.

OnSetCurrentValue

The onset method belongs to the property CurrentValue. Each time the value of the property is changed, the code implemented in the method is executed to update the position of the item's knob.

OnGetCurrentValue

The onget method belongs to the property CurrentValue. Each time the value of the property is evaluated within an expression, the code implemented in the method is executed to ensure that the returned value lies within the range specified in the properties MinValue and MaxValue.

OnSetMinValue

The onset method belongs to the property MinValue. Each time the value of the property is changed, the code implemented in the method is executed to update the position of the item's knob.

OnSetMaxValue

The onset method belongs to the property MaxValue. Each time the value of the property is changed, the code implemented in the method is executed to update the position of the item's knob.

Outlet

The property Outlet can refer to any other int32 property the item should remain synchronized with. When the user drags the slider's knob, the affected property is automatically updated to reflect the slider's current value. On the other hand, when the referred property is modified by another one, the item is automatically notified to remain in sync with the property.

This approach follows the model-view-controller (MVC) programming paradigm, where the Slider Item has the function of the view and controller and the property referred via Outlet serves as model. See also Outlet properties.

OnSetOutlet

The onset method belongs to the property Outlet. Each time a new property reference is assigned to Outlet, the code implemented in the method is executed to register the item as observer for the referred property.

OnChange

The property OnChange can refer to a slot method, which will receive a signal each time the user drags the slider's knob. It means, the method is signaled repeatedly while the user interacts with the item. Thereupon the method's logic will be executed. In the associated slot method you can evaluate the item's current value CurrentValue.

OnApply

The property OnApply can refer to a slot method, which will receive a signal as soon as the user has released the item after dragging the slider's knob. It means, the method is signaled when the user has finalized the interaction with the item. Thereupon the method's logic will be executed. In the associated slot method you can evaluate the item's current value CurrentValue.

onPressTouch

This internal slot method is called when the user touches the item's knob area (when the drag interaction begins). This slot method is connected to the member TouchHandler.

onReleaseTouch

This internal slot method is called when the user releases the touch screen after touching and evtl. dragging the item's knob (when the drag interaction ends). This slot method is connected to the member TouchHandler.

onDragTouch

This internal slot method is called when the user drags the finger while pressing the item's knob. The method calculates from the made displacement the alternation of the slider's value and updates the item accordingly. This slot method is connected to the member TouchHandler.

touchStartValue

This variable stores the item's value at the moment when the user begun to drag the knob. It is used to calculate the new value from the knob displacement.

KeyPlusHandler

This key handler reacts to key press events. When the user presses the key specified in the property Filter of the key handler (per default Plus), the handler is activated and the method onPressKey is called. When the user releases the key again, the method onReleaseKey is called.

KeyMinusHandler

This key handler reacts to key press events. When the user presses the key specified in the property Filter of the key handler (per default Minus), the handler is activated and the method onPressKey is called. When the user releases the key again, the method onReleaseKey is called.

onPressKey

This internal slot method is called when either the KeyPlusHandler or KeyMinusHandler has been activated (when the user has pressed the key specified in the property Filter of the respective key handler). Thereupon, the method increments or decrements the item's current value.

onReleaseKey

This internal slot method is called when either the KeyPlusHandler or KeyMinusHandler receives a key up event. With this the alternation of the item's value by using keys ends. The method has the job to notify the owner of the item that the user has finished the interaction.

keyStartValue

This variable stores the item's value valid at the moment when the user pressed a key handled by the KeyPlusHandler or KeyMinusHandler. It is used to detect the modification of the item's value when the user releases the key again.

onOutlet

This internal slot method will receive a signal, if the value of the property referred by Outlet has been changed by another widget or by the application logic. In response to this notification, the Slider Item will update its own CurrentValue property to reflect the new value of the referred property.

Background

This Filled Rectangle view displays in the template the background of the item. The background is white per default.

Border

This Border view displays in the template the border of the item. The border appears only when the item is selected (focused) or pressed. Depending on the item's actual state variables enabled, selected and dragging the border's visibility and color changes.

CaptionText

This Text view displays the caption text of the item. The text content is determined by the property Caption. Depending on the item's actual state, the text color changes between black and white/gray. The text view is configured with ellipsis mode to truncate long text with [...] sign.

Track

This Filled Rectangle view visualizes the track along which the knob is moved. The track is gray per default.

Knob

This Filled Rectangle view represents the knob the user can touch and drag with a finger. The rectangle moves then along the Track. Its position corresponds to CurrentValue within the range determined by the properties MinValue and MaxValue. Depending on the item's actual state variables enabled, dragging and selected the rectangle's color changes between gray and red.

TouchHandler

This Simple Touch Handler reacts to touch and mouse events and invokes the associated slot methods: onPressTouch, onReleaseTouch and onDragTouch.

UpdateViewState

This method is invoked automatically after the state of the component has been changed. In case of the Slider Item, the method updates the views Background, Border, CaptionText and Knob so they reflect the item's current state.

GetMinimalSize

This method is invoked by the owner menu to determine the minimum width required by this item. The method calculates the width based on the caption text length, slider track width plus margins. This information is used by the menu for automatic size adjustment.

enabled

This variable stores the current state of the item. It is true if the item is allowed to handle user inputs (the component is enabled). See also Control the Enabled state of nested components. If the item is actually disabled, the variable is consequently false.

selected

This variable stores the current state of the item. It is true if the item is actually focused for keyboard inputs (more precisely, the item is selected within its owner menu component). The user can thereupon control the item via keyboard.

dragging

This variable stores the current state of the item. It is true if the item should appear pressed. This is usually the case when the user touches the item's knob actively or presses a key intended to control the item (the user is dragging the slider's knob).

Understand the state management of the component

During its lifetime, the Slider Item will assume different states as direct effect of user interactions and as result of other superior state alternations within the GUI application. To track its current state the Slider Item manages the above mentioned state variables: enabled, selected and dragging. The variables are evaluated and updated within the implementation of the method UpdateViewState. This method evaluates the common component states (Enabled and Selected) and verifies whether the user is actually interacting with the item via touch screen or keyboard (the user is dragging the item's knob). At the end of the method the estimated states are stored in the state variables:

// Estimate the new state var bool isEnabled = aState.contains( Core::ViewState[ Enabled ]); var bool isSelected = aState.contains( Core::ViewState[ Selected ]); var bool isDragging = TouchHandler.Down || KeyPlusHandler.Down || KeyMinusHandler.Down; [...] // Remember the new state enabled = isEnabled; selected = isSelected; dragging = isDragging;

For example, the local variable isDragging will become true if the user is actually touching within the area of the item's knob (the variable Down of the TouchHandler is true) or the user is pressing a key destined to increment or decrement the item's value (the variable Down of the KeyPlusHandler or KeyMinusHandler is true). Being in this state, the item should assume the appearance indicating that the user is actively dragging it.

When common component states (Enabled or Selected) have changed, the method UpdateViewState is invoked automatically. You don't need to worry about it. Any other state alternations need to explicitly request an invocation of the UpdateViewState method. This is especially true for state alternations caused by touch handlers, keyboard handlers or timer objects. When you modify the Slider Item to handle other user inputs or perform other animations always ensure that each state alternation (each event) does request an invocation of UpdateViewState. In your implementation you use for this purpose the method InvalidateViewState.

For example, the slot method onPressTouch is associated to the TouchHandler and it is invoked when the user presses the finger within the item's knob area. In response to this, the dragging state of the item has to be updated. The method onPressTouch requests thus the invocation of UpdateViewState:

// Touching the item may affect its appearance to change. Request the // UpdateViewState() method to be called in order to refresh the items's // appearance. InvalidateViewState();

Depending on your design expectations the default state management may require adaptations. Generally, you are free to modify the method UpdateViewState, add further state variables to your Slider Item or even remove the existing functionality if it is not needed in your case.

For example, the default implementation of the Slider Item template manages the state selected. This state indicates that the item is actually selected within the superior menu. The item could thus receive keyboard events. However, whether the item is really able to receive events does depend on the selection status of its owner menu and all other superior components. Only when the item lies on the focus path, the item will receive keyboard events. If your application case requires the item to distinguish this additional state, you can add a variable focused similar to the approach described in the Horizontal Slider template.

Adapt the appearance of the component

Originally, if not yet modified, the Slider Item appears as a white rectangle with a caption text and an embedded horizontal slider. Our intention is to keep the component templates as minimalistic as possible so they don't distract you with less important design details. It's up to you to adapt the item to have the expected appearance. The available possibilities are explained in the following sections. Please note, that you can combine the different approaches according to your application case:

1. Modify existing views

The Slider Item template contains per default the views Background, Border, CaptionText, Track and Knob. If desired, you can modify the views. For example, you can display them with other colors depending on the actual state of the item. For this purpose you change the implementation of the above explained method UpdateViewState responsible for tracking the state alternations within the Slider Item.

When you open the method UpdateViewState you will see that it does not only update the state variables but also updates the views existing within the component. Accordingly, depending on the actual state of the item the views Background, Border, CaptionText and Knob are displayed with other colors and the border's visibility changes. By simply modifying this implementation you change the appearance. For example, you change the knob color from red to green when the user actively drags on it:

if ( !isEnabled ) { Background.Color = #AAAAAAFF; Border.Visible = false; CaptionText.Color = #888888FF; Knob.Color = #CCCCCCFF; } else if ( isDragging ) { Background.Color = #FFFFFFFF; Border.Color = #000000FF; Border.Visible = true; CaptionText.Color = #000000FF; Knob.Color = #00FF00FF; // <-- green color } else if ( isSelected ) { Background.Color = #FFFFFFFF; Border.Color = #444444FF; Border.Visible = true; CaptionText.Color = #000000FF; Knob.Color = #444444FF; } // Enabled but not dragging nor selected. else { Background.Color = #FFFFFFFF; Border.Visible = false; CaptionText.Color = #000000FF; Knob.Color = #444444FF; }

In case of the Slider Item component the implementation of the UpdateViewState method also calculates the position of the view Knob from the item's CurrentValue property and the value range determined by the properties MinValue and MaxValue. Per default, the knob can move along the entire width of the track:

// Get the current pixel position of the item's knob (corresponds // to the touch handler area) and the min/max positions the user may // drag the knob within the slider's bounds. var rect knobArea = Knob.GetExtent(); var int32 minPos = Track.Bounds.x1; var int32 maxPos = Track.Bounds.x2 - knobArea.w; var point oldKnobPos = knobArea.origin; var point newKnobPos = oldKnobPos; // Convert the item's current value to a pixel position within the // range allowed for the knob to be moved. if ( MaxValue != MinValue ) newKnobPos.x = ((( CurrentValue - MinValue ) * ( maxPos - minPos )) / ( MaxValue - MinValue )) + minPos; // If the just calculated new knob position does differ from the knob's // current position, move the knob (and the associated touch handler) // accordingly. if ( newKnobPos != oldKnobPos ) { TouchHandler.MoveView( newKnobPos - oldKnobPos, false ); Knob. MoveView( newKnobPos - oldKnobPos, false ); }

If desired, this implementation can be modified and the position of the knob can be calculated in a different manner (for example, with other margins or in a different area). Since such adaptation affects the implementation of other methods found in the item, this task is explained separately in the section Understand the calculation of knob's position.

2. Remove existing views

If not needed, you can delete the per default existing views Background, Border, CaptionText, Track or Knob. Doing this, please don't forget to also remove all references to the deleted views from the implementation of the UpdateViewState and GetMinimalSize methods. Otherwise you will get error messages next time the Composer contents is reloaded. To avoid this error message we recommend to perform the steps in following order:

In the UpdateViewState method adapt the implementation to not refer anymore to the undesired view. For example, if you decided to delete the Border view, simply remove all corresponding code lines. The remaining implementation looks then like this:

if ( !isEnabled ) { Background.Color = #AAAAAAFF; CaptionText.Color = #888888FF; Knob.Color = #CCCCCCFF; } else if ( isDragging ) { Background.Color = #FFFFFFFF; CaptionText.Color = #000000FF; Knob.Color = #FF0000FF; } else if ( isSelected ) { Background.Color = #FFFFFFFF; CaptionText.Color = #000000FF; Knob.Color = #444444FF; } // Enabled but not dragging nor selected. else { Background.Color = #FFFFFFFF; CaptionText.Color = #000000FF; Knob.Color = #444444FF; }

Review the method GetMinimalSize whether it contains any references to the affected view. Modify the code appropriately.

Now you can delete the affected view.

3. Add further views to the component

You can add further views to your Slider Item. For example, you can add an Image view to display an icon within the item. Or you add a Text view to display the current slider value as text. The appearance of the views (e.g. colors) can be fixed, predetermined at the design time. Or it can change according to the actual state of the item. For example, if the user is actually dragging item's knob, the text could show the value with different color. Following steps explain the workflow:

First add the desired view to the component. For example add a Text view.

Name the just added view according to its function in the component. For example ValueText.

In order to arrange the view within the Slider Item, move the view, or you simply grab one of its corners and resize it in this way. If you want the view to appear behind other views you can reorder it explicitly.

In Inspector window configure the properties of the just added view. For example, in case of the Text view you can select the font to render the text.

In the UpdateViewState method adapt the implementation to update the properties of the view according to the item's current state. This can be, for example, the string content and color of the Text view displaying the current value. For this purpose you could add following code to the method:

// Update the appearance of the ValueText view to show current value. ValueText.String = string( CurrentValue ); if ( !isEnabled ) ValueText.Color = #CCCCCCFF; // light gray color // Enabled, evtl. selected or pressed. else ValueText.Color = #000000FF; // black color

If you added elements that affect the item's minimum width, don't forget to adapt the implementation of the GetMinimalSize method to include the additional space needed:

// We assume: the item requires space for the caption, the value text view, // the slider track, and margins between these elements. var bool ellipsis = CaptionText.Ellipsis; CaptionText.Ellipsis = false; var point size = point( CaptionText.GetContentArea().w + 10 + ValueText.Bounds.w + 10 + Track.Bounds.w + 10, 0 ); CaptionText.Ellipsis = ellipsis; // Assume the min. height corresponds to the actual design height of the item. size.y = 40; return size;

4. Replace existing views

Also possible, you can replace the existing views by other views. For example, in the original version, the Slider Item displays the knob as an ordinary filled rectangle. To make the component more sophisticated, you could replace the here used Filled Rectangle view by e.g. Image view. For this purpose:

Think about which view is adequate as replacement for the old view existing per default in the component. An overview of existing views is found in the section Using Views.

Delete the old view.

Add the desired view to the component.

Name the just added view to have the name of the previously removed view.

Eventually move the view, or you simply grab one of its corners and resize it in this way. If you want the view to appear behind other views you can reorder it explicitly.

If necessary, in Inspector window configure the properties of the just added view.

If necessary, modify the implementation of other methods existing in the component if these evaluate/modify the properties of the replaced view.

Configure the layout of the component

The initial size of the Slider Item is determined by the thick blue border surrounding the Canvas area. It corresponds to the default size that all instances of this Slider Item component will have. However, when menu items are embedded within a menu, the Menu component will automatically adjust the size of items according to:

The content of the item (especially the caption text length and slider width)

The minimum size calculated by the item's GetMinimalSize method

The formation of the menu (horizontal or vertical)

The available space within the menu

The available space on the display

If desired, you can adjust the Canvas area of the item component itself. This affects the default height and visual proportions of the item during editing. For this purpose you click and drag the edges of the surrounding border (see also Resize the Canvas area). Once the size is changed, you can then adapt (move, resize) the views existing within the component.

The views existing per default in the Slider Item template (Background, Border, CaptionText and TouchHandler) are configured to automatically adjust to fill the area of the item. The Track and Knob views are positioned according to their layout configuration. All other views you have eventually added later to the Slider Item are not adjusted automatically.

To control the adjustment you have to explicitly configure for each view its Layout property. (see also Configure component layout). Let's assume, in order to display the current value you have added a Text view to the Slider Item. Then you have arranged the view within the Canvas area according to your design expectation. If you want now that the view remains aligned at the right side when the item width changes, you enable in the property Layout of this view following settings:

Implement the interface of the component

When creating your own Slider Item component you should ensure that instances of the item can be configured to control all the features implemented in it. For example, if you have enhanced the item to display some icon or unit text, you should allow these to be specified individually for each item instance. In this way several item instances can exist at the same time, each displaying another icon or unit text.

To control the features in your item you use properties. A property can be understood as variable where the corresponding setting is stored, e.g. the unit text to display next to the slider. When the value of the property changes, the item can react to it and update its appearance accordingly. The properties reflect thus the settings within your item. Together they form the interface of the component.

Usually, each particular setting within a component is represented by the corresponding property. Thus, an item where icon and unit text can be configured will require two properties: one for the icon and one for the unit text. In its original version, the Slider Item contains already seven properties Caption, CurrentValue, MinValue, MaxValue, Outlet, OnApply and OnChange. It allows the item instance to configure its caption, actual value, value range, or to exchange data with other components. In order to enhance this interface by your own properties, following steps are necessary:

Add a new property to the Slider Item component.

Name the property according to the setting it should represent. For example, the property intended to store the unit text could be named Unit.

Determine the data type of the property. For example, the property intended to store the unit text will store a string.

Determine the initialization value of the property. This value should correspond to the item's default state. For example, the property intended to store the unit text could be initialized with "" if no unit should be displayed by default.

The property is accompanied by its onget method. Except particular cases, this method is not needed and can be deleted now.

The property is accompanied by its onset method. Open this method for editing.

Adapt the implementation of the onset method so it updates the item according to its new value. For example, in case of the property intended to store the unit text, you will probably update some Text view where the unit is displayed:

// The value doesn't change - nothing to do. if ( pure Unit == value ) return; // Remember the property's new value. pure Unit = value; // Update the view to display the just modified unit text. UnitText.String = value; // Changing the unit may affect the minimum width of the item. Request the // superior menu to recalculate its layout. if ( Owner ) Owner.InvalidateLayout();

If the new property affects the minimum size of the item (like unit text or additional decorations do), adapt also the GetMinimalSize method to include the space needed by the new element.

That is all. Now when you deal with instances of the Slider Item component, you can evaluate and modify the properties similarly to how you access variables. Especially, when the item instance is selected, you see in Inspector window the property and can change it there. The modification is immediately visible in the Composer window.

If desired, the properties can also be modified at the runtime of your application. For example, the slider value range can be adapted to reflect different settings. For this purpose you access and modify the affected property directly within Chora code:

var int32 maxSpeed = ... // Let the slider adapt its range to current maximum speed. SpeedSliderItem.MaxValue = maxSpeed;

Understand the handling of touch inputs

The Slider Item is an interactive component. It can be controlled via touch screen or by the mouse device. For this purpose the template contains the Simple Touch Handler object named TouchHandler. Per default, this handler occupies the area corresponding to the item's knob - this is thus the touch sensitive area. To this handler are associated three slot methods: onPressTouch, onReleaseTouch and onDragTouch. As their names imply the methods are invoked at the beginning of the interaction (press), at its end (release) and repeatedly during the interaction when the user drags the finger (mouse pointer):

The methods take care of two tasks. As first they request an invocation of the UpdateViewState method if due to the performed touch (mouse) interaction the visual appearance of the item does need the update. The second task is the calculation from the recent touched position the corresponding item's value and sending signals to slot methods associated to item's properties OnChange and OnApply if due to the interaction the value has been changed.

In order to be able to detect value alternations, just in the moment when the user starts the touch (mouse) interaction with the item, the property CurrentValue is stored in the internal variable touchStartValue. The variable provides thus a reference value for further comparisations with CurrentValue. This is implemented in the slot method onPressTouch:

// Clicking on a menu item focuses (selects) the item. If you don't want it, // remove the following code line. Owner.Focus = this; // To avoid interferences when the user tries to interact with the item // simultaneously via keyboard and touch screen, disable the key handler // while the touch interaction is in progress. KeyPlusHandler.Enabled = false; KeyMinusHandler.Enabled = false; // Touching the item may affect its appearance to change. Request the // UpdateViewState() method to be called in order to refresh the items's // appearance. InvalidateViewState(); // Remember which was the item's value at the beginning of the knob drag // operation. It is used in the slot method 'onDragTouch' to calculate the // value from the movement the user made on the screen. touchStartValue = CurrentValue;

Later, when the user finalizes the touch (mouse) interaction, the property CurrentValue is compared with the internal variable touchStartValue. If the value has changed, a signal is sent to the slot method stored in the property OnApply. The owner of the item can thereupon react to this notification. Consequently, this functionality is implemented in the slot method onReleaseTouch:

// To avoid interferences when the user tries to interact with the item // simultaneously via keyboard and touch screen, the key handler are disabled // while the touch interaction is in progress. When the touch interaction // ends, enable the key handlers again. KeyPlusHandler.Enabled = true; KeyMinusHandler.Enabled = true; // When the touch interaction ends, the appearance of the item may change. // Request the UpdateViewState() method to be called in order to refresh the // item's appearance. InvalidateViewState(); // Did the user really changed the item's value? If yes, notify the owner. if ( CurrentValue != touchStartValue ) postsignal OnApply;

The slot method onDragTouch is invoked when the user drags the finger while touching within the area of the item's knob. The implementation of the method calculates from the new touch position the corresponding CurrentValue and sends signals to the slot method stored in the property OnChange. The owner of the item can thereupon react to this notification:

// Calculate the displacement in pixel relative to the originally touched position. // Also calculate the possible movement area for the knob --> the range between // 'minPos' and 'maxPos'. var point delta = TouchHandler.CurrentPos - TouchHandler.HittingPos; var int32 minPos = Track.Bounds.x1; var int32 maxPos = Track.Bounds.x2 - Knob.GetExtent().w; var int32 oldValue = CurrentValue; // Calculate from the knob displacement the alternation of the item's value. // Knowing the value at the beginning of the drag interaction -> get the new // item's value. if ( maxPos > minPos ) CurrentValue = (( delta.x * ( MaxValue - MinValue )) / ( maxPos - minPos )) + touchStartValue; // The displacement was too small to change the item's value or the knob has // already reached the min/max position. if ( oldValue == CurrentValue ) return; // Notify the owner of the item that the value has been changed. postsignal OnChange; // If a property is associated to the item, update it accordingly and notify // other widgets also associated to this property. if ( Outlet != null ) { Outlet^ = CurrentValue; notifyobservers Outlet; }

Usually you will not need to edit the implementation of these methods. It corresponds already to whatever typical slider items do. Nevertheless, you are free to change this default functionality if you want some particular behavior to be implemented in your item. For example, in the default implementation, the knob can move along the entire width of the track. If desired, this can be modified and the position of the knob can be calculated in a different manner. Since such adaptation affects the implementation of other methods found in the item, this task is explained separately in the section Understand the calculation of knob's position.

Per default the TouchHandler is configured to handle all touch events. If necessary you can change this behavior and restrict the item to react only when the user performs a particular touch interaction. The TouchHandler provides for this purpose diverse configuration properties like MinStrikeCount, MaxStrikeCount, NoOfFingers or LimitToFinger. For more details, see Configure the filter condition.

If your device does not contain any touch screen nor mouse, the per default existing TouchHandler and its associated slot methods are unnecessary ballast. You can remove them in such case. We recommend to do this in following order to avoid eventual error message because of broken references between still existing and the already removed members:

First remove all expressions accessing TouchHandler from the UpdateViewState method.

Edit the onPressKey method and remove the expressions accessing TouchHandler.

Edit the onReleaseKey method and remove the expressions accessing TouchHandler.

Using the Inspector window delete the TouchHandler object.

Delete the three slot methods: onPressTouch, onReleaseTouch and onDragTouch.

Delete the internal variable: touchStartValue.

Finally delete the annotation grouping the slot methods.

Understand the handling of keyboard inputs

The Slider Item is an interactive component. It can be controlled via keyboard or hardware buttons. For this purpose the template contains two Key Press Handler objects named KeyPlusHandler and KeyMinusHandler. To the both handlers are associated the slot methods: onPressKey and onReleaseKey. The methods are thus invoked when the user presses or releases a key matching the filter condition specified in one of the handler objects. These are per default the keys Plus and Minus:

The methods take care of two tasks. As first they request an invocation of the UpdateViewState method if due to the performed interaction the visual appearance of the item does need the update. The second task is the increment/decrement of the item's value and sending signals to slot methods associated to item's properties OnChange and OnApply if due to the interaction the value has been changed.

In order to be able to detect value alternations, just in the moment when the user starts the keyboard interaction with the item, the property CurrentValue is stored in the internal variable keyStartValue. The variable provides thus a reference value for further comparisations with CurrentValue. This is implemented in the slot method onPressKey:

// To avoid interferences when the user tries to interact with the item // simultaneously via keyboard and touch screen, disable the touch handler // while the keyboard interaction is in progress. TouchHandler.Enabled = false; // Pressing a key may affect the appearance of the item to change. Request // the UpdateViewState() method to be called in order to refresh the item's // appearance. InvalidateViewState(); var int32 oldValue = CurrentValue; // With the first key press event, remember which was the item's value at // the beginning of the interaction. Later when the user releases the key, // this remembered value is compared with the current value to decide whether // notify or not the owner about an alternation of the value. if ( !((Core::KeyPressHandler)sender).Repetition ) keyStartValue = CurrentValue; // Depending on which key handler has fired, increment or decrement the // item's current value. if ( sender == KeyPlusHandler ) CurrentValue = oldValue + 1; if ( sender == KeyMinusHandler ) CurrentValue = oldValue - 1; // The knob has already reached the min/max position. if ( oldValue == CurrentValue ) return; // Notify the owner of the item that the value has been changed. postsignal OnChange; // If a property is associated to the item, update it accordingly and notify // other widgets also associated to this property. if ( Outlet != null ) { Outlet^ = CurrentValue; notifyobservers Outlet; }

Later, when the user finalizes the keyboard interaction, the property CurrentValue is compared with the internal variable keyStartValue. If the value has changed, a signal is sent to the slot method stored in the property OnApply. The owner of the item can thereupon react to this notification. Consequently, this functionality is implemented in the slot method onReleaseKey:

// To avoid interferences when the user tries to interact with the item // simultaneously via keyboard and touch screen, the touch handler is disabled // while the keyboard interaction is in progress. When the keyboard interaction // ends, enable the touch handler again. TouchHandler.Enabled = true; // Releasing a key may affect the appearance of the item to change. Request // the UpdateViewState() method to be called in order to refresh the item's // appearance. InvalidateViewState(); // Did the user really changed the item's value? If yes, notify the owner. if ( CurrentValue != keyStartValue ) { postsignal OnApply; // If a property is associated to the item, update it accordingly and notify // other widgets also associated to this property. if ( Outlet != null ) { Outlet^ = CurrentValue; notifyobservers Outlet; } }

Usually you will not need to edit the implementation of those methods. It corresponds already to whatever typical slider items do. Nevertheless, you are free to change this default functionality if you want some particular behavior to be implemented in your item. For example, you can change the increment/decrement step so the item accelerates when the user continues pressing the key. In such case you could modify the onPressKey method as shown in the code below. Here the increment/decrement depends on the counter RepetitionCount of the respective key handler. The longer the user holds the key pressed, the bigger the counter:

// Depending on which key handler has fired, increment or decrement the // item's current value. Note, the longer the user presses the key, // the bigger the increment/decrement. if ( sender == KeyPlusHandler ) CurrentValue = oldValue + 1 + ( KeyPlusHandler. RepetitionCount / 10 ); if ( sender == KeyMinusHandler ) CurrentValue = oldValue - 1 - ( KeyMinusHandler.RepetitionCount / 10 );

In some application cases, the handling of keyboard repetitions is not desired and the item is expected to react on the press event only. In such case, you can add following condition to the onPressKey method. In this version, the item increments/decrements its value only in the moment when the user presses the key. Holding the key pressed has no effect:

// Pressing a key may affect the appearance of the item to change. Request // the UpdateViewState() method to be called in order to refresh the item's // appearance. InvalidateViewState(); // Ignore repetitions. if (((Core::KeyPressHandler)sender).Repetition ) return; [...]

Per default the key handlers are configured to accept the keys Plus and Minus. If necessary you can change this behavior and adapt the item to react to other keys or key groups. The key handler provides for this purpose the configuration property Filter. For example, if you want the item to react when the user presses the Left and Right keys, set the property Filter of the object KeyPlusHandler to the value Core::KeyCode.Right and the of the object KeyMinusHandler to the value Core::KeyCode.Left. For more details, see Configure the filter condition.

If your device does not contain any keyboard nor hardware buttons, the per default existing key handler and their associated slot methods are unnecessary ballast. You can remove them in such case. We recommend to do this in following order to avoid eventual error messages because of broken references between still existing and the already removed members:

First remove all expressions accessing KeyPlusHandler and KeyMinusHandler from the UpdateViewState method.

Similarly, remove all expressions accessing KeyPlusHandler and KeyMinusHandler from the onPressTouch method.

Similarly, remove all expressions accessing KeyPlusHandler and KeyMinusHandler from the onReleaseTouch method.

Then delete the KeyPlusHandler and KeyMinusHandler objects.

Then delete the slot methods: onPressKey and onReleaseKey.

Delete the internal variable: keyStartValue.

Finally delete the annotation grouping the handlers and the slot methods.

Understand the GetMinimalSize method

The GetMinimalSize method plays a crucial role in the automatic layout system of menus. This method is called by the owner menu to determine how much horizontal space the item requires at minimum. The menu uses this information to:

Calculate the minimum width needed to display all items without truncating content

Adjust its own size to fit all items comfortably

Determine when scrolling is needed for large menus

The default implementation calculates the minimum width based on the caption text length, the slider track width plus margins:

// We assume: the item requires space for the caption and 20 pixel margin on // the left and on the right of the caption plus the space needed for the slider. // Important: perform the calculation without the caption text being evtl. // truncated and replaced by an ellipsis sign. var bool ellipsis = CaptionText.Ellipsis; CaptionText.Ellipsis = false; var point size = point( CaptionText.GetContentArea().w + 20 + Track.Bounds.w, 0 ); CaptionText.Ellipsis = ellipsis; // Assume the min. height corresponds to the actual design height of the item. size.y = 40; return size;

Please note that the calculation temporarily disables the ellipsis mode of the text view to get the full text width without truncation. This ensures that the menu knows how much space is really needed to display the complete caption.

If you have modified the item appearance by adding icons, value display, unit text, or other decorations, you must adapt this method to include the additional space needed. For example, if you added an icon on the left side and a text view showing the current value on the right, take their widths as well as additional margins in account:

// Include space for: icon + margin (10px) + caption + margin (10px) + // slider track + margin (10px) + value text + margin (10px) var bool ellipsis = CaptionText.Ellipsis; CaptionText.Ellipsis = false; var point size = point( ItemIcon.Bounds.w + 10 + CaptionText.GetContentArea().w + 10 + Track.Bounds.w + 10 + ValueText.Bounds.w + 10, 0 ); CaptionText.Ellipsis = ellipsis; size.y = 40; return size;

TIP

For horizontal menus: If you configure the menu to arrange items horizontally (by setting the Formation property of the menu's Outline to Core::Formation.LeftToRight), you need to adapt this method to calculate the minimum height instead of minimum width.

Understand the Outlet updates

The Slider Item can be connected via its property Outlet with any other int32 property the item should remain synchronized with. When the user drags the slider's knob, the affected property is automatically updated to reflect the slider's current value. On the other hand, when the referred property is modified by another one, the item is automatically notified to remain in sync with the property. This approach follows the model-view-controller (MVC) programming paradigm, where the Slider Item has the function of the view and controller and the property referred via Outlet serves as model. See also Outlet properties.

When the state of the item changes due to a user interaction, the item updates the value of the referred property. This is, for example, the case in the slot method onDragTouch. The implementation found there simply assigns the actual value of the Slider Item's CurrentValue property to the property referred via Outlet and broadcasts a notification to all other widgets connected to the same property so they also can update their state accordingly:

// If a property is associated to the item, update it accordingly and notify // other widgets also associated to this property. if ( Outlet != null ) { Outlet^ = CurrentValue; notifyobservers Outlet; }

In order to react to alternations of the referred property (to receive notifications broadcasted by other widgets connected with the same property), the Slider Item implements the method onOutlet. Each time the referred property is notified, this slot method is automatically invoked and following code is executed:

// Read the current value of the associated property and update accordingly // the state of the item. if ( Outlet != null ) CurrentValue = Outlet^;

In order to be invoked, the method onOutlet needs to be registered with the referred property as so-called observer. The corresponding code is implemented in the onset method OnSetOutlet. Thus, just in the moment when a property reference is assigned to the Slider Item's Outlet property, following code is executed:

// Check if the new outlet differs from the currently used outlet if ( pure Outlet == value ) return; // Detach from the previous outlet if ( pure Outlet != null ) detachobserver onOutlet, pure Outlet; // Store the new outlet ... pure Outlet = value; // ... and attach to the new one if ( value != null ) attachobserver onOutlet, value; // Finally, retrieve the current state of the property associated via 'Outlet'. if ( value != null ) postsignal onOutlet;

Understand the calculation of knob's position

The position of the item's knob correlates directly with the value of the property CurrentValue and the range specified by the properties MinValue and MaxValue. If CurrentValue is equal MinValue, the knob is aligned at the leftmost position. If CurrentValue is equal MaxValue, the knob is rightmost aligned. All other intermediate values result in the knob position being interpolated linearly within this range of motion. The code responsible to adjust the knob's position is implemented in the above explained method UpdateViewState:

// Get the current pixel position of the item's knob (corresponds // to the touch handler area) and the min/max positions the user may // drag the knob within the slider's bounds. var rect knobArea = Knob.GetExtent(); var int32 minPos = Track.Bounds.x1; var int32 maxPos = Track.Bounds.x2 - knobArea.w; var point oldKnobPos = knobArea.origin; var point newKnobPos = oldKnobPos; // Convert the item's current value to a pixel position within the // range allowed for the knob to be moved. if ( MaxValue != MinValue ) newKnobPos.x = ((( CurrentValue - MinValue ) * ( maxPos - minPos )) / ( MaxValue - MinValue )) + minPos; // If the just calculated new knob position does differ from the knob's // current position, move the knob (and the associated touch handler) // accordingly. if ( newKnobPos != oldKnobPos ) { TouchHandler.MoveView( newKnobPos - oldKnobPos, false ); Knob. MoveView( newKnobPos - oldKnobPos, false ); }

Please note in the above implementation that when moving the Knob view, the position of the TouchHandler is adapted accordingly. This is because the knob is per default the unique touch-sensitive area. Consequently, when the knob changes the position, the touch handler has to follow. In this default implementation, the knob can move along the entire width of the Track view. You can modify this default implementation and, for example, calculate with different margins or positioning. In such case, however, you should also adapt the counterpart of this calculation found in the method onDragTouch.

Each time, the user touches within the area of the item's knob and drags the finger thereupon, the method onDragTouch is invoked. As explained in the section Understand the handling of touch inputs the implementation of this method calculates from the made drag operation and the variable touchStartValue a new item's value. The variable touchStartValue contains a copy of the CurrentValue property just at the beginning of the touch interaction. Consequently, changes resulting from a drag operation are calculated relative to this value:

// Calculate the displacement in pixel relative to the originally touched position. // Also calculate the possible movement area for the knob --> the range between // 'minPos' and 'maxPos'. var point delta = TouchHandler.CurrentPos - TouchHandler.HittingPos; var int32 minPos = Track.Bounds.x1; var int32 maxPos = Track.Bounds.x2 - Knob.GetExtent().w; var int32 oldValue = CurrentValue; // Calculate from the knob displacement the alternation of the item's value. // Knowing the value at the beginning of the drag interaction -> get the new // item's value. if ( maxPos > minPos ) CurrentValue = (( delta.x * ( MaxValue - MinValue )) / ( maxPos - minPos )) + touchStartValue;

Let's assume you want to change the slider to use a different track area or add margins around the knob movement range. In such case, you would need to make following modifications in the item component:

In the method UpdateViewState change the initialization of the local variables minPos and maxPos according to the desired positioning (e.g. with 10 pixel margins):

var int32 minPos = Track.Bounds.x1 + 10; // 10 pixel left margin var int32 maxPos = Track.Bounds.x2 - 10 - knobArea.w; // 10 pixel right margin

Do the same adaptation in the method onDragTouch.

The default implementation of the Slider Item provides horizontal slider control, which is suitable for menus arranged vertically (top to bottom). However, the Slider Item can also be adapted to provide vertical slider control. This would be typically convenient for menus with horizontal formation (left to right or right to left).

To adapt the slider item for vertical operation, you need to modify the knob position calculations in both the UpdateViewState and onDragTouch methods. Instead of calculating and using the x coordinate of the knob position, you would calculate and use the y coordinate. The track boundaries Track.Bounds.x1 and Track.Bounds.x2 would be replaced by Track.Bounds.y1 and Track.Bounds.y2, and the knob would move vertically instead of horizontally. Additionally, you would need to rotate or reposition the Track and Knob views to appear vertical, and configure the TouchHandler property RetargetCondition to use Core::RetargetReason[WipeLeft, WipeRight] instead of Core::RetargetReason[WipeDown, WipeUp] to allow horizontal wiping gestures in the menu while the slider handles vertical dragging.

Perform state changes with animations

In the section Adapt the appearance of the component you learned how state alternations within the Slider Item are processed and how views existing in the component are updated in order to reflect the actual state. Accordingly, when the user touches the item (starts the dragging interaction), the item does appear pressed. When the user releases the item again (ends the dragging interaction), the item restores its released appearance. The default implementation performs such appearance updates instantly, just in the moment when the respective interaction took place.

If desired, you can modify the Slider Item to update its appearance with animations. For example, instead of instantly switching between the dragging and released appearance, such item can fade-in/out the views, modulate their colors or animate the knob size. For this purpose you use the Animation Effects. With an Animation Effect you can animate a property of a view existing in the item. Following are the steps how to do this:

Depending on the data type of the property to animate, add the appropriate Animation Effect to the Slider Item. For example, to animate a color property, add a Pulse color effect.

Connect the effect object with the property to animate. For example, if you want to animate the color of the Knob view, connect the Pulse color effect with the property Color of the Knob view.

Configure the duration and eventually the desired timing (easing) for the animation.

Once started, the effect will animate the property endlessly. In our case, however, the animation should stop at its end. For this purpose set the effect's property NoOfCycles to the value 1.

Configure the key values for the animation. In case of the pulse color effect, it is the start and the end color. For example, to animate the color between dark gray and red, configure the effect's property Value1 with the #444444FF (dark gray) and the property Value2 with #FF0000FF (red). This step is appropriate if the key values are fixed, known at the design time. Otherwise, the key values must be set at runtime shortly before the effect is triggered.

Open the method UpdateViewState and modify its implementation to trigger the effect object when the item switches between the states dragging and not dragging. For example in case of the above mentioned pulse color effect to animate the color of the Knob, following implementation could be adequate:

// Estimate the new state var bool isEnabled = aState.contains( Core::ViewState[ Enabled ]); var bool isSelected = aState.contains( Core::ViewState[ Selected ]); var bool isDragging = TouchHandler.Down || KeyPlusHandler.Down || KeyMinusHandler.Down; [...] // Following code updates the views instantly, without animations. if ( !isEnabled ) { Background.Color = #AAAAAAFF; Border.Visible = false; CaptionText.Color = #888888FF; } else if ( isDragging ) { Background.Color = #FFFFFFFF; Border.Color = #000000FF; Border.Visible = true; CaptionText.Color = #000000FF; } else if ( isSelected ) { Background.Color = #FFFFFFFF; Border.Color = #444444FF; Border.Visible = true; CaptionText.Color = #000000FF; } // Enabled but not dragging nor selected. else { Background.Color = #FFFFFFFF; Border.Visible = false; CaptionText.Color = #000000FF; } // Switching from not dragging -> dragging state. Start the animation. if ( isEnabled && isDragging && !dragging ) { ColorEffect.Reversed = false; ColorEffect.Enabled = true; } // Switching from dragging -> not dragging state. Start the animation. else if ( isEnabled && !isDragging && dragging ) { ColorEffect.Reversed = true; ColorEffect.Enabled = true; } // Remember the new state enabled = isEnabled; selected = isSelected; dragging = isDragging;

Since the properties of the affected views are now modified with animations, you will eventually need to adapt their initial values to correspond to the default state of the item. For example, you might need to set the property Color of the view Knob to the value #444444FF (default color is dark gray).