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/animation/constraint.h>
24 #include <dali/public-api/animation/time-period.h>
25 #include <dali/public-api/object/ref-object.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/scripting/scripting.h>
28 #include <dali/integration-api/debug.h>
34 const float DEFAULT_CONSTRAINT_DURATION = 0.0f;
37 * Constraint that sets a child property relative to parents Width or Height
39 struct RelativeToWidthOrHeight
42 * Constraint that is relative (%) to parent width/height and applies a
43 * unit based padding before the relative calculation.
44 * @param scale of parent minus padding between 0 and 1
45 * @param padding in world coordinate units
46 * @param fixed part in world coordinate units
48 RelativeToWidthOrHeight( float scale, float padding, float fixed )
49 : mScaleFactor( scale ),
55 inline float operator()( const float& parentWidthOrHeight )
57 return mFixed + ( parentWidthOrHeight - mPadding ) * mScaleFactor;
60 float operator()( const float& current,
61 const PropertyInput& parentWidthOrHeight )
63 return operator()( parentWidthOrHeight.GetFloat() );
71 #if defined(DEBUG_ENABLED)
72 // debugging support, very useful when new features are added or bugs are hunted down
73 // currently not called from code so compiler will optimize these away, kept here for future debugging
75 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
76 #define TV_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
78 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
80 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
82 for( unsigned int i = 0; i < array.GetRows(); ++i )
84 for( unsigned int j = 0; j < array.GetColumns(); ++j )
86 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
92 TV_LOG("Array[%d,%d]=%c %d,%d,%d,%d ", i, j, actor,
93 data.position.rowIndex, data.position.columnIndex,
94 data.position.rowSpan, data.position.columnSpan );
100 // debugging support, very useful when new features are added or bugs are hunted down
101 // currently not called from code so compiler will optimize these away, kept here for future debugging
102 void PrintArray( Array2d<Size>& array )
104 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
106 for( unsigned int i = 0; i < array.GetRows(); ++i )
108 for( unsigned int j = 0; j < array.GetColumns(); ++j )
110 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
115 // debugging support, very useful when new features are added or bugs are hunted down
116 // currently not called from code so compiler will optimize these away, kept here for future debugging
117 void PrintVector( std::vector<float>& array )
119 TV_LOG( "vector, size [%d]\n", array.size() );
121 for( unsigned int i = 0; i < array.size(); ++i )
123 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
127 #endif // defined(DEBUG_ENABLED)
137 const Property::Index TableView::PROPERTY_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX );
138 const Property::Index TableView::PROPERTY_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 1 );
139 const Property::Index TableView::PROPERTY_CELL_PADDING( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 2 );
140 const Property::Index TableView::PROPERTY_LAYOUT_ANIMATION_DURATION( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 3 );
141 const Property::Index TableView::PROPERTY_LAYOUT_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 4 );
142 const Property::Index TableView::PROPERTY_LAYOUT_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 5 );
150 const Scripting::StringEnum< Toolkit::TableView::LayoutPolicy > LAYOUT_POLICY_STRING_TABLE[] =
152 { "fixed", Toolkit::TableView::Fixed },
153 { "relative", Toolkit::TableView::Relative },
154 { "fill", Toolkit::TableView::Fill }
157 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
162 return Toolkit::TableView::New(0, 0);
164 TypeRegistration mType( typeid(Toolkit::TableView), typeid(Toolkit::Control), Create );
166 PropertyRegistration property1( mType, "rows", Toolkit::TableView::PROPERTY_ROWS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
167 PropertyRegistration property2( mType, "columns", Toolkit::TableView::PROPERTY_COLUMNS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
168 PropertyRegistration property3( mType, "cell-padding", Toolkit::TableView::PROPERTY_CELL_PADDING, Property::VECTOR2, &TableView::SetProperty, &TableView::GetProperty );
169 PropertyRegistration property4( mType, "layout-animation-duration", Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION, Property::FLOAT, &TableView::SetProperty, &TableView::GetProperty );
170 PropertyRegistration property5( mType, "layout-rows", Toolkit::TableView::PROPERTY_LAYOUT_ROWS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
171 PropertyRegistration property6( mType, "layout-columns", Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
175 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
177 // Create the implementation, temporarily owned by this handle on stack
178 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
180 // Pass ownership to CustomActor handle
181 Toolkit::TableView handle( *impl );
183 // Second-phase init of the implementation
184 // This can only be done after the CustomActor connection has been made...
190 bool TableView::AddChild( Actor child, Toolkit::TableView::CellPosition position )
192 // check that the child is valid
193 DALI_ASSERT_ALWAYS( child );
195 // if child is already parented, we adopt it
196 if( child.GetParent() )
198 child.GetParent().Remove( child );
200 // check if we need to expand our data array
201 if( position.rowIndex >= mCellData.GetRows() )
203 // only adding new rows
204 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
206 if( position.columnIndex >= mCellData.GetColumns() )
208 // only adding new columns
209 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
211 // check if there already is something in this cell
212 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
214 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
216 RelayoutingLock lock( *this );
220 // put the actor to the main cell
223 data.position = position;
224 mCellData[ position.rowIndex ][ position.columnIndex ] = data;
225 // if child spans multiple rows of columns
226 bool spanned = false;
227 if( position.rowSpan > 1 )
229 // span might go outside table
230 if( position.rowIndex + position.rowSpan > mCellData.GetRows() )
232 // increase table size for the full span, only increasing rows
233 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
237 if( position.columnSpan > 1 )
239 // span might go outside table
240 if( position.columnIndex + position.columnSpan > mCellData.GetColumns() )
242 // increase table size for the full span, only increasing columns
243 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
247 // if it spanned multiple rows, put the cellinfo in all of those
250 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
252 // store same information to all cells, this way we can identify
253 // if a cell is the prime location of an actor or a spanned one
254 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
256 // store same information to all cells, this way we can identify
257 // if a cell is the prime location of an actor or a spanned one
258 mCellData[ row ][ column ] = data;
262 // relayout the whole table
264 return true; // addition successful
267 Actor TableView::GetChildAt( Toolkit::TableView::CellPosition position )
269 // check if we have this row and column in the table
270 if( ( position.columnIndex >= mCellData.GetColumns() )||
271 ( position.rowIndex >= mCellData.GetRows() ) )
273 // return an empty handle
276 // return the child handle
277 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
280 Actor TableView::RemoveChildAt( Toolkit::TableView::CellPosition position )
282 // get the child handle
283 Actor child = GetChildAt( position );
284 // if no real actor there, nothing else to be done
287 RelayoutingLock lock( *this );
288 // Remove the child, this will trigger a call to OnControlChildRemove
289 Self().Remove( child );
291 // relayout the table only if instances were found
292 if( RemoveAllInstances( child ) )
297 // return the child back to caller
301 bool TableView::FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position )
303 // only find valid child actors
306 // walk through the layout data
307 const unsigned int rowCount = mCellData.GetRows();
308 const unsigned int columnCount = mCellData.GetColumns();
309 for( unsigned int row = 0; row < rowCount; ++row )
311 for( unsigned int column = 0; column < columnCount; ++column )
313 if( mCellData[ row ][ column ].actor == child )
315 position = mCellData[ row ][ column ].position;
324 void TableView::InsertRow( unsigned int rowIndex )
326 RelayoutingLock lock( *this );
327 mCellData.InsertRow( rowIndex );
328 // need to update the cellinfos for the items that moved
329 const unsigned int rowCount = mCellData.GetRows();
330 const unsigned int columnCount = mCellData.GetColumns();
331 for( unsigned int row = 0; row < rowCount; ++row )
333 for( unsigned int column = 0; column < columnCount; ++column )
335 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
336 // if cell is spanning and above and spans to inserted row
337 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
338 ( position.rowIndex + position.rowSpan > rowIndex ) )
340 // increase span by one
342 // copy cell to occupy the new column
343 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
345 // if below of inserted row, increase row index
346 else if( row > rowIndex )
348 // increase index by one
353 mRelativeSizes.InsertRow( rowIndex );
354 // inserting a row requires adjusting the height vectors
355 mFixedHeights.insert( mFixedHeights.begin() + rowIndex, 0 );
356 mRelativeHeights.insert( mRelativeHeights.begin() + rowIndex, 0 );
360 void TableView::DeleteRow( unsigned int rowIndex )
362 std::vector< Actor > ignored;
363 DeleteRow( rowIndex, ignored );
366 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
368 RelayoutingLock lock( *this );
369 std::vector< CellData > lost;
370 mCellData.DeleteRow( rowIndex, lost );
371 // need to update the cellinfos for the items that moved
372 const unsigned int rowCount = mCellData.GetRows();
373 const unsigned int columnCount = mCellData.GetColumns();
374 for( unsigned int row = 0; row < rowCount; ++row )
376 for( unsigned int column = 0; column < columnCount; ++column )
378 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
379 // if cell is spanning and above and spans to deleted row
380 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
381 ( position.rowIndex + position.rowSpan > rowIndex ) )
383 // decrease span by one
384 if( position.rowSpan > 1 )
389 // if below of or at the inserted row, decrease row index
390 else if( row >= rowIndex )
392 // decrease index by one
393 if( position.rowIndex > 1 )
400 // 1 row removed, 0 columns
401 RemoveAndGetLostActors( lost, removed, 1u, 0u );
402 // resize the data structures
403 mRelativeSizes.DeleteRow( rowIndex );
404 // deleting a row requires adjusting the height vectors
405 mFixedHeights.erase( mFixedHeights.begin() + rowIndex );
406 mRelativeHeights.erase( mRelativeHeights.begin() + rowIndex );
410 void TableView::InsertColumn( unsigned int columnIndex )
412 RelayoutingLock lock( *this );
413 mCellData.InsertColumn( columnIndex );
414 // need to update the cellinfos for the items that moved
415 const unsigned int rowCount = mCellData.GetRows();
416 const unsigned int columnCount = mCellData.GetColumns();
417 for( unsigned int row = 0; row < rowCount; ++row )
419 for( unsigned int column = 0; column < columnCount; ++column )
421 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
422 // if cell is spanning and left side and spans to inserted column
423 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
424 ( position.columnIndex + position.columnSpan > columnIndex ) )
426 // increase span by one
427 position.columnSpan++;
428 // copy cell to occupy the new column
429 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
431 // if on the right side of inserted column, increase column index
432 else if( column > columnIndex )
434 // increase index by one
435 position.columnIndex++;
439 // relative sizes gets recalculated on Relayout
440 mRelativeSizes.InsertColumn( columnIndex );
441 // inserting a column requires adjusting the width vectors
442 mFixedWidths.insert( mFixedWidths.begin() + columnIndex, 0 );
443 mRelativeWidths.insert( mRelativeWidths.begin() + columnIndex, 0 );
447 void TableView::DeleteColumn( unsigned int columnIndex )
449 std::vector< Actor > ignored;
450 DeleteColumn( columnIndex, ignored );
453 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
455 RelayoutingLock lock( *this );
456 std::vector< CellData > lost;
457 mCellData.DeleteColumn( columnIndex, lost );
458 // need to update the cellinfos for the items that moved
459 const unsigned int rowCount = mCellData.GetRows();
460 const unsigned int columnCount = mCellData.GetColumns();
461 for( unsigned int row = 0; row < rowCount; ++row )
463 for( unsigned int column = 0; column < columnCount; ++column )
465 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
466 // if cell is spanning and left side and spans to inserted column
467 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
468 ( position.columnIndex + position.columnSpan > columnIndex ) )
470 // decrease span by one
471 if( position.columnSpan > 1 )
473 position.columnSpan--;
476 // if on the right side of or at the inserted column, decrease column index
477 else if( column >= columnIndex )
479 // decrease index by one
480 if( position.columnIndex > 0 )
482 position.columnIndex--;
487 // 0 rows, 1 column removed
488 RemoveAndGetLostActors( lost, removed, 0u, 1u );
489 // resize the data structures
490 mRelativeSizes.DeleteColumn( columnIndex );
491 // deleting a column requires adjusting the width vectors
492 mFixedWidths.erase( mFixedWidths.begin() + columnIndex );
493 mRelativeWidths.erase( mRelativeWidths.begin() + columnIndex );
498 void TableView::Resize( unsigned int rows, unsigned int columns )
500 std::vector< Actor > ignored;
501 Resize( rows, columns, ignored );
504 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
506 RelayoutingLock lock( *this );
507 unsigned int oldRows = GetRows();
508 unsigned int oldColumns = GetColumns();
510 std::vector< CellData > lost;
511 ResizeContainers( rows, columns, lost );
512 // calculate if we lost rows or columns
513 unsigned int rowsRemoved = 0;
514 unsigned int newRows = GetRows();
515 if( oldRows < newRows )
517 rowsRemoved = newRows - oldRows;
519 unsigned int columnsRemoved = 0;
520 unsigned int newColumns = GetColumns();
521 if( oldColumns < newColumns )
523 rowsRemoved = newColumns - oldColumns;
525 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
526 // finally relayout once all actors are removed
530 void TableView::SetCellPadding( Size padding )
532 // if padding really changed
533 if( padding != mPadding )
541 Size TableView::GetCellPadding()
546 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
548 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
549 // add the fixed height to the array of fixed heights
550 mFixedHeights[ rowIndex ] = height;
551 // remove the relative height of the same row
552 mRelativeHeights[ rowIndex ] = 0.f;
553 // relayout all cells, no lock needed as nothing added or removed
557 float TableView::GetFixedHeight( unsigned int rowIndex ) const
559 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
561 return mFixedHeights[ rowIndex ];
564 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
566 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
567 // add the relative height to the array of relative heights
568 mRelativeHeights[ rowIndex ] = heightPercentage;
569 // remove the fixed height of the same row
570 mFixedHeights[ rowIndex ] = 0.f;
571 // relayout all cells, no lock needed as nothing added or removed
575 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
577 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
579 return mRelativeHeights[ rowIndex ];
582 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
584 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
585 // add the fixed width to the array of fixed column widths
586 mFixedWidths[ columnIndex ] = width;
587 // remove the relative width of the same column
588 mRelativeWidths[ columnIndex ] = 0.f;
589 // relayout all cells, no lock needed as nothing added or removed
593 float TableView::GetFixedWidth( unsigned int columnIndex ) const
595 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
597 return mFixedWidths[ columnIndex ];
600 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
602 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
603 // add the relative widths to the array of relative widths
604 mRelativeWidths[ columnIndex ] = widthPercentage;
605 // remove the fixed width of the same column
606 mFixedWidths[ columnIndex ] = 0.f;
607 // relayout all cells, no lock needed as nothing added or removed
611 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
613 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
615 return mRelativeWidths[ columnIndex ];
618 void TableView::SetLayoutAnimationDuration( float duration )
620 mConstraintDuration = duration;
623 float TableView::GetLayoutAnimationDuration()
625 return mConstraintDuration;
628 void TableView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
630 float fixedHeightsTotal = 0.0f;
631 float fixedWidthsTotal = 0.0f;
633 // 1. update the relative sizes and calculate total fixed height and width
634 UpdateRelativeSizes( fixedHeightsTotal, fixedWidthsTotal );
636 // 2. go through the layout data and create constraints
637 float cumulatedFixedHeight = 0.0f;
638 float cumulatedRelativeHeight = 0.0f;
641 const unsigned int rowCount = mCellData.GetRows();
642 const unsigned int columnCount = mCellData.GetColumns();
643 // float versions of the count + 1 to keep precision
644 const float maxRowPlusOne( rowCount + 1 );
645 const float maxColumnPlusOne( columnCount + 1 );
646 for( unsigned int row = 0; row < rowCount; ++row )
648 // reset widths at the start of each row
649 float cumulatedFixedWidth = 0.0f;
650 float cumulatedRelativeWidth = 0.0f;
651 for( unsigned int column = 0; column < columnCount; ++column )
653 // check if this cell has an actor
654 Actor actor = mCellData[ row ][ column ].actor;
655 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
656 // if there is an actor and this is the main cell of the actor
657 // an actor can be in multiple cells if its row or columnspan is more than 1
658 // we however must only lay out each actor only once
659 if( ( actor )&&( position.rowIndex == row )&&( position.columnIndex == column ) )
661 // anchor actor correctly
662 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
663 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
664 // remove old constraints
665 actor.RemoveConstraints();
668 // get the row and column indices
669 float rowPos( position.rowIndex );
670 float colPos( position.columnIndex );
671 // constrain the actor position to be relative to the width and height of table
672 // minus the padding of course (padding is all around cells)
673 Vector2 relativePosition( cumulatedRelativeWidth, cumulatedRelativeHeight );
674 // fixed height rows and fixed width cells are considered as padding so
675 // they are removed from the total size for relative
676 // for position only consider cumulated fixed rows and columns from top and left
677 Vector2 positionPadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
678 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
679 Vector2 fixedPosition( ( colPos + 1.0f ) * mPadding.width + cumulatedFixedWidth,
680 ( rowPos + 1.0f ) * mPadding.height + cumulatedFixedHeight );
682 Constraint widthConstraint = Constraint::New<float>( Actor::POSITION_X,
683 ParentSource( Actor::SIZE_WIDTH ),
684 RelativeToWidthOrHeight( relativePosition.x, positionPadding.x, fixedPosition.x ) );
686 Constraint heightConstraint = Constraint::New<float>( Actor::POSITION_Y,
687 ParentSource( Actor::SIZE_HEIGHT ),
688 RelativeToWidthOrHeight( relativePosition.y, positionPadding.y, fixedPosition.y ) );
690 widthConstraint.SetApplyTime( mConstraintDuration );
691 heightConstraint.SetApplyTime( mConstraintDuration );
693 // bake constrained position value if constraint is removed
694 widthConstraint.SetRemoveAction( Constraint::Bake );
695 heightConstraint.SetRemoveAction( Constraint::Bake );
697 actor.ApplyConstraint( widthConstraint );
698 actor.ApplyConstraint( heightConstraint );
701 // constrain the actor size to be relative to the size of table
702 // get the relative size for this cell
703 Vector2 relativeSize( mRelativeSizes[ row ][ column ] );
704 Vector2 fixedSize( mFixedWidths[ column ], mFixedHeights[ row ] );
705 // if we span multiple cells, need to sum them all up, both fixed and relative parts
706 if( position.rowSpan > 1 )
708 for( unsigned int i = 1; i < position.rowSpan; ++i )
710 // accumulate the height only
711 relativeSize.height += mRelativeSizes[ row + i ][ column ].height;
712 fixedSize.height += mFixedHeights[ row + i ];
715 if( position.columnSpan > 1 )
717 for( unsigned int i = 1; i < position.columnSpan; ++i )
719 // accumulate the width only
720 relativeSize.width += mRelativeSizes[ row ][ column + i ].width;
721 fixedSize.width += mFixedWidths[ column + i ];
724 // minus the padding from size (padding is all around cells)
725 // if item spans multiple columns or rows then less padding is added (default span is 1)
726 // fixed height rows and fixed width cells are considered as padding so they are removed
727 // from the total available size for relative cells
728 Vector2 sizePadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
729 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
730 // and added to the fixed size multiplied by the span of rows and columns
731 fixedSize.width += ( position.columnSpan - 1.0f ) * mPadding.width;
732 fixedSize.height += ( position.rowSpan - 1.0f ) * mPadding.height;
734 RelativeToWidthOrHeight relativeWidthFunctor( relativeSize.x, sizePadding.x, fixedSize.x );
735 RelativeToWidthOrHeight relativeHeightFunctor( relativeSize.y, sizePadding.y, fixedSize.y );
737 widthConstraint = Constraint::New<float>( Actor::SIZE_WIDTH,
738 ParentSource( Actor::SIZE_WIDTH ),
739 relativeWidthFunctor );
741 heightConstraint = Constraint::New<float>( Actor::SIZE_HEIGHT,
742 ParentSource( Actor::SIZE_HEIGHT ),
743 relativeHeightFunctor );
745 widthConstraint.SetApplyTime( mConstraintDuration );
746 heightConstraint.SetApplyTime( mConstraintDuration );
748 // bake constrained size value if constraint is removed
749 widthConstraint.SetRemoveAction( Constraint::Bake );
750 heightConstraint.SetRemoveAction( Constraint::Bake );
752 actor.ApplyConstraint( widthConstraint );
753 actor.ApplyConstraint( heightConstraint );
756 Relayout ( actor, Vector2( relativeWidthFunctor( size.width ), relativeHeightFunctor( size.height ) ), container );
758 // for position we need to keep track of current fixed width and relative width
759 // increase for next column
760 cumulatedFixedWidth += mFixedWidths[ column ];
761 cumulatedRelativeWidth += mRelativeSizes[ row ][ column ].width;
763 // for position we need to keep track of current fixed height and relative height
764 // increase for next row
765 cumulatedFixedHeight += mFixedHeights[ row ];
766 cumulatedRelativeHeight += mRelativeSizes[ row ][ 0 ].height; // all columns share same height
770 unsigned int TableView::GetRows()
772 return mCellData.GetRows();
775 unsigned int TableView::GetColumns()
777 return mCellData.GetColumns();
780 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
782 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
786 TableView& tableViewImpl( GetImpl( tableView ) );
789 case Toolkit::TableView::PROPERTY_ROWS:
791 if( value.Get<unsigned int>() != tableViewImpl.GetRows() )
793 tableViewImpl.Resize( value.Get<unsigned int>(), tableViewImpl.GetColumns() );
797 case Toolkit::TableView::PROPERTY_COLUMNS:
799 if( value.Get<unsigned int>() != tableViewImpl.GetColumns() )
801 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<unsigned int>() );
805 case Toolkit::TableView::PROPERTY_CELL_PADDING:
807 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
810 case Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION:
812 tableViewImpl.SetLayoutAnimationDuration( value.Get<float>() );
815 case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
817 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, value );
820 case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
822 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, value );
829 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
831 Property::Value value;
833 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
837 TableView& tableViewImpl( GetImpl( tableView ) );
840 case Toolkit::TableView::PROPERTY_ROWS:
842 value = tableViewImpl.GetRows();
845 case Toolkit::TableView::PROPERTY_COLUMNS:
847 value = tableViewImpl.GetColumns();
850 case Toolkit::TableView::PROPERTY_CELL_PADDING:
852 value = tableViewImpl.GetCellPadding();
855 case Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION:
857 value = tableViewImpl.GetLayoutAnimationDuration();
860 case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
862 value = tableViewImpl.GetRowHeightsPropertyValue();
865 case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
867 value = tableViewImpl.GetColumnWidthsPropertyValue();
876 void TableView::OnControlChildAdd( Actor& child )
878 if( mLayoutingChild )
880 // we're in the middle of laying out children so no point doing anything here
884 Toolkit::TableView::CellPosition cellPosition;
885 if( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
887 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
889 if( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
891 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
893 if( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) != Property::INVALID_INDEX )
895 Vector2 indices = child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) ).Get<Vector2 >();
896 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
897 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
899 AddChild( child, cellPosition );
904 // check if we're already laying out this child somewhere on the table
905 // walk through the layout data
906 const unsigned int rowCount = mCellData.GetRows();
907 const unsigned int columnCount = mCellData.GetColumns();
908 // child not yet laid out, find the first free slot
909 for( unsigned int row = 0; row < rowCount; ++row )
911 for( unsigned int column = 0; column < columnCount; ++column )
913 // no actor means free cell
914 if( !(mCellData[ row ][ column ].actor) )
916 // put the actor in the cell
919 data.position.columnIndex = column;
920 data.position.rowIndex = row;
921 mCellData[ row ][ column ] = data;
928 // still here, no room for the poor child so increase the array. Need a new row
929 ResizeContainers( rowCount + 1, columnCount );
930 // put the actor to the first cell of the new row
933 data.position.rowIndex = rowCount;
934 data.position.columnIndex = 0;
935 mCellData[ rowCount ][ 0 ] = data;
936 // finally relayout the table
940 void TableView::OnControlChildRemove( Actor& child )
942 // dont process if we're in the middle of bigger operation like delete row, column or resize
943 if( !mLayoutingChild )
945 // relayout the table only if instances were found
946 if( RemoveAllInstances( child ) )
953 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
954 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
955 mCellData( initialRows, initialColumns ),
956 mLayoutingChild( false ),
957 mConstraintDuration( DEFAULT_CONSTRAINT_DURATION )
959 SetKeyboardNavigationSupport( true );
960 ResizeContainers( initialRows, initialColumns );
963 void TableView::OnInitialize()
965 // Make self as keyboard focusable and focus group
967 self.SetKeyboardFocusable(true);
968 SetAsKeyboardFocusGroup(true);
971 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
973 std::vector<CellData> ignored;
974 ResizeContainers( rows, columns, ignored );
977 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
979 mCellData.Resize( rows, columns, removed );
980 // we dont care if these go smaller, data will be regenerated or is not needed anymore
981 mRelativeSizes.Resize( rows, columns );
982 mFixedHeights.resize( rows );
983 mRelativeHeights.resize( rows );
984 mFixedWidths.resize( columns );
985 mRelativeWidths.resize( columns );
988 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
989 unsigned int rowsRemoved, unsigned int columnsRemoved )
991 // iterate through all lost cells
992 std::vector< CellData >::const_iterator iter = lost.begin();
993 for( ; iter != lost.end(); ++iter )
995 // if it is a valid actor
998 // is this actor still somewhere else in the table
999 Toolkit::TableView::CellPosition position;
1000 if( FindChildPosition( (*iter).actor, position ) )
1002 // it must be spanning multiple cells, position contains the top left most one
1003 // check if position is left of the removed location
1004 if( position.columnIndex < (*iter).position.columnIndex )
1006 // if column span is greater than 1
1007 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1009 // decrease column span
1010 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1013 // check if position is left of the removed location
1014 if( position.rowIndex < (*iter).position.rowIndex )
1016 // if row span is greater than 1
1017 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1019 // decrease row span
1020 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1026 // this actor is gone for good
1027 // add actor to removed container
1028 removed.push_back( (*iter).actor );
1029 // we dont want the child actor anymore
1030 Self().Remove( (*iter).actor );
1036 bool TableView::RemoveAllInstances( Actor child )
1039 // walk through the layout data
1040 const unsigned int rowCount = mCellData.GetRows();
1041 const unsigned int columnCount = mCellData.GetColumns();
1042 for( unsigned int row = 0; row < rowCount; ++row )
1044 for( unsigned int column = 0; column < columnCount; ++column )
1046 if( mCellData[ row ][ column ].actor == child )
1048 // clear the cell, NOTE that the cell might be spanning multiple cells
1049 mCellData[ row ][ column ] = CellData();
1057 void TableView::UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal )
1059 // 1. check all the fixed heights and widths to know how much size they take in total
1060 // as well as the relative heights and widths to know how much is left for the 'fill' cells
1061 unsigned int fixedRowCount = 0;
1062 unsigned int relativeRowCount = 0;
1063 float relativeHeightsTotal = 0.0f;
1064 const unsigned int rowCount = mCellData.GetRows();
1065 for( unsigned int row = 0; row < rowCount; ++row )
1067 if( mFixedHeights[ row ] > 0.0f )
1070 fixedHeightsTotal += mFixedHeights[ row ];
1072 if( mRelativeHeights[ row ] > 0.0f )
1075 relativeHeightsTotal += mRelativeHeights[ row ];
1078 unsigned int fixedColumnCount = 0;
1079 unsigned int relativeColumnCount = 0;
1080 const unsigned int columnCount = mCellData.GetColumns();
1081 float relativeWidthsTotal = 0.0f;
1082 for( unsigned int column = 0; column < columnCount; ++column )
1084 if( mFixedWidths[ column ] > 0.0f )
1087 fixedWidthsTotal += mFixedWidths[ column ];
1089 if( mRelativeWidths[ column ] > 0.0f )
1091 ++relativeColumnCount;
1092 relativeWidthsTotal += mRelativeWidths[ column ];
1096 // 2. cap the relative width and height totals to 100%
1097 if( relativeHeightsTotal > 1.0f )
1099 relativeHeightsTotal = 1.0f;
1101 if( relativeWidthsTotal > 1.0f )
1103 relativeWidthsTotal = 1.0f;
1106 // 3. create a table of relative sizes so we can lookup for cells that span multiple rows & colums
1107 const float fillRowCount( rowCount - relativeRowCount - fixedRowCount );
1108 const float fillColumnCount( columnCount - relativeColumnCount - fixedColumnCount );
1110 // walk through the data containers
1111 for( unsigned int row = 0; row < rowCount; ++row )
1113 float relativeHeight = 0.0f;
1114 // if we have a fixed height, relative height is 0
1115 if( mFixedHeights[ row ] > 0.0f )
1117 relativeHeight = 0.0f;
1119 // else if we're given a specific row height %, use that
1120 else if( mRelativeHeights[ row ] > 0.0f )
1122 relativeHeight = mRelativeHeights[ row ];
1124 // else if there are fill rows
1125 else if( fillRowCount > 0 )
1127 // this is a 'fill' row. it gets the remainder of the 100% divided evenly between 'fill' rows
1128 relativeHeight = (1.0f - relativeHeightsTotal ) / fillRowCount;
1130 for( unsigned int column = 0; column < columnCount; ++column )
1132 float relativeWidth = 0.0f;
1133 // if we have a fixed width, relative width is 0
1134 if( mFixedWidths[ column ] > 0.0f )
1136 relativeWidth = 0.0f;
1138 // else if we're given a specific column width %, use that
1139 else if( mRelativeWidths[ column ] > 0.0f )
1141 relativeWidth = mRelativeWidths[ column ];
1143 // else if there are fill columns
1144 else if( fillColumnCount > 0 )
1146 // this is a 'fill' column. it gets the remainder of the 100% divided evenly between 'fill' columns
1147 relativeWidth = (1.0f - relativeWidthsTotal ) / fillColumnCount;
1150 mRelativeSizes[ row ][ column ] = Size( relativeWidth, relativeHeight );
1155 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1156 void(TableView::*funcFixed)(unsigned int, float),
1157 void(TableView::*funcRelative)(unsigned int, float),
1158 const Property::Value& value )
1160 if( Property::MAP == value.GetType() )
1162 Property::Map map = value.Get<Property::Map>();
1163 unsigned int rowIndex(0);
1164 for ( unsigned int i = 0, count = map.Count(); i < count; ++i )
1166 Property::Value& item = map.GetValue(i);
1168 if( std::istringstream(map.GetKey(i)) >> rowIndex // the key is a number
1169 && Property::MAP == item.GetType())
1171 if( item.HasKey( "policy" ) && item.HasKey( "value" ) )
1173 Toolkit::TableView::LayoutPolicy policy = Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( item.GetValue("policy").Get<std::string>(), LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT );
1174 if( policy == Toolkit::TableView::Fixed )
1176 (tableViewImpl.*funcFixed)( rowIndex, item.GetValue("value").Get<float>() );
1178 else if( policy == Toolkit::TableView::Relative )
1180 (tableViewImpl.*funcRelative)( rowIndex, item.GetValue("value").Get<float>() );
1188 Property::Value TableView::GetRowHeightsPropertyValue()
1191 GetMapPropertyValue( mFixedHeights, mRelativeHeights, map);
1192 return Property::Value(map);
1195 Property::Value TableView::GetColumnWidthsPropertyValue()
1198 GetMapPropertyValue( mFixedWidths, mRelativeWidths, map);
1199 return Property::Value(map);
1202 void TableView::GetMapPropertyValue( const std::vector<float>& fixedSize, const std::vector<float>& relativeSize, Property::Map& map )
1204 std::string fixedPolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Fixed, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1205 std::string relativePolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Relative, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1207 size_t count = fixedSize.size();
1208 for( size_t index = 0; index < count; index++ )
1210 if( ! EqualsZero( fixedSize[index] ) )
1213 item[ "policy" ] = fixedPolicy;
1214 item[ "value" ] = fixedSize[index];
1216 map[ static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str() ] = item;
1218 else if( ! EqualsZero( relativeSize[index] ) )
1221 item[ "policy" ] = relativePolicy;
1222 item[ "value" ] = relativeSize[index];
1224 map[ static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str() ] = item;
1229 TableView::~TableView()
1234 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1236 Actor nextFocusableActor;
1238 if ( !currentFocusedActor )
1240 // Nothing is currently focused, so the child in the first cell should be focused.
1241 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1245 Toolkit::TableView::CellPosition position;
1246 if( FindChildPosition( currentFocusedActor, position ) )
1248 // The current focused actor is a child of TableView
1249 bool focusLost = false;
1250 int currentRow = position.rowIndex;
1251 int currentColumn = position.columnIndex;
1252 int numberOfColumns = GetColumns();
1253 int numberOfRows = GetRows();
1255 switch ( direction )
1257 case Toolkit::Control::Left:
1259 if(--currentColumn < 0)
1261 currentColumn = numberOfColumns - 1;
1262 if(--currentRow < 0)
1264 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1265 focusLost = (currentRow == 0);
1270 case Toolkit::Control::Right:
1272 if(++currentColumn > numberOfColumns - 1)
1275 if(++currentRow > numberOfRows - 1)
1277 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1278 focusLost = (currentRow == numberOfRows - 1);
1283 case Toolkit::Control::Up:
1285 if(--currentRow < 0)
1287 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1288 focusLost = (currentRow == 0);
1292 case Toolkit::Control::Down:
1294 if(++currentRow > numberOfRows - 1)
1296 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1297 focusLost = (currentRow == numberOfRows - 1);
1303 // Move the focus if we haven't lost it.
1306 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1311 // The current focused actor is not within table view, so the child in the first cell should be focused.
1312 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1316 return nextFocusableActor;
1319 } // namespace Internal
1321 } // namespace Toolkit