From: Adeel Kazmi Date: Fri, 19 Feb 2016 15:53:35 +0000 (+0000) Subject: (Programming Guide) Initial chapter on creating a custom control X-Git-Tag: dali_1.1.23~1^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=971bfa9c6730fefc49e45c5df796ce7e34e43ae3 (Programming Guide) Initial chapter on creating a custom control - Update Properties to include Animatable Properties - Also updated the examples - Updated Accessibility - Added a CMakeLists.txt to the example-code directly to directly build the example code Change-Id: I8a363e79a13d639b14be6d1d4ae487eea9d35563 --- diff --git a/docs/content/example-code/.gitignore b/docs/content/example-code/.gitignore new file mode 100644 index 0000000..06994ce --- /dev/null +++ b/docs/content/example-code/.gitignore @@ -0,0 +1,6 @@ +/CMakeFiles +/cmake_install.cmake +/build +*.example +/Makefile +/CMakeCache.txt diff --git a/docs/content/example-code/CMakeLists.txt b/docs/content/example-code/CMakeLists.txt new file mode 100644 index 0000000..133ab59 --- /dev/null +++ b/docs/content/example-code/CMakeLists.txt @@ -0,0 +1,22 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(PKG_LIST dali-core + dali-adaptor + dali-toolkit) +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(REQUIRED_PKGS REQUIRED ${PKG_LIST}) + +FOREACH(flag ${REQUIRED_PKGS_CFLAGS}) + SET(REQUIRED_CFLAGS "${REQUIRED_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${REQUIRED_CFLAGS} -Werror -Wall") +SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") + +FILE(GLOB SRCS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.cpp") + +FOREACH(EXAMPLE ${SRCS}) + STRING(REGEX REPLACE ".cpp" "" EXECUTABLE "${EXAMPLE}") + ADD_EXECUTABLE (${EXECUTABLE}.example ${EXAMPLE}) + TARGET_LINK_LIBRARIES(${EXECUTABLE}.example ${REQUIRED_PKGS_LDFLAGS}) +ENDFOREACH(EXAMPLE) diff --git a/docs/content/example-code/images/cards.jpg b/docs/content/example-code/images/cards.jpg new file mode 100644 index 0000000..11bd590 Binary files /dev/null and b/docs/content/example-code/images/cards.jpg differ diff --git a/docs/content/example-code/properties.cpp b/docs/content/example-code/properties.cpp new file mode 100644 index 0000000..dcbb47f --- /dev/null +++ b/docs/content/example-code/properties.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ + +// The name we will use to register our custom property by. +const char* const TAG_PROPERTY_NAME = "tagIdentifier"; + +// The image for our image view +const char* const IMAGE_CARDS = "images/cards.jpg"; +} // namespace + +/** + * This example shows how to set properties in C++ and how to register and look-up custom properties. + * An image is added to the screen which changes and a custom property is added to the image-view. + * This value is incremented every time the image is touched and the text-label is updated. + */ +class PropertyController: public ConnectionTracker +{ +public: + + PropertyController( Application& application ) + : mTagText(), + mTagPropertyIndex( Property::INVALID_INDEX ) + { + // Connect to the Application's Init signal + application.InitSignal().Connect( this, &PropertyController::Create ); + } + + ~PropertyController() + { + } + + // C++ EXAMPLE + void Create( Application& application ) + { + // Get the stage handle + Stage stage = Stage::GetCurrent(); + + mImageView = ImageView::New(); + + // Set the property to move to the center + mImageView.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER ); + + // Set another property to set the image-map + Property::Map imageMap; + imageMap[ "rendererType" ] = "image"; + imageMap[ "imageUrl" ] = IMAGE_CARDS; + imageMap[ "width" ] = 100; + imageMap[ "height" ] = 100; + mImageView.SetProperty( ImageView::Property::IMAGE, imageMap ); + + // Add the image view to the stage + stage.Add( mImageView ); + + // Register a custom float property on mImageView and use it to store the number of times we are tapped + mTagPropertyIndex = mImageView.RegisterProperty( TAG_PROPERTY_NAME, 0, Property::READ_WRITE /* Event-side only, i.e. not animatable */ ); + + // Connect to the touch-event + mImageView.TouchedSignal().Connect( this, &PropertyController::OnTouched ); + + // Create text label + mTagText = Toolkit::TextLabel::New( "0" ); + mTagText.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + mTagText.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + mTagText.SetProperty( TextLabel::Property::TEXT_COLOR, Color::WHITE ); + mTagText.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" ); + stage.Add( mTagText ); + } + + /** + * Called when the image view is touched + * param[in] touch The touch-event + * return Set to true if the signal was consumed correctly + */ + bool OnTouched( Actor actor, const TouchEvent& touch ) + { + int touchedCount = 0; + + // Look up the tag property by the cached property index. + // Note: If the property belongs to a control in another library, or we do not know the index, we can look the index up first with: + // Property::Index index = actor.GetPropertyIndex( TAG_PROPERTY_NAME ); + actor.GetProperty( mTagPropertyIndex ).Get( touchedCount ); + + // Increment and set back again + ++touchedCount; + actor.SetProperty( mTagPropertyIndex, touchedCount ); + + // Set the text in the text-label + std::stringstream valueText; + valueText << touchedCount; + mTagText.SetProperty( TextLabel::Property::TEXT, valueText.str() ); + + return true; // Consumed + } + // C++ EXAMPLE END + +private: + + ImageView mImageView; ///< An image view to show an image + TextLabel mTagText; ///< A text label used to show the last button pressed. + Property::Index mTagPropertyIndex; ///< A cached property index of our custom tag property. +}; + +// Entry point for applications. +int main( int argc, char **argv ) +{ + Application application = Application::New( &argc, &argv ); + + PropertyController test( application ); + application.MainLoop(); + + return 0; +} diff --git a/docs/content/example-code/property-example.cpp b/docs/content/example-code/property-example.cpp deleted file mode 100644 index 388385d..0000000 --- a/docs/content/example-code/property-example.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2014 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include - -using namespace Dali; - -namespace -{ - -// The name we will use to register our custom property by. -const char* const TAG_PROPERTY_NAME = "tagIdentifier"; - -const char* const PUSHBUTTON_PRESS_IMAGE = DALI_IMAGE_DIR "button-down.9.png"; -const char* const PUSHBUTTON_BUTTON_IMAGE = DALI_IMAGE_DIR "button-up.9.png"; - -// Define the grid geometry. -#define BUTTON_ROWS 9.0f -#define BUTTON_COLUMNS 7.0f -#define BUTTON_GAP 10.0f - -} // namespace - -/** - * This example shows how to register and look-up custom properties. - * A button grid is created, each with a new "tag" property which is set to a unique value. - * When pressed, the "tag" property is looked up to retrieve the unique value and display it. - */ -class PropertyButtonsController: public ConnectionTracker -{ - public: - - PropertyButtonsController( Application& application ) - { - // Connect to the Application's Init signal - application.InitSignal().Connect( this, &PropertyButtonsController::Create ); - } - - ~PropertyButtonsController() - { - } - - void Create( Application& application ) - { - // Setup precalculations for button size and start positions. - Toolkit::PushButton button; - int index = 0; - Vector2 stageSize = Stage::GetCurrent().GetSize(); - float buttonSize = ( stageSize.x - ( BUTTON_GAP * ( BUTTON_COLUMNS + 1 ) ) ) / BUTTON_COLUMNS; - float yStart = ( stageSize.y - ( ( buttonSize * BUTTON_ROWS ) + ( BUTTON_GAP * ( BUTTON_ROWS - 1 ) ) ) ) / 2.0f; - - // Create a grid of buttons. - for( int y = 0; y < BUTTON_ROWS; ++y ) - { - for( int x = 0; x < BUTTON_COLUMNS; ++x ) - { - // Create a button and position it. - button = Toolkit::PushButton::New(); - button.SetParentOrigin( ParentOrigin::TOP_LEFT ); - button.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - button.SetPosition( Vector3( BUTTON_GAP + ( x * ( buttonSize + BUTTON_GAP ) ), yStart + ( y * ( buttonSize + BUTTON_GAP ) ), 0.0f ) ); - button.SetSize( Vector3( buttonSize, buttonSize, 0) ); - button.SetSelectedImage( Dali::ResourceImage::New( PUSHBUTTON_PRESS_IMAGE ) ); - button.SetButtonImage( Dali::ResourceImage::New( PUSHBUTTON_BUTTON_IMAGE ) ); - - // Label the button with a unique value. - std::stringstream label; - label << index; - button.SetLabel( label.str() ); - - // Register our custom property, and use it to store a unique number. - // Store the index to the property so we can look it up later. - // Note: This is much faster than looking the property up by name and should always be used if possible. - // As all our control types are the same (PushButtons) the indecies to our unique property is the same for each one. - Property::Value tag = ( float )index; - mTagPropertyIndex = button.RegisterProperty( TAG_PROPERTY_NAME, tag ); - - // Hook a callback when the button is clicked. - button.ClickedSignal().Connect( this, &PropertyButtonsController::OnButtonClicked ); - - // Add the button to the stage. - Stage::GetCurrent().Add( button ); - index++; - } - } - - // Create the last selected button text view. - mTagText = Toolkit::TextLabel::New( "None selected" ); - mTagText.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); - mTagText.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); - mTagText.SetPosition( Vector3( 0.0f, -30.0f, 0.0f ) ); - Stage::GetCurrent().Add( mTagText ); - } - - /** - * Called when any button within the grid is clicked. - * param[in] The clicked button control - * return Set to true if the signal was consumed correctly - */ - bool OnButtonClicked( Toolkit::Button button ) - { - std::stringstream valueText; - // Look up the tag property by the cached property index. - // Note: If the property belongs to a control in another library, or we do not know the index, we can look the index up first with: - // Property::Index index = button.GetPropertyIndex( TAG_PROPERTY_NAME ); - valueText << "Selected: " << button.GetProperty< float >( mTagPropertyIndex ); - - mTagText.SetProperty( TextLabel::Property::TEXT, valueText.str() ); - - return true; - } - - private: - - Toolkit::TextLabel mTagText; ///< A text label used to show the last button pressed. - Property::Index mTagPropertyIndex; ///< A cached property index of our custom tag property. -}; - -// Entry point for applications. -int main( int argc, char **argv ) -{ - Application application = Application::New( &argc, &argv ); - - PropertyButtonsController test( application ); - application.MainLoop(); - - return 0; -} diff --git a/docs/content/images/creating-custom-controls/control-handle-body.png b/docs/content/images/creating-custom-controls/control-handle-body.png new file mode 100644 index 0000000..4349789 Binary files /dev/null and b/docs/content/images/creating-custom-controls/control-handle-body.png differ diff --git a/docs/content/images/creating-custom-controls/popup-normal.png b/docs/content/images/creating-custom-controls/popup-normal.png new file mode 100644 index 0000000..ce06133 Binary files /dev/null and b/docs/content/images/creating-custom-controls/popup-normal.png differ diff --git a/docs/content/images/creating-custom-controls/popup-styled.png b/docs/content/images/creating-custom-controls/popup-styled.png new file mode 100644 index 0000000..831950a Binary files /dev/null and b/docs/content/images/creating-custom-controls/popup-styled.png differ diff --git a/docs/content/images/creating-custom-controls/rendering.png b/docs/content/images/creating-custom-controls/rendering.png new file mode 100644 index 0000000..4ec4e1d Binary files /dev/null and b/docs/content/images/creating-custom-controls/rendering.png differ diff --git a/docs/content/main.md b/docs/content/main.md index 0b06506..2a9fd0b 100644 --- a/docs/content/main.md +++ b/docs/content/main.md @@ -84,11 +84,9 @@ + [Overview](@ref viewing-modes) ### Extending DALi - + Control Base Class Services - + How to write Custom UI Components + + [How to write Custom UI Components](@ref creating-custom-controls) + [Size Negotiation for Controls](@ref size-negotiation-controls) + [Type Registration](@ref type-registration) - + How to make Controls Scriptable + [Automated Tests](@ref auto_testing) + [Programming Guide](@ref documentationguide) + [JavaScript Wrapping Guide for DALi developers](@ref javascriptwrapping) diff --git a/docs/content/programming-guide/properties.h b/docs/content/programming-guide/properties.h index b9c87e9..b445a2e 100644 --- a/docs/content/programming-guide/properties.h +++ b/docs/content/programming-guide/properties.h @@ -26,34 +26,34 @@ There are some pre-defined macros designed to help with and standardise the defi These macros generate an array of property details which allow efficient lookup of flags like "animatable" or "constraint input". -Example: ImageActor +Example: Layer -Within the public-api header file; image-actor.h: +Within the public-api header file; layer.h: @code -/** - * @brief An enumeration of properties belonging to the ImageActor class. - * Properties additional to Actor. - */ -struct Property -{ - enum + /** + * @brief An enumeration of properties belonging to the Layer class. + * + * Properties additional to Actor. + */ + struct Property { - PIXEL_AREA = DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX, ///< name "pixelArea", type Rect - STYLE, ///< name "style", type std::string - BORDER, ///< name "border", type Vector4 - IMAGE, ///< name "image", type Map {"filename":"", "loadPolicy":...} + enum + { + CLIPPING_ENABLE = DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX, ///< name "clippingEnable", type bool @SINCE_1_0.0 + CLIPPING_BOX, ///< name "clippingBox", type Rect @SINCE_1_0.0 + BEHAVIOR, ///< name "behavior", type String @SINCE_1_0.0 + }; }; -}; @endcode -From @ref Dali::ImageActor::Property +From @ref Dali::Layer::Property Notes: - The properties are enumerated within a named struct to give them a namespace. - The properties are then refered to as <OBJECT>::%Property::<PROPERTY_NAME>. -Within the internal implementation; image-actor-impl.cpp: +Within the internal implementation; layer-impl.cpp: @code namespace // Unnamed namespace @@ -61,12 +61,11 @@ namespace // Unnamed namespace // Properties -// Name Type writable animatable constraint-input enum for index-checking +// Name Type writable animatable constraint-input enum for index-checking DALI_PROPERTY_TABLE_BEGIN -DALI_PROPERTY( "pixelArea", RECTANGLE, true, false, true, Dali::ImageActor::Property::PIXEL_AREA ) -DALI_PROPERTY( "style", STRING, true, false, true, Dali::ImageActor::Property::STYLE ) -DALI_PROPERTY( "border", VECTOR4, true, false, true, Dali::ImageActor::Property::BORDER ) -DALI_PROPERTY( "image", MAP, true, false, false, Dali::ImageActor::Property::IMAGE ) +DALI_PROPERTY( "clippingEnable", BOOLEAN, true, false, true, Dali::Layer::Property::CLIPPING_ENABLE ) +DALI_PROPERTY( "clippingBox", RECTANGLE, true, false, true, Dali::Layer::Property::CLIPPING_BOX ) +DALI_PROPERTY( "behavior", STRING, true, false, false, Dali::Layer::Property::BEHAVIOR ) DALI_PROPERTY_TABLE_END( DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX ) @endcode @@ -82,22 +81,23 @@ DALI_PROPERTY_TABLE_END( DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX )

How to implement a property within Dali-toolkit:

-Note that toolkit properties have extra limitations in that they cannot be animated or used as a constraint input. For this reason there is no requirement for a table of property details. -Macros are still used to define properties, but for the following reasons: +Macros are used to define properties for the following reasons: + +- To standardise the way properties are defined. +- To handle type-registering for properties, signals and actions in one place. +- To facilitate the posibility of running the code with the type-registry disabled. -To standardise the way properties are defined. -To handle type-registering for properties, signals and actions in one place. -To facilitate the posibility of running the code with the type-registry disabled. +Two different macros are provided depending on whether the property is to be an event-side only property or an animatable property. There are two stages: - Define the properties as an enum in the public-api header file, along with a definition of the property ranges. - Define the property details using the pre-defined macros to perform the type-registering of the properties. This is done for signals and actions also. -Example: Button +Example: ImageView -Source file: button.h: -Note that the “PropertyRange” contents “PROPERTY_START_INDEX” is also used by the macro for order checking. +Source file: image-view.h: +Note that the “PropertyRange” contents “PROPERTY_START_INDEX” & "ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX" are also used by the macro for order checking. @code /** @@ -105,8 +105,11 @@ Note that the “PropertyRange” contents “PROPERTY_START_INDEX” is also us */ enum PropertyRange { - PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1, - PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000 ///< Reserve property indices + PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1, ///< @SINCE_1_0.0 + PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000, ///< Reserve property indices @SINCE_1_0.0 + + ANIMATABLE_PROPERTY_START_INDEX = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX, ///< @SINCE_1_1.18 + ANIMATABLE_PROPERTY_END_INDEX = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX + 1000 ///< Reserve animatable property indices, @SINCE_1_1.18 }; /** @@ -116,23 +119,41 @@ Note that the “PropertyRange” contents “PROPERTY_START_INDEX” is also us { enum { - DISABLED = PROPERTY_START_INDEX, ///< name "disabled", @see SetDisabled(), type bool - AUTO_REPEATING, ///< name "autoRepeating", @see SetAutoRepeating(), type bool - INITIAL_AUTO_REPEATING_DELAY, ///< name "initialAutoRepeatingDelay", @see SetInitialAutoRepeatingDelay(), type float - NEXT_AUTO_REPEATING_DELAY, ///< name "nextAutoRepeatingDelay", @see SetNextAutoRepeatingDelay(), type float - TOGGLABLE, ///< name "togglable", @see SetTogglableButton(), type bool - SELECTED, ///< name "selected", @see SetSelected(), type bool - NORMAL_STATE_ACTOR, ///< name "normalStateActor", @see SetButtonImage(), type Map - SELECTED_STATE_ACTOR, ///< name "selectedStateActor", @see SetSelectedImage(), type Map - DISABLED_STATE_ACTOR, ///< name "disabledStateActor", @see SetDisabledImage(), type Map - LABEL_ACTOR, ///< name "labelActor", @see SetLabel(), type Map + // Event side properties + + /** + * @DEPRECATED_1_1.16. Use IMAGE instead. + * @brief name "resourceUrl", type string + * @SINCE_1_0.0 + */ + RESOURCE_URL = PROPERTY_START_INDEX, + /** + * @brief name "image", type string if it is a url, map otherwise + * @SINCE_1_0.0 + */ + IMAGE, + /** + * @brief name "preMultipliedAlpha", type Boolean + * @SINCE_1_1.18 + * @pre image must be initialized. + */ + PRE_MULTIPLIED_ALPHA, + + // Animatable properties + + /** + * @brief name "pixelArea", type Vector4 + * @details Pixel area is a relative value with the whole image area as [0.0, 0.0, 1.0, 1.0]. + * @SINCE_1_0.18 + */ + PIXEL_AREA = ANIMATABLE_PROPERTY_START_INDEX, }; }; @endcode -Source file: button-impl.cpp, within an unnamed namespace: +Source file: image-view-impl.cpp, within an unnamed namespace: -@clip{"button-impl.cpp",DALI_TYPE_REGISTRATION_BEGIN,DALI_TYPE_REGISTRATION_END} +@clip{"image-view-impl.cpp",DALI_TYPE_REGISTRATION_BEGIN,DALI_TYPE_REGISTRATION_END} Notes: @@ -141,7 +162,6 @@ Source file: button-impl.cpp, within an unnamed namespace: - Signals and actions are registered likewise in that order. - Properties type-registered using these macros will have their order checked at compile time. If you get an indexing compile error, check the order matches the enum order. -

@section property-indices Property Indices @@ -158,16 +178,14 @@ There are some predefined start indecies and ranges that should be used for comm DALi has a property system and provides several different kinds of properties. The following table shows the index range of the different properties in place. - - - - - - - -
Kind Description Start IndexEnd Index
Default Properties defined within DALi Core, e.g. Dali::Actor, Dali::ShaderEffect default properties etc. \link Dali::DEFAULT_OBJECT_PROPERTY_START_INDEX DEFAULT_OBJECT_PROPERTY_START_INDEX\endlink (0)9999999
Registered Properties registered using Dali::PropertyRegistration \link Dali::PROPERTY_REGISTRATION_START_INDEX PROPERTY_REGISTRATION_START_INDEX\endlink (10000000)\link Dali::PROPERTY_REGISTRATION_MAX_INDEX PROPERTY_REGISTRATION_MAX_INDEX\endlink (19999999)
Control Property range reserved by Dali::Toolkit::Control \link Dali::Toolkit::Control::CONTROL_PROPERTY_START_INDEX CONTROL_PROPERTY_START_INDEX\endlink (10000000) - \link Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX CONTROL_PROPERTY_END_INDEX\endlink (10001000)
Derived Control Property range for control deriving directly from Dali::Toolkit::Control 10001001\link Dali::PROPERTY_REGISTRATION_MAX_INDEX PROPERTY_REGISTRATION_MAX_INDEX\endlink (19999999)
Custom Custom properties added to instance using Dali::Handle::RegisterProperty \link Dali::PROPERTY_CUSTOM_START_INDEX PROPERTY_CUSTOM_START_INDEX\endlink (50000000)Onwards...
- +| Kind | Description | Start Index | End Index | +|:----------------------|:--------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------:| +| Default | Properties defined within DALi Core, e.g. Dali::Actor, Dali::ShaderEffect default properties etc. | \link Dali::DEFAULT_OBJECT_PROPERTY_START_INDEX DEFAULT_OBJECT_PROPERTY_START_INDEX\endlink | \link Dali::DEFAULT_PROPERTY_MAX_COUNT DEFAULT_PROPERTY_MAX_COUNT\endlink (9999999) | +| Registered | Properties registered using Dali::PropertyRegistration | \link Dali::PROPERTY_REGISTRATION_START_INDEX PROPERTY_REGISTRATION_START_INDEX\endlink (10000000) | \link Dali::PROPERTY_REGISTRATION_MAX_INDEX PROPERTY_REGISTRATION_MAX_INDEX\endlink (19999999) | +| Registered Animatable | Animatable properties registered using Dali::PropertyRegistration | \link Dali::ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX\endlink (20000000) | \link Dali::ANIMATABLE_PROPERTY_REGISTRATION_MAX_INDEX ANIMATABLE_PROPERTY_REGISTRATION_MAX_INDEX\endlink (29999999) | +| Control | Property range reserved by Dali::Toolkit::Control | \link Dali::Toolkit::Control::CONTROL_PROPERTY_START_INDEX CONTROL_PROPERTY_START_INDEX\endlink (10000000) | \link Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX CONTROL_PROPERTY_END_INDEX\endlink (10001000) | +| Derived Control | Property range for control deriving directly from Dali::Toolkit::Control | 10001001 | \link Dali::PROPERTY_REGISTRATION_MAX_INDEX PROPERTY_REGISTRATION_MAX_INDEX\endlink (19999999) | +| Custom | Custom properties added to instance using Dali::Handle::RegisterProperty | \link Dali::PROPERTY_CUSTOM_START_INDEX PROPERTY_CUSTOM_START_INDEX\endlink (50000000) | Onwards... |

@@ -180,12 +198,13 @@ An application developer can use an existing property, or, if necessary, registe Here is a code example. This example shows how to register and look-up custom properties. -A grid of buttons is created, each with a new "tag" property which is set to a unique value. The index to this property is cached for later use. -When pressed, the property is looked up by index (as this is much faster than a text lookup of the property name). +An image is added to the screen which changes and a custom property is added to the image-view. +This value is incremented every time the image is touched and the text-label is updated. +When touched, the property is looked up by index (as this is much faster than a text lookup of the property name). -Property lookup via index should always be used unless the indecies cannot be known. If the property reader was completely decoupled from the creation, EG. A custom control with a custom property being used by external application code, then it may be necessary. In this case the application writer should aim to perform the text lookup once at start-up, and cache the property index locally. +Property lookup via index should always be used unless the indicies cannot be known. If the property reader was completely decoupled from the creation, e.g. A custom control with a custom property being used by external application code, then it may be necessary. In this case the application writer should aim to perform the text lookup once at start-up, and cache the property index locally. -@clip{"property-example.cpp", void Create, return true;} +@clip{"properties.cpp", // C++ EXAMPLE, // C++ EXAMPLE END} Once run, a grid of buttons will appear. When a button is pressed, the unique number stored in the property (in this case the index) is displayed at the bottom of the screen. @@ -196,18 +215,22 @@ Once run, a grid of buttons will appear. When a button is pressed, the unique nu Note that constraints cannot be used within JavaScript, so below is a simple example that sets one of the default properties; scale: @code -var image = new dali.ResourceImage( {url:"background.png"} ); -var imageActor = new dali.ImageActor( image ); +var imageView = new dali.Control( "ImageView" ); // by default an actor is anchored to the top-left of it's parent actor // change it to the middle -imageActor.parentOrigin = dali.CENTER; - -// scale it up by 2 times in x,y -imageActor.scale = [ 2, 2, 1 ]; +imageView.parentOrigin = dali.CENTER; + +// Set an image view property +imageView.image = { + "rendererType" : "image", + "imageUrl" : "images/icon-0.png", + "width" : 100, + "height" : 100 +}; // add to the stage -dali.stage.add( imageActor ); +dali.stage.add( imageView ); @endcode For a more detailed example see the ShaderEffect example in the JavaScript documentation. @@ -220,32 +243,19 @@ This is a basic example of a button defined in JSON by setting the default prope @code { - "constants": { - "CONFIG_SCRIPT_LOG_LEVEL": "Verbose" - }, - "stage": [ - // First Button + "stage": + [ { - "type": "PushButton", - "parentOrigin": "TOP_CENTER", - "anchorPoint": "TOP_CENTER", + "type": "ImageView", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", "position": [0, 0, 0], - "size": [0, 200, 0], - "normalStateActor": { - "type": "ImageActor", - "image": { - "filename": "{DALI_IMAGE_DIR}blocks-brick-1.png" - } - }, - "selectedStateActor": { - "type": "ImageActor", - "image": { - "filename": "{DALI_IMAGE_DIR}blocks-brick-2.png" - } - }, - "labelActor": { - "type": "TextLabel", - "text": "Normal" + "image": + { + "rendererType" : "image", + "imageUrl" : "images/icon-0.png", + "width" : 100, + "height" : 100 } } ] diff --git a/docs/content/shared-javascript-and-cpp-documentation/accessibility.md b/docs/content/shared-javascript-and-cpp-documentation/accessibility.md index 78a54aa..a9fc67c 100644 --- a/docs/content/shared-javascript-and-cpp-documentation/accessibility.md +++ b/docs/content/shared-javascript-and-cpp-documentation/accessibility.md @@ -242,16 +242,19 @@ Stage::GetCurrent().Add( table ); ### Using accessibility within a custom control (C++) {#accessibilitycustomcontrol} -As well as the Activation signal, if implementing a custom control from C++ you can use the OnAccessibilityActivate() virtual function. - -Other virtual accessibility methods are available for overriding within control also: - -- virtual bool OnAccessibilityPan( PanGesture gesture ); -- virtual bool OnAccessibilityTouch( const TouchEvent& touchEvent ); -- virtual bool OnAccessibilityValueChange( bool isIncrease ); // (i.e. value change of slider control) -- virtual bool OnAccessibilityZoom(); - - +Accessibility behaviour can be customized in a custom UI control by overriding all or some of the following methods. + +| Method | Description | +|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| OnAccessibilityActivated | When the control is *activated* or selected, in accessibility mode. | +| OnAccessibilityPan | When an accessibility pan gesture occurs while this control is focussed. | +| OnAccessibilityTouch | Touch events are delivered differently in Accessibility mode. This method should be overridden if some special behaviour is required when these touch events are received. | +| OnAccessibilityValueChange | When a value is changed while this control is focussed (e.g. value change of a slider control). | +| OnAccessibilityZoom | Should be overridden if behaviour is required when the magnification level changes when this control is focussed. | + +If these events are consumed, then the method should return true. +The default behaviour in the control base classes returns false, i.e. not consumed. + ### Using accessibility signals for extra control {#accessibilitysignals} For more specific control of functionality when accessibility is enabled, there are several signals within the accessibility manager's public API that can be connected to. diff --git a/docs/content/shared-javascript-and-cpp-documentation/creating-custom-controls.md b/docs/content/shared-javascript-and-cpp-documentation/creating-custom-controls.md new file mode 100644 index 0000000..b40262b --- /dev/null +++ b/docs/content/shared-javascript-and-cpp-documentation/creating-custom-controls.md @@ -0,0 +1,340 @@ + + +[TOC] + +# Creating Custom UI Controls {#creating-custom-controls} + +DALi provides the ability to create custom UI controls. +This can be done by extending Dali::Toolkit::Control and Dali::Toolkit::Internal::Control classes. + +Custom controls are created using the [handle/body idiom](@ref handle-body-idiom) used in DALi. + +![ ](../assets/img/creating-custom-controls/control-handle-body.png) +![ ](creating-custom-controls/control-handle-body.png) + +### 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). ++ 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. + +___________________________________________________________________________________________________ + +## Rendering Content {#creating-controls-rendering-content} + +To render content, the required actors can be created and added to the control itself as its children. +However, this solution is not fully optimised and means extra actors will be added, and thus, need to be processed by DALi. + +Controls should be as generic as possible so the recommendation is to re-use control renderers to create the content required as described in the [Control Renderers](@ref control-renderers) section. +Currently, this is devel-api though, so is subject to change. + +![ ](../assets/img/creating-custom-controls/rendering.png) +![ ](creating-custom-controls/rendering.png) + +___________________________________________________________________________________________________ + +## Ensuring Control is Stylable {#creating-controls-stylable} + +DALi's property system allows custom controls to be easily styled. +The [JSON Syntax](@ref script-json-specification) is used in the stylesheets: + +**JSON Styling Syntax Example:** +~~~ +{ + "styles": + { + "textfield": + { + "pointSize":18, + "primaryCursorColor":[0.0,0.72,0.9,1.0], + "secondaryCursorColor":[0.0,0.72,0.9,1.0], + "cursorWidth":1, + "selectionHighlightColor":[0.75,0.96,1.0,1.0], + "grabHandleImage" : "{DALI_STYLE_IMAGE_DIR}cursor_handler_drop_center.png", + "selectionHandleImageLeft" : {"filename":"{DALI_STYLE_IMAGE_DIR}selection_handle_drop_left.png" }, + "selectionHandleImageRight": {"filename":"{DALI_STYLE_IMAGE_DIR}selection_handle_drop_right.png" } + } + } +} +~~~ + +Styling gives the UI designer the ability to change the look and feel of the control without any code changes. + +| Normal Style | Customized Style | +|:------------:|:----------------:| +|![ ](../assets/img/creating-custom-controls/popup-normal.png) ![ ](creating-custom-controls/popup-normal.png) | ![ ](../assets/img/creating-custom-controls/popup-styled.png) ![ ](creating-custom-controls/popup-styled.png)| + +More information regarding styling can be found in the [Styling](@ref styling) section. +___________________________________________________________________________________________________ + +### Type Registration {#creating-controls-type-registration} + +The TypeRegistry is used to register your custom control. +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. +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: + + **Event-side only:** A function is called to set this property or to retrieve the value of this property. + Usually, the value is stored as a member parameter of the Impl class. + Other operations can also be done, as required, in this called function. + + **Animatable Properties:** These are double-buffered properties that can be animated. + + **Custom Properties:** These are dynamic properties that are created for every single instance of the control. + Therefore, these tend to take a lot of memory and are usually used by applications or other controls to dynamically set certain attributes on their children. + The index for these properties can also be different for every instance. + +Careful consideration must be taken when choosing which property type to use for the properties of the custom control. +For example, an Animatable property type can be animated but requires a lot more resources (both in its execution and memory footprint) compared to an event-side only property. +___________________________________________________________________________________________________ + +## Control Services {#creating-controls-control-services} + +### Initialization {#creating-controls-init} + +Controls are initialized in two steps: in the constructor, and then in the Initialize() method. +This is so that a handle/body connection is made within DALi Core. +See Dali::CustomActor & Dali::CustomActorImpl for more information. + +It is recommended to do provide a New() method in the custom control implementation where the Initialize() method should be called. + +~~~{.cpp} +// C++ +MyUIControl MyUIControlImpl::New() +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< MyUIControlImpl > controlImpl = new MyUIControlImpl; + + // Pass ownership to handle + MyUIControl handle( *controlImpl ); + + // Second-phase init of the implementation + controlImpl->Initialize(); + + return handle; +} +~~~ +Another advantage of using a New() method is that the constructor for MyUIControl can be made private (or protected). + +This will trigger the Dali::Toolkit::Internal::Control Initialize() method which will in-turn, call the virtual method OnInitialize(). +This should be overridden by the custom ui control. +~~~{.cpp} +// C++ +void MyUIControlImpl::OnInitialize() +{ + // Create renderers, register events etc. +} +~~~ +___________________________________________________________________________________________________ + +### Control Behaviour {#creating-controls-behaviour} + +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_TOUCH_EVENTS | If our control requires [touch events](@ref creating-controls-events). | +| 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. | +___________________________________________________________________________________________________ + +### Touch, Hover & Wheel Events {#creating-controls-events} + ++ A **touch event** is when any touch occurs within the bounds of the custom actor. ++ 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 should needs to utilise these events, then the correct behaviour flag should be used when constructing the control. + +Then the appropriate method should be overridden. +~~~{.cpp} +// C++ +bool MyUIControlImpl::OnTouchEvent( const TouchEvent& event ) +{ + bool consumed = false; + + // Handle touch event + + // Return true if handled/consumed, false otherwise + return consumed; +} +~~~ +~~~{.cpp} +// C++ +bool MyUIControlImpl::OnHoverEvent( const HoverEvent& event ) +{ + bool consumed = false; + + // Handle hover event + + // Return true if handled/consumed, false otherwise + return consumed; +} +~~~ +~~~{.cpp} +// C++ +bool MyUIControlImpl::OnWheelEvent( const WheelEvent& event ) +{ + bool consumed = false; + + // Handle wheel event + + // Return true if handled/consumed, false otherwise + return consumed; +} +~~~ +___________________________________________________________________________________________________ + +### Gestures {#creating-controls-gestures} + +DALi has a gesture system which analyses a stream of touch events and attempts to determine the intention of the user. +The following gesture detectors are provided: + + + **Pan:** When the user starts panning (or dragging) one or more fingers. + The panning should start from within the bounds of the control. + + **Pinch:** Detects when two touch points move towards or away from each other. + The center point of the pinch should be within the bounds of the control. + + **Tap:** When the user taps within the bounds of the control. + + **LongPress:** When the user presses and holds on a certain point within the bounds of a control. + +The control base class provides basic set up to detect these gestures. +If any of these detectors are required then this can be specified in the OnInitialize() method (or as required). + +~~~{.cpp} +// C++ +void MyUIControlImpl::OnInitialize() +{ + // Only enable pan gesture detection + EnableGestureDetection( Gesture::Pan ); + + // Or if several gestures are required + EnableGestureDetection( Gesture::Type( Gesture::Pinch | Gesture::Tap | Gesture::LongPress ) ); +} +~~~ + +The above snippet of code will only enable the default gesture detection for each type. +If customization of the gesture detection is required, then the gesture-detector can be retrieved and set up accordingly in the same method. + +~~~{.cpp} +// C++ +PanGestureDetector panGestureDetector = GetPanGestureDetector(); +panGestureDetector.AddDirection( PanGestureDetector::DIRECTION_VERTICAL ); +~~~ + +Finally, the appropriate method should be overridden: +~~~{.cpp} +// C++ +void MyUIControlImpl::OnPan( const PanGesture& pan ) +{ + // Handle pan-gesture +} +~~~ +~~~{.cpp} +// C++ +void MyUIControlImpl::OnPinch( const PinchGesture& pinch ) +{ + // Handle pinch-event +} +~~~ +~~~{.cpp} +// C++ +void MyUIControlImpl::OnTap( const TapGesture& tap ) +{ + // Handle tap-gesture +} +~~~ +~~~{.cpp} +// C++ +void MyUIControlImpl::OnLongPress( const LongPressGesture& longPress ) +{ + // Handle long-press-gesture +} +~~~ + +___________________________________________________________________________________________________ + +### Accessibility {#creating-controls-accessibility} + +Accessibility is functionality that has been designed to aid usage by the visually impaired. +More information can be found in the [Accessibility](@ref accessibility) section. + +Accessibility behaviour can be customized in the control by overriding certain virtual methods. +This is detailed [here](@ref accessibilitycustomcontrol). + +___________________________________________________________________________________________________ + +### Signals {#creating-controls-signals} + +If applications need to react to changes in the control state, controls can inform those applications using Dali::Signal. + +First, create a signature of the function the signal will call in the handle header file: +~~~{.cpp} +// C++: my-ui-control.h +typedef Signal< void () > SignalType; +~~~ + +Then Create methods to get to the signal: +~~~{.cpp} +// C++: my-ui-control.h +MyUIControl::SignalType& MyCustomSignal(); +~~~ + +The source file should just call the impl: +~~~{.cpp} +// C++: my-ui-control.cpp +MyUIControl::SignalType& MyUIControl::MyCustomSignal() +{ + return Dali::Toolkit::GetImplementation( *this ).MyCustomSignal(); +} +~~~ + +In the impl file, create an instance of the signal as follows and return it in the appropriate method: +~~~{.cpp} +// C++: my-ui-control-impl.h +public: + + MyUIControl::SignalType MyUIControl::MyCustomSignal() + { + return mMyCustomSignal; + } + +private: + + MyUIControl::SignalType mMyCustomSignal; +~~~ + +Then, when you wish to emit this signal: +~~~{.cpp} +// C++: my-ui-control-impl.cpp +mMyCustomSignal.Emit(); +~~~ +There is no need to check if there is anything connected to this signal as this is done by the framework. + +The application can then connect to the signal as follows: +~~~{.cpp} +void AppFunction() +{ + // Do Something +} + +... + +customControl.MyCustomSignal.Connect( this, &AppFunction ); +~~~ +___________________________________________________________________________________________________ + +### Other Features {#creating-controls-other} + + + [Background](@ref background) + +___________________________________________________________________________________________________ + +@class _Guide_Creating_UI_Controls + +*/