2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/table-view/table-view-impl.h>
23 #include <dali/public-api/object/ref-object.h>
24 #include <dali/public-api/object/type-registry.h>
25 #include <dali/devel-api/object/type-registry-helper.h>
26 #include <dali/devel-api/scripting/scripting.h>
27 #include <dali/public-api/size-negotiation/relayout-container.h>
28 #include <dali/integration-api/debug.h>
35 * Custom properties for where to put the actor.
37 * When an actor is add to the tableView through Actor::Add() instead of TableView::AddChild,
38 * the following custom properties of the actor are checked to decide the actor position inside the table
40 * These non-animatable properties should be registered to the child which would be added to the table
42 const char * const CELL_INDEX_PROPERTY_NAME("cellIndex");
43 const char * const ROW_SPAN_PROPERTY_NAME("rowSpan");
44 const char * const COLUMN_SPAN_PROPERTY_NAME("columnSpan");
45 const char * const CELL_HORIZONTAL_ALIGNMENT_PROPERTY_NAME("cellHorizontalAlignment");
46 const char * const CELL_VERTICAL_ALIGNMENT_PROPERTY_NAME("cellVerticalAlignment");
49 * @brief Should the tableview fit around the given actor
51 * @param[in] actor The child actor to test against
52 * @param[dimension] The dimension to test against
54 bool FitToChild( Actor actor, Dimension::Type dimension )
56 return actor.GetResizePolicy( dimension ) != ResizePolicy::FILL_TO_PARENT && actor.GetRelayoutSize( dimension ) > 0.0f;
59 #if defined(DEBUG_ENABLED)
60 // debugging support, very useful when new features are added or bugs are hunted down
61 // currently not called from code so compiler will optimize these away, kept here for future debugging
63 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
64 #define TV_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
65 //#define TABLEVIEW_DEBUG 1
67 #if defined(TABLEVIEW_DEBUG)
68 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
70 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
72 for( unsigned int i = 0; i < array.GetRows(); ++i )
74 for( unsigned int j = 0; j < array.GetColumns(); ++j )
76 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
78 std::string actorName;
82 actorName = data.actor.GetName();
84 TV_LOG("Array[%d,%d]=%c %s %d,%d,%d,%d ", i, j, actor, actorName.c_str(),
85 data.position.rowIndex, data.position.columnIndex,
86 data.position.rowSpan, data.position.columnSpan );
92 // debugging support, very useful when new features are added or bugs are hunted down
93 // currently not called from code so compiler will optimize these away, kept here for future debugging
94 void PrintArray( Array2d<Size>& array )
96 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
98 for( unsigned int i = 0; i < array.GetRows(); ++i )
100 for( unsigned int j = 0; j < array.GetColumns(); ++j )
102 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
107 // debugging support, very useful when new features are added or bugs are hunted down
108 // currently not called from code so compiler will optimize these away, kept here for future debugging
109 void PrintVector( std::vector<float>& array )
111 TV_LOG( "vector, size [%d]\n", array.size() );
113 for( unsigned int i = 0; i < array.size(); ++i )
115 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
119 #endif // defined(TABLEVIEW_DEBUG)
120 #endif // defined(DEBUG_ENABLED)
139 return Toolkit::TableView::New( 0, 0 );
142 // Setup properties, signals and actions using the type-registry.
143 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TableView, Toolkit::Control, Create );
145 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "rows", INTEGER, ROWS )
146 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "columns", INTEGER, COLUMNS )
147 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "cellPadding", VECTOR2, CELL_PADDING )
148 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layoutRows", MAP, LAYOUT_ROWS )
149 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layoutColumns", MAP, LAYOUT_COLUMNS )
151 DALI_TYPE_REGISTRATION_END()
153 const Scripting::StringEnum LAYOUT_POLICY_STRING_TABLE[] =
155 { "fixed", Toolkit::TableView::FIXED },
156 { "relative", Toolkit::TableView::RELATIVE },
157 { "fill", Toolkit::TableView::FILL },
158 { "fit", Toolkit::TableView::FIT }
160 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
162 const Scripting::StringEnum HORIZONTAL_ALIGNMENT_STRING_TABLE[] =
164 {"left", HorizontalAlignment::LEFT},
165 {"center", HorizontalAlignment::CENTER},
166 {"right", HorizontalAlignment::RIGHT}
168 const unsigned int HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(HORIZONTAL_ALIGNMENT_STRING_TABLE) / sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE[0] );
170 const Scripting::StringEnum VERTICAL_ALIGNMENT_STRING_TABLE[] =
172 {"top", VerticalAlignment::TOP},
173 {"center", VerticalAlignment::CENTER},
174 {"bottom", VerticalAlignment::BOTTOM}
176 const unsigned int VERTICAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(VERTICAL_ALIGNMENT_STRING_TABLE) / sizeof( VERTICAL_ALIGNMENT_STRING_TABLE[0] );
178 } // Unnamed namespace
180 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
182 // Create the implementation, temporarily owned by this handle on stack
183 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
185 // Pass ownership to CustomActor handle
186 Toolkit::TableView handle( *impl );
188 // Second-phase init of the implementation
189 // This can only be done after the CustomActor connection has been made...
195 bool TableView::AddChild( Actor& child, const Toolkit::TableView::CellPosition& position )
197 // check that the child is valid
198 DALI_ASSERT_ALWAYS( child );
200 // if child is already parented, we adopt it
203 // check if we need to expand our data array
204 if( position.rowIndex >= mCellData.GetRows() )
206 // only adding new rows
207 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
210 if( position.columnIndex >= mCellData.GetColumns() )
212 // only adding new columns
213 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
216 // check if there already is something in this cell
217 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
219 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
222 RelayoutingLock lock( *this );
226 // if child spans multiple rows of columns
227 if( ( position.rowSpan > 1 ) && ( position.rowIndex + position.rowSpan > mCellData.GetRows() ) )
229 // increase table size for the full span, only increasing rows
230 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
233 if( ( position.columnSpan > 1 ) && ( position.columnIndex + position.columnSpan > mCellData.GetColumns() ) )
235 // increase table size for the full span, only increasing columns
236 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
239 // Fill in all cells that need the data
242 data.position = position;
244 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
246 // store same information to all cells, this way we can identify
247 // if a cell is the prime location of an actor or a spanned one
248 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
250 // store same information to all cells, this way we can identify
251 // if a cell is the prime location of an actor or a spanned one
252 mCellData[ row ][ column ] = data;
256 // Relayout the whole table
257 if( mRowData[position.rowIndex].sizePolicy == Toolkit::TableView::FIT && position.rowSpan == 1 )
261 if( mColumnData[position.columnIndex].sizePolicy == Toolkit::TableView::FIT && position.columnSpan == 1 )
268 return true; // Addition successful
271 Actor TableView::GetChildAt( const Toolkit::TableView::CellPosition& position )
273 if( ( position.rowIndex < mCellData.GetRows() ) && ( position.columnIndex < mCellData.GetColumns() ) )
275 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
278 // Return an empty handle
282 Actor TableView::RemoveChildAt( const Toolkit::TableView::CellPosition& position )
284 // get the child handle
285 Actor child = GetChildAt( position );
286 // if no real actor there, nothing else to be done
289 RelayoutingLock lock( *this );
290 // Remove the child, this will trigger a call to OnChildRemove
291 Self().Remove( child );
293 // relayout the table only if instances were found
294 if( RemoveAllInstances( child ) )
296 if( mRowData[position.rowIndex].sizePolicy == Toolkit::TableView::FIT )
300 if( mColumnData[position.columnIndex].sizePolicy == Toolkit::TableView::FIT )
307 // return the child back to caller
311 bool TableView::FindChildPosition( const Actor& child, Toolkit::TableView::CellPosition& positionOut )
313 // Only find valid child actors
316 // Walk through the layout data
317 const unsigned int rowCount = mCellData.GetRows();
318 const unsigned int columnCount = mCellData.GetColumns();
320 for( unsigned int row = 0; row < rowCount; ++row )
322 for( unsigned int column = 0; column < columnCount; ++column )
324 if( mCellData[ row ][ column ].actor == child )
326 positionOut = mCellData[ row ][ column ].position;
336 void TableView::InsertRow( unsigned int rowIndex )
338 RelayoutingLock lock( *this );
340 mCellData.InsertRow( rowIndex );
342 // Need to update the cell infos for the items that moved
343 const unsigned int rowCount = mCellData.GetRows();
344 const unsigned int columnCount = mCellData.GetColumns();
346 for( unsigned int row = 0; row < rowCount; ++row )
348 for( unsigned int column = 0; column < columnCount; ++column )
350 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
352 // If cell is spanning and above and spans to inserted row
353 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
354 ( position.rowIndex + position.rowSpan > rowIndex ) )
359 // Copy cell to occupy the new column
360 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
362 else if( row > rowIndex ) // If below of inserted row, increase row index
370 // Expand row data array
371 mRowData.Insert( mRowData.Begin() + rowIndex, RowColumnData() );
373 // Sizes may have changed, so relayout
378 void TableView::DeleteRow( unsigned int rowIndex )
380 std::vector< Actor > ignored;
381 DeleteRow( rowIndex, ignored );
384 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
386 RelayoutingLock lock( *this );
389 std::vector< CellData > lost;
390 mCellData.DeleteRow( rowIndex, lost );
392 // Need to update the cell infos for the items that moved
393 const unsigned int rowCount = mCellData.GetRows();
394 const unsigned int columnCount = mCellData.GetColumns();
396 for( unsigned int row = 0; row < rowCount; ++row )
398 for( unsigned int column = 0; column < columnCount; ++column )
400 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
402 // If cell is spanning and above and spans to deleted row
403 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
404 ( position.rowIndex + position.rowSpan > rowIndex ) )
407 if( position.rowSpan > 1 )
412 else if( row >= rowIndex ) // If below of or at the inserted row, decrease row index
415 if( position.rowIndex > 0 )
423 // 1 row removed, 0 columns
424 RemoveAndGetLostActors( lost, removed, 1u, 0u );
426 // Contract row data array
427 mRowData.Erase( mRowData.Begin() + rowIndex );
429 // Sizes may have changed, so relayout
431 // it is possible that the deletion of row leads to remove of child which might further lead to the change of FIT column
437 void TableView::InsertColumn( unsigned int columnIndex )
439 RelayoutingLock lock( *this );
441 // Insert the new column
442 mCellData.InsertColumn( columnIndex );
444 // Need to update the cell infos for the items that moved
445 const unsigned int rowCount = mCellData.GetRows();
446 const unsigned int columnCount = mCellData.GetColumns();
448 for( unsigned int row = 0; row < rowCount; ++row )
450 for( unsigned int column = 0; column < columnCount; ++column )
452 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
454 // If cell is spanning and left side and spans to inserted column
455 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
456 ( position.columnIndex + position.columnSpan > columnIndex ) )
459 position.columnSpan++;
461 // Copy cell to occupy the new column
462 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
464 else if( column > columnIndex ) // If on the right side of inserted column, increase column index
467 position.columnIndex++;
472 // Expand column data array
473 mColumnData.Insert( mColumnData.Begin() + columnIndex, RowColumnData() );
475 // Sizes may have changed so relayout
480 void TableView::DeleteColumn( unsigned int columnIndex )
482 std::vector< Actor > ignored;
483 DeleteColumn( columnIndex, ignored );
486 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
488 RelayoutingLock lock( *this );
491 std::vector< CellData > lost;
492 mCellData.DeleteColumn( columnIndex, lost );
494 // Need to update the cell infos for the items that moved
495 const unsigned int rowCount = mCellData.GetRows();
496 const unsigned int columnCount = mCellData.GetColumns();
498 for( unsigned int row = 0; row < rowCount; ++row )
500 for( unsigned int column = 0; column < columnCount; ++column )
502 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
504 // If cell is spanning and left side and spans to inserted column
505 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
506 ( position.columnIndex + position.columnSpan > columnIndex ) )
509 if( position.columnSpan > 1 )
511 position.columnSpan--;
514 else if( column >= columnIndex ) // If on the right side of or at the inserted column, decrease column index
517 if( position.columnIndex > 0 )
519 position.columnIndex--;
525 // 0 rows, 1 column removed
526 RemoveAndGetLostActors( lost, removed, 0u, 1u );
528 // Contract column data array
529 mColumnData.Erase( mColumnData.Begin() + columnIndex );
531 // Size may have changed so relayout
533 // it is possible that the deletion of column leads to remove of child which might further lead to the change of FIT row
539 void TableView::Resize( unsigned int rows, unsigned int columns )
541 std::vector< Actor > ignored;
542 Resize( rows, columns, ignored );
545 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
547 RelayoutingLock lock( *this );
549 unsigned int oldRows = GetRows();
550 unsigned int oldColumns = GetColumns();
553 std::vector< CellData > lost;
554 ResizeContainers( rows, columns, lost );
556 // Calculate if we lost rows
557 unsigned int rowsRemoved = 0;
558 unsigned int newRows = GetRows();
560 if( oldRows < newRows )
562 rowsRemoved = newRows - oldRows;
565 // Calculate if we lost columns
566 unsigned int columnsRemoved = 0;
567 unsigned int newColumns = GetColumns();
568 if( oldColumns < newColumns )
570 rowsRemoved = newColumns - oldColumns;
573 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
575 // Sizes may have changed so request a relayout
581 void TableView::SetCellPadding( Size padding )
583 // If padding really changed
584 if( padding != mPadding )
592 Size TableView::GetCellPadding()
597 void TableView::SetFitHeight( unsigned int rowIndex )
599 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
601 if( mRowData[ rowIndex ].sizePolicy != Toolkit::TableView::FIT )
603 mRowData[ rowIndex ].sizePolicy = Toolkit::TableView::FIT;
610 bool TableView::IsFitHeight( unsigned int rowIndex ) const
612 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
614 return mRowData[ rowIndex ].sizePolicy == Toolkit::TableView::FIT;
617 void TableView::SetFitWidth( unsigned int columnIndex )
619 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
621 if( mColumnData[ columnIndex ].sizePolicy != Toolkit::TableView::FIT )
623 mColumnData[ columnIndex ].sizePolicy = Toolkit::TableView::FIT;
630 bool TableView::IsFitWidth( unsigned int columnIndex ) const
632 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
634 return mColumnData[ columnIndex ].sizePolicy == Toolkit::TableView::FIT;
637 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
639 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
641 RowColumnData& data = mRowData[ rowIndex ];
643 data.sizePolicy = Toolkit::TableView::FIXED;
649 float TableView::GetFixedHeight( unsigned int rowIndex ) const
651 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
653 return mRowData[ rowIndex ].size;
656 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
658 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
660 RowColumnData& data = mColumnData[ columnIndex ];
662 data.sizePolicy = Toolkit::TableView::FIXED;
668 float TableView::GetFixedWidth( unsigned int columnIndex ) const
670 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
672 return mColumnData[ columnIndex ].size;
675 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
677 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
679 RowColumnData& data = mRowData[ rowIndex ];
680 data.fillRatio = heightPercentage;
681 data.sizePolicy = Toolkit::TableView::RELATIVE;
687 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
689 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
691 return mRowData[ rowIndex ].fillRatio;
694 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
696 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
698 RowColumnData& data = mColumnData[ columnIndex ];
699 data.fillRatio = widthPercentage;
700 data.sizePolicy = Toolkit::TableView::RELATIVE;
706 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
708 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
710 return mColumnData[ columnIndex ].fillRatio;
713 void TableView::OnCalculateRelayoutSize( Dimension::Type dimension )
715 if( (dimension & Dimension::WIDTH) && mColumnDirty )
718 * FIXED and FIT have size in pixel
719 * Nothing to do with FIXED, as its value is assigned by user and will not get changed
721 * Need to update the size for FIT column here
723 CalculateFitSizes( mColumnData, Dimension::WIDTH );
725 /* RELATIVE and FILL have size in ratio
726 * Their size in pixel is not available until we get the negotiated size for the whole table
727 * Nothing to do with RELATIVE, as its ratio is assigned by user and will not get changed
729 * Need to update the ratio for FILL column here
731 CalculateFillSizes( mColumnData );
733 mFixedTotals.width = CalculateTotalFixedSize( mColumnData );
736 if( (dimension & Dimension::HEIGHT) && mRowDirty )
738 // refer to the comment above
739 CalculateFitSizes( mRowData, Dimension::HEIGHT );
741 // refer to the comment above
742 CalculateFillSizes( mRowData );
744 mFixedTotals.height = CalculateTotalFixedSize( mRowData );
748 void TableView::OnLayoutNegotiated( float size, Dimension::Type dimension )
750 // Update the column sizes
751 if( (dimension & Dimension::WIDTH) && mColumnDirty )
753 float remainingSize = size - mFixedTotals.width;
754 if( remainingSize < 0.0f )
756 remainingSize = 0.0f;
759 // update every column position in ColumnData array
760 float cumulatedWidth = 0.0f;
761 for( unsigned int column = 0, columnCount = mCellData.GetColumns(); column < columnCount; ++column )
763 if( mColumnData[ column ].sizePolicy == Toolkit::TableView::FILL || mColumnData[ column ].sizePolicy == Toolkit::TableView::RELATIVE)
765 mColumnData[ column ].size = mColumnData[ column ].fillRatio * remainingSize;
768 cumulatedWidth += mColumnData[ column ].size;
769 mColumnData[column].position = cumulatedWidth;
772 mColumnDirty = false;
775 // Update the row sizes
776 if( (dimension & Dimension::HEIGHT) && mRowDirty )
778 float remainingSize = size - mFixedTotals.height;
779 if( remainingSize < 0.0f )
781 remainingSize = 0.0f;
784 // update every row position in RowData array
785 float cumulatedHeight = 0.0f;
786 for( unsigned int row = 0, rowCount = mCellData.GetRows(); row < rowCount; ++row )
788 if( mRowData[ row ].sizePolicy == Toolkit::TableView::FILL || mRowData[ row ].sizePolicy == Toolkit::TableView::RELATIVE)
790 mRowData[ row ].size = mRowData[ row ].fillRatio * remainingSize;
793 cumulatedHeight += mRowData[ row ].size;
794 mRowData[row].position = cumulatedHeight;
801 void TableView::OnSizeSet( const Vector3& size )
803 // If this table view is size negotiated by another actor or control, then the
804 // rows and columns must be recalculated or the new size will not take effect.
805 mRowDirty = mColumnDirty = true;
809 void TableView::OnRelayout( const Vector2& size, RelayoutContainer& container )
811 // Go through the layout data
812 for( unsigned int row = 0, rowCount = mCellData.GetRows(); row < rowCount; ++row )
814 for( unsigned int column = 0, columnCount = mCellData.GetColumns(); column < columnCount; ++column )
816 CellData& cellData= mCellData[ row ][ column ];
817 Actor& actor = cellData.actor;
818 const Toolkit::TableView::CellPosition position = cellData.position;
820 // If there is an actor and this is the main cell of the actor.
821 // An actor can be in multiple cells if its row or column span is more than 1.
822 // We however must lay out each actor only once.
823 if( actor && position.rowIndex == row && position.columnIndex == column )
825 // Anchor actor to top left of the cell
826 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
827 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
830 actor.GetPadding( padding );
832 float left = column > 0 ? mColumnData[column-1].position : 0.f;
833 float right = mColumnData[column+position.columnSpan-1].position;
834 float top = row > 0 ? mRowData[row-1].position : 0.f;
835 float bottom = mRowData[row+position.rowSpan-1].position;
837 if( cellData.horizontalAlignment == HorizontalAlignment::LEFT )
839 actor.SetX( left + mPadding.width + padding.left );
841 else if( cellData.horizontalAlignment == HorizontalAlignment::RIGHT )
843 actor.SetX( right - mPadding.width - padding.right - actor.GetRelayoutSize( Dimension::WIDTH ) );
845 else //if( cellData.horizontalAlignment == HorizontalAlignment::CENTER )
847 actor.SetX( (left + right + padding.left - padding.right - actor.GetRelayoutSize( Dimension::WIDTH )) * 0.5f );
850 if( cellData.verticalAlignment == VerticalAlignment::TOP )
852 actor.SetY( top + mPadding.height + padding.top );
854 else if( cellData.verticalAlignment == VerticalAlignment::BOTTOM )
856 actor.SetY( bottom - mPadding.height - padding.bottom - actor.GetRelayoutSize( Dimension::HEIGHT ) );
858 else //if( cellData.verticalAlignment = VerticalAlignment::CENTER )
860 actor.SetY( (top + bottom + padding.top - padding.bottom - actor.GetRelayoutSize( Dimension::HEIGHT )) * 0.5f );
867 unsigned int TableView::GetRows()
869 return mCellData.GetRows();
872 unsigned int TableView::GetColumns()
874 return mCellData.GetColumns();
877 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
879 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
883 TableView& tableViewImpl( GetImpl( tableView ) );
886 case Toolkit::TableView::Property::ROWS:
889 if( value.Get( rows ) && rows >= 0 )
891 if( static_cast<unsigned int>(rows) != tableViewImpl.GetRows() )
893 tableViewImpl.Resize( rows, tableViewImpl.GetColumns() );
898 case Toolkit::TableView::Property::COLUMNS:
901 if( value.Get( columns ) && columns >= 0 )
903 if( static_cast<unsigned int>( columns ) != tableViewImpl.GetColumns() )
905 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<int>() );
910 case Toolkit::TableView::Property::CELL_PADDING:
912 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
915 case Toolkit::TableView::Property::LAYOUT_ROWS:
917 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, &TableView::SetFitHeight, value );
920 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
922 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, &TableView::SetFitWidth, value );
929 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
931 Property::Value value;
933 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
937 TableView& tableViewImpl( GetImpl( tableView ) );
940 case Toolkit::TableView::Property::ROWS:
942 value = static_cast<int>( tableViewImpl.GetRows() );
945 case Toolkit::TableView::Property::COLUMNS:
947 value = static_cast<int>( tableViewImpl.GetColumns() );
950 case Toolkit::TableView::Property::CELL_PADDING:
952 value = tableViewImpl.GetCellPadding();
955 case Toolkit::TableView::Property::LAYOUT_ROWS:
957 value = tableViewImpl.GetRowHeightsPropertyValue();
960 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
962 value = tableViewImpl.GetColumnWidthsPropertyValue();
971 void TableView::OnChildAdd( Actor& child )
973 Control::OnChildAdd( child );
975 if( mLayoutingChild )
977 // we're in the middle of laying out children so no point doing anything here
981 // Test properties on actor
982 HorizontalAlignment::Type horizontalAlignment = HorizontalAlignment::LEFT;
983 VerticalAlignment::Type verticalAlignment = VerticalAlignment::TOP;
984 if( child.GetPropertyIndex( CELL_HORIZONTAL_ALIGNMENT_PROPERTY_NAME ) != Property::INVALID_INDEX )
986 std::string value = child.GetProperty( child.GetPropertyIndex(CELL_HORIZONTAL_ALIGNMENT_PROPERTY_NAME) ).Get<std::string >();
987 Scripting::GetEnumeration< HorizontalAlignment::Type >( value.c_str(),
988 HORIZONTAL_ALIGNMENT_STRING_TABLE,
989 HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT,
990 horizontalAlignment );
992 if( child.GetPropertyIndex( CELL_VERTICAL_ALIGNMENT_PROPERTY_NAME ) != Property::INVALID_INDEX )
994 std::string value = child.GetProperty( child.GetPropertyIndex(CELL_VERTICAL_ALIGNMENT_PROPERTY_NAME) ).Get<std::string >();
995 Scripting::GetEnumeration< VerticalAlignment::Type >( value.c_str(),
996 VERTICAL_ALIGNMENT_STRING_TABLE,
997 VERTICAL_ALIGNMENT_STRING_TABLE_COUNT,
1002 Toolkit::TableView::CellPosition cellPosition;
1003 if( child.GetPropertyIndex(ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
1005 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
1008 if( child.GetPropertyIndex(COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
1010 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
1013 if( child.GetPropertyIndex(CELL_INDEX_PROPERTY_NAME) != Property::INVALID_INDEX )
1015 Vector2 indices = child.GetProperty( child.GetPropertyIndex(CELL_INDEX_PROPERTY_NAME) ).Get<Vector2 >();
1016 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
1017 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
1019 AddChild( child, cellPosition );
1020 SetCellAlignment(cellPosition, horizontalAlignment, verticalAlignment);
1026 // Find the first available cell to store the actor in
1027 const unsigned int rowCount = mCellData.GetRows();
1028 const unsigned int columnCount = mCellData.GetColumns();
1029 for( unsigned int row = 0; row < rowCount; ++row )
1031 for( unsigned int column = 0; column < columnCount; ++column )
1033 if( !(mCellData[ row ][ column ].actor) )
1035 // Put the actor in the cell
1038 data.position.columnIndex = column;
1039 data.position.rowIndex = row;
1040 data.horizontalAlignment = horizontalAlignment;
1041 data.verticalAlignment = verticalAlignment;
1042 mCellData[ row ][ column ] = data;
1051 // No empty cells, so increase size of the table
1052 unsigned int newColumnCount = ( columnCount > 0 ) ? columnCount : 1;
1053 ResizeContainers( rowCount + 1, newColumnCount );
1055 // Put the actor in the first cell of the new row
1058 data.position.rowIndex = rowCount;
1059 data.position.columnIndex = 0;
1060 data.horizontalAlignment = horizontalAlignment;
1061 data.verticalAlignment = verticalAlignment;
1062 mCellData[ rowCount ][ 0 ] = data;
1066 void TableView::OnChildRemove( Actor& child )
1068 // dont process if we're in the middle of bigger operation like delete row, column or resize
1069 if( !mLayoutingChild )
1071 // relayout the table only if instances were found
1072 if( RemoveAllInstances( child ) )
1078 Control::OnChildRemove( child );
1081 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
1082 : Control( ControlBehaviour( REQUIRES_STYLE_CHANGE_SIGNALS ) ),
1083 mCellData( initialRows, initialColumns ),
1084 mLayoutingChild( false ),
1085 mRowDirty( true ), // Force recalculation first time
1086 mColumnDirty( true )
1088 SetKeyboardNavigationSupport( true );
1089 ResizeContainers( initialRows, initialColumns );
1092 void TableView::OnInitialize()
1094 // Make self as keyboard focusable and focus group
1095 Actor self = Self();
1096 self.SetKeyboardFocusable(true);
1097 SetAsKeyboardFocusGroup(true);
1100 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
1102 std::vector<CellData> ignored;
1103 ResizeContainers( rows, columns, ignored );
1106 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
1109 mCellData.Resize( rows, columns, removed );
1111 // We don't care if these go smaller, data will be regenerated or is not needed anymore
1112 mRowData.Resize( rows );
1113 mColumnData.Resize( columns );
1116 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
1117 unsigned int rowsRemoved, unsigned int columnsRemoved )
1119 // iterate through all lost cells
1120 std::vector< CellData >::const_iterator iter = lost.begin();
1121 for( ; iter != lost.end(); ++iter )
1123 // if it is a valid actor
1126 // is this actor still somewhere else in the table
1127 Toolkit::TableView::CellPosition position;
1128 if( FindChildPosition( (*iter).actor, position ) )
1130 // it must be spanning multiple cells, position contains the top left most one
1131 // check if position is left of the removed location
1132 if( position.columnIndex < (*iter).position.columnIndex )
1134 // if column span is greater than 1
1135 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1137 // decrease column span
1138 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1141 // check if position is left of the removed location
1142 if( position.rowIndex < (*iter).position.rowIndex )
1144 // if row span is greater than 1
1145 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1147 // decrease row span
1148 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1154 // this actor is gone for good
1155 // add actor to removed container
1156 removed.push_back( (*iter).actor );
1157 // we dont want the child actor anymore
1158 Self().Remove( (*iter).actor );
1164 bool TableView::RemoveAllInstances( const Actor& child )
1167 // walk through the layout data
1168 const unsigned int rowCount = mCellData.GetRows();
1169 const unsigned int columnCount = mCellData.GetColumns();
1170 for( unsigned int row = 0; row < rowCount; ++row )
1172 for( unsigned int column = 0; column < columnCount; ++column )
1174 if( mCellData[ row ][ column ].actor == child )
1176 // clear the cell, NOTE that the cell might be spanning multiple cells
1177 mCellData[ row ][ column ] = CellData();
1185 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1186 void(TableView::*funcFixed)(unsigned int, float),
1187 void(TableView::*funcRelative)(unsigned int, float),
1188 void(TableView::*funcFit)(unsigned int),
1189 const Property::Value& value )
1191 Property::Map* map = value.GetMap();
1194 unsigned int index(0);
1195 for ( unsigned int i = 0, count = map->Count(); i < count; ++i )
1197 Property::Value& item = map->GetValue(i);
1198 Property::Map* childMap = item.GetMap();
1200 std::istringstream( map->GetKey(i) ) >> index;
1203 Property::Value* policy = childMap->Find( "policy" );
1204 Property::Value* childMapValue = childMap->Find( "value" );
1205 if( policy && childMapValue )
1207 std::string policyValue;
1208 policy->Get( policyValue );
1209 Toolkit::TableView::LayoutPolicy policy;
1210 if( Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( policyValue.c_str(),
1211 LAYOUT_POLICY_STRING_TABLE,
1212 LAYOUT_POLICY_STRING_TABLE_COUNT,
1215 if( policy == Toolkit::TableView::FIXED )
1217 (tableViewImpl.*funcFixed)( index, childMapValue->Get<float>() );
1219 else if( policy == Toolkit::TableView::RELATIVE )
1221 (tableViewImpl.*funcRelative)( index, childMapValue->Get<float>() );
1223 else if( policy == Toolkit::TableView::FIT )
1225 (tableViewImpl.*funcFit)( index );
1227 // do nothing for FILL policy
1235 Property::Value TableView::GetRowHeightsPropertyValue()
1238 GetMapPropertyValue( mRowData, map);
1239 return Property::Value(map);
1242 Property::Value TableView::GetColumnWidthsPropertyValue()
1245 GetMapPropertyValue( mColumnData, map);
1246 return Property::Value(map);
1249 void TableView::GetMapPropertyValue( const RowColumnArray& data, Property::Map& map )
1251 const char* fixedPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIXED,
1252 LAYOUT_POLICY_STRING_TABLE,
1253 LAYOUT_POLICY_STRING_TABLE_COUNT );
1254 const char* relativePolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::RELATIVE,
1255 LAYOUT_POLICY_STRING_TABLE,
1256 LAYOUT_POLICY_STRING_TABLE_COUNT );
1257 const char* fillPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FILL,
1258 LAYOUT_POLICY_STRING_TABLE,
1259 LAYOUT_POLICY_STRING_TABLE_COUNT );
1260 const char* fitPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIT,
1261 LAYOUT_POLICY_STRING_TABLE,
1262 LAYOUT_POLICY_STRING_TABLE_COUNT );
1264 const RowColumnArray::SizeType count = data.Size();
1265 for( RowColumnArray::SizeType i = 0; i < count; i++ )
1267 const RowColumnData& dataInstance = data[ i ];
1270 switch( dataInstance.sizePolicy )
1272 case Toolkit::TableView::FIXED:
1274 item[ "policy" ] = fixedPolicy;
1275 item[ "value" ] = dataInstance.size;
1278 case Toolkit::TableView::RELATIVE:
1280 item[ "policy" ] = relativePolicy;
1281 item[ "value" ] = dataInstance.fillRatio;
1284 case Toolkit::TableView::FIT:
1286 item[ "policy" ] = fitPolicy;
1287 item[ "value" ] = 0.f;
1290 case Toolkit::TableView::FILL:
1293 item[ "policy" ] = fillPolicy;
1294 item[ "value" ] = 0.f;
1298 std::ostringstream ss;
1300 map[ ss.str() ] = item;
1304 TableView::~TableView()
1309 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1311 Actor nextFocusableActor;
1313 if ( !currentFocusedActor )
1315 // Nothing is currently focused, so the child in the first cell should be focused.
1316 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1320 Toolkit::TableView::CellPosition position;
1321 if( FindChildPosition( currentFocusedActor, position ) )
1323 // The current focused actor is a child of TableView
1324 bool focusLost = false;
1325 int currentRow = position.rowIndex;
1326 int currentColumn = position.columnIndex;
1327 int numberOfColumns = GetColumns();
1328 int numberOfRows = GetRows();
1330 switch ( direction )
1332 case Toolkit::Control::KeyboardFocus::LEFT:
1334 if(--currentColumn < 0)
1336 currentColumn = numberOfColumns - 1;
1337 if(--currentRow < 0)
1339 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1340 focusLost = (currentRow == 0);
1345 case Toolkit::Control::KeyboardFocus::RIGHT:
1347 if(++currentColumn > numberOfColumns - 1)
1350 if(++currentRow > numberOfRows - 1)
1352 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1353 focusLost = (currentRow == numberOfRows - 1);
1358 case Toolkit::Control::KeyboardFocus::UP:
1360 if(--currentRow < 0)
1362 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1363 focusLost = (currentRow == 0);
1367 case Toolkit::Control::KeyboardFocus::DOWN:
1370 if(++currentRow > numberOfRows - 1)
1372 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1373 focusLost = (currentRow == numberOfRows - 1);
1379 // Move the focus if we haven't lost it.
1382 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1387 // The current focused actor is not within table view, so the child in the first cell should be focused.
1388 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1392 return nextFocusableActor;
1395 Vector3 TableView::GetNaturalSize()
1397 // Natural size is the size of all fixed cell widths or heights. This ignores cells with relative heights.
1398 return Vector3( mFixedTotals.width, mFixedTotals.height, 1.0f );
1401 float TableView::CalculateChildSize( const Actor& child, Dimension::Type dimension )
1403 Toolkit::TableView::CellPosition position;
1404 if( FindChildPosition( child, position) )
1408 case Dimension::WIDTH:
1410 float cellSize = 0.0f;
1411 cellSize = mColumnData[position.columnIndex+position.columnSpan-1].position
1412 - (position.columnIndex > 0 ? mColumnData[position.columnIndex-1].position : 0.f)
1413 - mPadding.width * 2.0f;
1415 if( cellSize < 0.0f )
1423 case Dimension::HEIGHT:
1425 float cellSize = 0.0f;
1427 cellSize = mRowData[position.rowIndex+position.rowSpan-1].position
1428 - (position.rowIndex > 0 ? mRowData[position.rowIndex-1].position : 0.f)
1429 - mPadding.height * 2.0f;
1431 if( cellSize < 0.0f )
1445 return 0.0f; // Child not found
1448 bool TableView::RelayoutDependentOnChildren( Dimension::Type dimension )
1450 if ( Control::RelayoutDependentOnChildren( dimension ) )
1455 return FindFit( mRowData ) || FindFit( mColumnData );
1458 void TableView::SetCellAlignment( Toolkit::TableView::CellPosition position, HorizontalAlignment::Type horizontal, VerticalAlignment::Type vertical )
1460 // Check if we need to expand our data array
1461 if( position.rowIndex >= mCellData.GetRows() )
1463 // Only adding new rows
1464 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
1467 if( position.columnIndex >= mCellData.GetColumns() )
1469 // Only adding new columns
1470 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
1473 // Set the alignment of the cell
1474 CellData& data = mCellData[ position.rowIndex ][ position.columnIndex ];
1475 data.horizontalAlignment = horizontal;
1476 data.verticalAlignment = vertical;
1479 void TableView::CalculateFillSizes( RowColumnArray& data )
1481 // First pass: Count number of fill entries and calculate used relative space
1482 Dali::Vector< RowColumnData* > fillData;
1483 float relativeTotal = 0.0f;
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 == Toolkit::TableView::RELATIVE )
1493 relativeTotal += dataInstance.fillRatio;
1495 else if(dataInstance.sizePolicy == Toolkit::TableView::FILL)
1497 fillData.PushBack( &dataInstance );
1501 // Second pass: Distribute remaining relative space
1502 const unsigned int fillCount = fillData.Size();
1505 if( relativeTotal > 1.0f )
1507 relativeTotal = 1.0f;
1510 const float evenFillRatio = (1.0f - relativeTotal ) / fillCount;
1512 for( unsigned int i = 0; i < fillCount; ++i )
1514 fillData[ i ]->fillRatio = evenFillRatio;
1519 float TableView::CalculateTotalFixedSize( const RowColumnArray& data )
1521 float totalSize = 0.0f;
1523 const unsigned int dataCount = data.Size();
1525 for( unsigned int i = 0; i < dataCount; ++i )
1527 const RowColumnData& dataInstance = data[ i ];
1529 switch( dataInstance.sizePolicy )
1531 // we have absolute size to FIXED and FIT column/row and relative size for RELATIVE and FILL column/row
1532 case Toolkit::TableView::FIXED:
1533 case Toolkit::TableView::FIT:
1535 totalSize += dataInstance.size;
1549 Vector2 TableView::GetCellPadding( Dimension::Type dimension )
1553 case Dimension::WIDTH:
1555 return Vector2( mPadding.x, mPadding.x );
1557 case Dimension::HEIGHT:
1559 return Vector2( mPadding.y, mPadding.y );
1570 void TableView::CalculateFitSizes( RowColumnArray& data, Dimension::Type dimension )
1572 Vector2 cellPadding = GetCellPadding( dimension );
1574 const unsigned int dataCount = data.Size();
1576 for( unsigned int i = 0; i < dataCount; ++i )
1578 RowColumnData& dataInstance = data[ i ];
1580 if( dataInstance.sizePolicy == Toolkit::TableView::FIT )
1582 // Find the size of the biggest actor in the row or column
1583 float maxActorHeight = 0.0f;
1585 unsigned int fitCount = ( dimension == Dimension::WIDTH ) ? mCellData.GetRows() : mCellData.GetColumns();
1587 for( unsigned int j = 0; j < fitCount; ++j )
1589 unsigned int row = ( dimension == Dimension::WIDTH ) ? j : i;
1590 unsigned int column = ( dimension == Dimension::WIDTH ) ? i : j;
1591 DALI_ASSERT_DEBUG( row < mCellData.GetRows() );
1592 DALI_ASSERT_DEBUG( column < mCellData.GetColumns() );
1594 const CellData& cellData = mCellData[ row ][ column ];
1595 const Actor& actor = cellData.actor;
1598 if( FitToChild( actor, dimension ) && ( dimension == Dimension::WIDTH ) ? ( cellData.position.columnSpan == 1 ) : ( cellData.position.rowSpan == 1 ) )
1600 maxActorHeight = std::max( maxActorHeight, actor.GetRelayoutSize( dimension ) + cellPadding.x + cellPadding.y );
1605 dataInstance.size = maxActorHeight;
1610 bool TableView::FindFit( const RowColumnArray& data )
1612 for( unsigned int i = 0, count = data.Size(); i < count; ++i )
1614 if( data[ i ].sizePolicy == Toolkit::TableView::FIT )
1623 } // namespace Internal
1625 } // namespace Toolkit