2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/table-view/table-view-impl.h>
23 #include <dali/public-api/object/ref-object.h>
24 #include <dali/public-api/object/type-registry.h>
25 #include <dali/devel-api/object/type-registry-helper.h>
26 #include <dali/devel-api/scripting/scripting.h>
27 #include <dali/public-api/size-negotiation/relayout-container.h>
28 #include <dali/integration-api/debug.h>
35 * Custom properties for where to put the actor.
37 * When an actor is add to the tableView through Actor::Add() instead of TableView::AddChild,
38 * the following custom properties of the actor are checked to decide the actor position inside the table
40 * These non-animatable properties should be registered to the child which would be added to the table
42 const char * const CELL_INDEX_PROPERTY_NAME("cell-index");
43 const char * const ROW_SPAN_PROPERTY_NAME("row-span");
44 const char * const COLUMN_SPAN_PROPERTY_NAME("column-span");
45 const char * const CELL_HORIZONTAL_ALIGNMENT_PROPERTY_NAME("cell-horizontal-alignment");
46 const char * const CELL_VERTICAL_ALIGNMENT_PROPERTY_NAME("cell-vertical-alignment");
49 * @brief Should the tableview fit around the given actor
51 * @param[in] actor The child actor to test against
52 * @param[dimension] The dimension to test against
54 bool FitToChild( Actor actor, Dimension::Type dimension )
56 return actor.GetResizePolicy( dimension ) != ResizePolicy::FILL_TO_PARENT && actor.GetRelayoutSize( dimension ) > 0.0f;
59 #if defined(DEBUG_ENABLED)
60 // debugging support, very useful when new features are added or bugs are hunted down
61 // currently not called from code so compiler will optimize these away, kept here for future debugging
63 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
64 #define TV_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
65 //#define TABLEVIEW_DEBUG 1
67 #if defined(TABLEVIEW_DEBUG)
68 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
70 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
72 for( unsigned int i = 0; i < array.GetRows(); ++i )
74 for( unsigned int j = 0; j < array.GetColumns(); ++j )
76 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
78 std::string actorName;
82 actorName = data.actor.GetName();
84 TV_LOG("Array[%d,%d]=%c %s %d,%d,%d,%d ", i, j, actor, actorName.c_str(),
85 data.position.rowIndex, data.position.columnIndex,
86 data.position.rowSpan, data.position.columnSpan );
92 // debugging support, very useful when new features are added or bugs are hunted down
93 // currently not called from code so compiler will optimize these away, kept here for future debugging
94 void PrintArray( Array2d<Size>& array )
96 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
98 for( unsigned int i = 0; i < array.GetRows(); ++i )
100 for( unsigned int j = 0; j < array.GetColumns(); ++j )
102 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
107 // debugging support, very useful when new features are added or bugs are hunted down
108 // currently not called from code so compiler will optimize these away, kept here for future debugging
109 void PrintVector( std::vector<float>& array )
111 TV_LOG( "vector, size [%d]\n", array.size() );
113 for( unsigned int i = 0; i < array.size(); ++i )
115 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
119 #endif // defined(TABLEVIEW_DEBUG)
120 #endif // defined(DEBUG_ENABLED)
139 return Toolkit::TableView::New( 0, 0 );
142 // Setup properties, signals and actions using the type-registry.
143 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TableView, Toolkit::Control, Create );
145 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "rows", UNSIGNED_INTEGER, ROWS )
146 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "columns", UNSIGNED_INTEGER, COLUMNS )
147 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "cell-padding", VECTOR2, CELL_PADDING )
148 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layout-rows", MAP, LAYOUT_ROWS )
149 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layout-columns", MAP, LAYOUT_COLUMNS )
151 DALI_TYPE_REGISTRATION_END()
153 const Scripting::StringEnum LAYOUT_POLICY_STRING_TABLE[] =
155 { "fixed", Toolkit::TableView::FIXED },
156 { "relative", Toolkit::TableView::RELATIVE },
157 { "fill", Toolkit::TableView::FILL },
158 { "fit", Toolkit::TableView::FIT }
160 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
162 const Scripting::StringEnum HORIZONTAL_ALIGNMENT_STRING_TABLE[] =
164 {"left", HorizontalAlignment::LEFT},
165 {"center", HorizontalAlignment::CENTER},
166 {"right", HorizontalAlignment::RIGHT}
168 const unsigned int HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(HORIZONTAL_ALIGNMENT_STRING_TABLE) / sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE[0] );
170 const Scripting::StringEnum VERTICAL_ALIGNMENT_STRING_TABLE[] =
172 {"top", VerticalAlignment::TOP},
173 {"center", VerticalAlignment::CENTER},
174 {"bottom", VerticalAlignment::BOTTOM}
176 const unsigned int VERTICAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(VERTICAL_ALIGNMENT_STRING_TABLE) / sizeof( VERTICAL_ALIGNMENT_STRING_TABLE[0] );
178 } // Unnamed namespace
180 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
182 // Create the implementation, temporarily owned by this handle on stack
183 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
185 // Pass ownership to CustomActor handle
186 Toolkit::TableView handle( *impl );
188 // Second-phase init of the implementation
189 // This can only be done after the CustomActor connection has been made...
195 bool TableView::AddChild( Actor& child, const Toolkit::TableView::CellPosition& position )
197 // check that the child is valid
198 DALI_ASSERT_ALWAYS( child );
200 // if child is already parented, we adopt it
203 // check if we need to expand our data array
204 if( position.rowIndex >= mCellData.GetRows() )
206 // only adding new rows
207 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
210 if( position.columnIndex >= mCellData.GetColumns() )
212 // only adding new columns
213 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
216 // check if there already is something in this cell
217 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
219 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
222 RelayoutingLock lock( *this );
226 // if child spans multiple rows of columns
227 if( ( position.rowSpan > 1 ) && ( position.rowIndex + position.rowSpan > mCellData.GetRows() ) )
229 // increase table size for the full span, only increasing rows
230 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
233 if( ( position.columnSpan > 1 ) && ( position.columnIndex + position.columnSpan > mCellData.GetColumns() ) )
235 // increase table size for the full span, only increasing columns
236 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
239 // Fill in all cells that need the data
242 data.position = position;
244 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
246 // store same information to all cells, this way we can identify
247 // if a cell is the prime location of an actor or a spanned one
248 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
250 // store same information to all cells, this way we can identify
251 // if a cell is the prime location of an actor or a spanned one
252 mCellData[ row ][ column ] = data;
256 // Relayout the whole table
259 return true; // Addition successful
262 Actor TableView::GetChildAt( const Toolkit::TableView::CellPosition& position )
264 if( ( position.rowIndex < mCellData.GetRows() ) && ( position.columnIndex < mCellData.GetColumns() ) )
266 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
269 // Return an empty handle
273 Actor TableView::RemoveChildAt( const Toolkit::TableView::CellPosition& position )
275 // get the child handle
276 Actor child = GetChildAt( position );
277 // if no real actor there, nothing else to be done
280 RelayoutingLock lock( *this );
281 // Remove the child, this will trigger a call to OnControlChildRemove
282 Self().Remove( child );
284 // relayout the table only if instances were found
285 if( RemoveAllInstances( child ) )
290 // return the child back to caller
294 bool TableView::FindChildPosition( const Actor& child, Toolkit::TableView::CellPosition& positionOut )
296 // Only find valid child actors
299 // Walk through the layout data
300 const unsigned int rowCount = mCellData.GetRows();
301 const unsigned int columnCount = mCellData.GetColumns();
303 for( unsigned int row = 0; row < rowCount; ++row )
305 for( unsigned int column = 0; column < columnCount; ++column )
307 if( mCellData[ row ][ column ].actor == child )
309 positionOut = mCellData[ row ][ column ].position;
319 void TableView::InsertRow( unsigned int rowIndex )
321 RelayoutingLock lock( *this );
323 mCellData.InsertRow( rowIndex );
325 // Need to update the cell infos for the items that moved
326 const unsigned int rowCount = mCellData.GetRows();
327 const unsigned int columnCount = mCellData.GetColumns();
329 for( unsigned int row = 0; row < rowCount; ++row )
331 for( unsigned int column = 0; column < columnCount; ++column )
333 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
335 // If cell is spanning and above and spans to inserted row
336 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
337 ( position.rowIndex + position.rowSpan > rowIndex ) )
342 // Copy cell to occupy the new column
343 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
345 else if( row > rowIndex ) // If below of inserted row, increase row index
353 // Expand row data array
354 mRowData.Insert( mRowData.Begin() + rowIndex, RowColumnData() );
356 // Sizes may have changed, so relayout
357 mRowColumnDirty = true;
361 void TableView::DeleteRow( unsigned int rowIndex )
363 std::vector< Actor > ignored;
364 DeleteRow( rowIndex, ignored );
367 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
369 RelayoutingLock lock( *this );
372 std::vector< CellData > lost;
373 mCellData.DeleteRow( rowIndex, lost );
375 // Need to update the cell infos for the items that moved
376 const unsigned int rowCount = mCellData.GetRows();
377 const unsigned int columnCount = mCellData.GetColumns();
379 for( unsigned int row = 0; row < rowCount; ++row )
381 for( unsigned int column = 0; column < columnCount; ++column )
383 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
385 // If cell is spanning and above and spans to deleted row
386 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
387 ( position.rowIndex + position.rowSpan > rowIndex ) )
390 if( position.rowSpan > 1 )
395 else if( row >= rowIndex ) // If below of or at the inserted row, decrease row index
398 if( position.rowIndex > 1 )
406 // 1 row removed, 0 columns
407 RemoveAndGetLostActors( lost, removed, 1u, 0u );
409 // Contract row data array
410 mRowData.Erase( mRowData.Begin() + rowIndex );
412 // Sizes may have changed, so relayout
413 mRowColumnDirty = true;
417 void TableView::InsertColumn( unsigned int columnIndex )
419 RelayoutingLock lock( *this );
421 // Insert the new column
422 mCellData.InsertColumn( columnIndex );
424 // Need to update the cell infos for the items that moved
425 const unsigned int rowCount = mCellData.GetRows();
426 const unsigned int columnCount = mCellData.GetColumns();
428 for( unsigned int row = 0; row < rowCount; ++row )
430 for( unsigned int column = 0; column < columnCount; ++column )
432 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
434 // If cell is spanning and left side and spans to inserted column
435 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
436 ( position.columnIndex + position.columnSpan > columnIndex ) )
439 position.columnSpan++;
441 // Copy cell to occupy the new column
442 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
444 else if( column > columnIndex ) // If on the right side of inserted column, increase column index
447 position.columnIndex++;
452 // Expand column data array
453 mColumnData.Insert( mColumnData.Begin() + columnIndex, RowColumnData() );
455 // Sizes may have changed so relayout
456 mRowColumnDirty = true;
460 void TableView::DeleteColumn( unsigned int columnIndex )
462 std::vector< Actor > ignored;
463 DeleteColumn( columnIndex, ignored );
466 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
468 RelayoutingLock lock( *this );
471 std::vector< CellData > lost;
472 mCellData.DeleteColumn( columnIndex, lost );
474 // Need to update the cell infos for the items that moved
475 const unsigned int rowCount = mCellData.GetRows();
476 const unsigned int columnCount = mCellData.GetColumns();
478 for( unsigned int row = 0; row < rowCount; ++row )
480 for( unsigned int column = 0; column < columnCount; ++column )
482 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
484 // If cell is spanning and left side and spans to inserted column
485 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
486 ( position.columnIndex + position.columnSpan > columnIndex ) )
489 if( position.columnSpan > 1 )
491 position.columnSpan--;
494 else if( column >= columnIndex ) // If on the right side of or at the inserted column, decrease column index
497 if( position.columnIndex > 0 )
499 position.columnIndex--;
505 // 0 rows, 1 column removed
506 RemoveAndGetLostActors( lost, removed, 0u, 1u );
508 // Contract column data array
509 mColumnData.Erase( mColumnData.Begin() + columnIndex );
511 // Size may have changed so relayout
512 mRowColumnDirty = true;
516 void TableView::Resize( unsigned int rows, unsigned int columns )
518 std::vector< Actor > ignored;
519 Resize( rows, columns, ignored );
522 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
524 RelayoutingLock lock( *this );
526 unsigned int oldRows = GetRows();
527 unsigned int oldColumns = GetColumns();
530 std::vector< CellData > lost;
531 ResizeContainers( rows, columns, lost );
533 // Calculate if we lost rows
534 unsigned int rowsRemoved = 0;
535 unsigned int newRows = GetRows();
537 if( oldRows < newRows )
539 rowsRemoved = newRows - oldRows;
542 // Calculate if we lost columns
543 unsigned int columnsRemoved = 0;
544 unsigned int newColumns = GetColumns();
545 if( oldColumns < newColumns )
547 rowsRemoved = newColumns - oldColumns;
550 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
552 // Sizes may have changed so request a relayout
553 mRowColumnDirty = true;
557 void TableView::SetCellPadding( Size padding )
559 // If padding really changed
560 if( padding != mPadding )
568 Size TableView::GetCellPadding()
573 void TableView::SetFitHeight( unsigned int rowIndex )
575 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
577 if( mRowData[ rowIndex ].sizePolicy != Toolkit::TableView::FIT )
579 mRowData[ rowIndex ].sizePolicy = Toolkit::TableView::FIT;
581 mRowColumnDirty = true;
586 bool TableView::IsFitHeight( unsigned int rowIndex ) const
588 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
590 return mRowData[ rowIndex ].sizePolicy == Toolkit::TableView::FIT;
593 void TableView::SetFitWidth( unsigned int columnIndex )
595 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
597 if( mColumnData[ columnIndex ].sizePolicy != Toolkit::TableView::FIT )
599 mColumnData[ columnIndex ].sizePolicy = Toolkit::TableView::FIT;
601 mRowColumnDirty = true;
606 bool TableView::IsFitWidth( unsigned int columnIndex ) const
608 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
610 return mColumnData[ columnIndex ].sizePolicy == Toolkit::TableView::FIT;
613 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
615 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
617 RowColumnData& data = mRowData[ rowIndex ];
619 data.sizePolicy = Toolkit::TableView::FIXED;
621 mRowColumnDirty = true;
625 float TableView::GetFixedHeight( unsigned int rowIndex ) const
627 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
629 return mRowData[ rowIndex ].size;
632 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
634 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
636 RowColumnData& data = mColumnData[ columnIndex ];
638 data.sizePolicy = Toolkit::TableView::FIXED;
640 mRowColumnDirty = true;
644 float TableView::GetFixedWidth( unsigned int columnIndex ) const
646 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
648 return mColumnData[ columnIndex ].size;
651 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
653 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
655 RowColumnData& data = mRowData[ rowIndex ];
656 data.fillRatio = heightPercentage;
657 data.sizePolicy = Toolkit::TableView::RELATIVE;
659 mRowColumnDirty = true;
663 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
665 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
667 return mRowData[ rowIndex ].fillRatio;
670 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
672 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
674 RowColumnData& data = mColumnData[ columnIndex ];
675 data.fillRatio = widthPercentage;
676 data.sizePolicy = Toolkit::TableView::RELATIVE;
678 mRowColumnDirty = true;
682 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
684 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
686 return mColumnData[ columnIndex ].fillRatio;
689 void TableView::CalculateRowColumnData()
691 // Calculate the relative sizes
692 if( mRowColumnDirty )
694 ComputeRelativeSizes( mRowData );
695 ComputeRelativeSizes( mColumnData );
697 mRowColumnDirty = false;
701 void TableView::OnCalculateRelayoutSize( Dimension::Type dimension )
703 CalculateRowColumnData();
705 if( dimension & Dimension::WIDTH )
707 CalculateFixedSizes( mColumnData, Dimension::WIDTH );
708 mFixedTotals.width = CalculateTotalFixedSize( mColumnData );
711 if( dimension & Dimension::HEIGHT )
713 CalculateFixedSizes( mRowData, Dimension::HEIGHT );
714 mFixedTotals.height = CalculateTotalFixedSize( mRowData );
718 void TableView::OnLayoutNegotiated( float size, Dimension::Type dimension )
720 CalculateRowColumnData();
722 // Calculate the value of all relative sized rows and columns
723 if( dimension & Dimension::WIDTH )
725 float remainingSize = size - mFixedTotals.width;
726 if( remainingSize < 0.0f )
728 remainingSize = 0.0f;
731 CalculateRelativeSizes( mColumnData, remainingSize );
734 if( dimension & Dimension::HEIGHT )
736 float remainingSize = size - mFixedTotals.height;
737 if( remainingSize < 0.0f )
739 remainingSize = 0.0f;
742 CalculateRelativeSizes( mRowData, remainingSize );
746 void TableView::OnRelayout( const Vector2& size, RelayoutContainer& container )
748 CalculateRowColumnData();
750 // update every column position in ColumnData array
751 float cumulatedWidth = 0.0f;
752 const unsigned int columnCount = mCellData.GetColumns();
753 for( unsigned int column = 0; column < columnCount; ++column )
755 mColumnData[column].position = cumulatedWidth;
756 cumulatedWidth += mColumnData[ column ].size;
759 // update every row position in RowData array
760 float cumulatedHeight = 0.0f;
761 const unsigned int rowCount = mCellData.GetRows();
762 for( unsigned int row = 0; row < rowCount; ++row )
764 mRowData[row].position = cumulatedHeight;
765 cumulatedHeight += mRowData[ row ].size;
768 // Go through the layout data
769 for( unsigned int row = 0; row < rowCount; ++row )
771 for( unsigned int column = 0; column < columnCount; ++column )
773 CellData& cellData= mCellData[ row ][ column ];
774 Actor& actor = cellData.actor;
775 const Toolkit::TableView::CellPosition position = cellData.position;
777 // If there is an actor and this is the main cell of the actor.
778 // An actor can be in multiple cells if its row or column span is more than 1.
779 // We however must lay out each actor only once.
780 if( actor && position.rowIndex == row && position.columnIndex == column )
782 // Anchor actor to top left of the cell
783 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
784 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
786 Vector2( actor.GetRelayoutSize( Dimension::WIDTH ), actor.GetRelayoutSize( Dimension::HEIGHT ) );
789 actor.GetPadding( padding );
791 if( cellData.horizontalAlignment == HorizontalAlignment::LEFT )
793 actor.SetX( mColumnData[column].position + mPadding.width + padding.left );
797 float cellRightPosition = column+position.columnSpan < columnCount ? mColumnData[column+position.columnSpan].position : cumulatedWidth;
799 if( cellData.horizontalAlignment == HorizontalAlignment::RIGHT )
801 actor.SetX( cellRightPosition - mPadding.width - padding.right - actor.GetRelayoutSize( Dimension::WIDTH ) );
803 else //if( cellData.horizontalAlignment == HorizontalAlignment::CENTER )
805 actor.SetX( (mColumnData[column].position + cellRightPosition
806 + padding.left - padding.right
807 - actor.GetRelayoutSize( Dimension::WIDTH )) * 0.5f );
811 if( cellData.verticalAlignment == VerticalAlignment::TOP )
813 actor.SetY( mRowData[row].position + mPadding.height + padding.top );
817 float cellBottomPosition = row+position.rowSpan < rowCount ? mRowData[row+position.rowSpan].position : cumulatedHeight;
819 if( cellData.verticalAlignment == VerticalAlignment::BOTTOM )
822 actor.SetY( cellBottomPosition - mPadding.height - padding.bottom - actor.GetRelayoutSize( Dimension::HEIGHT ) );
824 else //if( cellData.verticalAlignment = VerticalAlignment::CENTER )
826 actor.SetY( (mRowData[row].position + cellBottomPosition
827 + padding.top - padding.bottom
828 - actor.GetRelayoutSize( Dimension::HEIGHT )) * 0.5f );
836 unsigned int TableView::GetRows()
838 return mCellData.GetRows();
841 unsigned int TableView::GetColumns()
843 return mCellData.GetColumns();
846 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
848 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
852 TableView& tableViewImpl( GetImpl( tableView ) );
855 case Toolkit::TableView::Property::ROWS:
857 if( value.Get<unsigned int>() != tableViewImpl.GetRows() )
859 tableViewImpl.Resize( value.Get<unsigned int>(), tableViewImpl.GetColumns() );
863 case Toolkit::TableView::Property::COLUMNS:
865 if( value.Get<unsigned int>() != tableViewImpl.GetColumns() )
867 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<unsigned int>() );
871 case Toolkit::TableView::Property::CELL_PADDING:
873 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
876 case Toolkit::TableView::Property::LAYOUT_ROWS:
878 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, &TableView::SetFitHeight, value );
881 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
883 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, &TableView::SetFitWidth, value );
890 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
892 Property::Value value;
894 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
898 TableView& tableViewImpl( GetImpl( tableView ) );
901 case Toolkit::TableView::Property::ROWS:
903 value = tableViewImpl.GetRows();
906 case Toolkit::TableView::Property::COLUMNS:
908 value = tableViewImpl.GetColumns();
911 case Toolkit::TableView::Property::CELL_PADDING:
913 value = tableViewImpl.GetCellPadding();
916 case Toolkit::TableView::Property::LAYOUT_ROWS:
918 value = tableViewImpl.GetRowHeightsPropertyValue();
921 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
923 value = tableViewImpl.GetColumnWidthsPropertyValue();
932 void TableView::OnControlChildAdd( Actor& child )
934 if( mLayoutingChild )
936 // we're in the middle of laying out children so no point doing anything here
940 // Test properties on actor
941 HorizontalAlignment::Type horizontalAlignment = HorizontalAlignment::LEFT;
942 VerticalAlignment::Type verticalAlignment = VerticalAlignment::TOP;
943 if( child.GetPropertyIndex( CELL_HORIZONTAL_ALIGNMENT_PROPERTY_NAME ) != Property::INVALID_INDEX )
945 std::string value = child.GetProperty( child.GetPropertyIndex(CELL_HORIZONTAL_ALIGNMENT_PROPERTY_NAME) ).Get<std::string >();
946 Scripting::GetEnumeration< HorizontalAlignment::Type >( value.c_str(),
947 HORIZONTAL_ALIGNMENT_STRING_TABLE,
948 HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT,
949 horizontalAlignment );
951 if( child.GetPropertyIndex( CELL_VERTICAL_ALIGNMENT_PROPERTY_NAME ) != Property::INVALID_INDEX )
953 std::string value = child.GetProperty( child.GetPropertyIndex(CELL_VERTICAL_ALIGNMENT_PROPERTY_NAME) ).Get<std::string >();
954 Scripting::GetEnumeration< VerticalAlignment::Type >( value.c_str(),
955 VERTICAL_ALIGNMENT_STRING_TABLE,
956 VERTICAL_ALIGNMENT_STRING_TABLE_COUNT,
961 Toolkit::TableView::CellPosition cellPosition;
962 if( child.GetPropertyIndex(ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
964 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
967 if( child.GetPropertyIndex(COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
969 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
972 if( child.GetPropertyIndex(CELL_INDEX_PROPERTY_NAME) != Property::INVALID_INDEX )
974 Vector2 indices = child.GetProperty( child.GetPropertyIndex(CELL_INDEX_PROPERTY_NAME) ).Get<Vector2 >();
975 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
976 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
978 AddChild( child, cellPosition );
979 SetCellAlignment(cellPosition, horizontalAlignment, verticalAlignment);
986 // Find the first available cell to store the actor in
987 const unsigned int rowCount = mCellData.GetRows();
988 const unsigned int columnCount = mCellData.GetColumns();
989 for( unsigned int row = 0; row < rowCount; ++row )
991 for( unsigned int column = 0; column < columnCount; ++column )
993 if( !(mCellData[ row ][ column ].actor) )
995 // Put the actor in the cell
998 data.position.columnIndex = column;
999 data.position.rowIndex = row;
1000 data.horizontalAlignment = horizontalAlignment;
1001 data.verticalAlignment = verticalAlignment;
1002 mCellData[ row ][ column ] = data;
1011 // No empty cells, so increase size of the table
1012 unsigned int newColumnCount = ( columnCount > 0 ) ? columnCount : 1;
1013 ResizeContainers( rowCount + 1, newColumnCount );
1015 // Put the actor in the first cell of the new row
1018 data.position.rowIndex = rowCount;
1019 data.position.columnIndex = 0;
1020 data.horizontalAlignment = horizontalAlignment;
1021 data.verticalAlignment = verticalAlignment;
1022 mCellData[ rowCount ][ 0 ] = data;
1026 void TableView::OnControlChildRemove( Actor& child )
1028 // dont process if we're in the middle of bigger operation like delete row, column or resize
1029 if( !mLayoutingChild )
1031 // relayout the table only if instances were found
1032 if( RemoveAllInstances( child ) )
1039 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
1040 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
1041 mCellData( initialRows, initialColumns ),
1042 mLayoutingChild( false ),
1043 mRowColumnDirty( true ) // Force recalculation first time
1045 SetKeyboardNavigationSupport( true );
1046 ResizeContainers( initialRows, initialColumns );
1049 void TableView::OnInitialize()
1051 // Make self as keyboard focusable and focus group
1052 Actor self = Self();
1053 self.SetKeyboardFocusable(true);
1054 SetAsKeyboardFocusGroup(true);
1057 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
1059 std::vector<CellData> ignored;
1060 ResizeContainers( rows, columns, ignored );
1063 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
1066 mCellData.Resize( rows, columns, removed );
1068 // We don't care if these go smaller, data will be regenerated or is not needed anymore
1069 mRowData.Resize( rows );
1070 mColumnData.Resize( columns );
1073 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
1074 unsigned int rowsRemoved, unsigned int columnsRemoved )
1076 // iterate through all lost cells
1077 std::vector< CellData >::const_iterator iter = lost.begin();
1078 for( ; iter != lost.end(); ++iter )
1080 // if it is a valid actor
1083 // is this actor still somewhere else in the table
1084 Toolkit::TableView::CellPosition position;
1085 if( FindChildPosition( (*iter).actor, position ) )
1087 // it must be spanning multiple cells, position contains the top left most one
1088 // check if position is left of the removed location
1089 if( position.columnIndex < (*iter).position.columnIndex )
1091 // if column span is greater than 1
1092 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1094 // decrease column span
1095 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1098 // check if position is left of the removed location
1099 if( position.rowIndex < (*iter).position.rowIndex )
1101 // if row span is greater than 1
1102 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1104 // decrease row span
1105 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1111 // this actor is gone for good
1112 // add actor to removed container
1113 removed.push_back( (*iter).actor );
1114 // we dont want the child actor anymore
1115 Self().Remove( (*iter).actor );
1121 bool TableView::RemoveAllInstances( const Actor& child )
1124 // walk through the layout data
1125 const unsigned int rowCount = mCellData.GetRows();
1126 const unsigned int columnCount = mCellData.GetColumns();
1127 for( unsigned int row = 0; row < rowCount; ++row )
1129 for( unsigned int column = 0; column < columnCount; ++column )
1131 if( mCellData[ row ][ column ].actor == child )
1133 // clear the cell, NOTE that the cell might be spanning multiple cells
1134 mCellData[ row ][ column ] = CellData();
1142 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1143 void(TableView::*funcFixed)(unsigned int, float),
1144 void(TableView::*funcRelative)(unsigned int, float),
1145 void(TableView::*funcFit)(unsigned int),
1146 const Property::Value& value )
1148 Property::Map* map = value.GetMap();
1151 unsigned int index(0);
1152 for ( unsigned int i = 0, count = map->Count(); i < count; ++i )
1154 Property::Value& item = map->GetValue(i);
1155 Property::Map* childMap = item.GetMap();
1157 std::istringstream( map->GetKey(i) ) >> index;
1160 Property::Value* policy = childMap->Find( "policy" );
1161 Property::Value* value = childMap->Find( "value" );
1162 if( policy && value )
1164 std::string policyValue;
1165 policy->Get( policyValue );
1166 Toolkit::TableView::LayoutPolicy policy;
1167 if( Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( policyValue.c_str(),
1168 LAYOUT_POLICY_STRING_TABLE,
1169 LAYOUT_POLICY_STRING_TABLE_COUNT,
1172 if( policy == Toolkit::TableView::FIXED )
1174 (tableViewImpl.*funcFixed)( index, value->Get<float>() );
1176 else if( policy == Toolkit::TableView::RELATIVE )
1178 (tableViewImpl.*funcRelative)( index, value->Get<float>() );
1180 else if( policy == Toolkit::TableView::FIT )
1182 (tableViewImpl.*funcFit)( index );
1184 // do nothing for FILL policy
1192 Property::Value TableView::GetRowHeightsPropertyValue()
1195 GetMapPropertyValue( mRowData, map);
1196 return Property::Value(map);
1199 Property::Value TableView::GetColumnWidthsPropertyValue()
1202 GetMapPropertyValue( mColumnData, map);
1203 return Property::Value(map);
1206 void TableView::GetMapPropertyValue( const RowColumnArray& data, Property::Map& map )
1208 const char* fixedPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIXED,
1209 LAYOUT_POLICY_STRING_TABLE,
1210 LAYOUT_POLICY_STRING_TABLE_COUNT );
1211 const char* relativePolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::RELATIVE,
1212 LAYOUT_POLICY_STRING_TABLE,
1213 LAYOUT_POLICY_STRING_TABLE_COUNT );
1214 const char* fillPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FILL,
1215 LAYOUT_POLICY_STRING_TABLE,
1216 LAYOUT_POLICY_STRING_TABLE_COUNT );
1217 const char* fitPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIT,
1218 LAYOUT_POLICY_STRING_TABLE,
1219 LAYOUT_POLICY_STRING_TABLE_COUNT );
1221 const RowColumnArray::SizeType count = data.Size();
1222 for( RowColumnArray::SizeType i = 0; i < count; i++ )
1224 const RowColumnData& dataInstance = data[ i ];
1227 switch( dataInstance.sizePolicy )
1229 case Toolkit::TableView::FIXED:
1231 item[ "policy" ] = fixedPolicy;
1232 item[ "value" ] = dataInstance.size;
1235 case Toolkit::TableView::RELATIVE:
1237 item[ "policy" ] = relativePolicy;
1238 item[ "value" ] = dataInstance.fillRatio;
1241 case Toolkit::TableView::FIT:
1243 item[ "policy" ] = fitPolicy;
1244 item[ "value" ] = 0.f;
1247 case Toolkit::TableView::FILL:
1250 item[ "policy" ] = fillPolicy;
1251 item[ "value" ] = 0.f;
1255 std::ostringstream ss;
1257 map[ ss.str() ] = item;
1261 TableView::~TableView()
1266 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1268 Actor nextFocusableActor;
1270 if ( !currentFocusedActor )
1272 // Nothing is currently focused, so the child in the first cell should be focused.
1273 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1277 Toolkit::TableView::CellPosition position;
1278 if( FindChildPosition( currentFocusedActor, position ) )
1280 // The current focused actor is a child of TableView
1281 bool focusLost = false;
1282 int currentRow = position.rowIndex;
1283 int currentColumn = position.columnIndex;
1284 int numberOfColumns = GetColumns();
1285 int numberOfRows = GetRows();
1287 switch ( direction )
1289 case Toolkit::Control::KeyboardFocus::LEFT:
1291 if(--currentColumn < 0)
1293 currentColumn = numberOfColumns - 1;
1294 if(--currentRow < 0)
1296 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1297 focusLost = (currentRow == 0);
1302 case Toolkit::Control::KeyboardFocus::RIGHT:
1304 if(++currentColumn > numberOfColumns - 1)
1307 if(++currentRow > numberOfRows - 1)
1309 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1310 focusLost = (currentRow == numberOfRows - 1);
1315 case Toolkit::Control::KeyboardFocus::UP:
1317 if(--currentRow < 0)
1319 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1320 focusLost = (currentRow == 0);
1324 case Toolkit::Control::KeyboardFocus::DOWN:
1327 if(++currentRow > numberOfRows - 1)
1329 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1330 focusLost = (currentRow == numberOfRows - 1);
1336 // Move the focus if we haven't lost it.
1339 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1344 // The current focused actor is not within table view, so the child in the first cell should be focused.
1345 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1349 return nextFocusableActor;
1352 Vector3 TableView::GetNaturalSize()
1354 // Natural size is the size of all fixed cell widths or heights. This ignores cells with relative heights.
1355 return Vector3( mFixedTotals.width, mFixedTotals.height, 1.0f );
1358 float TableView::CalculateChildSize( const Actor& child, Dimension::Type dimension )
1360 CalculateRowColumnData();
1362 const unsigned int rowCount = mCellData.GetRows();
1363 const unsigned int columnCount = mCellData.GetColumns();
1365 for( unsigned int row = 0; row < rowCount; ++row )
1367 for( unsigned int column = 0; column < columnCount; ++column )
1369 // check if this cell has an actor
1370 Actor& actor = mCellData[ row ][ column ].actor;
1372 if( actor && ( actor == child ) )
1374 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
1376 // If there is an actor and this is the main cell of the actor.
1377 // An actor can be in multiple cells if its row or columnspan is more than 1.
1378 if ( ( position.rowIndex == row ) && ( position.columnIndex == column ) )
1382 case Dimension::WIDTH:
1384 float cellSize = 0.0f;
1386 // Accumulate the width
1387 for( unsigned int i = 0; i < position.columnSpan; ++i )
1389 DALI_ASSERT_DEBUG( column + i < mColumnData.Size() );
1390 cellSize += mColumnData[ column + i ].size;
1394 cellSize -= mPadding.width * 2.0f;
1395 if( cellSize < 0.0f )
1403 case Dimension::HEIGHT:
1405 float cellSize = 0.0f;
1407 // Accumulate the height
1408 for( unsigned int i = 0; i < position.rowSpan; ++i )
1410 DALI_ASSERT_DEBUG( row + i < mRowData.Size() );
1411 cellSize += mRowData[ row + i ].size;
1415 cellSize -= mPadding.height * 2.0f;
1416 if( cellSize < 0.0f )
1434 return 0.0f; // Child not found
1437 bool TableView::RelayoutDependentOnChildren( Dimension::Type dimension )
1439 if ( Control::RelayoutDependentOnChildren( dimension ) )
1444 return FindFit( mRowData ) || FindFit( mColumnData );
1447 void TableView::SetCellAlignment( Toolkit::TableView::CellPosition position, HorizontalAlignment::Type horizontal, VerticalAlignment::Type vertical )
1449 // Check if we need to expand our data array
1450 if( position.rowIndex >= mCellData.GetRows() )
1452 // Only adding new rows
1453 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
1456 if( position.columnIndex >= mCellData.GetColumns() )
1458 // Only adding new columns
1459 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
1462 // Set the alignment of the cell
1463 CellData& data = mCellData[ position.rowIndex ][ position.columnIndex ];
1464 data.horizontalAlignment = horizontal;
1465 data.verticalAlignment = vertical;
1468 void TableView::ComputeRelativeSizes( RowColumnArray& data )
1470 // First pass: Count number of fill entries and calculate used relative space
1471 Dali::Vector< RowColumnData* > fillData;
1472 float relativeTotal = 0.0f;
1474 const unsigned int dataCount = data.Size();
1476 for( unsigned int i = 0; i < dataCount; ++i )
1478 RowColumnData& dataInstance = data[ i ];
1480 if( dataInstance.sizePolicy == Toolkit::TableView::RELATIVE )
1482 relativeTotal += dataInstance.fillRatio;
1484 else if(dataInstance.sizePolicy == Toolkit::TableView::FILL)
1486 fillData.PushBack( &dataInstance );
1490 // Second pass: Distribute remaining relative space
1491 const unsigned int fillCount = fillData.Size();
1494 if( relativeTotal > 1.0f )
1496 relativeTotal = 1.0f;
1499 const float evenFillRatio = (1.0f - relativeTotal ) / fillCount;
1501 for( unsigned int i = 0; i < fillCount; ++i )
1503 fillData[ i ]->fillRatio = evenFillRatio;
1508 float TableView::CalculateTotalFixedSize( const RowColumnArray& data )
1510 float totalSize = 0.0f;
1512 const unsigned int dataCount = data.Size();
1514 for( unsigned int i = 0; i < dataCount; ++i )
1516 const RowColumnData& dataInstance = data[ i ];
1518 switch( dataInstance.sizePolicy )
1520 // we have absolute size to FIXED and FIT column/row and relative size for RELATIVE and FILL column/row
1521 case Toolkit::TableView::FIXED:
1522 case Toolkit::TableView::FIT:
1524 totalSize += dataInstance.size;
1538 Vector2 TableView::GetCellPadding( Dimension::Type dimension )
1542 case Dimension::WIDTH:
1544 return Vector2( mPadding.x, mPadding.x );
1546 case Dimension::HEIGHT:
1548 return Vector2( mPadding.y, mPadding.y );
1559 void TableView::CalculateFixedSizes( RowColumnArray& data, Dimension::Type dimension )
1561 Vector2 cellPadding = GetCellPadding( dimension );
1563 const unsigned int dataCount = data.Size();
1565 for( unsigned int i = 0; i < dataCount; ++i )
1567 RowColumnData& dataInstance = data[ i ];
1569 if( dataInstance.sizePolicy == Toolkit::TableView::FIT )
1571 // Find the size of the biggest actor in the row or column
1572 float maxActorHeight = 0.0f;
1574 unsigned int fitCount = ( dimension == Dimension::WIDTH ) ? mCellData.GetRows() : mCellData.GetColumns();
1576 for( unsigned int j = 0; j < fitCount; ++j )
1578 unsigned int row = ( dimension == Dimension::WIDTH ) ? j : i;
1579 unsigned int column = ( dimension == Dimension::WIDTH ) ? i : j;
1580 DALI_ASSERT_DEBUG( row < mCellData.GetRows() );
1581 DALI_ASSERT_DEBUG( column < mCellData.GetColumns() );
1583 const CellData& cellData = mCellData[ row ][ column ];
1584 const Actor& actor = cellData.actor;
1587 if( FitToChild( actor, dimension ) && ( dimension == Dimension::WIDTH ) ? ( cellData.position.columnSpan == 1 ) : ( cellData.position.rowSpan == 1 ) )
1589 maxActorHeight = std::max( maxActorHeight, actor.GetRelayoutSize( dimension ) + cellPadding.x + cellPadding.y );
1594 dataInstance.size = maxActorHeight;
1599 void TableView::CalculateRelativeSizes( RowColumnArray& data, float size )
1601 const unsigned int dataCount = data.Size();
1603 for( unsigned int i = 0; i < dataCount; ++i )
1605 RowColumnData& dataInstance = data[ i ];
1607 if( dataInstance.sizePolicy == Toolkit::TableView::FILL || dataInstance.sizePolicy == Toolkit::TableView::RELATIVE)
1609 dataInstance.size = dataInstance.fillRatio * size;
1614 bool TableView::FindFit( const RowColumnArray& data )
1616 for( unsigned int i = 0, count = data.Size(); i < count; ++i )
1618 if( data[ i ].sizePolicy == Toolkit::TableView::FIT )
1627 } // namespace Internal
1629 } // namespace Toolkit