Merge "Fixes some alignment issues." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
index 25ff08d..47afc96 100644 (file)
@@ -34,10 +34,11 @@ namespace
 {
 
 #if defined(DEBUG_ENABLED)
-  Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Verbose, true, "LOG_TEXT_CONTROLS");
+  Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
 const float MAX_FLOAT = std::numeric_limits<float>::max();
+const unsigned int POINTS_PER_INCH = 72;
 
 const std::string EMPTY_STRING("");
 
@@ -73,6 +74,8 @@ void Controller::EnableTextInput( DecoratorPtr decorator )
 
 void Controller::SetText( const std::string& text )
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
+
   // Remove the previously set text
   ResetText();
 
@@ -81,9 +84,10 @@ void Controller::SetText( const std::string& text )
   if( mImpl->mEventData )
   {
     // If popup shown then hide it by switching to Editing state
-    if ( EventData::SELECTING == mImpl->mEventData->mState ||
-         EventData::SELECTION_CHANGED == mImpl->mEventData->mState ||
-         EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState )
+    if( ( EventData::SELECTING == mImpl->mEventData->mState )          ||
+        ( EventData::SELECTION_CHANGED == mImpl->mEventData->mState )  ||
+        ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
+        ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
     {
       mImpl->ChangeState( EventData::EDITING );
     }
@@ -221,7 +225,7 @@ int Controller::GetMaximumNumberOfCharacters()
   return mImpl->mMaximumNumberOfCharacters;
 }
 
-void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
+void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily, bool userDefined )
 {
   if( !mImpl->mFontDefaults )
   {
@@ -229,7 +233,7 @@ void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
   }
 
   mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
-
+  mImpl->mUserDefinedFontFamily = userDefined;
   // Clear the font-specific data
   ClearFontData();
 
@@ -286,6 +290,15 @@ void Controller::SetDefaultPointSize( float pointSize )
 
   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
 
+  unsigned int horizontalDpi( 0u );
+  unsigned int verticalDpi( 0u );
+  mImpl->mFontClient.GetDpi( horizontalDpi, verticalDpi );
+
+  // Adjust the metrics if the fixed-size font should be down-scaled
+  int maxEmojiSize( pointSize/POINTS_PER_INCH * verticalDpi );
+  DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultPointSize %p setting MaxEmojiSize %d\n", this, maxEmojiSize );
+  mImpl->mMetrics->SetMaxEmojiSize( maxEmojiSize );
+
   // Clear the font-specific data
   ClearFontData();
 
@@ -305,6 +318,23 @@ float Controller::GetDefaultPointSize() const
   return 0.0f;
 }
 
+void Controller::UpdateAfterFontChange( std::string& newDefaultFont )
+{
+  DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange");
+
+  ClearFontData();
+
+  if ( !mImpl->mUserDefinedFontFamily ) // If user defined font then should not update when system font changes
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
+    mImpl->mFontDefaults->mDefaultFontFamily=newDefaultFont;
+    mImpl->UpdateModel( ALL_OPERATIONS );
+    mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
+    mImpl->mRecalculateNaturalSize = true;
+    mImpl->RequestRelayout();
+  }
+}
+
 void Controller::SetTextColor( const Vector4& textColor )
 {
   mImpl->mTextColor = textColor;
@@ -640,11 +670,24 @@ bool Controller::Relayout( const Size& size )
   // Do not re-do any operation until something changes.
   mImpl->mOperationsPending = NO_OPERATION;
 
+  // Keep the current offset and alignment as it will be used to update the decorator's positions.
+  Vector2 offset;
+  if( mImpl->mEventData )
+  {
+    offset = mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition;
+  }
+
   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
   CalculateTextAlignment( size );
 
   if( mImpl->mEventData )
   {
+    // If there is a nex size, the scroll position needs to be clamped.
+    mImpl->ClampHorizontalScroll( layoutSize );
+
+    // Update the decorator's positions.
+    mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition - offset );
+
     // Move the cursor, grab handle etc.
     updated = mImpl->ProcessInputEvents() || updated;
   }
@@ -680,6 +723,13 @@ void Controller::ProcessModifyEvents()
     }
   }
 
+  if( mImpl->mEventData &&
+      0 != events.size() )
+  {
+    // When the text is being modified, delay cursor blinking
+    mImpl->mEventData->mDecorator->DelayCursorBlink();
+  }
+
   // Discard temporary text
   events.clear();
 }
@@ -708,8 +758,9 @@ void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
     mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
 
     // Update the cursor if it's in editing mode.
-    if( ( EventData::EDITING == mImpl->mEventData->mState ) ||
-        ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) )
+    if( ( EventData::EDITING == mImpl->mEventData->mState )            ||
+        ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
+        ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
     {
       mImpl->mEventData->mUpdateCursorPosition = true;
     }
@@ -762,8 +813,13 @@ void Controller::TextInsertedEvent()
                                                            REORDER );
 
   // Queue a cursor reposition event; this must wait until after DoRelayout()
-  mImpl->mEventData->mUpdateCursorPosition = true;
-  mImpl->mEventData->mScrollAfterUpdatePosition = true;
+  if( ( EventData::EDITING == mImpl->mEventData->mState )            ||
+      ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
+      ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
+  {
+    mImpl->mEventData->mUpdateCursorPosition = true;
+    mImpl->mEventData->mScrollAfterUpdatePosition = true;
+  }
 }
 
 void Controller::TextDeletedEvent()
@@ -1091,7 +1147,12 @@ void Controller::KeyboardFocusGainEvent()
 
   if( mImpl->mEventData )
   {
-    mImpl->ChangeState( EventData::EDITING );
+    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.
+    }
 
     if( mImpl->IsShowingPlaceholderText() )
     {
@@ -1099,7 +1160,6 @@ void Controller::KeyboardFocusGainEvent()
       ShowPlaceholderText();
     }
 
-    mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
     mImpl->RequestRelayout();
   }
 }
@@ -1185,7 +1245,8 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
       textChanged = true;
     }
 
-    if ( mImpl->mEventData->mState != EventData::INTERRUPTED &&  mImpl->mEventData->mState != EventData::INACTIVE )
+    if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
+         ( mImpl->mEventData->mState != EventData::INACTIVE ) )
     {
       mImpl->ChangeState( EventData::EDITING );
     }
@@ -1284,24 +1345,6 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
         mImpl->mEventData->mPreEditFlag = true;
 
-        if( 0u != mImpl->mVisualModel->mCharactersToGlyph.Count() )
-        {
-          // Add the underline for the pre-edit text.
-          const GlyphIndex* const charactersToGlyphBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
-          const Length* const glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
-
-          const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mImpl->mEventData->mPreEditStartPosition );
-          const CharacterIndex lastPreEditCharacter = mImpl->mEventData->mPreEditStartPosition + ( ( mImpl->mEventData->mPreEditLength > 0u ) ? mImpl->mEventData->mPreEditLength - 1u : 0u );
-          const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
-          const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
-
-          GlyphRun underlineRun;
-          underlineRun.glyphIndex = glyphStart;
-          underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
-
-          // TODO: At the moment the underline runs are only for pre-edit.
-          mImpl->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
-        }
         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
       }
     }
@@ -1383,33 +1426,50 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y )
 
   if( NULL != mImpl->mEventData )
   {
-    const bool isShowingPlaceholderText = mImpl->IsShowingPlaceholderText();
     if( 1u == tapCount )
     {
-      if( !isShowingPlaceholderText &&
-          ( EventData::EDITING == mImpl->mEventData->mState ) )
+      // This is to avoid unnecessary relayouts when tapping an empty text-field
+      bool relayoutNeeded( false );
+
+      if( mImpl->IsShowingRealText() &&
+          EventData::EDITING == mImpl->mEventData->mState )
       {
+        // Show grab handle on second tap
         mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
+        relayoutNeeded = true;
       }
-      else if( EventData::EDITING_WITH_GRAB_HANDLE != mImpl->mEventData->mState  )
+      else if( EventData::EDITING                  != mImpl->mEventData->mState &&
+               EventData::EDITING_WITH_GRAB_HANDLE != mImpl->mEventData->mState )
       {
-        // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
+        // Show cursor on first tap
         mImpl->ChangeState( EventData::EDITING );
+        relayoutNeeded = true;
+      }
+      else if( mImpl->IsShowingRealText() )
+      {
+        // Move the cursor
+        relayoutNeeded = true;
       }
 
-      Event event( Event::TAP_EVENT );
-      event.p1.mUint = tapCount;
-      event.p2.mFloat = x;
-      event.p3.mFloat = y;
-      mImpl->mEventData->mEventQueue.push_back( event );
+      // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
+      if( relayoutNeeded )
+      {
+        Event event( Event::TAP_EVENT );
+        event.p1.mUint = tapCount;
+        event.p2.mFloat = x;
+        event.p3.mFloat = y;
+        mImpl->mEventData->mEventQueue.push_back( event );
 
-      mImpl->RequestRelayout();
+        mImpl->RequestRelayout();
+      }
     }
-    else if( !isShowingPlaceholderText &&
-             mImpl->mEventData->mSelectionEnabled &&
-             ( 2u == tapCount ) )
+    else if( 2u == tapCount )
     {
-      SelectEvent( x, y, false );
+      if( mImpl->mEventData->mSelectionEnabled &&
+          mImpl->IsShowingRealText() )
+      {
+        SelectEvent( x, y, false );
+      }
     }
   }