From: David Steele Date: Fri, 15 Jun 2018 15:38:10 +0000 (+0100) Subject: On Actor child order change signal, forces relayout of container. X-Git-Tag: dali_1.3.30~6 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=428cb5932b23d06d1871c0935d2462cc362828ea;hp=6e19027ba207adc974995d39b17197915f60d92e On Actor child order change signal, forces relayout of container. If the child order changes, then relayout occurs. Note that the basic layouts work in LayoutID order, not actor sibling order. A specialized layout has to be written to order the children by sibling order. Added custom layout to test harness. Change-Id: I7c8f393ff804192eed74a23fd06b0b5763de1dc1 Signed-off-by: David Steele --- diff --git a/automated-tests/src/dali-toolkit/CMakeLists.txt b/automated-tests/src/dali-toolkit/CMakeLists.txt index c3bbf75..5852cfc 100755 --- a/automated-tests/src/dali-toolkit/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit/CMakeLists.txt @@ -72,6 +72,8 @@ SET(TC_SOURCES # Append list of test harness files (Won't get parsed for test cases) LIST(APPEND TC_SOURCES + custom-layout.cpp + custom-layout-impl.cpp dali-toolkit-test-utils/toolkit-adaptor.cpp dali-toolkit-test-utils/toolkit-accessibility-adaptor.cpp dali-toolkit-test-utils/toolkit-application.cpp diff --git a/automated-tests/src/dali-toolkit/custom-layout-impl.cpp b/automated-tests/src/dali-toolkit/custom-layout-impl.cpp new file mode 100644 index 0000000..97ded95 --- /dev/null +++ b/automated-tests/src/dali-toolkit/custom-layout-impl.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 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 "custom-layout-impl.h" + +// EXTERNAL INCLUDES +#include +#include + +namespace Test +{ + +namespace Internal +{ + +using Dali::Actor; +using Dali::Toolkit::MeasuredSize; + +CustomLayoutPtr CustomLayout::New() +{ + CustomLayoutPtr layout( new CustomLayout() ); + return layout; +} + +void CustomLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec ) +{ + auto accumulatedWidth = 0; + auto maxHeight = 0; + + // In this layout we will: + // Measuring the layout with the children in a horizontal configuration, one after another + // Set the required width to be the accumulated width of our children + // Set the required height to be the maximum height of any of our children + + for( unsigned int i=0; iGetMeasuredWidth(); + maxHeight = std::max( childLayout->GetMeasuredHeight().mValue, maxHeight ); + } + } + + // Finally, call this method to set the dimensions we would like + SetMeasuredDimensions( MeasuredSize( accumulatedWidth ), MeasuredSize( maxHeight ) ); +} + +void CustomLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom ) +{ + LayoutLength childTop( 0 ); + LayoutLength childLeft( 0 ); + + // We want to vertically align the children to the middle + auto height = bottom - top; + auto middle = height / 2; + + auto owner = GetOwner(); + auto actor = Actor::DownCast(owner); + + // Horizontally align the children to the left + int count = actor.GetChildCount(); + int currentLeft = 0; + + for( int i = 0; i < count; i++) + { + Dali::Toolkit::Control child = Dali::Toolkit::Control::DownCast( actor.GetChildAt( i ) ); + if( !child ) + { + continue; + } + + Dali::Toolkit::LayoutItem childLayout = Dali::Toolkit::DevelControl::GetLayout( child ); + if( childLayout ) + { + Dali::Toolkit::Internal::LayoutItem& childLayoutImpl = GetImplementation( childLayout ); + + auto childWidth = childLayoutImpl.GetMeasuredWidth(); + auto childHeight = childLayoutImpl.GetMeasuredHeight(); + + childTop = middle - (childHeight / 2); + childLayoutImpl.Layout( currentLeft, childTop, currentLeft + childWidth, childTop + childHeight ); + currentLeft += childWidth; + } + } +} + +} // namespace Internal + +} // namespace Test diff --git a/automated-tests/src/dali-toolkit/custom-layout-impl.h b/automated-tests/src/dali-toolkit/custom-layout-impl.h new file mode 100644 index 0000000..b2dcb99 --- /dev/null +++ b/automated-tests/src/dali-toolkit/custom-layout-impl.h @@ -0,0 +1,106 @@ +#ifndef TEST_INTERNAL_CUSTOM_LAYOUT_H +#define TEST_INTERNAL_CUSTOM_LAYOUT_H + +/* + * Copyright (c) 2018 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 +#include + +// INTERNAL INCLUDES +#include "custom-layout.h" + +namespace Test +{ + +namespace Internal +{ + +using Dali::Toolkit::MeasureSpec; +using Dali::Toolkit::LayoutLength; + +class CustomLayout; +using CustomLayoutPtr = Dali::IntrusivePtr< CustomLayout >; + +/** + * @brief The implementation of our custom layout. + * + * Here we will override the methods that we require to mimic a very simple horizontal layout. + */ +class CustomLayout final : public Dali::Toolkit::Internal::LayoutGroup +{ +public: + + /** + * @brief Create a new CustomLayout object. + * @return An intrusive pointer to the created CustomLayout object + */ + static CustomLayoutPtr New(); + + // Movable but not copyable + CustomLayout( const CustomLayout& other ) = delete; + CustomLayout& operator=( const CustomLayout& ) = delete; + CustomLayout( CustomLayout&& other ) = default; + CustomLayout& operator=( CustomLayout&& other ) = default; + +private: + + /** + * @brief Default Constructor + */ + CustomLayout() = default; + + /** + * Virtual Destructor + */ + virtual ~CustomLayout() = default; + + /** + * @copydoc LayoutItem::OnMeasure + * + * Overriding this method so that we can calculate the size we require using our children's sizes + */ + virtual void OnMeasure( MeasureSpec widthMeasureSpec, Dali::Toolkit::MeasureSpec heightMeasureSpec ) override; + + /** + * @copydoc LayoutItem::OnLayout + * + * Overriding this method so that we can layout our children as required. + */ + virtual void OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom ) override; + +}; + +} // namespace Internal + +inline Internal::CustomLayout& GetImplementation( Test::CustomLayout& handle ) +{ + DALI_ASSERT_ALWAYS( handle && "CustomLayout handle is empty" ); + Dali::BaseObject& object = handle.GetBaseObject(); + return static_cast( object ); +} + +inline const Internal::CustomLayout& GetImplementation( const Test::CustomLayout& handle ) +{ + DALI_ASSERT_ALWAYS( handle && "CustomLayout handle is empty" ); + const Dali::BaseObject& object = handle.GetBaseObject(); + return static_cast( object ); +} + +} // namespace Test + +#endif // TEST_INTERNAL_CUSTOM_LAYOUT_H diff --git a/automated-tests/src/dali-toolkit/custom-layout.cpp b/automated-tests/src/dali-toolkit/custom-layout.cpp new file mode 100644 index 0000000..4a85014 --- /dev/null +++ b/automated-tests/src/dali-toolkit/custom-layout.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 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 "custom-layout.h" + +// INTERNAL HEADERS +#include "custom-layout-impl.h" + +namespace Test +{ + +CustomLayout CustomLayout::New() +{ + Internal::CustomLayoutPtr internal = Internal::CustomLayout::New(); + return CustomLayout( internal.Get() ); +} + +CustomLayout CustomLayout::DownCast( BaseHandle handle ) +{ + return CustomLayout( dynamic_cast< Test::Internal::CustomLayout* >( handle.GetObjectPtr() ) ); +} + +CustomLayout::CustomLayout( Internal::CustomLayout* object ) +: Dali::Toolkit::LayoutGroup( object ) +{ +} + +void CustomLayout::RequestLayout() +{ + GetImplementation( *this ).RequestLayout(); +} +} // namespace Test diff --git a/automated-tests/src/dali-toolkit/custom-layout.h b/automated-tests/src/dali-toolkit/custom-layout.h new file mode 100644 index 0000000..fa977e7 --- /dev/null +++ b/automated-tests/src/dali-toolkit/custom-layout.h @@ -0,0 +1,109 @@ +#ifndef TEST_CUSTOM_LAYOUT_H +#define TEST_CUSTOM_LAYOUT_H + +/* + * Copyright (c) 2018 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 +#include + +namespace Test +{ + +namespace Internal +{ +class CustomLayout; +} + +/** + * @brief This is the handle class to a very simple Custom Layout. + * + * This class lays out its children horizontally in a very simple manner. + */ +class CustomLayout : public Dali::Toolkit::LayoutGroup +{ +public: + + /** + * @brief Creates an uninitialized CustomLayout handle. + * + * Initialize it using CustomLayout::New(). + * Calling member functions with an uninitialized handle is not allowed. + */ + CustomLayout() = default; + + /** + * @brief Creates a CustomLayout object. + */ + static CustomLayout New(); + + + /** + * @brief Default destructor. + * + * This is non-virtual, since derived Handle types must not contain data or virtual methods + */ + ~CustomLayout() = default; + + /** + * @brief Copy constructor + */ + CustomLayout( const CustomLayout& ) = default; + + /** + * @brief Assigment operator + */ + CustomLayout& operator=( const CustomLayout& ) = default; + + /** + * @brief Move constructor + */ + CustomLayout( CustomLayout&& ) = default; + + /** + * @brief Movable assignment operator + */ + CustomLayout& operator=( CustomLayout&& ) = default; + + /** + * @brief Downcasts a handle to a CustomLayout handle. + * + * If handle points to a CustomLayout, the downcast produces a valid handle. + * If not, the returned handle is left uninitialized. + + * @param[in] handle to an object + * @return Handle to a CustomLayout or an uninitialized handle + */ + static CustomLayout DownCast( BaseHandle handle ); + + void RequestLayout(); + +public: // Not intended for application developers + + /// @cond internal + /** + * @brief This constructor is used by CustomLayout::New() methods. + * + * @param[in] actor A pointer to a newly allocated Dali resource + */ + explicit CustomLayout( Internal::CustomLayout* body ); + /// @endcond +}; + +} // namespace Test + +#endif // TEST_CUSTOM_LAYOUT_H diff --git a/automated-tests/src/dali-toolkit/utc-Dali-Layouting.cpp b/automated-tests/src/dali-toolkit/utc-Dali-Layouting.cpp index 4246d16..75b42b8 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-Layouting.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-Layouting.cpp @@ -24,6 +24,8 @@ #include #include +#include <../custom-layout.h> + #include using namespace Dali; @@ -1126,3 +1128,67 @@ int UtcDaliLayouting_VboxLayout03(void) END_TEST; } + + + +int UtcDaliLayouting_RelayoutOnChildOrderChanged(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliLayouting_RelayoutOnChildOrderChanged"); + tet_infoline(" Test that if the sibling order changes, the container is re-laid out automatically"); + + Stage stage = Stage::GetCurrent(); + + auto hbox = Control::New(); + auto hboxLayout = Test::CustomLayout::New(); + DevelControl::SetLayout( hbox, hboxLayout ); + hbox.SetName( "HBox"); + + std::vector< Control > controls; + controls.push_back( CreateLeafControl( 40, 40 ) ); + controls.push_back( CreateLeafControl( 60, 40 ) ); + controls.push_back( CreateLeafControl( 80, 40 ) ); + controls.push_back( CreateLeafControl( 100, 40 ) ); + + for( auto&& iter : controls ) + { + hbox.Add( iter ); + } + hbox.SetParentOrigin( ParentOrigin::CENTER ); + hbox.SetAnchorPoint( AnchorPoint::CENTER ); + stage.Add( hbox ); + + // Ensure layouting happens + application.SendNotification(); + application.Render(); + + // hbox centers elements vertically, it fills test harness stage, which is 480x800. + // hbox left justifies elements + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::POSITION ), Vector3( 40.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::POSITION ), Vector3( 100.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::POSITION ), Vector3( 180.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::SIZE ), Vector3( 40.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::SIZE ), Vector3( 60.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::SIZE ), Vector3( 80.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + controls[0].RaiseToTop(); // 0->3; 1, 2, 3, 0 + controls[2].Lower(); // 2->1; 2, 1, 3, 0 + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::POSITION ), Vector3( 80.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::POSITION ), Vector3( 140.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::POSITION ), Vector3( 240.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::SIZE ), Vector3( 40.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::SIZE ), Vector3( 60.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::SIZE ), Vector3( 80.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + END_TEST; +} diff --git a/dali-toolkit/devel-api/layouting/layout-group-impl.cpp b/dali-toolkit/devel-api/layouting/layout-group-impl.cpp index 428ac89..18dd1d6 100644 --- a/dali-toolkit/devel-api/layouting/layout-group-impl.cpp +++ b/dali-toolkit/devel-api/layouting/layout-group-impl.cpp @@ -406,6 +406,7 @@ void LayoutGroup::OnInitialize() DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner ); DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner ); + DevelActor::ChildOrderChangedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildOrderChanged ); DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet ); if( control.GetParent() ) @@ -500,6 +501,16 @@ void LayoutGroup::ChildRemovedFromOwner( Actor child ) } } +void LayoutGroup::ChildOrderChanged() +{ + RequestLayout(); + // Force Children to be relaid out: + for( auto&& child : mImpl->mChildren ) + { + child.child->SetLayoutRequested(); + } +} + void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value ) { DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n"); diff --git a/dali-toolkit/devel-api/layouting/layout-group-impl.h b/dali-toolkit/devel-api/layouting/layout-group-impl.h index 27363aa..90c40fa 100644 --- a/dali-toolkit/devel-api/layouting/layout-group-impl.h +++ b/dali-toolkit/devel-api/layouting/layout-group-impl.h @@ -255,6 +255,11 @@ private: void ChildRemovedFromOwner( Actor child ); /** + * Callback when child order is changed + */ + void ChildOrderChanged(); + + /** * Callback when an owner property is set. Triggers a relayout if it's a child property */ void OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value );