2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/table-view/table-view-impl.h>
23 #include <dali/public-api/adaptor-framework/physical-keyboard.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/animation/time-period.h>
26 #include <dali/public-api/object/ref-object.h>
27 #include <dali/public-api/object/type-registry.h>
28 #include <dali/public-api/scripting/scripting.h>
29 #include <dali/integration-api/debug.h>
32 #include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
39 const float DEFAULT_CONSTRAINT_DURATION = 0.0f;
42 * Constraint that sets a child property relative to parents Width or Height
44 struct RelativeToWidthOrHeight
47 * Constraint that is relative (%) to parent width/height and applies a
48 * unit based padding before the relative calculation.
49 * @param scale of parent minus padding between 0 and 1
50 * @param padding in world coordinate units
51 * @param fixed part in world coordinate units
53 RelativeToWidthOrHeight( float scale, float padding, float fixed )
54 : mScaleFactor( scale ),
60 inline float operator()( const float& parentWidthOrHeight )
62 return mFixed + ( parentWidthOrHeight - mPadding ) * mScaleFactor;
65 float operator()( const float& current,
66 const PropertyInput& parentWidthOrHeight )
68 return operator()( parentWidthOrHeight.GetFloat() );
76 #if defined(DEBUG_ENABLED)
77 // debugging support, very useful when new features are added or bugs are hunted down
78 // currently not called from code so compiler will optimize these away, kept here for future debugging
80 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
81 #define TV_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
83 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
85 TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
87 for( unsigned int i = 0; i < array.GetRows(); ++i )
89 for( unsigned int j = 0; j < array.GetColumns(); ++j )
91 Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
97 TV_LOG("Array[%d,%d]=%c %d,%d,%d,%d ", i, j, actor,
98 data.position.rowIndex, data.position.columnIndex,
99 data.position.rowSpan, data.position.columnSpan );
105 // debugging support, very useful when new features are added or bugs are hunted down
106 // currently not called from code so compiler will optimize these away, kept here for future debugging
107 void PrintArray( Array2d<Size>& array )
109 TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
111 for( unsigned int i = 0; i < array.GetRows(); ++i )
113 for( unsigned int j = 0; j < array.GetColumns(); ++j )
115 TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
120 // debugging support, very useful when new features are added or bugs are hunted down
121 // currently not called from code so compiler will optimize these away, kept here for future debugging
122 void PrintVector( vector<float>& array )
124 TV_LOG( "vector, size [%d]\n", array.size() );
126 for( unsigned int i = 0; i < array.size(); ++i )
128 TV_LOG( "vector[%d]=%.2f ", i, array[i] );
132 #endif // defined(DEBUG_ENABLED)
142 const Property::Index TableView::PROPERTY_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX );
143 const Property::Index TableView::PROPERTY_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 1 );
144 const Property::Index TableView::PROPERTY_CELL_PADDING( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 2 );
145 const Property::Index TableView::PROPERTY_LAYOUT_ANIMATION_DURATION( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 3 );
146 const Property::Index TableView::PROPERTY_LAYOUT_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 4 );
147 const Property::Index TableView::PROPERTY_LAYOUT_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 5 );
155 const Scripting::StringEnum< Toolkit::TableView::LayoutPolicy > LAYOUT_POLICY_STRING_TABLE[] =
157 { "fixed", Toolkit::TableView::Fixed },
158 { "relative", Toolkit::TableView::Relative },
159 { "fill", Toolkit::TableView::Fill }
162 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
167 return Toolkit::TableView::New(0, 0);
169 TypeRegistration mType( typeid(Toolkit::TableView), typeid(Toolkit::Control), Create );
171 PropertyRegistration property1( mType, "rows", Toolkit::TableView::PROPERTY_ROWS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
172 PropertyRegistration property2( mType, "columns", Toolkit::TableView::PROPERTY_COLUMNS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
173 PropertyRegistration property3( mType, "cell-padding", Toolkit::TableView::PROPERTY_CELL_PADDING, Property::VECTOR2, &TableView::SetProperty, &TableView::GetProperty );
174 PropertyRegistration property4( mType, "layout-animation-duration", Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION, Property::FLOAT, &TableView::SetProperty, &TableView::GetProperty );
175 PropertyRegistration property5( mType, "layout-rows", Toolkit::TableView::PROPERTY_LAYOUT_ROWS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
176 PropertyRegistration property6( mType, "layout-columns", Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
180 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
182 // Create the implementation, temporarily owned by this handle on stack
183 IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
185 // Pass ownership to CustomActor handle
186 Toolkit::TableView handle( *impl );
188 // Second-phase init of the implementation
189 // This can only be done after the CustomActor connection has been made...
195 bool TableView::AddChild( Actor child, Toolkit::TableView::CellPosition position )
197 // check that the child is valid
198 DALI_ASSERT_ALWAYS( child );
200 // if child is already parented, we adopt it
201 if( child.GetParent() )
203 child.GetParent().Remove( child );
205 // check if we need to expand our data array
206 if( position.rowIndex >= mCellData.GetRows() )
208 // only adding new rows
209 ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
211 if( position.columnIndex >= mCellData.GetColumns() )
213 // only adding new columns
214 ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
216 // check if there already is something in this cell
217 if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
219 return false; // cannot share a cell, it would complicate all logic and not bring much benefit
221 RelayoutingLock lock( *this );
225 // put the actor to the main cell
228 data.position = position;
229 mCellData[ position.rowIndex ][ position.columnIndex ] = data;
230 // if child spans multiple rows of columns
231 bool spanned = false;
232 if( position.rowSpan > 1 )
234 // span might go outside table
235 if( position.rowIndex + position.rowSpan > mCellData.GetRows() )
237 // increase table size for the full span, only increasing rows
238 ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
242 if( position.columnSpan > 1 )
244 // span might go outside table
245 if( position.columnIndex + position.columnSpan > mCellData.GetColumns() )
247 // increase table size for the full span, only increasing columns
248 ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
252 // if it spanned multiple rows, put the cellinfo in all of those
255 for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
257 // store same information to all cells, this way we can identify
258 // if a cell is the prime location of an actor or a spanned one
259 for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
261 // store same information to all cells, this way we can identify
262 // if a cell is the prime location of an actor or a spanned one
263 mCellData[ row ][ column ] = data;
267 // relayout the whole table
269 return true; // addition successful
272 Actor TableView::GetChildAt( Toolkit::TableView::CellPosition position )
274 // check if we have this row and column in the table
275 if( ( position.columnIndex >= mCellData.GetColumns() )||
276 ( position.rowIndex >= mCellData.GetRows() ) )
278 // return an empty handle
281 // return the child handle
282 return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
285 Actor TableView::RemoveChildAt( Toolkit::TableView::CellPosition position )
287 // get the child handle
288 Actor child = GetChildAt( position );
289 // if no real actor there, nothing else to be done
292 RelayoutingLock lock( *this );
293 // Remove the child, this will trigger a call to OnControlChildRemove
294 Self().Remove( child );
296 // relayout the table only if instances were found
297 if( RemoveAllInstances( child ) )
302 // return the child back to caller
306 bool TableView::FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position )
308 // only find valid child actors
311 // walk through the layout data
312 const unsigned int rowCount = mCellData.GetRows();
313 const unsigned int columnCount = mCellData.GetColumns();
314 for( unsigned int row = 0; row < rowCount; ++row )
316 for( unsigned int column = 0; column < columnCount; ++column )
318 if( mCellData[ row ][ column ].actor == child )
320 position = mCellData[ row ][ column ].position;
329 void TableView::InsertRow( unsigned int rowIndex )
331 RelayoutingLock lock( *this );
332 mCellData.InsertRow( rowIndex );
333 // need to update the cellinfos for the items that moved
334 const unsigned int rowCount = mCellData.GetRows();
335 const unsigned int columnCount = mCellData.GetColumns();
336 for( unsigned int row = 0; row < rowCount; ++row )
338 for( unsigned int column = 0; column < columnCount; ++column )
340 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
341 // if cell is spanning and above and spans to inserted row
342 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
343 ( position.rowIndex + position.rowSpan > rowIndex ) )
345 // increase span by one
347 // copy cell to occupy the new column
348 mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
350 // if below of inserted row, increase row index
351 else if( row > rowIndex )
353 // increase index by one
358 mRelativeSizes.InsertRow( rowIndex );
359 // inserting a row requires adjusting the height vectors
360 mFixedHeights.insert( mFixedHeights.begin() + rowIndex, 0 );
361 mRelativeHeights.insert( mRelativeHeights.begin() + rowIndex, 0 );
365 void TableView::DeleteRow( unsigned int rowIndex )
367 vector< Actor > ignored;
368 DeleteRow( rowIndex, ignored );
371 void TableView::DeleteRow( unsigned int rowIndex, vector<Actor>& removed )
373 RelayoutingLock lock( *this );
374 vector< CellData > lost;
375 mCellData.DeleteRow( rowIndex, lost );
376 // need to update the cellinfos for the items that moved
377 const unsigned int rowCount = mCellData.GetRows();
378 const unsigned int columnCount = mCellData.GetColumns();
379 for( unsigned int row = 0; row < rowCount; ++row )
381 for( unsigned int column = 0; column < columnCount; ++column )
383 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
384 // if cell is spanning and above and spans to deleted row
385 if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
386 ( position.rowIndex + position.rowSpan > rowIndex ) )
388 // decrease span by one
389 if( position.rowSpan > 1 )
394 // if below of or at the inserted row, decrease row index
395 else if( row >= rowIndex )
397 // decrease index by one
398 if( position.rowIndex > 1 )
405 // 1 row removed, 0 columns
406 RemoveAndGetLostActors( lost, removed, 1u, 0u );
407 // resize the data structures
408 mRelativeSizes.DeleteRow( rowIndex );
409 // deleting a row requires adjusting the height vectors
410 mFixedHeights.erase( mFixedHeights.begin() + rowIndex );
411 mRelativeHeights.erase( mRelativeHeights.begin() + rowIndex );
415 void TableView::InsertColumn( unsigned int columnIndex )
417 RelayoutingLock lock( *this );
418 mCellData.InsertColumn( columnIndex );
419 // need to update the cellinfos for the items that moved
420 const unsigned int rowCount = mCellData.GetRows();
421 const unsigned int columnCount = mCellData.GetColumns();
422 for( unsigned int row = 0; row < rowCount; ++row )
424 for( unsigned int column = 0; column < columnCount; ++column )
426 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
427 // if cell is spanning and left side and spans to inserted column
428 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
429 ( position.columnIndex + position.columnSpan > columnIndex ) )
431 // increase span by one
432 position.columnSpan++;
433 // copy cell to occupy the new column
434 mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
436 // if on the right side of inserted column, increase column index
437 else if( column > columnIndex )
439 // increase index by one
440 position.columnIndex++;
444 // relative sizes gets recalculated on Relayout
445 mRelativeSizes.InsertColumn( columnIndex );
446 // inserting a column requires adjusting the width vectors
447 mFixedWidths.insert( mFixedWidths.begin() + columnIndex, 0 );
448 mRelativeWidths.insert( mRelativeWidths.begin() + columnIndex, 0 );
452 void TableView::DeleteColumn( unsigned int columnIndex )
454 vector< Actor > ignored;
455 DeleteColumn( columnIndex, ignored );
458 void TableView::DeleteColumn( unsigned int columnIndex, vector<Actor>& removed )
460 RelayoutingLock lock( *this );
461 vector< CellData > lost;
462 mCellData.DeleteColumn( columnIndex, lost );
463 // need to update the cellinfos for the items that moved
464 const unsigned int rowCount = mCellData.GetRows();
465 const unsigned int columnCount = mCellData.GetColumns();
466 for( unsigned int row = 0; row < rowCount; ++row )
468 for( unsigned int column = 0; column < columnCount; ++column )
470 Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
471 // if cell is spanning and left side and spans to inserted column
472 if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
473 ( position.columnIndex + position.columnSpan > columnIndex ) )
475 // decrease span by one
476 if( position.columnSpan > 1 )
478 position.columnSpan--;
481 // if on the right side of or at the inserted column, decrease column index
482 else if( column >= columnIndex )
484 // decrease index by one
485 if( position.columnIndex > 0 )
487 position.columnIndex--;
492 // 0 rows, 1 column removed
493 RemoveAndGetLostActors( lost, removed, 0u, 1u );
494 // resize the data structures
495 mRelativeSizes.DeleteColumn( columnIndex );
496 // deleting a column requires adjusting the width vectors
497 mFixedWidths.erase( mFixedWidths.begin() + columnIndex );
498 mRelativeWidths.erase( mRelativeWidths.begin() + columnIndex );
503 void TableView::Resize( unsigned int rows, unsigned int columns )
505 vector< Actor > ignored;
506 Resize( rows, columns, ignored );
509 void TableView::Resize( unsigned int rows, unsigned int columns, vector<Actor>& removed )
511 RelayoutingLock lock( *this );
512 unsigned int oldRows = GetRows();
513 unsigned int oldColumns = GetColumns();
515 vector< CellData > lost;
516 ResizeContainers( rows, columns, lost );
517 // calculate if we lost rows or columns
518 unsigned int rowsRemoved = 0;
519 unsigned int newRows = GetRows();
520 if( oldRows < newRows )
522 rowsRemoved = newRows - oldRows;
524 unsigned int columnsRemoved = 0;
525 unsigned int newColumns = GetColumns();
526 if( oldColumns < newColumns )
528 rowsRemoved = newColumns - oldColumns;
530 RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
531 // finally relayout once all actors are removed
535 void TableView::SetCellPadding( Size padding )
537 // if padding really changed
538 if( padding != mPadding )
546 Size TableView::GetCellPadding()
551 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
553 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
554 // add the fixed height to the array of fixed heights
555 mFixedHeights[ rowIndex ] = height;
556 // remove the relative height of the same row
557 mRelativeHeights[ rowIndex ] = 0.f;
558 // relayout all cells, no lock needed as nothing added or removed
562 float TableView::GetFixedHeight( unsigned int rowIndex ) const
564 DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
566 return mFixedHeights[ rowIndex ];
569 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
571 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
572 // add the relative height to the array of relative heights
573 mRelativeHeights[ rowIndex ] = heightPercentage;
574 // remove the fixed height of the same row
575 mFixedHeights[ rowIndex ] = 0.f;
576 // relayout all cells, no lock needed as nothing added or removed
580 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
582 DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
584 return mRelativeHeights[ rowIndex ];
587 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
589 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
590 // add the fixed width to the array of fixed column widths
591 mFixedWidths[ columnIndex ] = width;
592 // remove the relative width of the same column
593 mRelativeWidths[ columnIndex ] = 0.f;
594 // relayout all cells, no lock needed as nothing added or removed
598 float TableView::GetFixedWidth( unsigned int columnIndex ) const
600 DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
602 return mFixedWidths[ columnIndex ];
605 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
607 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
608 // add the relative widths to the array of relative widths
609 mRelativeWidths[ columnIndex ] = widthPercentage;
610 // remove the fixed width of the same column
611 mFixedWidths[ columnIndex ] = 0.f;
612 // relayout all cells, no lock needed as nothing added or removed
616 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
618 DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
620 return mRelativeWidths[ columnIndex ];
623 void TableView::SetLayoutAnimationDuration( float duration )
625 mConstraintDuration = duration;
628 float TableView::GetLayoutAnimationDuration()
630 return mConstraintDuration;
633 void TableView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
635 float fixedHeightsTotal = 0.0f;
636 float fixedWidthsTotal = 0.0f;
638 // 1. update the relative sizes and calculate total fixed height and width
639 UpdateRelativeSizes( fixedHeightsTotal, fixedWidthsTotal );
641 // 2. go through the layout data and create constraints
642 float cumulatedFixedHeight = 0.0f;
643 float cumulatedRelativeHeight = 0.0f;
646 const unsigned int rowCount = mCellData.GetRows();
647 const unsigned int columnCount = mCellData.GetColumns();
648 // float versions of the count + 1 to keep precision
649 const float maxRowPlusOne( rowCount + 1 );
650 const float maxColumnPlusOne( columnCount + 1 );
651 for( unsigned int row = 0; row < rowCount; ++row )
653 // reset widths at the start of each row
654 float cumulatedFixedWidth = 0.0f;
655 float cumulatedRelativeWidth = 0.0f;
656 for( unsigned int column = 0; column < columnCount; ++column )
658 // check if this cell has an actor
659 Actor actor = mCellData[ row ][ column ].actor;
660 const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
661 // if there is an actor and this is the main cell of the actor
662 // an actor can be in multiple cells if its row or columnspan is more than 1
663 // we however must only lay out each actor only once
664 if( ( actor )&&( position.rowIndex == row )&&( position.columnIndex == column ) )
666 // anchor actor correctly
667 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
668 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
669 // remove old constraints
670 actor.RemoveConstraints();
673 // get the row and column indices
674 float rowPos( position.rowIndex );
675 float colPos( position.columnIndex );
676 // constrain the actor position to be relative to the width and height of table
677 // minus the padding of course (padding is all around cells)
678 Vector2 relativePosition( cumulatedRelativeWidth, cumulatedRelativeHeight );
679 // fixed height rows and fixed width cells are considered as padding so
680 // they are removed from the total size for relative
681 // for position only consider cumulated fixed rows and columns from top and left
682 Vector2 positionPadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
683 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
684 Vector2 fixedPosition( ( colPos + 1.0f ) * mPadding.width + cumulatedFixedWidth,
685 ( rowPos + 1.0f ) * mPadding.height + cumulatedFixedHeight );
687 Constraint widthConstraint = Constraint::New<float>( Actor::POSITION_X,
688 ParentSource( Actor::SIZE_WIDTH ),
689 RelativeToWidthOrHeight( relativePosition.x, positionPadding.x, fixedPosition.x ) );
691 Constraint heightConstraint = Constraint::New<float>( Actor::POSITION_Y,
692 ParentSource( Actor::SIZE_HEIGHT ),
693 RelativeToWidthOrHeight( relativePosition.y, positionPadding.y, fixedPosition.y ) );
695 widthConstraint.SetApplyTime( mConstraintDuration );
696 heightConstraint.SetApplyTime( mConstraintDuration );
698 // bake constrained position value if constraint is removed
699 widthConstraint.SetRemoveAction( Constraint::Bake );
700 heightConstraint.SetRemoveAction( Constraint::Bake );
702 actor.ApplyConstraint( widthConstraint );
703 actor.ApplyConstraint( heightConstraint );
706 // constrain the actor size to be relative to the size of table
707 // get the relative size for this cell
708 Vector2 relativeSize( mRelativeSizes[ row ][ column ] );
709 Vector2 fixedSize( mFixedWidths[ column ], mFixedHeights[ row ] );
710 // if we span multiple cells, need to sum them all up, both fixed and relative parts
711 if( position.rowSpan > 1 )
713 for( unsigned int i = 1; i < position.rowSpan; ++i )
715 // accumulate the height only
716 relativeSize.height += mRelativeSizes[ row + i ][ column ].height;
717 fixedSize.height += mFixedHeights[ row + i ];
720 if( position.columnSpan > 1 )
722 for( unsigned int i = 1; i < position.columnSpan; ++i )
724 // accumulate the width only
725 relativeSize.width += mRelativeSizes[ row ][ column + i ].width;
726 fixedSize.width += mFixedWidths[ column + i ];
729 // minus the padding from size (padding is all around cells)
730 // if item spans multiple columns or rows then less padding is added (default span is 1)
731 // fixed height rows and fixed width cells are considered as padding so they are removed
732 // from the total available size for relative cells
733 Vector2 sizePadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
734 maxRowPlusOne * mPadding.height + fixedHeightsTotal );
735 // and added to the fixed size multiplied by the span of rows and columns
736 fixedSize.width += ( position.columnSpan - 1.0f ) * mPadding.width;
737 fixedSize.height += ( position.rowSpan - 1.0f ) * mPadding.height;
739 RelativeToWidthOrHeight relativeWidthFunctor( relativeSize.x, sizePadding.x, fixedSize.x );
740 RelativeToWidthOrHeight relativeHeightFunctor( relativeSize.y, sizePadding.y, fixedSize.y );
742 widthConstraint = Constraint::New<float>( Actor::SIZE_WIDTH,
743 ParentSource( Actor::SIZE_WIDTH ),
744 relativeWidthFunctor );
746 heightConstraint = Constraint::New<float>( Actor::SIZE_HEIGHT,
747 ParentSource( Actor::SIZE_HEIGHT ),
748 relativeHeightFunctor );
750 widthConstraint.SetApplyTime( mConstraintDuration );
751 heightConstraint.SetApplyTime( mConstraintDuration );
753 // bake constrained size value if constraint is removed
754 widthConstraint.SetRemoveAction( Constraint::Bake );
755 heightConstraint.SetRemoveAction( Constraint::Bake );
757 actor.ApplyConstraint( widthConstraint );
758 actor.ApplyConstraint( heightConstraint );
761 Relayout ( actor, Vector2( relativeWidthFunctor( size.width ), relativeHeightFunctor( size.height ) ), container );
763 // for position we need to keep track of current fixed width and relative width
764 // increase for next column
765 cumulatedFixedWidth += mFixedWidths[ column ];
766 cumulatedRelativeWidth += mRelativeSizes[ row ][ column ].width;
768 // for position we need to keep track of current fixed height and relative height
769 // increase for next row
770 cumulatedFixedHeight += mFixedHeights[ row ];
771 cumulatedRelativeHeight += mRelativeSizes[ row ][ 0 ].height; // all columns share same height
775 unsigned int TableView::GetRows()
777 return mCellData.GetRows();
780 unsigned int TableView::GetColumns()
782 return mCellData.GetColumns();
785 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
787 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
791 TableView& tableViewImpl( GetImpl( tableView ) );
794 case Toolkit::TableView::PROPERTY_ROWS:
796 if( value.Get<unsigned int>() != tableViewImpl.GetRows() )
798 tableViewImpl.Resize( value.Get<unsigned int>(), tableViewImpl.GetColumns() );
802 case Toolkit::TableView::PROPERTY_COLUMNS:
804 if( value.Get<unsigned int>() != tableViewImpl.GetColumns() )
806 tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<unsigned int>() );
810 case Toolkit::TableView::PROPERTY_CELL_PADDING:
812 tableViewImpl.SetCellPadding( value.Get<Vector2>() );
815 case Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION:
817 tableViewImpl.SetLayoutAnimationDuration( value.Get<float>() );
820 case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
822 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, value );
825 case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
827 SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, value );
834 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
836 Property::Value value;
838 Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
842 TableView& tableViewImpl( GetImpl( tableView ) );
845 case Toolkit::TableView::PROPERTY_ROWS:
847 value = tableViewImpl.GetRows();
850 case Toolkit::TableView::PROPERTY_COLUMNS:
852 value = tableViewImpl.GetColumns();
855 case Toolkit::TableView::PROPERTY_CELL_PADDING:
857 value = tableViewImpl.GetCellPadding();
860 case Toolkit::TableView::PROPERTY_LAYOUT_ANIMATION_DURATION:
862 value = tableViewImpl.GetLayoutAnimationDuration();
865 case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
867 value = tableViewImpl.GetRowHeightsPropertyValue();
870 case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
872 value = tableViewImpl.GetColumnWidthsPropertyValue();
881 void TableView::OnControlChildAdd( Actor& child )
883 if( mLayoutingChild )
885 // we're in the middle of laying out children so no point doing anything here
889 Toolkit::TableView::CellPosition cellPosition;
890 if( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
892 cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
894 if( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
896 cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
898 if( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) != Property::INVALID_INDEX )
900 Vector2 indices = child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) ).Get<Vector2 >();
901 cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
902 cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
904 AddChild( child, cellPosition );
909 // check if we're already laying out this child somewhere on the table
910 // walk through the layout data
911 const unsigned int rowCount = mCellData.GetRows();
912 const unsigned int columnCount = mCellData.GetColumns();
913 // child not yet laid out, find the first free slot
914 for( unsigned int row = 0; row < rowCount; ++row )
916 for( unsigned int column = 0; column < columnCount; ++column )
918 // no actor means free cell
919 if( !(mCellData[ row ][ column ].actor) )
921 // put the actor in the cell
924 data.position.columnIndex = column;
925 data.position.rowIndex = row;
926 mCellData[ row ][ column ] = data;
933 // still here, no room for the poor child so increase the array. Need a new row
934 ResizeContainers( rowCount + 1, columnCount );
935 // put the actor to the first cell of the new row
938 data.position.rowIndex = rowCount;
939 data.position.columnIndex = 0;
940 mCellData[ rowCount ][ 0 ] = data;
941 // finally relayout the table
945 void TableView::OnControlChildRemove( Actor& child )
947 // dont process if we're in the middle of bigger operation like delete row, column or resize
948 if( !mLayoutingChild )
950 // relayout the table only if instances were found
951 if( RemoveAllInstances( child ) )
958 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
959 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
960 mCellData( initialRows, initialColumns ),
961 mLayoutingChild( false ),
962 mConstraintDuration( DEFAULT_CONSTRAINT_DURATION )
964 SetKeyboardNavigationSupport( true );
965 ResizeContainers( initialRows, initialColumns );
968 void TableView::OnInitialize()
970 // Make self as keyboard focusable and focus group
972 self.SetKeyboardFocusable(true);
973 SetAsKeyboardFocusGroup(true);
976 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
978 vector<CellData> ignored;
979 ResizeContainers( rows, columns, ignored );
982 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, vector<CellData>& removed )
984 mCellData.Resize( rows, columns, removed );
985 // we dont care if these go smaller, data will be regenerated or is not needed anymore
986 mRelativeSizes.Resize( rows, columns );
987 mFixedHeights.resize( rows );
988 mRelativeHeights.resize( rows );
989 mFixedWidths.resize( columns );
990 mRelativeWidths.resize( columns );
993 void TableView::RemoveAndGetLostActors( const vector<CellData>& lost, vector<Actor>& removed,
994 unsigned int rowsRemoved, unsigned int columnsRemoved )
996 // iterate through all lost cells
997 vector< CellData >::const_iterator iter = lost.begin();
998 for( ; iter != lost.end(); ++iter )
1000 // if it is a valid actor
1003 // is this actor still somewhere else in the table
1004 Toolkit::TableView::CellPosition position;
1005 if( FindChildPosition( (*iter).actor, position ) )
1007 // it must be spanning multiple cells, position contains the top left most one
1008 // check if position is left of the removed location
1009 if( position.columnIndex < (*iter).position.columnIndex )
1011 // if column span is greater than 1
1012 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1014 // decrease column span
1015 mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1018 // check if position is left of the removed location
1019 if( position.rowIndex < (*iter).position.rowIndex )
1021 // if row span is greater than 1
1022 if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1024 // decrease row span
1025 mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1031 // this actor is gone for good
1032 // add actor to removed container
1033 removed.push_back( (*iter).actor );
1034 // we dont want the child actor anymore
1035 Self().Remove( (*iter).actor );
1041 bool TableView::RemoveAllInstances( Actor child )
1044 // walk through the layout data
1045 const unsigned int rowCount = mCellData.GetRows();
1046 const unsigned int columnCount = mCellData.GetColumns();
1047 for( unsigned int row = 0; row < rowCount; ++row )
1049 for( unsigned int column = 0; column < columnCount; ++column )
1051 if( mCellData[ row ][ column ].actor == child )
1053 // clear the cell, NOTE that the cell might be spanning multiple cells
1054 mCellData[ row ][ column ] = CellData();
1062 void TableView::UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal )
1064 // 1. check all the fixed heights and widths to know how much size they take in total
1065 // as well as the relative heights and widths to know how much is left for the 'fill' cells
1066 unsigned int fixedRowCount = 0;
1067 unsigned int relativeRowCount = 0;
1068 float relativeHeightsTotal = 0.0f;
1069 const unsigned int rowCount = mCellData.GetRows();
1070 for( unsigned int row = 0; row < rowCount; ++row )
1072 if( mFixedHeights[ row ] > 0.0f )
1075 fixedHeightsTotal += mFixedHeights[ row ];
1077 if( mRelativeHeights[ row ] > 0.0f )
1080 relativeHeightsTotal += mRelativeHeights[ row ];
1083 unsigned int fixedColumnCount = 0;
1084 unsigned int relativeColumnCount = 0;
1085 const unsigned int columnCount = mCellData.GetColumns();
1086 float relativeWidthsTotal = 0.0f;
1087 for( unsigned int column = 0; column < columnCount; ++column )
1089 if( mFixedWidths[ column ] > 0.0f )
1092 fixedWidthsTotal += mFixedWidths[ column ];
1094 if( mRelativeWidths[ column ] > 0.0f )
1096 ++relativeColumnCount;
1097 relativeWidthsTotal += mRelativeWidths[ column ];
1101 // 2. cap the relative width and height totals to 100%
1102 if( relativeHeightsTotal > 1.0f )
1104 relativeHeightsTotal = 1.0f;
1106 if( relativeWidthsTotal > 1.0f )
1108 relativeWidthsTotal = 1.0f;
1111 // 3. create a table of relative sizes so we can lookup for cells that span multiple rows & colums
1112 const float fillRowCount( rowCount - relativeRowCount - fixedRowCount );
1113 const float fillColumnCount( columnCount - relativeColumnCount - fixedColumnCount );
1115 // walk through the data containers
1116 for( unsigned int row = 0; row < rowCount; ++row )
1118 float relativeHeight = 0.0f;
1119 // if we have a fixed height, relative height is 0
1120 if( mFixedHeights[ row ] > 0.0f )
1122 relativeHeight = 0.0f;
1124 // else if we're given a specific row height %, use that
1125 else if( mRelativeHeights[ row ] > 0.0f )
1127 relativeHeight = mRelativeHeights[ row ];
1129 // else if there are fill rows
1130 else if( fillRowCount > 0 )
1132 // this is a 'fill' row. it gets the remainder of the 100% divided evenly between 'fill' rows
1133 relativeHeight = (1.0f - relativeHeightsTotal ) / fillRowCount;
1135 for( unsigned int column = 0; column < columnCount; ++column )
1137 float relativeWidth = 0.0f;
1138 // if we have a fixed width, relative width is 0
1139 if( mFixedWidths[ column ] > 0.0f )
1141 relativeWidth = 0.0f;
1143 // else if we're given a specific column width %, use that
1144 else if( mRelativeWidths[ column ] > 0.0f )
1146 relativeWidth = mRelativeWidths[ column ];
1148 // else if there are fill columns
1149 else if( fillColumnCount > 0 )
1151 // this is a 'fill' column. it gets the remainder of the 100% divided evenly between 'fill' columns
1152 relativeWidth = (1.0f - relativeWidthsTotal ) / fillColumnCount;
1155 mRelativeSizes[ row ][ column ] = Size( relativeWidth, relativeHeight );
1160 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1161 void(TableView::*funcFixed)(unsigned int, float),
1162 void(TableView::*funcRelative)(unsigned int, float),
1163 const Property::Value& value )
1165 if( Property::MAP == value.GetType() )
1167 Property::Map map = value.Get<Property::Map>();
1168 unsigned int rowIndex(0);
1169 for ( unsigned int i = 0, count = map.Count(); i < count; ++i )
1171 Property::Value& item = map.GetValue(i);
1173 if( istringstream(map.GetKey(i)) >> rowIndex // the key is a number
1174 && Property::MAP == item.GetType())
1176 if( item.HasKey( "policy" ) && item.HasKey( "value" ) )
1178 Toolkit::TableView::LayoutPolicy policy = Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( item.GetValue("policy").Get<string>(), LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT );
1179 if( policy == Toolkit::TableView::Fixed )
1181 (tableViewImpl.*funcFixed)( rowIndex, item.GetValue("value").Get<float>() );
1183 else if( policy == Toolkit::TableView::Relative )
1185 (tableViewImpl.*funcRelative)( rowIndex, item.GetValue("value").Get<float>() );
1193 Property::Value TableView::GetRowHeightsPropertyValue()
1196 GetMapPropertyValue( mFixedHeights, mRelativeHeights, map);
1197 return Property::Value(map);
1200 Property::Value TableView::GetColumnWidthsPropertyValue()
1203 GetMapPropertyValue( mFixedWidths, mRelativeWidths, map);
1204 return Property::Value(map);
1207 void TableView::GetMapPropertyValue( const std::vector<float>& fixedSize, const std::vector<float>& relativeSize, Property::Map& map )
1209 string fixedPolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Fixed, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1210 string relativePolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Relative, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1211 Property::StringValuePair fixedPolicyPair( "policy", fixedPolicy );
1212 Property::StringValuePair relativePolicyPair( "policy", relativePolicy );
1214 size_t count = fixedSize.size();
1215 for( size_t index = 0; index < count; index++ )
1217 if( ! EqualsZero( fixedSize[index] ) )
1219 Property::StringValuePair valuePair( "value", fixedSize[index] );
1221 item[ fixedPolicyPair.first ] = fixedPolicyPair.second;
1222 item[ valuePair.first ] = valuePair.second;
1224 map[ static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str() ] = item;
1226 else if( ! EqualsZero( relativeSize[index] ) )
1228 Property::StringValuePair valuePair( "value", relativeSize[index] );
1230 item[ relativePolicyPair.first ] = relativePolicyPair.second;
1231 item[ valuePair.first ] = valuePair.second;
1233 map[ static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str() ] = item;
1238 TableView::~TableView()
1243 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1245 Actor nextFocusableActor;
1247 if ( !currentFocusedActor )
1249 // Nothing is currently focused, so the child in the first cell should be focused.
1250 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1254 Toolkit::TableView::CellPosition position;
1255 if( FindChildPosition( currentFocusedActor, position ) )
1257 // The current focused actor is a child of TableView
1258 bool focusLost = false;
1259 int currentRow = position.rowIndex;
1260 int currentColumn = position.columnIndex;
1261 int numberOfColumns = GetColumns();
1262 int numberOfRows = GetRows();
1264 switch ( direction )
1266 case Toolkit::Control::Left:
1268 if(--currentColumn < 0)
1270 currentColumn = numberOfColumns - 1;
1271 if(--currentRow < 0)
1273 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1274 focusLost = (currentRow == 0);
1279 case Toolkit::Control::Right:
1281 if(++currentColumn > numberOfColumns - 1)
1284 if(++currentRow > numberOfRows - 1)
1286 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1287 focusLost = (currentRow == numberOfRows - 1);
1292 case Toolkit::Control::Up:
1294 if(--currentRow < 0)
1296 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1297 focusLost = (currentRow == 0);
1301 case Toolkit::Control::Down:
1303 if(++currentRow > numberOfRows - 1)
1305 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1306 focusLost = (currentRow == numberOfRows - 1);
1312 // Move the focus if we haven't lost it.
1315 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1320 // The current focused actor is not within table view, so the child in the first cell should be focused.
1321 nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1325 return nextFocusableActor;
1328 } // namespace Internal
1330 } // namespace Toolkit