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/devel-api/scripting/scripting.h>
27 #include <dali/public-api/size-negotiation/relayout-container.h>
28 #include <dali/integration-api/debug.h>
35 * @brief Should the tableview fit around the given actor
37 * @param[in] actor The child actor to test against
38 * @param[dimension] The dimension to test against
40 bool FitToChild( Actor actor, Dimension::Type dimension )
42 return actor.GetResizePolicy( dimension ) != ResizePolicy::FILL_TO_PARENT && actor.GetRelayoutSize( dimension ) > 0.0f;
45 #if defined(DEBUG_ENABLED)
46 // debugging support, very useful when new features are added or bugs are hunted down
47 // currently not called from code so compiler will optimize these away, kept here for future debugging
49 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
50 #define TV_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
51 //#define TABLEVIEW_DEBUG 1
53 #if defined(TABLEVIEW_DEBUG)
54 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
56 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
58 for( unsigned int i = 0; i < array.GetRows(); ++i )
60 for( unsigned int j = 0; j < array.GetColumns(); ++j )
62 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
64 std::string actorName;
68 actorName = data.actor.GetName();
70 TV_LOG("Array[%d,%d]=%c %s %d,%d,%d,%d ", i, j, actor, actorName.c_str(),
71 data.position.rowIndex, data.position.columnIndex,
72 data.position.rowSpan, data.position.columnSpan );
78 // debugging support, very useful when new features are added or bugs are hunted down
79 // currently not called from code so compiler will optimize these away, kept here for future debugging
80 void PrintArray( Array2d<Size>& array )
82 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
84 for( unsigned int i = 0; i < array.GetRows(); ++i )
86 for( unsigned int j = 0; j < array.GetColumns(); ++j )
88 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
93 // debugging support, very useful when new features are added or bugs are hunted down
94 // currently not called from code so compiler will optimize these away, kept here for future debugging
95 void PrintVector( std::vector<float>& array )
97 TV_LOG( "vector, size [%d]\n", array.size() );
99 for( unsigned int i = 0; i < array.size(); ++i )
101 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
105 #endif // defined(TABLEVIEW_DEBUG)
106 #endif // defined(DEBUG_ENABLED)
125 return Toolkit::TableView::New( 0, 0 );
128 // Setup properties, signals and actions using the type-registry.
129 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TableView, Toolkit::Control, Create );
131 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "rows", INTEGER, ROWS )
132 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "columns", INTEGER, COLUMNS )
133 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "cellPadding", VECTOR2, CELL_PADDING )
134 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layoutRows", MAP, LAYOUT_ROWS )
135 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layoutColumns", MAP, LAYOUT_COLUMNS )
136 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "cellIndex", VECTOR2, CELL_INDEX )
137 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "rowSpan", FLOAT, ROW_SPAN )
138 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "columnSpan", FLOAT, COLUMN_SPAN )
139 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "cellHorizontalAlignment", STRING, CELL_HORIZONTAL_ALIGNMENT )
140 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "cellVerticalAlignment", STRING, CELL_VERTICAL_ALIGNMENT )
142 DALI_TYPE_REGISTRATION_END()
144 const Scripting::StringEnum LAYOUT_POLICY_STRING_TABLE[] =
146 { "fixed", Toolkit::TableView::FIXED },
147 { "relative", Toolkit::TableView::RELATIVE },
148 { "fill", Toolkit::TableView::FILL },
149 { "fit", Toolkit::TableView::FIT }
151 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
153 const Scripting::StringEnum HORIZONTAL_ALIGNMENT_STRING_TABLE[] =
155 {"left", HorizontalAlignment::LEFT},
156 {"center", HorizontalAlignment::CENTER},
157 {"right", HorizontalAlignment::RIGHT}
159 const unsigned int HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(HORIZONTAL_ALIGNMENT_STRING_TABLE) / sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE[0] );
161 const Scripting::StringEnum VERTICAL_ALIGNMENT_STRING_TABLE[] =
163 {"top", VerticalAlignment::TOP},
164 {"center", VerticalAlignment::CENTER},
165 {"bottom", VerticalAlignment::BOTTOM}
167 const unsigned int VERTICAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(VERTICAL_ALIGNMENT_STRING_TABLE) / sizeof( VERTICAL_ALIGNMENT_STRING_TABLE[0] );
169 } // Unnamed namespace
171 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
173 // Create the implementation, temporarily owned by this handle on stack
174 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
176 // Pass ownership to CustomActor handle
177 Toolkit::TableView handle( *impl );
179 // Second-phase init of the implementation
180 // This can only be done after the CustomActor connection has been made...
186 bool TableView::AddChild( Actor& child, const Toolkit::TableView::CellPosition& position )
188 // check that the child is valid
189 DALI_ASSERT_ALWAYS( child );
191 // if child is already parented, we adopt it
194 // check if we need to expand our data array
195 if( position.rowIndex >= mCellData.GetRows() )
197 // only adding new rows
198 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
201 if( position.columnIndex >= mCellData.GetColumns() )
203 // only adding new columns
204 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
207 // check if there already is something in this cell
208 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
210 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
213 RelayoutingLock lock( *this );
217 // if child spans multiple rows of columns
218 if( ( position.rowSpan > 1 ) && ( position.rowIndex + position.rowSpan > mCellData.GetRows() ) )
220 // increase table size for the full span, only increasing rows
221 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
224 if( ( position.columnSpan > 1 ) && ( position.columnIndex + position.columnSpan > mCellData.GetColumns() ) )
226 // increase table size for the full span, only increasing columns
227 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
230 // Fill in all cells that need the data
233 data.position = position;
235 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
237 // store same information to all cells, this way we can identify
238 // if a cell is the prime location of an actor or a spanned one
239 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
241 // store same information to all cells, this way we can identify
242 // if a cell is the prime location of an actor or a spanned one
243 mCellData[ row ][ column ] = data;
247 // Relayout the whole table
248 if( mRowData[position.rowIndex].sizePolicy == Toolkit::TableView::FIT && position.rowSpan == 1 )
252 if( mColumnData[position.columnIndex].sizePolicy == Toolkit::TableView::FIT && position.columnSpan == 1 )
259 return true; // Addition successful
262 Actor TableView::GetChildAt( const Toolkit::TableView::CellPosition& position )
264 if( ( position.rowIndex < mCellData.GetRows() ) && ( position.columnIndex < mCellData.GetColumns() ) )
266 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
269 // Return an empty handle
273 Actor TableView::RemoveChildAt( const Toolkit::TableView::CellPosition& position )
275 // get the child handle
276 Actor child = GetChildAt( position );
277 // if no real actor there, nothing else to be done
280 RelayoutingLock lock( *this );
281 // Remove the child, this will trigger a call to OnChildRemove
282 Self().Remove( child );
284 // relayout the table only if instances were found
285 if( RemoveAllInstances( child ) )
287 if( mRowData[position.rowIndex].sizePolicy == Toolkit::TableView::FIT )
291 if( mColumnData[position.columnIndex].sizePolicy == Toolkit::TableView::FIT )
298 // return the child back to caller
302 bool TableView::FindChildPosition( const Actor& child, Toolkit::TableView::CellPosition& positionOut )
304 // Only find valid child actors
307 // Walk through the layout data
308 const unsigned int rowCount = mCellData.GetRows();
309 const unsigned int columnCount = mCellData.GetColumns();
311 for( unsigned int row = 0; row < rowCount; ++row )
313 for( unsigned int column = 0; column < columnCount; ++column )
315 if( mCellData[ row ][ column ].actor == child )
317 positionOut = mCellData[ row ][ column ].position;
327 void TableView::InsertRow( unsigned int rowIndex )
329 RelayoutingLock lock( *this );
331 mCellData.InsertRow( rowIndex );
333 // Need to update the cell infos for the items that moved
334 const unsigned int rowCount = mCellData.GetRows();
335 const unsigned int columnCount = mCellData.GetColumns();
337 for( unsigned int row = 0; row < rowCount; ++row )
339 for( unsigned int column = 0; column < columnCount; ++column )
341 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
343 // If cell is spanning and above and spans to inserted row
344 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
345 ( position.rowIndex + position.rowSpan > rowIndex ) )
350 // Copy cell to occupy the new column
351 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
353 else if( row > rowIndex ) // If below of inserted row, increase row index
361 // Expand row data array
362 mRowData.Insert( mRowData.Begin() + rowIndex, RowColumnData() );
364 // Sizes may have changed, so relayout
369 void TableView::DeleteRow( unsigned int rowIndex )
371 std::vector< Actor > ignored;
372 DeleteRow( rowIndex, ignored );
375 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
377 RelayoutingLock lock( *this );
380 std::vector< CellData > lost;
381 mCellData.DeleteRow( rowIndex, lost );
383 // Need to update the cell infos for the items that moved
384 const unsigned int rowCount = mCellData.GetRows();
385 const unsigned int columnCount = mCellData.GetColumns();
387 for( unsigned int row = 0; row < rowCount; ++row )
389 for( unsigned int column = 0; column < columnCount; ++column )
391 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
393 // If cell is spanning and above and spans to deleted row
394 if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
395 ( position.rowIndex + position.rowSpan > rowIndex ) )
398 if( position.rowSpan > 1 )
403 else if( row >= rowIndex ) // If below of or at the inserted row, decrease row index
406 if( position.rowIndex > 0 )
414 // 1 row removed, 0 columns
415 RemoveAndGetLostActors( lost, removed, 1u, 0u );
417 // Contract row data array
418 mRowData.Erase( mRowData.Begin() + rowIndex );
420 // Sizes may have changed, so relayout
422 // it is possible that the deletion of row leads to remove of child which might further lead to the change of FIT column
428 void TableView::InsertColumn( unsigned int columnIndex )
430 RelayoutingLock lock( *this );
432 // Insert the new column
433 mCellData.InsertColumn( columnIndex );
435 // Need to update the cell infos for the items that moved
436 const unsigned int rowCount = mCellData.GetRows();
437 const unsigned int columnCount = mCellData.GetColumns();
439 for( unsigned int row = 0; row < rowCount; ++row )
441 for( unsigned int column = 0; column < columnCount; ++column )
443 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
445 // If cell is spanning and left side and spans to inserted column
446 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
447 ( position.columnIndex + position.columnSpan > columnIndex ) )
450 position.columnSpan++;
452 // Copy cell to occupy the new column
453 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
455 else if( column > columnIndex ) // If on the right side of inserted column, increase column index
458 position.columnIndex++;
463 // Expand column data array
464 mColumnData.Insert( mColumnData.Begin() + columnIndex, RowColumnData() );
466 // Sizes may have changed so relayout
471 void TableView::DeleteColumn( unsigned int columnIndex )
473 std::vector< Actor > ignored;
474 DeleteColumn( columnIndex, ignored );
477 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
479 RelayoutingLock lock( *this );
482 std::vector< CellData > lost;
483 mCellData.DeleteColumn( columnIndex, lost );
485 // Need to update the cell infos for the items that moved
486 const unsigned int rowCount = mCellData.GetRows();
487 const unsigned int columnCount = mCellData.GetColumns();
489 for( unsigned int row = 0; row < rowCount; ++row )
491 for( unsigned int column = 0; column < columnCount; ++column )
493 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
495 // If cell is spanning and left side and spans to inserted column
496 if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
497 ( position.columnIndex + position.columnSpan > columnIndex ) )
500 if( position.columnSpan > 1 )
502 position.columnSpan--;
505 else if( column >= columnIndex ) // If on the right side of or at the inserted column, decrease column index
508 if( position.columnIndex > 0 )
510 position.columnIndex--;
516 // 0 rows, 1 column removed
517 RemoveAndGetLostActors( lost, removed, 0u, 1u );
519 // Contract column data array
520 mColumnData.Erase( mColumnData.Begin() + columnIndex );
522 // Size may have changed so relayout
524 // it is possible that the deletion of column leads to remove of child which might further lead to the change of FIT row
530 void TableView::Resize( unsigned int rows, unsigned int columns )
532 std::vector< Actor > ignored;
533 Resize( rows, columns, ignored );
536 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
538 RelayoutingLock lock( *this );
540 unsigned int oldRows = GetRows();
541 unsigned int oldColumns = GetColumns();
544 std::vector< CellData > lost;
545 ResizeContainers( rows, columns, lost );
547 // Calculate if we lost rows
548 unsigned int rowsRemoved = 0;
549 unsigned int newRows = GetRows();
551 if( oldRows < newRows )
553 rowsRemoved = newRows - oldRows;
556 // Calculate if we lost columns
557 unsigned int columnsRemoved = 0;
558 unsigned int newColumns = GetColumns();
559 if( oldColumns < newColumns )
561 rowsRemoved = newColumns - oldColumns;
564 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
566 // Sizes may have changed so request a relayout
572 void TableView::SetCellPadding( Size padding )
574 // If padding really changed
575 if( padding != mPadding )
583 Size TableView::GetCellPadding()
588 void TableView::SetFitHeight( unsigned int rowIndex )
590 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
592 if( mRowData[ rowIndex ].sizePolicy != Toolkit::TableView::FIT )
594 mRowData[ rowIndex ].sizePolicy = Toolkit::TableView::FIT;
601 bool TableView::IsFitHeight( unsigned int rowIndex ) const
603 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
605 return mRowData[ rowIndex ].sizePolicy == Toolkit::TableView::FIT;
608 void TableView::SetFitWidth( unsigned int columnIndex )
610 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
612 if( mColumnData[ columnIndex ].sizePolicy != Toolkit::TableView::FIT )
614 mColumnData[ columnIndex ].sizePolicy = Toolkit::TableView::FIT;
621 bool TableView::IsFitWidth( unsigned int columnIndex ) const
623 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
625 return mColumnData[ columnIndex ].sizePolicy == Toolkit::TableView::FIT;
628 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
630 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
632 RowColumnData& data = mRowData[ rowIndex ];
634 data.sizePolicy = Toolkit::TableView::FIXED;
640 float TableView::GetFixedHeight( unsigned int rowIndex ) const
642 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
644 return mRowData[ rowIndex ].size;
647 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
649 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
651 RowColumnData& data = mColumnData[ columnIndex ];
653 data.sizePolicy = Toolkit::TableView::FIXED;
659 float TableView::GetFixedWidth( unsigned int columnIndex ) const
661 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
663 return mColumnData[ columnIndex ].size;
666 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
668 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
670 RowColumnData& data = mRowData[ rowIndex ];
671 data.fillRatio = heightPercentage;
672 data.sizePolicy = Toolkit::TableView::RELATIVE;
678 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
680 DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
682 return mRowData[ rowIndex ].fillRatio;
685 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
687 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
689 RowColumnData& data = mColumnData[ columnIndex ];
690 data.fillRatio = widthPercentage;
691 data.sizePolicy = Toolkit::TableView::RELATIVE;
697 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
699 DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
701 return mColumnData[ columnIndex ].fillRatio;
704 void TableView::OnCalculateRelayoutSize( Dimension::Type dimension )
706 if( (dimension & Dimension::WIDTH) && mColumnDirty )
709 * FIXED and FIT have size in pixel
710 * Nothing to do with FIXED, as its value is assigned by user and will not get changed
712 * Need to update the size for FIT column here
714 CalculateFitSizes( mColumnData, Dimension::WIDTH );
716 /* RELATIVE and FILL have size in ratio
717 * Their size in pixel is not available until we get the negotiated size for the whole table
718 * Nothing to do with RELATIVE, as its ratio is assigned by user and will not get changed
720 * Need to update the ratio for FILL column here
722 CalculateFillSizes( mColumnData );
724 mFixedTotals.width = CalculateTotalFixedSize( mColumnData );
727 if( (dimension & Dimension::HEIGHT) && mRowDirty )
729 // refer to the comment above
730 CalculateFitSizes( mRowData, Dimension::HEIGHT );
732 // refer to the comment above
733 CalculateFillSizes( mRowData );
735 mFixedTotals.height = CalculateTotalFixedSize( mRowData );
739 void TableView::OnLayoutNegotiated( float size, Dimension::Type dimension )
741 // Update the column sizes
742 if( (dimension & Dimension::WIDTH) && mColumnDirty )
744 float remainingSize = size - mFixedTotals.width;
745 if( remainingSize < 0.0f )
747 remainingSize = 0.0f;
750 // update every column position in ColumnData array
751 float cumulatedWidth = 0.0f;
752 for( unsigned int column = 0, columnCount = mCellData.GetColumns(); column < columnCount; ++column )
754 if( mColumnData[ column ].sizePolicy == Toolkit::TableView::FILL || mColumnData[ column ].sizePolicy == Toolkit::TableView::RELATIVE)
756 mColumnData[ column ].size = mColumnData[ column ].fillRatio * remainingSize;
759 cumulatedWidth += mColumnData[ column ].size;
760 mColumnData[column].position = cumulatedWidth;
763 mColumnDirty = false;
766 // Update the row sizes
767 if( (dimension & Dimension::HEIGHT) && mRowDirty )
769 float remainingSize = size - mFixedTotals.height;
770 if( remainingSize < 0.0f )
772 remainingSize = 0.0f;
775 // update every row position in RowData array
776 float cumulatedHeight = 0.0f;
777 for( unsigned int row = 0, rowCount = mCellData.GetRows(); row < rowCount; ++row )
779 if( mRowData[ row ].sizePolicy == Toolkit::TableView::FILL || mRowData[ row ].sizePolicy == Toolkit::TableView::RELATIVE)
781 mRowData[ row ].size = mRowData[ row ].fillRatio * remainingSize;
784 cumulatedHeight += mRowData[ row ].size;
785 mRowData[row].position = cumulatedHeight;
792 void TableView::OnSizeSet( const Vector3& size )
794 // If this table view is size negotiated by another actor or control, then the
795 // rows and columns must be recalculated or the new size will not take effect.
796 mRowDirty = mColumnDirty = true;
800 void TableView::OnRelayout( const Vector2& size, RelayoutContainer& container )
802 // Go through the layout data
803 for( unsigned int row = 0, rowCount = mCellData.GetRows(); row < rowCount; ++row )
805 for( unsigned int column = 0, columnCount = mCellData.GetColumns(); column < columnCount; ++column )
807 CellData& cellData= mCellData[ row ][ column ];
808 Actor& actor = cellData.actor;
809 const Toolkit::TableView::CellPosition position = cellData.position;
811 // If there is an actor and this is the main cell of the actor.
812 // An actor can be in multiple cells if its row or column span is more than 1.
813 // We however must lay out each actor only once.
814 if( actor && position.rowIndex == row && position.columnIndex == column )
816 // Anchor actor to top left of the cell
817 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
818 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
821 actor.GetPadding( padding );
823 float left = column > 0 ? mColumnData[column-1].position : 0.f;
824 float right = mColumnData[column+position.columnSpan-1].position;
825 float top = row > 0 ? mRowData[row-1].position : 0.f;
826 float bottom = mRowData[row+position.rowSpan-1].position;
828 if( cellData.horizontalAlignment == HorizontalAlignment::LEFT )
830 actor.SetX( left + mPadding.width + padding.left );
832 else if( cellData.horizontalAlignment == HorizontalAlignment::RIGHT )
834 actor.SetX( right - mPadding.width - padding.right - actor.GetRelayoutSize( Dimension::WIDTH ) );
836 else //if( cellData.horizontalAlignment == HorizontalAlignment::CENTER )
838 actor.SetX( (left + right + padding.left - padding.right - actor.GetRelayoutSize( Dimension::WIDTH )) * 0.5f );
841 if( cellData.verticalAlignment == VerticalAlignment::TOP )
843 actor.SetY( top + mPadding.height + padding.top );
845 else if( cellData.verticalAlignment == VerticalAlignment::BOTTOM )
847 actor.SetY( bottom - mPadding.height - padding.bottom - actor.GetRelayoutSize( Dimension::HEIGHT ) );
849 else //if( cellData.verticalAlignment = VerticalAlignment::CENTER )
851 actor.SetY( (top + bottom + padding.top - padding.bottom - actor.GetRelayoutSize( Dimension::HEIGHT )) * 0.5f );
858 unsigned int TableView::GetRows()
860 return mCellData.GetRows();
863 unsigned int TableView::GetColumns()
865 return mCellData.GetColumns();
868 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
870 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
874 TableView& tableViewImpl( GetImpl( tableView ) );
877 case Toolkit::TableView::Property::ROWS:
880 if( value.Get( rows ) && rows >= 0 )
882 if( static_cast<unsigned int>(rows) != tableViewImpl.GetRows() )
884 tableViewImpl.Resize( rows, tableViewImpl.GetColumns() );
889 case Toolkit::TableView::Property::COLUMNS:
892 if( value.Get( columns ) && columns >= 0 )
894 if( static_cast<unsigned int>( columns ) != tableViewImpl.GetColumns() )
896 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<int>() );
901 case Toolkit::TableView::Property::CELL_PADDING:
903 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
906 case Toolkit::TableView::Property::LAYOUT_ROWS:
908 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, &TableView::SetFitHeight, value );
911 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
913 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, &TableView::SetFitWidth, value );
920 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
922 Property::Value value;
924 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
928 TableView& tableViewImpl( GetImpl( tableView ) );
931 case Toolkit::TableView::Property::ROWS:
933 value = static_cast<int>( tableViewImpl.GetRows() );
936 case Toolkit::TableView::Property::COLUMNS:
938 value = static_cast<int>( tableViewImpl.GetColumns() );
941 case Toolkit::TableView::Property::CELL_PADDING:
943 value = tableViewImpl.GetCellPadding();
946 case Toolkit::TableView::Property::LAYOUT_ROWS:
948 value = tableViewImpl.GetRowHeightsPropertyValue();
951 case Toolkit::TableView::Property::LAYOUT_COLUMNS:
953 value = tableViewImpl.GetColumnWidthsPropertyValue();
962 void TableView::OnChildAdd( Actor& child )
964 Control::OnChildAdd( child );
966 if( mLayoutingChild )
968 // we're in the middle of laying out children so no point doing anything here
972 // Check child properties on actor to decide its position inside the table
973 HorizontalAlignment::Type horizontalAlignment = HorizontalAlignment::LEFT;
974 VerticalAlignment::Type verticalAlignment = VerticalAlignment::TOP;
976 if( child.GetPropertyType( Toolkit::TableView::ChildProperty::CELL_HORIZONTAL_ALIGNMENT ) != Property::NONE )
978 std::string value = child.GetProperty( Toolkit::TableView::ChildProperty::CELL_HORIZONTAL_ALIGNMENT ).Get<std::string >();
979 Scripting::GetEnumeration< HorizontalAlignment::Type >( value.c_str(),
980 HORIZONTAL_ALIGNMENT_STRING_TABLE,
981 HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT,
982 horizontalAlignment );
985 if( child.GetPropertyType( Toolkit::TableView::ChildProperty::CELL_VERTICAL_ALIGNMENT ) != Property::NONE )
987 std::string value = child.GetProperty( Toolkit::TableView::ChildProperty::CELL_VERTICAL_ALIGNMENT ).Get<std::string >();
988 Scripting::GetEnumeration< VerticalAlignment::Type >( value.c_str(),
989 VERTICAL_ALIGNMENT_STRING_TABLE,
990 VERTICAL_ALIGNMENT_STRING_TABLE_COUNT,
994 Toolkit::TableView::CellPosition cellPosition;
995 if( child.GetPropertyType( Toolkit::TableView::ChildProperty::ROW_SPAN ) != Property::NONE )
997 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( Toolkit::TableView::ChildProperty::ROW_SPAN ).Get<float>() );
1000 if( child.GetPropertyType( Toolkit::TableView::ChildProperty::COLUMN_SPAN ) != Property::NONE )
1002 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( Toolkit::TableView::ChildProperty::COLUMN_SPAN ).Get<float>() );
1005 if( child.GetPropertyType( Toolkit::TableView::ChildProperty::CELL_INDEX ) != Property::NONE )
1007 Vector2 indices = child.GetProperty( Toolkit::TableView::ChildProperty::CELL_INDEX ).Get<Vector2 >();
1008 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
1009 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
1011 AddChild( child, cellPosition );
1012 SetCellAlignment(cellPosition, horizontalAlignment, verticalAlignment);
1018 // Find the first available cell to store the actor in
1019 const unsigned int rowCount = mCellData.GetRows();
1020 const unsigned int columnCount = mCellData.GetColumns();
1021 for( unsigned int row = 0; row < rowCount; ++row )
1023 for( unsigned int column = 0; column < columnCount; ++column )
1025 if( !(mCellData[ row ][ column ].actor) )
1027 // Put the actor in the cell
1030 data.position.columnIndex = column;
1031 data.position.rowIndex = row;
1032 data.horizontalAlignment = horizontalAlignment;
1033 data.verticalAlignment = verticalAlignment;
1034 mCellData[ row ][ column ] = data;
1043 // No empty cells, so increase size of the table
1044 unsigned int newColumnCount = ( columnCount > 0 ) ? columnCount : 1;
1045 ResizeContainers( rowCount + 1, newColumnCount );
1047 // Put the actor in the first cell of the new row
1050 data.position.rowIndex = rowCount;
1051 data.position.columnIndex = 0;
1052 data.horizontalAlignment = horizontalAlignment;
1053 data.verticalAlignment = verticalAlignment;
1054 mCellData[ rowCount ][ 0 ] = data;
1058 void TableView::OnChildRemove( Actor& child )
1060 // dont process if we're in the middle of bigger operation like delete row, column or resize
1061 if( !mLayoutingChild )
1063 // relayout the table only if instances were found
1064 if( RemoveAllInstances( child ) )
1070 Control::OnChildRemove( child );
1073 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
1074 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
1075 mCellData( initialRows, initialColumns ),
1076 mLayoutingChild( false ),
1077 mRowDirty( true ), // Force recalculation first time
1078 mColumnDirty( true )
1080 SetKeyboardNavigationSupport( true );
1081 ResizeContainers( initialRows, initialColumns );
1084 void TableView::OnInitialize()
1086 // Make self as keyboard focusable and focus group
1087 Actor self = Self();
1088 self.SetKeyboardFocusable(true);
1089 SetAsKeyboardFocusGroup(true);
1092 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
1094 std::vector<CellData> ignored;
1095 ResizeContainers( rows, columns, ignored );
1098 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
1101 mCellData.Resize( rows, columns, removed );
1103 // We don't care if these go smaller, data will be regenerated or is not needed anymore
1104 mRowData.Resize( rows );
1105 mColumnData.Resize( columns );
1108 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
1109 unsigned int rowsRemoved, unsigned int columnsRemoved )
1111 // iterate through all lost cells
1112 std::vector< CellData >::const_iterator iter = lost.begin();
1113 for( ; iter != lost.end(); ++iter )
1115 // if it is a valid actor
1118 // is this actor still somewhere else in the table
1119 Toolkit::TableView::CellPosition position;
1120 if( FindChildPosition( (*iter).actor, position ) )
1122 // it must be spanning multiple cells, position contains the top left most one
1123 // check if position is left of the removed location
1124 if( position.columnIndex < (*iter).position.columnIndex )
1126 // if column span is greater than 1
1127 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1129 // decrease column span
1130 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1133 // check if position is left of the removed location
1134 if( position.rowIndex < (*iter).position.rowIndex )
1136 // if row span is greater than 1
1137 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1139 // decrease row span
1140 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1146 // this actor is gone for good
1147 // add actor to removed container
1148 removed.push_back( (*iter).actor );
1149 // we dont want the child actor anymore
1150 Self().Remove( (*iter).actor );
1156 bool TableView::RemoveAllInstances( const Actor& child )
1159 // walk through the layout data
1160 const unsigned int rowCount = mCellData.GetRows();
1161 const unsigned int columnCount = mCellData.GetColumns();
1162 for( unsigned int row = 0; row < rowCount; ++row )
1164 for( unsigned int column = 0; column < columnCount; ++column )
1166 if( mCellData[ row ][ column ].actor == child )
1168 // clear the cell, NOTE that the cell might be spanning multiple cells
1169 mCellData[ row ][ column ] = CellData();
1177 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1178 void(TableView::*funcFixed)(unsigned int, float),
1179 void(TableView::*funcRelative)(unsigned int, float),
1180 void(TableView::*funcFit)(unsigned int),
1181 const Property::Value& value )
1183 Property::Map* map = value.GetMap();
1186 unsigned int index(0);
1187 for ( unsigned int i = 0, count = map->Count(); i < count; ++i )
1189 Property::Value& item = map->GetValue(i);
1190 Property::Map* childMap = item.GetMap();
1192 std::istringstream( map->GetKey(i) ) >> index;
1195 Property::Value* policy = childMap->Find( "policy" );
1196 Property::Value* childMapValue = childMap->Find( "value" );
1197 if( policy && childMapValue )
1199 std::string policyValue;
1200 policy->Get( policyValue );
1201 Toolkit::TableView::LayoutPolicy policy;
1202 if( Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( policyValue.c_str(),
1203 LAYOUT_POLICY_STRING_TABLE,
1204 LAYOUT_POLICY_STRING_TABLE_COUNT,
1207 if( policy == Toolkit::TableView::FIXED )
1209 (tableViewImpl.*funcFixed)( index, childMapValue->Get<float>() );
1211 else if( policy == Toolkit::TableView::RELATIVE )
1213 (tableViewImpl.*funcRelative)( index, childMapValue->Get<float>() );
1215 else if( policy == Toolkit::TableView::FIT )
1217 (tableViewImpl.*funcFit)( index );
1219 // do nothing for FILL policy
1227 Property::Value TableView::GetRowHeightsPropertyValue()
1230 GetMapPropertyValue( mRowData, map);
1231 return Property::Value(map);
1234 Property::Value TableView::GetColumnWidthsPropertyValue()
1237 GetMapPropertyValue( mColumnData, map);
1238 return Property::Value(map);
1241 void TableView::GetMapPropertyValue( const RowColumnArray& data, Property::Map& map )
1243 const char* fixedPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIXED,
1244 LAYOUT_POLICY_STRING_TABLE,
1245 LAYOUT_POLICY_STRING_TABLE_COUNT );
1246 const char* relativePolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::RELATIVE,
1247 LAYOUT_POLICY_STRING_TABLE,
1248 LAYOUT_POLICY_STRING_TABLE_COUNT );
1249 const char* fillPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FILL,
1250 LAYOUT_POLICY_STRING_TABLE,
1251 LAYOUT_POLICY_STRING_TABLE_COUNT );
1252 const char* fitPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIT,
1253 LAYOUT_POLICY_STRING_TABLE,
1254 LAYOUT_POLICY_STRING_TABLE_COUNT );
1256 const RowColumnArray::SizeType count = data.Size();
1257 for( RowColumnArray::SizeType i = 0; i < count; i++ )
1259 const RowColumnData& dataInstance = data[ i ];
1262 switch( dataInstance.sizePolicy )
1264 case Toolkit::TableView::FIXED:
1266 item[ "policy" ] = fixedPolicy;
1267 item[ "value" ] = dataInstance.size;
1270 case Toolkit::TableView::RELATIVE:
1272 item[ "policy" ] = relativePolicy;
1273 item[ "value" ] = dataInstance.fillRatio;
1276 case Toolkit::TableView::FIT:
1278 item[ "policy" ] = fitPolicy;
1279 item[ "value" ] = 0.f;
1282 case Toolkit::TableView::FILL:
1285 item[ "policy" ] = fillPolicy;
1286 item[ "value" ] = 0.f;
1290 std::ostringstream ss;
1292 map[ ss.str() ] = item;
1296 TableView::~TableView()
1301 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1303 Actor nextFocusableActor;
1305 if ( !currentFocusedActor )
1307 // Nothing is currently focused, so the child in the first cell should be focused.
1308 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1312 Toolkit::TableView::CellPosition position;
1313 if( FindChildPosition( currentFocusedActor, position ) )
1315 // The current focused actor is a child of TableView
1316 bool focusLost = false;
1317 int currentRow = position.rowIndex;
1318 int currentColumn = position.columnIndex;
1319 int numberOfColumns = GetColumns();
1320 int numberOfRows = GetRows();
1322 switch ( direction )
1324 case Toolkit::Control::KeyboardFocus::LEFT:
1326 if(--currentColumn < 0)
1328 currentColumn = numberOfColumns - 1;
1329 if(--currentRow < 0)
1331 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1332 focusLost = (currentRow == 0);
1337 case Toolkit::Control::KeyboardFocus::RIGHT:
1339 if(++currentColumn > numberOfColumns - 1)
1342 if(++currentRow > numberOfRows - 1)
1344 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1345 focusLost = (currentRow == numberOfRows - 1);
1350 case Toolkit::Control::KeyboardFocus::UP:
1352 if(--currentRow < 0)
1354 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1355 focusLost = (currentRow == 0);
1359 case Toolkit::Control::KeyboardFocus::DOWN:
1362 if(++currentRow > numberOfRows - 1)
1364 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1365 focusLost = (currentRow == numberOfRows - 1);
1375 // Move the focus if we haven't lost it.
1378 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1383 // The current focused actor is not within table view, so the child in the first cell should be focused.
1384 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1388 return nextFocusableActor;
1391 Vector3 TableView::GetNaturalSize()
1393 // Natural size is the size of all fixed cell widths or heights. This ignores cells with relative heights.
1394 return Vector3( mFixedTotals.width, mFixedTotals.height, 1.0f );
1397 float TableView::CalculateChildSize( const Actor& child, Dimension::Type dimension )
1399 Toolkit::TableView::CellPosition position;
1400 if( FindChildPosition( child, position) )
1404 case Dimension::WIDTH:
1406 float cellSize = 0.0f;
1407 cellSize = mColumnData[position.columnIndex+position.columnSpan-1].position
1408 - (position.columnIndex > 0 ? mColumnData[position.columnIndex-1].position : 0.f)
1409 - mPadding.width * 2.0f;
1411 if( cellSize < 0.0f )
1419 case Dimension::HEIGHT:
1421 float cellSize = 0.0f;
1423 cellSize = mRowData[position.rowIndex+position.rowSpan-1].position
1424 - (position.rowIndex > 0 ? mRowData[position.rowIndex-1].position : 0.f)
1425 - mPadding.height * 2.0f;
1427 if( cellSize < 0.0f )
1441 return 0.0f; // Child not found
1444 bool TableView::RelayoutDependentOnChildren( Dimension::Type dimension )
1446 if ( Control::RelayoutDependentOnChildren( dimension ) )
1451 return FindFit( mRowData ) || FindFit( mColumnData );
1454 void TableView::SetCellAlignment( Toolkit::TableView::CellPosition position, HorizontalAlignment::Type horizontal, VerticalAlignment::Type vertical )
1456 // Check if we need to expand our data array
1457 if( position.rowIndex >= mCellData.GetRows() )
1459 // Only adding new rows
1460 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
1463 if( position.columnIndex >= mCellData.GetColumns() )
1465 // Only adding new columns
1466 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
1469 // Set the alignment of the cell
1470 CellData& data = mCellData[ position.rowIndex ][ position.columnIndex ];
1471 data.horizontalAlignment = horizontal;
1472 data.verticalAlignment = vertical;
1475 void TableView::CalculateFillSizes( RowColumnArray& data )
1477 // First pass: Count number of fill entries and calculate used relative space
1478 Dali::Vector< RowColumnData* > fillData;
1479 float relativeTotal = 0.0f;
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 == Toolkit::TableView::RELATIVE )
1489 relativeTotal += dataInstance.fillRatio;
1491 else if(dataInstance.sizePolicy == Toolkit::TableView::FILL)
1493 fillData.PushBack( &dataInstance );
1497 // Second pass: Distribute remaining relative space
1498 const unsigned int fillCount = fillData.Size();
1501 if( relativeTotal > 1.0f )
1503 relativeTotal = 1.0f;
1506 const float evenFillRatio = (1.0f - relativeTotal ) / fillCount;
1508 for( unsigned int i = 0; i < fillCount; ++i )
1510 fillData[ i ]->fillRatio = evenFillRatio;
1515 float TableView::CalculateTotalFixedSize( const RowColumnArray& data )
1517 float totalSize = 0.0f;
1519 const unsigned int dataCount = data.Size();
1521 for( unsigned int i = 0; i < dataCount; ++i )
1523 const RowColumnData& dataInstance = data[ i ];
1525 switch( dataInstance.sizePolicy )
1527 // we have absolute size to FIXED and FIT column/row and relative size for RELATIVE and FILL column/row
1528 case Toolkit::TableView::FIXED:
1529 case Toolkit::TableView::FIT:
1531 totalSize += dataInstance.size;
1545 Vector2 TableView::GetCellPadding( Dimension::Type dimension )
1549 case Dimension::WIDTH:
1551 return Vector2( mPadding.x, mPadding.x );
1553 case Dimension::HEIGHT:
1555 return Vector2( mPadding.y, mPadding.y );
1566 void TableView::CalculateFitSizes( RowColumnArray& data, Dimension::Type dimension )
1568 Vector2 cellPadding = GetCellPadding( dimension );
1570 const unsigned int dataCount = data.Size();
1572 for( unsigned int i = 0; i < dataCount; ++i )
1574 RowColumnData& dataInstance = data[ i ];
1576 if( dataInstance.sizePolicy == Toolkit::TableView::FIT )
1578 // Find the size of the biggest actor in the row or column
1579 float maxActorHeight = 0.0f;
1581 unsigned int fitCount = ( dimension == Dimension::WIDTH ) ? mCellData.GetRows() : mCellData.GetColumns();
1583 for( unsigned int j = 0; j < fitCount; ++j )
1585 unsigned int row = ( dimension == Dimension::WIDTH ) ? j : i;
1586 unsigned int column = ( dimension == Dimension::WIDTH ) ? i : j;
1587 DALI_ASSERT_DEBUG( row < mCellData.GetRows() );
1588 DALI_ASSERT_DEBUG( column < mCellData.GetColumns() );
1590 const CellData& cellData = mCellData[ row ][ column ];
1591 const Actor& actor = cellData.actor;
1594 if( FitToChild( actor, dimension ) && ( dimension == Dimension::WIDTH ) ? ( cellData.position.columnSpan == 1 ) : ( cellData.position.rowSpan == 1 ) )
1596 maxActorHeight = std::max( maxActorHeight, actor.GetRelayoutSize( dimension ) + cellPadding.x + cellPadding.y );
1601 dataInstance.size = maxActorHeight;
1606 bool TableView::FindFit( const RowColumnArray& data )
1608 for( unsigned int i = 0, count = data.Size(); i < count; ++i )
1610 if( data[ i ].sizePolicy == Toolkit::TableView::FIT )
1619 } // namespace Internal
1621 } // namespace Toolkit