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/public-api/object/type-registry-helper.h>
26 #include <dali/public-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)
53 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
55 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
57 for( unsigned int i = 0; i < array.GetRows(); ++i )
59 for( unsigned int j = 0; j < array.GetColumns(); ++j )
61 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
63 std::string actorName;
67 actorName = data.actor.GetName();
69 TV_LOG("Array[%d,%d]=%c %s %d,%d,%d,%d ", i, j, actor, actorName.c_str(),
70 data.position.rowIndex, data.position.columnIndex,
71 data.position.rowSpan, data.position.columnSpan );
77 // debugging support, very useful when new features are added or bugs are hunted down
78 // currently not called from code so compiler will optimize these away, kept here for future debugging
79 void PrintArray( Array2d<Size>& array )
81 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
83 for( unsigned int i = 0; i < array.GetRows(); ++i )
85 for( unsigned int j = 0; j < array.GetColumns(); ++j )
87 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
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 PrintVector( std::vector<float>& array )
96 TV_LOG( "vector, size [%d]\n", array.size() );
98 for( unsigned int i = 0; i < array.size(); ++i )
100 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
104 #endif // defined(DEBUG_ENABLED)
123 return Toolkit::TableView::New( 0, 0 );
126 // Setup properties, signals and actions using the type-registry.
127 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TableView, Toolkit::Control, Create );
129 DALI_PROPERTY_REGISTRATION( TableView, "rows", UNSIGNED_INTEGER, ROWS )
130 DALI_PROPERTY_REGISTRATION( TableView, "columns", UNSIGNED_INTEGER, COLUMNS )
131 DALI_PROPERTY_REGISTRATION( TableView, "cell-padding", VECTOR2, CELL_PADDING )
132 DALI_PROPERTY_REGISTRATION( TableView, "layout-rows", MAP, LAYOUT_ROWS )
133 DALI_PROPERTY_REGISTRATION( TableView, "layout-columns", MAP, LAYOUT_COLUMNS )
135 DALI_TYPE_REGISTRATION_END()
137 const Scripting::StringEnum< Toolkit::TableView::LayoutPolicy > LAYOUT_POLICY_STRING_TABLE[] =
139 { "fixed", Toolkit::TableView::FIXED },
140 { "relative", Toolkit::TableView::RELATIVE },
141 { "fill", Toolkit::TableView::FILL }
144 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
146 } // Unnamed namespace
148 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
150 // Create the implementation, temporarily owned by this handle on stack
151 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
153 // Pass ownership to CustomActor handle
154 Toolkit::TableView handle( *impl );
156 // Second-phase init of the implementation
157 // This can only be done after the CustomActor connection has been made...
163 bool TableView::AddChild( Actor& child, const Toolkit::TableView::CellPosition& position )
165 // check that the child is valid
166 DALI_ASSERT_ALWAYS( child );
168 // if child is already parented, we adopt it
169 if( child.GetParent() )
171 child.GetParent().Remove( child );
174 // check if we need to expand our data array
175 if( position.rowIndex >= mCellData.GetRows() )
177 // only adding new rows
178 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
181 if( position.columnIndex >= mCellData.GetColumns() )
183 // only adding new columns
184 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
187 // check if there already is something in this cell
188 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
190 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
193 RelayoutingLock lock( *this );
197 // if child spans multiple rows of columns
198 if( ( position.rowSpan > 1 ) && ( position.rowIndex + position.rowSpan > mCellData.GetRows() ) )
200 // increase table size for the full span, only increasing rows
201 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
204 if( ( position.columnSpan > 1 ) && ( position.columnIndex + position.columnSpan > mCellData.GetColumns() ) )
206 // increase table size for the full span, only increasing columns
207 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
210 // Fill in all cells that need the data
213 data.position = position;
215 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
217 // store same information to all cells, this way we can identify
218 // if a cell is the prime location of an actor or a spanned one
219 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
221 // store same information to all cells, this way we can identify
222 // if a cell is the prime location of an actor or a spanned one
223 mCellData[ row ][ column ] = data;
227 // Relayout the whole table
230 return true; // Addition successful
233 Actor TableView::GetChildAt( const Toolkit::TableView::CellPosition& position )
235 if( ( position.rowIndex < mCellData.GetRows() ) && ( position.columnIndex < mCellData.GetColumns() ) )
237 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
240 // Return an empty handle
244 Actor TableView::RemoveChildAt( const Toolkit::TableView::CellPosition& position )
246 // get the child handle
247 Actor child = GetChildAt( position );
248 // if no real actor there, nothing else to be done
251 RelayoutingLock lock( *this );
252 // Remove the child, this will trigger a call to OnControlChildRemove
253 Self().Remove( child );
255 // relayout the table only if instances were found
256 if( RemoveAllInstances( child ) )
261 // return the child back to caller
265 bool TableView::FindChildPosition( const Actor& child, Toolkit::TableView::CellPosition& positionOut )
267 // Only find valid child actors
270 // Walk through the layout data
271 const unsigned int rowCount = mCellData.GetRows();
272 const unsigned int columnCount = mCellData.GetColumns();
274 for( unsigned int row = 0; row < rowCount; ++row )
276 for( unsigned int column = 0; column < columnCount; ++column )
278 if( mCellData[ row ][ column ].actor == child )
280 positionOut = mCellData[ row ][ column ].position;
290 void TableView::InsertRow( unsigned int rowIndex )
292 RelayoutingLock lock( *this );
294 mCellData.InsertRow( rowIndex );
296 // Need to update the cell infos for the items that moved
297 const unsigned int rowCount = mCellData.GetRows();
298 const unsigned int columnCount = mCellData.GetColumns();
300 for( unsigned int row = 0; row < rowCount; ++row )
302 for( unsigned int column = 0; column < columnCount; ++column )
304 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
306 // If cell is spanning and above and spans to inserted row
307 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
308 ( position.rowIndex + position.rowSpan > rowIndex ) )
313 // Copy cell to occupy the new column
314 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
316 else if( row > rowIndex ) // If below of inserted row, increase row index
324 // Expand row data array
325 mRowData.Insert( mRowData.Begin() + rowIndex, RowColumnData() );
327 // Sizes may have changed, so relayout
328 mRowColumnDirty = true;
332 void TableView::DeleteRow( unsigned int rowIndex )
334 std::vector< Actor > ignored;
335 DeleteRow( rowIndex, ignored );
338 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
340 RelayoutingLock lock( *this );
343 std::vector< CellData > lost;
344 mCellData.DeleteRow( rowIndex, lost );
346 // Need to update the cell infos for the items that moved
347 const unsigned int rowCount = mCellData.GetRows();
348 const unsigned int columnCount = mCellData.GetColumns();
350 for( unsigned int row = 0; row < rowCount; ++row )
352 for( unsigned int column = 0; column < columnCount; ++column )
354 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
356 // If cell is spanning and above and spans to deleted row
357 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
358 ( position.rowIndex + position.rowSpan > rowIndex ) )
361 if( position.rowSpan > 1 )
366 else if( row >= rowIndex ) // If below of or at the inserted row, decrease row index
369 if( position.rowIndex > 1 )
377 // 1 row removed, 0 columns
378 RemoveAndGetLostActors( lost, removed, 1u, 0u );
380 // Contract row data array
381 mRowData.Erase( mRowData.Begin() + rowIndex );
383 // Sizes may have changed, so relayout
384 mRowColumnDirty = true;
388 void TableView::InsertColumn( unsigned int columnIndex )
390 RelayoutingLock lock( *this );
392 // Insert the new column
393 mCellData.InsertColumn( columnIndex );
395 // Need to update the cell infos for the items that moved
396 const unsigned int rowCount = mCellData.GetRows();
397 const unsigned int columnCount = mCellData.GetColumns();
399 for( unsigned int row = 0; row < rowCount; ++row )
401 for( unsigned int column = 0; column < columnCount; ++column )
403 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
405 // If cell is spanning and left side and spans to inserted column
406 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
407 ( position.columnIndex + position.columnSpan > columnIndex ) )
410 position.columnSpan++;
412 // Copy cell to occupy the new column
413 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
415 else if( column > columnIndex ) // If on the right side of inserted column, increase column index
418 position.columnIndex++;
423 // Expand column data array
424 mColumnData.Insert( mColumnData.Begin() + columnIndex, RowColumnData() );
426 // Sizes may have changed so relayout
427 mRowColumnDirty = true;
431 void TableView::DeleteColumn( unsigned int columnIndex )
433 std::vector< Actor > ignored;
434 DeleteColumn( columnIndex, ignored );
437 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
439 RelayoutingLock lock( *this );
442 std::vector< CellData > lost;
443 mCellData.DeleteColumn( columnIndex, lost );
445 // Need to update the cell infos for the items that moved
446 const unsigned int rowCount = mCellData.GetRows();
447 const unsigned int columnCount = mCellData.GetColumns();
449 for( unsigned int row = 0; row < rowCount; ++row )
451 for( unsigned int column = 0; column < columnCount; ++column )
453 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
455 // If cell is spanning and left side and spans to inserted column
456 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
457 ( position.columnIndex + position.columnSpan > columnIndex ) )
460 if( position.columnSpan > 1 )
462 position.columnSpan--;
465 else if( column >= columnIndex ) // If on the right side of or at the inserted column, decrease column index
468 if( position.columnIndex > 0 )
470 position.columnIndex--;
476 // 0 rows, 1 column removed
477 RemoveAndGetLostActors( lost, removed, 0u, 1u );
479 // Contract column data array
480 mColumnData.Erase( mColumnData.Begin() + columnIndex );
482 // Size may have changed so relayout
483 mRowColumnDirty = true;
487 void TableView::Resize( unsigned int rows, unsigned int columns )
489 std::vector< Actor > ignored;
490 Resize( rows, columns, ignored );
493 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
495 RelayoutingLock lock( *this );
497 unsigned int oldRows = GetRows();
498 unsigned int oldColumns = GetColumns();
501 std::vector< CellData > lost;
502 ResizeContainers( rows, columns, lost );
504 // Calculate if we lost rows
505 unsigned int rowsRemoved = 0;
506 unsigned int newRows = GetRows();
508 if( oldRows < newRows )
510 rowsRemoved = newRows - oldRows;
513 // Calculate if we lost columns
514 unsigned int columnsRemoved = 0;
515 unsigned int newColumns = GetColumns();
516 if( oldColumns < newColumns )
518 rowsRemoved = newColumns - oldColumns;
521 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
523 // Sizes may have changed so request a relayout
524 mRowColumnDirty = true;
528 void TableView::SetCellPadding( Size padding )
530 // If padding really changed
531 if( padding != mPadding )
539 Size TableView::GetCellPadding()
544 void TableView::SetRowPolicy( unsigned int rowIndex, CellSizePolicy policy )
546 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
548 if( mRowData[ rowIndex ].sizePolicy != policy )
550 mRowData[ rowIndex ].sizePolicy = policy;
552 mRowColumnDirty = true;
557 TableView::CellSizePolicy TableView::GetRowPolicy( unsigned int rowIndex ) const
559 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
561 return mRowData[ rowIndex ].sizePolicy;
564 void TableView::SetColumnPolicy( unsigned int columnIndex, CellSizePolicy policy )
566 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
568 if( mColumnData[ columnIndex ].sizePolicy != policy )
570 mColumnData[ columnIndex ].sizePolicy = policy;
572 mRowColumnDirty = true;
577 TableView::CellSizePolicy TableView::GetColumnPolicy( unsigned int columnIndex ) const
579 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
581 return mColumnData[ columnIndex ].sizePolicy;
584 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
586 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
588 RowColumnData& data = mRowData[ rowIndex ];
590 data.sizePolicy = FIXED;
592 mRowColumnDirty = true;
596 float TableView::GetFixedHeight( unsigned int rowIndex ) const
598 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
600 return mRowData[ rowIndex ].size;
603 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
605 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
607 RowColumnData& data = mColumnData[ columnIndex ];
609 data.sizePolicy = FIXED;
611 mRowColumnDirty = true;
615 float TableView::GetFixedWidth( unsigned int columnIndex ) const
617 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
619 return mColumnData[ columnIndex ].size;
622 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
624 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
626 RowColumnData& data = mRowData[ rowIndex ];
627 data.fillRatio = heightPercentage;
628 data.userFillRatio = true;
629 data.sizePolicy = FILL;
631 mRowColumnDirty = true;
635 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
637 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
639 return mRowData[ rowIndex ].fillRatio;
642 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
644 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
646 RowColumnData& data = mColumnData[ columnIndex ];
647 data.fillRatio = widthPercentage;
648 data.userFillRatio = true;
649 data.sizePolicy = FILL;
651 mRowColumnDirty = true;
655 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
657 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
659 return mColumnData[ columnIndex ].fillRatio;
662 void TableView::CalculateRowColumnData()
664 // Calculate the relative sizes
665 if( mRowColumnDirty )
667 ComputeRelativeSizes( mRowData );
668 ComputeRelativeSizes( mColumnData );
670 mRowColumnDirty = false;
674 void TableView::OnCalculateRelayoutSize( Dimension::Type dimension )
676 CalculateRowColumnData();
678 if( dimension & Dimension::WIDTH )
680 CalculateFixedSizes( mColumnData, Dimension::WIDTH );
681 mFixedTotals.width = CalculateTotalFixedSize( mColumnData );
684 if( dimension & Dimension::HEIGHT )
686 CalculateFixedSizes( mRowData, Dimension::HEIGHT );
687 mFixedTotals.height = CalculateTotalFixedSize( mRowData );
691 void TableView::OnLayoutNegotiated( float size, Dimension::Type dimension )
693 CalculateRowColumnData();
695 // Calculate the value of all relative sized rows and columns
696 if( dimension & Dimension::WIDTH )
698 float remainingSize = size - mFixedTotals.width;
699 if( remainingSize < 0.0f )
701 remainingSize = 0.0f;
704 CalculateRelativeSizes( mColumnData, remainingSize );
707 if( dimension & Dimension::HEIGHT )
709 float remainingSize = size - mFixedTotals.height;
710 if( remainingSize < 0.0f )
712 remainingSize = 0.0f;
715 CalculateRelativeSizes( mRowData, remainingSize );
719 void TableView::OnRelayout( const Vector2& size, RelayoutContainer& container )
721 CalculateRowColumnData();
723 // Go through the layout data
724 float cumulatedHeight = 0.0f;
726 const unsigned int rowCount = mCellData.GetRows();
727 const unsigned int columnCount = mCellData.GetColumns();
729 for( unsigned int row = 0; row < rowCount; ++row )
731 float cumulatedWidth = 0.0f;
733 for( unsigned int column = 0; column < columnCount; ++column )
735 Actor& actor = mCellData[ row ][ column ].actor;
736 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
738 // If there is an actor and this is the main cell of the actor.
739 // An actor can be in multiple cells if its row or columnspan is more than 1.
740 // We however must lay out each actor only once.
741 if( actor && ( position.rowIndex == row ) && ( position.columnIndex == column ) )
743 // Anchor actor to top left of table view
744 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
745 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
748 actor.GetPadding( padding );
750 Vector3 actorPosition( cumulatedWidth + mPadding.width + padding.left, // Left padding
751 cumulatedHeight + mPadding.height + padding.top, // Top padding
753 actor.SetPosition( actorPosition );
756 DALI_ASSERT_DEBUG( column < mColumnData.Size() );
757 cumulatedWidth += mColumnData[ column ].size;
760 DALI_ASSERT_DEBUG( row < mRowData.Size() );
761 cumulatedHeight += mRowData[ row ].size;
765 unsigned int TableView::GetRows()
767 return mCellData.GetRows();
770 unsigned int TableView::GetColumns()
772 return mCellData.GetColumns();
775 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
777 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
781 TableView& tableViewImpl( GetImpl( tableView ) );
784 case Toolkit::TableView::Property::ROWS:
786 if( value.Get<unsigned int>() != tableViewImpl.GetRows() )
788 tableViewImpl.Resize( value.Get<unsigned int>(), tableViewImpl.GetColumns() );
792 case Toolkit::TableView::Property::COLUMNS:
794 if( value.Get<unsigned int>() != tableViewImpl.GetColumns() )
796 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<unsigned int>() );
800 case Toolkit::TableView::Property::CELL_PADDING:
802 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
805 case Toolkit::TableView::Property::LAYOUT_ROWS:
807 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, value );
810 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
812 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, value );
819 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
821 Property::Value value;
823 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
827 TableView& tableViewImpl( GetImpl( tableView ) );
830 case Toolkit::TableView::Property::ROWS:
832 value = tableViewImpl.GetRows();
835 case Toolkit::TableView::Property::COLUMNS:
837 value = tableViewImpl.GetColumns();
840 case Toolkit::TableView::Property::CELL_PADDING:
842 value = tableViewImpl.GetCellPadding();
845 case Toolkit::TableView::Property::LAYOUT_ROWS:
847 value = tableViewImpl.GetRowHeightsPropertyValue();
850 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
852 value = tableViewImpl.GetColumnWidthsPropertyValue();
861 void TableView::OnControlChildAdd( Actor& child )
863 if( mLayoutingChild )
865 // we're in the middle of laying out children so no point doing anything here
871 // Test properties on actor
872 Toolkit::TableView::CellPosition cellPosition;
873 if( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
875 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
878 if( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
880 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
883 if( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) != Property::INVALID_INDEX )
885 Vector2 indices = child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) ).Get<Vector2 >();
886 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
887 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
889 AddChild( child, cellPosition );
895 // Find the first available cell to store the actor in
896 const unsigned int rowCount = mCellData.GetRows();
897 const unsigned int columnCount = mCellData.GetColumns();
898 for( unsigned int row = 0; row < rowCount; ++row )
900 for( unsigned int column = 0; column < columnCount; ++column )
902 if( !(mCellData[ row ][ column ].actor) )
904 // Put the actor in the cell
907 data.position.columnIndex = column;
908 data.position.rowIndex = row;
909 mCellData[ row ][ column ] = data;
917 // No empty cells, so increase size of the table
918 unsigned int newColumnCount = ( columnCount > 0 ) ? columnCount : 1;
919 ResizeContainers( rowCount + 1, newColumnCount );
921 // Put the actor in the first cell of the new row
924 data.position.rowIndex = rowCount;
925 data.position.columnIndex = 0;
926 mCellData[ rowCount ][ 0 ] = data;
929 void TableView::OnControlChildRemove( Actor& child )
931 // dont process if we're in the middle of bigger operation like delete row, column or resize
932 if( !mLayoutingChild )
934 // relayout the table only if instances were found
935 if( RemoveAllInstances( child ) )
942 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
943 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
944 mCellData( initialRows, initialColumns ),
945 mLayoutingChild( false ),
946 mRowColumnDirty( true ) // Force recalculation first time
948 SetKeyboardNavigationSupport( true );
949 ResizeContainers( initialRows, initialColumns );
952 void TableView::OnInitialize()
954 // Make self as keyboard focusable and focus group
956 self.SetKeyboardFocusable(true);
957 SetAsKeyboardFocusGroup(true);
960 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
962 std::vector<CellData> ignored;
963 ResizeContainers( rows, columns, ignored );
966 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
969 mCellData.Resize( rows, columns, removed );
971 // We don't care if these go smaller, data will be regenerated or is not needed anymore
972 mRowData.Resize( rows );
973 mColumnData.Resize( columns );
976 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
977 unsigned int rowsRemoved, unsigned int columnsRemoved )
979 // iterate through all lost cells
980 std::vector< CellData >::const_iterator iter = lost.begin();
981 for( ; iter != lost.end(); ++iter )
983 // if it is a valid actor
986 // is this actor still somewhere else in the table
987 Toolkit::TableView::CellPosition position;
988 if( FindChildPosition( (*iter).actor, position ) )
990 // it must be spanning multiple cells, position contains the top left most one
991 // check if position is left of the removed location
992 if( position.columnIndex < (*iter).position.columnIndex )
994 // if column span is greater than 1
995 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
997 // decrease column span
998 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1001 // check if position is left of the removed location
1002 if( position.rowIndex < (*iter).position.rowIndex )
1004 // if row span is greater than 1
1005 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1007 // decrease row span
1008 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1014 // this actor is gone for good
1015 // add actor to removed container
1016 removed.push_back( (*iter).actor );
1017 // we dont want the child actor anymore
1018 Self().Remove( (*iter).actor );
1024 bool TableView::RemoveAllInstances( const Actor& child )
1027 // walk through the layout data
1028 const unsigned int rowCount = mCellData.GetRows();
1029 const unsigned int columnCount = mCellData.GetColumns();
1030 for( unsigned int row = 0; row < rowCount; ++row )
1032 for( unsigned int column = 0; column < columnCount; ++column )
1034 if( mCellData[ row ][ column ].actor == child )
1036 // clear the cell, NOTE that the cell might be spanning multiple cells
1037 mCellData[ row ][ column ] = CellData();
1045 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1046 void(TableView::*funcFixed)(unsigned int, float),
1047 void(TableView::*funcRelative)(unsigned int, float),
1048 const Property::Value& value )
1050 if( Property::MAP == value.GetType() )
1052 Property::Map map = value.Get<Property::Map>();
1053 unsigned int rowIndex(0);
1054 for ( unsigned int i = 0, count = map.Count(); i < count; ++i )
1056 Property::Value& item = map.GetValue(i);
1058 if( std::istringstream(map.GetKey(i)) >> rowIndex // the key is a number
1059 && Property::MAP == item.GetType())
1061 if( item.HasKey( "policy" ) && item.HasKey( "value" ) )
1063 Toolkit::TableView::LayoutPolicy policy = Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( item.GetValue("policy").Get<std::string>().c_str(), LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT );
1064 if( policy == Toolkit::TableView::FIXED )
1066 (tableViewImpl.*funcFixed)( rowIndex, item.GetValue("value").Get<float>() );
1068 else if( policy == Toolkit::TableView::RELATIVE )
1070 (tableViewImpl.*funcRelative)( rowIndex, item.GetValue("value").Get<float>() );
1078 Property::Value TableView::GetRowHeightsPropertyValue()
1081 GetMapPropertyValue( mRowData, map);
1082 return Property::Value(map);
1085 Property::Value TableView::GetColumnWidthsPropertyValue()
1088 GetMapPropertyValue( mColumnData, map);
1089 return Property::Value(map);
1092 void TableView::GetMapPropertyValue( const RowColumnArray& data, Property::Map& map )
1094 std::string fixedPolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIXED, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1095 std::string relativePolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::RELATIVE, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1097 const RowColumnArray::SizeType count = data.Size();
1098 for( RowColumnArray::SizeType i = 0; i < count; i++ )
1100 const RowColumnData& dataInstance = data[ i ];
1102 switch( dataInstance.sizePolicy )
1107 item[ "policy" ] = fixedPolicy;
1108 item[ "value" ] = dataInstance.size;
1110 std::ostringstream ss;
1113 map[ ss.str() ] = item;
1121 item[ "policy" ] = relativePolicy;
1122 item[ "value" ] = dataInstance.fillRatio;
1124 std::ostringstream ss;
1127 map[ ss.str() ] = item;
1140 TableView::~TableView()
1145 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1147 Actor nextFocusableActor;
1149 if ( !currentFocusedActor )
1151 // Nothing is currently focused, so the child in the first cell should be focused.
1152 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1156 Toolkit::TableView::CellPosition position;
1157 if( FindChildPosition( currentFocusedActor, position ) )
1159 // The current focused actor is a child of TableView
1160 bool focusLost = false;
1161 int currentRow = position.rowIndex;
1162 int currentColumn = position.columnIndex;
1163 int numberOfColumns = GetColumns();
1164 int numberOfRows = GetRows();
1166 switch ( direction )
1168 case Toolkit::Control::Left:
1170 if(--currentColumn < 0)
1172 currentColumn = numberOfColumns - 1;
1173 if(--currentRow < 0)
1175 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1176 focusLost = (currentRow == 0);
1181 case Toolkit::Control::Right:
1183 if(++currentColumn > numberOfColumns - 1)
1186 if(++currentRow > numberOfRows - 1)
1188 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1189 focusLost = (currentRow == numberOfRows - 1);
1194 case Toolkit::Control::Up:
1196 if(--currentRow < 0)
1198 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1199 focusLost = (currentRow == 0);
1203 case Toolkit::Control::Down:
1206 if(++currentRow > numberOfRows - 1)
1208 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1209 focusLost = (currentRow == numberOfRows - 1);
1215 // Move the focus if we haven't lost it.
1218 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1223 // The current focused actor is not within table view, so the child in the first cell should be focused.
1224 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1228 return nextFocusableActor;
1231 Vector3 TableView::GetNaturalSize()
1233 // Natural size is the size of all fixed cell widths or heights. This ignores cells with relative heights.
1234 return Vector3( mFixedTotals.width, mFixedTotals.height, 1.0f );
1237 float TableView::CalculateChildSize( const Actor& child, Dimension::Type dimension )
1239 CalculateRowColumnData();
1241 const unsigned int rowCount = mCellData.GetRows();
1242 const unsigned int columnCount = mCellData.GetColumns();
1244 for( unsigned int row = 0; row < rowCount; ++row )
1246 for( unsigned int column = 0; column < columnCount; ++column )
1248 // check if this cell has an actor
1249 Actor& actor = mCellData[ row ][ column ].actor;
1251 if( actor && ( actor == child ) )
1253 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
1255 // If there is an actor and this is the main cell of the actor.
1256 // An actor can be in multiple cells if its row or columnspan is more than 1.
1257 if ( ( position.rowIndex == row ) && ( position.columnIndex == column ) )
1261 case Dimension::WIDTH:
1263 float cellSize = 0.0f;
1265 // Accumulate the width
1266 for( unsigned int i = 0; i < position.columnSpan; ++i )
1268 DALI_ASSERT_DEBUG( column + i < mColumnData.Size() );
1269 cellSize += mColumnData[ column + i ].size;
1273 cellSize -= mPadding.width * 2.0f;
1274 if( cellSize < 0.0f )
1282 case Dimension::HEIGHT:
1284 float cellSize = 0.0f;
1286 // Accumulate the height
1287 for( unsigned int i = 0; i < position.rowSpan; ++i )
1289 DALI_ASSERT_DEBUG( row + i < mRowData.Size() );
1290 cellSize += mRowData[ row + i ].size;
1294 cellSize -= mPadding.width * 2.0f;
1295 if( cellSize < 0.0f )
1313 return 0.0f; // Child not found
1316 bool TableView::RelayoutDependentOnChildren( Dimension::Type dimension )
1318 if ( Control::RelayoutDependentOnChildren( dimension ) )
1323 return FindFit( mRowData ) || FindFit( mColumnData );
1326 void TableView::SetCellAlignment( Toolkit::TableView::CellPosition position, HorizontalAlignment::Type horizontal, VerticalAlignment::Type vertical )
1328 // Check if we need to expand our data array
1329 if( position.rowIndex >= mCellData.GetRows() )
1331 // Only adding new rows
1332 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
1335 if( position.columnIndex >= mCellData.GetColumns() )
1337 // Only adding new columns
1338 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
1341 // Set the alignment of the cell
1342 CellData& data = mCellData[ position.rowIndex ][ position.columnIndex ];
1343 data.horizontalAlignment = horizontal;
1344 data.verticalAlignment = vertical;
1347 void TableView::ComputeRelativeSizes( RowColumnArray& data )
1349 // First pass: Count number of fill entries and calculate used relative space
1350 Dali::Vector< RowColumnData* > fillData;
1351 float relativeTotal = 0.0f;
1353 const unsigned int dataCount = data.Size();
1355 for( unsigned int i = 0; i < dataCount; ++i )
1357 RowColumnData& dataInstance = data[ i ];
1359 if( dataInstance.sizePolicy == FILL )
1361 if( dataInstance.userFillRatio )
1363 relativeTotal += dataInstance.fillRatio;
1367 fillData.PushBack( &dataInstance );
1372 // Second pass: Distribute remaining relative space
1373 const unsigned int fillCount = fillData.Size();
1376 if( relativeTotal > 1.0f )
1378 relativeTotal = 1.0f;
1381 const float evenFillRatio = (1.0f - relativeTotal ) / fillCount;
1383 for( unsigned int i = 0; i < fillCount; ++i )
1385 fillData[ i ]->fillRatio = evenFillRatio;
1390 float TableView::CalculateTotalFixedSize( const RowColumnArray& data )
1392 float totalSize = 0.0f;
1394 const unsigned int dataCount = data.Size();
1396 for( unsigned int i = 0; i < dataCount; ++i )
1398 const RowColumnData& dataInstance = data[ i ];
1400 switch( dataInstance.sizePolicy )
1405 totalSize += dataInstance.size;
1419 Vector2 TableView::GetCellPadding( Dimension::Type dimension )
1423 case Dimension::WIDTH:
1425 return Vector2( mPadding.x, mPadding.x );
1427 case Dimension::HEIGHT:
1429 return Vector2( mPadding.y, mPadding.y );
1440 void TableView::CalculateFixedSizes( RowColumnArray& data, Dimension::Type dimension )
1442 Vector2 cellPadding = GetCellPadding( dimension );
1444 const unsigned int dataCount = data.Size();
1446 for( unsigned int i = 0; i < dataCount; ++i )
1448 RowColumnData& dataInstance = data[ i ];
1450 if( dataInstance.sizePolicy == FIT )
1452 // Find the size of the biggest actor in the row or column
1453 float maxActorHeight = 0.0f;
1455 unsigned int fitCount = ( dimension == Dimension::WIDTH ) ? mCellData.GetRows() : mCellData.GetColumns();
1457 for( unsigned int j = 0; j < fitCount; ++j )
1459 unsigned int row = ( dimension == Dimension::WIDTH ) ? j : i;
1460 unsigned int column = ( dimension == Dimension::WIDTH ) ? i : j;
1461 DALI_ASSERT_DEBUG( row < mCellData.GetRows() );
1462 DALI_ASSERT_DEBUG( column < mCellData.GetColumns() );
1464 Actor& actor = mCellData[ row ][ column ].actor;
1467 if( FitToChild( actor, dimension ) )
1469 maxActorHeight = std::max( maxActorHeight, actor.GetRelayoutSize( dimension ) + cellPadding.x + cellPadding.y );
1474 dataInstance.size = maxActorHeight;
1479 void TableView::CalculateRelativeSizes( RowColumnArray& data, float size )
1481 const unsigned int dataCount = data.Size();
1483 for( unsigned int i = 0; i < dataCount; ++i )
1485 RowColumnData& dataInstance = data[ i ];
1487 if( dataInstance.sizePolicy == FILL )
1489 dataInstance.size = dataInstance.fillRatio * size;
1494 bool TableView::FindFit( const RowColumnArray& data )
1496 for( unsigned int i = 0, count = data.Size(); i < count; ++i )
1498 if( data[ i ].sizePolicy == FIT )
1507 } // namespace Internal
1509 } // namespace Toolkit