2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
18 #include <dali-toolkit/internal/controls/table-view/table-view-impl.h>
19 #include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
22 #include <dali/public-api/object/ref-object.h>
33 return Toolkit::TableView::New(0, 0);
35 TypeRegistration mType( typeid(Toolkit::TableView), typeid(Toolkit::Control), Create );
37 const float DEFAULT_CONSTRAINT_DURATION = 0.0f;
40 * Constraint that sets a child property relative to parents Width or Height
42 struct RelativeToWidthOrHeight
45 * Constraint that is relative (%) to parent width/height and applies a
46 * unit based padding before the relative calculation.
47 * @param scale of parent minus padding between 0 and 1
48 * @param padding in world coordinate units
49 * @param fixed part in world coordinate units
51 RelativeToWidthOrHeight( float scale, float padding, float fixed )
52 : mScaleFactor( scale ),
58 inline float operator()( const float& parentWidthOrHeight )
60 return mFixed + ( parentWidthOrHeight - mPadding ) * mScaleFactor;
63 float operator()( const float& current,
64 const PropertyInput& parentWidthOrHeight )
66 return operator()( parentWidthOrHeight.GetFloat() );
74 #if defined(DEBUG_ENABLED)
75 // debugging support, very useful when new features are added or bugs are hunted down
76 // currently not called from code so compiler will optimize these away, kept here for future debugging
78 #define TABLEVIEW_TAG "DALI Toolkit::TableView"
79 #define TV_LOG(fmt, args...) LOG(LOG_INFO, TABLEVIEW_TAG, fmt, ## args)
81 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
83 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
85 for( unsigned int i = 0; i < array.GetRows(); ++i )
87 for( unsigned int j = 0; j < array.GetColumns(); ++j )
89 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
95 TV_LOG("Array[%d,%d]=%c %d,%d,%d,%d ", i, j, actor,
96 data.position.rowIndex, data.position.columnIndex,
97 data.position.rowSpan, data.position.columnSpan );
103 // debugging support, very useful when new features are added or bugs are hunted down
104 // currently not called from code so compiler will optimize these away, kept here for future debugging
105 void PrintArray( Array2d<Size>& array )
107 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
109 for( unsigned int i = 0; i < array.GetRows(); ++i )
111 for( unsigned int j = 0; j < array.GetColumns(); ++j )
113 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
118 // debugging support, very useful when new features are added or bugs are hunted down
119 // currently not called from code so compiler will optimize these away, kept here for future debugging
120 void PrintVector( vector<float>& array )
122 TV_LOG( "vector, size [%d]\n", array.size() );
124 for( unsigned int i = 0; i < array.size(); ++i )
126 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
130 #endif // defined(DEBUG_ENABLED)
143 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
145 // Create the implementation, temporarily owned by this handle on stack
146 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
148 // Pass ownership to CustomActor handle
149 Toolkit::TableView handle( *impl );
151 // Second-phase init of the implementation
152 // This can only be done after the CustomActor connection has been made...
158 bool TableView::AddChild( Actor child, Toolkit::TableView::CellPosition position )
160 // check that the child is valid
161 DALI_ASSERT_ALWAYS( child );
163 // if child is already parented, we adopt it
164 if( child.GetParent() )
166 child.GetParent().Remove( child );
168 // check if we need to expand our data array
169 if( position.rowIndex >= mCellData.GetRows() )
171 // only adding new rows
172 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
174 if( position.columnIndex >= mCellData.GetColumns() )
176 // only adding new columns
177 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
179 // check if there already is something in this cell
180 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
182 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
184 RelayoutingLock lock( *this );
188 // put the actor to the main cell
191 data.position = position;
192 mCellData[ position.rowIndex ][ position.columnIndex ] = data;
193 // if child spans multiple rows of columns
194 bool spanned = false;
195 if( position.rowSpan > 1 )
197 // span might go outside table
198 if( position.rowIndex + position.rowSpan > mCellData.GetRows() )
200 // increase table size for the full span, only increasing rows
201 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
205 if( position.columnSpan > 1 )
207 // span might go outside table
208 if( position.columnIndex + position.columnSpan > mCellData.GetColumns() )
210 // increase table size for the full span, only increasing columns
211 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
215 // if it spanned multiple rows, put the cellinfo in all of those
218 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
220 // store same information to all cells, this way we can identify
221 // if a cell is the prime location of an actor or a spanned one
222 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
224 // store same information to all cells, this way we can identify
225 // if a cell is the prime location of an actor or a spanned one
226 mCellData[ row ][ column ] = data;
230 // relayout the whole table
232 return true; // addition successful
235 Actor TableView::GetChildAt( Toolkit::TableView::CellPosition position )
237 // check if we have this row and column in the table
238 if( ( position.columnIndex >= mCellData.GetColumns() )||
239 ( position.rowIndex >= mCellData.GetRows() ) )
241 // return an empty handle
244 // return the child handle
245 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
248 Actor TableView::RemoveChildAt( Toolkit::TableView::CellPosition position )
250 // get the child handle
251 Actor child = GetChildAt( position );
252 // if no real actor there, nothing else to be done
255 RelayoutingLock lock( *this );
256 // Remove the child, this will trigger a call to OnControlChildRemove
257 Self().Remove( child );
259 // relayout the table only if instances were found
260 if( RemoveAllInstances( child ) )
265 // return the child back to caller
269 bool TableView::FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position )
271 // only find valid child actors
274 // walk through the layout data
275 const unsigned int rowCount = mCellData.GetRows();
276 const unsigned int columnCount = mCellData.GetColumns();
277 for( unsigned int row = 0; row < rowCount; ++row )
279 for( unsigned int column = 0; column < columnCount; ++column )
281 if( mCellData[ row ][ column ].actor == child )
283 position = mCellData[ row ][ column ].position;
292 void TableView::InsertRow( unsigned int rowIndex )
294 RelayoutingLock lock( *this );
295 mCellData.InsertRow( rowIndex );
296 // need to update the cellinfos for the items that moved
297 const unsigned int rowCount = mCellData.GetRows();
298 const unsigned int columnCount = mCellData.GetColumns();
299 for( unsigned int row = 0; row < rowCount; ++row )
301 for( unsigned int column = 0; column < columnCount; ++column )
303 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
304 // if cell is spanning and above and spans to inserted row
305 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
306 ( position.rowIndex + position.rowSpan > rowIndex ) )
308 // increase span by one
310 // copy cell to occupy the new column
311 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
313 // if below of inserted row, increase row index
314 else if( row > rowIndex )
316 // increase index by one
321 mRelativeSizes.InsertRow( rowIndex );
322 // inserting a row requires adjusting the height vectors
323 mFixedHeights.insert( mFixedHeights.begin() + rowIndex, 0 );
324 mRelativeHeights.insert( mRelativeHeights.begin() + rowIndex, 0 );
328 void TableView::DeleteRow( unsigned int rowIndex )
330 vector< Actor > ignored;
331 DeleteRow( rowIndex, ignored );
334 void TableView::DeleteRow( unsigned int rowIndex, vector<Actor>& removed )
336 RelayoutingLock lock( *this );
337 vector< CellData > lost;
338 mCellData.DeleteRow( rowIndex, lost );
339 // need to update the cellinfos for the items that moved
340 const unsigned int rowCount = mCellData.GetRows();
341 const unsigned int columnCount = mCellData.GetColumns();
342 for( unsigned int row = 0; row < rowCount; ++row )
344 for( unsigned int column = 0; column < columnCount; ++column )
346 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
347 // if cell is spanning and above and spans to deleted row
348 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
349 ( position.rowIndex + position.rowSpan > rowIndex ) )
351 // decrease span by one
352 if( position.rowSpan > 1 )
357 // if below of or at the inserted row, decrease row index
358 else if( row >= rowIndex )
360 // decrease index by one
361 if( position.rowIndex > 1 )
368 // 1 row removed, 0 columns
369 RemoveAndGetLostActors( lost, removed, 1u, 0u );
370 // resize the data structures
371 mRelativeSizes.DeleteRow( rowIndex );
372 // deleting a row requires adjusting the height vectors
373 mFixedHeights.erase( mFixedHeights.begin() + rowIndex );
374 mRelativeHeights.erase( mRelativeHeights.begin() + rowIndex );
378 void TableView::InsertColumn( unsigned int columnIndex )
380 RelayoutingLock lock( *this );
381 mCellData.InsertColumn( columnIndex );
382 // need to update the cellinfos for the items that moved
383 const unsigned int rowCount = mCellData.GetRows();
384 const unsigned int columnCount = mCellData.GetColumns();
385 for( unsigned int row = 0; row < rowCount; ++row )
387 for( unsigned int column = 0; column < columnCount; ++column )
389 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
390 // if cell is spanning and left side and spans to inserted column
391 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
392 ( position.columnIndex + position.columnSpan > columnIndex ) )
394 // increase span by one
395 position.columnSpan++;
396 // copy cell to occupy the new column
397 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
399 // if on the right side of inserted column, increase column index
400 else if( column > columnIndex )
402 // increase index by one
403 position.columnIndex++;
407 // relative sizes gets recalculated on Relayout
408 mRelativeSizes.InsertColumn( columnIndex );
409 // inserting a column requires adjusting the width vectors
410 mFixedWidths.insert( mFixedWidths.begin() + columnIndex, 0 );
411 mRelativeWidths.insert( mRelativeWidths.begin() + columnIndex, 0 );
415 void TableView::DeleteColumn( unsigned int columnIndex )
417 vector< Actor > ignored;
418 DeleteColumn( columnIndex, ignored );
421 void TableView::DeleteColumn( unsigned int columnIndex, vector<Actor>& removed )
423 RelayoutingLock lock( *this );
424 vector< CellData > lost;
425 mCellData.DeleteColumn( columnIndex, lost );
426 // need to update the cellinfos for the items that moved
427 const unsigned int rowCount = mCellData.GetRows();
428 const unsigned int columnCount = mCellData.GetColumns();
429 for( unsigned int row = 0; row < rowCount; ++row )
431 for( unsigned int column = 0; column < columnCount; ++column )
433 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
434 // if cell is spanning and left side and spans to inserted column
435 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
436 ( position.columnIndex + position.columnSpan > columnIndex ) )
438 // decrease span by one
439 if( position.columnSpan > 1 )
441 position.columnSpan--;
444 // if on the right side of or at the inserted column, decrease column index
445 else if( column >= columnIndex )
447 // decrease index by one
448 if( position.columnIndex > 0 )
450 position.columnIndex--;
455 // 0 rows, 1 column removed
456 RemoveAndGetLostActors( lost, removed, 0u, 1u );
457 // resize the data structures
458 mRelativeSizes.DeleteColumn( columnIndex );
459 // deleting a column requires adjusting the width vectors
460 mFixedWidths.erase( mFixedWidths.begin() + columnIndex );
461 mRelativeWidths.erase( mRelativeWidths.begin() + columnIndex );
466 void TableView::Resize( unsigned int rows, unsigned int columns )
468 vector< Actor > ignored;
469 Resize( rows, columns, ignored );
472 void TableView::Resize( unsigned int rows, unsigned int columns, vector<Actor>& removed )
474 RelayoutingLock lock( *this );
475 unsigned int oldRows = GetRows();
476 unsigned int oldColumns = GetColumns();
478 vector< CellData > lost;
479 ResizeContainers( rows, columns, lost );
480 // calculate if we lost rows or columns
481 unsigned int rowsRemoved = 0;
482 unsigned int newRows = GetRows();
483 if( oldRows < newRows )
485 rowsRemoved = newRows - oldRows;
487 unsigned int columnsRemoved = 0;
488 unsigned int newColumns = GetColumns();
489 if( oldColumns < newColumns )
491 rowsRemoved = newColumns - oldColumns;
493 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
494 // finally relayout once all actors are removed
498 void TableView::SetCellPadding( Size padding )
500 // if padding really changed
501 if( padding != mPadding )
509 Size TableView::GetCellPadding()
514 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
516 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
517 // add the fixed height to the array of fixed heights
518 mFixedHeights[ rowIndex ] = height;
519 // relayout all cells, no lock needed as nothing added or removed
523 float TableView::GetFixedHeight( unsigned int rowIndex ) const
525 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
527 return mFixedHeights[ rowIndex ];
530 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
532 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
533 // add the relative height to the array of relative heights
534 mRelativeHeights[ rowIndex ] = heightPercentage;
535 // relayout all cells, no lock needed as nothing added or removed
539 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
541 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
543 return mRelativeHeights[ rowIndex ];
546 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
548 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
549 // add the fixed width to the array of fixed column widths
550 mFixedWidths[ columnIndex ] = width;
551 // relayout all cells, no lock needed as nothing added or removed
555 float TableView::GetFixedWidth( unsigned int columnIndex ) const
557 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
559 return mFixedWidths[ columnIndex ];
562 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
564 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
565 // add the relative widths to the array of relative widths
566 mRelativeWidths[ columnIndex ] = widthPercentage;
567 // relayout all cells, no lock needed as nothing added or removed
571 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
573 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
575 return mRelativeWidths[ columnIndex ];
578 void TableView::SetLayoutAnimationDuration( float duration )
580 mConstraintDuration = duration;
583 float TableView::GetLayoutAnimationDuration()
585 return mConstraintDuration;
588 void TableView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
590 float fixedHeightsTotal = 0.0f;
591 float fixedWidthsTotal = 0.0f;
593 // 1. update the relative sizes and calculate total fixed height and width
594 UpdateRelativeSizes( fixedHeightsTotal, fixedWidthsTotal );
596 // 2. go through the layout data and create constraints
597 float cumulatedFixedHeight = 0.0f;
598 float cumulatedRelativeHeight = 0.0f;
601 const unsigned int rowCount = mCellData.GetRows();
602 const unsigned int columnCount = mCellData.GetColumns();
603 // float versions of the count + 1 to keep precision
604 const float maxRowPlusOne( rowCount + 1 );
605 const float maxColumnPlusOne( columnCount + 1 );
606 for( unsigned int row = 0; row < rowCount; ++row )
608 // reset widths at the start of each row
609 float cumulatedFixedWidth = 0.0f;
610 float cumulatedRelativeWidth = 0.0f;
611 for( unsigned int column = 0; column < columnCount; ++column )
613 // check if this cell has an actor
614 Actor actor = mCellData[ row ][ column ].actor;
615 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
616 // if there is an actor and this is the main cell of the actor
617 // an actor can be in multiple cells if its row or columnspan is more than 1
618 // we however must only lay out each actor only once
619 if( ( actor )&&( position.rowIndex == row )&&( position.columnIndex == column ) )
621 // anchor actor correctly
622 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
623 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
624 // remove old constraints
625 actor.RemoveConstraints();
628 // get the row and column indices
629 float rowPos( position.rowIndex );
630 float colPos( position.columnIndex );
631 // constrain the actor position to be relative to the width and height of table
632 // minus the padding of course (padding is all around cells)
633 Vector2 relativePosition( cumulatedRelativeWidth, cumulatedRelativeHeight );
634 // fixed height rows and fixed width cells are considered as padding so
635 // they are removed from the total size for relative
636 // for position only consider cumulated fixed rows and columns from top and left
637 Vector2 positionPadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
638 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
639 Vector2 fixedPosition( ( colPos + 1.0f ) * mPadding.width + cumulatedFixedWidth,
640 ( rowPos + 1.0f ) * mPadding.height + cumulatedFixedHeight );
642 Constraint widthConstraint = Constraint::New<float>( Actor::POSITION_X,
643 ParentSource( Actor::SIZE_WIDTH ),
644 RelativeToWidthOrHeight( relativePosition.x, positionPadding.x, fixedPosition.x ) );
646 Constraint heightConstraint = Constraint::New<float>( Actor::POSITION_Y,
647 ParentSource( Actor::SIZE_HEIGHT ),
648 RelativeToWidthOrHeight( relativePosition.y, positionPadding.y, fixedPosition.y ) );
650 widthConstraint.SetApplyTime( mConstraintDuration );
651 heightConstraint.SetApplyTime( mConstraintDuration );
653 // bake constrained position value if constraint is removed
654 widthConstraint.SetRemoveAction( Constraint::Bake );
655 heightConstraint.SetRemoveAction( Constraint::Bake );
657 actor.ApplyConstraint( widthConstraint );
658 actor.ApplyConstraint( heightConstraint );
661 // constrain the actor size to be relative to the size of table
662 // get the relative size for this cell
663 Vector2 relativeSize( mRelativeSizes[ row ][ column ] );
664 Vector2 fixedSize( mFixedWidths[ column ], mFixedHeights[ row ] );
665 // if we span multiple cells, need to sum them all up, both fixed and relative parts
666 if( position.rowSpan > 1 )
668 for( unsigned int i = 1; i < position.rowSpan; ++i )
670 // accumulate the height only
671 relativeSize.height += mRelativeSizes[ row + i ][ column ].height;
672 fixedSize.height += mFixedHeights[ row + i ];
675 if( position.columnSpan > 1 )
677 for( unsigned int i = 1; i < position.columnSpan; ++i )
679 // accumulate the width only
680 relativeSize.width += mRelativeSizes[ row ][ column + i ].width;
681 fixedSize.width += mFixedWidths[ column + i ];
684 // minus the padding from size (padding is all around cells)
685 // if item spans multiple columns or rows then less padding is added (default span is 1)
686 // fixed height rows and fixed width cells are considered as padding so they are removed
687 // from the total available size for relative cells
688 Vector2 sizePadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
689 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
690 // and added to the fixed size multiplied by the span of rows and columns
691 fixedSize.width += ( position.columnSpan - 1.0f ) * mPadding.width;
692 fixedSize.height += ( position.rowSpan - 1.0f ) * mPadding.height;
694 RelativeToWidthOrHeight relativeWidthFunctor( relativeSize.x, sizePadding.x, fixedSize.x );
695 RelativeToWidthOrHeight relativeHeightFunctor( relativeSize.y, sizePadding.y, fixedSize.y );
697 widthConstraint = Constraint::New<float>( Actor::SIZE_WIDTH,
698 ParentSource( Actor::SIZE_WIDTH ),
699 relativeWidthFunctor );
701 heightConstraint = Constraint::New<float>( Actor::SIZE_HEIGHT,
702 ParentSource( Actor::SIZE_HEIGHT ),
703 relativeHeightFunctor );
705 widthConstraint.SetApplyTime( mConstraintDuration );
706 heightConstraint.SetApplyTime( mConstraintDuration );
708 // bake constrained size value if constraint is removed
709 widthConstraint.SetRemoveAction( Constraint::Bake );
710 heightConstraint.SetRemoveAction( Constraint::Bake );
712 actor.ApplyConstraint( widthConstraint );
713 actor.ApplyConstraint( heightConstraint );
716 Relayout ( actor, Vector2( relativeWidthFunctor( size.width ), relativeHeightFunctor( size.height ) ), container );
718 // for position we need to keep track of current fixed width and relative width
719 // increase for next column
720 cumulatedFixedWidth += mFixedWidths[ column ];
721 cumulatedRelativeWidth += mRelativeSizes[ row ][ column ].width;
723 // for position we need to keep track of current fixed height and relative height
724 // increase for next row
725 cumulatedFixedHeight += mFixedHeights[ row ];
726 cumulatedRelativeHeight += mRelativeSizes[ row ][ 0 ].height; // all columns share same height
730 unsigned int TableView::GetRows()
732 return mCellData.GetRows();
735 unsigned int TableView::GetColumns()
737 return mCellData.GetColumns();
740 void TableView::OnControlChildAdd( Actor& child )
742 if( mLayoutingChild )
744 // we're in the middle of laying out children so no point doing anything here
747 // check if we're already laying out this child somewhere on the table
748 // walk through the layout data
749 const unsigned int rowCount = mCellData.GetRows();
750 const unsigned int columnCount = mCellData.GetColumns();
751 // child not yet laid out, find the first free slot
752 for( unsigned int row = 0; row < rowCount; ++row )
754 for( unsigned int column = 0; column < columnCount; ++column )
756 // no actor means free cell
757 if( !(mCellData[ row ][ column ].actor) )
759 // put the actor in the cell
762 data.position.columnIndex = column;
763 data.position.rowIndex = row;
764 mCellData[ row ][ column ] = data;
771 // still here, no room for the poor child so increase the array. Need a new row
772 ResizeContainers( rowCount + 1, columnCount );
773 // put the actor to the first cell of the new row
776 data.position.rowIndex = rowCount;
777 data.position.columnIndex = 0;
778 mCellData[ rowCount ][ 0 ] = data;
779 // finally relayout the table
783 void TableView::OnControlChildRemove( Actor& child )
785 // dont process if we're in the middle of bigger operation like delete row, column or resize
786 if( !mLayoutingChild )
788 // relayout the table only if instances were found
789 if( RemoveAllInstances( child ) )
796 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
797 : ControlImpl( true ), // requires touch
798 mCellData( initialRows, initialColumns ),
799 mLayoutingChild( false ),
800 mConstraintDuration( DEFAULT_CONSTRAINT_DURATION )
802 SetKeyboardNavigationSupport( true );
803 ResizeContainers( initialRows, initialColumns );
806 void TableView::OnInitialize()
808 // Make self as keyboard focusable and focus group
810 self.SetKeyboardFocusable(true);
811 SetAsKeyboardFocusGroup(true);
814 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
816 vector<CellData> ignored;
817 ResizeContainers( rows, columns, ignored );
820 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, vector<CellData>& removed )
822 mCellData.Resize( rows, columns, removed );
823 // we dont care if these go smaller, data will be regenerated or is not needed anymore
824 mRelativeSizes.Resize( rows, columns );
825 mFixedHeights.resize( rows );
826 mRelativeHeights.resize( rows );
827 mFixedWidths.resize( columns );
828 mRelativeWidths.resize( columns );
831 void TableView::RemoveAndGetLostActors( const vector<CellData>& lost, vector<Actor>& removed,
832 unsigned int rowsRemoved, unsigned int columnsRemoved )
834 // iterate through all lost cells
835 vector< CellData >::const_iterator iter = lost.begin();
836 for( ; iter != lost.end(); ++iter )
838 // if it is a valid actor
841 // is this actor still somewhere else in the table
842 Toolkit::TableView::CellPosition position;
843 if( FindChildPosition( (*iter).actor, position ) )
845 // it must be spanning multiple cells, position contains the top left most one
846 // check if position is left of the removed location
847 if( position.columnIndex < (*iter).position.columnIndex )
849 // if column span is greater than 1
850 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
852 // decrease column span
853 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
856 // check if position is left of the removed location
857 if( position.rowIndex < (*iter).position.rowIndex )
859 // if row span is greater than 1
860 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
863 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
869 // this actor is gone for good
870 // add actor to removed container
871 removed.push_back( (*iter).actor );
872 // we dont want the child actor anymore
873 Self().Remove( (*iter).actor );
879 bool TableView::RemoveAllInstances( Actor child )
882 // walk through the layout data
883 const unsigned int rowCount = mCellData.GetRows();
884 const unsigned int columnCount = mCellData.GetColumns();
885 for( unsigned int row = 0; row < rowCount; ++row )
887 for( unsigned int column = 0; column < columnCount; ++column )
889 if( mCellData[ row ][ column ].actor == child )
891 // clear the cell, NOTE that the cell might be spanning multiple cells
892 mCellData[ row ][ column ] = CellData();
900 void TableView::UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal )
902 // 1. check all the fixed heights and widths to know how much size they take in total
903 // as well as the relative heights and widths to know how much is left for the 'fill' cells
904 unsigned int fixedRowCount = 0;
905 unsigned int relativeRowCount = 0;
906 float relativeHeightsTotal = 0.0f;
907 const unsigned int rowCount = mCellData.GetRows();
908 for( unsigned int row = 0; row < rowCount; ++row )
910 if( mFixedHeights[ row ] > 0.0f )
913 fixedHeightsTotal += mFixedHeights[ row ];
915 if( mRelativeHeights[ row ] > 0.0f )
918 relativeHeightsTotal += mRelativeHeights[ row ];
921 unsigned int fixedColumnCount = 0;
922 unsigned int relativeColumnCount = 0;
923 const unsigned int columnCount = mCellData.GetColumns();
924 float relativeWidthsTotal = 0.0f;
925 for( unsigned int column = 0; column < columnCount; ++column )
927 if( mFixedWidths[ column ] > 0.0f )
930 fixedWidthsTotal += mFixedWidths[ column ];
932 if( mRelativeWidths[ column ] > 0.0f )
934 ++relativeColumnCount;
935 relativeWidthsTotal += mRelativeWidths[ column ];
939 // 2. cap the relative width and height totals to 100%
940 if( relativeHeightsTotal > 1.0f )
942 relativeHeightsTotal = 1.0f;
944 if( relativeWidthsTotal > 1.0f )
946 relativeWidthsTotal = 1.0f;
949 // 3. create a table of relative sizes so we can lookup for cells that span multiple rows & colums
950 const float fillRowCount( rowCount - relativeRowCount - fixedRowCount );
951 const float fillColumnCount( columnCount - relativeColumnCount - fixedColumnCount );
953 // walk through the data containers
954 for( unsigned int row = 0; row < rowCount; ++row )
956 float relativeHeight = 0.0f;
957 // if we have a fixed height, relative height is 0
958 if( mFixedHeights[ row ] > 0.0f )
960 relativeHeight = 0.0f;
962 // else if we're given a specific row height %, use that
963 else if( mRelativeHeights[ row ] > 0.0f )
965 relativeHeight = mRelativeHeights[ row ];
967 // else if there are fill rows
968 else if( fillRowCount > 0 )
970 // this is a 'fill' row. it gets the remainder of the 100% divided evenly between 'fill' rows
971 relativeHeight = (1.0f - relativeHeightsTotal ) / fillRowCount;
973 for( unsigned int column = 0; column < columnCount; ++column )
975 float relativeWidth = 0.0f;
976 // if we have a fixed width, relative width is 0
977 if( mFixedWidths[ column ] > 0.0f )
979 relativeWidth = 0.0f;
981 // else if we're given a specific column width %, use that
982 else if( mRelativeWidths[ column ] > 0.0f )
984 relativeWidth = mRelativeWidths[ column ];
986 // else if there are fill columns
987 else if( fillColumnCount > 0 )
989 // this is a 'fill' column. it gets the remainder of the 100% divided evenly between 'fill' columns
990 relativeWidth = (1.0f - relativeWidthsTotal ) / fillColumnCount;
993 mRelativeSizes[ row ][ column ] = Size( relativeWidth, relativeHeight );
998 TableView::~TableView()
1003 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1005 Actor nextFocusableActor;
1007 if ( !currentFocusedActor )
1009 // Nothing is currently focused, so the child in the first cell should be focused.
1010 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1014 Toolkit::TableView::CellPosition position;
1015 if( FindChildPosition( currentFocusedActor, position ) )
1017 // The current focused actor is a child of TableView
1018 bool focusLost = false;
1019 int currentRow = position.rowIndex;
1020 int currentColumn = position.columnIndex;
1021 int numberOfColumns = GetColumns();
1022 int numberOfRows = GetRows();
1024 switch ( direction )
1028 if(--currentColumn < 0)
1030 currentColumn = numberOfColumns - 1;
1031 if(--currentRow < 0)
1033 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1034 focusLost = (currentRow == 0);
1039 case Control::Right:
1041 if(++currentColumn > numberOfColumns - 1)
1044 if(++currentRow > numberOfRows - 1)
1046 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1047 focusLost = (currentRow == numberOfRows - 1);
1054 if(--currentRow < 0)
1056 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1057 focusLost = (currentRow == 0);
1063 if(++currentRow > numberOfRows - 1)
1065 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1066 focusLost = (currentRow == numberOfRows - 1);
1072 // Move the focus if we haven't lost it.
1075 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1080 // The current focused actor is not within table view, so the child in the first cell should be focused.
1081 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1085 return nextFocusableActor;
1088 } // namespace Internal
1090 } // namespace Toolkit