+ // Do not do again the only once operations.
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
+
+ // Do the size related operations again.
+ const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
+ ALIGN |
+ REORDER );
+
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
+
+ // Clear the update info. This info will be set the next time the text is updated.
+ mImpl->mTextUpdateInfo.Clear();
+
+ // Restore the actual control's width.
+ mImpl->mVisualModel->mControlSize.width = actualControlWidth;
+
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
+ }
+ else
+ {
+ layoutSize = mImpl->mVisualModel->GetLayoutSize();
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
+ }
+
+ return layoutSize.height;
+}
+
+// public : Relayout.
+
+Controller::UpdateTextType Controller::Relayout( const Size& size )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, mImpl->mIsAutoScrollEnabled ?"true":"false" );
+
+ UpdateTextType updateTextType = NONE_UPDATED;
+
+ if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
+ {
+ if( 0u != mImpl->mVisualModel->mGlyphPositions.Count() )
+ {
+ mImpl->mVisualModel->mGlyphPositions.Clear();
+ updateTextType = MODEL_UPDATED;
+ }
+
+ // Clear the update info. This info will be set the next time the text is updated.
+ mImpl->mTextUpdateInfo.Clear();
+
+ // Not worth to relayout if width or height is equal to zero.
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
+
+ return updateTextType;
+ }
+
+ // Whether a new size has been set.
+ const bool newSize = ( size != mImpl->mVisualModel->mControlSize );
+
+ if( newSize )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mVisualModel->mControlSize.width, mImpl->mVisualModel->mControlSize.height );
+
+ // Layout operations that need to be done if the size changes.
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
+ LAYOUT |
+ ALIGN |
+ UPDATE_LAYOUT_SIZE |
+ REORDER );
+ // Set the update info to relayout the whole text.
+ mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
+ mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
+ }
+
+ // Whether there are modify events.
+ if( 0u != mImpl->mModifyEvents.Count() )
+ {
+ // Style operations that need to be done if the text is modified.
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
+ COLOR );
+ }
+
+ // Make sure the model is up-to-date before layouting.
+ ProcessModifyEvents();
+ bool updated = mImpl->UpdateModel( mImpl->mOperationsPending );
+
+ // Layout the text.
+ Size layoutSize;
+ updated = DoRelayout( size,
+ mImpl->mOperationsPending,
+ layoutSize ) || updated;
+
+ if( updated )
+ {
+ updateTextType = MODEL_UPDATED;
+ }
+
+ // Do not re-do any operation until something changes.
+ mImpl->mOperationsPending = NO_OPERATION;
+
+ // Whether the text control is editable
+ const bool isEditable = NULL != mImpl->mEventData;
+
+ // Keep the current offset as it will be used to update the decorator's positions (if the size changes).
+ Vector2 offset;
+ if( newSize && isEditable )
+ {
+ offset = mImpl->mScrollPosition;
+ }
+
+ if( !isEditable || !IsMultiLineEnabled() )
+ {
+ // After doing the text layout, the vertical offset to place the actor in the desired position can be calculated.
+ CalculateVerticalOffset( size );
+ }
+
+ if( isEditable )
+ {
+ if( newSize )
+ {
+ // If there is a new size, the scroll position needs to be clamped.
+ mImpl->ClampHorizontalScroll( layoutSize );
+
+ // Update the decorator's positions is needed if there is a new size.
+ mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mScrollPosition - offset );
+ }
+
+ // Move the cursor, grab handle etc.
+ if( mImpl->ProcessInputEvents() )
+ {
+ updateTextType = static_cast<UpdateTextType>( updateTextType | DECORATOR_UPDATED );
+ }
+ }
+
+ // Clear the update info. This info will be set the next time the text is updated.
+ mImpl->mTextUpdateInfo.Clear();
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
+
+ return updateTextType;
+}
+
+void Controller::RequestRelayout()
+{
+ mImpl->RequestRelayout();
+}
+
+// public : Input style change signals.
+
+bool Controller::IsInputStyleChangedSignalsQueueEmpty()
+{
+ return ( NULL == mImpl->mEventData ) || ( 0u == mImpl->mEventData->mInputStyleChangedQueue.Count() );
+}
+
+void Controller::ProcessInputStyleChangedSignals()
+{
+ if( NULL == mImpl->mEventData )
+ {
+ // Nothing to do.
+ return;
+ }
+
+ for( Vector<InputStyle::Mask>::ConstIterator it = mImpl->mEventData->mInputStyleChangedQueue.Begin(),
+ endIt = mImpl->mEventData->mInputStyleChangedQueue.End();
+ it != endIt;
+ ++it )
+ {
+ const InputStyle::Mask mask = *it;
+
+ if( NULL != mImpl->mEditableControlInterface )
+ {
+ // Emit the input style changed signal.
+ mImpl->mEditableControlInterface->InputStyleChanged( mask );
+ }
+ }
+
+ mImpl->mEventData->mInputStyleChangedQueue.Clear();
+}
+
+// public : Text-input Event Queuing.
+
+void Controller::KeyboardFocusGainEvent()
+{
+ DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
+
+ if( NULL != mImpl->mEventData )
+ {
+ if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
+ ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
+ {
+ mImpl->ChangeState( EventData::EDITING );
+ mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
+ }
+ mImpl->NotifyImfMultiLineStatus();
+ if( mImpl->IsShowingPlaceholderText() )
+ {
+ // Show alternative placeholder-text when editing
+ ShowPlaceholderText();
+ }