[3.0] Fix for Text::Controller::RepositionSelectionHandles()
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
index 95115da..fafd57a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
 #include <dali-toolkit/internal/text/multi-language-support.h>
 #include <dali-toolkit/internal/text/segmentation.h>
 #include <dali-toolkit/internal/text/shaper.h>
+#include <dali-toolkit/internal/text/text-control-interface.h>
 #include <dali-toolkit/internal/text/text-run-container.h>
 
 namespace
@@ -73,6 +74,8 @@ EventData::EventData( DecoratorPtr decorator )
   mPlaceholderTextInactive(),
   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
   mEventQueue(),
+  mInputStyleChangedQueue(),
+  mPreviousState( INACTIVE ),
   mState( INACTIVE ),
   mPrimaryCursorPosition( 0u ),
   mLeftSelectionPosition( 0u ),
@@ -80,6 +83,8 @@ EventData::EventData( DecoratorPtr decorator )
   mPreEditStartPosition( 0u ),
   mPreEditLength( 0u ),
   mCursorHookPositionX( 0.f ),
+  mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
+  mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
   mIsShowingPlaceholderText( false ),
   mPreEditFlag( false ),
   mDecoratorUpdated( false ),
@@ -87,11 +92,13 @@ EventData::EventData( DecoratorPtr decorator )
   mGrabHandleEnabled( true ),
   mGrabHandlePopupEnabled( true ),
   mSelectionEnabled( true ),
+  mUpdateCursorHookPosition( false ),
   mUpdateCursorPosition( false ),
   mUpdateGrabHandlePosition( false ),
   mUpdateLeftSelectionPosition( false ),
   mUpdateRightSelectionPosition( false ),
   mIsLeftHandleSelected( false ),
+  mIsRightHandleSelected( false ),
   mUpdateHighlightBox( false ),
   mScrollAfterUpdatePosition( false ),
   mScrollAfterDelete( false ),
@@ -221,10 +228,25 @@ bool Controller::Impl::ProcessInputEvents()
 
       if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
       {
-        CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
+        if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
+        {
+          CursorInfo& infoLeft = leftHandleInfo;
+
+          const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
+          ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
+
+          CursorInfo& infoRight = rightHandleInfo;
+
+          const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
+          ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
+        }
+        else
+        {
+          CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
 
-        const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
-        ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
+          const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
+          ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
+        }
       }
     }
 
@@ -255,6 +277,8 @@ bool Controller::Impl::ProcessInputEvents()
       mEventData->mUpdateLeftSelectionPosition = false;
       mEventData->mUpdateRightSelectionPosition = false;
       mEventData->mUpdateHighlightBox = false;
+      mEventData->mIsLeftHandleSelected = false;
+      mEventData->mIsRightHandleSelected = false;
     }
 
     mEventData->mScrollAfterUpdatePosition = false;
@@ -262,6 +286,10 @@ bool Controller::Impl::ProcessInputEvents()
 
   if( mEventData->mUpdateInputStyle )
   {
+    // Keep a copy of the current input style.
+    InputStyle currentInputStyle;
+    currentInputStyle.Copy( mEventData->mInputStyle );
+
     // Set the default style first.
     RetrieveDefaultInputStyle( mEventData->mInputStyle );
 
@@ -271,6 +299,16 @@ bool Controller::Impl::ProcessInputEvents()
     // Retrieve the style from the style runs stored in the logical model.
     mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
 
+    // Compare if the input style has changed.
+    const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
+
+    if( hasInputStyleChanged )
+    {
+      const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
+      // Queue the input style changed signal.
+      mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
+    }
+
     mEventData->mUpdateInputStyle = false;
   }
 
@@ -307,6 +345,15 @@ void Controller::Impl::NotifyImfManager()
   }
 }
 
+void Controller::Impl::NotifyImfMultiLineStatus()
+{
+  if ( mEventData )
+  {
+    LayoutEngine::Layout layout = mLayoutEngine.GetLayout();
+    mEventData->mImfManager.NotifyTextInputMultiLine( layout == LayoutEngine::MULTI_LINE_BOX );
+  }
+}
+
 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
 {
   CharacterIndex cursorPosition = 0u;
@@ -824,15 +871,22 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
       // Validate the fonts set through the mark-up string.
       Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
 
-      // Get the default font id.
-      const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
+      // Get the default font's description.
+      TextAbstraction::FontDescription defaultFontDescription;
+      TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
+      if( NULL != mFontDefaults )
+      {
+        defaultFontDescription = mFontDefaults->mFontDescription;
+        defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
+      }
 
       // Validates the fonts. If there is a character with no assigned font it sets a default one.
       // After this call, fonts are validated.
       multilanguageSupport.ValidateFonts( utf32Characters,
                                           scripts,
                                           fontDescriptionRuns,
-                                          defaultFontId,
+                                          defaultFontDescription,
+                                          defaultPointSize,
                                           startIndex,
                                           requestedNumberOfCharacters,
                                           validFonts );
@@ -990,11 +1044,25 @@ void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
   inputStyle.size = 0.f;
 
-  inputStyle.familyDefined = false;
-  inputStyle.weightDefined = false;
-  inputStyle.widthDefined = false;
-  inputStyle.slantDefined = false;
-  inputStyle.sizeDefined = false;
+  inputStyle.lineSpacing = 0.f;
+
+  inputStyle.underlineProperties.clear();
+  inputStyle.shadowProperties.clear();
+  inputStyle.embossProperties.clear();
+  inputStyle.outlineProperties.clear();
+
+  inputStyle.isFamilyDefined = false;
+  inputStyle.isWeightDefined = false;
+  inputStyle.isWidthDefined = false;
+  inputStyle.isSlantDefined = false;
+  inputStyle.isSizeDefined = false;
+
+  inputStyle.isLineSpacingDefined = false;
+
+  inputStyle.isUnderlineDefined = false;
+  inputStyle.isShadowDefined = false;
+  inputStyle.isEmbossDefined = false;
+  inputStyle.isOutlineDefined = false;
 
   // Sets the default font's family name, weight, width, slant and size.
   if( mFontDefaults )
@@ -1002,31 +1070,31 @@ void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
     if( mFontDefaults->familyDefined )
     {
       inputStyle.familyName = mFontDefaults->mFontDescription.family;
-      inputStyle.familyDefined = true;
+      inputStyle.isFamilyDefined = true;
     }
 
     if( mFontDefaults->weightDefined )
     {
       inputStyle.weight = mFontDefaults->mFontDescription.weight;
-      inputStyle.weightDefined = true;
+      inputStyle.isWeightDefined = true;
     }
 
     if( mFontDefaults->widthDefined )
     {
       inputStyle.width = mFontDefaults->mFontDescription.width;
-      inputStyle.widthDefined = true;
+      inputStyle.isWidthDefined = true;
     }
 
     if( mFontDefaults->slantDefined )
     {
       inputStyle.slant = mFontDefaults->mFontDescription.slant;
-      inputStyle.slantDefined = true;
+      inputStyle.isSlantDefined = true;
     }
 
     if( mFontDefaults->sizeDefined )
     {
       inputStyle.size = mFontDefaults->mDefaultPointSize;
-      inputStyle.sizeDefined = true;
+      inputStyle.isSizeDefined = true;
     }
   }
 }
@@ -1100,14 +1168,17 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event )
       const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
 
       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
+      bool matchedCharacter = false;
       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
                                                                         mLogicalModel,
                                                                         mMetrics,
                                                                         mEventData->mCursorHookPositionX,
-                                                                        hitPointY );
+                                                                        hitPointY,
+                                                                        CharacterHitTest::TAP,
+                                                                        matchedCharacter );
     }
   }
-  else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
+  else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
   {
     // Get first the line index of the current cursor position index.
     CharacterIndex characterIndex = 0u;
@@ -1133,11 +1204,14 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event )
       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
 
       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
+      bool matchedCharacter = false;
       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
                                                                         mLogicalModel,
                                                                         mMetrics,
                                                                         mEventData->mCursorHookPositionX,
-                                                                        hitPointY );
+                                                                        hitPointY,
+                                                                        CharacterHitTest::TAP,
+                                                                        matchedCharacter );
     }
   }
 
@@ -1163,11 +1237,15 @@ void Controller::Impl::OnTapEvent( const Event& event )
         // Keep the tap 'x' position. Used to move the cursor.
         mEventData->mCursorHookPositionX = xPosition;
 
+        // Whether to touch point hits on a glyph.
+        bool matchedCharacter = false;
         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
                                                                           mLogicalModel,
                                                                           mMetrics,
                                                                           xPosition,
-                                                                          yPosition );
+                                                                          yPosition,
+                                                                          CharacterHitTest::TAP,
+                                                                          matchedCharacter );
 
         // When the cursor position is changing, delay cursor blinking
         mEventData->mDecorator->DelayCursorBlink();
@@ -1189,6 +1267,20 @@ void Controller::Impl::OnTapEvent( const Event& event )
         mEventData->mImfManager.NotifyCursorPosition();
       }
     }
+    else if( 2u == tapCount )
+    {
+      if( mEventData->mSelectionEnabled )
+      {
+        // Convert from control's coords to text's coords.
+        const float xPosition = event.p2.mFloat - mScrollPosition.x;
+        const float yPosition = event.p3.mFloat - mScrollPosition.y;
+
+        // Calculates the logical position from the x,y coords.
+        RepositionSelectionHandles( xPosition,
+                                    yPosition,
+                                    mEventData->mDoubleTapAction );
+      }
+    }
   }
 }
 
@@ -1259,10 +1351,25 @@ void Controller::Impl::OnLongPressEvent( const Event& event )
 {
   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
 
-  if( EventData::EDITING == mEventData->mState )
+  if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
   {
-    ChangeState ( EventData::EDITING_WITH_POPUP );
+    ChangeState( EventData::EDITING_WITH_POPUP );
     mEventData->mDecoratorUpdated = true;
+    mEventData->mUpdateInputStyle = true;
+  }
+  else
+  {
+    if( mEventData->mSelectionEnabled )
+    {
+      // Convert from control's coords to text's coords.
+      const float xPosition = event.p2.mFloat - mScrollPosition.x;
+      const float yPosition = event.p3.mFloat - mScrollPosition.y;
+
+      // Calculates the logical position from the x,y coords.
+      RepositionSelectionHandles( xPosition,
+                                  yPosition,
+                                  mEventData->mLongPressAction );
+    }
   }
 }
 
@@ -1285,11 +1392,14 @@ void Controller::Impl::OnHandleEvent( const Event& event )
     const float yPosition = event.p3.mFloat - mScrollPosition.y;
 
     // Need to calculate the handle's new position.
+    bool matchedCharacter = false;
     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
                                                                           mLogicalModel,
                                                                           mMetrics,
                                                                           xPosition,
-                                                                          yPosition );
+                                                                          yPosition,
+                                                                          CharacterHitTest::SCROLL,
+                                                                          matchedCharacter );
 
     if( Event::GRAB_HANDLE_EVENT == event.type )
     {
@@ -1326,6 +1436,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
       // Will define the order to scroll the text to match the handle position.
       mEventData->mIsLeftHandleSelected = true;
+      mEventData->mIsRightHandleSelected = false;
     }
     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
     {
@@ -1346,6 +1457,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
       // Will define the order to scroll the text to match the handle position.
       mEventData->mIsLeftHandleSelected = false;
+      mEventData->mIsRightHandleSelected = true;
     }
   } // end ( HANDLE_PRESSED == state )
   else if( ( HANDLE_RELEASED == state ) ||
@@ -1358,11 +1470,14 @@ void Controller::Impl::OnHandleEvent( const Event& event )
       const float xPosition = event.p2.mFloat - mScrollPosition.x;
       const float yPosition = event.p3.mFloat - mScrollPosition.y;
 
+      bool matchedCharacter = false;
       handlePosition = Text::GetClosestCursorIndex( mVisualModel,
                                                     mLogicalModel,
                                                     mMetrics,
                                                     xPosition,
-                                                    yPosition );
+                                                    yPosition,
+                                                    CharacterHitTest::SCROLL,
+                                                    matchedCharacter );
     }
 
     if( Event::GRAB_HANDLE_EVENT == event.type )
@@ -1474,11 +1589,14 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
       // Get the new handle position.
       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
+      bool matchedCharacter = false;
       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
                                                                          mLogicalModel,
                                                                          mMetrics,
                                                                          position.x - mScrollPosition.x,
-                                                                         position.y - mScrollPosition.y );
+                                                                         position.y - mScrollPosition.y,
+                                                                         CharacterHitTest::SCROLL,
+                                                                         matchedCharacter );
 
       if( mEventData->mPrimaryCursorPosition != handlePosition )
       {
@@ -1515,11 +1633,14 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
       // Get the new handle position.
       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
+      bool matchedCharacter = false;
       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
                                                                          mLogicalModel,
                                                                          mMetrics,
                                                                          position.x - mScrollPosition.x,
-                                                                         position.y - mScrollPosition.y );
+                                                                         position.y - mScrollPosition.y,
+                                                                         CharacterHitTest::SCROLL,
+                                                                         matchedCharacter );
 
       if( leftSelectionHandleEvent )
       {
@@ -1572,7 +1693,8 @@ void Controller::Impl::OnSelectEvent( const Event& event )
 
     // Calculates the logical position from the x,y coords.
     RepositionSelectionHandles( xPosition,
-                                yPosition );
+                                yPosition,
+                                Controller::NoTextTap::HIGHLIGHT );
   }
 }
 
@@ -1626,14 +1748,38 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete
 
     if( deleteAfterRetrieval ) // Only delete text if copied successfully
     {
+      // Keep a copy of the current input style.
+      InputStyle currentInputStyle;
+      currentInputStyle.Copy( mEventData->mInputStyle );
+
       // Set as input style the style of the first deleted character.
       mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
 
+      // Compare if the input style has changed.
+      const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
+
+      if( hasInputStyleChanged )
+      {
+        const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
+        // Queue the input style changed signal.
+        mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
+      }
+
       mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
 
       // Mark the paragraphs to be updated.
-      mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
-      mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
+      if( LayoutEngine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
+      {
+        mTextUpdateInfo.mCharacterIndex = 0;
+        mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+        mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
+        mTextUpdateInfo.mClearAll = true;
+      }
+      else
+      {
+        mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
+        mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
+      }
 
       // Delete text between handles
       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
@@ -1688,11 +1834,11 @@ void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
   ChangeState( EventData::EDITING );
 }
 
-void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
+void Controller::Impl::RequestGetTextFromClipboard()
 {
   if ( mClipboard )
   {
-    retrievedString =  mClipboard.GetItem( itemIndex );
+    mClipboard.RequestItem();
   }
 }
 
@@ -1883,15 +2029,15 @@ void Controller::Impl::RepositionSelectionHandles()
     // Whether to retrieve the next line.
     if( index == lastGlyphOfLine )
     {
-      // Retrieve the next line.
-      ++lineRun;
-
-      // Get the last glyph of the new line.
-      lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
-
       ++lineIndex;
       if( lineIndex < firstLineIndex + numberOfLines )
       {
+        // Retrieve the next line.
+        ++lineRun;
+
+        // Get the last glyph of the new line.
+        lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
+
         // Keep the offset and height of the current selection box.
         const float currentLineOffset = selectionBoxInfo->lineOffset;
         const float currentLineHeight = selectionBoxInfo->lineHeight;
@@ -2089,7 +2235,7 @@ void Controller::Impl::RepositionSelectionHandles()
   mEventData->mDecoratorUpdated = true;
 }
 
-void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
+void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
 {
   if( NULL == mEventData )
   {
@@ -2097,12 +2243,6 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY
     return;
   }
 
-  if( IsShowingPlaceholderText() )
-  {
-    // Nothing to do if there is the place-holder text.
-    return;
-  }
-
   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
   const Length numberOfLines  = mVisualModel->mLines.Count();
   if( ( 0 == numberOfGlyphs ) ||
@@ -2115,16 +2255,18 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY
   // Find which word was selected
   CharacterIndex selectionStart( 0 );
   CharacterIndex selectionEnd( 0 );
-  const bool indicesFound = FindSelectionIndices( mVisualModel,
+  CharacterIndex noTextHitIndex( 0 );
+  const bool characterHit = FindSelectionIndices( mVisualModel,
                                                   mLogicalModel,
                                                   mMetrics,
                                                   visualX,
                                                   visualY,
                                                   selectionStart,
-                                                  selectionEnd );
+                                                  selectionEnd,
+                                                  noTextHitIndex );
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
 
-  if( indicesFound )
+  if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
   {
     ChangeState( EventData::SELECTING );
 
@@ -2135,14 +2277,30 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY
     mEventData->mUpdateRightSelectionPosition = true;
     mEventData->mUpdateHighlightBox = true;
 
+    // It may happen an IMF commit event arrives before the selection event
+    // if the IMF manager is in pre-edit state. The commit event will set the
+    // mEventData->mUpdateCursorPosition flag to true. If it's not set back
+    // to false, the highlight box won't be updated.
+    mEventData->mUpdateCursorPosition = false;
+
     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
   }
-  else
+  else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
   {
     // Nothing to select. i.e. a white space, out of bounds
-    ChangeState( EventData::EDITING );
+    ChangeState( EventData::EDITING_WITH_POPUP );
+
+    mEventData->mPrimaryCursorPosition = noTextHitIndex;
 
-    mEventData->mPrimaryCursorPosition = selectionEnd;
+    mEventData->mUpdateCursorPosition = true;
+    mEventData->mUpdateGrabHandlePosition = true;
+    mEventData->mScrollAfterUpdatePosition = true;
+    mEventData->mUpdateInputStyle = true;
+  }
+  else if( Controller::NoTextTap::NO_ACTION == action )
+  {
+    // Nothing to select. i.e. a white space, out of bounds
+    mEventData->mPrimaryCursorPosition = noTextHitIndex;
 
     mEventData->mUpdateCursorPosition = true;
     mEventData->mUpdateGrabHandlePosition = true;
@@ -2230,7 +2388,6 @@ void Controller::Impl::ChangeState( EventData::State newState )
         mEventData->mDecorator->SetHighlightActive( false );
         mEventData->mDecorator->SetPopupActive( false );
         mEventData->mDecoratorUpdated = true;
-        HideClipboard();
         break;
       }
       case EventData::INTERRUPTED:
@@ -2241,7 +2398,6 @@ void Controller::Impl::ChangeState( EventData::State newState )
         mEventData->mDecorator->SetHighlightActive( false );
         mEventData->mDecorator->SetPopupActive( false );
         mEventData->mDecoratorUpdated = true;
-        HideClipboard();
         break;
       }
       case EventData::SELECTING:
@@ -2277,7 +2433,6 @@ void Controller::Impl::ChangeState( EventData::State newState )
           mEventData->mDecorator->SetPopupActive( false );
         }
         mEventData->mDecoratorUpdated = true;
-        HideClipboard();
         break;
       }
       case EventData::EDITING_WITH_POPUP:
@@ -2304,7 +2459,6 @@ void Controller::Impl::ChangeState( EventData::State newState )
           SetPopupButtons();
           mEventData->mDecorator->SetPopupActive( true );
         }
-        HideClipboard();
         mEventData->mDecoratorUpdated = true;
         break;
       }
@@ -2327,7 +2481,6 @@ void Controller::Impl::ChangeState( EventData::State newState )
           mEventData->mDecorator->SetPopupActive( false );
         }
         mEventData->mDecoratorUpdated = true;
-        HideClipboard();
         break;
       }
       case EventData::SELECTION_HANDLE_PANNING:
@@ -2385,7 +2538,6 @@ void Controller::Impl::ChangeState( EventData::State newState )
           SetPopupButtons();
           mEventData->mDecorator->SetPopupActive( true );
         }
-        HideClipboard();
         mEventData->mDecoratorUpdated = true;
         break;
       }
@@ -2452,13 +2604,18 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
     return;
   }
 
-  Text::GetCursorPosition( mVisualModel,
-                           mLogicalModel,
-                           mMetrics,
-                           logical,
+  const bool isMultiLine = ( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
+  GetCursorPositionParameters parameters;
+  parameters.visualModel = mVisualModel;
+  parameters.logicalModel = mLogicalModel;
+  parameters.metrics = mMetrics;
+  parameters.logical = logical;
+  parameters.isMultiline = isMultiLine;
+
+  Text::GetCursorPosition( parameters,
                            cursorInfo );
 
-  if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
+  if( isMultiLine )
   {
     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
 
@@ -2611,13 +2768,13 @@ void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
 
 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
 {
-  // Clamp between -space & 0.
+  // Clamp between -space & -alignment offset.
 
   if( layoutSize.width > mVisualModel->mControlSize.width )
   {
-    const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
+    const float space = ( layoutSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset;
     mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
-    mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
+    mScrollPosition.x = ( mScrollPosition.x > -mAlignmentOffset ) ? -mAlignmentOffset : mScrollPosition.x;
 
     mEventData->mDecoratorUpdated = true;
   }
@@ -2629,6 +2786,12 @@ void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
 
 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
 {
+  if( LayoutEngine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
+  {
+    // Nothing to do if the text is single line.
+    return;
+  }
+
   // Clamp between -space & 0.
   if( layoutSize.height > mVisualModel->mControlSize.height )
   {
@@ -2668,13 +2831,16 @@ void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, flo
     mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
   }
 
-  if( decoratorPositionBeginY < 0.f )
-  {
-    mScrollPosition.y = -position.y;
-  }
-  else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
+  if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
   {
-    mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
+    if( decoratorPositionBeginY < 0.f )
+    {
+      mScrollPosition.y = -position.y;
+    }
+    else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
+    {
+      mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
+    }
   }
 }
 
@@ -2685,10 +2851,13 @@ void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
 
   // Calculate the offset to match the cursor position before the character was deleted.
   mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
-  mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
-
   ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
-  ClampVerticalScroll( mVisualModel->GetLayoutSize() );
+
+  if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
+  {
+    mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
+    ClampVerticalScroll( mVisualModel->GetLayoutSize() );
+  }
 
   // Makes the new cursor position visible if needed.
   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
@@ -2696,7 +2865,10 @@ void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
 
 void Controller::Impl::RequestRelayout()
 {
-  mControlInterface.RequestTextRelayout();
+  if( NULL != mControlInterface )
+  {
+    mControlInterface->RequestTextRelayout();
+  }
 }
 
 } // namespace Text