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>
36 * @brief Should the tableview fit around the given actor
38 * @param[in] actor The child actor to test against
39 * @param[dimension] The dimnesion to test against
41 bool FitToChild( Actor actor, Dimension::Type dimension )
43 return actor.GetResizePolicy( dimension ) != ResizePolicy::FILL_TO_PARENT && actor.GetRelayoutSize( dimension ) > 0.0f;
46 #if defined(DEBUG_ENABLED)
47 // debugging support, very useful when new features are added or bugs are hunted down
48 // currently not called from code so compiler will optimize these away, kept here for future debugging
50 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
51 #define TV_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
52 //#define TABLEVIEW_DEBUG 1
54 #if defined(TABLEVIEW_DEBUG)
55 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
57 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
59 for( unsigned int i = 0; i < array.GetRows(); ++i )
61 for( unsigned int j = 0; j < array.GetColumns(); ++j )
63 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
65 std::string actorName;
69 actorName = data.actor.GetName();
71 TV_LOG("Array[%d,%d]=%c %s %d,%d,%d,%d ", i, j, actor, actorName.c_str(),
72 data.position.rowIndex, data.position.columnIndex,
73 data.position.rowSpan, data.position.columnSpan );
79 // debugging support, very useful when new features are added or bugs are hunted down
80 // currently not called from code so compiler will optimize these away, kept here for future debugging
81 void PrintArray( Array2d<Size>& array )
83 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
85 for( unsigned int i = 0; i < array.GetRows(); ++i )
87 for( unsigned int j = 0; j < array.GetColumns(); ++j )
89 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
94 // debugging support, very useful when new features are added or bugs are hunted down
95 // currently not called from code so compiler will optimize these away, kept here for future debugging
96 void PrintVector( std::vector<float>& array )
98 TV_LOG( "vector, size [%d]\n", array.size() );
100 for( unsigned int i = 0; i < array.size(); ++i )
102 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
106 #endif // defined(TABLEVIEW_DEBUG)
107 #endif // defined(DEBUG_ENABLED)
126 return Toolkit::TableView::New( 0, 0 );
129 // Setup properties, signals and actions using the type-registry.
130 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TableView, Toolkit::Control, Create );
132 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "rows", UNSIGNED_INTEGER, ROWS )
133 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "columns", UNSIGNED_INTEGER, COLUMNS )
134 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "cell-padding", VECTOR2, CELL_PADDING )
135 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layout-rows", MAP, LAYOUT_ROWS )
136 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layout-columns", MAP, LAYOUT_COLUMNS )
138 DALI_TYPE_REGISTRATION_END()
140 const Scripting::StringEnum LAYOUT_POLICY_STRING_TABLE[] =
142 { "fixed", Toolkit::TableView::FIXED },
143 { "relative", Toolkit::TableView::RELATIVE },
144 { "fill", Toolkit::TableView::FILL }
147 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
149 } // Unnamed namespace
151 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
153 // Create the implementation, temporarily owned by this handle on stack
154 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
156 // Pass ownership to CustomActor handle
157 Toolkit::TableView handle( *impl );
159 // Second-phase init of the implementation
160 // This can only be done after the CustomActor connection has been made...
166 bool TableView::AddChild( Actor& child, const Toolkit::TableView::CellPosition& position )
168 // check that the child is valid
169 DALI_ASSERT_ALWAYS( child );
171 // if child is already parented, we adopt it
172 if( child.GetParent() )
174 child.GetParent().Remove( child );
177 // check if we need to expand our data array
178 if( position.rowIndex >= mCellData.GetRows() )
180 // only adding new rows
181 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
184 if( position.columnIndex >= mCellData.GetColumns() )
186 // only adding new columns
187 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
190 // check if there already is something in this cell
191 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
193 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
196 RelayoutingLock lock( *this );
200 // if child spans multiple rows of columns
201 if( ( position.rowSpan > 1 ) && ( position.rowIndex + position.rowSpan > mCellData.GetRows() ) )
203 // increase table size for the full span, only increasing rows
204 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
207 if( ( position.columnSpan > 1 ) && ( position.columnIndex + position.columnSpan > mCellData.GetColumns() ) )
209 // increase table size for the full span, only increasing columns
210 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
213 // Fill in all cells that need the data
216 data.position = position;
218 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
220 // store same information to all cells, this way we can identify
221 // if a cell is the prime location of an actor or a spanned one
222 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
224 // store same information to all cells, this way we can identify
225 // if a cell is the prime location of an actor or a spanned one
226 mCellData[ row ][ column ] = data;
230 // Relayout the whole table
233 return true; // Addition successful
236 Actor TableView::GetChildAt( const Toolkit::TableView::CellPosition& position )
238 if( ( position.rowIndex < mCellData.GetRows() ) && ( position.columnIndex < mCellData.GetColumns() ) )
240 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
243 // Return an empty handle
247 Actor TableView::RemoveChildAt( const Toolkit::TableView::CellPosition& position )
249 // get the child handle
250 Actor child = GetChildAt( position );
251 // if no real actor there, nothing else to be done
254 RelayoutingLock lock( *this );
255 // Remove the child, this will trigger a call to OnControlChildRemove
256 Self().Remove( child );
258 // relayout the table only if instances were found
259 if( RemoveAllInstances( child ) )
264 // return the child back to caller
268 bool TableView::FindChildPosition( const Actor& child, Toolkit::TableView::CellPosition& positionOut )
270 // Only find valid child actors
273 // Walk through the layout data
274 const unsigned int rowCount = mCellData.GetRows();
275 const unsigned int columnCount = mCellData.GetColumns();
277 for( unsigned int row = 0; row < rowCount; ++row )
279 for( unsigned int column = 0; column < columnCount; ++column )
281 if( mCellData[ row ][ column ].actor == child )
283 positionOut = mCellData[ row ][ column ].position;
293 void TableView::InsertRow( unsigned int rowIndex )
295 RelayoutingLock lock( *this );
297 mCellData.InsertRow( rowIndex );
299 // Need to update the cell infos for the items that moved
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 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
309 // If cell is spanning and above and spans to inserted row
310 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
311 ( position.rowIndex + position.rowSpan > rowIndex ) )
316 // Copy cell to occupy the new column
317 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
319 else if( row > rowIndex ) // If below of inserted row, increase row index
327 // Expand row data array
328 mRowData.Insert( mRowData.Begin() + rowIndex, RowColumnData() );
330 // Sizes may have changed, so relayout
331 mRowColumnDirty = true;
335 void TableView::DeleteRow( unsigned int rowIndex )
337 std::vector< Actor > ignored;
338 DeleteRow( rowIndex, ignored );
341 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
343 RelayoutingLock lock( *this );
346 std::vector< CellData > lost;
347 mCellData.DeleteRow( rowIndex, lost );
349 // Need to update the cell infos for the items that moved
350 const unsigned int rowCount = mCellData.GetRows();
351 const unsigned int columnCount = mCellData.GetColumns();
353 for( unsigned int row = 0; row < rowCount; ++row )
355 for( unsigned int column = 0; column < columnCount; ++column )
357 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
359 // If cell is spanning and above and spans to deleted row
360 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
361 ( position.rowIndex + position.rowSpan > rowIndex ) )
364 if( position.rowSpan > 1 )
369 else if( row >= rowIndex ) // If below of or at the inserted row, decrease row index
372 if( position.rowIndex > 1 )
380 // 1 row removed, 0 columns
381 RemoveAndGetLostActors( lost, removed, 1u, 0u );
383 // Contract row data array
384 mRowData.Erase( mRowData.Begin() + rowIndex );
386 // Sizes may have changed, so relayout
387 mRowColumnDirty = true;
391 void TableView::InsertColumn( unsigned int columnIndex )
393 RelayoutingLock lock( *this );
395 // Insert the new column
396 mCellData.InsertColumn( columnIndex );
398 // Need to update the cell infos for the items that moved
399 const unsigned int rowCount = mCellData.GetRows();
400 const unsigned int columnCount = mCellData.GetColumns();
402 for( unsigned int row = 0; row < rowCount; ++row )
404 for( unsigned int column = 0; column < columnCount; ++column )
406 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
408 // If cell is spanning and left side and spans to inserted column
409 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
410 ( position.columnIndex + position.columnSpan > columnIndex ) )
413 position.columnSpan++;
415 // Copy cell to occupy the new column
416 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
418 else if( column > columnIndex ) // If on the right side of inserted column, increase column index
421 position.columnIndex++;
426 // Expand column data array
427 mColumnData.Insert( mColumnData.Begin() + columnIndex, RowColumnData() );
429 // Sizes may have changed so relayout
430 mRowColumnDirty = true;
434 void TableView::DeleteColumn( unsigned int columnIndex )
436 std::vector< Actor > ignored;
437 DeleteColumn( columnIndex, ignored );
440 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
442 RelayoutingLock lock( *this );
445 std::vector< CellData > lost;
446 mCellData.DeleteColumn( columnIndex, lost );
448 // Need to update the cell infos for the items that moved
449 const unsigned int rowCount = mCellData.GetRows();
450 const unsigned int columnCount = mCellData.GetColumns();
452 for( unsigned int row = 0; row < rowCount; ++row )
454 for( unsigned int column = 0; column < columnCount; ++column )
456 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
458 // If cell is spanning and left side and spans to inserted column
459 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
460 ( position.columnIndex + position.columnSpan > columnIndex ) )
463 if( position.columnSpan > 1 )
465 position.columnSpan--;
468 else if( column >= columnIndex ) // If on the right side of or at the inserted column, decrease column index
471 if( position.columnIndex > 0 )
473 position.columnIndex--;
479 // 0 rows, 1 column removed
480 RemoveAndGetLostActors( lost, removed, 0u, 1u );
482 // Contract column data array
483 mColumnData.Erase( mColumnData.Begin() + columnIndex );
485 // Size may have changed so relayout
486 mRowColumnDirty = true;
490 void TableView::Resize( unsigned int rows, unsigned int columns )
492 std::vector< Actor > ignored;
493 Resize( rows, columns, ignored );
496 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
498 RelayoutingLock lock( *this );
500 unsigned int oldRows = GetRows();
501 unsigned int oldColumns = GetColumns();
504 std::vector< CellData > lost;
505 ResizeContainers( rows, columns, lost );
507 // Calculate if we lost rows
508 unsigned int rowsRemoved = 0;
509 unsigned int newRows = GetRows();
511 if( oldRows < newRows )
513 rowsRemoved = newRows - oldRows;
516 // Calculate if we lost columns
517 unsigned int columnsRemoved = 0;
518 unsigned int newColumns = GetColumns();
519 if( oldColumns < newColumns )
521 rowsRemoved = newColumns - oldColumns;
524 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
526 // Sizes may have changed so request a relayout
527 mRowColumnDirty = true;
531 void TableView::SetCellPadding( Size padding )
533 // If padding really changed
534 if( padding != mPadding )
542 Size TableView::GetCellPadding()
547 void TableView::SetRowPolicy( unsigned int rowIndex, CellSizePolicy policy )
549 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
551 if( mRowData[ rowIndex ].sizePolicy != policy )
553 mRowData[ rowIndex ].sizePolicy = policy;
555 mRowColumnDirty = true;
560 TableView::CellSizePolicy TableView::GetRowPolicy( unsigned int rowIndex ) const
562 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
564 return mRowData[ rowIndex ].sizePolicy;
567 void TableView::SetColumnPolicy( unsigned int columnIndex, CellSizePolicy policy )
569 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
571 if( mColumnData[ columnIndex ].sizePolicy != policy )
573 mColumnData[ columnIndex ].sizePolicy = policy;
575 mRowColumnDirty = true;
580 TableView::CellSizePolicy TableView::GetColumnPolicy( unsigned int columnIndex ) const
582 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
584 return mColumnData[ columnIndex ].sizePolicy;
587 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
589 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
591 RowColumnData& data = mRowData[ rowIndex ];
593 data.sizePolicy = FIXED;
595 mRowColumnDirty = true;
599 float TableView::GetFixedHeight( unsigned int rowIndex ) const
601 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
603 return mRowData[ rowIndex ].size;
606 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
608 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
610 RowColumnData& data = mColumnData[ columnIndex ];
612 data.sizePolicy = FIXED;
614 mRowColumnDirty = true;
618 float TableView::GetFixedWidth( unsigned int columnIndex ) const
620 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
622 return mColumnData[ columnIndex ].size;
625 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
627 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
629 RowColumnData& data = mRowData[ rowIndex ];
630 data.fillRatio = heightPercentage;
631 data.userFillRatio = true;
632 data.sizePolicy = FILL;
634 mRowColumnDirty = true;
638 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
640 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
642 return mRowData[ rowIndex ].fillRatio;
645 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
647 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
649 RowColumnData& data = mColumnData[ columnIndex ];
650 data.fillRatio = widthPercentage;
651 data.userFillRatio = true;
652 data.sizePolicy = FILL;
654 mRowColumnDirty = true;
658 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
660 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
662 return mColumnData[ columnIndex ].fillRatio;
665 void TableView::CalculateRowColumnData()
667 // Calculate the relative sizes
668 if( mRowColumnDirty )
670 ComputeRelativeSizes( mRowData );
671 ComputeRelativeSizes( mColumnData );
673 mRowColumnDirty = false;
677 void TableView::OnCalculateRelayoutSize( Dimension::Type dimension )
679 CalculateRowColumnData();
681 if( dimension & Dimension::WIDTH )
683 CalculateFixedSizes( mColumnData, Dimension::WIDTH );
684 mFixedTotals.width = CalculateTotalFixedSize( mColumnData );
687 if( dimension & Dimension::HEIGHT )
689 CalculateFixedSizes( mRowData, Dimension::HEIGHT );
690 mFixedTotals.height = CalculateTotalFixedSize( mRowData );
694 void TableView::OnLayoutNegotiated( float size, Dimension::Type dimension )
696 CalculateRowColumnData();
698 // Calculate the value of all relative sized rows and columns
699 if( dimension & Dimension::WIDTH )
701 float remainingSize = size - mFixedTotals.width;
702 if( remainingSize < 0.0f )
704 remainingSize = 0.0f;
707 CalculateRelativeSizes( mColumnData, remainingSize );
710 if( dimension & Dimension::HEIGHT )
712 float remainingSize = size - mFixedTotals.height;
713 if( remainingSize < 0.0f )
715 remainingSize = 0.0f;
718 CalculateRelativeSizes( mRowData, remainingSize );
722 void TableView::OnRelayout( const Vector2& size, RelayoutContainer& container )
724 CalculateRowColumnData();
726 // Go through the layout data
727 float cumulatedHeight = 0.0f;
729 const unsigned int rowCount = mCellData.GetRows();
730 const unsigned int columnCount = mCellData.GetColumns();
732 for( unsigned int row = 0; row < rowCount; ++row )
734 float cumulatedWidth = 0.0f;
736 for( unsigned int column = 0; column < columnCount; ++column )
738 Actor& actor = mCellData[ row ][ column ].actor;
739 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
741 // If there is an actor and this is the main cell of the actor.
742 // An actor can be in multiple cells if its row or columnspan is more than 1.
743 // We however must lay out each actor only once.
744 if( actor && ( position.rowIndex == row ) && ( position.columnIndex == column ) )
746 // Anchor actor to top left of table view
747 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
748 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
751 actor.GetPadding( padding );
753 Vector3 actorPosition( cumulatedWidth + mPadding.width + padding.left, // Left padding
754 cumulatedHeight + mPadding.height + padding.top, // Top padding
756 actor.SetPosition( actorPosition );
759 DALI_ASSERT_DEBUG( column < mColumnData.Size() );
760 cumulatedWidth += mColumnData[ column ].size;
763 DALI_ASSERT_DEBUG( row < mRowData.Size() );
764 cumulatedHeight += mRowData[ row ].size;
768 unsigned int TableView::GetRows()
770 return mCellData.GetRows();
773 unsigned int TableView::GetColumns()
775 return mCellData.GetColumns();
778 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
780 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
784 TableView& tableViewImpl( GetImpl( tableView ) );
787 case Toolkit::TableView::Property::ROWS:
789 if( value.Get<unsigned int>() != tableViewImpl.GetRows() )
791 tableViewImpl.Resize( value.Get<unsigned int>(), tableViewImpl.GetColumns() );
795 case Toolkit::TableView::Property::COLUMNS:
797 if( value.Get<unsigned int>() != tableViewImpl.GetColumns() )
799 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<unsigned int>() );
803 case Toolkit::TableView::Property::CELL_PADDING:
805 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
808 case Toolkit::TableView::Property::LAYOUT_ROWS:
810 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, value );
813 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
815 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, value );
822 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
824 Property::Value value;
826 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
830 TableView& tableViewImpl( GetImpl( tableView ) );
833 case Toolkit::TableView::Property::ROWS:
835 value = tableViewImpl.GetRows();
838 case Toolkit::TableView::Property::COLUMNS:
840 value = tableViewImpl.GetColumns();
843 case Toolkit::TableView::Property::CELL_PADDING:
845 value = tableViewImpl.GetCellPadding();
848 case Toolkit::TableView::Property::LAYOUT_ROWS:
850 value = tableViewImpl.GetRowHeightsPropertyValue();
853 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
855 value = tableViewImpl.GetColumnWidthsPropertyValue();
864 void TableView::OnControlChildAdd( Actor& child )
866 if( mLayoutingChild )
868 // we're in the middle of laying out children so no point doing anything here
874 // Test properties on actor
875 Toolkit::TableView::CellPosition cellPosition;
876 if( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
878 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
881 if( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
883 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
886 if( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) != Property::INVALID_INDEX )
888 Vector2 indices = child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) ).Get<Vector2 >();
889 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
890 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
892 AddChild( child, cellPosition );
898 // Find the first available cell to store the actor in
899 const unsigned int rowCount = mCellData.GetRows();
900 const unsigned int columnCount = mCellData.GetColumns();
901 for( unsigned int row = 0; row < rowCount; ++row )
903 for( unsigned int column = 0; column < columnCount; ++column )
905 if( !(mCellData[ row ][ column ].actor) )
907 // Put the actor in the cell
910 data.position.columnIndex = column;
911 data.position.rowIndex = row;
912 mCellData[ row ][ column ] = data;
920 // No empty cells, so increase size of the table
921 unsigned int newColumnCount = ( columnCount > 0 ) ? columnCount : 1;
922 ResizeContainers( rowCount + 1, newColumnCount );
924 // Put the actor in the first cell of the new row
927 data.position.rowIndex = rowCount;
928 data.position.columnIndex = 0;
929 mCellData[ rowCount ][ 0 ] = data;
932 void TableView::OnControlChildRemove( Actor& child )
934 // dont process if we're in the middle of bigger operation like delete row, column or resize
935 if( !mLayoutingChild )
937 // relayout the table only if instances were found
938 if( RemoveAllInstances( child ) )
945 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
946 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
947 mCellData( initialRows, initialColumns ),
948 mLayoutingChild( false ),
949 mRowColumnDirty( true ) // Force recalculation first time
951 SetKeyboardNavigationSupport( true );
952 ResizeContainers( initialRows, initialColumns );
955 void TableView::OnInitialize()
957 // Make self as keyboard focusable and focus group
959 self.SetKeyboardFocusable(true);
960 SetAsKeyboardFocusGroup(true);
963 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
965 std::vector<CellData> ignored;
966 ResizeContainers( rows, columns, ignored );
969 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
972 mCellData.Resize( rows, columns, removed );
974 // We don't care if these go smaller, data will be regenerated or is not needed anymore
975 mRowData.Resize( rows );
976 mColumnData.Resize( columns );
979 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
980 unsigned int rowsRemoved, unsigned int columnsRemoved )
982 // iterate through all lost cells
983 std::vector< CellData >::const_iterator iter = lost.begin();
984 for( ; iter != lost.end(); ++iter )
986 // if it is a valid actor
989 // is this actor still somewhere else in the table
990 Toolkit::TableView::CellPosition position;
991 if( FindChildPosition( (*iter).actor, position ) )
993 // it must be spanning multiple cells, position contains the top left most one
994 // check if position is left of the removed location
995 if( position.columnIndex < (*iter).position.columnIndex )
997 // if column span is greater than 1
998 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1000 // decrease column span
1001 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1004 // check if position is left of the removed location
1005 if( position.rowIndex < (*iter).position.rowIndex )
1007 // if row span is greater than 1
1008 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1010 // decrease row span
1011 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1017 // this actor is gone for good
1018 // add actor to removed container
1019 removed.push_back( (*iter).actor );
1020 // we dont want the child actor anymore
1021 Self().Remove( (*iter).actor );
1027 bool TableView::RemoveAllInstances( const Actor& child )
1030 // walk through the layout data
1031 const unsigned int rowCount = mCellData.GetRows();
1032 const unsigned int columnCount = mCellData.GetColumns();
1033 for( unsigned int row = 0; row < rowCount; ++row )
1035 for( unsigned int column = 0; column < columnCount; ++column )
1037 if( mCellData[ row ][ column ].actor == child )
1039 // clear the cell, NOTE that the cell might be spanning multiple cells
1040 mCellData[ row ][ column ] = CellData();
1048 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1049 void(TableView::*funcFixed)(unsigned int, float),
1050 void(TableView::*funcRelative)(unsigned int, float),
1051 const Property::Value& value )
1053 Property::Map* map = value.GetMap();
1056 unsigned int rowIndex(0);
1057 for ( unsigned int i = 0, count = map->Count(); i < count; ++i )
1059 Property::Value& item = map->GetValue(i);
1060 Property::Map* childMap = item.GetMap();
1062 std::istringstream( map->GetKey(i) ) >> rowIndex;
1065 Property::Value* policy = childMap->Find( "policy" );
1066 Property::Value* value = childMap->Find( "value" );
1067 if( policy && value )
1069 std::string policyValue;
1070 policy->Get( policyValue );
1071 Toolkit::TableView::LayoutPolicy policy;
1072 if( Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( policyValue.c_str(),
1073 LAYOUT_POLICY_STRING_TABLE,
1074 LAYOUT_POLICY_STRING_TABLE_COUNT,
1077 if( policy == Toolkit::TableView::FIXED )
1079 (tableViewImpl.*funcFixed)( rowIndex, value->Get<float>() );
1081 else if( policy == Toolkit::TableView::RELATIVE )
1083 (tableViewImpl.*funcRelative)( rowIndex, value->Get<float>() );
1092 Property::Value TableView::GetRowHeightsPropertyValue()
1095 GetMapPropertyValue( mRowData, map);
1096 return Property::Value(map);
1099 Property::Value TableView::GetColumnWidthsPropertyValue()
1102 GetMapPropertyValue( mColumnData, map);
1103 return Property::Value(map);
1106 void TableView::GetMapPropertyValue( const RowColumnArray& data, Property::Map& map )
1108 const char* name = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIXED,
1109 LAYOUT_POLICY_STRING_TABLE,
1110 LAYOUT_POLICY_STRING_TABLE_COUNT );
1111 std::string fixedPolicy;
1116 name = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::RELATIVE,
1117 LAYOUT_POLICY_STRING_TABLE,
1118 LAYOUT_POLICY_STRING_TABLE_COUNT );
1119 std::string relativePolicy;
1122 relativePolicy = name;
1125 const RowColumnArray::SizeType count = data.Size();
1126 for( RowColumnArray::SizeType i = 0; i < count; i++ )
1128 const RowColumnData& dataInstance = data[ i ];
1130 switch( dataInstance.sizePolicy )
1135 item[ "policy" ] = fixedPolicy;
1136 item[ "value" ] = dataInstance.size;
1138 std::ostringstream ss;
1141 map[ ss.str() ] = item;
1149 item[ "policy" ] = relativePolicy;
1150 item[ "value" ] = dataInstance.fillRatio;
1152 std::ostringstream ss;
1155 map[ ss.str() ] = item;
1168 TableView::~TableView()
1173 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1175 Actor nextFocusableActor;
1177 if ( !currentFocusedActor )
1179 // Nothing is currently focused, so the child in the first cell should be focused.
1180 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1184 Toolkit::TableView::CellPosition position;
1185 if( FindChildPosition( currentFocusedActor, position ) )
1187 // The current focused actor is a child of TableView
1188 bool focusLost = false;
1189 int currentRow = position.rowIndex;
1190 int currentColumn = position.columnIndex;
1191 int numberOfColumns = GetColumns();
1192 int numberOfRows = GetRows();
1194 switch ( direction )
1196 case Toolkit::Control::KeyboardFocus::LEFT:
1198 if(--currentColumn < 0)
1200 currentColumn = numberOfColumns - 1;
1201 if(--currentRow < 0)
1203 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1204 focusLost = (currentRow == 0);
1209 case Toolkit::Control::KeyboardFocus::RIGHT:
1211 if(++currentColumn > numberOfColumns - 1)
1214 if(++currentRow > numberOfRows - 1)
1216 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1217 focusLost = (currentRow == numberOfRows - 1);
1222 case Toolkit::Control::KeyboardFocus::UP:
1224 if(--currentRow < 0)
1226 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1227 focusLost = (currentRow == 0);
1231 case Toolkit::Control::KeyboardFocus::DOWN:
1234 if(++currentRow > numberOfRows - 1)
1236 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1237 focusLost = (currentRow == numberOfRows - 1);
1243 // Move the focus if we haven't lost it.
1246 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1251 // The current focused actor is not within table view, so the child in the first cell should be focused.
1252 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1256 return nextFocusableActor;
1259 Vector3 TableView::GetNaturalSize()
1261 // Natural size is the size of all fixed cell widths or heights. This ignores cells with relative heights.
1262 return Vector3( mFixedTotals.width, mFixedTotals.height, 1.0f );
1265 float TableView::CalculateChildSize( const Actor& child, Dimension::Type dimension )
1267 CalculateRowColumnData();
1269 const unsigned int rowCount = mCellData.GetRows();
1270 const unsigned int columnCount = mCellData.GetColumns();
1272 for( unsigned int row = 0; row < rowCount; ++row )
1274 for( unsigned int column = 0; column < columnCount; ++column )
1276 // check if this cell has an actor
1277 Actor& actor = mCellData[ row ][ column ].actor;
1279 if( actor && ( actor == child ) )
1281 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
1283 // If there is an actor and this is the main cell of the actor.
1284 // An actor can be in multiple cells if its row or columnspan is more than 1.
1285 if ( ( position.rowIndex == row ) && ( position.columnIndex == column ) )
1289 case Dimension::WIDTH:
1291 float cellSize = 0.0f;
1293 // Accumulate the width
1294 for( unsigned int i = 0; i < position.columnSpan; ++i )
1296 DALI_ASSERT_DEBUG( column + i < mColumnData.Size() );
1297 cellSize += mColumnData[ column + i ].size;
1301 cellSize -= mPadding.width * 2.0f;
1302 if( cellSize < 0.0f )
1310 case Dimension::HEIGHT:
1312 float cellSize = 0.0f;
1314 // Accumulate the height
1315 for( unsigned int i = 0; i < position.rowSpan; ++i )
1317 DALI_ASSERT_DEBUG( row + i < mRowData.Size() );
1318 cellSize += mRowData[ row + i ].size;
1322 cellSize -= mPadding.width * 2.0f;
1323 if( cellSize < 0.0f )
1341 return 0.0f; // Child not found
1344 bool TableView::RelayoutDependentOnChildren( Dimension::Type dimension )
1346 if ( Control::RelayoutDependentOnChildren( dimension ) )
1351 return FindFit( mRowData ) || FindFit( mColumnData );
1354 void TableView::SetCellAlignment( Toolkit::TableView::CellPosition position, HorizontalAlignment::Type horizontal, VerticalAlignment::Type vertical )
1356 // Check if we need to expand our data array
1357 if( position.rowIndex >= mCellData.GetRows() )
1359 // Only adding new rows
1360 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
1363 if( position.columnIndex >= mCellData.GetColumns() )
1365 // Only adding new columns
1366 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
1369 // Set the alignment of the cell
1370 CellData& data = mCellData[ position.rowIndex ][ position.columnIndex ];
1371 data.horizontalAlignment = horizontal;
1372 data.verticalAlignment = vertical;
1375 void TableView::ComputeRelativeSizes( RowColumnArray& data )
1377 // First pass: Count number of fill entries and calculate used relative space
1378 Dali::Vector< RowColumnData* > fillData;
1379 float relativeTotal = 0.0f;
1381 const unsigned int dataCount = data.Size();
1383 for( unsigned int i = 0; i < dataCount; ++i )
1385 RowColumnData& dataInstance = data[ i ];
1387 if( dataInstance.sizePolicy == FILL )
1389 if( dataInstance.userFillRatio )
1391 relativeTotal += dataInstance.fillRatio;
1395 fillData.PushBack( &dataInstance );
1400 // Second pass: Distribute remaining relative space
1401 const unsigned int fillCount = fillData.Size();
1404 if( relativeTotal > 1.0f )
1406 relativeTotal = 1.0f;
1409 const float evenFillRatio = (1.0f - relativeTotal ) / fillCount;
1411 for( unsigned int i = 0; i < fillCount; ++i )
1413 fillData[ i ]->fillRatio = evenFillRatio;
1418 float TableView::CalculateTotalFixedSize( const RowColumnArray& data )
1420 float totalSize = 0.0f;
1422 const unsigned int dataCount = data.Size();
1424 for( unsigned int i = 0; i < dataCount; ++i )
1426 const RowColumnData& dataInstance = data[ i ];
1428 switch( dataInstance.sizePolicy )
1433 totalSize += dataInstance.size;
1447 Vector2 TableView::GetCellPadding( Dimension::Type dimension )
1451 case Dimension::WIDTH:
1453 return Vector2( mPadding.x, mPadding.x );
1455 case Dimension::HEIGHT:
1457 return Vector2( mPadding.y, mPadding.y );
1468 void TableView::CalculateFixedSizes( RowColumnArray& data, Dimension::Type dimension )
1470 Vector2 cellPadding = GetCellPadding( dimension );
1472 const unsigned int dataCount = data.Size();
1474 for( unsigned int i = 0; i < dataCount; ++i )
1476 RowColumnData& dataInstance = data[ i ];
1478 if( dataInstance.sizePolicy == FIT )
1480 // Find the size of the biggest actor in the row or column
1481 float maxActorHeight = 0.0f;
1483 unsigned int fitCount = ( dimension == Dimension::WIDTH ) ? mCellData.GetRows() : mCellData.GetColumns();
1485 for( unsigned int j = 0; j < fitCount; ++j )
1487 unsigned int row = ( dimension == Dimension::WIDTH ) ? j : i;
1488 unsigned int column = ( dimension == Dimension::WIDTH ) ? i : j;
1489 DALI_ASSERT_DEBUG( row < mCellData.GetRows() );
1490 DALI_ASSERT_DEBUG( column < mCellData.GetColumns() );
1492 const CellData& cellData = mCellData[ row ][ column ];
1493 const Actor& actor = cellData.actor;
1496 if( FitToChild( actor, dimension ) && ( dimension == Dimension::WIDTH ) ? ( cellData.position.columnSpan == 1 ) : ( cellData.position.rowSpan == 1 ) )
1498 maxActorHeight = std::max( maxActorHeight, actor.GetRelayoutSize( dimension ) + cellPadding.x + cellPadding.y );
1503 dataInstance.size = maxActorHeight;
1508 void TableView::CalculateRelativeSizes( RowColumnArray& data, float size )
1510 const unsigned int dataCount = data.Size();
1512 for( unsigned int i = 0; i < dataCount; ++i )
1514 RowColumnData& dataInstance = data[ i ];
1516 if( dataInstance.sizePolicy == FILL )
1518 dataInstance.size = dataInstance.fillRatio * size;
1523 bool TableView::FindFit( const RowColumnArray& data )
1525 for( unsigned int i = 0, count = data.Size(); i < count; ++i )
1527 if( data[ i ].sizePolicy == FIT )
1536 } // namespace Internal
1538 } // namespace Toolkit