--- /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>
+
+namespace Demo
+{
+
+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();
+ 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;
+
+ // Horizontally align the children to the middle of the space they are given too
+ auto width = right - left;
+ int count = GetChildCount();
+ auto childIncrement = width / count;
+ auto center = childIncrement / 2;
+
+ // Check layout direction
+ auto owner = GetOwner();
+ auto actor = Actor::DownCast(owner);
+ const bool isLayoutRtl = actor ? actor.GetProperty< bool >( Actor::Property::LAYOUT_DIRECTION ) : false;
+
+ for( int i = 0; i < count; i++)
+ {
+ auto itemIndex = isLayoutRtl ? count - 1 - i : i; // If RTL, then layout the last item first
+
+ Dali::Toolkit::Internal::LayoutItemPtr childLayout = GetChildAt( itemIndex );
+ if( childLayout != nullptr )
+ {
+ auto childWidth = childLayout->GetMeasuredWidth();
+ auto childHeight = childLayout->GetMeasuredHeight();
+
+ childTop = middle - (childHeight / 2);
+
+ auto left = childLeft + center - childWidth / 2;
+
+ childLayout->Layout( left, childTop, left + childWidth, childTop + childHeight );
+ childLeft += childIncrement;
+ }
+ }
+}
+
+} // namespace Internal
+
+} // namespace Demo
--- /dev/null
+#ifndef DEMO_INTERNAL_CUSTOM_LAYOUT_H
+#define DEMO_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 Demo
+{
+
+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( Demo::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 Demo::CustomLayout& handle )
+{
+ DALI_ASSERT_ALWAYS( handle && "CustomLayout handle is empty" );
+ const Dali::BaseObject& object = handle.GetBaseObject();
+ return static_cast<const Internal::CustomLayout&>( object );
+}
+
+} // namespace Demo
+
+#endif // DEMO_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 Demo
+{
+
+CustomLayout CustomLayout::New()
+{
+ Internal::CustomLayoutPtr internal = Internal::CustomLayout::New();
+ return CustomLayout( internal.Get() );
+}
+
+CustomLayout CustomLayout::DownCast( BaseHandle handle )
+{
+ return CustomLayout( dynamic_cast< Demo::Internal::CustomLayout* >( handle.GetObjectPtr() ) );
+}
+
+CustomLayout::CustomLayout( Internal::CustomLayout* object )
+: Dali::Toolkit::LayoutGroup( object )
+{
+}
+
+} // namespace Demo
--- /dev/null
+#ifndef DEMO_CUSTOM_LAYOUT_H
+#define DEMO_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 Demo
+{
+
+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 );
+
+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 Demo
+
+#endif // DEMO_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.
+ *
+ */
+
+#include <dali-toolkit/dali-toolkit.h>
+
+#include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/devel-api/layouting/hbox-layout.h>
+#include <dali-toolkit/devel-api/layouting/layout-item-impl.h>
+
+#include "custom-layout.h"
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+namespace
+{
+
+/// Child image filenames
+const char* IMAGE_PATH[] = {
+ DEMO_IMAGE_DIR "application-icon-101.png",
+ DEMO_IMAGE_DIR "application-icon-102.png",
+ DEMO_IMAGE_DIR "application-icon-103.png",
+ DEMO_IMAGE_DIR "application-icon-104.png",
+};
+const unsigned int NUMBER_OF_IMAGES = sizeof( IMAGE_PATH ) / sizeof( char* );
+
+/**
+ * @brief Helper function to create ImageViews with given filename and size.
+ * @param[in] filename The filename of the image to use
+ * @param[in] size The size that the image should be loaded at
+ * @return The created ImageView
+ */
+ImageView CreateChildImageView( const char* filename, Size size )
+{
+ ImageView imageView = ImageView::New();
+ Property::Map imagePropertyMap;
+ imagePropertyMap[ Toolkit::Visual::Property::TYPE ] = Toolkit::Visual::IMAGE;
+ imagePropertyMap[ Toolkit::ImageVisual::Property::URL ] = filename;
+ imagePropertyMap[ ImageVisual::Property::DESIRED_WIDTH ] = size.width;
+ imagePropertyMap[ ImageVisual::Property::DESIRED_HEIGHT ] = size.height;
+ imageView.SetProperty(Toolkit::ImageView::Property::IMAGE , imagePropertyMap );
+ imageView.SetName("ImageView");
+ imageView.SetAnchorPoint( AnchorPoint::TOP_LEFT );
+ imageView.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
+ return imageView;
+}
+
+} // unnamed namespace
+
+/**
+ * @brief Demonstrates how to create a very simple layout and apply that to any Control.
+ */
+class SimpleLayoutExample : public ConnectionTracker
+{
+public:
+
+ /**
+ * @brief Constructor.
+ * @param[in] application A reference to the Application class.
+ */
+ SimpleLayoutExample( Application& application )
+ : mApplication( application )
+ {
+ // Connect to the Application's Init signal
+ mApplication.InitSignal().Connect( this, &SimpleLayoutExample::Create );
+ }
+
+private:
+
+ /**
+ * @brief Called to initialise the application content
+ * @param[in] application A reference to the Application class.
+ */
+ void Create( Application& application )
+ {
+ // Get a handle to the stage, change the background color and connect to the Touch & Key signals
+ Stage stage = Stage::GetCurrent();
+ stage.SetBackgroundColor( Color::WHITE );
+ stage.GetRootLayer().TouchSignal().Connect( this, &SimpleLayoutExample::OnTouch );
+ stage.KeyEventSignal().Connect( this, &SimpleLayoutExample::OnKeyEvent );
+ stage.KeepRendering(0.5f); // TODO: Should remove after bugfix, but currently required to ensure renders are done after resources are loaded
+
+
+ // Create a new control
+ Control control = Control::New();
+ control.SetParentOrigin( ParentOrigin::CENTER );
+ control.SetAnchorPoint( AnchorPoint::CENTER );
+ stage.Add( control);
+
+ // Set our Custom Layout on the control
+ auto layout = Demo::CustomLayout::New();
+ DevelControl::SetLayout( control, layout );
+
+ // Add child image-views to the created control
+ for( auto i = 0u; i < NUMBER_OF_IMAGES; ++i )
+ {
+ control.Add( CreateChildImageView( IMAGE_PATH[ i ], Size( 100.0f, 100.0f ) ) );
+ }
+ }
+
+ /**
+ * @brief Called when the stage is touched.
+ *
+ * We will use this to quit the application.
+ */
+ bool OnTouch( Actor /* actor */, const TouchData& /* touch */ )
+ {
+ mApplication.Quit();
+ return true;
+ }
+
+ /**
+ * @brief Called when any key event is received.
+ *
+ * Will use this to quit the application if Back or the Escape key is received
+ * @param[in] event The key event information
+ */
+ void OnKeyEvent( const KeyEvent& event )
+ {
+ if( event.state == KeyEvent::Down )
+ {
+ if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
+ {
+ mApplication.Quit();
+ }
+ }
+ }
+
+private:
+ Application& mApplication; ///< A reference to the application object.
+};
+
+int DALI_EXPORT_API main( int argc, char **argv )
+{
+ Application application = Application::New( &argc, &argv );
+ SimpleLayoutExample test( application );
+ application.MainLoop();
+ return 0;
+}