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>
20 #include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
23 #include <dali/public-api/object/ref-object.h>
32 const float DEFAULT_CONSTRAINT_DURATION = 0.0f;
35 * Constraint that sets a child property relative to parents Width or Height
37 struct RelativeToWidthOrHeight
40 * Constraint that is relative (%) to parent width/height and applies a
41 * unit based padding before the relative calculation.
42 * @param scale of parent minus padding between 0 and 1
43 * @param padding in world coordinate units
44 * @param fixed part in world coordinate units
46 RelativeToWidthOrHeight( float scale, float padding, float fixed )
47 : mScaleFactor( scale ),
53 inline float operator()( const float& parentWidthOrHeight )
55 return mFixed + ( parentWidthOrHeight - mPadding ) * mScaleFactor;
58 float operator()( const float& current,
59 const PropertyInput& parentWidthOrHeight )
61 return operator()( parentWidthOrHeight.GetFloat() );
69 #if defined(DEBUG_ENABLED)
70 // debugging support, very useful when new features are added or bugs are hunted down
71 // currently not called from code so compiler will optimize these away, kept here for future debugging
73 #define TABLEVIEW_TAG "DALI Toolkit::TableView"
74 #define TV_LOG(fmt, args...) LOG(LOG_INFO, TABLEVIEW_TAG, fmt, ## args)
76 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
78 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
80 for( unsigned int i = 0; i < array.GetRows(); ++i )
82 for( unsigned int j = 0; j < array.GetColumns(); ++j )
84 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
90 TV_LOG("Array[%d,%d]=%c %d,%d,%d,%d ", i, j, actor,
91 data.position.rowIndex, data.position.columnIndex,
92 data.position.rowSpan, data.position.columnSpan );
98 // debugging support, very useful when new features are added or bugs are hunted down
99 // currently not called from code so compiler will optimize these away, kept here for future debugging
100 void PrintArray( Array2d<Size>& array )
102 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
104 for( unsigned int i = 0; i < array.GetRows(); ++i )
106 for( unsigned int j = 0; j < array.GetColumns(); ++j )
108 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
113 // debugging support, very useful when new features are added or bugs are hunted down
114 // currently not called from code so compiler will optimize these away, kept here for future debugging
115 void PrintVector( vector<float>& array )
117 TV_LOG( "vector, size [%d]\n", array.size() );
119 for( unsigned int i = 0; i < array.size(); ++i )
121 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
125 #endif // defined(DEBUG_ENABLED)
135 const Property::Index TableView::PROPERTY_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX );
136 const Property::Index TableView::PROPERTY_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 1 );
137 const Property::Index TableView::PROPERTY_CELL_PADDING( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 2 );
138 const Property::Index TableView::PROPERTY_LAYOUT_ANIMATION_DURATION( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 3 );
139 const Property::Index TableView::PROPERTY_LAYOUT_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 4 );
140 const Property::Index TableView::PROPERTY_LAYOUT_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 5 );
148 const Scripting::StringEnum< Toolkit::TableView::LayoutPolicy > LAYOUT_POLICY_STRING_TABLE[] =
150 { "fixed", Toolkit::TableView::Fixed },
151 { "relative", Toolkit::TableView::Relative },
152 { "fill", Toolkit::TableView::Fill }
155 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
160 return Toolkit::TableView::New(0, 0);
162 TypeRegistration mType( typeid(Toolkit::TableView), typeid(Toolkit::Control), Create );
164 PropertyRegistration property1( mType, "rows", Toolkit::TableView::PROPERTY_ROWS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
165 PropertyRegistration property2( mType, "columns", Toolkit::TableView::PROPERTY_COLUMNS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
166 PropertyRegistration property3( mType, "cell-padding", Toolkit::TableView::PROPERTY_CELL_PADDING, Property::VECTOR2, &TableView::SetProperty, &TableView::GetProperty );
167 PropertyRegistration property4( mType, "layout-animation-duration", Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION, Property::FLOAT, &TableView::SetProperty, &TableView::GetProperty );
168 PropertyRegistration property5( mType, "layout-rows", Toolkit::TableView::PROPERTY_LAYOUT_ROWS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
169 PropertyRegistration property6( mType, "layout-columns", Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
173 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
175 // Create the implementation, temporarily owned by this handle on stack
176 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
178 // Pass ownership to CustomActor handle
179 Toolkit::TableView handle( *impl );
181 // Second-phase init of the implementation
182 // This can only be done after the CustomActor connection has been made...
188 bool TableView::AddChild( Actor child, Toolkit::TableView::CellPosition position )
190 // check that the child is valid
191 DALI_ASSERT_ALWAYS( child );
193 // if child is already parented, we adopt it
194 if( child.GetParent() )
196 child.GetParent().Remove( child );
198 // check if we need to expand our data array
199 if( position.rowIndex >= mCellData.GetRows() )
201 // only adding new rows
202 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
204 if( position.columnIndex >= mCellData.GetColumns() )
206 // only adding new columns
207 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
209 // check if there already is something in this cell
210 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
212 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
214 RelayoutingLock lock( *this );
218 // put the actor to the main cell
221 data.position = position;
222 mCellData[ position.rowIndex ][ position.columnIndex ] = data;
223 // if child spans multiple rows of columns
224 bool spanned = false;
225 if( position.rowSpan > 1 )
227 // span might go outside table
228 if( position.rowIndex + position.rowSpan > mCellData.GetRows() )
230 // increase table size for the full span, only increasing rows
231 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
235 if( position.columnSpan > 1 )
237 // span might go outside table
238 if( position.columnIndex + position.columnSpan > mCellData.GetColumns() )
240 // increase table size for the full span, only increasing columns
241 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
245 // if it spanned multiple rows, put the cellinfo in all of those
248 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
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 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
254 // store same information to all cells, this way we can identify
255 // if a cell is the prime location of an actor or a spanned one
256 mCellData[ row ][ column ] = data;
260 // relayout the whole table
262 return true; // addition successful
265 Actor TableView::GetChildAt( Toolkit::TableView::CellPosition position )
267 // check if we have this row and column in the table
268 if( ( position.columnIndex >= mCellData.GetColumns() )||
269 ( position.rowIndex >= mCellData.GetRows() ) )
271 // return an empty handle
274 // return the child handle
275 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
278 Actor TableView::RemoveChildAt( Toolkit::TableView::CellPosition position )
280 // get the child handle
281 Actor child = GetChildAt( position );
282 // if no real actor there, nothing else to be done
285 RelayoutingLock lock( *this );
286 // Remove the child, this will trigger a call to OnControlChildRemove
287 Self().Remove( child );
289 // relayout the table only if instances were found
290 if( RemoveAllInstances( child ) )
295 // return the child back to caller
299 bool TableView::FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position )
301 // only find valid child actors
304 // walk through the layout data
305 const unsigned int rowCount = mCellData.GetRows();
306 const unsigned int columnCount = mCellData.GetColumns();
307 for( unsigned int row = 0; row < rowCount; ++row )
309 for( unsigned int column = 0; column < columnCount; ++column )
311 if( mCellData[ row ][ column ].actor == child )
313 position = mCellData[ row ][ column ].position;
322 void TableView::InsertRow( unsigned int rowIndex )
324 RelayoutingLock lock( *this );
325 mCellData.InsertRow( rowIndex );
326 // need to update the cellinfos for the items that moved
327 const unsigned int rowCount = mCellData.GetRows();
328 const unsigned int columnCount = mCellData.GetColumns();
329 for( unsigned int row = 0; row < rowCount; ++row )
331 for( unsigned int column = 0; column < columnCount; ++column )
333 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
334 // if cell is spanning and above and spans to inserted row
335 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
336 ( position.rowIndex + position.rowSpan > rowIndex ) )
338 // increase span by one
340 // copy cell to occupy the new column
341 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
343 // if below of inserted row, increase row index
344 else if( row > rowIndex )
346 // increase index by one
351 mRelativeSizes.InsertRow( rowIndex );
352 // inserting a row requires adjusting the height vectors
353 mFixedHeights.insert( mFixedHeights.begin() + rowIndex, 0 );
354 mRelativeHeights.insert( mRelativeHeights.begin() + rowIndex, 0 );
358 void TableView::DeleteRow( unsigned int rowIndex )
360 vector< Actor > ignored;
361 DeleteRow( rowIndex, ignored );
364 void TableView::DeleteRow( unsigned int rowIndex, vector<Actor>& removed )
366 RelayoutingLock lock( *this );
367 vector< CellData > lost;
368 mCellData.DeleteRow( rowIndex, lost );
369 // need to update the cellinfos for the items that moved
370 const unsigned int rowCount = mCellData.GetRows();
371 const unsigned int columnCount = mCellData.GetColumns();
372 for( unsigned int row = 0; row < rowCount; ++row )
374 for( unsigned int column = 0; column < columnCount; ++column )
376 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
377 // if cell is spanning and above and spans to deleted row
378 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
379 ( position.rowIndex + position.rowSpan > rowIndex ) )
381 // decrease span by one
382 if( position.rowSpan > 1 )
387 // if below of or at the inserted row, decrease row index
388 else if( row >= rowIndex )
390 // decrease index by one
391 if( position.rowIndex > 1 )
398 // 1 row removed, 0 columns
399 RemoveAndGetLostActors( lost, removed, 1u, 0u );
400 // resize the data structures
401 mRelativeSizes.DeleteRow( rowIndex );
402 // deleting a row requires adjusting the height vectors
403 mFixedHeights.erase( mFixedHeights.begin() + rowIndex );
404 mRelativeHeights.erase( mRelativeHeights.begin() + rowIndex );
408 void TableView::InsertColumn( unsigned int columnIndex )
410 RelayoutingLock lock( *this );
411 mCellData.InsertColumn( columnIndex );
412 // need to update the cellinfos for the items that moved
413 const unsigned int rowCount = mCellData.GetRows();
414 const unsigned int columnCount = mCellData.GetColumns();
415 for( unsigned int row = 0; row < rowCount; ++row )
417 for( unsigned int column = 0; column < columnCount; ++column )
419 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
420 // if cell is spanning and left side and spans to inserted column
421 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
422 ( position.columnIndex + position.columnSpan > columnIndex ) )
424 // increase span by one
425 position.columnSpan++;
426 // copy cell to occupy the new column
427 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
429 // if on the right side of inserted column, increase column index
430 else if( column > columnIndex )
432 // increase index by one
433 position.columnIndex++;
437 // relative sizes gets recalculated on Relayout
438 mRelativeSizes.InsertColumn( columnIndex );
439 // inserting a column requires adjusting the width vectors
440 mFixedWidths.insert( mFixedWidths.begin() + columnIndex, 0 );
441 mRelativeWidths.insert( mRelativeWidths.begin() + columnIndex, 0 );
445 void TableView::DeleteColumn( unsigned int columnIndex )
447 vector< Actor > ignored;
448 DeleteColumn( columnIndex, ignored );
451 void TableView::DeleteColumn( unsigned int columnIndex, vector<Actor>& removed )
453 RelayoutingLock lock( *this );
454 vector< CellData > lost;
455 mCellData.DeleteColumn( columnIndex, lost );
456 // need to update the cellinfos for the items that moved
457 const unsigned int rowCount = mCellData.GetRows();
458 const unsigned int columnCount = mCellData.GetColumns();
459 for( unsigned int row = 0; row < rowCount; ++row )
461 for( unsigned int column = 0; column < columnCount; ++column )
463 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
464 // if cell is spanning and left side and spans to inserted column
465 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
466 ( position.columnIndex + position.columnSpan > columnIndex ) )
468 // decrease span by one
469 if( position.columnSpan > 1 )
471 position.columnSpan--;
474 // if on the right side of or at the inserted column, decrease column index
475 else if( column >= columnIndex )
477 // decrease index by one
478 if( position.columnIndex > 0 )
480 position.columnIndex--;
485 // 0 rows, 1 column removed
486 RemoveAndGetLostActors( lost, removed, 0u, 1u );
487 // resize the data structures
488 mRelativeSizes.DeleteColumn( columnIndex );
489 // deleting a column requires adjusting the width vectors
490 mFixedWidths.erase( mFixedWidths.begin() + columnIndex );
491 mRelativeWidths.erase( mRelativeWidths.begin() + columnIndex );
496 void TableView::Resize( unsigned int rows, unsigned int columns )
498 vector< Actor > ignored;
499 Resize( rows, columns, ignored );
502 void TableView::Resize( unsigned int rows, unsigned int columns, vector<Actor>& removed )
504 RelayoutingLock lock( *this );
505 unsigned int oldRows = GetRows();
506 unsigned int oldColumns = GetColumns();
508 vector< CellData > lost;
509 ResizeContainers( rows, columns, lost );
510 // calculate if we lost rows or columns
511 unsigned int rowsRemoved = 0;
512 unsigned int newRows = GetRows();
513 if( oldRows < newRows )
515 rowsRemoved = newRows - oldRows;
517 unsigned int columnsRemoved = 0;
518 unsigned int newColumns = GetColumns();
519 if( oldColumns < newColumns )
521 rowsRemoved = newColumns - oldColumns;
523 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
524 // finally relayout once all actors are removed
528 void TableView::SetCellPadding( Size padding )
530 // if padding really changed
531 if( padding != mPadding )
539 Size TableView::GetCellPadding()
544 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
546 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
547 // add the fixed height to the array of fixed heights
548 mFixedHeights[ rowIndex ] = height;
549 // remove the relative height of the same row
550 mRelativeHeights[ rowIndex ] = 0.f;
551 // relayout all cells, no lock needed as nothing added or removed
555 float TableView::GetFixedHeight( unsigned int rowIndex ) const
557 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
559 return mFixedHeights[ rowIndex ];
562 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
564 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
565 // add the relative height to the array of relative heights
566 mRelativeHeights[ rowIndex ] = heightPercentage;
567 // remove the fixed height of the same row
568 mFixedHeights[ rowIndex ] = 0.f;
569 // relayout all cells, no lock needed as nothing added or removed
573 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
575 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
577 return mRelativeHeights[ rowIndex ];
580 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
582 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
583 // add the fixed width to the array of fixed column widths
584 mFixedWidths[ columnIndex ] = width;
585 // remove the relative width of the same column
586 mRelativeWidths[ columnIndex ] = 0.f;
587 // relayout all cells, no lock needed as nothing added or removed
591 float TableView::GetFixedWidth( unsigned int columnIndex ) const
593 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
595 return mFixedWidths[ columnIndex ];
598 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
600 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
601 // add the relative widths to the array of relative widths
602 mRelativeWidths[ columnIndex ] = widthPercentage;
603 // remove the fixed width of the same column
604 mFixedWidths[ columnIndex ] = 0.f;
605 // relayout all cells, no lock needed as nothing added or removed
609 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
611 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
613 return mRelativeWidths[ columnIndex ];
616 void TableView::SetLayoutAnimationDuration( float duration )
618 mConstraintDuration = duration;
621 float TableView::GetLayoutAnimationDuration()
623 return mConstraintDuration;
626 void TableView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
628 float fixedHeightsTotal = 0.0f;
629 float fixedWidthsTotal = 0.0f;
631 // 1. update the relative sizes and calculate total fixed height and width
632 UpdateRelativeSizes( fixedHeightsTotal, fixedWidthsTotal );
634 // 2. go through the layout data and create constraints
635 float cumulatedFixedHeight = 0.0f;
636 float cumulatedRelativeHeight = 0.0f;
639 const unsigned int rowCount = mCellData.GetRows();
640 const unsigned int columnCount = mCellData.GetColumns();
641 // float versions of the count + 1 to keep precision
642 const float maxRowPlusOne( rowCount + 1 );
643 const float maxColumnPlusOne( columnCount + 1 );
644 for( unsigned int row = 0; row < rowCount; ++row )
646 // reset widths at the start of each row
647 float cumulatedFixedWidth = 0.0f;
648 float cumulatedRelativeWidth = 0.0f;
649 for( unsigned int column = 0; column < columnCount; ++column )
651 // check if this cell has an actor
652 Actor actor = mCellData[ row ][ column ].actor;
653 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
654 // if there is an actor and this is the main cell of the actor
655 // an actor can be in multiple cells if its row or columnspan is more than 1
656 // we however must only lay out each actor only once
657 if( ( actor )&&( position.rowIndex == row )&&( position.columnIndex == column ) )
659 // anchor actor correctly
660 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
661 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
662 // remove old constraints
663 actor.RemoveConstraints();
666 // get the row and column indices
667 float rowPos( position.rowIndex );
668 float colPos( position.columnIndex );
669 // constrain the actor position to be relative to the width and height of table
670 // minus the padding of course (padding is all around cells)
671 Vector2 relativePosition( cumulatedRelativeWidth, cumulatedRelativeHeight );
672 // fixed height rows and fixed width cells are considered as padding so
673 // they are removed from the total size for relative
674 // for position only consider cumulated fixed rows and columns from top and left
675 Vector2 positionPadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
676 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
677 Vector2 fixedPosition( ( colPos + 1.0f ) * mPadding.width + cumulatedFixedWidth,
678 ( rowPos + 1.0f ) * mPadding.height + cumulatedFixedHeight );
680 Constraint widthConstraint = Constraint::New<float>( Actor::POSITION_X,
681 ParentSource( Actor::SIZE_WIDTH ),
682 RelativeToWidthOrHeight( relativePosition.x, positionPadding.x, fixedPosition.x ) );
684 Constraint heightConstraint = Constraint::New<float>( Actor::POSITION_Y,
685 ParentSource( Actor::SIZE_HEIGHT ),
686 RelativeToWidthOrHeight( relativePosition.y, positionPadding.y, fixedPosition.y ) );
688 widthConstraint.SetApplyTime( mConstraintDuration );
689 heightConstraint.SetApplyTime( mConstraintDuration );
691 // bake constrained position value if constraint is removed
692 widthConstraint.SetRemoveAction( Constraint::Bake );
693 heightConstraint.SetRemoveAction( Constraint::Bake );
695 actor.ApplyConstraint( widthConstraint );
696 actor.ApplyConstraint( heightConstraint );
699 // constrain the actor size to be relative to the size of table
700 // get the relative size for this cell
701 Vector2 relativeSize( mRelativeSizes[ row ][ column ] );
702 Vector2 fixedSize( mFixedWidths[ column ], mFixedHeights[ row ] );
703 // if we span multiple cells, need to sum them all up, both fixed and relative parts
704 if( position.rowSpan > 1 )
706 for( unsigned int i = 1; i < position.rowSpan; ++i )
708 // accumulate the height only
709 relativeSize.height += mRelativeSizes[ row + i ][ column ].height;
710 fixedSize.height += mFixedHeights[ row + i ];
713 if( position.columnSpan > 1 )
715 for( unsigned int i = 1; i < position.columnSpan; ++i )
717 // accumulate the width only
718 relativeSize.width += mRelativeSizes[ row ][ column + i ].width;
719 fixedSize.width += mFixedWidths[ column + i ];
722 // minus the padding from size (padding is all around cells)
723 // if item spans multiple columns or rows then less padding is added (default span is 1)
724 // fixed height rows and fixed width cells are considered as padding so they are removed
725 // from the total available size for relative cells
726 Vector2 sizePadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
727 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
728 // and added to the fixed size multiplied by the span of rows and columns
729 fixedSize.width += ( position.columnSpan - 1.0f ) * mPadding.width;
730 fixedSize.height += ( position.rowSpan - 1.0f ) * mPadding.height;
732 RelativeToWidthOrHeight relativeWidthFunctor( relativeSize.x, sizePadding.x, fixedSize.x );
733 RelativeToWidthOrHeight relativeHeightFunctor( relativeSize.y, sizePadding.y, fixedSize.y );
735 widthConstraint = Constraint::New<float>( Actor::SIZE_WIDTH,
736 ParentSource( Actor::SIZE_WIDTH ),
737 relativeWidthFunctor );
739 heightConstraint = Constraint::New<float>( Actor::SIZE_HEIGHT,
740 ParentSource( Actor::SIZE_HEIGHT ),
741 relativeHeightFunctor );
743 widthConstraint.SetApplyTime( mConstraintDuration );
744 heightConstraint.SetApplyTime( mConstraintDuration );
746 // bake constrained size value if constraint is removed
747 widthConstraint.SetRemoveAction( Constraint::Bake );
748 heightConstraint.SetRemoveAction( Constraint::Bake );
750 actor.ApplyConstraint( widthConstraint );
751 actor.ApplyConstraint( heightConstraint );
754 Relayout ( actor, Vector2( relativeWidthFunctor( size.width ), relativeHeightFunctor( size.height ) ), container );
756 // for position we need to keep track of current fixed width and relative width
757 // increase for next column
758 cumulatedFixedWidth += mFixedWidths[ column ];
759 cumulatedRelativeWidth += mRelativeSizes[ row ][ column ].width;
761 // for position we need to keep track of current fixed height and relative height
762 // increase for next row
763 cumulatedFixedHeight += mFixedHeights[ row ];
764 cumulatedRelativeHeight += mRelativeSizes[ row ][ 0 ].height; // all columns share same height
768 unsigned int TableView::GetRows()
770 return mCellData.GetRows();
773 unsigned int TableView::GetColumns()
775 return mCellData.GetColumns();
778 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
780 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
784 TableView& tableViewImpl( GetImpl( tableView ) );
787 case Toolkit::TableView::PROPERTY_ROWS:
789 if( value.Get<unsigned int>() != tableViewImpl.GetRows() )
791 tableViewImpl.Resize( value.Get<unsigned int>(), tableViewImpl.GetColumns() );
795 case Toolkit::TableView::PROPERTY_COLUMNS:
797 if( value.Get<unsigned int>() != tableViewImpl.GetColumns() )
799 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<unsigned int>() );
803 case Toolkit::TableView::PROPERTY_CELL_PADDING:
805 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
808 case Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION:
810 tableViewImpl.SetLayoutAnimationDuration( value.Get<float>() );
813 case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
815 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, value );
818 case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
820 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, value );
827 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
829 Property::Value value;
831 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
835 TableView& tableViewImpl( GetImpl( tableView ) );
838 case Toolkit::TableView::PROPERTY_ROWS:
840 value = tableViewImpl.GetRows();
843 case Toolkit::TableView::PROPERTY_COLUMNS:
845 value = tableViewImpl.GetColumns();
848 case Toolkit::TableView::PROPERTY_CELL_PADDING:
850 value = tableViewImpl.GetCellPadding();
853 case Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION:
855 value = tableViewImpl.GetLayoutAnimationDuration();
858 case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
860 value = tableViewImpl.GetRowHeightsPropertyValue();
863 case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
865 value = tableViewImpl.GetColumnWidthsPropertyValue();
874 void TableView::OnControlChildAdd( Actor& child )
876 if( mLayoutingChild )
878 // we're in the middle of laying out children so no point doing anything here
882 Toolkit::TableView::CellPosition cellPosition;
883 if( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
885 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
887 if( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
889 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
891 if( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) != Property::INVALID_INDEX )
893 Vector2 indices = child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) ).Get<Vector2 >();
894 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
895 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
897 AddChild( child, cellPosition );
902 // check if we're already laying out this child somewhere on the table
903 // walk through the layout data
904 const unsigned int rowCount = mCellData.GetRows();
905 const unsigned int columnCount = mCellData.GetColumns();
906 // child not yet laid out, find the first free slot
907 for( unsigned int row = 0; row < rowCount; ++row )
909 for( unsigned int column = 0; column < columnCount; ++column )
911 // no actor means free cell
912 if( !(mCellData[ row ][ column ].actor) )
914 // put the actor in the cell
917 data.position.columnIndex = column;
918 data.position.rowIndex = row;
919 mCellData[ row ][ column ] = data;
926 // still here, no room for the poor child so increase the array. Need a new row
927 ResizeContainers( rowCount + 1, columnCount );
928 // put the actor to the first cell of the new row
931 data.position.rowIndex = rowCount;
932 data.position.columnIndex = 0;
933 mCellData[ rowCount ][ 0 ] = data;
934 // finally relayout the table
938 void TableView::OnControlChildRemove( Actor& child )
940 // dont process if we're in the middle of bigger operation like delete row, column or resize
941 if( !mLayoutingChild )
943 // relayout the table only if instances were found
944 if( RemoveAllInstances( child ) )
951 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
952 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
953 mCellData( initialRows, initialColumns ),
954 mLayoutingChild( false ),
955 mConstraintDuration( DEFAULT_CONSTRAINT_DURATION )
957 SetKeyboardNavigationSupport( true );
958 ResizeContainers( initialRows, initialColumns );
961 void TableView::OnInitialize()
963 // Make self as keyboard focusable and focus group
965 self.SetKeyboardFocusable(true);
966 SetAsKeyboardFocusGroup(true);
969 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
971 vector<CellData> ignored;
972 ResizeContainers( rows, columns, ignored );
975 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, vector<CellData>& removed )
977 mCellData.Resize( rows, columns, removed );
978 // we dont care if these go smaller, data will be regenerated or is not needed anymore
979 mRelativeSizes.Resize( rows, columns );
980 mFixedHeights.resize( rows );
981 mRelativeHeights.resize( rows );
982 mFixedWidths.resize( columns );
983 mRelativeWidths.resize( columns );
986 void TableView::RemoveAndGetLostActors( const vector<CellData>& lost, vector<Actor>& removed,
987 unsigned int rowsRemoved, unsigned int columnsRemoved )
989 // iterate through all lost cells
990 vector< CellData >::const_iterator iter = lost.begin();
991 for( ; iter != lost.end(); ++iter )
993 // if it is a valid actor
996 // is this actor still somewhere else in the table
997 Toolkit::TableView::CellPosition position;
998 if( FindChildPosition( (*iter).actor, position ) )
1000 // it must be spanning multiple cells, position contains the top left most one
1001 // check if position is left of the removed location
1002 if( position.columnIndex < (*iter).position.columnIndex )
1004 // if column span is greater than 1
1005 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1007 // decrease column span
1008 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1011 // check if position is left of the removed location
1012 if( position.rowIndex < (*iter).position.rowIndex )
1014 // if row span is greater than 1
1015 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1017 // decrease row span
1018 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1024 // this actor is gone for good
1025 // add actor to removed container
1026 removed.push_back( (*iter).actor );
1027 // we dont want the child actor anymore
1028 Self().Remove( (*iter).actor );
1034 bool TableView::RemoveAllInstances( Actor child )
1037 // walk through the layout data
1038 const unsigned int rowCount = mCellData.GetRows();
1039 const unsigned int columnCount = mCellData.GetColumns();
1040 for( unsigned int row = 0; row < rowCount; ++row )
1042 for( unsigned int column = 0; column < columnCount; ++column )
1044 if( mCellData[ row ][ column ].actor == child )
1046 // clear the cell, NOTE that the cell might be spanning multiple cells
1047 mCellData[ row ][ column ] = CellData();
1055 void TableView::UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal )
1057 // 1. check all the fixed heights and widths to know how much size they take in total
1058 // as well as the relative heights and widths to know how much is left for the 'fill' cells
1059 unsigned int fixedRowCount = 0;
1060 unsigned int relativeRowCount = 0;
1061 float relativeHeightsTotal = 0.0f;
1062 const unsigned int rowCount = mCellData.GetRows();
1063 for( unsigned int row = 0; row < rowCount; ++row )
1065 if( mFixedHeights[ row ] > 0.0f )
1068 fixedHeightsTotal += mFixedHeights[ row ];
1070 if( mRelativeHeights[ row ] > 0.0f )
1073 relativeHeightsTotal += mRelativeHeights[ row ];
1076 unsigned int fixedColumnCount = 0;
1077 unsigned int relativeColumnCount = 0;
1078 const unsigned int columnCount = mCellData.GetColumns();
1079 float relativeWidthsTotal = 0.0f;
1080 for( unsigned int column = 0; column < columnCount; ++column )
1082 if( mFixedWidths[ column ] > 0.0f )
1085 fixedWidthsTotal += mFixedWidths[ column ];
1087 if( mRelativeWidths[ column ] > 0.0f )
1089 ++relativeColumnCount;
1090 relativeWidthsTotal += mRelativeWidths[ column ];
1094 // 2. cap the relative width and height totals to 100%
1095 if( relativeHeightsTotal > 1.0f )
1097 relativeHeightsTotal = 1.0f;
1099 if( relativeWidthsTotal > 1.0f )
1101 relativeWidthsTotal = 1.0f;
1104 // 3. create a table of relative sizes so we can lookup for cells that span multiple rows & colums
1105 const float fillRowCount( rowCount - relativeRowCount - fixedRowCount );
1106 const float fillColumnCount( columnCount - relativeColumnCount - fixedColumnCount );
1108 // walk through the data containers
1109 for( unsigned int row = 0; row < rowCount; ++row )
1111 float relativeHeight = 0.0f;
1112 // if we have a fixed height, relative height is 0
1113 if( mFixedHeights[ row ] > 0.0f )
1115 relativeHeight = 0.0f;
1117 // else if we're given a specific row height %, use that
1118 else if( mRelativeHeights[ row ] > 0.0f )
1120 relativeHeight = mRelativeHeights[ row ];
1122 // else if there are fill rows
1123 else if( fillRowCount > 0 )
1125 // this is a 'fill' row. it gets the remainder of the 100% divided evenly between 'fill' rows
1126 relativeHeight = (1.0f - relativeHeightsTotal ) / fillRowCount;
1128 for( unsigned int column = 0; column < columnCount; ++column )
1130 float relativeWidth = 0.0f;
1131 // if we have a fixed width, relative width is 0
1132 if( mFixedWidths[ column ] > 0.0f )
1134 relativeWidth = 0.0f;
1136 // else if we're given a specific column width %, use that
1137 else if( mRelativeWidths[ column ] > 0.0f )
1139 relativeWidth = mRelativeWidths[ column ];
1141 // else if there are fill columns
1142 else if( fillColumnCount > 0 )
1144 // this is a 'fill' column. it gets the remainder of the 100% divided evenly between 'fill' columns
1145 relativeWidth = (1.0f - relativeWidthsTotal ) / fillColumnCount;
1148 mRelativeSizes[ row ][ column ] = Size( relativeWidth, relativeHeight );
1153 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1154 void(TableView::*funcFixed)(unsigned int, float),
1155 void(TableView::*funcRelative)(unsigned int, float),
1156 const Property::Value& value )
1158 if( Property::MAP == value.GetType() )
1160 Property::Map map = value.Get<Property::Map>();
1161 unsigned int rowIndex;
1162 for( Property::Map::const_iterator iter = map.begin(); iter != map.end(); iter++)
1164 if( istringstream(iter->first) >> rowIndex // the key is a number
1165 && Property::MAP == (iter->second).GetType())
1167 Property::Value item = iter->second;
1168 if( item.HasKey( "policy" ) && item.HasKey( "value" ) )
1170 Toolkit::TableView::LayoutPolicy policy = Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( item.GetValue("policy").Get<string>(), LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT );
1171 if( policy == Toolkit::TableView::Fixed )
1173 (tableViewImpl.*funcFixed)( rowIndex, item.GetValue("value").Get<float>() );
1175 else if( policy == Toolkit::TableView::Relative )
1177 (tableViewImpl.*funcRelative)( rowIndex, item.GetValue("value").Get<float>() );
1185 Property::Value TableView::GetRowHeightsPropertyValue()
1188 GetMapPropertyValue( mFixedHeights, mRelativeHeights, map);
1189 return Property::Value(map);
1192 Property::Value TableView::GetColumnWidthsPropertyValue()
1195 GetMapPropertyValue( mFixedWidths, mRelativeWidths, map);
1196 return Property::Value(map);
1199 void TableView::GetMapPropertyValue( const std::vector<float>& fixedSize, const std::vector<float>& relativeSize, Property::Map& map )
1201 string fixedPolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Fixed, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1202 string relativePolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Relative, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1203 Property::StringValuePair fixedPolicyPair( "policy", fixedPolicy );
1204 Property::StringValuePair relativePolicyPair( "policy", relativePolicy );
1206 size_t count = fixedSize.size();
1207 for( size_t index = 0; index < count; index++ )
1209 if( ! EqualsZero( fixedSize[index] ) )
1211 Property::StringValuePair valuePair( "value", fixedSize[index] );
1213 item.push_back( fixedPolicyPair );
1214 item.push_back( valuePair );
1216 map.push_back( Property::StringValuePair( static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str(), item ) );
1218 else if( ! EqualsZero( relativeSize[index] ) )
1220 Property::StringValuePair valuePair( "value", relativeSize[index] );
1222 item.push_back( relativePolicyPair );
1223 item.push_back( valuePair );
1225 map.push_back( Property::StringValuePair( static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str(), item ) );
1230 TableView::~TableView()
1235 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1237 Actor nextFocusableActor;
1239 if ( !currentFocusedActor )
1241 // Nothing is currently focused, so the child in the first cell should be focused.
1242 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1246 Toolkit::TableView::CellPosition position;
1247 if( FindChildPosition( currentFocusedActor, position ) )
1249 // The current focused actor is a child of TableView
1250 bool focusLost = false;
1251 int currentRow = position.rowIndex;
1252 int currentColumn = position.columnIndex;
1253 int numberOfColumns = GetColumns();
1254 int numberOfRows = GetRows();
1256 switch ( direction )
1258 case Toolkit::Control::Left:
1260 if(--currentColumn < 0)
1262 currentColumn = numberOfColumns - 1;
1263 if(--currentRow < 0)
1265 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1266 focusLost = (currentRow == 0);
1271 case Toolkit::Control::Right:
1273 if(++currentColumn > numberOfColumns - 1)
1276 if(++currentRow > numberOfRows - 1)
1278 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1279 focusLost = (currentRow == numberOfRows - 1);
1284 case Toolkit::Control::Up:
1286 if(--currentRow < 0)
1288 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1289 focusLost = (currentRow == 0);
1293 case Toolkit::Control::Down:
1295 if(++currentRow > numberOfRows - 1)
1297 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1298 focusLost = (currentRow == numberOfRows - 1);
1304 // Move the focus if we haven't lost it.
1307 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1312 // The current focused actor is not within table view, so the child in the first cell should be focused.
1313 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1317 return nextFocusableActor;
1320 } // namespace Internal
1322 } // namespace Toolkit