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< Toolkit::TableView::LayoutPolicy > 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 if( Property::MAP == value.GetType() )
1055 Property::Map map = value.Get<Property::Map>();
1056 unsigned int rowIndex(0);
1057 for ( unsigned int i = 0, count = map.Count(); i < count; ++i )
1059 Property::Value& item = map.GetValue(i);
1061 if( std::istringstream(map.GetKey(i)) >> rowIndex // the key is a number
1062 && Property::MAP == item.GetType())
1064 if( item.HasKey( "policy" ) && item.HasKey( "value" ) )
1066 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 );
1067 if( policy == Toolkit::TableView::FIXED )
1069 (tableViewImpl.*funcFixed)( rowIndex, item.GetValue("value").Get<float>() );
1071 else if( policy == Toolkit::TableView::RELATIVE )
1073 (tableViewImpl.*funcRelative)( rowIndex, item.GetValue("value").Get<float>() );
1081 Property::Value TableView::GetRowHeightsPropertyValue()
1084 GetMapPropertyValue( mRowData, map);
1085 return Property::Value(map);
1088 Property::Value TableView::GetColumnWidthsPropertyValue()
1091 GetMapPropertyValue( mColumnData, map);
1092 return Property::Value(map);
1095 void TableView::GetMapPropertyValue( const RowColumnArray& data, Property::Map& map )
1097 std::string fixedPolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIXED, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1098 std::string relativePolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::RELATIVE, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1100 const RowColumnArray::SizeType count = data.Size();
1101 for( RowColumnArray::SizeType i = 0; i < count; i++ )
1103 const RowColumnData& dataInstance = data[ i ];
1105 switch( dataInstance.sizePolicy )
1110 item[ "policy" ] = fixedPolicy;
1111 item[ "value" ] = dataInstance.size;
1113 std::ostringstream ss;
1116 map[ ss.str() ] = item;
1124 item[ "policy" ] = relativePolicy;
1125 item[ "value" ] = dataInstance.fillRatio;
1127 std::ostringstream ss;
1130 map[ ss.str() ] = item;
1143 TableView::~TableView()
1148 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1150 Actor nextFocusableActor;
1152 if ( !currentFocusedActor )
1154 // Nothing is currently focused, so the child in the first cell should be focused.
1155 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1159 Toolkit::TableView::CellPosition position;
1160 if( FindChildPosition( currentFocusedActor, position ) )
1162 // The current focused actor is a child of TableView
1163 bool focusLost = false;
1164 int currentRow = position.rowIndex;
1165 int currentColumn = position.columnIndex;
1166 int numberOfColumns = GetColumns();
1167 int numberOfRows = GetRows();
1169 switch ( direction )
1171 case Toolkit::Control::KeyboardFocus::LEFT:
1173 if(--currentColumn < 0)
1175 currentColumn = numberOfColumns - 1;
1176 if(--currentRow < 0)
1178 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1179 focusLost = (currentRow == 0);
1184 case Toolkit::Control::KeyboardFocus::RIGHT:
1186 if(++currentColumn > numberOfColumns - 1)
1189 if(++currentRow > numberOfRows - 1)
1191 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1192 focusLost = (currentRow == numberOfRows - 1);
1197 case Toolkit::Control::KeyboardFocus::UP:
1199 if(--currentRow < 0)
1201 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1202 focusLost = (currentRow == 0);
1206 case Toolkit::Control::KeyboardFocus::DOWN:
1209 if(++currentRow > numberOfRows - 1)
1211 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1212 focusLost = (currentRow == numberOfRows - 1);
1218 // Move the focus if we haven't lost it.
1221 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1226 // The current focused actor is not within table view, so the child in the first cell should be focused.
1227 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1231 return nextFocusableActor;
1234 Vector3 TableView::GetNaturalSize()
1236 // Natural size is the size of all fixed cell widths or heights. This ignores cells with relative heights.
1237 return Vector3( mFixedTotals.width, mFixedTotals.height, 1.0f );
1240 float TableView::CalculateChildSize( const Actor& child, Dimension::Type dimension )
1242 CalculateRowColumnData();
1244 const unsigned int rowCount = mCellData.GetRows();
1245 const unsigned int columnCount = mCellData.GetColumns();
1247 for( unsigned int row = 0; row < rowCount; ++row )
1249 for( unsigned int column = 0; column < columnCount; ++column )
1251 // check if this cell has an actor
1252 Actor& actor = mCellData[ row ][ column ].actor;
1254 if( actor && ( actor == child ) )
1256 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
1258 // If there is an actor and this is the main cell of the actor.
1259 // An actor can be in multiple cells if its row or columnspan is more than 1.
1260 if ( ( position.rowIndex == row ) && ( position.columnIndex == column ) )
1264 case Dimension::WIDTH:
1266 float cellSize = 0.0f;
1268 // Accumulate the width
1269 for( unsigned int i = 0; i < position.columnSpan; ++i )
1271 DALI_ASSERT_DEBUG( column + i < mColumnData.Size() );
1272 cellSize += mColumnData[ column + i ].size;
1276 cellSize -= mPadding.width * 2.0f;
1277 if( cellSize < 0.0f )
1285 case Dimension::HEIGHT:
1287 float cellSize = 0.0f;
1289 // Accumulate the height
1290 for( unsigned int i = 0; i < position.rowSpan; ++i )
1292 DALI_ASSERT_DEBUG( row + i < mRowData.Size() );
1293 cellSize += mRowData[ row + i ].size;
1297 cellSize -= mPadding.width * 2.0f;
1298 if( cellSize < 0.0f )
1316 return 0.0f; // Child not found
1319 bool TableView::RelayoutDependentOnChildren( Dimension::Type dimension )
1321 if ( Control::RelayoutDependentOnChildren( dimension ) )
1326 return FindFit( mRowData ) || FindFit( mColumnData );
1329 void TableView::SetCellAlignment( Toolkit::TableView::CellPosition position, HorizontalAlignment::Type horizontal, VerticalAlignment::Type vertical )
1331 // Check if we need to expand our data array
1332 if( position.rowIndex >= mCellData.GetRows() )
1334 // Only adding new rows
1335 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
1338 if( position.columnIndex >= mCellData.GetColumns() )
1340 // Only adding new columns
1341 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
1344 // Set the alignment of the cell
1345 CellData& data = mCellData[ position.rowIndex ][ position.columnIndex ];
1346 data.horizontalAlignment = horizontal;
1347 data.verticalAlignment = vertical;
1350 void TableView::ComputeRelativeSizes( RowColumnArray& data )
1352 // First pass: Count number of fill entries and calculate used relative space
1353 Dali::Vector< RowColumnData* > fillData;
1354 float relativeTotal = 0.0f;
1356 const unsigned int dataCount = data.Size();
1358 for( unsigned int i = 0; i < dataCount; ++i )
1360 RowColumnData& dataInstance = data[ i ];
1362 if( dataInstance.sizePolicy == FILL )
1364 if( dataInstance.userFillRatio )
1366 relativeTotal += dataInstance.fillRatio;
1370 fillData.PushBack( &dataInstance );
1375 // Second pass: Distribute remaining relative space
1376 const unsigned int fillCount = fillData.Size();
1379 if( relativeTotal > 1.0f )
1381 relativeTotal = 1.0f;
1384 const float evenFillRatio = (1.0f - relativeTotal ) / fillCount;
1386 for( unsigned int i = 0; i < fillCount; ++i )
1388 fillData[ i ]->fillRatio = evenFillRatio;
1393 float TableView::CalculateTotalFixedSize( const RowColumnArray& data )
1395 float totalSize = 0.0f;
1397 const unsigned int dataCount = data.Size();
1399 for( unsigned int i = 0; i < dataCount; ++i )
1401 const RowColumnData& dataInstance = data[ i ];
1403 switch( dataInstance.sizePolicy )
1408 totalSize += dataInstance.size;
1422 Vector2 TableView::GetCellPadding( Dimension::Type dimension )
1426 case Dimension::WIDTH:
1428 return Vector2( mPadding.x, mPadding.x );
1430 case Dimension::HEIGHT:
1432 return Vector2( mPadding.y, mPadding.y );
1443 void TableView::CalculateFixedSizes( RowColumnArray& data, Dimension::Type dimension )
1445 Vector2 cellPadding = GetCellPadding( dimension );
1447 const unsigned int dataCount = data.Size();
1449 for( unsigned int i = 0; i < dataCount; ++i )
1451 RowColumnData& dataInstance = data[ i ];
1453 if( dataInstance.sizePolicy == FIT )
1455 // Find the size of the biggest actor in the row or column
1456 float maxActorHeight = 0.0f;
1458 unsigned int fitCount = ( dimension == Dimension::WIDTH ) ? mCellData.GetRows() : mCellData.GetColumns();
1460 for( unsigned int j = 0; j < fitCount; ++j )
1462 unsigned int row = ( dimension == Dimension::WIDTH ) ? j : i;
1463 unsigned int column = ( dimension == Dimension::WIDTH ) ? i : j;
1464 DALI_ASSERT_DEBUG( row < mCellData.GetRows() );
1465 DALI_ASSERT_DEBUG( column < mCellData.GetColumns() );
1467 const CellData& cellData = mCellData[ row ][ column ];
1468 const Actor& actor = cellData.actor;
1471 if( FitToChild( actor, dimension ) && ( dimension == Dimension::WIDTH ) ? ( cellData.position.columnSpan == 1 ) : ( cellData.position.rowSpan == 1 ) )
1473 maxActorHeight = std::max( maxActorHeight, actor.GetRelayoutSize( dimension ) + cellPadding.x + cellPadding.y );
1478 dataInstance.size = maxActorHeight;
1483 void TableView::CalculateRelativeSizes( RowColumnArray& data, float size )
1485 const unsigned int dataCount = data.Size();
1487 for( unsigned int i = 0; i < dataCount; ++i )
1489 RowColumnData& dataInstance = data[ i ];
1491 if( dataInstance.sizePolicy == FILL )
1493 dataInstance.size = dataInstance.fillRatio * size;
1498 bool TableView::FindFit( const RowColumnArray& data )
1500 for( unsigned int i = 0, count = data.Size(); i < count; ++i )
1502 if( data[ i ].sizePolicy == FIT )
1511 } // namespace Internal
1513 } // namespace Toolkit