A simple Custom Layout example 34/180734/3
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 1 Jun 2018 12:50:18 +0000 (13:50 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 1 Jun 2018 16:15:02 +0000 (17:15 +0100)
Change-Id: Iea8559d899c594d90a2c00ee7faeb60bf6098c4f

examples/simple-layout/custom-layout-impl.cpp [new file with mode: 0644]
examples/simple-layout/custom-layout-impl.h [new file with mode: 0644]
examples/simple-layout/custom-layout.cpp [new file with mode: 0644]
examples/simple-layout/custom-layout.h [new file with mode: 0644]
examples/simple-layout/simple-layout-example.cpp [new file with mode: 0644]

diff --git a/examples/simple-layout/custom-layout-impl.cpp b/examples/simple-layout/custom-layout-impl.cpp
new file mode 100644 (file)
index 0000000..173dd35
--- /dev/null
@@ -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 <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
diff --git a/examples/simple-layout/custom-layout-impl.h b/examples/simple-layout/custom-layout-impl.h
new file mode 100644 (file)
index 0000000..6ff4e64
--- /dev/null
@@ -0,0 +1,106 @@
+#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
diff --git a/examples/simple-layout/custom-layout.cpp b/examples/simple-layout/custom-layout.cpp
new file mode 100644 (file)
index 0000000..ef90709
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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
diff --git a/examples/simple-layout/custom-layout.h b/examples/simple-layout/custom-layout.h
new file mode 100644 (file)
index 0000000..a561501
--- /dev/null
@@ -0,0 +1,107 @@
+#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
diff --git a/examples/simple-layout/simple-layout-example.cpp b/examples/simple-layout/simple-layout-example.cpp
new file mode 100644 (file)
index 0000000..efa5351
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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;
+}