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>
34 return Toolkit::TableView::New(0, 0);
36 TypeRegistration mType( typeid(Toolkit::TableView), typeid(Toolkit::Control), Create );
38 const float DEFAULT_CONSTRAINT_DURATION = 0.0f;
41 * Constraint that sets a child property relative to parents Width or Height
43 struct RelativeToWidthOrHeight
46 * Constraint that is relative (%) to parent width/height and applies a
47 * unit based padding before the relative calculation.
48 * @param scale of parent minus padding between 0 and 1
49 * @param padding in world coordinate units
50 * @param fixed part in world coordinate units
52 RelativeToWidthOrHeight( float scale, float padding, float fixed )
53 : mScaleFactor( scale ),
59 inline float operator()( const float& parentWidthOrHeight )
61 return mFixed + ( parentWidthOrHeight - mPadding ) * mScaleFactor;
64 float operator()( const float& current,
65 const PropertyInput& parentWidthOrHeight )
67 return operator()( parentWidthOrHeight.GetFloat() );
75 #if defined(DEBUG_ENABLED)
76 // debugging support, very useful when new features are added or bugs are hunted down
77 // currently not called from code so compiler will optimize these away, kept here for future debugging
79 #define TABLEVIEW_TAG "DALI Toolkit::TableView"
80 #define TV_LOG(fmt, args...) LOG(LOG_INFO, TABLEVIEW_TAG, fmt, ## args)
82 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
84 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
86 for( unsigned int i = 0; i < array.GetRows(); ++i )
88 for( unsigned int j = 0; j < array.GetColumns(); ++j )
90 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
96 TV_LOG("Array[%d,%d]=%c %d,%d,%d,%d ", i, j, actor,
97 data.position.rowIndex, data.position.columnIndex,
98 data.position.rowSpan, data.position.columnSpan );
104 // debugging support, very useful when new features are added or bugs are hunted down
105 // currently not called from code so compiler will optimize these away, kept here for future debugging
106 void PrintArray( Array2d<Size>& array )
108 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
110 for( unsigned int i = 0; i < array.GetRows(); ++i )
112 for( unsigned int j = 0; j < array.GetColumns(); ++j )
114 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
119 // debugging support, very useful when new features are added or bugs are hunted down
120 // currently not called from code so compiler will optimize these away, kept here for future debugging
121 void PrintVector( vector<float>& array )
123 TV_LOG( "vector, size [%d]\n", array.size() );
125 for( unsigned int i = 0; i < array.size(); ++i )
127 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
131 #endif // defined(DEBUG_ENABLED)
144 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
146 // Create the implementation, temporarily owned by this handle on stack
147 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
149 // Pass ownership to CustomActor handle
150 Toolkit::TableView handle( *impl );
152 // Second-phase init of the implementation
153 // This can only be done after the CustomActor connection has been made...
159 bool TableView::AddChild( Actor child, Toolkit::TableView::CellPosition position )
161 // check that the child is valid
162 DALI_ASSERT_ALWAYS( child );
164 // if child is already parented, we adopt it
165 if( child.GetParent() )
167 child.GetParent().Remove( child );
169 // check if we need to expand our data array
170 if( position.rowIndex >= mCellData.GetRows() )
172 // only adding new rows
173 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
175 if( position.columnIndex >= mCellData.GetColumns() )
177 // only adding new columns
178 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
180 // check if there already is something in this cell
181 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
183 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
185 RelayoutingLock lock( *this );
189 // put the actor to the main cell
192 data.position = position;
193 mCellData[ position.rowIndex ][ position.columnIndex ] = data;
194 // if child spans multiple rows of columns
195 bool spanned = false;
196 if( position.rowSpan > 1 )
198 // span might go outside table
199 if( position.rowIndex + position.rowSpan > mCellData.GetRows() )
201 // increase table size for the full span, only increasing rows
202 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
206 if( position.columnSpan > 1 )
208 // span might go outside table
209 if( position.columnIndex + position.columnSpan > mCellData.GetColumns() )
211 // increase table size for the full span, only increasing columns
212 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
216 // if it spanned multiple rows, put the cellinfo in all of those
219 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
221 // store same information to all cells, this way we can identify
222 // if a cell is the prime location of an actor or a spanned one
223 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
225 // store same information to all cells, this way we can identify
226 // if a cell is the prime location of an actor or a spanned one
227 mCellData[ row ][ column ] = data;
231 // relayout the whole table
233 return true; // addition successful
236 Actor TableView::GetChildAt( Toolkit::TableView::CellPosition position )
238 // check if we have this row and column in the table
239 if( ( position.columnIndex >= mCellData.GetColumns() )||
240 ( position.rowIndex >= mCellData.GetRows() ) )
242 // return an empty handle
245 // return the child handle
246 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
249 Actor TableView::RemoveChildAt( Toolkit::TableView::CellPosition position )
251 // get the child handle
252 Actor child = GetChildAt( position );
253 // if no real actor there, nothing else to be done
256 RelayoutingLock lock( *this );
257 // Remove the child, this will trigger a call to OnControlChildRemove
258 Self().Remove( child );
260 // relayout the table only if instances were found
261 if( RemoveAllInstances( child ) )
266 // return the child back to caller
270 bool TableView::FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position )
272 // only find valid child actors
275 // walk through the layout data
276 const unsigned int rowCount = mCellData.GetRows();
277 const unsigned int columnCount = mCellData.GetColumns();
278 for( unsigned int row = 0; row < rowCount; ++row )
280 for( unsigned int column = 0; column < columnCount; ++column )
282 if( mCellData[ row ][ column ].actor == child )
284 position = mCellData[ row ][ column ].position;
293 void TableView::InsertRow( unsigned int rowIndex )
295 RelayoutingLock lock( *this );
296 mCellData.InsertRow( rowIndex );
297 // need to update the cellinfos for the items that moved
298 const unsigned int rowCount = mCellData.GetRows();
299 const unsigned int columnCount = mCellData.GetColumns();
300 for( unsigned int row = 0; row < rowCount; ++row )
302 for( unsigned int column = 0; column < columnCount; ++column )
304 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
305 // if cell is spanning and above and spans to inserted row
306 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
307 ( position.rowIndex + position.rowSpan > rowIndex ) )
309 // increase span by one
311 // copy cell to occupy the new column
312 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
314 // if below of inserted row, increase row index
315 else if( row > rowIndex )
317 // increase index by one
322 mRelativeSizes.InsertRow( rowIndex );
323 // inserting a row requires adjusting the height vectors
324 mFixedHeights.insert( mFixedHeights.begin() + rowIndex, 0 );
325 mRelativeHeights.insert( mRelativeHeights.begin() + rowIndex, 0 );
329 void TableView::DeleteRow( unsigned int rowIndex )
331 vector< Actor > ignored;
332 DeleteRow( rowIndex, ignored );
335 void TableView::DeleteRow( unsigned int rowIndex, vector<Actor>& removed )
337 RelayoutingLock lock( *this );
338 vector< CellData > lost;
339 mCellData.DeleteRow( rowIndex, lost );
340 // need to update the cellinfos for the items that moved
341 const unsigned int rowCount = mCellData.GetRows();
342 const unsigned int columnCount = mCellData.GetColumns();
343 for( unsigned int row = 0; row < rowCount; ++row )
345 for( unsigned int column = 0; column < columnCount; ++column )
347 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
348 // if cell is spanning and above and spans to deleted row
349 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
350 ( position.rowIndex + position.rowSpan > rowIndex ) )
352 // decrease span by one
353 if( position.rowSpan > 1 )
358 // if below of or at the inserted row, decrease row index
359 else if( row >= rowIndex )
361 // decrease index by one
362 if( position.rowIndex > 1 )
369 // 1 row removed, 0 columns
370 RemoveAndGetLostActors( lost, removed, 1u, 0u );
371 // resize the data structures
372 mRelativeSizes.DeleteRow( rowIndex );
373 // deleting a row requires adjusting the height vectors
374 mFixedHeights.erase( mFixedHeights.begin() + rowIndex );
375 mRelativeHeights.erase( mRelativeHeights.begin() + rowIndex );
379 void TableView::InsertColumn( unsigned int columnIndex )
381 RelayoutingLock lock( *this );
382 mCellData.InsertColumn( columnIndex );
383 // need to update the cellinfos for the items that moved
384 const unsigned int rowCount = mCellData.GetRows();
385 const unsigned int columnCount = mCellData.GetColumns();
386 for( unsigned int row = 0; row < rowCount; ++row )
388 for( unsigned int column = 0; column < columnCount; ++column )
390 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
391 // if cell is spanning and left side and spans to inserted column
392 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
393 ( position.columnIndex + position.columnSpan > columnIndex ) )
395 // increase span by one
396 position.columnSpan++;
397 // copy cell to occupy the new column
398 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
400 // if on the right side of inserted column, increase column index
401 else if( column > columnIndex )
403 // increase index by one
404 position.columnIndex++;
408 // relative sizes gets recalculated on Relayout
409 mRelativeSizes.InsertColumn( columnIndex );
410 // inserting a column requires adjusting the width vectors
411 mFixedWidths.insert( mFixedWidths.begin() + columnIndex, 0 );
412 mRelativeWidths.insert( mRelativeWidths.begin() + columnIndex, 0 );
416 void TableView::DeleteColumn( unsigned int columnIndex )
418 vector< Actor > ignored;
419 DeleteColumn( columnIndex, ignored );
422 void TableView::DeleteColumn( unsigned int columnIndex, vector<Actor>& removed )
424 RelayoutingLock lock( *this );
425 vector< CellData > lost;
426 mCellData.DeleteColumn( columnIndex, lost );
427 // need to update the cellinfos for the items that moved
428 const unsigned int rowCount = mCellData.GetRows();
429 const unsigned int columnCount = mCellData.GetColumns();
430 for( unsigned int row = 0; row < rowCount; ++row )
432 for( unsigned int column = 0; column < columnCount; ++column )
434 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
435 // if cell is spanning and left side and spans to inserted column
436 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
437 ( position.columnIndex + position.columnSpan > columnIndex ) )
439 // decrease span by one
440 if( position.columnSpan > 1 )
442 position.columnSpan--;
445 // if on the right side of or at the inserted column, decrease column index
446 else if( column >= columnIndex )
448 // decrease index by one
449 if( position.columnIndex > 0 )
451 position.columnIndex--;
456 // 0 rows, 1 column removed
457 RemoveAndGetLostActors( lost, removed, 0u, 1u );
458 // resize the data structures
459 mRelativeSizes.DeleteColumn( columnIndex );
460 // deleting a column requires adjusting the width vectors
461 mFixedWidths.erase( mFixedWidths.begin() + columnIndex );
462 mRelativeWidths.erase( mRelativeWidths.begin() + columnIndex );
467 void TableView::Resize( unsigned int rows, unsigned int columns )
469 vector< Actor > ignored;
470 Resize( rows, columns, ignored );
473 void TableView::Resize( unsigned int rows, unsigned int columns, vector<Actor>& removed )
475 RelayoutingLock lock( *this );
476 unsigned int oldRows = GetRows();
477 unsigned int oldColumns = GetColumns();
479 vector< CellData > lost;
480 ResizeContainers( rows, columns, lost );
481 // calculate if we lost rows or columns
482 unsigned int rowsRemoved = 0;
483 unsigned int newRows = GetRows();
484 if( oldRows < newRows )
486 rowsRemoved = newRows - oldRows;
488 unsigned int columnsRemoved = 0;
489 unsigned int newColumns = GetColumns();
490 if( oldColumns < newColumns )
492 rowsRemoved = newColumns - oldColumns;
494 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
495 // finally relayout once all actors are removed
499 void TableView::SetCellPadding( Size padding )
501 // if padding really changed
502 if( padding != mPadding )
510 Size TableView::GetCellPadding()
515 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
517 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
518 // add the fixed height to the array of fixed heights
519 mFixedHeights[ rowIndex ] = height;
520 // relayout all cells, no lock needed as nothing added or removed
524 float TableView::GetFixedHeight( unsigned int rowIndex ) const
526 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
528 return mFixedHeights[ rowIndex ];
531 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
533 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
534 // add the relative height to the array of relative heights
535 mRelativeHeights[ rowIndex ] = heightPercentage;
536 // relayout all cells, no lock needed as nothing added or removed
540 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
542 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
544 return mRelativeHeights[ rowIndex ];
547 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
549 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
550 // add the fixed width to the array of fixed column widths
551 mFixedWidths[ columnIndex ] = width;
552 // relayout all cells, no lock needed as nothing added or removed
556 float TableView::GetFixedWidth( unsigned int columnIndex ) const
558 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
560 return mFixedWidths[ columnIndex ];
563 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
565 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
566 // add the relative widths to the array of relative widths
567 mRelativeWidths[ columnIndex ] = widthPercentage;
568 // relayout all cells, no lock needed as nothing added or removed
572 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
574 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
576 return mRelativeWidths[ columnIndex ];
579 void TableView::SetLayoutAnimationDuration( float duration )
581 mConstraintDuration = duration;
584 float TableView::GetLayoutAnimationDuration()
586 return mConstraintDuration;
589 void TableView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
591 float fixedHeightsTotal = 0.0f;
592 float fixedWidthsTotal = 0.0f;
594 // 1. update the relative sizes and calculate total fixed height and width
595 UpdateRelativeSizes( fixedHeightsTotal, fixedWidthsTotal );
597 // 2. go through the layout data and create constraints
598 float cumulatedFixedHeight = 0.0f;
599 float cumulatedRelativeHeight = 0.0f;
602 const unsigned int rowCount = mCellData.GetRows();
603 const unsigned int columnCount = mCellData.GetColumns();
604 // float versions of the count + 1 to keep precision
605 const float maxRowPlusOne( rowCount + 1 );
606 const float maxColumnPlusOne( columnCount + 1 );
607 for( unsigned int row = 0; row < rowCount; ++row )
609 // reset widths at the start of each row
610 float cumulatedFixedWidth = 0.0f;
611 float cumulatedRelativeWidth = 0.0f;
612 for( unsigned int column = 0; column < columnCount; ++column )
614 // check if this cell has an actor
615 Actor actor = mCellData[ row ][ column ].actor;
616 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
617 // if there is an actor and this is the main cell of the actor
618 // an actor can be in multiple cells if its row or columnspan is more than 1
619 // we however must only lay out each actor only once
620 if( ( actor )&&( position.rowIndex == row )&&( position.columnIndex == column ) )
622 // anchor actor correctly
623 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
624 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
625 // remove old constraints
626 actor.RemoveConstraints();
629 // get the row and column indices
630 float rowPos( position.rowIndex );
631 float colPos( position.columnIndex );
632 // constrain the actor position to be relative to the width and height of table
633 // minus the padding of course (padding is all around cells)
634 Vector2 relativePosition( cumulatedRelativeWidth, cumulatedRelativeHeight );
635 // fixed height rows and fixed width cells are considered as padding so
636 // they are removed from the total size for relative
637 // for position only consider cumulated fixed rows and columns from top and left
638 Vector2 positionPadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
639 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
640 Vector2 fixedPosition( ( colPos + 1.0f ) * mPadding.width + cumulatedFixedWidth,
641 ( rowPos + 1.0f ) * mPadding.height + cumulatedFixedHeight );
643 Constraint widthConstraint = Constraint::New<float>( Actor::POSITION_X,
644 ParentSource( Actor::SIZE_WIDTH ),
645 RelativeToWidthOrHeight( relativePosition.x, positionPadding.x, fixedPosition.x ) );
647 Constraint heightConstraint = Constraint::New<float>( Actor::POSITION_Y,
648 ParentSource( Actor::SIZE_HEIGHT ),
649 RelativeToWidthOrHeight( relativePosition.y, positionPadding.y, fixedPosition.y ) );
651 widthConstraint.SetApplyTime( mConstraintDuration );
652 heightConstraint.SetApplyTime( mConstraintDuration );
654 // bake constrained position value if constraint is removed
655 widthConstraint.SetRemoveAction( Constraint::Bake );
656 heightConstraint.SetRemoveAction( Constraint::Bake );
658 actor.ApplyConstraint( widthConstraint );
659 actor.ApplyConstraint( heightConstraint );
662 // constrain the actor size to be relative to the size of table
663 // get the relative size for this cell
664 Vector2 relativeSize( mRelativeSizes[ row ][ column ] );
665 Vector2 fixedSize( mFixedWidths[ column ], mFixedHeights[ row ] );
666 // if we span multiple cells, need to sum them all up, both fixed and relative parts
667 if( position.rowSpan > 1 )
669 for( unsigned int i = 1; i < position.rowSpan; ++i )
671 // accumulate the height only
672 relativeSize.height += mRelativeSizes[ row + i ][ column ].height;
673 fixedSize.height += mFixedHeights[ row + i ];
676 if( position.columnSpan > 1 )
678 for( unsigned int i = 1; i < position.columnSpan; ++i )
680 // accumulate the width only
681 relativeSize.width += mRelativeSizes[ row ][ column + i ].width;
682 fixedSize.width += mFixedWidths[ column + i ];
685 // minus the padding from size (padding is all around cells)
686 // if item spans multiple columns or rows then less padding is added (default span is 1)
687 // fixed height rows and fixed width cells are considered as padding so they are removed
688 // from the total available size for relative cells
689 Vector2 sizePadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
690 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
691 // and added to the fixed size multiplied by the span of rows and columns
692 fixedSize.width += ( position.columnSpan - 1.0f ) * mPadding.width;
693 fixedSize.height += ( position.rowSpan - 1.0f ) * mPadding.height;
695 RelativeToWidthOrHeight relativeWidthFunctor( relativeSize.x, sizePadding.x, fixedSize.x );
696 RelativeToWidthOrHeight relativeHeightFunctor( relativeSize.y, sizePadding.y, fixedSize.y );
698 widthConstraint = Constraint::New<float>( Actor::SIZE_WIDTH,
699 ParentSource( Actor::SIZE_WIDTH ),
700 relativeWidthFunctor );
702 heightConstraint = Constraint::New<float>( Actor::SIZE_HEIGHT,
703 ParentSource( Actor::SIZE_HEIGHT ),
704 relativeHeightFunctor );
706 widthConstraint.SetApplyTime( mConstraintDuration );
707 heightConstraint.SetApplyTime( mConstraintDuration );
709 // bake constrained size value if constraint is removed
710 widthConstraint.SetRemoveAction( Constraint::Bake );
711 heightConstraint.SetRemoveAction( Constraint::Bake );
713 actor.ApplyConstraint( widthConstraint );
714 actor.ApplyConstraint( heightConstraint );
717 Relayout ( actor, Vector2( relativeWidthFunctor( size.width ), relativeHeightFunctor( size.height ) ), container );
719 // for position we need to keep track of current fixed width and relative width
720 // increase for next column
721 cumulatedFixedWidth += mFixedWidths[ column ];
722 cumulatedRelativeWidth += mRelativeSizes[ row ][ column ].width;
724 // for position we need to keep track of current fixed height and relative height
725 // increase for next row
726 cumulatedFixedHeight += mFixedHeights[ row ];
727 cumulatedRelativeHeight += mRelativeSizes[ row ][ 0 ].height; // all columns share same height
731 unsigned int TableView::GetRows()
733 return mCellData.GetRows();
736 unsigned int TableView::GetColumns()
738 return mCellData.GetColumns();
741 void TableView::OnControlChildAdd( Actor& child )
743 if( mLayoutingChild )
745 // we're in the middle of laying out children so no point doing anything here
748 // check if we're already laying out this child somewhere on the table
749 // walk through the layout data
750 const unsigned int rowCount = mCellData.GetRows();
751 const unsigned int columnCount = mCellData.GetColumns();
752 // child not yet laid out, find the first free slot
753 for( unsigned int row = 0; row < rowCount; ++row )
755 for( unsigned int column = 0; column < columnCount; ++column )
757 // no actor means free cell
758 if( !(mCellData[ row ][ column ].actor) )
760 // put the actor in the cell
763 data.position.columnIndex = column;
764 data.position.rowIndex = row;
765 mCellData[ row ][ column ] = data;
772 // still here, no room for the poor child so increase the array. Need a new row
773 ResizeContainers( rowCount + 1, columnCount );
774 // put the actor to the first cell of the new row
777 data.position.rowIndex = rowCount;
778 data.position.columnIndex = 0;
779 mCellData[ rowCount ][ 0 ] = data;
780 // finally relayout the table
784 void TableView::OnControlChildRemove( Actor& child )
786 // dont process if we're in the middle of bigger operation like delete row, column or resize
787 if( !mLayoutingChild )
789 // relayout the table only if instances were found
790 if( RemoveAllInstances( child ) )
797 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
798 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
799 mCellData( initialRows, initialColumns ),
800 mLayoutingChild( false ),
801 mConstraintDuration( DEFAULT_CONSTRAINT_DURATION )
803 SetKeyboardNavigationSupport( true );
804 ResizeContainers( initialRows, initialColumns );
807 void TableView::OnInitialize()
809 // Make self as keyboard focusable and focus group
811 self.SetKeyboardFocusable(true);
812 SetAsKeyboardFocusGroup(true);
815 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
817 vector<CellData> ignored;
818 ResizeContainers( rows, columns, ignored );
821 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, vector<CellData>& removed )
823 mCellData.Resize( rows, columns, removed );
824 // we dont care if these go smaller, data will be regenerated or is not needed anymore
825 mRelativeSizes.Resize( rows, columns );
826 mFixedHeights.resize( rows );
827 mRelativeHeights.resize( rows );
828 mFixedWidths.resize( columns );
829 mRelativeWidths.resize( columns );
832 void TableView::RemoveAndGetLostActors( const vector<CellData>& lost, vector<Actor>& removed,
833 unsigned int rowsRemoved, unsigned int columnsRemoved )
835 // iterate through all lost cells
836 vector< CellData >::const_iterator iter = lost.begin();
837 for( ; iter != lost.end(); ++iter )
839 // if it is a valid actor
842 // is this actor still somewhere else in the table
843 Toolkit::TableView::CellPosition position;
844 if( FindChildPosition( (*iter).actor, position ) )
846 // it must be spanning multiple cells, position contains the top left most one
847 // check if position is left of the removed location
848 if( position.columnIndex < (*iter).position.columnIndex )
850 // if column span is greater than 1
851 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
853 // decrease column span
854 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
857 // check if position is left of the removed location
858 if( position.rowIndex < (*iter).position.rowIndex )
860 // if row span is greater than 1
861 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
864 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
870 // this actor is gone for good
871 // add actor to removed container
872 removed.push_back( (*iter).actor );
873 // we dont want the child actor anymore
874 Self().Remove( (*iter).actor );
880 bool TableView::RemoveAllInstances( Actor child )
883 // walk through the layout data
884 const unsigned int rowCount = mCellData.GetRows();
885 const unsigned int columnCount = mCellData.GetColumns();
886 for( unsigned int row = 0; row < rowCount; ++row )
888 for( unsigned int column = 0; column < columnCount; ++column )
890 if( mCellData[ row ][ column ].actor == child )
892 // clear the cell, NOTE that the cell might be spanning multiple cells
893 mCellData[ row ][ column ] = CellData();
901 void TableView::UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal )
903 // 1. check all the fixed heights and widths to know how much size they take in total
904 // as well as the relative heights and widths to know how much is left for the 'fill' cells
905 unsigned int fixedRowCount = 0;
906 unsigned int relativeRowCount = 0;
907 float relativeHeightsTotal = 0.0f;
908 const unsigned int rowCount = mCellData.GetRows();
909 for( unsigned int row = 0; row < rowCount; ++row )
911 if( mFixedHeights[ row ] > 0.0f )
914 fixedHeightsTotal += mFixedHeights[ row ];
916 if( mRelativeHeights[ row ] > 0.0f )
919 relativeHeightsTotal += mRelativeHeights[ row ];
922 unsigned int fixedColumnCount = 0;
923 unsigned int relativeColumnCount = 0;
924 const unsigned int columnCount = mCellData.GetColumns();
925 float relativeWidthsTotal = 0.0f;
926 for( unsigned int column = 0; column < columnCount; ++column )
928 if( mFixedWidths[ column ] > 0.0f )
931 fixedWidthsTotal += mFixedWidths[ column ];
933 if( mRelativeWidths[ column ] > 0.0f )
935 ++relativeColumnCount;
936 relativeWidthsTotal += mRelativeWidths[ column ];
940 // 2. cap the relative width and height totals to 100%
941 if( relativeHeightsTotal > 1.0f )
943 relativeHeightsTotal = 1.0f;
945 if( relativeWidthsTotal > 1.0f )
947 relativeWidthsTotal = 1.0f;
950 // 3. create a table of relative sizes so we can lookup for cells that span multiple rows & colums
951 const float fillRowCount( rowCount - relativeRowCount - fixedRowCount );
952 const float fillColumnCount( columnCount - relativeColumnCount - fixedColumnCount );
954 // walk through the data containers
955 for( unsigned int row = 0; row < rowCount; ++row )
957 float relativeHeight = 0.0f;
958 // if we have a fixed height, relative height is 0
959 if( mFixedHeights[ row ] > 0.0f )
961 relativeHeight = 0.0f;
963 // else if we're given a specific row height %, use that
964 else if( mRelativeHeights[ row ] > 0.0f )
966 relativeHeight = mRelativeHeights[ row ];
968 // else if there are fill rows
969 else if( fillRowCount > 0 )
971 // this is a 'fill' row. it gets the remainder of the 100% divided evenly between 'fill' rows
972 relativeHeight = (1.0f - relativeHeightsTotal ) / fillRowCount;
974 for( unsigned int column = 0; column < columnCount; ++column )
976 float relativeWidth = 0.0f;
977 // if we have a fixed width, relative width is 0
978 if( mFixedWidths[ column ] > 0.0f )
980 relativeWidth = 0.0f;
982 // else if we're given a specific column width %, use that
983 else if( mRelativeWidths[ column ] > 0.0f )
985 relativeWidth = mRelativeWidths[ column ];
987 // else if there are fill columns
988 else if( fillColumnCount > 0 )
990 // this is a 'fill' column. it gets the remainder of the 100% divided evenly between 'fill' columns
991 relativeWidth = (1.0f - relativeWidthsTotal ) / fillColumnCount;
994 mRelativeSizes[ row ][ column ] = Size( relativeWidth, relativeHeight );
999 TableView::~TableView()
1004 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1006 Actor nextFocusableActor;
1008 if ( !currentFocusedActor )
1010 // Nothing is currently focused, so the child in the first cell should be focused.
1011 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1015 Toolkit::TableView::CellPosition position;
1016 if( FindChildPosition( currentFocusedActor, position ) )
1018 // The current focused actor is a child of TableView
1019 bool focusLost = false;
1020 int currentRow = position.rowIndex;
1021 int currentColumn = position.columnIndex;
1022 int numberOfColumns = GetColumns();
1023 int numberOfRows = GetRows();
1025 switch ( direction )
1027 case Toolkit::Control::Left:
1029 if(--currentColumn < 0)
1031 currentColumn = numberOfColumns - 1;
1032 if(--currentRow < 0)
1034 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1035 focusLost = (currentRow == 0);
1040 case Toolkit::Control::Right:
1042 if(++currentColumn > numberOfColumns - 1)
1045 if(++currentRow > numberOfRows - 1)
1047 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1048 focusLost = (currentRow == numberOfRows - 1);
1053 case Toolkit::Control::Up:
1055 if(--currentRow < 0)
1057 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1058 focusLost = (currentRow == 0);
1062 case Toolkit::Control::Down:
1064 if(++currentRow > numberOfRows - 1)
1066 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1067 focusLost = (currentRow == numberOfRows - 1);
1073 // Move the focus if we haven't lost it.
1076 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1081 // The current focused actor is not within table view, so the child in the first cell should be focused.
1082 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1086 return nextFocusableActor;
1089 } // namespace Internal
1091 } // namespace Toolkit