From: Adeel Kazmi Date: Thu, 9 Aug 2018 08:36:07 +0000 (+0000) Subject: Merge "Adding GridLayout" into devel/master X-Git-Tag: dali_1.3.36~1 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=2dd55c62173e94588e4bb45e263a32b3d77af65a;hp=496563cc996dfa3201f466aba3a0427b78f21d38 Merge "Adding GridLayout" into devel/master --- diff --git a/automated-tests/src/dali-toolkit/CMakeLists.txt b/automated-tests/src/dali-toolkit/CMakeLists.txt index 5852cfc..086f621 100755 --- a/automated-tests/src/dali-toolkit/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit/CMakeLists.txt @@ -20,6 +20,7 @@ SET(TC_SOURCES utc-Dali-FlexContainer.cpp utc-Dali-FlexLayout.cpp utc-Dali-GaussianBlurView.cpp + utc-Dali-GridLayout.cpp utc-Dali-ImageView.cpp utc-Dali-ImageVisual.cpp utc-Dali-JsonParser.cpp diff --git a/automated-tests/src/dali-toolkit/utc-Dali-GridLayout.cpp b/automated-tests/src/dali-toolkit/utc-Dali-GridLayout.cpp new file mode 100644 index 0000000..4950241 --- /dev/null +++ b/automated-tests/src/dali-toolkit/utc-Dali-GridLayout.cpp @@ -0,0 +1,349 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include <../custom-layout.h> + +#include + +using namespace Dali; +using namespace Toolkit; + +void utc_dali_toolkit_grid_layouting_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void utc_dali_toolkit_grid_layouting_cleanup(void) +{ + test_return_value = TET_PASS; +} + +int UtcDaliLayouting_GridLayout01(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliLayouting_GridLayout01 2 Column, 4 Items"); + + const auto NUMBER_OF_COLUMNS = 2; + const auto NUMBER_OF_ITEMS = 4; + + tet_printf( "Testing %d columns with %d items\n", NUMBER_OF_COLUMNS, NUMBER_OF_ITEMS ); + + Stage stage = Stage::GetCurrent(); + + auto rootControl = Control::New(); + auto absoluteLayout = AbsoluteLayout::New(); + DevelControl::SetLayout( rootControl, absoluteLayout ); + rootControl.SetName( "AbsoluteLayout" ); + stage.Add( rootControl ); + + auto gridContainer = Control::New(); + auto gridLayout = Grid::New(); + gridLayout.SetNumberOfColumns( NUMBER_OF_COLUMNS ); + gridContainer.SetName( "GridLayout"); + DevelControl::SetLayout( gridContainer, gridLayout ); + gridContainer.SetProperty( LayoutItem::ChildProperty::WIDTH_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + gridContainer.SetProperty( LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + + std::vector< Control > controls; + for( auto i=0; i < NUMBER_OF_ITEMS; i++ ) + { + controls.push_back( CreateLeafControl( 100, 100 ) ); + } + + for( auto&& iter : controls ) + { + gridContainer.Add( iter ); + } + + rootControl.Add( gridContainer ); + + // Ensure layouting happens + application.SendNotification(); + application.Render(); + + // Grid will layout first 2 items on first row then last 2 on second row. + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f, 0.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::POSITION ), Vector3( 100.0f, 0.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::POSITION ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + // Item sizes will not be changed + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + tet_printf( "Confirm number of columns is as set\n"); + DALI_TEST_EQUALS( gridLayout.GetNumberOfColumns(), NUMBER_OF_COLUMNS, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliLayouting_GridLayout02(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliLayouting_GridLayout02"); + + const auto NUMBER_OF_COLUMNS = 3; + const auto NUMBER_OF_ITEMS = 7; + + tet_printf( "Testing %d columns with %d items\n", NUMBER_OF_COLUMNS, NUMBER_OF_ITEMS ); + + Stage stage = Stage::GetCurrent(); + + auto rootControl = Control::New(); + auto absoluteLayout = AbsoluteLayout::New(); + DevelControl::SetLayout( rootControl, absoluteLayout ); + rootControl.SetName( "AbsoluteLayout" ); + stage.Add( rootControl ); + + auto gridContainer = Control::New(); + auto gridLayout = Grid::New(); + gridLayout.SetNumberOfColumns( NUMBER_OF_COLUMNS ); + gridContainer.SetName( "GridLayout"); + DevelControl::SetLayout( gridContainer, gridLayout ); + gridContainer.SetProperty( LayoutItem::ChildProperty::WIDTH_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + gridContainer.SetProperty( LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + + std::vector< Control > controls; + for( auto i=0; i < NUMBER_OF_ITEMS; i++ ) + { + controls.push_back( CreateLeafControl( 100, 100 ) ); + } + + for( auto&& iter : controls ) + { + gridContainer.Add( iter ); + } + + rootControl.Add( gridContainer ); + + // Ensure layouting happens + application.SendNotification(); + application.Render(); + + // grid layouts out 3 items per row, which is 480x800. + // Row 1 + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f, 0.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::POSITION ), Vector3( 100.0f, 0.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::POSITION ), Vector3( 200.0f, 0.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + // Row 2 + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[4].GetProperty( Actor::Property::POSITION ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[5].GetProperty( Actor::Property::POSITION ), Vector3( 200.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + // Row 3 + DALI_TEST_EQUALS( controls[6].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f, 200.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[4].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[5].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[6].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliLayouting_GridLayout03(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliLayouting_GridLayout03 Grid Padding"); + + const auto NUMBER_OF_COLUMNS = 2; + const auto NUMBER_OF_ITEMS = 4; + + tet_printf( "Testing %d columns with %d items\n", NUMBER_OF_COLUMNS, NUMBER_OF_ITEMS ); + + Extents GRID_PADDING( Extents( 10, 10, 20, 20 ) ); // start,end,top,bottom + + tet_printf( "Testing with Padding 10,10,20,20\n"); + + Stage stage = Stage::GetCurrent(); + + auto rootControl = Control::New(); + auto absoluteLayout = AbsoluteLayout::New(); + DevelControl::SetLayout( rootControl, absoluteLayout ); + rootControl.SetName( "AbsoluteLayout" ); + stage.Add( rootControl ); + + auto gridContainer = Control::New(); + auto gridLayout = Grid::New(); + gridLayout.SetNumberOfColumns( NUMBER_OF_COLUMNS ); + gridContainer.SetName( "GridLayout"); + DevelControl::SetLayout( gridContainer, gridLayout ); + gridContainer.SetProperty( LayoutItem::ChildProperty::WIDTH_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + gridContainer.SetProperty( LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + gridContainer.SetProperty( Control::Property::PADDING, GRID_PADDING ); + + std::vector< Control > controls; + for( auto i=0; i < NUMBER_OF_ITEMS; i++ ) + { + controls.push_back( CreateLeafControl( 100, 100 ) ); + } + + for( auto&& iter : controls ) + { + gridContainer.Add( iter ); + } + + rootControl.Add( gridContainer ); + + // Ensure layouting happens + application.SendNotification(); + application.Render(); + + tet_infoline(" UtcDaliLayouting_GridLayout03 Grid Padding 2 Column, 4 Items"); + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f + GRID_PADDING.start , 0.0f + GRID_PADDING.top, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::POSITION ), Vector3( 100.0f + GRID_PADDING.start, 0.0f + GRID_PADDING.top, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::POSITION ), Vector3( 0.0f + GRID_PADDING.start, 100.0f + GRID_PADDING.top , 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::POSITION ), Vector3( 100.0f + GRID_PADDING.start, 100.0f + GRID_PADDING.top, 0.0f ), 0.0001f, TEST_LOCATION ); + + tet_infoline(" UtcDaliLayouting_GridLayout03 Size of Grid should include padding"); + DALI_TEST_EQUALS( gridContainer.GetProperty( Actor::Property::SIZE ), Vector3( 100.0f * NUMBER_OF_COLUMNS + GRID_PADDING.start + + GRID_PADDING.end, + 100.0f * ( NUMBER_OF_ITEMS / NUMBER_OF_COLUMNS ) + + GRID_PADDING.top + GRID_PADDING.bottom, + 0.0f ), 0.0001f, TEST_LOCATION ); + + tet_infoline(" UtcDaliLayouting_GridLayout03 Item sizes unchanged"); + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliLayouting_GridLayout04(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliLayouting_GridLayout04 Child Margin"); + + const auto NUMBER_OF_COLUMNS = 2; + const auto NUMBER_OF_ITEMS = 4; + + tet_printf( "Testing %d columns with %d items\n", NUMBER_OF_COLUMNS, NUMBER_OF_ITEMS ); + + Extents GRID_PADDING( Extents( 10, 10, 20, 20 ) ); // start,end,top,bottom + Extents ITEM_MARGIN( Extents( 10, 10, 5, 5 ) ); // start,end,top,bottom + + tet_printf( "Testing with Margin 10,10,5,5\n"); + + Stage stage = Stage::GetCurrent(); + + auto rootControl = Control::New(); + auto absoluteLayout = AbsoluteLayout::New(); + DevelControl::SetLayout( rootControl, absoluteLayout ); + rootControl.SetName( "AbsoluteLayout" ); + stage.Add( rootControl ); + + auto gridContainer = Control::New(); + auto gridLayout = Grid::New(); + gridLayout.SetNumberOfColumns( NUMBER_OF_COLUMNS ); + gridContainer.SetName( "GridLayout"); + DevelControl::SetLayout( gridContainer, gridLayout ); + gridContainer.SetProperty( LayoutItem::ChildProperty::WIDTH_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + gridContainer.SetProperty( LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, ChildLayoutData::WRAP_CONTENT ); + gridContainer.SetProperty( Control::Property::PADDING, GRID_PADDING ); + + std::vector< Control > controls; + for( auto i=0; i < NUMBER_OF_ITEMS; i++ ) + { + auto control = CreateLeafControl( 100, 100 ); + control.SetProperty(Toolkit::Control::Property::MARGIN, ITEM_MARGIN ); + controls.push_back( control ); + } + + for( auto&& iter : controls ) + { + gridContainer.Add( iter ); + } + + rootControl.Add( gridContainer ); + + // Ensure layouting happens + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::POSITION ), + Vector3( 0.0f + GRID_PADDING.start + ITEM_MARGIN.start, + 0.0f + GRID_PADDING.top + ITEM_MARGIN.top, + 0.0f ), + 0.0001f, TEST_LOCATION ); + + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::POSITION ), + Vector3( 100.0f + GRID_PADDING.start + ITEM_MARGIN.start *2 + ITEM_MARGIN.end, + 0.0f + GRID_PADDING.top + ITEM_MARGIN.top, + 0.0f ), 0.0001f, TEST_LOCATION ); + + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::POSITION ), + Vector3( 0.0f + GRID_PADDING.start + ITEM_MARGIN.start, + 100.0f + GRID_PADDING.top + ITEM_MARGIN.top*2 + ITEM_MARGIN.bottom, + 0.0f ), 0.0001f, TEST_LOCATION ); + + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::POSITION ), + Vector3( 100.0f + GRID_PADDING.start + ITEM_MARGIN.start*2 + ITEM_MARGIN.end, + 100.0f + GRID_PADDING.top + ITEM_MARGIN.top*2 + ITEM_MARGIN.bottom, + 0.0f ), 0.0001f, TEST_LOCATION ); + + tet_infoline(" UtcDaliLayouting_GridLayout03 Size of Grid should include padding and margins"); + + const auto NUMBER_OF_ROWS = ( NUMBER_OF_ITEMS / NUMBER_OF_COLUMNS ); + + DALI_TEST_EQUALS( gridContainer.GetProperty( Actor::Property::SIZE ), Vector3( 100.0f * NUMBER_OF_COLUMNS + GRID_PADDING.start + GRID_PADDING.end + + ITEM_MARGIN.start *NUMBER_OF_COLUMNS + ITEM_MARGIN.end *NUMBER_OF_COLUMNS, + 100.0f * NUMBER_OF_ROWS + + GRID_PADDING.top + GRID_PADDING.bottom + + ITEM_MARGIN.bottom *NUMBER_OF_ROWS + ITEM_MARGIN.bottom *NUMBER_OF_ROWS, + 0.0f ), 0.0001f, TEST_LOCATION ); + + tet_infoline(" UtcDaliLayouting_GridLayout03 Item sizes unchanged"); + DALI_TEST_EQUALS( controls[0].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[1].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[2].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + DALI_TEST_EQUALS( controls[3].GetProperty( Actor::Property::SIZE ), Vector3( 100.0f, 100.0f, 0.0f ), 0.0001f, TEST_LOCATION ); + + END_TEST; +} + + +int UtcDaliLayouting_GridLayoutDownCast(void) +{ + TestApplication application; + tet_infoline(" UtcDaliLayouting_GridLayoutDownCast - Testing Downcast"); + + Grid gridLayout = Grid::New(); + + LayoutGroup layoutGroup( gridLayout ); + + Grid gridLayoutCandidate = Grid::DownCast( layoutGroup ); + DALI_TEST_CHECK( gridLayoutCandidate ); + + END_TEST; +} \ No newline at end of file diff --git a/dali-toolkit/devel-api/file.list b/dali-toolkit/devel-api/file.list index e974720..754c815 100755 --- a/dali-toolkit/devel-api/file.list +++ b/dali-toolkit/devel-api/file.list @@ -35,6 +35,7 @@ devel_api_src_files = \ $(devel_api_src_dir)/layouting/flex-layout.cpp \ $(devel_api_src_dir)/layouting/absolute-layout.cpp \ $(devel_api_src_dir)/layouting/linear-layout.cpp \ + $(devel_api_src_dir)/layouting/grid.cpp \ $(devel_api_src_dir)/layouting/layout-item.cpp \ $(devel_api_src_dir)/layouting/layout-item-impl.cpp \ $(devel_api_src_dir)/layouting/layout-controller.cpp \ @@ -86,6 +87,7 @@ devel_api_layouting_header_files = \ $(devel_api_src_dir)/layouting/absolute-layout.h \ $(devel_api_src_dir)/layouting/child-layout-data.h \ $(devel_api_src_dir)/layouting/flex-layout.h \ + $(devel_api_src_dir)/layouting/grid.h \ $(devel_api_src_dir)/layouting/linear-layout.h \ $(devel_api_src_dir)/layouting/layout-child-impl.h \ $(devel_api_src_dir)/layouting/layout-controller.h \ diff --git a/dali-toolkit/devel-api/layouting/grid.cpp b/dali-toolkit/devel-api/layouting/grid.cpp new file mode 100644 index 0000000..1d9958d --- /dev/null +++ b/dali-toolkit/devel-api/layouting/grid.cpp @@ -0,0 +1,74 @@ +/* + * 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 + +//EXTERNAL HEADERS +//INTERNAL HEADERS +#include + +namespace Dali +{ +namespace Toolkit +{ + +Grid::Grid() +{ +} + +Grid Grid::New() +{ + Internal::GridPtr internal = Internal::Grid::New(); + return Grid( internal.Get() ); +} + +Grid Grid::DownCast( BaseHandle handle ) +{ + return Grid( dynamic_cast< Dali::Toolkit::Internal::Grid*>( handle.GetObjectPtr() ) ); +} + +Grid::Grid( const Grid& other ) +: LayoutGroup( other ) +{ +} + +Grid& Grid::operator=( const Grid& other ) +{ + if( &other != this ) + { + LayoutGroup::operator=( other ); + } + return *this; +} + +void Grid::SetNumberOfColumns( int columns ) +{ + GetImplementation(*this).SetNumberOfColumns( columns ); +} + +int Grid::GetNumberOfColumns() const +{ + return GetImplementation(*this).GetNumberOfColumns(); +} + +Grid::Grid( Dali::Toolkit::Internal::Grid* object ) +: LayoutGroup( object ) +{ +} + +} // namespace Toolkit +} // namespace Dali \ No newline at end of file diff --git a/dali-toolkit/devel-api/layouting/grid.h b/dali-toolkit/devel-api/layouting/grid.h new file mode 100644 index 0000000..e26b93f --- /dev/null +++ b/dali-toolkit/devel-api/layouting/grid.h @@ -0,0 +1,113 @@ +#ifndef DALI_TOOLKIT_LAYOUTING_GRID_LAYOUT_H +#define DALI_TOOLKIT_LAYOUTING_GRID_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. + */ + +#include +#include +#include +#include +#include + +namespace Dali +{ +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Grid; +} + +/** + * @brief + * This class implements a horizontal box layout, automatically handling + * right to left or left to right direction change. + */ +class DALI_TOOLKIT_API Grid : public LayoutGroup +{ +public: + + /** + * @brief Creates an uninitialized Grid handle. + * + * Initialize it using Grid::New(). + * Calling member functions with an uninitialized handle is not allowed. + */ + Grid(); + + /** + * @brief Creates a Grid object. + */ + static Grid New(); + + /** + * @brief Downcasts a handle to a Grid handle. + * + * If handle points to a Grid, the downcast produces a valid handle. + * If not, the returned handle is left uninitialized. + + * @param[in] handle to an object + * @return Handle to a Grid or an uninitialized handle + */ + static Grid DownCast( BaseHandle handle ); + + /** + * @brief Copy constructor + */ + Grid( const Grid& other ); + + /** + * @brief Assigment operator + */ + Grid& operator=( const Grid& other ); + + /** + * @brief Default destructor. + * + * This is non-virtual, since derived Handle types must not contain data or virtual methods + */ + ~Grid()=default; + + /** + * @brief Set the number of columns in the Grid. + * @param[in] columns number of columns, + */ + void SetNumberOfColumns( int columns ); + + /** + * @brief Get the number of columns in the grid. + * @return the number of columns. + */ + int GetNumberOfColumns() const; + +public: // Not intended for application developers + + /// @cond internal + /** + * @brief This constructor is used by Grid::New() methods. + * + * @param[in] actor A pointer to a newly allocated Dali resource + */ + explicit DALI_INTERNAL Grid( Internal::Grid* body ); + /// @endcond +}; + +} // namespace Toolkit +} // namespace Dali + +#endif // DALI_TOOLKIT_LAYOUTING_GRID_LAYOUT_H diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 328d1d6..6475502 100755 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -14,6 +14,8 @@ toolkit_src_files = \ $(toolkit_src_dir)/layouting/flex-layout-impl.cpp \ $(toolkit_src_dir)/layouting/absolute-layout-impl.cpp \ $(toolkit_src_dir)/layouting/linear-layout-impl.cpp \ + $(toolkit_src_dir)/layouting/grid-locations.cpp \ + $(toolkit_src_dir)/layouting/grid-impl.cpp \ $(toolkit_src_dir)/layouting/layout-item-data-impl.cpp \ $(toolkit_src_dir)/layouting/layout-group-data-impl.cpp \ $(toolkit_src_dir)/layouting/layout-controller-debug.cpp \ diff --git a/dali-toolkit/internal/layouting/grid-impl.cpp b/dali-toolkit/internal/layouting/grid-impl.cpp new file mode 100644 index 0000000..a83ed99 --- /dev/null +++ b/dali-toolkit/internal/layouting/grid-impl.cpp @@ -0,0 +1,270 @@ +/* + * 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 + +//EXTERNAL HEADERS +#include +#include +#include + +//INTERNAL HEADERS +#include +#include +#include +#include +#include + +#if defined(DEBUG_ENABLED) +static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_LAYOUT" ); +#endif + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +GridPtr Grid::New() +{ + GridPtr layout( new Grid() ); + return layout; +} + +Grid::Grid() +: LayoutGroup(), + mTotalHeight( 0 ), + mTotalWidth( 0 ), + mNumColumns( AUTO_FIT ), + mNumRows( AUTO_FIT ), + mRequestedColumnWidth( 0 ), + mRequestedNumColumns( AUTO_FIT ) +{ + mLocations = GridLocations::New(); +} + +Grid::~Grid(){} + +void Grid::SetNumberOfColumns( int columns ) +{ + mRequestedNumColumns = columns; + // Store value and Relayout if changed. + if( columns != mNumColumns ) + { + mNumColumns = columns; + RequestLayout(); + } +} + +int Grid::GetNumberOfColumns() const +{ + return mNumColumns; +} + +void Grid::DetermineNumberOfColumns( int availableSpace ) +{ + if( mRequestedNumColumns == AUTO_FIT ) + { + if( availableSpace > 0 ) + { + // Can only calculate number of columns if a column width has been set + mNumColumns = ( mRequestedColumnWidth > 0 ) ? ( availableSpace / mRequestedColumnWidth ) : 1; + } + } +} + +void Grid::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec ) +{ + DALI_LOG_STREAM( gLogFilter, Debug::Verbose, + "Grid::OnMeasure Actor Id:" << Actor::DownCast(GetOwner()).GetId() << + " Owner:" << Actor::DownCast(GetOwner()).GetName() << + " MeasureSpecs( width:"< 0 ) + { + auto childLayoutItem = GetChildAt( 0 ); + if( childLayoutItem ) + { + auto childOwner = childLayoutItem->GetOwner(); + + MeasureChild( childLayoutItem, widthMeasureSpec, heightMeasureSpec ); + desiredChildHeight = childLayoutItem->GetMeasuredHeight(); + desiredChildWidth = childLayoutItem->GetMeasuredWidth(); + + // If child has a margin then add it to desired size + Extents childMargin = childOwner.GetProperty( Toolkit::Control::Property::MARGIN ); + desiredChildHeight += childMargin.top + childMargin.bottom; + desiredChildWidth += childMargin.start + childMargin.end; + + mTotalWidth = desiredChildWidth * mNumColumns; + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredWidth(%d) \n", mTotalWidth.mValue ); + } // Child is LayoutItem + } // Child exists + + // Include padding for max and min checks + mTotalWidth += gridLayoutPadding.start + gridLayoutPadding.end; + + // Ensure width does not exceed specified atmost width or less than mininum width + mTotalWidth = std::max( mTotalWidth, GetSuggestedMinimumWidth() ); + + // widthMode EXACTLY so grid must be the given width + if( gridWidthMode == MeasureSpec::Mode::EXACTLY || gridWidthMode == MeasureSpec::Mode::AT_MOST ) + { + // In the case of AT_MOST, widthSize is the max limit. + mTotalWidth = std::min( mTotalWidth, widthSize ); + } + + availableContentWidth = mTotalWidth - gridLayoutPadding.start - gridLayoutPadding.end; + widthSize = mTotalWidth; + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnMeasure availableContentWidth(%d) mTotalWidth(%d) \n", + availableContentWidth, + mTotalWidth.mValue ); + // HEIGHT SPECIFICATIONS + + // heightMode EXACTLY so grid must be the given height + if( gridHeightMode == MeasureSpec::Mode::EXACTLY || gridHeightMode == MeasureSpec::Mode::AT_MOST ) + { + if( childCount > 0 ) + { + mTotalHeight = gridLayoutPadding.top + gridLayoutPadding.bottom; + + for( auto i = 0u; i < childCount; i += mNumColumns ) + { + mTotalHeight += desiredChildHeight; + } + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnMeasure TotalDesiredHeight(%d) \n", + mTotalHeight.mValue ); + + // Ensure ourHeight does not exceed specified atmost height + mTotalHeight = std::min( mTotalHeight, heightSize ); + mTotalHeight = std::max( mTotalHeight, GetSuggestedMinimumHeight() ); + + heightSize = mTotalHeight; + } // Child exists + + // In the case of AT_MOST, availableContentHeigth is the max limit. + availableContentHeight = heightSize - gridLayoutPadding.top - gridLayoutPadding.bottom; + } + else + { + // Grid expands to fit content + + // If number of columns AUTO_FIT then set to 1 column. + + // Calculate numbers of rows, round down result as later check for remainder. + mNumRows = childCount / ( ( mNumColumns ) ? mNumColumns : 1 ); + // If number of cells not cleanly dividable by colums, add another row to house remainder cells. + mNumRows += ( childCount % ( ( mNumColumns ) ? mNumColumns : 1 ) ) ? 1 : 0; + + availableContentHeight = desiredChildHeight * mNumRows; + } + + // If number of columns not defined + DetermineNumberOfColumns( availableContentWidth ); + + // Locations define the start, end,top and bottom of each cell. + mLocations->CalculateLocations( mNumColumns, availableContentWidth, availableContentHeight, childCount, 0, 0 ); + + + SetMeasuredDimensions( ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK ), + ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK ) ); +} + +void Grid::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom ) +{ + auto owner = GetOwner(); + auto actor = Actor::DownCast(owner); + auto count = GetChildCount(); + + GridLocations::LocationVector locations = mLocations->GetLocations(); + + Extents gridLayoutPadding = GetPadding(); + Extents childMargins; + + // Margin for all children dependant on if set on first child + if( count ) + { + LayoutItemPtr childLayout = GetChildAt( 0 ); + if( childLayout ) + { + auto childOwner = childLayout->GetOwner(); + if( childOwner ) + { + childMargins = childOwner.GetProperty( Toolkit::Control::Property::MARGIN ); + } + } + } + + for( unsigned int i = 0; i < count; i++) + { + // for each child + int childIndex = i; + LayoutItemPtr childLayout = GetChildAt( childIndex ); + if( childLayout != nullptr ) + { + // Get start and end position of child x1,x2 + auto x1 = locations[ i ].xStart; + auto x2 = locations[ i ].xEnd; + + // Get top and bottom position of child y1,y2 + auto y1 = locations[ i ].yTop; + auto y2 = locations[ i ].yBottom; + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Grid::OnLayout cellSize(%d,%d) \n", x2 - x1, y2 - y1); + + // Offset children by the grids padding if present + x1 += gridLayoutPadding.start; + x2 += gridLayoutPadding.start; + y1 += gridLayoutPadding.top; + y2 += gridLayoutPadding.top; + + // Offset children by the margin of the first child ( if required ). + x1 += childMargins.start; + x2 -= childMargins.end; + y1 += childMargins.top; + y2 -= childMargins.bottom; + + childLayout->Layout( x1, y1, x2, y2 ); + } + } +} + +} // namespace Internal +} // namespace Toolkit +} // namespace Dali diff --git a/dali-toolkit/internal/layouting/grid-impl.h b/dali-toolkit/internal/layouting/grid-impl.h new file mode 100644 index 0000000..6afb3f5 --- /dev/null +++ b/dali-toolkit/internal/layouting/grid-impl.h @@ -0,0 +1,137 @@ +#ifndef DALI_TOOLKIT_INTERNAL_LAYOUTING_GRID_IMPL_H +#define DALI_TOOLKIT_INTERNAL_LAYOUTING_GRID_IMPL_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_HEADERS +#include +#include + +// INTERNAL_HEADERS +#include +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +class Grid; +using GridPtr = IntrusivePtr; + +class Grid final : public LayoutGroup +{ +public: + + /** + * Creates a pointer to an Internal Grid + */ + static GridPtr New(); + + /** + * Set grid's the number of columns + * @param[in] columns number of columns + */ + void SetNumberOfColumns( int columns ); + + /** + * Get number of columns, -1 would indicate AUTO_FIT + * which means the number has not been set but will be calculated. + * @return the number of columns set + */ + int GetNumberOfColumns() const; + + // Movable but not copyable + Grid( const Grid& other ) = delete; + Grid& operator=( const Grid& other ) = delete; + Grid( Grid && ) = default; + Grid& operator=( Grid && ) = default; + +protected: + + /** + * Default Constructor + */ + Grid(); + + /** + * Destructor + */ + ~Grid(); + + /** + * @copydoc LayoutItem::OnMeasure + */ + virtual void OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec ) override; + + /** + * @copydoc LayoutItem::OnLayout + */ + virtual void OnLayout( bool changed, LayoutLength l, LayoutLength t, LayoutLength r, LayoutLength b ) override; + +private: + + /** + * @brief + * For the given availableSpace, calculate or retreive the number of required columns. + * @param[in] availableSpace the space available for a child in each column. + */ + void DetermineNumberOfColumns( int availableSpace ); + +private: + + GridLocationsPtr mLocations; + + LayoutLength mTotalHeight; + LayoutLength mTotalWidth; + + const int AUTO_FIT = -1; + + int mNumColumns; + int mNumRows; + + int mHorizontalSpacing = 0; + int mVerticalSpacing = 0; + int mRequestedHorizontalSpacing; + int mRequestedColumnWidth; + int mRequestedNumColumns; + int mRequestedNumRows; +}; + +} // namespace Internal + +inline Internal::Grid& GetImplementation( Dali::Toolkit::Grid& handle ) +{ + DALI_ASSERT_ALWAYS( handle && "Grid handle is empty" ); + BaseObject& object = handle.GetBaseObject(); + return static_cast( object ); +} + +inline const Internal::Grid& GetImplementation( const Dali::Toolkit::Grid& handle ) +{ + DALI_ASSERT_ALWAYS( handle && "Grid handle is empty" ); + const BaseObject& object = handle.GetBaseObject(); + return static_cast( object ); +} + +} // namespace Toolkit +} // namespace Dali + +#endif // DALI_TOOLKIT_INTERNAL_LAYOUTINGInner \ No newline at end of file diff --git a/dali-toolkit/internal/layouting/grid-locations.cpp b/dali-toolkit/internal/layouting/grid-locations.cpp new file mode 100644 index 0000000..8cb1cca --- /dev/null +++ b/dali-toolkit/internal/layouting/grid-locations.cpp @@ -0,0 +1,155 @@ +/* + * 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 + +// EXTERNAL HEADERS +#include + +// INTERNAL HEADERS +#include + +namespace +{ + +#if defined(DEBUG_ENABLED) +static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_AXIS" ); +#endif + +} + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +GridLocationsPtr GridLocations::New() +{ + GridLocationsPtr gridAxis( new GridLocations() ); + return gridAxis; +} + +GridLocations::GridLocations() +: mLocations() +{ +} + +GridLocations::~GridLocations(){} + +void GridLocations::CalculateLocations( int numberOfColumns, + unsigned int availableWidth, + unsigned int availableHeight, + unsigned int numberOfCells, + unsigned int columnWidth, + unsigned int rowHeight ) +{ + mLocations.clear(); + + // Calculate width and height of columns and rows. + + // Calculate numbers of rows, round down result as later check for remainder. + unsigned int numberOfRows = numberOfCells/numberOfColumns; + // If number of cells not cleanly dividable by colums, add another row to house remainder cells. + numberOfRows += (numberOfCells%numberOfColumns)?1:0; + + unsigned int maxColumnWidth = availableWidth / numberOfColumns; + + if( columnWidth > 0 ) + { + // Column width supplied so use this unless exceeds available width. + columnWidth = std::min( columnWidth, maxColumnWidth ); + } + else + { + // Column width not supplied so use calculated value + columnWidth = maxColumnWidth; + } + + unsigned int maxRowHeight = availableHeight / numberOfRows; + + if( rowHeight > 0 ) + { + // Column height supplied so use this unless exceeds available height. + rowHeight = std::min( rowHeight, maxRowHeight ); + } + else + { + // Column height not supplied so use calculated value + rowHeight = maxRowHeight; + } + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "ColumWidth[%d] RowHeight[%d] NumberOfRows[%d] NumberOfColumns[%d]\n", + columnWidth, rowHeight, numberOfRows, numberOfColumns ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Remainder[%d]\n", numberOfCells%numberOfColumns ); + + int y1 = 0; + int y2 = y1 + rowHeight; + + // Calculate start, end, top and bottom coordinate of each cell. + + // Iterate rows + for( auto i = 0u; i < numberOfRows; i++ ) + { + int x1 = 0; + int x2 = x1 + columnWidth; + + // Iterate columns + for( auto j = 0; j < numberOfColumns; j++ ) + { + GridLocations::Cell cell( x1, x2, y1, y2 ); + mLocations.push_back( cell ); + // Calculate starting x and ending x position of each column + x1 = x2; + x2 = x2 + columnWidth; + } + + // Calculate top y and bottom y position of each row. + y1 = y2; + y2 = y2 + rowHeight; + } + +#if defined(DEBUG_ENABLED) + std::ostringstream oss; + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GridLocations::CalculateLocations (%d)\n", numberOfCells ); + for( auto i = 0u; i < numberOfCells; i++ ) + { + DALI_LOG_STREAM( gLogFilter, Debug::Verbose,"x1:"< +#include +#include +#include + +// INTERNAL HEADERS +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +class GridLocations; +using GridLocationsPtr = IntrusivePtr; + +/* @brief This internal class houses the algorithm for computing the locations and size of cells. + * + * A Grid layout uses two instances of this class + * distinguished by the "horizontal" flag which is true for the horizontal axis and false + * for the vertical one. + */ + +class GridLocations : public RefObject +{ + +public: + + static const int UNDEFINED = INT_MIN; + + static const unsigned int HORIZONTAL = 0; + static const unsigned int VERTICAL = 1; + + struct Cell + { + int xStart; + int xEnd; + int yTop; + int yBottom; + int explictlyDefined; + + Cell( int x1, int x2, int y1, int y2): xStart(x1), xEnd(x2), yTop(y1), yBottom(y2){}; + }; + + typedef std::vector< Cell > LocationVector; + +public: + + static GridLocationsPtr New(); + + /* + * Uses the given parameters to calculate the x,y coordinates of each cell and cell size. + */ + void CalculateLocations( int numberOfColumns, unsigned int availableWidth, + unsigned int availableHeight, unsigned int numberOfCells, + unsigned int columnWidth, unsigned int rowHeight ); + + LocationVector GetLocations(); + +private: + + GridLocations(); + ~GridLocations(); + GridLocations( const GridLocations& other ) = delete; + GridLocations& operator=( const GridLocations& other ) = delete; + +private: + + LocationVector mLocations; + +}; + +} // namespace Internal +} // namespace Toolkit +} // namespace Dali + +#endif // DALI_TOOLKIT_INTERNAL_LAYOUTING_GRID_LOCATIONS_H \ No newline at end of file