From 428cb5932b23d06d1871c0935d2462cc362828ea Mon Sep 17 00:00:00 2001 From: David Steele Date: Fri, 15 Jun 2018 16:38:10 +0100 Subject: [PATCH 1/1] 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 --- automated-tests/src/dali-toolkit/CMakeLists.txt | 2 + .../src/dali-toolkit/custom-layout-impl.cpp | 105 ++++++++++++++++++++ .../src/dali-toolkit/custom-layout-impl.h | 106 ++++++++++++++++++++ automated-tests/src/dali-toolkit/custom-layout.cpp | 46 +++++++++ automated-tests/src/dali-toolkit/custom-layout.h | 109 +++++++++++++++++++++ .../src/dali-toolkit/utc-Dali-Layouting.cpp | 66 +++++++++++++ .../devel-api/layouting/layout-group-impl.cpp | 11 +++ .../devel-api/layouting/layout-group-impl.h | 5 + 8 files changed, 450 insertions(+) create mode 100644 automated-tests/src/dali-toolkit/custom-layout-impl.cpp create mode 100644 automated-tests/src/dali-toolkit/custom-layout-impl.h create mode 100644 automated-tests/src/dali-toolkit/custom-layout.cpp create mode 100644 automated-tests/src/dali-toolkit/custom-layout.h 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 ); -- 2.7.4