From: Richard Huang Date: Fri, 19 Feb 2016 17:30:08 +0000 (+0000) Subject: Flexbox UI control implementation X-Git-Tag: dali_1.1.31~5^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=9dc39e046394875d17ed30cddcc47572feb924c3 Flexbox UI control implementation Change-Id: I70217a6e04cba71fcf5eaeaf0b10cb82b116a4e8 --- diff --git a/automated-tests/src/dali-toolkit/CMakeLists.txt b/automated-tests/src/dali-toolkit/CMakeLists.txt index bf5f24b..ca2aaee 100644 --- a/automated-tests/src/dali-toolkit/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit/CMakeLists.txt @@ -15,6 +15,7 @@ SET(TC_SOURCES utc-Dali-ConfirmationPopup.cpp utc-Dali-CubeTransitionEffect.cpp utc-Dali-EffectsView.cpp + utc-Dali-FlexContainer.cpp utc-Dali-GaussianBlurView.cpp utc-Dali-ImageView.cpp utc-Dali-JsonParser.cpp diff --git a/automated-tests/src/dali-toolkit/utc-Dali-FlexContainer.cpp b/automated-tests/src/dali-toolkit/utc-Dali-FlexContainer.cpp new file mode 100644 index 0000000..a2009d0 --- /dev/null +++ b/automated-tests/src/dali-toolkit/utc-Dali-FlexContainer.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2016 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 +#include +#include + +using namespace Dali; +using namespace Toolkit; + +void dali_flexflexContainer_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void dali_flexflexContainer_cleanup(void) +{ + test_return_value = TET_PASS; +} + +namespace +{ + +const char* const PROPERTY_NAME_CONTENT_DIRECTION = "contentDirection"; +const char* const PROPERTY_NAME_FLEX_DIRECTION = "flexDirection"; +const char* const PROPERTY_NAME_FLEX_WRAP = "flexWrap"; +const char* const PROPERTY_NAME_JUSTIFY_CONTENT = "justifyContent"; +const char* const PROPERTY_NAME_ALIGN_ITEMS = "alignItems"; +const char* const PROPERTY_NAME_ALIGN_CONTENT = "alignContent"; + +} // namespace + +int UtcDaliToolkitFlexContainerConstructorP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerConstructorP"); + FlexContainer flexContainer; + DALI_TEST_CHECK( !flexContainer ); + END_TEST; +} + +int UtcDaliToolkitFlexContainerNewP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerNewP"); + FlexContainer flexContainer = FlexContainer::New(); + DALI_TEST_CHECK( flexContainer ); + END_TEST; +} + +int UtcDaliToolkitFlexContainerDownCastP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerDownCastP"); + FlexContainer flexContainer1 = FlexContainer::New(); + BaseHandle object( flexContainer1 ); + + FlexContainer flexContainer2 = FlexContainer::DownCast( object ); + DALI_TEST_CHECK( flexContainer2 ); + + FlexContainer flexContainer3 = DownCast< FlexContainer >( object ); + DALI_TEST_CHECK( flexContainer3 ); + END_TEST; +} + +int UtcDaliToolkitFlexContainerDownCastN(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerDownCastN"); + BaseHandle uninitializedObject; + FlexContainer flexContainer1 = FlexContainer::DownCast( uninitializedObject ); + DALI_TEST_CHECK( !flexContainer1 ); + + FlexContainer flexContainer2 = DownCast< FlexContainer >( uninitializedObject ); + DALI_TEST_CHECK( !flexContainer2 ); + END_TEST; +} + +int UtcDaliToolkitFlexContainerCopyConstructorP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerCopyConstructorP"); + FlexContainer flexContainer = FlexContainer::New(); + flexContainer.SetProperty( FlexContainer::Property::FLEX_DIRECTION, FlexContainer::ROW_REVERSE ); + + FlexContainer copy( flexContainer ); + DALI_TEST_CHECK( copy ); + DALI_TEST_CHECK( copy.GetProperty( FlexContainer::Property::FLEX_DIRECTION ) == flexContainer.GetProperty( FlexContainer::Property::FLEX_DIRECTION ) ); + + END_TEST; +} + +int UtcDaliToolkitFlexContainerAssignmentOperatorP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerAssingmentOperatorP"); + FlexContainer flexContainer = FlexContainer::New(); + flexContainer.SetProperty( FlexContainer::Property::FLEX_DIRECTION, FlexContainer::ROW_REVERSE ); + + FlexContainer copy = flexContainer; + DALI_TEST_CHECK( copy ); + DALI_TEST_CHECK( copy.GetProperty( FlexContainer::Property::FLEX_DIRECTION ) == flexContainer.GetProperty( FlexContainer::Property::FLEX_DIRECTION ) ); + END_TEST; +} + +// Positive test case for a method +int UtcDaliToolkitFlexContainerGetPropertyP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerGetPropertyP"); + FlexContainer flexContainer = FlexContainer::New(); + DALI_TEST_CHECK( flexContainer ); + + // Check Property Indices are correct + DALI_TEST_CHECK( flexContainer.GetPropertyIndex( PROPERTY_NAME_CONTENT_DIRECTION ) == FlexContainer::Property::CONTENT_DIRECTION ); + DALI_TEST_CHECK( flexContainer.GetPropertyIndex( PROPERTY_NAME_FLEX_DIRECTION ) == FlexContainer::Property::FLEX_DIRECTION ); + DALI_TEST_CHECK( flexContainer.GetPropertyIndex( PROPERTY_NAME_FLEX_WRAP ) == FlexContainer::Property::FLEX_WRAP ); + DALI_TEST_CHECK( flexContainer.GetPropertyIndex( PROPERTY_NAME_JUSTIFY_CONTENT ) == FlexContainer::Property::JUSTIFY_CONTENT ); + DALI_TEST_CHECK( flexContainer.GetPropertyIndex( PROPERTY_NAME_ALIGN_ITEMS ) == FlexContainer::Property::ALIGN_ITEMS ); + DALI_TEST_CHECK( flexContainer.GetPropertyIndex( PROPERTY_NAME_ALIGN_CONTENT ) == FlexContainer::Property::ALIGN_CONTENT ); + + END_TEST; +} + +int UtcDaliToolkitFlexContainerSetPropertyP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolkitFlexContainerSetPropertyP"); + FlexContainer flexContainer = FlexContainer::New(); + DALI_TEST_CHECK( flexContainer ); + + // Check content direction property. + flexContainer.SetProperty( FlexContainer::Property::CONTENT_DIRECTION, FlexContainer::RTL ); + DALI_TEST_EQUALS( (FlexContainer::ContentDirection)flexContainer.GetProperty( FlexContainer::Property::CONTENT_DIRECTION ), FlexContainer::RTL, TEST_LOCATION ); + + // Check flex direction property. + flexContainer.SetProperty( FlexContainer::Property::FLEX_DIRECTION, FlexContainer::COLUMN_REVERSE ); + DALI_TEST_EQUALS( (FlexContainer::FlexDirection)flexContainer.GetProperty( FlexContainer::Property::FLEX_DIRECTION ), FlexContainer::COLUMN_REVERSE, TEST_LOCATION ); + + // Check flex wrap property. + flexContainer.SetProperty( FlexContainer::Property::FLEX_WRAP, FlexContainer::WRAP ); + DALI_TEST_EQUALS( (FlexContainer::WrapType)flexContainer.GetProperty( FlexContainer::Property::FLEX_WRAP ), FlexContainer::WRAP, TEST_LOCATION ); + + // Check justify content property. + flexContainer.SetProperty( FlexContainer::Property::JUSTIFY_CONTENT, FlexContainer::JUSTIFY_SPACE_BETWEEN ); + DALI_TEST_EQUALS( (FlexContainer::Justification)flexContainer.GetProperty( FlexContainer::Property::JUSTIFY_CONTENT ), FlexContainer::JUSTIFY_SPACE_BETWEEN, TEST_LOCATION ); + + // Check align items property. + flexContainer.SetProperty( FlexContainer::Property::ALIGN_ITEMS, FlexContainer::ALIGN_FLEX_START ); + DALI_TEST_EQUALS( (FlexContainer::Alignment)flexContainer.GetProperty( FlexContainer::Property::ALIGN_ITEMS ), FlexContainer::ALIGN_FLEX_START, TEST_LOCATION ); + + // Check align content property. + flexContainer.SetProperty( FlexContainer::Property::ALIGN_CONTENT, FlexContainer::ALIGN_STRETCH ); + DALI_TEST_EQUALS( (FlexContainer::Alignment)flexContainer.GetProperty( FlexContainer::Property::ALIGN_CONTENT ), FlexContainer::ALIGN_STRETCH, TEST_LOCATION ); + + END_TEST; +} + diff --git a/build/tizen/dali-toolkit/Makefile.am b/build/tizen/dali-toolkit/Makefile.am index 3d63299..a760d27 100644 --- a/build/tizen/dali-toolkit/Makefile.am +++ b/build/tizen/dali-toolkit/Makefile.am @@ -90,6 +90,7 @@ develapicontrolsdir = $(develapidir)/controls develapibloomviewdir = $(develapicontrolsdir)/bloom-view develapibubbleemitterdir = $(develapicontrolsdir)/bubble-effect develapieffectsviewdir = $(develapicontrolsdir)/effects-view +develapiflexcontainerdir = $(develapicontrolsdir)/flex-container develapimagnifierdir = $(develapicontrolsdir)/magnifier develapirendererfactorydir = $(develapicontrolsdir)/renderer-factory develapipopupdir = $(develapicontrolsdir)/popup @@ -111,6 +112,7 @@ develapibloomview_HEADERS = $(devel_api_bloom_view_header_files) develapibubbleemitter_HEADERS = $(devel_api_bubble_emitter_header_files) develapibuilder_HEADERS = $(devel_api_builder_header_files) develapieffectsview_HEADERS = $(devel_api_effects_view_header_files) +develapiflexcontainer_HEADERS = $(devel_api_flex_container_header_files) develapifocusmanager_HEADERS = $(devel_api_focus_manager_header_files) develapiimageatlas_HEADERS = $(devel_api_image_atlas_header_files) develapimagnifier_HEADERS = $(devel_api_magnifier_header_files) diff --git a/dali-toolkit/devel-api/controls/flex-container/flex-container.cpp b/dali-toolkit/devel-api/controls/flex-container/flex-container.cpp new file mode 100644 index 0000000..ab51c4f --- /dev/null +++ b/dali-toolkit/devel-api/controls/flex-container/flex-container.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016 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. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +FlexContainer FlexContainer::New() +{ + return Internal::FlexContainer::New(); +} + +FlexContainer::FlexContainer() +{ +} + +FlexContainer::FlexContainer( const FlexContainer& handle ) +: Control( handle ) +{ +} + +FlexContainer& FlexContainer::operator=( const FlexContainer& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +FlexContainer::~FlexContainer() +{ +} + +FlexContainer FlexContainer::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +FlexContainer::FlexContainer( Internal::FlexContainer& implementation ) +: Control(implementation) +{ +} + +FlexContainer::FlexContainer( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer( internal ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/devel-api/controls/flex-container/flex-container.h b/dali-toolkit/devel-api/controls/flex-container/flex-container.h new file mode 100644 index 0000000..b8540e9 --- /dev/null +++ b/dali-toolkit/devel-api/controls/flex-container/flex-container.h @@ -0,0 +1,258 @@ +#ifndef __DALI_TOOLKIT_FLEX_CONTAINER_H__ +#define __DALI_TOOLKIT_FLEX_CONTAINER_H__ + +/* + * Copyright (c) 2016 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. + * + */ + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class FlexContainer; +} + +/** + * @addtogroup dali_toolkit_controls_flex_container + * @{ + */ + +/** + * @brief FlexContainer implements a subset of the flexbox spec (defined by W3C): + * + * https://www.w3.org/TR/css3-flexbox/ + * + * It aims at providing a more efficient way to lay out, align and distribute space among + * items in the container, even when their size is unknown or dynamic. + * + * FlexContainer has the ability to alter the width and height of its children (i.e. flex + * items) to fill the available space in the best possible way on different screen sizes. + * FlexContainer can expand items to fill available free space, or shrink them to prevent + * overflow. + * + * Below is an illustration of the various directions and terms as applied to a flex + * container with the "flex direction" defined as "row". + * + * flex container + * --------------------------------------------------------------- cross start + * | ------------------ --------|--------------------------- | + * | | | | | | | + * | | | | | | | + * | | flex item 1 | | | flex item 2 | | main axis + * |-|----------------|-|-------|--------------------------|-|------------> + * | | | | | | | + * | | | | | | | + * | | | | | | | + * | ------------------ --------|--------------------------- | + * -----------------------------|--------------------------------- cross end + * | | | + * | main start | cross axis | main end + * | | | + * v + * + * @nosubgrouping + *

Per-child Custom properties for script supporting:

+ * + * The following custom properties of the actor are checked to decide how to lay out the + * actor inside the flex container. + * + * These properties are registered dynamically to the child and are non-animatable. + * + * | %Property Name | Type | + * |-------------------------|-------------| + * | flex | float | + * | alignSelf | integer | + * | flexPadding | Vector4 | + * | flexBorder | Vector4 | + * | flexMargin | Vector4 | + * + * The available values for alignSelf are: ALIGN_AUTO, ALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH + * + * @code + * "name":"icon", + * "type":"ImageView", + * "image":"image.png", + * "customProperties": { + * "flex":1, // property to make the item to receive the specified proportion of the free space in the container. If all items in the container use this pattern, their sizes will be proportional to the specified flex factor. + * "alignSelf":"flexStart", // property to specify how the item will align along the cross axis, if set, this overides the default alignment for all items in the container + * "flexPadding":[10, 10, 10, 10], // property to specify the space around the content (inside the flex border) of the item, if not set, default value is [0, 0, 0, 0] + * "flexBorder":[5, 5, 5, 5], // property to specify the border that goes around the flex padding and the content of the item, if not set, default value is [0, 0, 0, 0] + * "flexMargin":[10, 10, 10, 10] // property to specify the space outside the flex border, if not set, default value is [0, 0, 0, 0] + * } + * @endcode + */ + +class DALI_IMPORT_API FlexContainer : public Control +{ +public: + + /** + * @brief The direction of the main axis in the flex container. This determines + * the direction that flex items are laid out in the flex container. + */ + enum FlexDirection + { + COLUMN, ///< The flexible items are displayed vertically as a column + COLUMN_REVERSE, ///< The flexible items are displayed vertically as a column, but in reverse order + ROW, ///< The flexible items are displayed horizontally as a row + ROW_REVERSE ///< The flexible items are displayed horizontally as a row, but in reverse order + }; + + /** + * @brief The primary direction in which content is ordered in the flex container + * and on which sides the “start” and “end” are. + */ + enum ContentDirection + { + INHERIT, ///< Inherits the same direction from the parent + LTR, ///< From left to right + RTL ///< From right to left + }; + + /** + * @brief Alignment of the flex items when the items do not use all available + * space on the main-axis. + */ + enum Justification + { + JUSTIFY_FLEX_START, ///< Items are positioned at the beginning of the container + JUSTIFY_CENTER, ///< Items are positioned at the center of the container + JUSTIFY_FLEX_END, ///< Items are positioned at the end of the container + JUSTIFY_SPACE_BETWEEN, ///< Items are positioned with equal space between the lines + JUSTIFY_SPACE_AROUND ///< Items are positioned with equal space before, between, and after the lines + }; + + /** + * @brief Alignment of the flex items or lines when the items or lines do not + * use all available space on the cross-axis. + */ + enum Alignment + { + ALIGN_AUTO, ///< Inherits the same alignment from the parent (only valid for "alignSelf" property) + ALIGN_FLEX_START, ///< At the beginning of the container + ALIGN_CENTER, ///< At the center of the container + ALIGN_FLEX_END, ///< At the end of the container + ALIGN_STRETCH ///< Stretch to fit the container + }; + + /** + * @brief The wrap type of the flex container when there is no enough room for + * all the items on one flex line. + */ + enum WrapType + { + NO_WRAP, ///< Flex items laid out in single line (shrunk to fit the flex container along the main axis) + WRAP ///< Flex items laid out in multiple lines if needed + }; + +public: + + /** + * @brief The start and end property ranges for this control. + */ + enum PropertyRange + { + PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1, + PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000 ///< Reserve property indices + }; + + /** + * @brief An enumeration of properties belonging to the FlexContainer class. + */ + struct Property + { + enum + { + CONTENT_DIRECTION = PROPERTY_START_INDEX, ///< name "contentDirection", The primary direction in which content is ordered, @see FlexContainer::ContentDirection, type INTEGER + FLEX_DIRECTION, ///< name "flexDirection", The direction of the main-axis which determines the direction that flex items are laid out, @see FlexContainer::FlexDirection, type INTEGER + FLEX_WRAP, ///< name "flexWrap", Whether the flex items should wrap or not if there is no enough room for them on one flex line, @see FlexContainer::WrapType, type INTEGER + JUSTIFY_CONTENT, ///< name "justifyContent", The alignment of flex items when the items do not use all available space on the main-axis, @see FlexContainer::Justification, type INTEGER + ALIGN_ITEMS, ///< name "alignItems", The alignment of flex items when the items do not use all available space on the cross-axis, @see FlexContainer::Alignment, type INTEGER + ALIGN_CONTENT ///< name "alignContent", Similar to "alignItems", but it aligns flex lines, so only works when there are multiple lines, @see FlexContainer::Alignment, type INTEGER + }; + }; + + /** + * Create a FlexContainer handle; this can be initialised with FlexContainer::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + FlexContainer(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + FlexContainer( const FlexContainer& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + FlexContainer& operator=( const FlexContainer& handle ); + + /** + * @brief Destructor + * + * This is non-virtual since derived Handle types must not contain data or virtual methods. + */ + ~FlexContainer(); + + /** + * Create the FlexContainer control. + * @return A handle to the FlexContainer control. + */ + static FlexContainer New(); + + /** + * Downcast an Object handle to FlexContainer. If handle points to a FlexContainer the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a FlexContainer or an uninitialized handle + */ + static FlexContainer DownCast( BaseHandle handle ); + + +public: // Not intended for application developers + + /** + * @brief Creates a handle using the Toolkit::Internal implementation. + * + * @param[in] implementation The Control implementation. + */ + DALI_INTERNAL FlexContainer( Internal::FlexContainer& implementation ); + + /** + * @brief Allows the creation of this Control from an Internal::CustomActor pointer. + * + * @param[in] internal A pointer to the internal CustomActor. + */ + explicit DALI_INTERNAL FlexContainer( Dali::Internal::CustomActor* internal ); +}; + +/** + * @} + */ +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_FLEX_CONTAINER_H__ diff --git a/dali-toolkit/devel-api/file.list b/dali-toolkit/devel-api/file.list index a2220cd..8987002 100755 --- a/dali-toolkit/devel-api/file.list +++ b/dali-toolkit/devel-api/file.list @@ -7,6 +7,7 @@ devel_api_src_files = \ $(devel_api_src_dir)/controls/bloom-view/bloom-view.cpp \ $(devel_api_src_dir)/controls/bubble-effect/bubble-emitter.cpp \ $(devel_api_src_dir)/controls/effects-view/effects-view.cpp \ + $(devel_api_src_dir)/controls/flex-container/flex-container.cpp \ $(devel_api_src_dir)/controls/magnifier/magnifier.cpp \ $(devel_api_src_dir)/controls/popup/confirmation-popup.cpp \ $(devel_api_src_dir)/controls/popup/popup.cpp \ @@ -47,6 +48,9 @@ devel_api_builder_header_files = \ devel_api_effects_view_header_files = \ $(devel_api_src_dir)/controls/effects-view/effects-view.h +devel_api_flex_container_header_files = \ + $(devel_api_src_dir)/controls/flex-container/flex-container.h + devel_api_magnifier_header_files = \ $(devel_api_src_dir)/controls/magnifier/magnifier.h diff --git a/dali-toolkit/internal/controls/flex-container/flex-container-impl.cpp b/dali-toolkit/internal/controls/flex-container/flex-container-impl.cpp new file mode 100644 index 0000000..323a2cd --- /dev/null +++ b/dali-toolkit/internal/controls/flex-container/flex-container-impl.cpp @@ -0,0 +1,825 @@ +/* + * Copyright (c) 2016 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. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +using namespace Dali; + +namespace +{ +/* + * Custom properties for how to lay out the actor. + * + * When an actor is add to the flex container, the following custom properties of the actor + * are checked to decide how to lay out the actor inside the flex container. + * + * These non-animatable properties should be registered to the child which would be added + * to the flex container, and once added their values can not be changed. + */ +const char * const FLEX_PROPERTY_NAME("flex"); +const char * const ALIGN_SELF_PROPERTY_NAME("alignSelf"); +const char * const FLEX_PADDING_PROPERTY_NAME("flexPadding"); +const char * const FLEX_BORDER_PROPERTY_NAME("flexBorder"); +const char * const FLEX_MARGIN_PROPERTY_NAME("flexMargin"); + +#if defined(DEBUG_ENABLED) +// debugging support, very useful when new features are added or bugs are hunted down +// currently not called from code so compiler will optimize these away, kept here for future debugging + +#define FLEX_CONTAINER_TAG "DALI Toolkit::FlexContainer " +#define FC_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, FLEX_CONTAINER_TAG fmt, ## args) +//#define FLEX_CONTAINER_DEBUG 1 + +#if defined(FLEX_CONTAINER_DEBUG) +void PrintNode( Toolkit::Internal::FlexContainer::FlexItemNodeContainer itemNodes ) +{ + // Print the style property and layout of all the children + for( unsigned int i = 0; i < itemNodes.size(); ++i ) + { + FC_LOG( "Item %d style: \n", i ); + print_css_node( itemNodes[i].node, (css_print_options_t)( CSS_PRINT_STYLE | CSS_PRINT_CHILDREN ) ); + FC_LOG( "Item %d layout: \n", i ); + print_css_node( itemNodes[i].node, (css_print_options_t)( CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN ) ); + FC_LOG( "\n" ); + } +} + +#endif // defined(FLEX_CONTAINER_DEBUG) +#endif // defined(DEBUG_ENABLED) + + +} // namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +// Type registration +BaseHandle Create() +{ + return Toolkit::FlexContainer::New(); +} + +// Setup properties, signals and actions using the type-registry. +DALI_TYPE_REGISTRATION_BEGIN( Toolkit::FlexContainer, Toolkit::Control, Create ); + +DALI_PROPERTY_REGISTRATION( Toolkit, FlexContainer, "contentDirection", INTEGER, CONTENT_DIRECTION ) +DALI_PROPERTY_REGISTRATION( Toolkit, FlexContainer, "flexDirection", INTEGER, FLEX_DIRECTION ) +DALI_PROPERTY_REGISTRATION( Toolkit, FlexContainer, "flexWrap", INTEGER, FLEX_WRAP ) +DALI_PROPERTY_REGISTRATION( Toolkit, FlexContainer, "justifyContent", INTEGER, JUSTIFY_CONTENT ) +DALI_PROPERTY_REGISTRATION( Toolkit, FlexContainer, "alignItems", INTEGER, ALIGN_ITEMS ) +DALI_PROPERTY_REGISTRATION( Toolkit, FlexContainer, "alignContent", INTEGER, ALIGN_CONTENT ) + +DALI_TYPE_REGISTRATION_END() + +const Scripting::StringEnum ALIGN_SELF_STRING_TABLE[] = +{ + { "auto", Toolkit::FlexContainer::ALIGN_AUTO }, + { "flexStart", Toolkit::FlexContainer::ALIGN_FLEX_START }, + { "center", Toolkit::FlexContainer::ALIGN_CENTER }, + { "flexEnd", Toolkit::FlexContainer::ALIGN_FLEX_END }, + { "stretch", Toolkit::FlexContainer::ALIGN_STRETCH } +}; +const unsigned int ALIGN_SELF_STRING_TABLE_COUNT = sizeof( ALIGN_SELF_STRING_TABLE ) / sizeof( ALIGN_SELF_STRING_TABLE[0] ); + +const Scripting::StringEnum CONTENT_DIRECTION_STRING_TABLE[] = +{ + { "inherit", Toolkit::FlexContainer::INHERIT }, + { "LTR", Toolkit::FlexContainer::LTR }, + { "RTL", Toolkit::FlexContainer::RTL } +}; +const unsigned int CONTENT_DIRECTION_STRING_TABLE_COUNT = sizeof( CONTENT_DIRECTION_STRING_TABLE ) / sizeof( CONTENT_DIRECTION_STRING_TABLE[0] ); + +const Scripting::StringEnum FLEX_DIRECTION_STRING_TABLE[] = +{ + { "column", Toolkit::FlexContainer::COLUMN }, + { "columnReverse", Toolkit::FlexContainer::COLUMN_REVERSE }, + { "row", Toolkit::FlexContainer::ROW }, + { "rowReverse", Toolkit::FlexContainer::ROW_REVERSE } +}; +const unsigned int FLEX_DIRECTION_STRING_TABLE_COUNT = sizeof( FLEX_DIRECTION_STRING_TABLE ) / sizeof( FLEX_DIRECTION_STRING_TABLE[0] ); + +const Scripting::StringEnum FLEX_WRAP_STRING_TABLE[] = +{ + { "noWrap", Toolkit::FlexContainer::NO_WRAP }, + { "wrap", Toolkit::FlexContainer::WRAP } +}; +const unsigned int FLEX_WRAP_STRING_TABLE_COUNT = sizeof( FLEX_WRAP_STRING_TABLE ) / sizeof( FLEX_WRAP_STRING_TABLE[0] ); + +const Scripting::StringEnum JUSTIFY_CONTENT_STRING_TABLE[] = +{ + { "flexStart", Toolkit::FlexContainer::JUSTIFY_FLEX_START }, + { "center", Toolkit::FlexContainer::JUSTIFY_CENTER }, + { "flexEnd", Toolkit::FlexContainer::JUSTIFY_FLEX_END }, + { "spaceBetween", Toolkit::FlexContainer::JUSTIFY_SPACE_BETWEEN }, + { "spaceAround", Toolkit::FlexContainer::JUSTIFY_SPACE_AROUND } +}; +const unsigned int JUSTIFY_CONTENT_STRING_TABLE_COUNT = sizeof( JUSTIFY_CONTENT_STRING_TABLE ) / sizeof( JUSTIFY_CONTENT_STRING_TABLE[0] ); + +const Scripting::StringEnum ALIGN_ITEMS_STRING_TABLE[] = +{ + { "flexStart", Toolkit::FlexContainer::ALIGN_FLEX_START }, + { "center", Toolkit::FlexContainer::ALIGN_CENTER }, + { "flexEnd", Toolkit::FlexContainer::ALIGN_FLEX_END }, + { "stretch", Toolkit::FlexContainer::ALIGN_STRETCH } +}; +const unsigned int ALIGN_ITEMS_STRING_TABLE_COUNT = sizeof( ALIGN_ITEMS_STRING_TABLE ) / sizeof( ALIGN_ITEMS_STRING_TABLE[0] ); + +const Scripting::StringEnum ALIGN_CONTENT_STRING_TABLE[] = +{ + { "flexStart", Toolkit::FlexContainer::ALIGN_FLEX_START }, + { "center", Toolkit::FlexContainer::ALIGN_CENTER }, + { "flexEnd", Toolkit::FlexContainer::ALIGN_FLEX_END }, + { "stretch", Toolkit::FlexContainer::ALIGN_STRETCH } +}; +const unsigned int ALIGN_CONTENT_STRING_TABLE_COUNT = sizeof( ALIGN_CONTENT_STRING_TABLE ) / sizeof( ALIGN_CONTENT_STRING_TABLE[0] ); + +/** + * The function used by the layout algorithm to be get the style properties + * and layout information of the child at the given index. + */ +css_node_t* GetChildNodeAtIndex( void *childrenNodes, int i ) +{ + FlexContainer::FlexItemNodeContainer childrenNodeContainer = *( static_cast( childrenNodes ) ); + return childrenNodeContainer[i].node; +} + +/** + * The function used by the layout algorithm to check whether the node is dirty + * for relayout. + */ +bool IsNodeDirty( void *itemNodes ) +{ + // We only calculate the layout when the child is added or removed, or when + // style properties are changed. So should always return true here. + return true; +} + +} // Unnamed namespace + +Toolkit::FlexContainer FlexContainer::New() +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< FlexContainer > impl = new FlexContainer(); + + // Pass ownership to CustomActor handle + Toolkit::FlexContainer handle( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +FlexContainer::~FlexContainer() +{ + free_css_node( mRootNode.node ); + + for( unsigned int i = 0; i < mChildrenNodes.size(); i++ ) + { + free_css_node( mChildrenNodes[i].node ); + } + + mChildrenNodes.clear(); +} + +void FlexContainer::SetContentDirection( Toolkit::FlexContainer::ContentDirection contentDirection ) +{ + if( mContentDirection != contentDirection ) + { + mContentDirection = contentDirection; + mRootNode.node->style.direction = static_cast( mContentDirection ); + + RelayoutRequest(); + } +} + +Toolkit::FlexContainer::ContentDirection FlexContainer::GetContentDirection() +{ + return mContentDirection; +} + +void FlexContainer::SetFlexDirection( Toolkit::FlexContainer::FlexDirection flexDirection ) +{ + if( mFlexDirection != flexDirection ) + { + mFlexDirection = flexDirection; + mRootNode.node->style.flex_direction = static_cast( mFlexDirection ); + + RelayoutRequest(); + } +} + +Toolkit::FlexContainer::FlexDirection FlexContainer::GetFlexDirection() +{ + return mFlexDirection; +} + +void FlexContainer::SetFlexWrap( Toolkit::FlexContainer::WrapType flexWrap ) +{ + if( mFlexWrap != flexWrap ) + { + mFlexWrap = flexWrap; + mRootNode.node->style.flex_wrap = static_cast( mFlexWrap ); + + RelayoutRequest(); + } +} + +Toolkit::FlexContainer::WrapType FlexContainer::GetFlexWrap() +{ + return mFlexWrap; +} + +void FlexContainer::SetJustifyContent( Toolkit::FlexContainer::Justification justifyContent ) +{ + if( mJustifyContent != justifyContent ) + { + mJustifyContent = justifyContent; + mRootNode.node->style.justify_content = static_cast( mJustifyContent ); + + RelayoutRequest(); + } +} + +Toolkit::FlexContainer::Justification FlexContainer::GetJustifyContent() +{ + return mJustifyContent; +} + +void FlexContainer::SetAlignItems( Toolkit::FlexContainer::Alignment alignItems ) +{ + if( mAlignItems != alignItems ) + { + mAlignItems = alignItems; + mRootNode.node->style.align_items = static_cast( mAlignItems ); + + RelayoutRequest(); + } +} + +Toolkit::FlexContainer::Alignment FlexContainer::GetAlignItems() +{ + return mAlignItems; +} + +void FlexContainer::SetAlignContent( Toolkit::FlexContainer::Alignment alignContent ) +{ + if( mAlignContent != alignContent ) + { + mAlignContent = alignContent; + mRootNode.node->style.align_content = static_cast( mAlignContent ); + + RelayoutRequest(); + } +} + +Toolkit::FlexContainer::Alignment FlexContainer::GetAlignContent() +{ + return mAlignContent; +} + +void FlexContainer::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value ) +{ + Toolkit::FlexContainer flexContainer = Toolkit::FlexContainer::DownCast( Dali::BaseHandle( object ) ); + + if( flexContainer ) + { + FlexContainer& flexContainerImpl( GetImpl( flexContainer ) ); + switch( index ) + { + case Toolkit::FlexContainer::Property::CONTENT_DIRECTION: + { + Toolkit::FlexContainer::ContentDirection contentDirection( Toolkit::FlexContainer::INHERIT ); + + if( value.GetType() == Property::INTEGER ) + { + flexContainerImpl.SetContentDirection( static_cast( value.Get< int >() ) ); + } + else if( Scripting::GetEnumeration< Toolkit::FlexContainer::ContentDirection >( value.Get< std::string >().c_str(), + CONTENT_DIRECTION_STRING_TABLE, + CONTENT_DIRECTION_STRING_TABLE_COUNT, + contentDirection ) ) + { + flexContainerImpl.SetContentDirection(contentDirection); + } + break; + } + case Toolkit::FlexContainer::Property::FLEX_DIRECTION: + { + Toolkit::FlexContainer::FlexDirection flexDirection( Toolkit::FlexContainer::COLUMN ); + + if( value.GetType() == Property::INTEGER ) + { + flexContainerImpl.SetFlexDirection( static_cast( value.Get< int >() ) ); + } + else if( Scripting::GetEnumeration< Toolkit::FlexContainer::FlexDirection >( value.Get< std::string >().c_str(), + FLEX_DIRECTION_STRING_TABLE, + FLEX_DIRECTION_STRING_TABLE_COUNT, + flexDirection ) ) + { + flexContainerImpl.SetFlexDirection(flexDirection); + } + break; + } + case Toolkit::FlexContainer::Property::FLEX_WRAP: + { + Toolkit::FlexContainer::WrapType flexWrap( Toolkit::FlexContainer::NO_WRAP ); + + if( value.GetType() == Property::INTEGER ) + { + flexContainerImpl.SetFlexWrap( static_cast( value.Get< int >() ) ); + } + else if( Scripting::GetEnumeration< Toolkit::FlexContainer::WrapType >( value.Get< std::string >().c_str(), + FLEX_WRAP_STRING_TABLE, + FLEX_WRAP_STRING_TABLE_COUNT, + flexWrap ) ) + { + flexContainerImpl.SetFlexWrap(flexWrap); + } + break; + } + case Toolkit::FlexContainer::Property::JUSTIFY_CONTENT: + { + Toolkit::FlexContainer::Justification justifyContent( Toolkit::FlexContainer::JUSTIFY_FLEX_START ); + + if( value.GetType() == Property::INTEGER ) + { + flexContainerImpl.SetJustifyContent( static_cast( value.Get< int >() ) ); + } + else if( Scripting::GetEnumeration< Toolkit::FlexContainer::Justification >( value.Get< std::string >().c_str(), + JUSTIFY_CONTENT_STRING_TABLE, + JUSTIFY_CONTENT_STRING_TABLE_COUNT, + justifyContent ) ) + { + flexContainerImpl.SetJustifyContent(justifyContent); + } + break; + } + case Toolkit::FlexContainer::Property::ALIGN_ITEMS: + { + Toolkit::FlexContainer::Alignment alignItems( Toolkit::FlexContainer::ALIGN_STRETCH ); + + if( value.GetType() == Property::INTEGER ) + { + flexContainerImpl.SetAlignItems( static_cast( value.Get< int >() ) ); + } + else if( Scripting::GetEnumeration< Toolkit::FlexContainer::Alignment >( value.Get< std::string >().c_str(), + ALIGN_ITEMS_STRING_TABLE, + ALIGN_ITEMS_STRING_TABLE_COUNT, + alignItems ) ) + { + flexContainerImpl.SetAlignItems(alignItems); + } + break; + } + case Toolkit::FlexContainer::Property::ALIGN_CONTENT: + { + Toolkit::FlexContainer::Alignment alignContent( Toolkit::FlexContainer::ALIGN_FLEX_START ); + + if( value.GetType() == Property::INTEGER ) + { + flexContainerImpl.SetAlignContent( static_cast( value.Get< int >() ) ); + } + else if( Scripting::GetEnumeration< Toolkit::FlexContainer::Alignment >( value.Get< std::string >().c_str(), + ALIGN_CONTENT_STRING_TABLE, + ALIGN_CONTENT_STRING_TABLE_COUNT, + alignContent ) ) + { + flexContainerImpl.SetAlignContent(alignContent); + } + break; + } + } + } +} + +Property::Value FlexContainer::GetProperty( BaseObject* object, Property::Index index ) +{ + Property::Value value; + + Toolkit::FlexContainer flexContainer = Toolkit::FlexContainer::DownCast( Dali::BaseHandle( object ) ); + + if( flexContainer ) + { + FlexContainer& flexContainerImpl( GetImpl( flexContainer ) ); + switch( index ) + { + case Toolkit::FlexContainer::Property::CONTENT_DIRECTION: + { + value = flexContainerImpl.GetContentDirection(); + break; + } + case Toolkit::FlexContainer::Property::FLEX_DIRECTION: + { + value = flexContainerImpl.GetFlexDirection(); + break; + } + case Toolkit::FlexContainer::Property::FLEX_WRAP: + { + value = flexContainerImpl.GetFlexWrap(); + break; + } + case Toolkit::FlexContainer::Property::JUSTIFY_CONTENT: + { + value = flexContainerImpl.GetJustifyContent(); + break; + } + case Toolkit::FlexContainer::Property::ALIGN_ITEMS: + { + value = flexContainerImpl.GetAlignItems(); + break; + } + case Toolkit::FlexContainer::Property::ALIGN_CONTENT: + { + value = flexContainerImpl.GetAlignContent(); + break; + } + } + } + + return value; +} + +void FlexContainer::OnChildAdd( Actor& child ) +{ + Control::OnChildAdd( child ); + + // Anchor actor to top left of the container + child.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + child.SetParentOrigin( ParentOrigin::TOP_LEFT ); + + // Create a new node for the child. + FlexItemNode childNode; + childNode.actor = child; + childNode.node = new_css_node(); + childNode.node->get_child = GetChildNodeAtIndex; + childNode.node->is_dirty = IsNodeDirty; + mChildrenNodes.push_back(childNode); +} + +void FlexContainer::OnChildRemove( Actor& child ) +{ + for( unsigned int i = 0; i < mChildrenNodes.size(); i++ ) + { + if( mChildrenNodes[i].actor.GetHandle() == child ) + { + free_css_node( mChildrenNodes[i].node ); + mChildrenNodes.erase( mChildrenNodes.begin() + i ); + + // Relayout the container only if instances were found + RelayoutRequest(); + break; + } + } + + Control::OnChildRemove( child ); +} + +void FlexContainer::OnRelayout( const Vector2& size, RelayoutContainer& container ) +{ + for( unsigned int i = 0; i < mChildrenNodes.size(); i++ ) + { + Actor child = mChildrenNodes[i].actor.GetHandle(); + if( child ) + { + float negotiatedWidth = child.GetRelayoutSize(Dimension::WIDTH); + float negotiatedHeight = child.GetRelayoutSize(Dimension::HEIGHT); + + if( negotiatedWidth > 0 ) + { + mChildrenNodes[i].node->style.dimensions[CSS_WIDTH] = negotiatedWidth; + } + if( negotiatedHeight > 0 ) + { + mChildrenNodes[i].node->style.dimensions[CSS_HEIGHT] = negotiatedHeight; + } + } + } + + // Relayout the container + RelayoutChildren(); + + for( unsigned int i = 0; i < mChildrenNodes.size(); i++ ) + { + Actor child = mChildrenNodes[i].actor.GetHandle(); + if( child ) + { + if( child.GetResizePolicy( Dimension::WIDTH ) != ResizePolicy::USE_ASSIGNED_SIZE ) + { + child.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::WIDTH ); + } + if( child.GetResizePolicy( Dimension::HEIGHT ) != ResizePolicy::USE_ASSIGNED_SIZE ) + { + child.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::HEIGHT ); + } + + container.Add( child, Vector2(mChildrenNodes[i].node->layout.dimensions[CSS_WIDTH], mChildrenNodes[i].node->layout.dimensions[CSS_HEIGHT] ) ); + } + } +} + +bool FlexContainer::RelayoutDependentOnChildren( Dimension::Type dimension ) +{ + return true; +} + +void FlexContainer::OnSizeSet( const Vector3& size ) +{ + if( mRootNode.node ) + { + Actor self = Self(); + + mRootNode.node->style.dimensions[CSS_WIDTH] = size.x; + mRootNode.node->style.dimensions[CSS_HEIGHT] = size.y; + + RelayoutRequest(); + } +} + +void FlexContainer::OnSizeAnimation( Animation& animation, const Vector3& targetSize ) +{ + // @todo Animate the children to their target size and position +} + +void FlexContainer::ComputeLayout() +{ + if( mRootNode.node ) + { + mRootNode.node->children_count = mChildrenNodes.size(); + + // Intialize the layout. + mRootNode.node->layout.position[CSS_LEFT] = 0; + mRootNode.node->layout.position[CSS_TOP] = 0; + mRootNode.node->layout.position[CSS_BOTTOM] = 0; + mRootNode.node->layout.position[CSS_RIGHT] = 0; + mRootNode.node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + mRootNode.node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + for( unsigned int i = 0; i < mChildrenNodes.size(); i++ ) + { + css_node_t* childNode = mChildrenNodes[i].node; + Actor childActor = mChildrenNodes[i].actor.GetHandle(); + + childNode->layout.position[CSS_LEFT] = 0; + childNode->layout.position[CSS_TOP] = 0; + childNode->layout.position[CSS_BOTTOM] = 0; + childNode->layout.position[CSS_RIGHT] = 0; + childNode->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + childNode->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + // Intialize the style of the child. + childNode->style.minDimensions[CSS_WIDTH] = childActor.GetMinimumSize().x; + childNode->style.minDimensions[CSS_HEIGHT] = childActor.GetMinimumSize().y; + childNode->style.maxDimensions[CSS_WIDTH] = childActor.GetMaximumSize().x; + childNode->style.maxDimensions[CSS_HEIGHT] = childActor.GetMaximumSize().y; + + // Test custom properties on the child + if( childActor.GetPropertyIndex( FLEX_PROPERTY_NAME ) != Property::INVALID_INDEX ) + { + childNode->style.flex = childActor.GetProperty( childActor.GetPropertyIndex(FLEX_PROPERTY_NAME) ).Get(); + } + + Property::Index alignSelfPropertyIndex = childActor.GetPropertyIndex( ALIGN_SELF_PROPERTY_NAME ); + if( alignSelfPropertyIndex != Property::INVALID_INDEX ) + { + Property::Value alignSelfPropertyValue = childActor.GetProperty( alignSelfPropertyIndex ); + + Toolkit::FlexContainer::Alignment alignSelf( Toolkit::FlexContainer::ALIGN_AUTO ); + if( alignSelfPropertyValue.GetType() == Property::INTEGER ) + { + alignSelf = static_cast( alignSelfPropertyValue.Get< int >() ); + } + else if( alignSelfPropertyValue.GetType() == Property::STRING ) + { + std::string value = alignSelfPropertyValue.Get(); + Scripting::GetEnumeration< Toolkit::FlexContainer::Alignment >( value.c_str(), + ALIGN_SELF_STRING_TABLE, + ALIGN_SELF_STRING_TABLE_COUNT, + alignSelf ); + } + childNode->style.align_self = static_cast(alignSelf); + } + + if( childActor.GetPropertyIndex( FLEX_PADDING_PROPERTY_NAME ) != Property::INVALID_INDEX ) + { + Vector4 flexPadding = childActor.GetProperty( childActor.GetPropertyIndex(FLEX_PADDING_PROPERTY_NAME) ).Get(); + childNode->style.padding[CSS_LEFT] = flexPadding.x; + childNode->style.padding[CSS_TOP] = flexPadding.y; + childNode->style.padding[CSS_RIGHT] = flexPadding.z; + childNode->style.padding[CSS_BOTTOM] = flexPadding.w; + } + + if( childActor.GetPropertyIndex( FLEX_BORDER_PROPERTY_NAME ) != Property::INVALID_INDEX ) + { + Vector4 flexBorder = childActor.GetProperty( childActor.GetPropertyIndex(FLEX_BORDER_PROPERTY_NAME) ).Get(); + childNode->style.border[CSS_LEFT] = flexBorder.x; + childNode->style.border[CSS_TOP] = flexBorder.y; + childNode->style.border[CSS_RIGHT] = flexBorder.z; + childNode->style.border[CSS_BOTTOM] = flexBorder.w; + } + + if( childActor.GetPropertyIndex( FLEX_MARGIN_PROPERTY_NAME ) != Property::INVALID_INDEX ) + { + Vector4 flexMargin = childActor.GetProperty( childActor.GetPropertyIndex(FLEX_MARGIN_PROPERTY_NAME) ).Get(); + childNode->style.margin[CSS_LEFT] = flexMargin.x; + childNode->style.margin[CSS_TOP] = flexMargin.y; + childNode->style.margin[CSS_RIGHT] = flexMargin.z; + childNode->style.margin[CSS_BOTTOM] = flexMargin.w; + } + } + + // Calculate the layout + layoutNode( mRootNode.node, Self().GetMaximumSize().x, Self().GetMaximumSize().y, mRootNode.node->style.direction ); + } +} + +void FlexContainer::RelayoutChildren() +{ + ComputeLayout(); + + // Set size and position of children according to the layout calculation + for( unsigned int i = 0; i < mChildrenNodes.size(); i++ ) + { + Dali::Actor child = mChildrenNodes[i].actor.GetHandle(); + if( child ) + { + child.SetX( mChildrenNodes[i].node->layout.position[CSS_LEFT] ); + child.SetY( mChildrenNodes[i].node->layout.position[CSS_TOP] ); + } + } +} + +Actor FlexContainer::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled) +{ + Actor nextFocusableActor; + + // First check whether there is any items in the container + if( mChildrenNodes.size() > 0 ) + { + if ( !currentFocusedActor || currentFocusedActor == Self() ) + { + // Nothing is currently focused, so the first child in the container should be focused. + nextFocusableActor = mChildrenNodes[0].actor.GetHandle(); + } + else + { + // Check whether the current focused actor is within flex container + int currentFocusedActorIndex = -1; + for( unsigned int index = 0; index < mChildrenNodes.size(); index++ ) + { + if( currentFocusedActor == mChildrenNodes[index].actor.GetHandle() ) + { + currentFocusedActorIndex = index; + break; + } + } + + if( currentFocusedActorIndex > -1 ) + { + int previousCheckedActorIndex = -1; + int nextFocusedActorIndex = currentFocusedActorIndex; + switch ( direction ) + { + case Toolkit::Control::KeyboardFocus::LEFT: + case Toolkit::Control::KeyboardFocus::UP: + { + // Search the next focusable actor in the backward direction + do + { + nextFocusedActorIndex--; + if( nextFocusedActorIndex < 0 ) + { + nextFocusedActorIndex = loopEnabled ? mChildrenNodes.size() - 1 : 0; + } + if( nextFocusedActorIndex != previousCheckedActorIndex && nextFocusedActorIndex != currentFocusedActorIndex ) + { + previousCheckedActorIndex = nextFocusedActorIndex; + } + else + { + break; + } + } while ( !mChildrenNodes[nextFocusedActorIndex].actor.GetHandle().IsKeyboardFocusable() ); + break; + } + case Toolkit::Control::KeyboardFocus::RIGHT: + case Toolkit::Control::KeyboardFocus::DOWN: + { + // Search the next focusable actor in the forward direction + do + { + nextFocusedActorIndex++; + if( nextFocusedActorIndex > static_cast(mChildrenNodes.size() - 1) ) + { + nextFocusedActorIndex = loopEnabled ? 0 : mChildrenNodes.size() - 1; + } + if( nextFocusedActorIndex != previousCheckedActorIndex && nextFocusedActorIndex != currentFocusedActorIndex ) + { + previousCheckedActorIndex = nextFocusedActorIndex; + } + else + { + break; + } + } while ( !mChildrenNodes[nextFocusedActorIndex].actor.GetHandle().IsKeyboardFocusable() ); + break; + } + } + + if( nextFocusedActorIndex != currentFocusedActorIndex ) + { + nextFocusableActor = mChildrenNodes[nextFocusedActorIndex].actor.GetHandle(); + } + else + { + // No focusble child in the container + nextFocusableActor = Actor(); + } + } + else + { + // The current focused actor is not within flex container, so the first child in the container should be focused. + nextFocusableActor = mChildrenNodes[0].actor.GetHandle(); + } + } + } + + return nextFocusableActor; +} + +FlexContainer::FlexContainer() +: Control( ControlBehaviour( ACTOR_BEHAVIOUR_NONE ) ), + mContentDirection( Toolkit::FlexContainer::INHERIT ), + mFlexDirection( Toolkit::FlexContainer::COLUMN ), + mFlexWrap( Toolkit::FlexContainer::NO_WRAP ), + mJustifyContent( Toolkit::FlexContainer::JUSTIFY_FLEX_START ), + mAlignItems( Toolkit::FlexContainer::ALIGN_STRETCH ), + mAlignContent( Toolkit::FlexContainer::ALIGN_FLEX_START ) +{ + SetKeyboardNavigationSupport( true ); +} + +void FlexContainer::OnInitialize() +{ + // Initialize the node for the flex container itself + Dali::Actor self = Self(); + mRootNode.actor = self; + mRootNode.node = new_css_node(); + mRootNode.node->context = &mChildrenNodes; + + // Set default style + mRootNode.node->style.direction = static_cast( mContentDirection ); + mRootNode.node->style.flex_direction = static_cast( mFlexDirection ); + mRootNode.node->style.flex_wrap = static_cast( mFlexWrap ); + mRootNode.node->style.justify_content = static_cast( mJustifyContent ); + mRootNode.node->style.align_items = static_cast( mAlignItems ); + mRootNode.node->style.align_content = static_cast( mAlignContent ); + + // Set callbacks. + mRootNode.node->get_child = GetChildNodeAtIndex; + mRootNode.node->is_dirty = IsNodeDirty; + + // Make self as keyboard focusable and focus group + self.SetKeyboardFocusable( true ); + SetAsKeyboardFocusGroup( true ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/flex-container/flex-container-impl.h b/dali-toolkit/internal/controls/flex-container/flex-container-impl.h new file mode 100644 index 0000000..58c25b6 --- /dev/null +++ b/dali-toolkit/internal/controls/flex-container/flex-container-impl.h @@ -0,0 +1,275 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_FLEX_CONTAINER_H__ +#define __DALI_TOOLKIT_INTERNAL_FLEX_CONTAINER_H__ + +/* + * Copyright (c) 2016 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. + * + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include +extern "C" +{ +#include "layout.h" +} + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * FlexContainer is a custom control for laying out actors in a flexbox layout + * @see Dali::Toolkit:FlexContainer for more details + */ +class FlexContainer : public Control +{ +public: + + /** + * The structure to store the style properties and layout information of flex item + */ + struct FlexItemNode + { + WeakHandle< Dali::Actor > actor; ///< Actor handle of the flex item + css_node_t* node; ///< The style properties and layout information + }; + + typedef std::vector< FlexItemNode > FlexItemNodeContainer; + +public: + + /** + * Construct a new FlexContainer. + */ + FlexContainer(); + + /** + * Create a new FlexContainer. + * @return A smart-pointer to the newly allocated FlexContainer. + */ + static Toolkit::FlexContainer New(); + + /** + * @brief Set the primary direction in which content is ordered. + * @param[in] contentDirection The direction of the content. + */ + void SetContentDirection(Toolkit::FlexContainer::ContentDirection contentDirection); + + /** + * @brief Get the direction of the content. + * @return The direction of the content. + */ + Toolkit::FlexContainer::ContentDirection GetContentDirection(); + + /** + * @brief Set the direction flex items are laid out. + * @param[in] flexDirection The direction flex items are laid out. + */ + void SetFlexDirection(Toolkit::FlexContainer::FlexDirection flexDirection); + + /** + * @brief Get the direction flex items are laid out. + * @return The direction flex items are laid out. + */ + Toolkit::FlexContainer::FlexDirection GetFlexDirection(); + + /** + * @brief Set whether the flex items should wrap or not, if there + * is no enough room for them on one flex line. + * @param[in] flexWrap The wrap type. + */ + void SetFlexWrap(Toolkit::FlexContainer::WrapType flexWrap); + + /** + * @brief Get whether the flex items should wrap or not, if there + * is no enough room for them on one flex line. + * @return The wrap type. + */ + Toolkit::FlexContainer::WrapType GetFlexWrap(); + + /** + * @brief Set the horizontal alignment of the flex items when the items + * do not use all available space on the main-axis. + * @param[in] justifyContent The horizontal alignment of flex items. + */ + void SetJustifyContent(Toolkit::FlexContainer::Justification justifyContent); + + /** + * @brief Get the horizontal alignment of the flex items when the items + * do not use all available space on the main-axis. + * @return The horizontal alignment of flex items. + */ + Toolkit::FlexContainer::Justification GetJustifyContent(); + + /** + * @brief Set the vertical alignment of the flex items when the items + * do not use all available space on the cross-axis. + * @param[in] alignItems The vertical alignment of flex items. + */ + void SetAlignItems(Toolkit::FlexContainer::Alignment alignItems); + + /** + * @brief Get the vertical alignment of the flex items when the items + * do not use all available space on the cross-axis. + * @return The vertical alignment of flex items. + */ + Toolkit::FlexContainer::Alignment GetAlignItems(); + + /** + * @brief Set the vertical alignment of the flex lines when the lines + * do not use all available space on the cross-axis. + * @param[in] alignItems The vertical alignment of flex lines. + */ + void SetAlignContent(Toolkit::FlexContainer::Alignment alignContent); + + /** + * @brief Get the vertical alignment of the flex lines when the lines + * do not use all available space on the cross-axis. + * @return The vertical alignment of flex lines. + */ + Toolkit::FlexContainer::Alignment GetAlignContent(); + + // Properties + + /** + * Called when a property of an object of this type is set. + * @param[in] object The object whose property is set. + * @param[in] index The property index. + * @param[in] value The new property value. + */ + static void SetProperty( BaseObject* object, Property::Index index, const Property::Value& value ); + + /** + * Called to retrieve a property of an object of this type. + * @param[in] object The object whose property is to be retrieved. + * @param[in] index The property index. + * @return The current value of the property. + */ + static Property::Value GetProperty( BaseObject* object, Property::Index index ); + +private: // From Control + + /** + * @copydoc Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Control::OnChildAdd(Actor& child) + */ + virtual void OnChildAdd( Actor& child ); + + /** + * @copydoc Control::OnChildRemove(Actor& child) + */ + virtual void OnChildRemove( Actor& child ); + + /** + * @copydoc Control::OnRelayout + */ + virtual void OnRelayout( const Vector2& size, RelayoutContainer& container ); + + /** + * @copydoc Control::RelayoutDependentOnChildren() + */ + virtual bool RelayoutDependentOnChildren( Dimension::Type dimension = Dimension::ALL_DIMENSIONS ); + + /** + * @copydoc Control::GetNextKeyboardFocusableActor + */ + virtual Actor GetNextKeyboardFocusableActor( Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled ); + + /** + * @copydoc CustomActorImpl::OnSizeSet( const Vector3& size ) + */ + virtual void OnSizeSet( const Vector3& size ); + + /** + * @copydoc CustomActorImpl::OnSizeAnimation(Animation&, const Vector3&) + */ + virtual void OnSizeAnimation(Animation& animation, const Vector3& targetSize); + +private: // Implementation + + /** + * Calculate the layout properties of all the children + */ + void ComputeLayout(); + + /** + * Calculate the layout of the children and relayout them with their new size and position + */ + void RelayoutChildren(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~FlexContainer(); + +private: + + // Undefined copy constructor and assignment operators + FlexContainer(const FlexContainer&); + FlexContainer& operator=(const FlexContainer& rhs); + +private: // Data + + FlexItemNode mRootNode; ///< Style properties and layout information of flex container + FlexItemNodeContainer mChildrenNodes; ///< Style properties and layout information of flex items in the container + + Toolkit::FlexContainer::ContentDirection mContentDirection; ///< The content direction of the container + Toolkit::FlexContainer::FlexDirection mFlexDirection; ///< The flex direction of the container + Toolkit::FlexContainer::WrapType mFlexWrap; ///< The wrap type of the container + Toolkit::FlexContainer::Justification mJustifyContent; ///< The alignment of flex items in the container on the main-axis + Toolkit::FlexContainer::Alignment mAlignItems; ///< The alignment of flex items in the container on the cross-axis + Toolkit::FlexContainer::Alignment mAlignContent; ///< The alignment of flex lines in the container on the cross-axis +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::FlexContainer& GetImpl( Toolkit::FlexContainer& tableView ) +{ + DALI_ASSERT_ALWAYS(tableView); + + Dali::RefObject& handle = tableView.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::FlexContainer& GetImpl( const Toolkit::FlexContainer& tableView ) +{ + DALI_ASSERT_ALWAYS(tableView); + + const Dali::RefObject& handle = tableView.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_FLEX_CONTAINER_H__ diff --git a/dali-toolkit/internal/controls/flex-container/layout.c b/dali-toolkit/internal/controls/flex-container/layout.c new file mode 100644 index 0000000..618d01b --- /dev/null +++ b/dali-toolkit/internal/controls/flex-container/layout.c @@ -0,0 +1,1371 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include +#include +#include +#include + +// in concatenated header, don't include Layout.h it's already at the top +#ifndef CSS_LAYOUT_IMPLEMENTATION +#include "layout.h" +#endif + +#ifdef _MSC_VER +#include +#define isnan _isnan + +/* define fmaxf if < VC12 */ +#if _MSC_VER < 1800 +__forceinline const float fmaxf(const float a, const float b) { + return (a > b) ? a : b; +} +#endif +#endif + +bool isUndefined(float value) { + return isnan(value); +} + +static bool eq(float a, float b) { + if (isUndefined(a)) { + return isUndefined(b); + } + return fabs(a - b) < 0.0001; +} + +void init_css_node(css_node_t *node) { + node->style.align_items = CSS_ALIGN_STRETCH; + node->style.align_content = CSS_ALIGN_FLEX_START; + + node->style.direction = CSS_DIRECTION_INHERIT; + node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN; + + // Some of the fields default to undefined and not 0 + node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.position[CSS_LEFT] = CSS_UNDEFINED; + node->style.position[CSS_TOP] = CSS_UNDEFINED; + node->style.position[CSS_RIGHT] = CSS_UNDEFINED; + node->style.position[CSS_BOTTOM] = CSS_UNDEFINED; + + node->style.margin[CSS_START] = CSS_UNDEFINED; + node->style.margin[CSS_END] = CSS_UNDEFINED; + node->style.padding[CSS_START] = CSS_UNDEFINED; + node->style.padding[CSS_END] = CSS_UNDEFINED; + node->style.border[CSS_START] = CSS_UNDEFINED; + node->style.border[CSS_END] = CSS_UNDEFINED; + + node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + // Such that the comparison is always going to be false + node->layout.last_requested_dimensions[CSS_WIDTH] = -1; + node->layout.last_requested_dimensions[CSS_HEIGHT] = -1; + node->layout.last_parent_max_width = -1; + node->layout.last_parent_max_height = -1; + node->layout.last_direction = (css_direction_t)-1; + node->layout.should_update = true; +} + +css_node_t *new_css_node() { + css_node_t *node = (css_node_t *)calloc(1, sizeof(*node)); + init_css_node(node); + return node; +} + +void free_css_node(css_node_t *node) { + free(node); +} + +static void indent(int n) { + int i; + for (i = 0; i < n; ++i) { + printf(" "); + } +} + +static void print_number_0(const char *str, float number) { + if (!eq(number, 0)) { + printf("%s: %g, ", str, number); + } +} + +static void print_number_nan(const char *str, float number) { + if (!isnan(number)) { + printf("%s: %g, ", str, number); + } +} + +static bool four_equal(float four[4]) { + return + eq(four[0], four[1]) && + eq(four[0], four[2]) && + eq(four[0], four[3]); +} + + +static void print_css_node_rec( + css_node_t *node, + css_print_options_t options, + int level +) { + indent(level); + printf("{"); + + if (node->print) { + node->print(node->context); + } + + if (options & CSS_PRINT_LAYOUT) { + printf("layout: {"); + printf("width: %g, ", node->layout.dimensions[CSS_WIDTH]); + printf("height: %g, ", node->layout.dimensions[CSS_HEIGHT]); + printf("top: %g, ", node->layout.position[CSS_TOP]); + printf("left: %g", node->layout.position[CSS_LEFT]); + printf("}, "); + } + + if (options & CSS_PRINT_STYLE) { + if (node->style.direction == CSS_DIRECTION_INHERIT) { + printf("direction: 'inherit', "); + } else if (node->style.direction == CSS_DIRECTION_LTR) { + printf("direction: 'LTR', "); + } else if (node->style.direction == CSS_DIRECTION_RTL) { + printf("direction: 'RTL', "); + } + + if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) { + printf("flexDirection: 'column', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { + printf("flexDirection: 'columnReverse', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) { + printf("flexDirection: 'row', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { + printf("flexDirection: 'rowReverse', "); + } + + if (node->style.justify_content == CSS_JUSTIFY_FLEX_START) { + printf("justifyContent: 'flex-start', "); + } else if (node->style.justify_content == CSS_JUSTIFY_CENTER) { + printf("justifyContent: 'center', "); + } else if (node->style.justify_content == CSS_JUSTIFY_FLEX_END) { + printf("justifyContent: 'flex-end', "); + } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_AROUND) { + printf("justifyContent: 'space-around', "); + } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_BETWEEN) { + printf("justifyContent: 'space-between', "); + } + + if (node->style.align_items == CSS_ALIGN_FLEX_START) { + printf("alignItems: 'flex-start', "); + } else if (node->style.align_items == CSS_ALIGN_CENTER) { + printf("alignItems: 'center', "); + } else if (node->style.align_items == CSS_ALIGN_FLEX_END) { + printf("alignItems: 'flex-end', "); + } else if (node->style.align_items == CSS_ALIGN_STRETCH) { + printf("alignItems: 'stretch', "); + } + + if (node->style.align_content == CSS_ALIGN_FLEX_START) { + printf("alignContent: 'flex-start', "); + } else if (node->style.align_content == CSS_ALIGN_CENTER) { + printf("alignContent: 'center', "); + } else if (node->style.align_content == CSS_ALIGN_FLEX_END) { + printf("alignContent: 'flex-end', "); + } else if (node->style.align_content == CSS_ALIGN_STRETCH) { + printf("alignContent: 'stretch', "); + } + + if (node->style.align_self == CSS_ALIGN_FLEX_START) { + printf("alignSelf: 'flex-start', "); + } else if (node->style.align_self == CSS_ALIGN_CENTER) { + printf("alignSelf: 'center', "); + } else if (node->style.align_self == CSS_ALIGN_FLEX_END) { + printf("alignSelf: 'flex-end', "); + } else if (node->style.align_self == CSS_ALIGN_STRETCH) { + printf("alignSelf: 'stretch', "); + } else if (node->style.align_self == CSS_ALIGN_AUTO) { + printf("alignSelf: 'auto', "); + } + + if (node->style.flex_wrap == CSS_NOWRAP) { + printf("flexWrap: 'no-wrap', "); + } else if (node->style.flex_wrap == CSS_WRAP) { + printf("flexWrap: 'wrap', "); + } + + print_number_nan("flex", node->style.flex); + + if (four_equal(node->style.margin)) { + print_number_0("margin", node->style.margin[CSS_LEFT]); + } else { + print_number_0("marginLeft", node->style.margin[CSS_LEFT]); + print_number_0("marginRight", node->style.margin[CSS_RIGHT]); + print_number_0("marginTop", node->style.margin[CSS_TOP]); + print_number_0("marginBottom", node->style.margin[CSS_BOTTOM]); + print_number_0("marginStart", node->style.margin[CSS_START]); + print_number_0("marginEnd", node->style.margin[CSS_END]); + } + + if (four_equal(node->style.padding)) { + print_number_0("padding", node->style.margin[CSS_LEFT]); + } else { + print_number_0("paddingLeft", node->style.padding[CSS_LEFT]); + print_number_0("paddingRight", node->style.padding[CSS_RIGHT]); + print_number_0("paddingTop", node->style.padding[CSS_TOP]); + print_number_0("paddingBottom", node->style.padding[CSS_BOTTOM]); + print_number_0("paddingStart", node->style.padding[CSS_START]); + print_number_0("paddingEnd", node->style.padding[CSS_END]); + } + + if (four_equal(node->style.border)) { + print_number_0("borderWidth", node->style.border[CSS_LEFT]); + } else { + print_number_0("borderLeftWidth", node->style.border[CSS_LEFT]); + print_number_0("borderRightWidth", node->style.border[CSS_RIGHT]); + print_number_0("borderTopWidth", node->style.border[CSS_TOP]); + print_number_0("borderBottomWidth", node->style.border[CSS_BOTTOM]); + print_number_0("borderStartWidth", node->style.border[CSS_START]); + print_number_0("borderEndWidth", node->style.border[CSS_END]); + } + + print_number_nan("width", node->style.dimensions[CSS_WIDTH]); + print_number_nan("height", node->style.dimensions[CSS_HEIGHT]); + + if(node->style.minDimensions[CSS_WIDTH] != CSS_UNDEFINED) { + print_number_nan("minWidth", node->style.minDimensions[CSS_WIDTH]); + } + if(node->style.minDimensions[CSS_HEIGHT] != CSS_UNDEFINED) { + print_number_nan("minHeight", node->style.minDimensions[CSS_HEIGHT]); + } + if(node->style.maxDimensions[CSS_WIDTH] != CSS_UNDEFINED) { + print_number_nan("maxWidth", node->style.maxDimensions[CSS_WIDTH]); + } + if(node->style.maxDimensions[CSS_HEIGHT] != CSS_UNDEFINED) { + print_number_nan("maxHeight", node->style.maxDimensions[CSS_HEIGHT]); + } + + if (node->style.position_type == CSS_POSITION_ABSOLUTE) { + printf("position: 'absolute', "); + } + else if (node->style.position_type == CSS_POSITION_RELATIVE) { + printf("position: 'relative', "); + } + + print_number_nan("position-left", node->style.position[CSS_LEFT]); + print_number_nan("position-right", node->style.position[CSS_RIGHT]); + print_number_nan("position-top", node->style.position[CSS_TOP]); + print_number_nan("position-bottom", node->style.position[CSS_BOTTOM]); + } + + if (options & CSS_PRINT_CHILDREN && node->children_count > 0) { + printf("children: [\n"); + int i; + for (i = 0; i < node->children_count; ++i) { + print_css_node_rec(node->get_child(node->context, i), options, level + 1); + } + indent(level); + printf("]},\n"); + } else { + printf("},\n"); + } +} + +void print_css_node(css_node_t *node, css_print_options_t options) { + print_css_node_rec(node, options, 0); +} + + +static css_position_t leading[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT +}; +static css_position_t trailing[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_TOP, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_LEFT +}; +static css_position_t pos[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT +}; +static css_dimension_t dim[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_HEIGHT, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_HEIGHT, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_WIDTH +}; + +static bool isRowDirection(css_flex_direction_t flex_direction) { + return flex_direction == CSS_FLEX_DIRECTION_ROW || + flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE; +} + +static bool isColumnDirection(css_flex_direction_t flex_direction) { + return flex_direction == CSS_FLEX_DIRECTION_COLUMN || + flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE; +} + +static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) { + return node->style.margin[CSS_START]; + } + + return node->style.margin[leading[axis]]; +} + +static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) { + return node->style.margin[CSS_END]; + } + + return node->style.margin[trailing[axis]]; +} + +static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.padding[CSS_START]) && + node->style.padding[CSS_START] >= 0) { + return node->style.padding[CSS_START]; + } + + if (node->style.padding[leading[axis]] >= 0) { + return node->style.padding[leading[axis]]; + } + + return 0; +} + +static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.padding[CSS_END]) && + node->style.padding[CSS_END] >= 0) { + return node->style.padding[CSS_END]; + } + + if (node->style.padding[trailing[axis]] >= 0) { + return node->style.padding[trailing[axis]]; + } + + return 0; +} + +static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.border[CSS_START]) && + node->style.border[CSS_START] >= 0) { + return node->style.border[CSS_START]; + } + + if (node->style.border[leading[axis]] >= 0) { + return node->style.border[leading[axis]]; + } + + return 0; +} + +static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.border[CSS_END]) && + node->style.border[CSS_END] >= 0) { + return node->style.border[CSS_END]; + } + + if (node->style.border[trailing[axis]] >= 0) { + return node->style.border[trailing[axis]]; + } + + return 0; +} + +static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { + return getLeadingPadding(node, axis) + getLeadingBorder(node, axis); +} + +static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { + return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); +} + +static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) { + return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); +} + +static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) { + return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); +} + +static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) { + return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis); +} + +static css_align_t getAlignItem(css_node_t *node, css_node_t *child) { + if (child->style.align_self != CSS_ALIGN_AUTO) { + return child->style.align_self; + } + return node->style.align_items; +} + +static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) { + css_direction_t direction = node->style.direction; + + if (direction == CSS_DIRECTION_INHERIT) { + direction = parentDirection > CSS_DIRECTION_INHERIT ? parentDirection : CSS_DIRECTION_LTR; + } + + return direction; +} + +static css_flex_direction_t getFlexDirection(css_node_t *node) { + return node->style.flex_direction; +} + +static css_flex_direction_t resolveAxis(css_flex_direction_t flex_direction, css_direction_t direction) { + if (direction == CSS_DIRECTION_RTL) { + if (flex_direction == CSS_FLEX_DIRECTION_ROW) { + return CSS_FLEX_DIRECTION_ROW_REVERSE; + } else if (flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { + return CSS_FLEX_DIRECTION_ROW; + } + } + + return flex_direction; +} + +static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_direction, css_direction_t direction) { + if (isColumnDirection(flex_direction)) { + return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); + } else { + return CSS_FLEX_DIRECTION_COLUMN; + } +} + +static float getFlex(css_node_t *node) { + return node->style.flex; +} + +static bool isFlex(css_node_t *node) { + return ( + node->style.position_type == CSS_POSITION_RELATIVE && + getFlex(node) > 0 + ); +} + +static bool isFlexWrap(css_node_t *node) { + return node->style.flex_wrap == CSS_WRAP; +} + +static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) { + return node->layout.dimensions[dim[axis]] + + getLeadingMargin(node, axis) + + getTrailingMargin(node, axis); +} + +static bool isStyleDimDefined(css_node_t *node, css_flex_direction_t axis) { + float value = node->style.dimensions[dim[axis]]; + return !isUndefined(value) && value >= 0.0; +} + +static bool isLayoutDimDefined(css_node_t *node, css_flex_direction_t axis) { + float value = node->layout.dimensions[dim[axis]]; + return !isUndefined(value) && value >= 0.0; +} + +static bool isPosDefined(css_node_t *node, css_position_t position) { + return !isUndefined(node->style.position[position]); +} + +static bool isMeasureDefined(css_node_t *node) { + return node->measure; +} + +static float getPosition(css_node_t *node, css_position_t position) { + float result = node->style.position[position]; + if (!isUndefined(result)) { + return result; + } + return 0; +} + +static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) { + float min = CSS_UNDEFINED; + float max = CSS_UNDEFINED; + + if (isColumnDirection(axis)) { + min = node->style.minDimensions[CSS_HEIGHT]; + max = node->style.maxDimensions[CSS_HEIGHT]; + } else if (isRowDirection(axis)) { + min = node->style.minDimensions[CSS_WIDTH]; + max = node->style.maxDimensions[CSS_WIDTH]; + } + + float boundValue = value; + + if (!isUndefined(max) && max >= 0.0 && boundValue > max) { + boundValue = max; + } + if (!isUndefined(min) && min >= 0.0 && boundValue < min) { + boundValue = min; + } + + return boundValue; +} + +// When the user specifically sets a value for width or height +static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { + // The parent already computed us a width or height. We just skip it + if (isLayoutDimDefined(node, axis)) { + return; + } + // We only run if there's a width or height defined + if (!isStyleDimDefined(node, axis)) { + return; + } + + // The dimensions can never be smaller than the padding and border + node->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(node, axis, node->style.dimensions[dim[axis]]), + getPaddingAndBorderAxis(node, axis) + ); +} + +static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) { + child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] - + child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]]; + } + +// If both left and right are defined, then use left. Otherwise return +// +left or -right depending on which is defined. +static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { + float lead = node->style.position[leading[axis]]; + if (!isUndefined(lead)) { + return lead; + } + return -getPosition(node, trailing[axis]); +} + +static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) { + /** START_GENERATED **/ + css_direction_t direction = resolveDirection(node, parentDirection); + css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); + css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); + css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); + + // Handle width and height style attributes + setDimensionFromStyle(node, mainAxis); + setDimensionFromStyle(node, crossAxis); + + // Set the resolved resolution in the node's layout + node->layout.direction = direction; + + // The position is set by the parent, but we need to complete it with a + // delta composed of the margin and left/top/right/bottom + node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + + // Inline immutable values from the target node to avoid excessive method + // invocations during the layout calculation. + int childCount = node->children_count; + float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis); + float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); + + if (isMeasureDefined(node)) { + bool isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis); + + float width = CSS_UNDEFINED; + css_measure_mode_t widthMode = CSS_MEASURE_MODE_UNDEFINED; + if (isStyleDimDefined(node, resolvedRowAxis)) { + width = node->style.dimensions[CSS_WIDTH]; + widthMode = CSS_MEASURE_MODE_EXACTLY; + } else if (isResolvedRowDimDefined) { + width = node->layout.dimensions[dim[resolvedRowAxis]]; + widthMode = CSS_MEASURE_MODE_EXACTLY; + } else { + width = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis); + widthMode = CSS_MEASURE_MODE_AT_MOST; + } + width -= paddingAndBorderAxisResolvedRow; + if (isUndefined(width)) { + widthMode = CSS_MEASURE_MODE_UNDEFINED; + } + + float height = CSS_UNDEFINED; + css_measure_mode_t heightMode = CSS_MEASURE_MODE_UNDEFINED; + if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + height = node->style.dimensions[CSS_HEIGHT]; + heightMode = CSS_MEASURE_MODE_EXACTLY; + } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + height = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]; + heightMode = CSS_MEASURE_MODE_EXACTLY; + } else { + height = parentMaxHeight - + getMarginAxis(node, resolvedRowAxis); + heightMode = CSS_MEASURE_MODE_AT_MOST; + } + height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); + if (isUndefined(height)) { + heightMode = CSS_MEASURE_MODE_UNDEFINED; + } + + // We only need to give a dimension for the text if we haven't got any + // for it computed yet. It can either be from the style attribute or because + // the element is flexible. + bool isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined; + bool isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && + isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); + + // Let's not measure the text if we already know both dimensions + if (isRowUndefined || isColumnUndefined) { + css_dim_t measureDim = node->measure( + node->context, + width, + widthMode, + height, + heightMode + ); + if (isRowUndefined) { + node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + + paddingAndBorderAxisResolvedRow; + } + if (isColumnUndefined) { + node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + + paddingAndBorderAxisColumn; + } + } + if (childCount == 0) { + return; + } + } + + bool isNodeFlexWrap = isFlexWrap(node); + + css_justify_t justifyContent = node->style.justify_content; + + float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); + float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); + float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); + float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); + + bool isMainDimDefined = isLayoutDimDefined(node, mainAxis); + bool isCrossDimDefined = isLayoutDimDefined(node, crossAxis); + bool isMainRowDirection = isRowDirection(mainAxis); + + int i; + int ii; + css_node_t* child; + css_flex_direction_t axis; + + css_node_t* firstAbsoluteChild = NULL; + css_node_t* currentAbsoluteChild = NULL; + + float definedMainDim = CSS_UNDEFINED; + if (isMainDimDefined) { + definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; + } + + // We want to execute the next two loops one per line with flex-wrap + int startLine = 0; + int endLine = 0; + // int nextOffset = 0; + int alreadyComputedNextLayout = 0; + // We aggregate the total dimensions of the container in those two variables + float linesCrossDim = 0; + float linesMainDim = 0; + int linesCount = 0; + while (endLine < childCount) { + // Layout non flexible children and count children by type + + // mainContentDim is accumulation of the dimensions and margin of all the + // non flexible children. This will be used in order to either set the + // dimensions of the node if none already exist, or to compute the + // remaining space left for the flexible children. + float mainContentDim = 0; + + // There are three kind of children, non flexible, flexible and absolute. + // We need to know how many there are in order to distribute the space. + int flexibleChildrenCount = 0; + float totalFlexible = 0; + int nonFlexibleChildrenCount = 0; + + // Use the line loop to position children in the main axis for as long + // as they are using a simple stacking behaviour. Children that are + // immediately stacked in the initial loop will not be touched again + // in . + bool isSimpleStackMain = + (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) || + (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER); + int firstComplexMain = (isSimpleStackMain ? childCount : startLine); + + // Use the initial line loop to position children in the cross axis for + // as long as they are relatively positioned with alignment STRETCH or + // FLEX_START. Children that are immediately stacked in the initial loop + // will not be touched again in . + bool isSimpleStackCross = true; + int firstComplexCross = childCount; + + css_node_t* firstFlexChild = NULL; + css_node_t* currentFlexChild = NULL; + + float mainDim = leadingPaddingAndBorderMain; + float crossDim = 0; + + float maxWidth = CSS_UNDEFINED; + float maxHeight = CSS_UNDEFINED; + for (i = startLine; i < childCount; ++i) { + child = node->get_child(node->context, i); + child->line_index = linesCount; + + child->next_absolute_child = NULL; + child->next_flex_child = NULL; + + css_align_t alignItem = getAlignItem(node, child); + + // Pre-fill cross axis dimensions when the child is using stretch before + // we call the recursive layout pass + if (alignItem == CSS_ALIGN_STRETCH && + child->style.position_type == CSS_POSITION_RELATIVE && + isCrossDimDefined && + !isStyleDimDefined(child, crossAxis)) { + child->layout.dimensions[dim[crossAxis]] = fmaxf( + boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - + paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, crossAxis) + ); + } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) { + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild == NULL) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild != NULL) { + currentAbsoluteChild->next_absolute_child = child; + } + currentAbsoluteChild = child; + + // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both + // left and right or top and bottom). + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + if (isLayoutDimDefined(node, axis) && + !isStyleDimDefined(child, axis) && + isPosDefined(child, leading[axis]) && + isPosDefined(child, trailing[axis])) { + child->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(child, axis, node->layout.dimensions[dim[axis]] - + getPaddingAndBorderAxis(node, axis) - + getMarginAxis(child, axis) - + getPosition(child, leading[axis]) - + getPosition(child, trailing[axis])), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, axis) + ); + } + } + } + + float nextContentDim = 0; + + // It only makes sense to consider a child flexible if we have a computed + // dimension for the node-> + if (isMainDimDefined && isFlex(child)) { + flexibleChildrenCount++; + totalFlexible += child->style.flex; + + // Store a private linked list of flexible children so that we can + // efficiently traverse them later. + if (firstFlexChild == NULL) { + firstFlexChild = child; + } + if (currentFlexChild != NULL) { + currentFlexChild->next_flex_child = child; + } + currentFlexChild = child; + + // Even if we don't know its exact size yet, we already know the padding, + // border and margin. We'll use this partial information, which represents + // the smallest possible size for the child, to compute the remaining + // available space. + nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + + getMarginAxis(child, mainAxis); + + } else { + maxWidth = CSS_UNDEFINED; + maxHeight = CSS_UNDEFINED; + + if (!isMainRowDirection) { + if (isLayoutDimDefined(node, resolvedRowAxis)) { + maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - + paddingAndBorderAxisResolvedRow; + } else { + maxWidth = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis) - + paddingAndBorderAxisResolvedRow; + } + } else { + if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + paddingAndBorderAxisColumn; + } else { + maxHeight = parentMaxHeight - + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - + paddingAndBorderAxisColumn; + } + } + + // This is the main recursive call. We layout non flexible children. + if (alreadyComputedNextLayout == 0) { + layoutNode(child, maxWidth, maxHeight, direction); + } + + // Absolute positioned elements do not take part of the layout, so we + // don't use them to compute mainContentDim + if (child->style.position_type == CSS_POSITION_RELATIVE) { + nonFlexibleChildrenCount++; + // At this point we know the final size and margin of the element. + nextContentDim = getDimWithMargin(child, mainAxis); + } + } + + // The element we are about to add would make us go to the next line + if (isNodeFlexWrap && + isMainDimDefined && + mainContentDim + nextContentDim > definedMainDim && + // If there's only one element, then it's bigger than the content + // and needs its own line + i != startLine) { + nonFlexibleChildrenCount--; + alreadyComputedNextLayout = 1; + break; + } + + // Disable simple stacking in the main axis for the current line as + // we found a non-trivial child-> The remaining children will be laid out + // in . + if (isSimpleStackMain && + (child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) { + isSimpleStackMain = false; + firstComplexMain = i; + } + + // Disable simple stacking in the cross axis for the current line as + // we found a non-trivial child-> The remaining children will be laid out + // in . + if (isSimpleStackCross && + (child->style.position_type != CSS_POSITION_RELATIVE || + (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) || + (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) { + isSimpleStackCross = false; + firstComplexCross = i; + } + + if (isSimpleStackMain) { + child->layout.position[pos[mainAxis]] += mainDim; + if (isMainDimDefined) { + setTrailingPosition(node, child, mainAxis); + } + + mainDim += getDimWithMargin(child, mainAxis); + crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + } + + if (isSimpleStackCross) { + child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; + if (isCrossDimDefined) { + setTrailingPosition(node, child, crossAxis); + } + } + + alreadyComputedNextLayout = 0; + mainContentDim += nextContentDim; + endLine = i + 1; + } + + // Layout flexible children and allocate empty space + + // In order to position the elements in the main axis, we have two + // controls. The space between the beginning and the first element + // and the space between each two elements. + float leadingMainDim = 0; + float betweenMainDim = 0; + + // The remaining available space that needs to be allocated + float remainingMainDim = 0; + if (isMainDimDefined) { + remainingMainDim = definedMainDim - mainContentDim; + } else { + remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; + } + + // If there are flexible children in the mix, they are going to fill the + // remaining space + if (flexibleChildrenCount != 0) { + float flexibleMainDim = remainingMainDim / totalFlexible; + float baseMainDim; + float boundMainDim; + + // If the flex share of remaining space doesn't meet min/max bounds, + // remove this child from flex calculations. + currentFlexChild = firstFlexChild; + while (currentFlexChild != NULL) { + baseMainDim = flexibleMainDim * currentFlexChild->style.flex + + getPaddingAndBorderAxis(currentFlexChild, mainAxis); + boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); + + if (baseMainDim != boundMainDim) { + remainingMainDim -= boundMainDim; + totalFlexible -= currentFlexChild->style.flex; + } + + currentFlexChild = currentFlexChild->next_flex_child; + } + flexibleMainDim = remainingMainDim / totalFlexible; + + // The non flexible children can overflow the container, in this case + // we should just assume that there is no space available. + if (flexibleMainDim < 0) { + flexibleMainDim = 0; + } + + currentFlexChild = firstFlexChild; + while (currentFlexChild != NULL) { + // At this point we know the final size of the element in the main + // dimension + currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, + flexibleMainDim * currentFlexChild->style.flex + + getPaddingAndBorderAxis(currentFlexChild, mainAxis) + ); + + maxWidth = CSS_UNDEFINED; + if (isLayoutDimDefined(node, resolvedRowAxis)) { + maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - + paddingAndBorderAxisResolvedRow; + } else if (!isMainRowDirection) { + maxWidth = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis) - + paddingAndBorderAxisResolvedRow; + } + maxHeight = CSS_UNDEFINED; + if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + paddingAndBorderAxisColumn; + } else if (isMainRowDirection) { + maxHeight = parentMaxHeight - + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - + paddingAndBorderAxisColumn; + } + + // And we recursively call the layout algorithm for this child + layoutNode(currentFlexChild, maxWidth, maxHeight, direction); + + child = currentFlexChild; + currentFlexChild = currentFlexChild->next_flex_child; + child->next_flex_child = NULL; + } + + // We use justifyContent to figure out how to allocate the remaining + // space available + } else if (justifyContent != CSS_JUSTIFY_FLEX_START) { + if (justifyContent == CSS_JUSTIFY_CENTER) { + leadingMainDim = remainingMainDim / 2; + } else if (justifyContent == CSS_JUSTIFY_FLEX_END) { + leadingMainDim = remainingMainDim; + } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) { + remainingMainDim = fmaxf(remainingMainDim, 0); + if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) { + betweenMainDim = remainingMainDim / + (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + } else { + betweenMainDim = 0; + } + } else if (justifyContent == CSS_JUSTIFY_SPACE_AROUND) { + // Space on the edges is half of the space between elements + betweenMainDim = remainingMainDim / + (flexibleChildrenCount + nonFlexibleChildrenCount); + leadingMainDim = betweenMainDim / 2; + } + } + + // Position elements in the main axis and compute dimensions + + // At this point, all the children have their dimensions set. We need to + // find their position. In order to do that, we accumulate data in + // variables that are also useful to compute the total dimensions of the + // container! + mainDim += leadingMainDim; + + for (i = firstComplexMain; i < endLine; ++i) { + child = node->get_child(node->context, i); + + if (child->style.position_type == CSS_POSITION_ABSOLUTE && + isPosDefined(child, leading[mainAxis])) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + + getLeadingBorder(node, mainAxis) + + getLeadingMargin(child, mainAxis); + } else { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child->layout.position[pos[mainAxis]] += mainDim; + + // Define the trailing position accordingly. + if (isMainDimDefined) { + setTrailingPosition(node, child, mainAxis); + } + + // Now that we placed the element, we need to update the variables + // We only need to do that for relative elements. Absolute elements + // do not take part in that phase. + if (child->style.position_type == CSS_POSITION_RELATIVE) { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + } + } + } + + float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; + if (!isCrossDimDefined) { + containerCrossAxis = fmaxf( + // For the cross dim, we add both sides at the end because the value + // is aggregate via a max function. Intermediate negative values + // can mess this computation otherwise + boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), + paddingAndBorderAxisCross + ); + } + + // Position elements in the cross axis + for (i = firstComplexCross; i < endLine; ++i) { + child = node->get_child(node->context, i); + + if (child->style.position_type == CSS_POSITION_ABSOLUTE && + isPosDefined(child, leading[crossAxis])) { + // In case the child is absolutely positionned and has a + // top/left/bottom/right being set, we override all the previously + // computed positions to set it correctly. + child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + + getLeadingBorder(node, crossAxis) + + getLeadingMargin(child, crossAxis); + + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis + if (child->style.position_type == CSS_POSITION_RELATIVE) { + /*eslint-disable */ + // This variable is intentionally re-defined as the code is transpiled to a block scope language + css_align_t alignItem = getAlignItem(node, child); + /*eslint-enable */ + if (alignItem == CSS_ALIGN_STRETCH) { + // You can only stretch if the dimension has not already been defined + // previously. + if (!isStyleDimDefined(child, crossAxis)) { + float dimCrossAxis = child->layout.dimensions[dim[crossAxis]]; + child->layout.dimensions[dim[crossAxis]] = fmaxf( + boundAxis(child, crossAxis, containerCrossAxis - + paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, crossAxis) + ); + + // If the size has changed, and this child has children we need to re-layout this child + if (dimCrossAxis != child->layout.dimensions[dim[crossAxis]] && child->children_count > 0) { + // Reset child margins before re-layout as they are added back in layoutNode and would be doubled + child->layout.position[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) + + getRelativePosition(child, mainAxis); + child->layout.position[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) + + getRelativePosition(child, mainAxis); + child->layout.position[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) + + getRelativePosition(child, crossAxis); + child->layout.position[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) + + getRelativePosition(child, crossAxis); + + layoutNode(child, maxWidth, maxHeight, direction); + } + } + } else if (alignItem != CSS_ALIGN_FLEX_START) { + // The remaining space between the parent dimensions+padding and child + // dimensions+margin. + float remainingCrossDim = containerCrossAxis - + paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis); + + if (alignItem == CSS_ALIGN_CENTER) { + leadingCrossDim += remainingCrossDim / 2; + } else { // CSS_ALIGN_FLEX_END + leadingCrossDim += remainingCrossDim; + } + } + } + + // And we apply the position + child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; + + // Define the trailing position accordingly. + if (isCrossDimDefined) { + setTrailingPosition(node, child, crossAxis); + } + } + } + + linesCrossDim += crossDim; + linesMainDim = fmaxf(linesMainDim, mainDim); + linesCount += 1; + startLine = endLine; + } + + // + // + // Note(prenaux): More than one line, we need to layout the crossAxis + // according to alignContent. + // + // Note that we could probably remove and handle the one line case + // here too, but for the moment this is safer since it won't interfere with + // previously working code. + // + // See specs: + // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm + // section 9.4 + // + if (linesCount > 1 && isCrossDimDefined) { + float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] - + paddingAndBorderAxisCross; + float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + + float crossDimLead = 0; + float currentLead = leadingPaddingAndBorderCross; + + css_align_t alignContent = node->style.align_content; + if (alignContent == CSS_ALIGN_FLEX_END) { + currentLead += remainingAlignContentDim; + } else if (alignContent == CSS_ALIGN_CENTER) { + currentLead += remainingAlignContentDim / 2; + } else if (alignContent == CSS_ALIGN_STRETCH) { + if (nodeCrossAxisInnerSize > linesCrossDim) { + crossDimLead = (remainingAlignContentDim / linesCount); + } + } + + int endIndex = 0; + for (i = 0; i < linesCount; ++i) { + int startIndex = endIndex; + + // compute the line's height and find the endIndex + float lineHeight = 0; + for (ii = startIndex; ii < childCount; ++ii) { + child = node->get_child(node->context, ii); + if (child->style.position_type != CSS_POSITION_RELATIVE) { + continue; + } + if (child->line_index != i) { + break; + } + if (isLayoutDimDefined(child, crossAxis)) { + lineHeight = fmaxf( + lineHeight, + child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis) + ); + } + } + endIndex = ii; + lineHeight += crossDimLead; + + for (ii = startIndex; ii < endIndex; ++ii) { + child = node->get_child(node->context, ii); + if (child->style.position_type != CSS_POSITION_RELATIVE) { + continue; + } + + css_align_t alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { + child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]]; + } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { + float childHeight = child->layout.dimensions[dim[crossAxis]]; + child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with undefined + // (auto) crossAxis dimension. + } + } + + currentLead += lineHeight; + } + } + + bool needsMainTrailingPos = false; + bool needsCrossTrailingPos = false; + + // If the user didn't specify a width or height, and it has not been set + // by the container, then we set it via the children. + if (!isMainDimDefined) { + node->layout.dimensions[dim[mainAxis]] = fmaxf( + // We're missing the last padding at this point to get the final + // dimension + boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), + // We can never assign a width smaller than the padding and borders + paddingAndBorderAxisMain + ); + + if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || + mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { + needsMainTrailingPos = true; + } + } + + if (!isCrossDimDefined) { + node->layout.dimensions[dim[crossAxis]] = fmaxf( + // For the cross dim, we add both sides at the end because the value + // is aggregate via a max function. Intermediate negative values + // can mess this computation otherwise + boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), + paddingAndBorderAxisCross + ); + + if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || + crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { + needsCrossTrailingPos = true; + } + } + + // Set trailing position if necessary + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node->get_child(node->context, i); + + if (needsMainTrailingPos) { + setTrailingPosition(node, child, mainAxis); + } + + if (needsCrossTrailingPos) { + setTrailingPosition(node, child, crossAxis); + } + } + } + + // Calculate dimensions for absolutely positioned elements + currentAbsoluteChild = firstAbsoluteChild; + while (currentAbsoluteChild != NULL) { + // Pre-fill dimensions when using absolute position and both offsets for + // the axis are defined (either both left and right or top and bottom). + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + + if (isLayoutDimDefined(node, axis) && + !isStyleDimDefined(currentAbsoluteChild, axis) && + isPosDefined(currentAbsoluteChild, leading[axis]) && + isPosDefined(currentAbsoluteChild, trailing[axis])) { + currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] - + getBorderAxis(node, axis) - + getMarginAxis(currentAbsoluteChild, axis) - + getPosition(currentAbsoluteChild, leading[axis]) - + getPosition(currentAbsoluteChild, trailing[axis]) + ), + // You never want to go smaller than padding + getPaddingAndBorderAxis(currentAbsoluteChild, axis) + ); + } + + if (isPosDefined(currentAbsoluteChild, trailing[axis]) && + !isPosDefined(currentAbsoluteChild, leading[axis])) { + currentAbsoluteChild->layout.position[leading[axis]] = + node->layout.dimensions[dim[axis]] - + currentAbsoluteChild->layout.dimensions[dim[axis]] - + getPosition(currentAbsoluteChild, trailing[axis]); + } + } + + child = currentAbsoluteChild; + currentAbsoluteChild = currentAbsoluteChild->next_absolute_child; + child->next_absolute_child = NULL; + } + /** END_GENERATED **/ +} + +void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) { + css_layout_t *layout = &node->layout; + css_direction_t direction = node->style.direction; + layout->should_update = true; + + bool skipLayout = + !node->is_dirty(node->context) && + eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) && + eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) && + eq(layout->last_parent_max_width, parentMaxWidth) && + eq(layout->last_parent_max_height, parentMaxHeight) && + eq(layout->last_direction, direction); + + if (skipLayout) { + layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH]; + layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT]; + layout->position[CSS_TOP] = layout->last_position[CSS_TOP]; + layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT]; + } else { + layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; + layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; + layout->last_parent_max_width = parentMaxWidth; + layout->last_parent_max_height = parentMaxHeight; + layout->last_direction = direction; + + int i, childCount; + for (i = 0, childCount = node->children_count; i < childCount; i++) { + resetNodeLayout(node->get_child(node->context, i)); + } + + layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection); + + layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; + layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; + layout->last_position[CSS_TOP] = layout->position[CSS_TOP]; + layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT]; + } + +#if defined(FLEXBOX_LAYOUT_NODE_DEBUG) + printf("Input: "); + print_css_node(node, (css_print_options_t)(CSS_PRINT_STYLE | CSS_PRINT_CHILDREN)); + printf("Output: "); + print_css_node(node, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN)); + printf("\n"); +#endif // defined(FLEXBOX_LAYOUT_NODE_DEBUG) +} + +void resetNodeLayout(css_node_t *node) { + node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->layout.position[CSS_LEFT] = 0; + node->layout.position[CSS_TOP] = 0; +} diff --git a/dali-toolkit/internal/controls/flex-container/layout.h b/dali-toolkit/internal/controls/flex-container/layout.h new file mode 100644 index 0000000..d6ad495 --- /dev/null +++ b/dali-toolkit/internal/controls/flex-container/layout.h @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef __LAYOUT_H +#define __LAYOUT_H + +#include +#ifndef __cplusplus +#include +#endif + +// Not defined in MSVC++ +#ifndef NAN +static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; +#define NAN (*(const float *)__nan) +#endif + +#define CSS_UNDEFINED NAN + +typedef enum { + CSS_DIRECTION_INHERIT = 0, + CSS_DIRECTION_LTR, + CSS_DIRECTION_RTL +} css_direction_t; + +typedef enum { + CSS_FLEX_DIRECTION_COLUMN = 0, + CSS_FLEX_DIRECTION_COLUMN_REVERSE, + CSS_FLEX_DIRECTION_ROW, + CSS_FLEX_DIRECTION_ROW_REVERSE +} css_flex_direction_t; + +typedef enum { + CSS_JUSTIFY_FLEX_START = 0, + CSS_JUSTIFY_CENTER, + CSS_JUSTIFY_FLEX_END, + CSS_JUSTIFY_SPACE_BETWEEN, + CSS_JUSTIFY_SPACE_AROUND +} css_justify_t; + +// Note: auto is only a valid value for alignSelf. It is NOT a valid value for +// alignItems. +typedef enum { + CSS_ALIGN_AUTO = 0, + CSS_ALIGN_FLEX_START, + CSS_ALIGN_CENTER, + CSS_ALIGN_FLEX_END, + CSS_ALIGN_STRETCH +} css_align_t; + +typedef enum { + CSS_POSITION_RELATIVE = 0, + CSS_POSITION_ABSOLUTE +} css_position_type_t; + +typedef enum { + CSS_NOWRAP = 0, + CSS_WRAP +} css_wrap_type_t; + +// Note: left and top are shared between position[2] and position[4], so +// they have to be before right and bottom. +typedef enum { + CSS_LEFT = 0, + CSS_TOP, + CSS_RIGHT, + CSS_BOTTOM, + CSS_START, + CSS_END, + CSS_POSITION_COUNT +} css_position_t; + +typedef enum { + CSS_MEASURE_MODE_UNDEFINED = 0, + CSS_MEASURE_MODE_EXACTLY, + CSS_MEASURE_MODE_AT_MOST +} css_measure_mode_t; + +typedef enum { + CSS_WIDTH = 0, + CSS_HEIGHT +} css_dimension_t; + +typedef struct { + float position[4]; + float dimensions[2]; + css_direction_t direction; + + // Instead of recomputing the entire layout every single time, we + // cache some information to break early when nothing changed + bool should_update; + float last_requested_dimensions[2]; + float last_parent_max_width; + float last_parent_max_height; + float last_dimensions[2]; + float last_position[2]; + css_direction_t last_direction; +} css_layout_t; + +typedef struct { + float dimensions[2]; +} css_dim_t; + +typedef struct { + css_direction_t direction; + css_flex_direction_t flex_direction; + css_justify_t justify_content; + css_align_t align_content; + css_align_t align_items; + css_align_t align_self; + css_position_type_t position_type; + css_wrap_type_t flex_wrap; + float flex; + float margin[6]; + float position[4]; + /** + * You should skip all the rules that contain negative values for the + * following attributes. For example: + * {padding: 10, paddingLeft: -5} + * should output: + * {left: 10 ...} + * the following two are incorrect: + * {left: -5 ...} + * {left: 0 ...} + */ + float padding[6]; + float border[6]; + float dimensions[2]; + float minDimensions[2]; + float maxDimensions[2]; +} css_style_t; + +typedef struct css_node css_node_t; +struct css_node { + css_style_t style; + css_layout_t layout; + int children_count; + int line_index; + + css_node_t *next_absolute_child; + css_node_t *next_flex_child; + + css_dim_t (*measure)(void *context, float width, css_measure_mode_t widthMode, float height, css_measure_mode_t heightMode); + void (*print)(void *context); + struct css_node* (*get_child)(void *context, int i); + bool (*is_dirty)(void *context); + void *context; +}; + +// Lifecycle of nodes and children +css_node_t *new_css_node(void); +void init_css_node(css_node_t *node); +void free_css_node(css_node_t *node); + +// Print utilities +typedef enum { + CSS_PRINT_LAYOUT = 1, + CSS_PRINT_STYLE = 2, + CSS_PRINT_CHILDREN = 4, +} css_print_options_t; +void print_css_node(css_node_t *node, css_print_options_t options); + +bool isUndefined(float value); + +// Function that computes the layout! +void layoutNode(css_node_t *node, float maxWidth, float maxHeight, css_direction_t parentDirection); + +// Reset the calculated layout values for a given node. You should call this before `layoutNode`. +void resetNodeLayout(css_node_t *node); + +#endif // __LAYOUT_H diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 293531d..ab7cac6 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -38,6 +38,8 @@ toolkit_src_files = \ $(toolkit_src_dir)/controls/buttons/push-button-impl.cpp \ $(toolkit_src_dir)/controls/buttons/radio-button-impl.cpp \ $(toolkit_src_dir)/controls/effects-view/effects-view-impl.cpp \ + $(toolkit_src_dir)/controls/flex-container/layout.c \ + $(toolkit_src_dir)/controls/flex-container/flex-container-impl.cpp \ $(toolkit_src_dir)/controls/gaussian-blur-view/gaussian-blur-view-impl.cpp \ $(toolkit_src_dir)/controls/image-view/image-view-impl.cpp \ $(toolkit_src_dir)/controls/magnifier/magnifier-impl.cpp \ diff --git a/plugins/dali-script-v8/src/object/property-value-wrapper.cpp b/plugins/dali-script-v8/src/object/property-value-wrapper.cpp index a7e2998..8469d7f 100644 --- a/plugins/dali-script-v8/src/object/property-value-wrapper.cpp +++ b/plugins/dali-script-v8/src/object/property-value-wrapper.cpp @@ -675,6 +675,10 @@ Dali::Property::Value PropertyValueWrapper::ExtractPropertyValue( v8::Isolate* i { daliPropertyValue = Dali::Property::Value( v8Value->Int32Value() ) ;//static_cast( V8Utils::GetNumberValue( isolate, v8Value) )); } + else if( V8Utils::IsStringPrimitiveOrObject( v8Value) ) // Take string as value for properties that internally convert the string to an enum + { + daliPropertyValue = Dali::Property::Value( V8Utils::GetStringValue( isolate, v8Value) ); + } break; } case Dali::Property::STRING: