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 <david.steele@samsung.com>
# 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
--- /dev/null
+/*
+ * 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 <dali/public-api/actors/actor.h>
+#include <dali-toolkit/devel-api/controls/control-devel.h>
+
+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; i<GetChildCount(); ++i )
+ {
+ auto childLayout = GetChildAt( i );
+ if( childLayout )
+ {
+ MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
+ accumulatedWidth += childLayout->GetMeasuredWidth();
+ 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
--- /dev/null
+#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 <dali/public-api/common/intrusive-ptr.h>
+#include <dali-toolkit/devel-api/layouting/layout-group-impl.h>
+
+// 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<Internal::CustomLayout&>( 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<const Internal::CustomLayout&>( object );
+}
+
+} // namespace Test
+
+#endif // TEST_INTERNAL_CUSTOM_LAYOUT_H
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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 <dali/public-api/object/base-handle.h>
+#include <dali-toolkit/devel-api/layouting/layout-group.h>
+
+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
#include <dali-toolkit/devel-api/layouting/absolute-layout.h>
#include <dali-toolkit/devel-api/layouting/linear-layout.h>
+#include <../custom-layout.h>
+
#include <layout-utils.h>
using namespace Dali;
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<Vector3>( Actor::Property::POSITION ), Vector3( 0.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[1].GetProperty<Vector3>( Actor::Property::POSITION ), Vector3( 40.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[2].GetProperty<Vector3>( Actor::Property::POSITION ), Vector3( 100.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[3].GetProperty<Vector3>( Actor::Property::POSITION ), Vector3( 180.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+
+ DALI_TEST_EQUALS( controls[0].GetProperty<Vector3>( Actor::Property::SIZE ), Vector3( 40.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[1].GetProperty<Vector3>( Actor::Property::SIZE ), Vector3( 60.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[2].GetProperty<Vector3>( Actor::Property::SIZE ), Vector3( 80.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[3].GetProperty<Vector3>( 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<Vector3>( Actor::Property::POSITION ), Vector3( 0.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[1].GetProperty<Vector3>( Actor::Property::POSITION ), Vector3( 80.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[3].GetProperty<Vector3>( Actor::Property::POSITION ), Vector3( 140.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[0].GetProperty<Vector3>( Actor::Property::POSITION ), Vector3( 240.0f, 380.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+
+ DALI_TEST_EQUALS( controls[0].GetProperty<Vector3>( Actor::Property::SIZE ), Vector3( 40.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[1].GetProperty<Vector3>( Actor::Property::SIZE ), Vector3( 60.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[2].GetProperty<Vector3>( Actor::Property::SIZE ), Vector3( 80.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+ DALI_TEST_EQUALS( controls[3].GetProperty<Vector3>( Actor::Property::SIZE ), Vector3( 100.0f, 40.0f, 0.0f ), 0.0001f, TEST_LOCATION );
+
+ END_TEST;
+}
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() )
}
}
+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");
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 );