![ ](../assets/img/creating-custom-controls/control-handle-body.png)
![ ](creating-custom-controls/control-handle-body.png)
+Namespaces are important
++ The handle & body classes should have the same name but in different namespaces
++ TypeRegistry relies on this convention
++ Here our custom control requires
+ + MyUIControl
+ + Internal::MyUIControl
+
### General Guidelines:
+ Try to avoid adding C++ APIs as they become difficult to maintain.
+ Use **properties** as much as possible as Controls should be data driven.
+ These controls will be used through JavaScript and JSON files so need to be compatible.
-+ Bear in mind that the Control is required to update when the properties change, not just the first time they are set (to deal with style change).
++ Bear in mind that the Control can be updated when the properties change (e.g. style change)
+ + Ensure control deals with these property changes gracefully
+ + Not just the first time they are set
++ Use Visuals rather than creating several child Actors
+ + DALi rendering pipeline more efficient
+ Accessibility actions should be considered when designing the Control.
+ Consider using signals if the application needs to be react to changes in the control state.
++ Use of Gestures should be preferred over analysing raw touch events
++ Check if you need to chain up to base class if overriding certain methods
___________________________________________________________________________________________________
![ ](../assets/img/creating-custom-controls/rendering.png)
![ ](creating-custom-controls/rendering.png)
-
+
+To add a visual to a control, first create a Property for the visual of type MAP, and ensure the name has a suffix of "_VISUAL". Then the visual is normally defined in the stylesheet, and the definition sent via SetProperty(), where you would then create the visual:
+
+~~~{.cpp}
+// C++
+void Internal::MyUIControl::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
+{
+ MyUIControl control = MyUIControl::DownCast( Dali::BaseHandle( object ) );
+ switch( index )
+ {
+ case MyUIControl::Property::MY_VISUAL:
+ {
+ Toolkit::VisualFactory visualFactory = Toolkit::VisualFactory::Get();
+ const Property::Map *map = value.GetMap();
+ if( map && !map->Empty() )
+ {
+ Toolkit::Visual::Base visual = visualFactory.CreateVisual( *map );
+ GetImplementation( control ).RegisterVisual( index, visual );
+ }
+ break;
+ }
+ //...
+ }
+}
+~~~
+
+The [Visuals](@ref visuals) section describes the property maps that can be used for each visual type.
+
___________________________________________________________________________________________________
## Ensuring Control is Stylable {#creating-controls-stylable}
This allows the creation of the control via a JSON file, as well as registering properties, signals and actions.
To ensure your control is stylable, the process described in [Type Registration](@ref type-registration) should be followed.
+
+#### Properties
To aid development, some macros are provided for registering properties which are described in the [Property](@ref properties) section.
Control properties can be one of three types:
~~~{.cpp}
// C++
-MyUIControl MyUIControlImpl::New()
+MyUIControl Internal::MyUIControl::New()
{
// Create the implementation, temporarily owned on stack
- IntrusivePtr< MyUIControlImpl > controlImpl = new MyUIControlImpl;
+ IntrusivePtr< Internal::MyUIControl > controlImpl = new Internal::MyUIControl;
// Pass ownership to handle
MyUIControl handle( *controlImpl );
This should be overridden by the custom ui control.
~~~{.cpp}
// C++
-void MyUIControlImpl::OnInitialize()
+void Internal::MyUIControl::OnInitialize()
{
- // Create visuals, register events etc.
+ // Create visuals using the VisualFactory, register events etc.
+ // Register any created visuals with Control base class
}
~~~
___________________________________________________________________________________________________
Dali::Toolkit::Internal::Control provides several behaviours which are specified through its constructor (@ref Dali::Toolkit::Internal::Control::Control()).
-| Behaviour | Description |
-|--------------------------------------|-------------------------------------------------------------------------|
-| ACTOR_BEHAVIOUR_NONE | No behaviour required. |
-| REQUIRES_HOVER_EVENTS | If our control requires [hover events](@ref creating-controls-events). |
-| REQUIRES_WHEEL_EVENTS | If our control requires [wheel events](@ref creating-controls-events). |
-| REQUIRES_STYLE_CHANGE_SIGNALS | True if need to monitor style change signals such as Theme/Font change. |
-| REQUIRES_KEYBOARD_NAVIGATION_SUPPORT | True if need to support keyboard navigation. |
+| Behaviour | Description |
+|--------------------------------------|----------------------------------------------------------------------------------------------------------------|
+| CONTROL_BEHAVIOUR_DEFAULT | Default behavior (size negotiation is on, style change is monitored, event callbacks are not called. |
+| DISABLE_SIZE_NEGOTIATION | If our control does not need size negotiation, i.e. control will be skipped by the size negotiation algorithm. |
+| REQUIRES_HOVER_EVENTS | If our control requires [hover events](@ref creating-controls-events). |
+| REQUIRES_WHEEL_EVENTS | If our control requires [wheel events](@ref creating-controls-events). |
+| DISABLE_STYLE_CHANGE_SIGNALS | True if control should not monitor style change signals such as Theme/Font change. |
+| REQUIRES_KEYBOARD_NAVIGATION_SUPPORT | True if need to support keyboard navigation. |
___________________________________________________________________________________________________
### Touch, Hover & Wheel Events {#creating-controls-events}
+ A **hover event** is when a pointer moves within the bounds of a custom actor (e.g. mouse pointer or hover pointer).
+ A **wheel event** is when the mouse wheel (or similar) is moved while hovering over an actor (via a mouse pointer or hover pointer).
-If the control needs to utilise hover and wheel events, then the correct behaviour flag should be used when constructing the control and then the appropriate method should be overridden.
+If the control needs to utilize hover and wheel events, then the correct behaviour flag should be used when constructing the control and then the appropriate method should be overridden.
~~~{.cpp}
// C++
-bool MyUIControlImpl::OnHoverEvent( const HoverEvent& event )
+bool Internal::MyUIControl::OnHoverEvent( const HoverEvent& event )
{
bool consumed = false;
~~~
~~~{.cpp}
// C++
-bool MyUIControlImpl::OnWheelEvent( const WheelEvent& event )
+bool Internal::MyUIControl::OnWheelEvent( const WheelEvent& event )
{
bool consumed = false;
~~~{.cpp}
// C++
-void MyUIControlImpl::OnInitialize()
+void Internal::MyUIControl::OnInitialize()
{
// Only enable pan gesture detection
EnableGestureDetection( Gesture::Pan );
Finally, the appropriate method should be overridden:
~~~{.cpp}
// C++
-void MyUIControlImpl::OnPan( const PanGesture& pan )
+void Internal::MyUIControl::OnPan( const PanGesture& pan )
{
// Handle pan-gesture
}
~~~
~~~{.cpp}
// C++
-void MyUIControlImpl::OnPinch( const PinchGesture& pinch )
+void Internal::MyUIControl::OnPinch( const PinchGesture& pinch )
{
// Handle pinch-event
}
~~~
~~~{.cpp}
// C++
-void MyUIControlImpl::OnTap( const TapGesture& tap )
+void Internal::MyUIControl::OnTap( const TapGesture& tap )
{
// Handle tap-gesture
}
~~~
~~~{.cpp}
// C++
-void MyUIControlImpl::OnLongPress( const LongPressGesture& longPress )
+void Internal::MyUIControl::OnLongPress( const LongPressGesture& longPress )
{
// Handle long-press-gesture
}
~~~{.cpp}
// C++
-void MyUIControlImpl::OnChildAdd( Actor& child );
+void Internal::MyUIControl::OnChildAdd( Actor& child );
{
- // Up call to Control first
- Control::OnChildAdd( child );
-
// Do any other operations required upon child addition
+
+ // Up call to Control at the end
+ Control::OnChildAdd( child );
}
~~~
~~~{.cpp}
// C++
-void MyUIControlImpl::OnChildRemove( Actor& child );
+void Internal::MyUIControl::OnChildRemove( Actor& child );
{
// Do any other operations required upon child removal
~~~{.cpp}
// C++
-void MyUIControlImpl::OnStageConnection( int depth )
+void Internal::MyUIControl::OnStageConnection( int depth )
{
- // Up call to Control first
- Control::OnStageConnection( depth );
-
// Do any other operations required upon stage connection
+
+ // Up call to Control at the end
+ Control::OnStageConnection( depth );
}
~~~
~~~{.cpp}
// C++
-void MyUIControlImpl::OnStageDisconnection()
+void Internal::MyUIControl::OnStageDisconnection()
{
// Do any other operations required upon stage disconnection
___________________________________________________________________________________________________
-### Size {#creating-controls-size}
+### Size Negotiation {#creating-controls-size-negotiation}
-Methods are provided that can be overridden if notification is required when our control's size is manipulated.
-An up call to the Control class is necessary if these methods are overridden.
+The following methods must be overridden for size negotiation to work correctly with a custom control.
~~~{.cpp}
// C++
-void MyUIControlImpl::OnSizeSet( const Vector3& targetSize )
+Vector3 Internal::MyUIControl::GetNaturalSize()
{
- // Up call to Control
- Control::OnSizeSet( targetSize );
+ // Return the natural size of the control
+ // This depends on our layout
+ // If we have one visual, then we can return the natural size of that
+ // If we have more visuals, then we need to calculate their positions within our control and work out the overall size we would like our control to be
+
+ // After working out the natural size of visuals that belong to this control,
+ // should also chain up to ensure other visuals belonging to the base class are
+ // also taken into account:
+ Vector2 baseSize = Control::GetNaturalSize(); // returns the size of the background.
+}
+~~~
+~~~{.cpp}
+// C++
+float Internal::MyUIControl::GetHeightForWidth( float width )
+{
+ // Called by the size negotiation algorithm if we have a fixed width
+ // We should calculate the height we would like our control to be for that width
+
+ // Should also chain up to determine the base class's preferred height:
+ float baseHeight = Control::GetHeightForWidth( width );
- // Do any other operations required upon size set
}
~~~
~~~{.cpp}
// C++
-void MyUIControlImpl::OnSizeAnimation( Animation& animation, const Vector3& targetSize )
+float Internal::MyUIControl::GetWidthForHeight( float height )
{
- // Up call to Control
- Control::OnSizeAnimation( animation, targetSize );
+ // Called by the size negotiation algorithm if we have a fixed height
+ // We should calculate the width we would like our control to be for that height
- // Do any other operations required upon size animation
+ // Should also chain up to determine the base class's preferred width:
+ float baseWidth = Control::GetWidth( height );
}
~~~
+~~~{.cpp}
+// C++
+void Internal::MyUIControl::OnRelayout( const Vector2& size, RelayoutContainer& container )
+{
+ // The size is what we have been given and what our control needs to fit into
+ // Here, we need to set the position and the size of our visuals
+ // If we have other controls/actors as children
+ // - Add the control/actor to the container paired with the size required
+ // - To ensure this works, you need to set up the control with a relayout policy of USE_ASSIGNED_SIZE
+ // - DO NOT CALL SetSize on this control: This will trigger another size negotiation calculation
+ // DO NOT chain up to base class.
+}
+~~~
+More information on size negotiation can be found [here](@ref size-negotiation-controls).
+
+___________________________________________________________________________________________________
+
+### Clipping Support {#creating-controls-clipping}
+
+When an Actor is set to clip its children, the renderers have to be added manually in order to specify what its children need to clip to.
+The Control base class automates the creation of the visuals when it is set to clip its children.
+
+This is only done if the application or custom control writer has not
+added any Renderers to the Control or registered any visuals
+(regardless of whether these visuals are enabled or not).
+
+If custom control writers want to define the clipping visuals themselves, then they should register all required visuals before the control is staged.
___________________________________________________________________________________________________